Update SPIRV-Tools to 8910ea5f1

Also applies a fix for `spvOpcodeString` being moved to the public spirv-tools interface.

Changes:
    8910ea5f1 Fix Wrange-loop-analysis warnings in SPIRV-Tools. (#3201)
    c316fb15f Add missing dependencies when generating spvtools_core_tables (#3199)
    03c9effb5 Brief guide to writing a spirv-fuzz fuzzer pass (#3190)
    dea1040fa Fix ignored const qualifier warning in static_cast (#3197)
    4a80497a8 Make spvOpcodeString part of the public API (#3174)
    03794b8f5 Fix static libraries linking order (#3189)
    79f8caf91 vscode: Add missing fields to schema.Opcode (#3169)
    4d912f4e6 spirv-val: Add support for SPV_AMD_shader_image_load_store_lod (#3186)
    77fefe765 spirvfuzz: Fix type-related bug, change undef to zero, and add assert (#3188)
    6c218ec60 spirv-fuzz: Fuzzer pass that adds access chains (#3182)
    77fb303e5 spirv-fuzz: Fuzzer pass to add function calls (#3178)
    3d4a0dd48 spirv-fuzz: Ensure that donated variables are always initialized (#3181)
    fe10239f9 spirv-fuzz: Add fuzzer passes to add loads/stores (#3176)
    1f03ac102 spirv-fuzz: Fuzzer passes to add local and global variables (#3175)
    9e52bc0d0 utils/vscode: LSP - Support OpExtInst (#3140)
    a3b5ad909 Fix typos in opt's help. Update environment version. (#3170)
    da5457f6b Start SPIRV-Tools v2020.2
    8dd174809 Finalize SPIRV-Tools v2020.1
    76616bab6 Update CHANGES
    bb56e892f spirv-fuzz: Fuzzer pass to add composite types (#3171)
    fdd0c8776 Update script that checks copyright years. (#3173)
    b7e0998e3 spirv-fuzz: Disallow copying of null and undefined pointers (#3172)
    a9624b4d5 Handle TimeAMD in AmdExtensionToKhrPass. (#3168)
    ddcc11763 Update CHANGES
    0265a9d4d Implement constant folding for many transcendentals (#3166)
    7a2d408de Fix typo in comment. (#3163)
    1fc7a9ec7 spirv-fuzz: Arbitrary variable facts (#3165)
    dd3f8d492 spirv-fuzz: Add outlining test (#3164)
    521223b70 spirv-fuzz: Make functions "livesafe" during donation (#3146)
    97f1d485b Dead branch elim fix (#3160)
    1b3441036 Fix chromium build (#3152)
    5d786f6cc Clarify mapping of target env to SPIR-V version (#3150)
    e7afeb060 Use dummy switch instead of dummy loop in MergeReturn pass. (#3151)
    6729c4a7c Fix structured exit validation (#3141)
    bb236c326 Add spvParseVulkanEnv (#3142)
    dd37d73c5 Handle conflict between debug info and existing validation rule (#3104)
    b97057e7f Use spirv-headers' BUILD.gn (#3148)
    6a1a1b326 Roll external/spirv-headers/ af64a9e82..dc77030ac (4 commits) (#3147)
    ab7ac60f1 spirv-fuzz: Refactoring and type-related fixes (#3144)
    323a81fc5 Validate Vulkan 1.2 capabilities
    d46a1b048 Add basic support for Vulkan 1.2: SPV_ENV_VULKAN_1_2
    f8d7df760 Fix OpLine bug of merge-blocks pass (#3130)
    8013d477a spirv-fuzz: add dead blocks (#3135)
    18b3b9456 Remove names and decorations of imported symbols (#3081)
    fb2e81981 Fix GN build for OpenCL.DebugInfo.100 update (#3134)
    c86dc0bcd Fix bad parameter names in error message (#3129)
    899735f45 Revert PR #3093 (#3131)
    a466b99db Disallow forward references in arrays (#3093)
    31acc7882 spirv-fuzz: Add fuzzer pass to perform module donation (#3117)
    c8bf14393 GetOperandConstants operand can be const (#3126)

Commands:
    git subtree pull --prefix third_party/SPIRV-Tools https://github.com/KhronosGroup/SPIRV-Tools master --squash

Bug: b/123642959
Bug: angleproject:4425
Change-Id: I73076a22b474893d900a017d94a5d122061381bc
diff --git a/src/Pipeline/SpirvShaderEnumNames.cpp b/src/Pipeline/SpirvShaderEnumNames.cpp
index 39a0bf0..7956995 100644
--- a/src/Pipeline/SpirvShaderEnumNames.cpp
+++ b/src/Pipeline/SpirvShaderEnumNames.cpp
@@ -15,14 +15,9 @@
 // This file contains code used to aid debugging.
 
 #include "SpirvShader.hpp"
-#include <spirv/unified1/spirv.h>
 
-// Prototypes for SPIRV-Tools functions that do not have public headers.
-// This is a C++ function, so the name is mangled, and signature changes will
-// result in a linker error instead of runtime signature mismatches.
-
-// Gets the name of an instruction, without the "Op" prefix.
-extern const char *spvOpcodeString(const SpvOp opcode);
+#include "spirv-tools/libspirv.h"
+#include "spirv/unified1/spirv.h"
 
 namespace sw {
 
diff --git a/third_party/SPIRV-Tools/BUILD.gn b/third_party/SPIRV-Tools/BUILD.gn
index 3c85c4e..b7cde34 100644
--- a/third_party/SPIRV-Tools/BUILD.gn
+++ b/third_party/SPIRV-Tools/BUILD.gn
@@ -37,6 +37,8 @@
 
     sources = [
       core_json_file,
+      debuginfo_insts_file,
+      cldebuginfo100_insts_file,
     ]
     outputs = [
       core_insts_file,
@@ -69,6 +71,7 @@
         "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
     debuginfo_insts_file = "source/extinst.debuginfo.grammar.json"
     cldebuginfo100_insts_file = "source/extinst.opencl.debuginfo.100.grammar.json"
+
     extension_enum_file = "${target_gen_dir}/extension_enum.inc"
     extension_map_file = "${target_gen_dir}/enum_string_mapping.inc"
 
@@ -86,6 +89,8 @@
     ]
     inputs = [
       core_json_file,
+      debuginfo_insts_file,
+      cldebuginfo100_insts_file,
     ]
     outputs = [
       extension_enum_file,
@@ -105,11 +110,18 @@
     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 = "source/extinst.debuginfo.grammar.json"
+    cldebuginfo100_insts_file = "source/extinst.opencl.debuginfo.100.grammar.json"
+
     glsl_insts_file = "${target_gen_dir}/glsl.std.450.insts.inc"
 
     args = [
       "--spirv-core-grammar",
       rebase_path(core_json_file, root_build_dir),
+      "--extinst-debuginfo-grammar",
+      rebase_path(debuginfo_insts_file, root_build_dir),
+      "--extinst-cldebuginfo100-grammar",
+      rebase_path(cldebuginfo100_insts_file, root_build_dir),
       "--extinst-glsl-grammar",
       rebase_path(glsl_json_file, root_build_dir),
       "--glsl-insts-output",
@@ -118,6 +130,8 @@
     inputs = [
       core_json_file,
       glsl_json_file,
+      debuginfo_insts_file,
+      cldebuginfo100_insts_file,
     ]
     outputs = [
       glsl_insts_file,
@@ -135,20 +149,29 @@
 
     core_json_file =
         "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
-    opengl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.opencl.std.100.grammar.json"
+    opencl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.opencl.std.100.grammar.json"
+    debuginfo_insts_file = "source/extinst.debuginfo.grammar.json"
+    cldebuginfo100_insts_file = "source/extinst.opencl.debuginfo.100.grammar.json"
+
     opencl_insts_file = "${target_gen_dir}/opencl.std.insts.inc"
 
     args = [
       "--spirv-core-grammar",
       rebase_path(core_json_file, root_build_dir),
+      "--extinst-debuginfo-grammar",
+      rebase_path(debuginfo_insts_file, root_build_dir),
+      "--extinst-cldebuginfo100-grammar",
+      rebase_path(cldebuginfo100_insts_file, root_build_dir),
       "--extinst-opencl-grammar",
-      rebase_path(opengl_json_file, root_build_dir),
+      rebase_path(opencl_json_file, root_build_dir),
       "--opencl-insts-output",
       rebase_path(opencl_insts_file, root_build_dir),
     ]
     inputs = [
       core_json_file,
-      opengl_json_file,
+      opencl_json_file,
+      debuginfo_insts_file,
+      cldebuginfo100_insts_file,
     ]
     outputs = [
       opencl_insts_file,
@@ -174,7 +197,7 @@
       rebase_path(extinst_output_base, root_build_dir),
     ]
     inputs = [
-      debug_insts_file,
+      invoker.grammar_file,
     ]
     outputs = [
       "${extinst_output_base}.h",
@@ -267,16 +290,16 @@
 }
 
 spvtools_vendor_tables = [
-  ["spv-amd-shader-explicit-vertex-parameter", ""],
-  ["spv-amd-shader-trinary-minmax", ""],
-  ["spv-amd-gcn-shader", ""],
-  ["spv-amd-shader-ballot", ""],
-  ["debuginfo", ""],
+  ["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_"],
 ]
 
 foreach(table_def, spvtools_vendor_tables) {
-  spvtools_vendor_table(table) {
+  spvtools_vendor_table(table_def[0]) {
     name = table_def[0]
     operand_kind_prefix = table_def[1]
   }
@@ -303,14 +326,6 @@
   }
 }
 
-source_set("spv_headers") {
-  sources = [
-    "$spirv_headers/include/spirv/1.2/GLSL.std.450.h",
-    "$spirv_headers/include/spirv/unified1/OpenCL.std.h",
-    "$spirv_headers/include/spirv/unified1/spirv.h",
-  ]
-}
-
 source_set("spvtools_headers") {
   sources = [
     "include/spirv-tools/instrument.hpp",
@@ -403,9 +418,9 @@
   ]
 
   public_deps = [
-    ":spv_headers",
     ":spvtools_core_enums_unified1",
     ":spvtools_headers",
+    "${spirv_headers}:spv_headers",
   ]
 
   if (build_with_chromium) {
@@ -469,6 +484,8 @@
 
   deps = [
     ":spvtools",
+    ":spvtools_language_header_cldebuginfo100",
+    ":spvtools_language_header_debuginfo",
   ]
   public_deps = [
     ":spvtools_headers",
@@ -696,6 +713,8 @@
 
   deps = [
     ":spvtools",
+    ":spvtools_language_header_cldebuginfo100",
+    ":spvtools_language_header_debuginfo",
     ":spvtools_vendor_tables_spv-amd-shader-ballot",
   ]
   public_deps = [
diff --git a/third_party/SPIRV-Tools/CHANGES b/third_party/SPIRV-Tools/CHANGES
index 2f2968e..48c93a4 100644
--- a/third_party/SPIRV-Tools/CHANGES
+++ b/third_party/SPIRV-Tools/CHANGES
@@ -1,7 +1,32 @@
 Revision history for SPIRV-Tools
 
-v2020.1-dev 2019-12-11
- - Start v2020.1-dev
+v2020.2-dev 2020-02-03
+ - Start v2020.2-dev
+
+v2020.1 2020-02-03
+ - General:
+   - Add support for SPV_KHR_non_semantic_info (#3110)
+   - Support OpenCL.DebugInfo.100 extended instruction set (#3080)
+   - Added support for Vulkan 1.2
+   - Add API function to better handle getting the necessary environment (#3142)
+   - Clarify mapping of target env to SPIR-V version (#3150)
+   - Implement constant folding for many transcendentals (#3166)
+ - Optimizer
+   - Change default version for CreatInstBindlessCheckPass to 2 (#3096, #3119)
+   - Better handling of OpLine on merge blocks (#3130)
+   - Use dummy switch instead of dummy loop in MergeReturn pass. (#3151)
+   - Handle TimeAMD in AmdExtensionToKhrPass. (#3168)
+ - Validator
+   - Fix structured exit validation (#3141)
+ - Reduce
+ - Fuzz
+   - Fuzzer pass to merge blocks (#3097)
+   - Transformation to add a new function to a module (#3114)
+   - Add fuzzer pass to perform module donation (#3117)
+   - Fuzzer passes to create and branch to new dead blocks (#3135)
+   - Fuzzer pass to add composite types (#3171)
+ - Linker:
+   - Remove names and decorations of imported symbols (#3081)
 
 v2019.5 2019-12-11
  - General:
diff --git a/third_party/SPIRV-Tools/CMakeLists.txt b/third_party/SPIRV-Tools/CMakeLists.txt
index 6ed56a8..ef9ad11 100644
--- a/third_party/SPIRV-Tools/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/CMakeLists.txt
@@ -300,7 +300,7 @@
            WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
 endif()
 
-set(SPIRV_LIBRARIES "-lSPIRV-Tools -lSPIRV-Tools-link -lSPIRV-Tools-opt")
+set(SPIRV_LIBRARIES "-lSPIRV-Tools-opt -lSPIRV-Tools -lSPIRV-Tools-link")
 set(SPIRV_SHARED_LIBRARIES "-lSPIRV-Tools-shared")
 
 # Build pkg-config file
diff --git a/third_party/SPIRV-Tools/DEPS b/third_party/SPIRV-Tools/DEPS
index 311b7ed..23c4111 100644
--- a/third_party/SPIRV-Tools/DEPS
+++ b/third_party/SPIRV-Tools/DEPS
@@ -6,7 +6,7 @@
   'effcee_revision': 'cd25ec17e9382f99a895b9ef53ff3c277464d07d',
   'googletest_revision': 'f2fb48c3b3d79a75a88a99fba6576b25d42ec528',
   're2_revision': '5bd613749fd530b576b890283bfb6bc6ea6246cb',
-  'spirv_headers_revision': 'af64a9e826bf5bb5fcd2434dd71be1e41e922563',
+  'spirv_headers_revision': 'dc77030acc9c6fe7ca21fff54c5a9d7b532d7da6',
 }
 
 deps = {
diff --git a/third_party/SPIRV-Tools/docs/spirv-fuzz.md b/third_party/SPIRV-Tools/docs/spirv-fuzz.md
new file mode 100644
index 0000000..e5439e3
--- /dev/null
+++ b/third_party/SPIRV-Tools/docs/spirv-fuzz.md
@@ -0,0 +1,83 @@
+# Guide to writing a spirv-fuzz fuzzer pass
+
+Writing a spirv-fuzz fuzzer pass usually requires two main contributions:
+
+- A *transformation*, capturing a small semantics-preserving change that can be made to a SPIR-V module.  This requires adding a protobuf message representing the transformation, and a corresponding class that implements the `Transformation` interface.
+- A new *fuzzer pass* class, implementing the `FuzzerPass` interface, that knows how to walk a SPIR-V module and apply the new transformation in a randomized fashion.
+
+In some cases, more than one kind of transformation is required for a single fuzzer pass, and in some cases the transformations that a new fuzzer pass requires have already been introduced by existing passes.  But the most common case is to introduce a transformation and fuzzer pass together.
+
+As an example, let's consider the `TransformationSetSelectionControl` transformation.  In SPIR-V, an `OpSelectionMerge` instruction (which intuitively indicates the start of an `if` or `switch` statement in a function) has a *selection control* mask, that can be one of `None`, `Flatten` or `DontFlatten`.  The details of these do not matter much for this little tutorial, but in brief, this parameter provides a hint to the shader compiler as to whether it would be profitable to attempt to flatten a piece of conditional code so that all of its statements are executed in a predicated fashion.
+
+As the selection control mask is just a hint, changing the value of this mask should have no semantic impact on the module.  The `TransformationSelectionControl` transformation specifies a new value for a given selection control mask.
+
+## Adding a new protobuf message
+
+Take a look at the `Transformation` message in `spvtoolsfuzz.proto`.  This has a `oneof` field that can be any one of the different spirv-fuzz transformations.  Observe that one of the options is `TransformationSetSelectionControl`.  When adding a transformation you first need to add an option for your transformation to the end of the `oneof` declaration.
+
+Now look at the `TransformationSetSelectionControl` message.  If adding your own transformation you need to add a new message for your transformation, and it should be placed alphabetically with respect to other transformations.
+
+The fields of `TransformationSetSelectionControl` provide just enough information to (a) determine whether a given example of this transformation is actually applicable, and (b) apply the transformation in the case that it is applicable.  The details of the transformation message will vary a lot between transformations.  In this case, the message has a `block_id` field, specifying a block that must end with `OpSelectionMerge`, and a `selection_control` field, which is the new value for the selection control mask of the `OpSelectionMerge` instruction.
+
+## Adding a new transformation class
+
+If your transformation is called `TransformationSomeThing`, you need to add `transformation_some_thing.h` and `transformation_some_thing.cpp` to `source/fuzz` and the corresponding `CMakeLists.txt` file.  So for `TransformationSetSelectionControl` we have `transformation_selection_control.h` and `transformation_selection_control.cpp`, and we will use this as an example to illustrate the expected contents of these files.
+
+The header file contains the specification of a class, `TransformationSetSelectionControl`, that implements the `Transformation` interface (from `transformation.h`).
+
+A transformation class should always have a single field, which should be the associated protobuf message; in our case:
+
+```
+ private:
+  protobufs::TransformationSetSelectionControl message_;
+```
+
+and two public constructors, one that takes a protobuf message; in our case:
+
+```
+  explicit TransformationSetSelectionControl(
+      const protobufs::TransformationSetSelectionControl& message);
+```
+
+and one that takes a parameter for each protobuf message field; in our case:
+
+```
+  TransformationSetSelectionControl(uint32_t block_id);
+```
+
+The first constructor allows an instance of the class to be created from a corresponding protobuf message.  The second should provide the ingredients necessary to populate a protobuf message.
+
+The class should also override the `IsApplicable`, `Apply` and `ToMessage` methods from `Transformation`.
+
+See `transformation_set_selection_control.h` for an example.
+
+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.
+
+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.
+
+## Writing tests for the transformation class
+
+Whenever you add a transformation class, `TransformationSomeThing`, you should add an associated test file, `transformation_some_thing_test.cpp`, under `test/fuzz`, adding it to the associated `CMakeLists.txt` file.
+
+For example `test/fuzz/transformation_set_selection_control_test.cpp` contains tests for `TransformationSetSelectionControl`.  Your tests should aim to cover one example from each scenario where the transformation is inapplicable, and check that it is indeed deemed inapplicable, and then check that the transformation does the right thing when applied in a few different ways.
+
+For example, the tests for `TransformationSetSelectionControl` check that a transformation of this kind is inapplicable if the `block_id` field of the transformation is not a block, or does not end in `OpSelectionMerge`, or if the `selection_control` mask has an illegal value.  It also checks that applying a sequence of valid transformations to a SPIR-V shader leads to a shader with appropriately modified selection controls.
+
+## Adding a new fuzzer pass class
+
+A *fuzzer pass* traverses a SPIR-V module looking for places to apply a certain kind of transformation, and randomly decides at which of these points to actually apply the transformation.  It might be necessary to apply other transformations in order to apply a given transformation (for example, if a transformation requires a certain type to be present in the module, said type can be added if not already present via another transformation).
+
+A fuzzer pass implements the `FuzzerPass` interface, and overrides its `Apply` method.  If your fuzzer pass is named `FuzzerPassSomeThing` then it should be represented by `fuzzer_pass_some_thing.h` and `fuzzer_pass_some_thing.cpp`, under `source/fuzz`; these should be added to the associated `CMakeLists.txt` file.
+
+Have a look at the source filed for `FuzzerPassAdjustSelectionControls`.  This pass considers every block that ends with `OpSelectionMerge`.  It decides randomly whether to adjust the selection control of this merge instruction via:
+
+```
+if (!GetFuzzerContext()->ChoosePercentage(
+        GetFuzzerContext()->GetChanceOfAdjustingSelectionControl())) {
+  continue;
+}
+```
+
+The `GetChanceOfAddingSelectionControl()` method has been added to `FuzzerContext` specifically to support this pass, and returns a percentage between 0 and 100.  It returns the `chance_of_adjusting_selection_control_` of `FuzzerContext`, which is randomly initialized to lie with the interval defined by `kChanceOfAdjustingSelectionControl` in `fuzzer_context.cpp`.  For any pass you write, you will need to add an analogous `GetChanceOf...` method to `FuzzerContext`, backed by an appropriate field, and you will need to decide on lower and upper bounds for this field and specify these via a `kChanceOf...` constant.
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h
index 5dcb81a..21a9608 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h
+++ b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h
@@ -419,8 +419,17 @@
 SPIRV_TOOLS_EXPORT const char* spvSoftwareVersionDetailsString(void);
 
 // Certain target environments impose additional restrictions on SPIR-V, so it's
-// often necessary to specify which one applies.  SPV_ENV_UNIVERSAL means
+// often necessary to specify which one applies.  SPV_ENV_UNIVERSAL_* implies an
 // environment-agnostic SPIR-V.
+//
+// When an API method needs to derive a SPIR-V version from a target environment
+// (from the spv_context object), the method will choose the highest version of
+// SPIR-V supported by the target environment.  Examples:
+//    SPV_ENV_VULKAN_1_0           ->  SPIR-V 1.0
+//    SPV_ENV_VULKAN_1_1           ->  SPIR-V 1.3
+//    SPV_ENV_VULKAN_1_1_SPIRV_1_4 ->  SPIR-V 1.4
+//    SPV_ENV_VULKAN_1_2           ->  SPIR-V 1.5
+// Consult the description of API entry points for specific rules.
 typedef enum {
   SPV_ENV_UNIVERSAL_1_0,  // SPIR-V 1.0 latest revision, no other restrictions.
   SPV_ENV_VULKAN_1_0,     // Vulkan 1.0 latest revision.
@@ -448,8 +457,12 @@
   SPV_ENV_VULKAN_1_1,     // Vulkan 1.1 latest revision.
   SPV_ENV_WEBGPU_0,       // Work in progress WebGPU 1.0.
   SPV_ENV_UNIVERSAL_1_4,  // SPIR-V 1.4 latest revision, no other restrictions.
-  SPV_ENV_VULKAN_1_1_SPIRV_1_4,  // Vulkan 1.1 with SPIR-V 1.4 binary.
+
+  // Vulkan 1.1 with VK_KHR_spirv_1_4, i.e. SPIR-V 1.4 binary.
+  SPV_ENV_VULKAN_1_1_SPIRV_1_4,
+
   SPV_ENV_UNIVERSAL_1_5,  // SPIR-V 1.5 latest revision, no other restrictions.
+  SPV_ENV_VULKAN_1_2,     // Vulkan 1.2 latest revision.
 } spv_target_env;
 
 // SPIR-V Validator can be parameterized with the following Universal Limits.
@@ -472,7 +485,24 @@
 // false and sets *env to SPV_ENV_UNIVERSAL_1_0.
 SPIRV_TOOLS_EXPORT bool spvParseTargetEnv(const char* s, spv_target_env* env);
 
-// Creates a context object.  Returns null if env is invalid.
+// Determines the target env value with the least features but which enables
+// the given Vulkan and SPIR-V versions. If such a target is supported, returns
+// true and writes the value to |env|, otherwise returns false.
+//
+// The Vulkan version is given as an unsigned 32-bit number as specified in
+// Vulkan section "29.2.1 Version Numbers": the major version number appears
+// in bits 22 to 21, and the minor version is in bits 12 to 21.  The SPIR-V
+// version is given in the SPIR-V version header word: major version in bits
+// 16 to 23, and minor version in bits 8 to 15.
+SPIRV_TOOLS_EXPORT bool spvParseVulkanEnv(uint32_t vulkan_ver,
+                                          uint32_t spirv_ver,
+                                          spv_target_env* env);
+
+// Creates a context object for most of the SPIRV-Tools API.
+// Returns null if env is invalid.
+//
+// See specific API calls for how the target environment is interpeted
+// (particularly assembly and validation).
 SPIRV_TOOLS_EXPORT spv_context spvContextCreate(spv_target_env env);
 
 // Destroys the given context object.
@@ -653,6 +683,8 @@
 // be stored into *binary. Any error will be written into *diagnostic if
 // diagnostic is non-null, otherwise the context's message consumer will be
 // used. The generated binary is independent of the context and may outlive it.
+// The SPIR-V binary version is set to the highest version of SPIR-V supported
+// by the context's target environment.
 SPIRV_TOOLS_EXPORT spv_result_t spvTextToBinary(const spv_const_context context,
                                                 const char* text,
                                                 const size_t length,
@@ -690,6 +722,12 @@
 // Validates a SPIR-V binary for correctness. Any errors will be written into
 // *diagnostic if diagnostic is non-null, otherwise the context's message
 // consumer will be used.
+//
+// Validate for SPIR-V spec rules for the SPIR-V version named in the
+// binary's header (at word offset 1).  Additionally, if the context target
+// environment is a client API (such as Vulkan 1.1), then validate for that
+// client API version, to the extent that it is verifiable from data in the
+// binary itself.
 SPIRV_TOOLS_EXPORT spv_result_t spvValidate(const spv_const_context context,
                                             const spv_const_binary binary,
                                             spv_diagnostic* diagnostic);
@@ -697,6 +735,12 @@
 // Validates a SPIR-V binary for correctness. Uses the provided Validator
 // options. Any errors will be written into *diagnostic if diagnostic is
 // non-null, otherwise the context's message consumer will be used.
+//
+// Validate for SPIR-V spec rules for the SPIR-V version named in the
+// binary's header (at word offset 1).  Additionally, if the context target
+// environment is a client API (such as Vulkan 1.1), then validate for that
+// client API version, to the extent that it is verifiable from data in the
+// binary itself, or in the validator options.
 SPIRV_TOOLS_EXPORT spv_result_t spvValidateWithOptions(
     const spv_const_context context, const spv_const_validator_options options,
     const spv_const_binary binary, spv_diagnostic* diagnostic);
@@ -722,6 +766,9 @@
 SPIRV_TOOLS_EXPORT spv_result_t
 spvDiagnosticPrint(const spv_diagnostic diagnostic);
 
+// Gets the name of an instruction, without the "Op" prefix.
+SPIRV_TOOLS_EXPORT const char* spvOpcodeString(const uint32_t opcode);
+
 // The binary parser interface.
 
 // A pointer to a function that accepts a parsed SPIR-V header.
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp
index ceadef8..5e1819e 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp
+++ b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp
@@ -36,6 +36,9 @@
  public:
   // Constructs a context targeting the given environment |env|.
   //
+  // See specific API calls for how the target environment is interpeted
+  // (particularly assembly and validation).
+  //
   // The constructed instance will have an empty message consumer, which just
   // ignores all messages from the library. Use SetMessageConsumer() to supply
   // one if messages are of concern.
@@ -279,16 +282,20 @@
   // Assembles the given assembly |text| and writes the result to |binary|.
   // Returns true on successful assembling. |binary| will be kept untouched if
   // assembling is unsuccessful.
+  // The SPIR-V binary version is set to the highest version of SPIR-V supported
+  // by the target environment with which this SpirvTools object was created.
   bool Assemble(const std::string& text, std::vector<uint32_t>* binary,
                 uint32_t options = kDefaultAssembleOption) const;
   // |text_size| specifies the number of bytes in |text|. A terminating null
   // character is not required to present in |text| as long as |text| is valid.
+  // The SPIR-V binary version is set to the highest version of SPIR-V supported
+  // by the target environment with which this SpirvTools object was created.
   bool Assemble(const char* text, size_t text_size,
                 std::vector<uint32_t>* binary,
                 uint32_t options = kDefaultAssembleOption) const;
 
   // Disassembles the given SPIR-V |binary| with the given |options| and writes
-  // the assembly to |text|. Returns ture on successful disassembling. |text|
+  // the assembly to |text|. Returns true on successful disassembling. |text|
   // will be kept untouched if diassembling is unsuccessful.
   bool Disassemble(const std::vector<uint32_t>& binary, std::string* text,
                    uint32_t options = kDefaultDisassembleOption) const;
@@ -300,10 +307,26 @@
   // Validates the given SPIR-V |binary|. Returns true if no issues are found.
   // Otherwise, returns false and communicates issues via the message consumer
   // registered.
+  // Validates for SPIR-V spec rules for the SPIR-V version named in the
+  // binary's header (at word offset 1).  Additionally, if the target
+  // environment is a client API (such as Vulkan 1.1), then validate for that
+  // client API version, to the extent that it is verifiable from data in the
+  // binary itself.
   bool Validate(const std::vector<uint32_t>& binary) const;
+  // Like the previous overload, but provides the binary as a pointer and size:
   // |binary_size| specifies the number of words in |binary|.
+  // Validates for SPIR-V spec rules for the SPIR-V version named in the
+  // binary's header (at word offset 1).  Additionally, if the target
+  // environment is a client API (such as Vulkan 1.1), then validate for that
+  // client API version, to the extent that it is verifiable from data in the
+  // binary itself.
   bool Validate(const uint32_t* binary, size_t binary_size) const;
   // Like the previous overload, but takes an options object.
+  // Validates for SPIR-V spec rules for the SPIR-V version named in the
+  // binary's header (at word offset 1).  Additionally, if the target
+  // environment is a client API (such as Vulkan 1.1), then validate for that
+  // client API version, to the extent that it is verifiable from data in the
+  // binary itself, or in the validator options.
   bool Validate(const uint32_t* binary, size_t binary_size,
                 spv_validator_options options) const;
 
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp b/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
index ba8cbaf..c31ccef 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
+++ b/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
@@ -65,9 +65,9 @@
   // Constructs an instance with the given target |env|, which is used to decode
   // the binaries to be optimized later.
   //
-  // The constructed instance will have an empty message consumer, which just
-  // ignores all messages from the library. Use SetMessageConsumer() to supply
-  // one if messages are of concern.
+  // The instance will have an empty message consumer, which ignores all
+  // messages from the library. Use SetMessageConsumer() to supply a consumer
+  // if messages are of concern.
   //
   // For collections of passes that are meant to transform the input into
   // another execution environment, then the source environment should be
@@ -164,17 +164,26 @@
   bool FlagHasValidForm(const std::string& flag) const;
 
   // Allows changing, after creation time, the target environment to be
-  // optimized for.  Should be called before calling Run().
+  // optimized for and validated.  Should be called before calling Run().
   void SetTargetEnv(const spv_target_env env);
 
   // Optimizes the given SPIR-V module |original_binary| and writes the
-  // optimized binary into |optimized_binary|.
+  // optimized binary into |optimized_binary|. The optimized binary uses
+  // the same SPIR-V version as the original binary.
+  //
   // Returns true on successful optimization, whether or not the module is
   // modified. Returns false if |original_binary| fails to validate or if errors
   // occur when processing |original_binary| using any of the registered passes.
   // In that case, no further passes are executed and the contents in
   // |optimized_binary| may be invalid.
   //
+  // By default, the binary is validated before any transforms are performed,
+  // and optionally after each transform.  Validation uses SPIR-V spec rules
+  // for the SPIR-V version named in the binary's header (at word offset 1).
+  // Additionally, if the target environment is a client API (such as
+  // Vulkan 1.1), then validate for that client API version, to the extent
+  // that it is verifiable from data in the binary itself.
+  //
   // It's allowed to alias |original_binary| to the start of |optimized_binary|.
   bool Run(const uint32_t* original_binary, size_t original_binary_size,
            std::vector<uint32_t>* optimized_binary) const;
@@ -190,6 +199,14 @@
 
   // Same as above, except it takes an options object.  See the documentation
   // for |OptimizerOptions| to see which options can be set.
+  //
+  // By default, the binary is validated before any transforms are performed,
+  // and optionally after each transform.  Validation uses SPIR-V spec rules
+  // for the SPIR-V version named in the binary's header (at word offset 1).
+  // Additionally, if the target environment is a client API (such as
+  // Vulkan 1.1), then validate for that client API version, to the extent
+  // that it is verifiable from data in the binary itself, or from the
+  // validator options set on the optimizer options.
   bool Run(const uint32_t* original_binary, const size_t original_binary_size,
            std::vector<uint32_t>* optimized_binary,
            const spv_optimizer_options opt_options) const;
diff --git a/third_party/SPIRV-Tools/source/ext_inst.cpp b/third_party/SPIRV-Tools/source/ext_inst.cpp
index e332f0d..e69c3c9 100644
--- a/third_party/SPIRV-Tools/source/ext_inst.cpp
+++ b/third_party/SPIRV-Tools/source/ext_inst.cpp
@@ -88,6 +88,7 @@
     case SPV_ENV_WEBGPU_0:
     case SPV_ENV_UNIVERSAL_1_4:
     case SPV_ENV_UNIVERSAL_1_5:
+    case SPV_ENV_VULKAN_1_2:
       *pExtInstTable = &kTable_1_0;
       return SPV_SUCCESS;
     default:
@@ -137,6 +138,14 @@
   return false;
 }
 
+bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type) {
+  if (type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
+      type == SPV_EXT_INST_TYPE_DEBUGINFO) {
+    return true;
+  }
+  return false;
+}
+
 spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table,
                                        const spv_ext_inst_type_t type,
                                        const char* name,
diff --git a/third_party/SPIRV-Tools/source/ext_inst.h b/third_party/SPIRV-Tools/source/ext_inst.h
index b42d82b..aff6e30 100644
--- a/third_party/SPIRV-Tools/source/ext_inst.h
+++ b/third_party/SPIRV-Tools/source/ext_inst.h
@@ -24,6 +24,9 @@
 // Returns true if the extended instruction set is non-semantic
 bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type);
 
+// Returns true if the extended instruction set is debug info
+bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type);
+
 // Finds the named extented instruction of the given type in the given extended
 // instruction table. On success, returns SPV_SUCCESS and writes a handle of
 // the instruction entry into *entry.
diff --git a/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt b/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
index bc7d453..4d5feea 100644
--- a/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
@@ -29,6 +29,7 @@
   )
 
   set(SPIRV_TOOLS_FUZZ_SOURCES
+        call_graph.h
         data_descriptor.h
         equivalence_relation.h
         fact_manager.h
@@ -36,9 +37,17 @@
         fuzzer.h
         fuzzer_context.h
         fuzzer_pass.h
+        fuzzer_pass_add_access_chains.h
+        fuzzer_pass_add_composite_types.h
+        fuzzer_pass_add_dead_blocks.h
         fuzzer_pass_add_dead_breaks.h
         fuzzer_pass_add_dead_continues.h
+        fuzzer_pass_add_function_calls.h
+        fuzzer_pass_add_global_variables.h
+        fuzzer_pass_add_loads.h
+        fuzzer_pass_add_local_variables.h
         fuzzer_pass_add_no_contraction_decorations.h
+        fuzzer_pass_add_stores.h
         fuzzer_pass_add_useful_constructs.h
         fuzzer_pass_adjust_function_controls.h
         fuzzer_pass_adjust_loop_controls.h
@@ -47,6 +56,7 @@
         fuzzer_pass_apply_id_synonyms.h
         fuzzer_pass_construct_composites.h
         fuzzer_pass_copy_objects.h
+        fuzzer_pass_donate_modules.h
         fuzzer_pass_merge_blocks.h
         fuzzer_pass_obfuscate_constants.h
         fuzzer_pass_outline_functions.h
@@ -62,14 +72,17 @@
         replayer.h
         shrinker.h
         transformation.h
+        transformation_access_chain.h
         transformation_add_constant_boolean.h
         transformation_add_constant_composite.h
         transformation_add_constant_scalar.h
+        transformation_add_dead_block.h
         transformation_add_dead_break.h
         transformation_add_dead_continue.h
         transformation_add_function.h
         transformation_add_global_undef.h
         transformation_add_global_variable.h
+        transformation_add_local_variable.h
         transformation_add_no_contraction_decoration.h
         transformation_add_type_array.h
         transformation_add_type_boolean.h
@@ -83,6 +96,8 @@
         transformation_composite_construct.h
         transformation_composite_extract.h
         transformation_copy_object.h
+        transformation_function_call.h
+        transformation_load.h
         transformation_merge_blocks.h
         transformation_move_block_down.h
         transformation_outline_function.h
@@ -94,19 +109,29 @@
         transformation_set_memory_operands_mask.h
         transformation_set_selection_control.h
         transformation_split_block.h
+        transformation_store.h
         transformation_vector_shuffle.h
         uniform_buffer_element_descriptor.h
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
 
+        call_graph.cpp
         data_descriptor.cpp
         fact_manager.cpp
         force_render_red.cpp
         fuzzer.cpp
         fuzzer_context.cpp
         fuzzer_pass.cpp
+        fuzzer_pass_add_access_chains.cpp
+        fuzzer_pass_add_composite_types.cpp
+        fuzzer_pass_add_dead_blocks.cpp
         fuzzer_pass_add_dead_breaks.cpp
         fuzzer_pass_add_dead_continues.cpp
+        fuzzer_pass_add_function_calls.cpp
+        fuzzer_pass_add_global_variables.cpp
+        fuzzer_pass_add_loads.cpp
+        fuzzer_pass_add_local_variables.cpp
         fuzzer_pass_add_no_contraction_decorations.cpp
+        fuzzer_pass_add_stores.cpp
         fuzzer_pass_add_useful_constructs.cpp
         fuzzer_pass_adjust_function_controls.cpp
         fuzzer_pass_adjust_loop_controls.cpp
@@ -115,6 +140,7 @@
         fuzzer_pass_apply_id_synonyms.cpp
         fuzzer_pass_construct_composites.cpp
         fuzzer_pass_copy_objects.cpp
+        fuzzer_pass_donate_modules.cpp
         fuzzer_pass_merge_blocks.cpp
         fuzzer_pass_obfuscate_constants.cpp
         fuzzer_pass_outline_functions.cpp
@@ -129,14 +155,17 @@
         replayer.cpp
         shrinker.cpp
         transformation.cpp
+        transformation_access_chain.cpp
         transformation_add_constant_boolean.cpp
         transformation_add_constant_composite.cpp
         transformation_add_constant_scalar.cpp
+        transformation_add_dead_block.cpp
         transformation_add_dead_break.cpp
         transformation_add_dead_continue.cpp
         transformation_add_function.cpp
         transformation_add_global_undef.cpp
         transformation_add_global_variable.cpp
+        transformation_add_local_variable.cpp
         transformation_add_no_contraction_decoration.cpp
         transformation_add_type_array.cpp
         transformation_add_type_boolean.cpp
@@ -150,6 +179,8 @@
         transformation_composite_construct.cpp
         transformation_composite_extract.cpp
         transformation_copy_object.cpp
+        transformation_function_call.cpp
+        transformation_load.cpp
         transformation_merge_blocks.cpp
         transformation_move_block_down.cpp
         transformation_outline_function.cpp
@@ -161,6 +192,7 @@
         transformation_set_memory_operands_mask.cpp
         transformation_set_selection_control.cpp
         transformation_split_block.cpp
+        transformation_store.cpp
         transformation_vector_shuffle.cpp
         uniform_buffer_element_descriptor.cpp
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
diff --git a/third_party/SPIRV-Tools/source/fuzz/call_graph.cpp b/third_party/SPIRV-Tools/source/fuzz/call_graph.cpp
new file mode 100644
index 0000000..15416fe
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/call_graph.cpp
@@ -0,0 +1,81 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/call_graph.h"
+
+#include <queue>
+
+namespace spvtools {
+namespace fuzz {
+
+CallGraph::CallGraph(opt::IRContext* context) {
+  // Initialize function in-degree and call graph edges to 0 and empty.
+  for (auto& function : *context->module()) {
+    function_in_degree_[function.result_id()] = 0;
+    call_graph_edges_[function.result_id()] = std::set<uint32_t>();
+  }
+
+  // Consider every function.
+  for (auto& function : *context->module()) {
+    // Avoid considering the same callee of this function multiple times by
+    // recording known callees.
+    std::set<uint32_t> known_callees;
+    // Consider every function call instruction in every block.
+    for (auto& block : function) {
+      for (auto& instruction : block) {
+        if (instruction.opcode() != SpvOpFunctionCall) {
+          continue;
+        }
+        // Get the id of the function being called.
+        uint32_t callee = instruction.GetSingleWordInOperand(0);
+        if (known_callees.count(callee)) {
+          // We have already considered a call to this function - ignore it.
+          continue;
+        }
+        // Increase the callee's in-degree and add an edge to the call graph.
+        function_in_degree_[callee]++;
+        call_graph_edges_[function.result_id()].insert(callee);
+        // Mark the callee as 'known'.
+        known_callees.insert(callee);
+      }
+    }
+  }
+}
+
+void CallGraph::PushDirectCallees(uint32_t function_id,
+                                  std::queue<uint32_t>* queue) const {
+  for (auto callee : GetDirectCallees(function_id)) {
+    queue->push(callee);
+  }
+}
+
+std::set<uint32_t> CallGraph::GetIndirectCallees(uint32_t function_id) const {
+  std::set<uint32_t> result;
+  std::queue<uint32_t> queue;
+  PushDirectCallees(function_id, &queue);
+
+  while (!queue.empty()) {
+    auto next = queue.front();
+    queue.pop();
+    if (result.count(next)) {
+      continue;
+    }
+    result.insert(next);
+    PushDirectCallees(next, &queue);
+  }
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/call_graph.h b/third_party/SPIRV-Tools/source/fuzz/call_graph.h
new file mode 100644
index 0000000..14cd23b
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/call_graph.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_CALL_GRAPH_H_
+#define SOURCE_FUZZ_CALL_GRAPH_H_
+
+#include <map>
+#include <set>
+
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Represents the acyclic call graph of a SPIR-V module.
+class CallGraph {
+ public:
+  // Creates a call graph corresponding to the given SPIR-V module.
+  explicit CallGraph(opt::IRContext* context);
+
+  // Returns a mapping from each function to its number of distinct callers.
+  const std::map<uint32_t, uint32_t>& GetFunctionInDegree() const {
+    return function_in_degree_;
+  }
+
+  // Returns the ids of the functions that |function_id| directly invokes.
+  const std::set<uint32_t>& GetDirectCallees(uint32_t function_id) const {
+    return call_graph_edges_.at(function_id);
+  }
+
+  // Returns the ids of the functions that |function_id| directly or indirectly
+  // invokes.
+  std::set<uint32_t> GetIndirectCallees(uint32_t function_id) const;
+
+ private:
+  // Pushes the direct callees of |function_id| on to |queue|.
+  void PushDirectCallees(uint32_t function_id,
+                         std::queue<uint32_t>* queue) const;
+
+  // Maps each function id to the ids of its immediate callees.
+  std::map<uint32_t, std::set<uint32_t>> call_graph_edges_;
+
+  // For each function id, stores the number of distinct functions that call
+  // the function.
+  std::map<uint32_t, uint32_t> function_in_degree_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_CALL_GRAPH_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp b/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
index ac3ea30..486e8f5 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
@@ -800,9 +800,102 @@
 // End of data synonym facts
 //==============================
 
+//==============================
+// Dead block facts
+
+// The purpose of this class is to group the fields and data used to represent
+// facts about data blocks.
+class FactManager::DeadBlockFacts {
+ public:
+  // See method in FactManager which delegates to this method.
+  void AddFact(const protobufs::FactBlockIsDead& fact);
+
+  // See method in FactManager which delegates to this method.
+  bool BlockIsDead(uint32_t block_id) const;
+
+ private:
+  std::set<uint32_t> dead_block_ids_;
+};
+
+void FactManager::DeadBlockFacts::AddFact(
+    const protobufs::FactBlockIsDead& fact) {
+  dead_block_ids_.insert(fact.block_id());
+}
+
+bool FactManager::DeadBlockFacts::BlockIsDead(uint32_t block_id) const {
+  return dead_block_ids_.count(block_id) != 0;
+}
+
+// End of dead block facts
+//==============================
+
+//==============================
+// Livesafe function facts
+
+// The purpose of this class is to group the fields and data used to represent
+// facts about livesafe functions.
+class FactManager::LivesafeFunctionFacts {
+ public:
+  // See method in FactManager which delegates to this method.
+  void AddFact(const protobufs::FactFunctionIsLivesafe& fact);
+
+  // See method in FactManager which delegates to this method.
+  bool FunctionIsLivesafe(uint32_t function_id) const;
+
+ private:
+  std::set<uint32_t> livesafe_function_ids_;
+};
+
+void FactManager::LivesafeFunctionFacts::AddFact(
+    const protobufs::FactFunctionIsLivesafe& fact) {
+  livesafe_function_ids_.insert(fact.function_id());
+}
+
+bool FactManager::LivesafeFunctionFacts::FunctionIsLivesafe(
+    uint32_t function_id) const {
+  return livesafe_function_ids_.count(function_id) != 0;
+}
+
+// End of livesafe function facts
+//==============================
+
+//==============================
+// Irrelevant pointee value facts
+
+// The purpose of this class is to group the fields and data used to represent
+// facts about pointers whose pointee values are irrelevant.
+class FactManager::IrrelevantPointeeValueFacts {
+ public:
+  // See method in FactManager which delegates to this method.
+  void AddFact(const protobufs::FactPointeeValueIsIrrelevant& fact);
+
+  // See method in FactManager which delegates to this method.
+  bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
+
+ private:
+  std::set<uint32_t> pointers_to_irrelevant_pointees_ids_;
+};
+
+void FactManager::IrrelevantPointeeValueFacts::AddFact(
+    const protobufs::FactPointeeValueIsIrrelevant& fact) {
+  pointers_to_irrelevant_pointees_ids_.insert(fact.pointer_id());
+}
+
+bool FactManager::IrrelevantPointeeValueFacts::PointeeValueIsIrrelevant(
+    uint32_t pointer_id) const {
+  return pointers_to_irrelevant_pointees_ids_.count(pointer_id) != 0;
+}
+
+// End of arbitrarily-valued variable facts
+//==============================
+
 FactManager::FactManager()
     : uniform_constant_facts_(MakeUnique<ConstantUniformFacts>()),
-      data_synonym_facts_(MakeUnique<DataSynonymFacts>()) {}
+      data_synonym_facts_(MakeUnique<DataSynonymFacts>()),
+      dead_block_facts_(MakeUnique<DeadBlockFacts>()),
+      livesafe_function_facts_(MakeUnique<LivesafeFunctionFacts>()),
+      irrelevant_pointee_value_facts_(
+          MakeUnique<IrrelevantPointeeValueFacts>()) {}
 
 FactManager::~FactManager() = default;
 
@@ -827,6 +920,12 @@
     case protobufs::Fact::kDataSynonymFact:
       data_synonym_facts_->AddFact(fact.data_synonym_fact(), context);
       return true;
+    case protobufs::Fact::kBlockIsDeadFact:
+      dead_block_facts_->AddFact(fact.block_is_dead_fact());
+      return true;
+    case protobufs::Fact::kFunctionIsLivesafeFact:
+      livesafe_function_facts_->AddFact(fact.function_is_livesafe_fact());
+      return true;
     default:
       assert(false && "Unknown fact type.");
       return false;
@@ -898,5 +997,35 @@
                                            context);
 }
 
+bool FactManager::BlockIsDead(uint32_t block_id) const {
+  return dead_block_facts_->BlockIsDead(block_id);
+}
+
+void FactManager::AddFactBlockIsDead(uint32_t block_id) {
+  protobufs::FactBlockIsDead fact;
+  fact.set_block_id(block_id);
+  dead_block_facts_->AddFact(fact);
+}
+
+bool FactManager::FunctionIsLivesafe(uint32_t function_id) const {
+  return livesafe_function_facts_->FunctionIsLivesafe(function_id);
+}
+
+void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) {
+  protobufs::FactFunctionIsLivesafe fact;
+  fact.set_function_id(function_id);
+  livesafe_function_facts_->AddFact(fact);
+}
+
+bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const {
+  return irrelevant_pointee_value_facts_->PointeeValueIsIrrelevant(pointer_id);
+}
+
+void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) {
+  protobufs::FactPointeeValueIsIrrelevant fact;
+  fact.set_pointer_id(pointer_id);
+  irrelevant_pointee_value_facts_->AddFact(fact);
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fact_manager.h b/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
index 62d9dac..55cbfa0 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
@@ -58,6 +58,16 @@
                           const protobufs::DataDescriptor& data2,
                           opt::IRContext* context);
 
+  // Records the fact that |block_id| is dead.
+  void AddFactBlockIsDead(uint32_t block_id);
+
+  // Records the fact that |function_id| is livesafe.
+  void AddFactFunctionIsLivesafe(uint32_t function_id);
+
+  // Records the fact that the value of the pointee associated with |pointer_id|
+  // is irrelevant: it does not affect the observable behaviour of the module.
+  void AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id);
+
   // The fact manager is responsible for managing a few distinct categories of
   // facts. In principle there could be different fact managers for each kind
   // of fact, but in practice providing one 'go to' place for facts is
@@ -130,6 +140,36 @@
   // End of id synonym facts
   //==============================
 
+  //==============================
+  // Querying facts about dead blocks
+
+  // Returns true if and ony if |block_id| is the id of a block known to be
+  // dynamically unreachable.
+  bool BlockIsDead(uint32_t block_id) const;
+
+  // End of dead block facts
+  //==============================
+
+  //==============================
+  // Querying facts about livesafe function
+
+  // Returns true if and ony if |function_id| is the id of a function known
+  // to be livesafe.
+  bool FunctionIsLivesafe(uint32_t function_id) const;
+
+  // End of dead livesafe function facts
+  //==============================
+
+  //==============================
+  // Querying facts about pointers with irrelevant pointee values
+
+  // Returns true if and ony if the value of the pointee associated with
+  // |pointer_id| is irrelevant.
+  bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
+
+  // End of irrelevant pointee value facts
+  //==============================
+
  private:
   // For each distinct kind of fact to be managed, we use a separate opaque
   // class type.
@@ -142,6 +182,20 @@
   class DataSynonymFacts;  // Opaque class for management of data synonym facts.
   std::unique_ptr<DataSynonymFacts>
       data_synonym_facts_;  // Unique pointer to internal data.
+
+  class DeadBlockFacts;  // Opaque class for management of dead block facts.
+  std::unique_ptr<DeadBlockFacts>
+      dead_block_facts_;  // Unique pointer to internal data.
+
+  class LivesafeFunctionFacts;  // Opaque class for management of livesafe
+                                // function facts.
+  std::unique_ptr<LivesafeFunctionFacts>
+      livesafe_function_facts_;  // Unique pointer to internal data.
+
+  class IrrelevantPointeeValueFacts;  // Opaque class for management of
+  // facts about pointers whose pointee values do not matter.
+  std::unique_ptr<IrrelevantPointeeValueFacts>
+      irrelevant_pointee_value_facts_;  // Unique pointer to internal data.
 };
 
 }  // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
index 95913d0..6c2821c 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
@@ -21,9 +21,17 @@
 #include "fuzzer_pass_adjust_memory_operands_masks.h"
 #include "source/fuzz/fact_manager.h"
 #include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_pass_add_access_chains.h"
+#include "source/fuzz/fuzzer_pass_add_composite_types.h"
+#include "source/fuzz/fuzzer_pass_add_dead_blocks.h"
 #include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
 #include "source/fuzz/fuzzer_pass_add_dead_continues.h"
+#include "source/fuzz/fuzzer_pass_add_function_calls.h"
+#include "source/fuzz/fuzzer_pass_add_global_variables.h"
+#include "source/fuzz/fuzzer_pass_add_loads.h"
+#include "source/fuzz/fuzzer_pass_add_local_variables.h"
 #include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
+#include "source/fuzz/fuzzer_pass_add_stores.h"
 #include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
 #include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
 #include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
@@ -31,6 +39,7 @@
 #include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
 #include "source/fuzz/fuzzer_pass_construct_composites.h"
 #include "source/fuzz/fuzzer_pass_copy_objects.h"
+#include "source/fuzz/fuzzer_pass_donate_modules.h"
 #include "source/fuzz/fuzzer_pass_merge_blocks.h"
 #include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
 #include "source/fuzz/fuzzer_pass_outline_functions.h"
@@ -52,15 +61,21 @@
 
 const uint32_t kChanceOfApplyingAnotherPass = 85;
 
-template <typename T>
+// A convenience method to add a fuzzer pass to |passes| with probability 0.5.
+// All fuzzer passes take |ir_context|, |fact_manager|, |fuzzer_context| and
+// |transformation_sequence_out| as parameters.  Extra arguments can be provided
+// via |extra_args|.
+template <typename T, typename... Args>
 void MaybeAddPass(
     std::vector<std::unique_ptr<FuzzerPass>>* passes,
     opt::IRContext* ir_context, FactManager* fact_manager,
     FuzzerContext* fuzzer_context,
-    protobufs::TransformationSequence* transformation_sequence_out) {
+    protobufs::TransformationSequence* transformation_sequence_out,
+    Args&&... extra_args) {
   if (fuzzer_context->ChooseEven()) {
     passes->push_back(MakeUnique<T>(ir_context, fact_manager, fuzzer_context,
-                                    transformation_sequence_out));
+                                    transformation_sequence_out,
+                                    std::forward<Args>(extra_args)...));
   }
 }
 
@@ -114,6 +129,7 @@
 Fuzzer::FuzzerResultStatus Fuzzer::Run(
     const std::vector<uint32_t>& binary_in,
     const protobufs::FactSequence& initial_facts,
+    const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
     std::vector<uint32_t>* binary_out,
     protobufs::TransformationSequence* transformation_sequence_out) const {
   // Check compatibility between the library version being linked with and the
@@ -169,12 +185,36 @@
   // Apply some semantics-preserving passes.
   std::vector<std::unique_ptr<FuzzerPass>> passes;
   while (passes.empty()) {
+    MaybeAddPass<FuzzerPassAddAccessChains>(&passes, ir_context.get(),
+                                            &fact_manager, &fuzzer_context,
+                                            transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddCompositeTypes>(&passes, ir_context.get(),
+                                              &fact_manager, &fuzzer_context,
+                                              transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddDeadBlocks>(&passes, ir_context.get(),
+                                          &fact_manager, &fuzzer_context,
+                                          transformation_sequence_out);
     MaybeAddPass<FuzzerPassAddDeadBreaks>(&passes, ir_context.get(),
                                           &fact_manager, &fuzzer_context,
                                           transformation_sequence_out);
     MaybeAddPass<FuzzerPassAddDeadContinues>(&passes, ir_context.get(),
                                              &fact_manager, &fuzzer_context,
                                              transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddFunctionCalls>(&passes, ir_context.get(),
+                                             &fact_manager, &fuzzer_context,
+                                             transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddGlobalVariables>(&passes, ir_context.get(),
+                                               &fact_manager, &fuzzer_context,
+                                               transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddLoads>(&passes, ir_context.get(), &fact_manager,
+                                     &fuzzer_context,
+                                     transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddLocalVariables>(&passes, ir_context.get(),
+                                              &fact_manager, &fuzzer_context,
+                                              transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(), &fact_manager,
+                                      &fuzzer_context,
+                                      transformation_sequence_out);
     MaybeAddPass<FuzzerPassApplyIdSynonyms>(&passes, ir_context.get(),
                                             &fact_manager, &fuzzer_context,
                                             transformation_sequence_out);
@@ -184,6 +224,9 @@
     MaybeAddPass<FuzzerPassCopyObjects>(&passes, ir_context.get(),
                                         &fact_manager, &fuzzer_context,
                                         transformation_sequence_out);
+    MaybeAddPass<FuzzerPassDonateModules>(
+        &passes, ir_context.get(), &fact_manager, &fuzzer_context,
+        transformation_sequence_out, donor_suppliers);
     MaybeAddPass<FuzzerPassMergeBlocks>(&passes, ir_context.get(),
                                         &fact_manager, &fuzzer_context,
                                         transformation_sequence_out);
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer.h
index c1d2dee..3ac73a1 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer.h
@@ -18,6 +18,7 @@
 #include <memory>
 #include <vector>
 
+#include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "spirv-tools/libspirv.hpp"
 
@@ -57,11 +58,14 @@
 
   // Transforms |binary_in| to |binary_out| 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|.  The transformation
-  // sequence that was applied is returned via |transformation_sequence_out|.
+  // which it will execute are provided via |initial_facts|.  A source of donor
+  // modules to be used by transformations is provided via |donor_suppliers|.
+  // The transformation sequence that was applied is returned via
+  // |transformation_sequence_out|.
   FuzzerResultStatus Run(
       const std::vector<uint32_t>& binary_in,
       const protobufs::FactSequence& initial_facts,
+      const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
       std::vector<uint32_t>* binary_out,
       protobufs::TransformationSequence* transformation_sequence_out) const;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
index 98585d9..1265772 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
@@ -23,10 +23,21 @@
 // Default <minimum, maximum> pairs of probabilities for applying various
 // transformations. All values are percentages. Keep them in alphabetical order.
 
+const std::pair<uint32_t, uint32_t> kChanceOfAddingAccessChain = {5, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherStructField = {20,
+                                                                         90};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingArrayOrStructType = {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBlock = {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBreak = {5, 80};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadContinue = {5, 80};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingGlobalVariable = {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingLoad = {5, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingLocalVariable = {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingMatrixType = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingNoContractionDecoration = {
     5, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingStore = {5, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorType = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingFunctionControl = {20,
                                                                          70};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingLoopControl = {20, 90};
@@ -34,8 +45,15 @@
                                                                             90};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingSelectionControl = {20,
                                                                           90};
+const std::pair<uint32_t, uint32_t> kChanceOfCallingFunction = {1, 10};
+const std::pair<uint32_t, uint32_t> kChanceOfChoosingStructTypeVsArrayType = {
+    20, 80};
 const std::pair<uint32_t, uint32_t> kChanceOfConstructingComposite = {20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfDonatingAdditionalModule = {5, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperWhenMakingAccessChain =
+    {50, 95};
+const std::pair<uint32_t, uint32_t> kChanceOfMakingDonorLivesafe = {40, 60};
 const std::pair<uint32_t, uint32_t> kChanceOfMergingBlocks = {20, 95};
 const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
@@ -47,6 +65,8 @@
 // Keep them in alphabetical order.
 const uint32_t kDefaultMaxLoopControlPartialCount = 100;
 const uint32_t kDefaultMaxLoopControlPeelCount = 100;
+const uint32_t kDefaultMaxLoopLimit = 20;
+const uint32_t kDefaultMaxNewArraySizeLimit = 100;
 
 // Default functions for controlling how deep to go during recursive
 // generation/transformation. Keep them in alphabetical order.
@@ -64,14 +84,36 @@
                              uint32_t min_fresh_id)
     : random_generator_(random_generator),
       next_fresh_id_(min_fresh_id),
+      max_loop_control_partial_count_(kDefaultMaxLoopControlPartialCount),
+      max_loop_control_peel_count_(kDefaultMaxLoopControlPeelCount),
+      max_loop_limit_(kDefaultMaxLoopLimit),
+      max_new_array_size_limit_(kDefaultMaxNewArraySizeLimit),
       go_deeper_in_constant_obfuscation_(
           kDefaultGoDeeperInConstantObfuscation) {
+  chance_of_adding_access_chain_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingAccessChain);
+  chance_of_adding_another_struct_field_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField);
+  chance_of_adding_array_or_struct_type_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType);
+  chance_of_adding_dead_block_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingDeadBlock);
   chance_of_adding_dead_break_ =
       ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak);
   chance_of_adding_dead_continue_ =
       ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue);
+  chance_of_adding_global_variable_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable);
+  chance_of_adding_load_ = ChooseBetweenMinAndMax(kChanceOfAddingLoad);
+  chance_of_adding_local_variable_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingLocalVariable);
+  chance_of_adding_matrix_type_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingMatrixType);
   chance_of_adding_no_contraction_decoration_ =
       ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration);
+  chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore);
+  chance_of_adding_vector_type_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingVectorType);
   chance_of_adjusting_function_control_ =
       ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl);
   chance_of_adjusting_loop_control_ =
@@ -80,9 +122,19 @@
       ChooseBetweenMinAndMax(kChanceOfAdjustingMemoryOperandsMask);
   chance_of_adjusting_selection_control_ =
       ChooseBetweenMinAndMax(kChanceOfAdjustingSelectionControl);
+  chance_of_calling_function_ =
+      ChooseBetweenMinAndMax(kChanceOfCallingFunction);
+  chance_of_choosing_struct_type_vs_array_type_ =
+      ChooseBetweenMinAndMax(kChanceOfChoosingStructTypeVsArrayType);
   chance_of_constructing_composite_ =
       ChooseBetweenMinAndMax(kChanceOfConstructingComposite);
   chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
+  chance_of_donating_additional_module_ =
+      ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
+  chance_of_going_deeper_when_making_access_chain_ =
+      ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain);
+  chance_of_making_donor_livesafe_ =
+      ChooseBetweenMinAndMax(kChanceOfMakingDonorLivesafe);
   chance_of_merging_blocks_ = ChooseBetweenMinAndMax(kChanceOfMergingBlocks);
   chance_of_moving_block_down_ =
       ChooseBetweenMinAndMax(kChanceOfMovingBlockDown);
@@ -93,8 +145,6 @@
   chance_of_replacing_id_with_synonym_ =
       ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
   chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
-  max_loop_control_partial_count_ = kDefaultMaxLoopControlPartialCount;
-  max_loop_control_peel_count_ = kDefaultMaxLoopControlPeelCount;
 }
 
 FuzzerContext::~FuzzerContext() = default;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
index 619c131..23127ff 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
@@ -46,25 +46,59 @@
   // method, and which must be non-empty.  Typically 'HasSizeMethod' will be an
   // std::vector.
   template <typename HasSizeMethod>
-  uint32_t RandomIndex(const HasSizeMethod& sequence) {
+  uint32_t RandomIndex(const HasSizeMethod& sequence) const {
     assert(sequence.size() > 0);
     return random_generator_->RandomUint32(
         static_cast<uint32_t>(sequence.size()));
   }
 
+  // Selects a random index into |sequence|, removes the element at that index
+  // and returns it.
+  template <typename T>
+  T RemoveAtRandomIndex(std::vector<T>* sequence) const {
+    uint32_t index = RandomIndex(*sequence);
+    T result = sequence->at(index);
+    sequence->erase(sequence->begin() + index);
+    return result;
+  }
+
   // Yields an id that is guaranteed not to be used in the module being fuzzed,
   // or to have been issued before.
   uint32_t GetFreshId();
 
   // Probabilities associated with applying various transformations.
   // Keep them in alphabetical order.
+  uint32_t GetChanceOfAddingAccessChain() {
+    return chance_of_adding_access_chain_;
+  }
+  uint32_t GetChanceOfAddingAnotherStructField() {
+    return chance_of_adding_another_struct_field_;
+  }
+  uint32_t GetChanceOfAddingArrayOrStructType() {
+    return chance_of_adding_array_or_struct_type_;
+  }
+  uint32_t GetChanceOfAddingDeadBlock() { return chance_of_adding_dead_block_; }
   uint32_t GetChanceOfAddingDeadBreak() { return chance_of_adding_dead_break_; }
   uint32_t GetChanceOfAddingDeadContinue() {
     return chance_of_adding_dead_continue_;
   }
+  uint32_t GetChanceOfAddingGlobalVariable() {
+    return chance_of_adding_global_variable_;
+  }
+  uint32_t GetChanceOfAddingLoad() { return chance_of_adding_load_; }
+  uint32_t GetChanceOfAddingLocalVariable() {
+    return chance_of_adding_local_variable_;
+  }
+  uint32_t GetChanceOfAddingMatrixType() {
+    return chance_of_adding_matrix_type_;
+  }
   uint32_t GetChanceOfAddingNoContractionDecoration() {
     return chance_of_adding_no_contraction_decoration_;
   }
+  uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
+  uint32_t GetChanceOfAddingVectorType() {
+    return chance_of_adding_vector_type_;
+  }
   uint32_t GetChanceOfAdjustingFunctionControl() {
     return chance_of_adjusting_function_control_;
   }
@@ -77,10 +111,23 @@
   uint32_t GetChanceOfAdjustingSelectionControl() {
     return chance_of_adjusting_selection_control_;
   }
+  uint32_t GetChanceOfCallingFunction() { return chance_of_calling_function_; }
+  uint32_t GetChanceOfChoosingStructTypeVsArrayType() {
+    return chance_of_choosing_struct_type_vs_array_type_;
+  }
   uint32_t GetChanceOfConstructingComposite() {
     return chance_of_constructing_composite_;
   }
   uint32_t GetChanceOfCopyingObject() { return chance_of_copying_object_; }
+  uint32_t GetChanceOfDonatingAdditionalModule() {
+    return chance_of_donating_additional_module_;
+  }
+  uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() {
+    return chance_of_going_deeper_when_making_access_chain_;
+  }
+  uint32_t ChanceOfMakingDonorLivesafe() {
+    return chance_of_making_donor_livesafe_;
+  }
   uint32_t GetChanceOfMergingBlocks() { return chance_of_merging_blocks_; }
   uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
   uint32_t GetChanceOfObfuscatingConstant() {
@@ -99,9 +146,19 @@
   uint32_t GetRandomLoopControlPartialCount() {
     return random_generator_->RandomUint32(max_loop_control_partial_count_);
   }
+  uint32_t GetRandomLoopLimit() {
+    return random_generator_->RandomUint32(max_loop_limit_);
+  }
+  uint32_t GetRandomSizeForNewArray() {
+    // Ensure that the array size is non-zero.
+    return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
+  }
 
-  // Functions to control how deeply to recurse.
-  // Keep them in alphabetical order.
+  // Other functions to control transformations. Keep them in alphabetical
+  // order.
+  uint32_t GetRandomIndexForAccessChain(uint32_t composite_size_bound) {
+    return random_generator_->RandomUint32(composite_size_bound);
+  }
   bool GoDeeperInConstantObfuscation(uint32_t depth) {
     return go_deeper_in_constant_obfuscation_(depth, random_generator_);
   }
@@ -114,15 +171,30 @@
 
   // Probabilities associated with applying various transformations.
   // Keep them in alphabetical order.
+  uint32_t chance_of_adding_access_chain_;
+  uint32_t chance_of_adding_another_struct_field_;
+  uint32_t chance_of_adding_array_or_struct_type_;
+  uint32_t chance_of_adding_dead_block_;
   uint32_t chance_of_adding_dead_break_;
   uint32_t chance_of_adding_dead_continue_;
+  uint32_t chance_of_adding_global_variable_;
+  uint32_t chance_of_adding_load_;
+  uint32_t chance_of_adding_local_variable_;
+  uint32_t chance_of_adding_matrix_type_;
   uint32_t chance_of_adding_no_contraction_decoration_;
+  uint32_t chance_of_adding_store_;
+  uint32_t chance_of_adding_vector_type_;
   uint32_t chance_of_adjusting_function_control_;
   uint32_t chance_of_adjusting_loop_control_;
   uint32_t chance_of_adjusting_memory_operands_mask_;
   uint32_t chance_of_adjusting_selection_control_;
+  uint32_t chance_of_calling_function_;
+  uint32_t chance_of_choosing_struct_type_vs_array_type_;
   uint32_t chance_of_constructing_composite_;
   uint32_t chance_of_copying_object_;
+  uint32_t chance_of_donating_additional_module_;
+  uint32_t chance_of_going_deeper_when_making_access_chain_;
+  uint32_t chance_of_making_donor_livesafe_;
   uint32_t chance_of_merging_blocks_;
   uint32_t chance_of_moving_block_down_;
   uint32_t chance_of_obfuscating_constant_;
@@ -135,6 +207,8 @@
   // Keep them in alphabetical order.
   uint32_t max_loop_control_partial_count_;
   uint32_t max_loop_control_peel_count_;
+  uint32_t max_loop_limit_;
+  uint32_t max_new_array_size_limit_;
 
   // Functions to determine with what probability to go deeper when generating
   // or mutating constructs recursively.
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
index 1da53f4..4a22a21 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
@@ -14,7 +14,18 @@
 
 #include "source/fuzz/fuzzer_pass.h"
 
+#include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_add_constant_boolean.h"
+#include "source/fuzz/transformation_add_constant_composite.h"
+#include "source/fuzz/transformation_add_constant_scalar.h"
+#include "source/fuzz/transformation_add_global_undef.h"
+#include "source/fuzz/transformation_add_type_boolean.h"
+#include "source/fuzz/transformation_add_type_float.h"
+#include "source/fuzz/transformation_add_type_int.h"
+#include "source/fuzz/transformation_add_type_matrix.h"
+#include "source/fuzz/transformation_add_type_pointer.h"
+#include "source/fuzz/transformation_add_type_vector.h"
 
 namespace spvtools {
 namespace fuzz {
@@ -30,10 +41,10 @@
 FuzzerPass::~FuzzerPass() = default;
 
 std::vector<opt::Instruction*> FuzzerPass::FindAvailableInstructions(
-    const opt::Function& function, opt::BasicBlock* block,
-    opt::BasicBlock::iterator inst_it,
+    opt::Function* function, opt::BasicBlock* block,
+    const opt::BasicBlock::iterator& inst_it,
     std::function<bool(opt::IRContext*, opt::Instruction*)>
-        instruction_is_relevant) {
+        instruction_is_relevant) const {
   // TODO(afd) The following is (relatively) simple, but may end up being
   //  prohibitively inefficient, as it walks the whole dominator tree for
   //  every instruction that is considered.
@@ -46,6 +57,14 @@
     }
   }
 
+  // Consider all function parameters
+  function->ForEachParam(
+      [this, &instruction_is_relevant, &result](opt::Instruction* param) {
+        if (instruction_is_relevant(GetIRContext(), param)) {
+          result.push_back(param);
+        }
+      });
+
   // Consider all previous instructions in this block
   for (auto prev_inst_it = block->begin(); prev_inst_it != inst_it;
        ++prev_inst_it) {
@@ -56,7 +75,7 @@
 
   // Walk the dominator tree to consider all instructions from dominating
   // blocks
-  auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(&function);
+  auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function);
   for (auto next_dominator = dominator_analysis->ImmediateDominator(block);
        next_dominator != nullptr;
        next_dominator =
@@ -72,7 +91,7 @@
 
 void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
     std::function<
-        void(const opt::Function& function, opt::BasicBlock* block,
+        void(opt::Function* function, opt::BasicBlock* block,
              opt::BasicBlock::iterator inst_it,
              const protobufs::InstructionDescriptor& instruction_descriptor)>
         maybe_apply_transformation) {
@@ -114,7 +133,7 @@
 
         // Invoke the provided function, which might apply a transformation.
         maybe_apply_transformation(
-            function, &block, inst_it,
+            &function, &block, inst_it,
             MakeInstructionDescriptor(
                 base, opcode,
                 skip_count.count(opcode) ? skip_count.at(opcode) : 0));
@@ -128,5 +147,307 @@
   }
 }
 
+uint32_t FuzzerPass::FindOrCreateBoolType() {
+  opt::analysis::Bool bool_type;
+  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&bool_type);
+  if (existing_id) {
+    return existing_id;
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddTypeBoolean(result));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreate32BitIntegerType(bool is_signed) {
+  opt::analysis::Integer int_type(32, is_signed);
+  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&int_type);
+  if (existing_id) {
+    return existing_id;
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddTypeInt(result, 32, is_signed));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreate32BitFloatType() {
+  opt::analysis::Float float_type(32);
+  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&float_type);
+  if (existing_id) {
+    return existing_id;
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddTypeFloat(result, 32));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id,
+                                            uint32_t component_count) {
+  assert(component_count >= 2 && component_count <= 4 &&
+         "Precondition: component count must be in range [2, 4].");
+  opt::analysis::Type* component_type =
+      GetIRContext()->get_type_mgr()->GetType(component_type_id);
+  assert(component_type && "Precondition: the component type must exist.");
+  opt::analysis::Vector vector_type(component_type, component_count);
+  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&vector_type);
+  if (existing_id) {
+    return existing_id;
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(
+      TransformationAddTypeVector(result, component_type_id, component_count));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreateMatrixType(uint32_t column_count,
+                                            uint32_t row_count) {
+  assert(column_count >= 2 && column_count <= 4 &&
+         "Precondition: column count must be in range [2, 4].");
+  assert(row_count >= 2 && row_count <= 4 &&
+         "Precondition: row count must be in range [2, 4].");
+  uint32_t column_type_id =
+      FindOrCreateVectorType(FindOrCreate32BitFloatType(), row_count);
+  opt::analysis::Type* column_type =
+      GetIRContext()->get_type_mgr()->GetType(column_type_id);
+  opt::analysis::Matrix matrix_type(column_type, column_count);
+  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&matrix_type);
+  if (existing_id) {
+    return existing_id;
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(
+      TransformationAddTypeMatrix(result, column_type_id, column_count));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreatePointerType(uint32_t base_type_id,
+                                             SpvStorageClass storage_class) {
+  // We do not use the type manager here, due to problems related to isomorphic
+  // but distinct structs not being regarded as different.
+  auto existing_id = fuzzerutil::MaybeGetPointerType(
+      GetIRContext(), base_type_id, storage_class);
+  if (existing_id) {
+    return existing_id;
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(
+      TransformationAddTypePointer(result, storage_class, base_type_id));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreatePointerTo32BitIntegerType(
+    bool is_signed, SpvStorageClass storage_class) {
+  return FindOrCreatePointerType(FindOrCreate32BitIntegerType(is_signed),
+                                 storage_class);
+}
+
+uint32_t FuzzerPass::FindOrCreate32BitIntegerConstant(uint32_t word,
+                                                      bool is_signed) {
+  auto uint32_type_id = FindOrCreate32BitIntegerType(is_signed);
+  opt::analysis::IntConstant int_constant(
+      GetIRContext()->get_type_mgr()->GetType(uint32_type_id)->AsInteger(),
+      {word});
+  auto existing_constant =
+      GetIRContext()->get_constant_mgr()->FindConstant(&int_constant);
+  if (existing_constant) {
+    return GetIRContext()
+        ->get_constant_mgr()
+        ->GetDefiningInstruction(existing_constant)
+        ->result_id();
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(
+      TransformationAddConstantScalar(result, uint32_type_id, {word}));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreate32BitFloatConstant(uint32_t word) {
+  auto float_type_id = FindOrCreate32BitFloatType();
+  opt::analysis::FloatConstant float_constant(
+      GetIRContext()->get_type_mgr()->GetType(float_type_id)->AsFloat(),
+      {word});
+  auto existing_constant =
+      GetIRContext()->get_constant_mgr()->FindConstant(&float_constant);
+  if (existing_constant) {
+    return GetIRContext()
+        ->get_constant_mgr()
+        ->GetDefiningInstruction(existing_constant)
+        ->result_id();
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(
+      TransformationAddConstantScalar(result, float_type_id, {word}));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value) {
+  auto bool_type_id = FindOrCreateBoolType();
+  opt::analysis::BoolConstant bool_constant(
+      GetIRContext()->get_type_mgr()->GetType(bool_type_id)->AsBool(), value);
+  auto existing_constant =
+      GetIRContext()->get_constant_mgr()->FindConstant(&bool_constant);
+  if (existing_constant) {
+    return GetIRContext()
+        ->get_constant_mgr()
+        ->GetDefiningInstruction(existing_constant)
+        ->result_id();
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddConstantBoolean(result, value));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
+  for (auto& inst : GetIRContext()->types_values()) {
+    if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) {
+      return inst.result_id();
+    }
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddGlobalUndef(result, type_id));
+  return result;
+}
+
+std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
+FuzzerPass::GetAvailableBaseTypesAndPointers(
+    SpvStorageClass storage_class) const {
+  // Records all of the base types available in the module.
+  std::vector<uint32_t> base_types;
+
+  // For each base type, records all the associated pointer types that target
+  // that base type and that have |storage_class| as their storage class.
+  std::map<uint32_t, std::vector<uint32_t>> base_type_to_pointers;
+
+  for (auto& inst : GetIRContext()->types_values()) {
+    switch (inst.opcode()) {
+      case SpvOpTypeArray:
+      case SpvOpTypeBool:
+      case SpvOpTypeFloat:
+      case SpvOpTypeInt:
+      case SpvOpTypeMatrix:
+      case SpvOpTypeStruct:
+      case SpvOpTypeVector:
+        // These types are suitable as pointer base types.  Record the type,
+        // and the fact that we cannot yet have seen any pointers that use this
+        // as its base type.
+        base_types.push_back(inst.result_id());
+        base_type_to_pointers.insert({inst.result_id(), {}});
+        break;
+      case SpvOpTypePointer:
+        if (inst.GetSingleWordInOperand(0) == storage_class) {
+          // The pointer has the desired storage class, so we are interested in
+          // it.  Associate it with its base type.
+          base_type_to_pointers.at(inst.GetSingleWordInOperand(1))
+              .push_back(inst.result_id());
+        }
+        break;
+      default:
+        break;
+    }
+  }
+  return {base_types, base_type_to_pointers};
+}
+
+uint32_t FuzzerPass::FindOrCreateZeroConstant(
+    uint32_t scalar_or_composite_type_id) {
+  auto type_instruction =
+      GetIRContext()->get_def_use_mgr()->GetDef(scalar_or_composite_type_id);
+  assert(type_instruction && "The type instruction must exist.");
+  switch (type_instruction->opcode()) {
+    case SpvOpTypeBool:
+      return FindOrCreateBoolConstant(false);
+    case SpvOpTypeFloat:
+      return FindOrCreate32BitFloatConstant(0);
+    case SpvOpTypeInt:
+      return FindOrCreate32BitIntegerConstant(
+          0, type_instruction->GetSingleWordInOperand(1) != 0);
+    case SpvOpTypeArray: {
+      return GetZeroConstantForHomogeneousComposite(
+          *type_instruction, type_instruction->GetSingleWordInOperand(0),
+          fuzzerutil::GetArraySize(*type_instruction, GetIRContext()));
+    }
+    case SpvOpTypeMatrix:
+    case SpvOpTypeVector: {
+      return GetZeroConstantForHomogeneousComposite(
+          *type_instruction, type_instruction->GetSingleWordInOperand(0),
+          type_instruction->GetSingleWordInOperand(1));
+    }
+    case SpvOpTypeStruct: {
+      std::vector<const opt::analysis::Constant*> field_zero_constants;
+      std::vector<uint32_t> field_zero_ids;
+      for (uint32_t index = 0; index < type_instruction->NumInOperands();
+           index++) {
+        uint32_t field_constant_id = FindOrCreateZeroConstant(
+            type_instruction->GetSingleWordInOperand(index));
+        field_zero_ids.push_back(field_constant_id);
+        field_zero_constants.push_back(
+            GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
+                field_constant_id));
+      }
+      return FindOrCreateCompositeConstant(
+          *type_instruction, field_zero_constants, field_zero_ids);
+    }
+    default:
+      assert(false && "Unknown type.");
+      return 0;
+  }
+}
+
+uint32_t FuzzerPass::FindOrCreateCompositeConstant(
+    const opt::Instruction& composite_type_instruction,
+    const std::vector<const opt::analysis::Constant*>& constants,
+    const std::vector<uint32_t>& constant_ids) {
+  assert(constants.size() == constant_ids.size() &&
+         "Precondition: |constants| and |constant_ids| must be in "
+         "correspondence.");
+
+  opt::analysis::Type* composite_type = GetIRContext()->get_type_mgr()->GetType(
+      composite_type_instruction.result_id());
+  std::unique_ptr<opt::analysis::Constant> composite_constant;
+  if (composite_type->AsArray()) {
+    composite_constant = MakeUnique<opt::analysis::ArrayConstant>(
+        composite_type->AsArray(), constants);
+  } else if (composite_type->AsMatrix()) {
+    composite_constant = MakeUnique<opt::analysis::MatrixConstant>(
+        composite_type->AsMatrix(), constants);
+  } else if (composite_type->AsStruct()) {
+    composite_constant = MakeUnique<opt::analysis::StructConstant>(
+        composite_type->AsStruct(), constants);
+  } else if (composite_type->AsVector()) {
+    composite_constant = MakeUnique<opt::analysis::VectorConstant>(
+        composite_type->AsVector(), constants);
+  } else {
+    assert(false &&
+           "Precondition: |composite_type| must declare a composite type.");
+    return 0;
+  }
+
+  uint32_t existing_constant =
+      GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
+          composite_constant.get(), composite_type_instruction.result_id());
+  if (existing_constant) {
+    return existing_constant;
+  }
+  uint32_t result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddConstantComposite(
+      result, composite_type_instruction.result_id(), constant_ids));
+  return result;
+}
+
+uint32_t FuzzerPass::GetZeroConstantForHomogeneousComposite(
+    const opt::Instruction& composite_type_instruction,
+    uint32_t component_type_id, uint32_t num_components) {
+  std::vector<const opt::analysis::Constant*> zero_constants;
+  std::vector<uint32_t> zero_ids;
+  uint32_t zero_component = FindOrCreateZeroConstant(component_type_id);
+  const opt::analysis::Constant* registered_zero_component =
+      GetIRContext()->get_constant_mgr()->FindDeclaredConstant(zero_component);
+  for (uint32_t i = 0; i < num_components; i++) {
+    zero_constants.push_back(registered_zero_component);
+    zero_ids.push_back(zero_component);
+  }
+  return FindOrCreateCompositeConstant(composite_type_instruction,
+                                       zero_constants, zero_ids);
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
index cf56e24..7052685 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
@@ -61,10 +61,10 @@
   // |instruction_is_relevant| predicate.  This, for instance, could ignore all
   // instructions that have a particular decoration.
   std::vector<opt::Instruction*> FindAvailableInstructions(
-      const opt::Function& function, opt::BasicBlock* block,
-      opt::BasicBlock::iterator inst_it,
+      opt::Function* function, opt::BasicBlock* block,
+      const opt::BasicBlock::iterator& inst_it,
       std::function<bool(opt::IRContext*, opt::Instruction*)>
-          instruction_is_relevant);
+          instruction_is_relevant) const;
 
   // A helper method that iterates through each instruction in each block, at
   // all times tracking an instruction descriptor that allows the latest
@@ -84,12 +84,145 @@
   // apply it.
   void MaybeAddTransformationBeforeEachInstruction(
       std::function<
-          void(const opt::Function& function, opt::BasicBlock* block,
+          void(opt::Function* function, opt::BasicBlock* block,
                opt::BasicBlock::iterator inst_it,
                const protobufs::InstructionDescriptor& instruction_descriptor)>
           maybe_apply_transformation);
 
+  // A generic helper for applying a transformation that should be applicable
+  // by construction, and adding it to the sequence of applied transformations.
+  template <typename TransformationType>
+  void ApplyTransformation(const TransformationType& transformation) {
+    assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+           "Transformation should be applicable by construction.");
+    transformation.Apply(GetIRContext(), GetFactManager());
+    *GetTransformations()->add_transformation() = transformation.ToMessage();
+  }
+
+  // Returns the id of an OpTypeBool instruction.  If such an instruction does
+  // not exist, a transformation is applied to add it.
+  uint32_t FindOrCreateBoolType();
+
+  // Returns the id of an OpTypeInt instruction, with width 32 and signedness
+  // specified by |is_signed|.  If such an instruction does not exist, a
+  // transformation is applied to add it.
+  uint32_t FindOrCreate32BitIntegerType(bool is_signed);
+
+  // Returns the id of an OpTypeFloat instruction, with width 32.  If such an
+  // instruction does not exist, a transformation is applied to add it.
+  uint32_t FindOrCreate32BitFloatType();
+
+  // Returns the id of an OpTypeVector instruction, with |component_type_id|
+  // (which must already exist) as its base type, and |component_count|
+  // elements (which must be in the range [2, 4]).  If such an instruction does
+  // not exist, a transformation is applied to add it.
+  uint32_t FindOrCreateVectorType(uint32_t component_type_id,
+                                  uint32_t component_count);
+
+  // Returns the id of an OpTypeMatrix instruction, with |column_count| columns
+  // and |row_count| rows (each of which must be in the range [2, 4]).  If the
+  // float and vector types required to build this matrix type or the matrix
+  // type itself do not exist, transformations are applied to add them.
+  uint32_t FindOrCreateMatrixType(uint32_t column_count, uint32_t row_count);
+
+  // Returns the id of a pointer type with base type |base_type_id| (which must
+  // already exist) and storage class |storage_class|.  A transformation is
+  // applied to add the pointer if it does not already exist.
+  uint32_t FindOrCreatePointerType(uint32_t base_type_id,
+                                   SpvStorageClass storage_class);
+
+  // Returns the id of an OpTypePointer instruction, with a 32-bit integer base
+  // type of signedness specified by |is_signed|.  If the pointer type or
+  // required integer base type do not exist, transformations are applied to add
+  // them.
+  uint32_t FindOrCreatePointerTo32BitIntegerType(bool is_signed,
+                                                 SpvStorageClass storage_class);
+
+  // Returns the id of an OpConstant instruction, with 32-bit integer type of
+  // signedness specified by |is_signed|, with |word| as its value.  If either
+  // the required integer type or the constant do not exist, transformations are
+  // applied to add them.
+  uint32_t FindOrCreate32BitIntegerConstant(uint32_t word, bool is_signed);
+
+  // Returns the id of an OpConstant instruction, with 32-bit floating-point
+  // type, with |word| as its value.  If either the required floating-point type
+  // or the constant do not exist, transformations are applied to add them.
+  uint32_t FindOrCreate32BitFloatConstant(uint32_t word);
+
+  // Returns the id of an OpConstantTrue or OpConstantFalse instruction,
+  // according to |value|.  If either the required instruction or the bool
+  // type do not exist, transformations are applied to add them.
+  uint32_t FindOrCreateBoolConstant(bool value);
+
+  // Returns the result id of an instruction of the form:
+  //   %id = OpUndef %|type_id|
+  // If no such instruction exists, a transformation is applied to add it.
+  uint32_t FindOrCreateGlobalUndef(uint32_t type_id);
+
+  // Yields a pair, (base_type_ids, base_type_ids_to_pointers), such that:
+  // - base_type_ids captures every scalar or composite type declared in the
+  //   module (i.e., all int, bool, float, vector, matrix, struct and array
+  //   types
+  // - base_type_ids_to_pointers maps every such base type to the sequence
+  //   of all pointer types that have storage class |storage_class| and the
+  //   given base type as their pointee type.  The sequence may be empty for
+  //   some base types if no pointers to those types are defined for the given
+  //   storage class, and the sequence will have multiple elements if there are
+  //   repeated pointer declarations for the same base type and storage class.
+  std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
+  GetAvailableBaseTypesAndPointers(SpvStorageClass storage_class) const;
+
+  // Given a type id, |scalar_or_composite_type_id|, which must correspond to
+  // some scalar or composite type, returns the result id of an instruction
+  // defining a constant of the given type that is zero or false at everywhere.
+  // If such an instruction does not yet exist, transformations are applied to
+  // add it.
+  //
+  // Examples:
+  // --------------+-------------------------------
+  //   TYPE        | RESULT is id corresponding to
+  // --------------+-------------------------------
+  //   bool        | false
+  // --------------+-------------------------------
+  //   bvec4       | (false, false, false, false)
+  // --------------+-------------------------------
+  //   float       | 0.0
+  // --------------+-------------------------------
+  //   vec2        | (0.0, 0.0)
+  // --------------+-------------------------------
+  //   int[3]      | [0, 0, 0]
+  // --------------+-------------------------------
+  //   struct S {  |
+  //     int i;    | S(0, false, (0u, 0u))
+  //     bool b;   |
+  //     uint2 u;  |
+  //   }           |
+  // --------------+-------------------------------
+  uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id);
+
  private:
+  // Array, matrix and vector are *homogeneous* composite types in the sense
+  // that every component of one of these types has the same type.  Given a
+  // homogeneous composite type instruction, |composite_type_instruction|,
+  // returns the id of a composite constant instruction for which every element
+  // is zero/false.  If such an instruction does not yet exist, transformations
+  // are applied to add it.
+  uint32_t GetZeroConstantForHomogeneousComposite(
+      const opt::Instruction& composite_type_instruction,
+      uint32_t component_type_id, uint32_t num_components);
+
+  // Helper to find an existing composite constant instruction of the given
+  // composite type with the given constant components, or to apply
+  // transformations to create such an instruction if it does not yet exist.
+  // Parameter |composite_type_instruction| must be a composite type
+  // instruction.  The parameters |constants| and |constant_ids| must have the
+  // same size, and it must be the case that for each i, |constant_ids[i]| is
+  // the result id of an instruction that defines |constants[i]|.
+  uint32_t FindOrCreateCompositeConstant(
+      const opt::Instruction& composite_type_instruction,
+      const std::vector<const opt::analysis::Constant*>& constants,
+      const std::vector<uint32_t>& constant_ids);
+
   opt::IRContext* ir_context_;
   FactManager* fact_manager_;
   FuzzerContext* fuzzer_context_;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.cpp
new file mode 100644
index 0000000..11f368e
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.cpp
@@ -0,0 +1,169 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_access_chains.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_access_chain.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddAccessChains::FuzzerPassAddAccessChains(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddAccessChains::~FuzzerPassAddAccessChains() = default;
+
+void FuzzerPassAddAccessChains::Apply() {
+  MaybeAddTransformationBeforeEachInstruction(
+      [this](opt::Function* function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor)
+          -> void {
+        assert(inst_it->opcode() ==
+                   instruction_descriptor.target_instruction_opcode() &&
+               "The opcode of the instruction we might insert before must be "
+               "the same as the opcode in the descriptor for the instruction");
+
+        // Check whether it is legitimate to insert an access chain
+        // instruction before this instruction.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAccessChain,
+                                                          inst_it)) {
+          return;
+        }
+
+        // Randomly decide whether to try inserting a load here.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAddingAccessChain())) {
+          return;
+        }
+
+        // Get all of the pointers that are currently in scope, excluding
+        // explicitly null and undefined pointers.
+        std::vector<opt::Instruction*> relevant_pointer_instructions =
+            FindAvailableInstructions(
+                function, block, inst_it,
+                [](opt::IRContext* context,
+                   opt::Instruction* instruction) -> bool {
+                  if (!instruction->result_id() || !instruction->type_id()) {
+                    // A pointer needs both a result and type id.
+                    return false;
+                  }
+                  switch (instruction->opcode()) {
+                    case SpvOpConstantNull:
+                    case SpvOpUndef:
+                      // Do not allow making an access chain from a null or
+                      // undefined pointer.  (We can eliminate these cases
+                      // before actually checking that the instruction is a
+                      // pointer.)
+                      return false;
+                    default:
+                      break;
+                  }
+                  // If the instruction has pointer type, we can legitimately
+                  // make an access chain from it.
+                  return context->get_def_use_mgr()
+                             ->GetDef(instruction->type_id())
+                             ->opcode() == SpvOpTypePointer;
+                });
+
+        // At this point, |relevant_instructions| contains all the pointers
+        // we might think of making an access chain from.
+        if (relevant_pointer_instructions.empty()) {
+          return;
+        }
+
+        auto chosen_pointer =
+            relevant_pointer_instructions[GetFuzzerContext()->RandomIndex(
+                relevant_pointer_instructions)];
+        std::vector<uint32_t> index_ids;
+        auto pointer_type = GetIRContext()->get_def_use_mgr()->GetDef(
+            chosen_pointer->type_id());
+        uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
+        while (true) {
+          auto subobject_type =
+              GetIRContext()->get_def_use_mgr()->GetDef(subobject_type_id);
+          if (!spvOpcodeIsComposite(subobject_type->opcode())) {
+            break;
+          }
+          if (!GetFuzzerContext()->ChoosePercentage(
+                  GetFuzzerContext()
+                      ->GetChanceOfGoingDeeperWhenMakingAccessChain())) {
+            break;
+          }
+          uint32_t bound;
+          switch (subobject_type->opcode()) {
+            case SpvOpTypeArray:
+              bound = fuzzerutil::GetArraySize(*subobject_type, GetIRContext());
+              break;
+            case SpvOpTypeMatrix:
+            case SpvOpTypeVector:
+              bound = subobject_type->GetSingleWordInOperand(1);
+              break;
+            case SpvOpTypeStruct:
+              bound = fuzzerutil::GetNumberOfStructMembers(*subobject_type);
+              break;
+            default:
+              assert(false && "Not a composite type opcode.");
+              // Set the bound to a value in order to keep release compilers
+              // happy.
+              bound = 0;
+              break;
+          }
+          if (bound == 0) {
+            // It is possible for a composite type to legitimately have zero
+            // sub-components, at least in the case of a struct, which
+            // can have no fields.
+            break;
+          }
+
+          // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3179) We
+          //  could allow non-constant indices when looking up non-structs,
+          //  using clamping to ensure they are in-bounds.
+          uint32_t index_value =
+              GetFuzzerContext()->GetRandomIndexForAccessChain(bound);
+          index_ids.push_back(FindOrCreate32BitIntegerConstant(
+              index_value, GetFuzzerContext()->ChooseEven()));
+          switch (subobject_type->opcode()) {
+            case SpvOpTypeArray:
+            case SpvOpTypeMatrix:
+            case SpvOpTypeVector:
+              subobject_type_id = subobject_type->GetSingleWordInOperand(0);
+              break;
+            case SpvOpTypeStruct:
+              subobject_type_id =
+                  subobject_type->GetSingleWordInOperand(index_value);
+              break;
+            default:
+              assert(false && "Not a composite type opcode.");
+          }
+        }
+        // The transformation we are about to create will only apply if a
+        // pointer suitable for the access chain's result type exists, so we
+        // create one if it does not.
+        FindOrCreatePointerType(subobject_type_id,
+                                static_cast<SpvStorageClass>(
+                                    pointer_type->GetSingleWordInOperand(0)));
+        // Apply the transformation to add an access chain.
+        ApplyTransformation(TransformationAccessChain(
+            GetFuzzerContext()->GetFreshId(), chosen_pointer->result_id(),
+            index_ids, instruction_descriptor));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.h
new file mode 100644
index 0000000..7e8ed61
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that randomly adds access chains based on pointers available in
+// the module.  Other passes can use these access chains, e.g. by loading from
+// them.
+class FuzzerPassAddAccessChains : public FuzzerPass {
+ public:
+  FuzzerPassAddAccessChains(opt::IRContext* ir_context,
+                            FactManager* fact_manager,
+                            FuzzerContext* fuzzer_context,
+                            protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddAccessChains();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_types.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_types.cpp
new file mode 100644
index 0000000..32c720e
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_types.cpp
@@ -0,0 +1,138 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_composite_types.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_add_type_array.h"
+#include "source/fuzz/transformation_add_type_struct.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddCompositeTypes::FuzzerPassAddCompositeTypes(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddCompositeTypes::~FuzzerPassAddCompositeTypes() = default;
+
+void FuzzerPassAddCompositeTypes::Apply() {
+  MaybeAddMissingVectorTypes();
+  MaybeAddMissingMatrixTypes();
+
+  // Randomly interleave between adding struct and array composite types
+  while (GetFuzzerContext()->ChoosePercentage(
+      GetFuzzerContext()->GetChanceOfAddingArrayOrStructType())) {
+    if (GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfChoosingStructTypeVsArrayType())) {
+      AddNewStructType();
+    } else {
+      AddNewArrayType();
+    }
+  }
+}
+
+void FuzzerPassAddCompositeTypes::MaybeAddMissingVectorTypes() {
+  // Functions to lazily supply scalar base types on demand if we decide to
+  // create vectors with the relevant base types.
+  std::function<uint32_t()> bool_type_supplier = [this]() -> uint32_t {
+    return FindOrCreateBoolType();
+  };
+  std::function<uint32_t()> float_type_supplier = [this]() -> uint32_t {
+    return FindOrCreate32BitFloatType();
+  };
+  std::function<uint32_t()> int_type_supplier = [this]() -> uint32_t {
+    return FindOrCreate32BitIntegerType(true);
+  };
+  std::function<uint32_t()> uint_type_supplier = [this]() -> uint32_t {
+    return FindOrCreate32BitIntegerType(false);
+  };
+
+  // Consider each of the base types with which we can make vectors.
+  for (auto& base_type_supplier : {bool_type_supplier, float_type_supplier,
+                                   int_type_supplier, uint_type_supplier}) {
+    // Consider each valid vector size.
+    for (uint32_t size = 2; size <= 4; size++) {
+      // Randomly decide whether to create (if it does not already exist) a
+      // vector with this size and base type.
+      if (GetFuzzerContext()->ChoosePercentage(
+              GetFuzzerContext()->GetChanceOfAddingVectorType())) {
+        FindOrCreateVectorType(base_type_supplier(), size);
+      }
+    }
+  }
+}
+
+void FuzzerPassAddCompositeTypes::MaybeAddMissingMatrixTypes() {
+  // Consider every valid matrix dimension.
+  for (uint32_t columns = 2; columns <= 4; columns++) {
+    for (uint32_t rows = 2; rows <= 4; rows++) {
+      // Randomly decide whether to create (if it does not already exist) a
+      // matrix with these dimensions.  As matrices can only have floating-point
+      // base type, we do not need to consider multiple base types as in the
+      // case for vectors.
+      if (GetFuzzerContext()->ChoosePercentage(
+              GetFuzzerContext()->GetChanceOfAddingMatrixType())) {
+        FindOrCreateMatrixType(columns, rows);
+      }
+    }
+  }
+}
+
+void FuzzerPassAddCompositeTypes::AddNewArrayType() {
+  ApplyTransformation(TransformationAddTypeArray(
+      GetFuzzerContext()->GetFreshId(), ChooseScalarOrCompositeType(),
+      FindOrCreate32BitIntegerConstant(
+          GetFuzzerContext()->GetRandomSizeForNewArray(), false)));
+}
+
+void FuzzerPassAddCompositeTypes::AddNewStructType() {
+  std::vector<uint32_t> field_type_ids;
+  do {
+    field_type_ids.push_back(ChooseScalarOrCompositeType());
+  } while (GetFuzzerContext()->ChoosePercentage(
+      GetFuzzerContext()->GetChanceOfAddingAnotherStructField()));
+  ApplyTransformation(TransformationAddTypeStruct(
+      GetFuzzerContext()->GetFreshId(), field_type_ids));
+}
+
+uint32_t FuzzerPassAddCompositeTypes::ChooseScalarOrCompositeType() {
+  // Gather up all the possibly-relevant types.
+  std::vector<uint32_t> candidates;
+  for (auto& inst : GetIRContext()->types_values()) {
+    switch (inst.opcode()) {
+      case SpvOpTypeArray:
+      case SpvOpTypeBool:
+      case SpvOpTypeFloat:
+      case SpvOpTypeInt:
+      case SpvOpTypeMatrix:
+      case SpvOpTypeStruct:
+      case SpvOpTypeVector:
+        candidates.push_back(inst.result_id());
+        break;
+      default:
+        break;
+    }
+  }
+  assert(!candidates.empty() &&
+         "This function should only be called if there is at least one scalar "
+         "or composite type available.");
+  // Return one of these types at random.
+  return candidates[GetFuzzerContext()->RandomIndex(candidates)];
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_types.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_types.h
new file mode 100644
index 0000000..29d4bb8
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_types.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that randomly adds missing vector and matrix types, and new
+// array and struct types, to the module.
+class FuzzerPassAddCompositeTypes : public FuzzerPass {
+ public:
+  FuzzerPassAddCompositeTypes(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddCompositeTypes();
+
+  void Apply() override;
+
+ private:
+  // Creates an array of a random size with a random existing base type and adds
+  // it to the module.
+  void AddNewArrayType();
+
+  // Creates a struct with fields of random existing types and adds it to the
+  // module.
+  void AddNewStructType();
+
+  // For each vector type not already present in the module, randomly decides
+  // whether to add it to the module.
+  void MaybeAddMissingVectorTypes();
+
+  // For each matrix type not already present in the module, randomly decides
+  // whether to add it to the module.
+  void MaybeAddMissingMatrixTypes();
+
+  // Returns the id of a scalar or composite type declared in the module,
+  // chosen randomly.
+  uint32_t ChooseScalarOrCompositeType();
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
new file mode 100644
index 0000000..c9bc9c4
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
@@ -0,0 +1,64 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_dead_blocks.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_add_dead_block.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddDeadBlocks::FuzzerPassAddDeadBlocks(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, 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
+  // candidates and actually apply transformations.  This separation is to
+  // avoid modifying the module as we traverse it.
+  std::vector<TransformationAddDeadBlock> candidate_transformations;
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      if (!GetFuzzerContext()->ChoosePercentage(
+              GetFuzzerContext()->GetChanceOfAddingDeadBlock())) {
+        continue;
+      }
+      // We speculatively create a transformation, and then apply it (below) if
+      // it turns out to be applicable.  This avoids duplicating the logic for
+      // applicability checking.
+      //
+      // It means that fresh ids for transformations that turn out not to be
+      // applicable end up being unused.
+      candidate_transformations.emplace_back(TransformationAddDeadBlock(
+          GetFuzzerContext()->GetFreshId(), block.id(),
+          GetFuzzerContext()->ChooseEven()));
+    }
+  }
+  // Apply all those transformations that are in fact applicable.
+  for (auto& transformation : candidate_transformations) {
+    if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
+      transformation.Apply(GetIRContext(), GetFactManager());
+      *GetTransformations()->add_transformation() = transformation.ToMessage();
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_blocks.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_blocks.h
new file mode 100644
index 0000000..01e3843
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_blocks.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BLOCKS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BLOCKS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass to add dynamically unreachable blocks to the module.  Future
+// passes can then manipulate such blocks.
+class FuzzerPassAddDeadBlocks : public FuzzerPass {
+ public:
+  FuzzerPassAddDeadBlocks(opt::IRContext* ir_context, FactManager* fact_manager,
+                          FuzzerContext* fuzzer_context,
+                          protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddDeadBlocks();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BLOCKS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.cpp
new file mode 100644
index 0000000..c89ae51
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.cpp
@@ -0,0 +1,247 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_function_calls.h"
+
+#include "source/fuzz/call_graph.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_add_global_variable.h"
+#include "source/fuzz/transformation_add_local_variable.h"
+#include "source/fuzz/transformation_function_call.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddFunctionCalls::FuzzerPassAddFunctionCalls(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddFunctionCalls::~FuzzerPassAddFunctionCalls() = default;
+
+void FuzzerPassAddFunctionCalls::Apply() {
+  MaybeAddTransformationBeforeEachInstruction(
+      [this](opt::Function* function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor)
+          -> void {
+        // Check whether it is legitimate to insert a function call before the
+        // instruction.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpFunctionCall,
+                                                          inst_it)) {
+          return;
+        }
+
+        // Randomly decide whether to try inserting a function call here.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfCallingFunction())) {
+          return;
+        }
+
+        // Compute the module's call graph - we don't cache it since it may
+        // change each time we apply a transformation.  If this proves to be
+        // a bottleneck the call graph data structure could be made updatable.
+        CallGraph call_graph(GetIRContext());
+
+        // Gather all the non-entry point functions different from this
+        // function.  It is important to ignore entry points as a function
+        // cannot be an entry point and the target of an OpFunctionCall
+        // instruction.  We ignore this function to avoid direct recursion.
+        std::vector<opt::Function*> candidate_functions;
+        for (auto& other_function : *GetIRContext()->module()) {
+          if (&other_function != function &&
+              !TransformationFunctionCall::FunctionIsEntryPoint(
+                  GetIRContext(), other_function.result_id())) {
+            candidate_functions.push_back(&other_function);
+          }
+        }
+
+        // Choose a function to call, at random, by considering candidate
+        // functions until a suitable one is found.
+        opt::Function* chosen_function = nullptr;
+        while (!candidate_functions.empty()) {
+          opt::Function* candidate_function =
+              GetFuzzerContext()->RemoveAtRandomIndex(&candidate_functions);
+          if (!GetFactManager()->BlockIsDead(block->id()) &&
+              !GetFactManager()->FunctionIsLivesafe(
+                  candidate_function->result_id())) {
+            // Unless in a dead block, only livesafe functions can be invoked
+            continue;
+          }
+          if (call_graph.GetIndirectCallees(candidate_function->result_id())
+                  .count(function->result_id())) {
+            // Calling this function could lead to indirect recursion
+            continue;
+          }
+          chosen_function = candidate_function;
+          break;
+        }
+
+        if (!chosen_function) {
+          // No suitable function was found to call.  (This can happen, for
+          // instance, if the current function is the only function in the
+          // module.)
+          return;
+        }
+
+        ApplyTransformation(TransformationFunctionCall(
+            GetFuzzerContext()->GetFreshId(), chosen_function->result_id(),
+            ChooseFunctionCallArguments(*chosen_function, function, block,
+                                        inst_it),
+            instruction_descriptor));
+      });
+}
+
+std::map<uint32_t, std::vector<opt::Instruction*>>
+FuzzerPassAddFunctionCalls::GetAvailableInstructionsSuitableForActualParameters(
+    opt::Function* function, opt::BasicBlock* block,
+    const opt::BasicBlock::iterator& inst_it) {
+  // Find all instructions in scope that could potentially be used as actual
+  // parameters.  Weed out unsuitable pointer arguments immediately.
+  std::vector<opt::Instruction*> potentially_suitable_instructions =
+      FindAvailableInstructions(
+          function, block, inst_it,
+          [this, block](opt::IRContext* context,
+                        opt::Instruction* inst) -> bool {
+            if (!inst->HasResultId() || !inst->type_id()) {
+              // An instruction needs a result id and type in order
+              // to be suitable as an actual parameter.
+              return false;
+            }
+            if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() ==
+                SpvOpTypePointer) {
+              switch (inst->opcode()) {
+                case SpvOpFunctionParameter:
+                case SpvOpVariable:
+                  // Function parameters and variables are the only
+                  // kinds of pointer that can be used as actual
+                  // parameters.
+                  break;
+                default:
+                  return false;
+              }
+              if (!GetFactManager()->BlockIsDead(block->id()) &&
+                  !GetFactManager()->PointeeValueIsIrrelevant(
+                      inst->result_id())) {
+                // We can only pass a pointer as an actual parameter
+                // if the pointee value for the pointer is irrelevant,
+                // or if the block from which we would make the
+                // function call is dead.
+                return false;
+              }
+            }
+            return true;
+          });
+
+  // Group all the instructions that are potentially viable as function actual
+  // parameters by their result types.
+  std::map<uint32_t, std::vector<opt::Instruction*>> result;
+  for (auto inst : potentially_suitable_instructions) {
+    if (result.count(inst->type_id()) == 0) {
+      // This is the first instruction of this type we have seen, so populate
+      // the map with an entry.
+      result.insert({inst->type_id(), {}});
+    }
+    // Add the instruction to the sequence of instructions already associated
+    // with this type.
+    result.at(inst->type_id()).push_back(inst);
+  }
+  return result;
+}
+
+std::vector<uint32_t> FuzzerPassAddFunctionCalls::ChooseFunctionCallArguments(
+    const opt::Function& callee, opt::Function* caller_function,
+    opt::BasicBlock* caller_block,
+    const opt::BasicBlock::iterator& caller_inst_it) {
+  auto type_to_available_instructions =
+      GetAvailableInstructionsSuitableForActualParameters(
+          caller_function, caller_block, caller_inst_it);
+
+  opt::Instruction* function_type = GetIRContext()->get_def_use_mgr()->GetDef(
+      callee.DefInst().GetSingleWordInOperand(1));
+  assert(function_type->opcode() == SpvOpTypeFunction &&
+         "The function type does not have the expected opcode.");
+  std::vector<uint32_t> result;
+  for (uint32_t arg_index = 1; arg_index < function_type->NumInOperands();
+       arg_index++) {
+    auto arg_type_id =
+        GetIRContext()
+            ->get_def_use_mgr()
+            ->GetDef(function_type->GetSingleWordInOperand(arg_index))
+            ->result_id();
+    if (type_to_available_instructions.count(arg_type_id)) {
+      std::vector<opt::Instruction*>& candidate_arguments =
+          type_to_available_instructions.at(arg_type_id);
+      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177) The value
+      //  selected here is arbitrary.  We should consider adding this
+      //  information as a fact so that the passed parameter could be
+      //  transformed/changed.
+      result.push_back(candidate_arguments[GetFuzzerContext()->RandomIndex(
+                                               candidate_arguments)]
+                           ->result_id());
+    } else {
+      // We don't have a suitable id in scope to pass, so we must make
+      // something up.
+      auto type_instruction =
+          GetIRContext()->get_def_use_mgr()->GetDef(arg_type_id);
+
+      if (type_instruction->opcode() == SpvOpTypePointer) {
+        // In the case of a pointer, we make a new variable, at function
+        // or global scope depending on the storage class of the
+        // pointer.
+
+        // Get a fresh id for the new variable.
+        uint32_t fresh_variable_id = GetFuzzerContext()->GetFreshId();
+
+        // The id of this variable is what we pass as the parameter to
+        // the call.
+        result.push_back(fresh_variable_id);
+
+        // Now bring the variable into existence.
+        if (type_instruction->GetSingleWordInOperand(0) ==
+            SpvStorageClassFunction) {
+          // Add a new zero-initialized local variable to the current
+          // function, noting that its pointee value is irrelevant.
+          ApplyTransformation(TransformationAddLocalVariable(
+              fresh_variable_id, arg_type_id, caller_function->result_id(),
+              FindOrCreateZeroConstant(
+                  type_instruction->GetSingleWordInOperand(1)),
+              true));
+        } else {
+          assert(type_instruction->GetSingleWordInOperand(0) ==
+                     SpvStorageClassPrivate &&
+                 "Only Function and Private storage classes are "
+                 "supported at present.");
+          // Add a new zero-initialized global variable to the module,
+          // noting that its pointee value is irrelevant.
+          ApplyTransformation(TransformationAddGlobalVariable(
+              fresh_variable_id, arg_type_id,
+              FindOrCreateZeroConstant(
+                  type_instruction->GetSingleWordInOperand(1)),
+              true));
+        }
+      } else {
+        // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): We use
+        //  constant zero for the parameter, but could consider adding a fact
+        //  to allow further passes to obfuscate it.
+        result.push_back(FindOrCreateZeroConstant(arg_type_id));
+      }
+    }
+  }
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.h
new file mode 100644
index 0000000..5d184fd
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_FUNCTION_CALLS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_FUNCTION_CALLS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that adds calls at random to (a) livesafe functions, from
+// anywhere, and (b) any functions, from dead blocks.
+class FuzzerPassAddFunctionCalls : public FuzzerPass {
+ public:
+  FuzzerPassAddFunctionCalls(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddFunctionCalls();
+
+  void Apply() override;
+
+ private:
+  // Identify all instructions available at |instr_it|, in block |block| of
+  // |function|, that are potentially suitable as function call actual
+  // parameters.  The results are grouped by type.
+  std::map<uint32_t, std::vector<opt::Instruction*>>
+  GetAvailableInstructionsSuitableForActualParameters(
+      opt::Function* function, opt::BasicBlock* block,
+      const opt::BasicBlock::iterator& inst_it);
+
+  // Randomly chooses suitable arguments to invoke |callee| right before
+  // instruction |caller_inst_it| of block |caller_block| in |caller_function|,
+  // based on both existing available instructions and the addition of new
+  // instructions to the module.
+  std::vector<uint32_t> ChooseFunctionCallArguments(
+      const opt::Function& callee, opt::Function* caller_function,
+      opt::BasicBlock* caller_block,
+      const opt::BasicBlock::iterator& caller_inst_it);
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_FUNCTION_CALLS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_global_variables.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_global_variables.cpp
new file mode 100644
index 0000000..1371f46
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_global_variables.cpp
@@ -0,0 +1,75 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_global_variables.h"
+
+#include "source/fuzz/transformation_add_global_variable.h"
+#include "source/fuzz/transformation_add_type_pointer.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default;
+
+void FuzzerPassAddGlobalVariables::Apply() {
+  auto base_type_ids_and_pointers =
+      GetAvailableBaseTypesAndPointers(SpvStorageClassPrivate);
+
+  // These are the base types that are available to this fuzzer pass.
+  auto& base_types = base_type_ids_and_pointers.first;
+
+  // These are the pointers to those base types that are *initially* available
+  // to the fuzzer pass.  The fuzzer pass might add pointer types in cases where
+  // none are available for a given base type.
+  auto& base_type_to_pointers = base_type_ids_and_pointers.second;
+
+  // Probabilistically keep adding global variables.
+  while (GetFuzzerContext()->ChoosePercentage(
+      GetFuzzerContext()->GetChanceOfAddingGlobalVariable())) {
+    // Choose a random base type; the new variable's type will be a pointer to
+    // this base type.
+    uint32_t base_type =
+        base_types[GetFuzzerContext()->RandomIndex(base_types)];
+    uint32_t pointer_type_id;
+    std::vector<uint32_t>& available_pointers_to_base_type =
+        base_type_to_pointers.at(base_type);
+    // Determine whether there is at least one pointer to this base type.
+    if (available_pointers_to_base_type.empty()) {
+      // There is not.  Make one, to use here, and add it to the available
+      // pointers for the base type so that future variables can potentially
+      // use it.
+      pointer_type_id = GetFuzzerContext()->GetFreshId();
+      available_pointers_to_base_type.push_back(pointer_type_id);
+      ApplyTransformation(TransformationAddTypePointer(
+          pointer_type_id, SpvStorageClassPrivate, base_type));
+    } else {
+      // There is - grab one.
+      pointer_type_id =
+          available_pointers_to_base_type[GetFuzzerContext()->RandomIndex(
+              available_pointers_to_base_type)];
+    }
+    ApplyTransformation(TransformationAddGlobalVariable(
+        GetFuzzerContext()->GetFreshId(), pointer_type_id,
+        FindOrCreateZeroConstant(base_type), true));
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_global_variables.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_global_variables.h
new file mode 100644
index 0000000..c71d147
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_global_variables.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that randomly adds global variables, with Private storage class,
+// to the module.
+class FuzzerPassAddGlobalVariables : public FuzzerPass {
+ public:
+  FuzzerPassAddGlobalVariables(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddGlobalVariables();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.cpp
new file mode 100644
index 0000000..2fe1220
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.cpp
@@ -0,0 +1,95 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_loads.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_load.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddLoads::FuzzerPassAddLoads(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddLoads::~FuzzerPassAddLoads() = default;
+
+void FuzzerPassAddLoads::Apply() {
+  MaybeAddTransformationBeforeEachInstruction(
+      [this](opt::Function* function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor)
+          -> void {
+        assert(inst_it->opcode() ==
+                   instruction_descriptor.target_instruction_opcode() &&
+               "The opcode of the instruction we might insert before must be "
+               "the same as the opcode in the descriptor for the instruction");
+
+        // Check whether it is legitimate to insert a load before this
+        // instruction.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, inst_it)) {
+          return;
+        }
+
+        // Randomly decide whether to try inserting a load here.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAddingLoad())) {
+          return;
+        }
+
+        std::vector<opt::Instruction*> relevant_instructions =
+            FindAvailableInstructions(
+                function, block, inst_it,
+                [](opt::IRContext* context,
+                   opt::Instruction* instruction) -> bool {
+                  if (!instruction->result_id() || !instruction->type_id()) {
+                    return false;
+                  }
+                  switch (instruction->result_id()) {
+                    case SpvOpConstantNull:
+                    case SpvOpUndef:
+                      // Do not allow loading from a null or undefined pointer;
+                      // this might be OK if the block is dead, but for now we
+                      // conservatively avoid it.
+                      return false;
+                    default:
+                      break;
+                  }
+                  return context->get_def_use_mgr()
+                             ->GetDef(instruction->type_id())
+                             ->opcode() == SpvOpTypePointer;
+                });
+
+        // At this point, |relevant_instructions| contains all the pointers
+        // we might think of loading from.
+        if (relevant_instructions.empty()) {
+          return;
+        }
+
+        // Choose a pointer at random, and create and apply a loading
+        // transformation based on it.
+        ApplyTransformation(TransformationLoad(
+            GetFuzzerContext()->GetFreshId(),
+            relevant_instructions[GetFuzzerContext()->RandomIndex(
+                                      relevant_instructions)]
+                ->result_id(),
+            instruction_descriptor));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.h
new file mode 100644
index 0000000..125bc5d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_LOADS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOADS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that adds stores, at random, from pointers in the module.
+class FuzzerPassAddLoads : public FuzzerPass {
+ public:
+  FuzzerPassAddLoads(opt::IRContext* ir_context, FactManager* fact_manager,
+                     FuzzerContext* fuzzer_context,
+                     protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddLoads();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_LOADS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_local_variables.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_local_variables.cpp
new file mode 100644
index 0000000..8d6d80d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_local_variables.cpp
@@ -0,0 +1,79 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_local_variables.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_add_local_variable.h"
+#include "source/fuzz/transformation_add_type_pointer.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddLocalVariables::~FuzzerPassAddLocalVariables() = default;
+
+void FuzzerPassAddLocalVariables::Apply() {
+  auto base_type_ids_and_pointers =
+      GetAvailableBaseTypesAndPointers(SpvStorageClassFunction);
+
+  // These are the base types that are available to this fuzzer pass.
+  auto& base_types = base_type_ids_and_pointers.first;
+
+  // These are the pointers to those base types that are *initially* available
+  // to the fuzzer pass.  The fuzzer pass might add pointer types in cases where
+  // none are available for a given base type.
+  auto& base_type_to_pointers = base_type_ids_and_pointers.second;
+
+  // Consider every function in the module.
+  for (auto& function : *GetIRContext()->module()) {
+    // Probabilistically keep adding random variables to this function.
+    while (GetFuzzerContext()->ChoosePercentage(
+        GetFuzzerContext()->GetChanceOfAddingLocalVariable())) {
+      // Choose a random base type; the new variable's type will be a pointer to
+      // this base type.
+      uint32_t base_type =
+          base_types[GetFuzzerContext()->RandomIndex(base_types)];
+      uint32_t pointer_type;
+      std::vector<uint32_t>& available_pointers_to_base_type =
+          base_type_to_pointers.at(base_type);
+      // Determine whether there is at least one pointer to this base type.
+      if (available_pointers_to_base_type.empty()) {
+        // There is not.  Make one, to use here, and add it to the available
+        // pointers for the base type so that future variables can potentially
+        // use it.
+        pointer_type = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(TransformationAddTypePointer(
+            pointer_type, SpvStorageClassFunction, base_type));
+        available_pointers_to_base_type.push_back(pointer_type);
+      } else {
+        // There is - grab one.
+        pointer_type =
+            available_pointers_to_base_type[GetFuzzerContext()->RandomIndex(
+                available_pointers_to_base_type)];
+      }
+      ApplyTransformation(TransformationAddLocalVariable(
+          GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(),
+          FindOrCreateZeroConstant(base_type), true));
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_local_variables.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_local_variables.h
new file mode 100644
index 0000000..eed3665
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_local_variables.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that randomly adds local variables, with Function storage class,
+// to the module.
+class FuzzerPassAddLocalVariables : public FuzzerPass {
+ public:
+  FuzzerPassAddLocalVariables(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddLocalVariables();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.cpp
new file mode 100644
index 0000000..d2c7b3d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.cpp
@@ -0,0 +1,128 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_stores.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_store.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddStores::FuzzerPassAddStores(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddStores::~FuzzerPassAddStores() = default;
+
+void FuzzerPassAddStores::Apply() {
+  MaybeAddTransformationBeforeEachInstruction(
+      [this](opt::Function* function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor)
+          -> void {
+        assert(inst_it->opcode() ==
+                   instruction_descriptor.target_instruction_opcode() &&
+               "The opcode of the instruction we might insert before must be "
+               "the same as the opcode in the descriptor for the instruction");
+
+        // Check whether it is legitimate to insert a store before this
+        // instruction.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
+                                                          inst_it)) {
+          return;
+        }
+
+        // Randomly decide whether to try inserting a store here.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAddingStore())) {
+          return;
+        }
+
+        // Look for pointers we might consider storing to.
+        std::vector<opt::Instruction*> relevant_pointers =
+            FindAvailableInstructions(
+                function, block, inst_it,
+                [this, block](opt::IRContext* context,
+                              opt::Instruction* instruction) -> bool {
+                  if (!instruction->result_id() || !instruction->type_id()) {
+                    return false;
+                  }
+                  auto type_inst = context->get_def_use_mgr()->GetDef(
+                      instruction->type_id());
+                  if (type_inst->opcode() != SpvOpTypePointer) {
+                    // Not a pointer.
+                    return false;
+                  }
+                  if (type_inst->GetSingleWordInOperand(0) ==
+                      SpvStorageClassInput) {
+                    // Read-only: cannot store to it.
+                    return false;
+                  }
+                  switch (instruction->result_id()) {
+                    case SpvOpConstantNull:
+                    case SpvOpUndef:
+                      // Do not allow storing to a null or undefined pointer;
+                      // this might be OK if the block is dead, but for now we
+                      // conservatively avoid it.
+                      return false;
+                    default:
+                      break;
+                  }
+                  return GetFactManager()->BlockIsDead(block->id()) ||
+                         GetFactManager()->PointeeValueIsIrrelevant(
+                             instruction->result_id());
+                });
+
+        // At this point, |relevant_pointers| contains all the pointers we might
+        // think of storing to.
+        if (relevant_pointers.empty()) {
+          return;
+        }
+
+        auto pointer = relevant_pointers[GetFuzzerContext()->RandomIndex(
+            relevant_pointers)];
+
+        std::vector<opt::Instruction*> relevant_values =
+            FindAvailableInstructions(
+                function, block, inst_it,
+                [pointer](opt::IRContext* context,
+                          opt::Instruction* instruction) -> bool {
+                  if (!instruction->result_id() || !instruction->type_id()) {
+                    return false;
+                  }
+                  return instruction->type_id() ==
+                         context->get_def_use_mgr()
+                             ->GetDef(pointer->type_id())
+                             ->GetSingleWordInOperand(1);
+                });
+
+        if (relevant_values.empty()) {
+          return;
+        }
+
+        // Choose a value at random, and create and apply a storing
+        // transformation based on it and the pointer.
+        ApplyTransformation(TransformationStore(
+            pointer->result_id(),
+            relevant_values[GetFuzzerContext()->RandomIndex(relevant_values)]
+                ->result_id(),
+            instruction_descriptor));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.h
new file mode 100644
index 0000000..9daa9e0
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_STORES_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_STORES_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that adds stores, at random, through pointers in the module,
+// either (a) from dead blocks, or (b) through pointers whose pointee values
+// are known not to affect the module's overall behaviour.
+class FuzzerPassAddStores : public FuzzerPass {
+ public:
+  FuzzerPassAddStores(opt::IRContext* ir_context, FactManager* fact_manager,
+                      FuzzerContext* fuzzer_context,
+                      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddStores();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_STORES_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
index 6ff42ca..e932017 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
@@ -97,9 +97,9 @@
           continue;
         }
 
-        if (!TransformationReplaceIdWithSynonym::IdsIsAvailableAtUse(
-                GetIRContext(), use_inst, use_in_operand_index,
-                synonym_to_try->object())) {
+        if (!fuzzerutil::IdIsAvailableAtUse(GetIRContext(), use_inst,
+                                            use_in_operand_index,
+                                            synonym_to_try->object())) {
           continue;
         }
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp
index ff0adab..e160302 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp
@@ -44,7 +44,7 @@
 
   MaybeAddTransformationBeforeEachInstruction(
       [this, &composite_type_ids](
-          const opt::Function& function, opt::BasicBlock* block,
+          opt::Function* function, opt::BasicBlock* block,
           opt::BasicBlock::iterator inst_it,
           const protobufs::InstructionDescriptor& instruction_descriptor)
           -> void {
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.cpp
index 35b15a3..0fbe5cb 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.cpp
@@ -30,7 +30,7 @@
 
 void FuzzerPassCopyObjects::Apply() {
   MaybeAddTransformationBeforeEachInstruction(
-      [this](const opt::Function& function, opt::BasicBlock* block,
+      [this](opt::Function* function, opt::BasicBlock* block,
              opt::BasicBlock::iterator inst_it,
              const protobufs::InstructionDescriptor& instruction_descriptor)
           -> void {
@@ -64,15 +64,11 @@
 
         // Choose a copyable instruction at random, and create and apply an
         // object copying transformation based on it.
-        uint32_t index = GetFuzzerContext()->RandomIndex(relevant_instructions);
-        TransformationCopyObject transformation(
-            relevant_instructions[index]->result_id(), instruction_descriptor,
-            GetFuzzerContext()->GetFreshId());
-        assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
-               "This transformation should be applicable by construction.");
-        transformation.Apply(GetIRContext(), GetFactManager());
-        *GetTransformations()->add_transformation() =
-            transformation.ToMessage();
+        ApplyTransformation(TransformationCopyObject(
+            relevant_instructions[GetFuzzerContext()->RandomIndex(
+                                      relevant_instructions)]
+                ->result_id(),
+            instruction_descriptor, GetFuzzerContext()->GetFreshId()));
       });
 }
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_donate_modules.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_donate_modules.cpp
new file mode 100644
index 0000000..27d8a6e
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_donate_modules.cpp
@@ -0,0 +1,739 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_donate_modules.h"
+
+#include <map>
+#include <queue>
+#include <set>
+
+#include "source/fuzz/call_graph.h"
+#include "source/fuzz/instruction_message.h"
+#include "source/fuzz/transformation_add_constant_boolean.h"
+#include "source/fuzz/transformation_add_constant_composite.h"
+#include "source/fuzz/transformation_add_constant_scalar.h"
+#include "source/fuzz/transformation_add_function.h"
+#include "source/fuzz/transformation_add_global_undef.h"
+#include "source/fuzz/transformation_add_global_variable.h"
+#include "source/fuzz/transformation_add_type_array.h"
+#include "source/fuzz/transformation_add_type_boolean.h"
+#include "source/fuzz/transformation_add_type_float.h"
+#include "source/fuzz/transformation_add_type_function.h"
+#include "source/fuzz/transformation_add_type_int.h"
+#include "source/fuzz/transformation_add_type_matrix.h"
+#include "source/fuzz/transformation_add_type_pointer.h"
+#include "source/fuzz/transformation_add_type_struct.h"
+#include "source/fuzz/transformation_add_type_vector.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassDonateModules::FuzzerPassDonateModules(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations,
+    const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, 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()) {
+    return;
+  }
+
+  // Donate at least one module, and probabilistically decide when to stop
+  // donating modules.
+  do {
+    // Choose a donor supplier at random, and get the module that it provides.
+    std::unique_ptr<opt::IRContext> donor_ir_context = donor_suppliers_.at(
+        GetFuzzerContext()->RandomIndex(donor_suppliers_))();
+    assert(donor_ir_context != nullptr && "Supplying of donor failed");
+    assert(fuzzerutil::IsValid(donor_ir_context.get()) &&
+           "The donor module must be valid");
+    // Donate the supplied module.
+    //
+    // Randomly decide whether to make the module livesafe (see
+    // FactFunctionIsLivesafe); doing so allows it to be used for live code
+    // injection but restricts its behaviour to allow this, and means that its
+    // functions cannot be transformed as if they were arbitrary dead code.
+    bool make_livesafe = GetFuzzerContext()->ChoosePercentage(
+        GetFuzzerContext()->ChanceOfMakingDonorLivesafe());
+    DonateSingleModule(donor_ir_context.get(), make_livesafe);
+  } while (GetFuzzerContext()->ChoosePercentage(
+      GetFuzzerContext()->GetChanceOfDonatingAdditionalModule()));
+}
+
+void FuzzerPassDonateModules::DonateSingleModule(
+    opt::IRContext* donor_ir_context, bool make_livesafe) {
+  // The ids used by the donor module may very well clash with ids defined in
+  // the recipient module.  Furthermore, some instructions defined in the donor
+  // module will be equivalent to instructions defined in the recipient module,
+  // and it is not always legal to re-declare equivalent instructions.  For
+  // example, OpTypeVoid cannot be declared twice.
+  //
+  // To handle this, we maintain a mapping from an id used in the donor module
+  // to the corresponding id that will be used by the donated code when it
+  // appears in the recipient module.
+  //
+  // This mapping is populated in two ways:
+  // (1) by mapping a donor instruction's result id to the id of some equivalent
+  //     existing instruction in the recipient (e.g. this has to be done for
+  //     OpTypeVoid)
+  // (2) by mapping a donor instruction's result id to a freshly chosen id that
+  //     is guaranteed to be different from any id already used by the recipient
+  //     (or from any id already chosen to handle a previous donor id)
+  std::map<uint32_t, uint32_t> original_id_to_donated_id;
+
+  HandleExternalInstructionImports(donor_ir_context,
+                                   &original_id_to_donated_id);
+  HandleTypesAndValues(donor_ir_context, &original_id_to_donated_id);
+  HandleFunctions(donor_ir_context, &original_id_to_donated_id, make_livesafe);
+
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3115) Handle some
+  //  kinds of decoration.
+}
+
+SpvStorageClass FuzzerPassDonateModules::AdaptStorageClass(
+    SpvStorageClass donor_storage_class) {
+  switch (donor_storage_class) {
+    case SpvStorageClassFunction:
+    case SpvStorageClassPrivate:
+      // We leave these alone
+      return donor_storage_class;
+    case SpvStorageClassInput:
+    case SpvStorageClassOutput:
+    case SpvStorageClassUniform:
+    case SpvStorageClassUniformConstant:
+    case SpvStorageClassPushConstant:
+      // We change these to Private
+      return SpvStorageClassPrivate;
+    default:
+      // Handle other cases on demand.
+      assert(false && "Currently unsupported storage class.");
+      return SpvStorageClassMax;
+  }
+}
+
+void FuzzerPassDonateModules::HandleExternalInstructionImports(
+    opt::IRContext* donor_ir_context,
+    std::map<uint32_t, uint32_t>* original_id_to_donated_id) {
+  // Consider every external instruction set import in the donor module.
+  for (auto& donor_import : donor_ir_context->module()->ext_inst_imports()) {
+    const auto& donor_import_name_words = donor_import.GetInOperand(0).words;
+    // Look for an identical import in the recipient module.
+    for (auto& existing_import : GetIRContext()->module()->ext_inst_imports()) {
+      const auto& existing_import_name_words =
+          existing_import.GetInOperand(0).words;
+      if (donor_import_name_words == existing_import_name_words) {
+        // A matching import has found.  Map the result id for the donor import
+        // to the id of the existing import, so that when donor instructions
+        // rely on the import they will be rewritten to use the existing import.
+        original_id_to_donated_id->insert(
+            {donor_import.result_id(), existing_import.result_id()});
+        break;
+      }
+    }
+    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3116): At present
+    //  we do not handle donation of instruction imports, i.e. we do not allow
+    //  the donor to import instruction sets that the recipient did not already
+    //  import.  It might be a good idea to allow this, but it requires some
+    //  thought.
+    assert(original_id_to_donated_id->count(donor_import.result_id()) &&
+           "Donation of imports is not yet supported.");
+  }
+}
+
+void FuzzerPassDonateModules::HandleTypesAndValues(
+    opt::IRContext* donor_ir_context,
+    std::map<uint32_t, uint32_t>* original_id_to_donated_id) {
+  // Consider every type/global/constant/undef in the module.
+  for (auto& type_or_value : donor_ir_context->module()->types_values()) {
+    // Each such instruction generates a result id, and as part of donation we
+    // need to associate the donor's result id with a new result id.  That new
+    // result id will either be the id of some existing instruction, or a fresh
+    // id.  This variable captures it.
+    uint32_t new_result_id;
+
+    // Decide how to handle each kind of instruction on a case-by-case basis.
+    //
+    // Because the donor module is required to be valid, when we encounter a
+    // type comprised of component types (e.g. an aggregate or pointer), we know
+    // that its component types will have been considered previously, and that
+    // |original_id_to_donated_id| will already contain an entry for them.
+    switch (type_or_value.opcode()) {
+      case SpvOpTypeVoid: {
+        // Void has to exist already in order for us to have an entry point.
+        // Get the existing id of void.
+        opt::analysis::Void void_type;
+        new_result_id = GetIRContext()->get_type_mgr()->GetId(&void_type);
+        assert(new_result_id &&
+               "The module being transformed will always have 'void' type "
+               "declared.");
+      } break;
+      case SpvOpTypeBool: {
+        // Bool cannot be declared multiple times, so use its existing id if
+        // present, or add a declaration of Bool with a fresh id if not.
+        opt::analysis::Bool bool_type;
+        auto bool_type_id = GetIRContext()->get_type_mgr()->GetId(&bool_type);
+        if (bool_type_id) {
+          new_result_id = bool_type_id;
+        } else {
+          new_result_id = GetFuzzerContext()->GetFreshId();
+          ApplyTransformation(TransformationAddTypeBoolean(new_result_id));
+        }
+      } break;
+      case SpvOpTypeInt: {
+        // Int cannot be declared multiple times with the same width and
+        // signedness, so check whether an existing identical Int type is
+        // present and use its id if so.  Otherwise add a declaration of the
+        // Int type used by the donor, with a fresh id.
+        const uint32_t width = type_or_value.GetSingleWordInOperand(0);
+        const bool is_signed =
+            static_cast<bool>(type_or_value.GetSingleWordInOperand(1));
+        opt::analysis::Integer int_type(width, is_signed);
+        auto int_type_id = GetIRContext()->get_type_mgr()->GetId(&int_type);
+        if (int_type_id) {
+          new_result_id = int_type_id;
+        } else {
+          new_result_id = GetFuzzerContext()->GetFreshId();
+          ApplyTransformation(
+              TransformationAddTypeInt(new_result_id, width, is_signed));
+        }
+      } break;
+      case SpvOpTypeFloat: {
+        // Similar to SpvOpTypeInt.
+        const uint32_t width = type_or_value.GetSingleWordInOperand(0);
+        opt::analysis::Float float_type(width);
+        auto float_type_id = GetIRContext()->get_type_mgr()->GetId(&float_type);
+        if (float_type_id) {
+          new_result_id = float_type_id;
+        } else {
+          new_result_id = GetFuzzerContext()->GetFreshId();
+          ApplyTransformation(TransformationAddTypeFloat(new_result_id, width));
+        }
+      } break;
+      case SpvOpTypeVector: {
+        // It is not legal to have two Vector type declarations with identical
+        // element types and element counts, so check whether an existing
+        // identical Vector type is present and use its id if so.  Otherwise add
+        // a declaration of the Vector type used by the donor, with a fresh id.
+
+        // When considering the vector's component type id, we look up the id
+        // use in the donor to find the id to which this has been remapped.
+        uint32_t component_type_id = original_id_to_donated_id->at(
+            type_or_value.GetSingleWordInOperand(0));
+        auto component_type =
+            GetIRContext()->get_type_mgr()->GetType(component_type_id);
+        assert(component_type && "The base type should be registered.");
+        auto component_count = type_or_value.GetSingleWordInOperand(1);
+        opt::analysis::Vector vector_type(component_type, component_count);
+        auto vector_type_id =
+            GetIRContext()->get_type_mgr()->GetId(&vector_type);
+        if (vector_type_id) {
+          new_result_id = vector_type_id;
+        } else {
+          new_result_id = GetFuzzerContext()->GetFreshId();
+          ApplyTransformation(TransformationAddTypeVector(
+              new_result_id, component_type_id, component_count));
+        }
+      } break;
+      case SpvOpTypeMatrix: {
+        // Similar to SpvOpTypeVector.
+        uint32_t column_type_id = original_id_to_donated_id->at(
+            type_or_value.GetSingleWordInOperand(0));
+        auto column_type =
+            GetIRContext()->get_type_mgr()->GetType(column_type_id);
+        assert(column_type && column_type->AsVector() &&
+               "The column type should be a registered vector type.");
+        auto column_count = type_or_value.GetSingleWordInOperand(1);
+        opt::analysis::Matrix matrix_type(column_type, column_count);
+        auto matrix_type_id =
+            GetIRContext()->get_type_mgr()->GetId(&matrix_type);
+        if (matrix_type_id) {
+          new_result_id = matrix_type_id;
+        } else {
+          new_result_id = GetFuzzerContext()->GetFreshId();
+          ApplyTransformation(TransformationAddTypeMatrix(
+              new_result_id, column_type_id, column_count));
+        }
+
+      } break;
+      case SpvOpTypeArray: {
+        // It is OK to have multiple structurally identical array types, so
+        // we go ahead and add a remapped version of the type declared by the
+        // donor.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(TransformationAddTypeArray(
+            new_result_id,
+            original_id_to_donated_id->at(
+                type_or_value.GetSingleWordInOperand(0)),
+            original_id_to_donated_id->at(
+                type_or_value.GetSingleWordInOperand(1))));
+      } break;
+      case SpvOpTypeStruct: {
+        // Similar to SpvOpTypeArray.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        std::vector<uint32_t> member_type_ids;
+        type_or_value.ForEachInId(
+            [&member_type_ids,
+             &original_id_to_donated_id](const uint32_t* component_type_id) {
+              member_type_ids.push_back(
+                  original_id_to_donated_id->at(*component_type_id));
+            });
+        ApplyTransformation(
+            TransformationAddTypeStruct(new_result_id, member_type_ids));
+      } break;
+      case SpvOpTypePointer: {
+        // Similar to SpvOpTypeArray.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(TransformationAddTypePointer(
+            new_result_id,
+            AdaptStorageClass(static_cast<SpvStorageClass>(
+                type_or_value.GetSingleWordInOperand(0))),
+            original_id_to_donated_id->at(
+                type_or_value.GetSingleWordInOperand(1))));
+      } break;
+      case SpvOpTypeFunction: {
+        // It is not OK to have multiple function types that use identical ids
+        // for their return and parameter types.  We thus go through all
+        // existing function types to look for a match.  We do not use the
+        // type manager here because we want to regard two function types that
+        // are structurally identical but that differ with respect to the
+        // actual ids used for pointer types as different.
+        //
+        // Example:
+        //
+        // %1 = OpTypeVoid
+        // %2 = OpTypeInt 32 0
+        // %3 = OpTypePointer Function %2
+        // %4 = OpTypePointer Function %2
+        // %5 = OpTypeFunction %1 %3
+        // %6 = OpTypeFunction %1 %4
+        //
+        // We regard %5 and %6 as distinct function types here, even though
+        // they both have the form "uint32* -> void"
+
+        std::vector<uint32_t> return_and_parameter_types;
+        for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) {
+          return_and_parameter_types.push_back(original_id_to_donated_id->at(
+              type_or_value.GetSingleWordInOperand(i)));
+        }
+        uint32_t existing_function_id = fuzzerutil::FindFunctionType(
+            GetIRContext(), return_and_parameter_types);
+        if (existing_function_id) {
+          new_result_id = existing_function_id;
+        } else {
+          // No match was found, so add a remapped version of the function type
+          // to the module, with a fresh id.
+          new_result_id = GetFuzzerContext()->GetFreshId();
+          std::vector<uint32_t> argument_type_ids;
+          for (uint32_t i = 1; i < type_or_value.NumInOperands(); i++) {
+            argument_type_ids.push_back(original_id_to_donated_id->at(
+                type_or_value.GetSingleWordInOperand(i)));
+          }
+          ApplyTransformation(TransformationAddTypeFunction(
+              new_result_id,
+              original_id_to_donated_id->at(
+                  type_or_value.GetSingleWordInOperand(0)),
+              argument_type_ids));
+        }
+      } break;
+      case SpvOpConstantTrue:
+      case SpvOpConstantFalse: {
+        // It is OK to have duplicate definitions of True and False, so add
+        // these to the module, using a remapped Bool type.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(TransformationAddConstantBoolean(
+            new_result_id, type_or_value.opcode() == SpvOpConstantTrue));
+      } break;
+      case SpvOpConstant: {
+        // It is OK to have duplicate constant definitions, so add this to the
+        // module using a remapped result type.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        std::vector<uint32_t> data_words;
+        type_or_value.ForEachInOperand(
+            [&data_words](const uint32_t* in_operand) {
+              data_words.push_back(*in_operand);
+            });
+        ApplyTransformation(TransformationAddConstantScalar(
+            new_result_id,
+            original_id_to_donated_id->at(type_or_value.type_id()),
+            data_words));
+      } break;
+      case SpvOpConstantComposite: {
+        // It is OK to have duplicate constant composite definitions, so add
+        // this to the module using remapped versions of all consituent ids and
+        // the result type.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        std::vector<uint32_t> constituent_ids;
+        type_or_value.ForEachInId(
+            [&constituent_ids,
+             &original_id_to_donated_id](const uint32_t* constituent_id) {
+              constituent_ids.push_back(
+                  original_id_to_donated_id->at(*constituent_id));
+            });
+        ApplyTransformation(TransformationAddConstantComposite(
+            new_result_id,
+            original_id_to_donated_id->at(type_or_value.type_id()),
+            constituent_ids));
+      } break;
+      case SpvOpVariable: {
+        // This is a global variable that could have one of various storage
+        // classes.  However, we change all global variable pointer storage
+        // classes (such as Uniform, Input and Output) to private when donating
+        // pointer types.  Thus this variable's pointer type is guaranteed to
+        // have storage class private.  As a result, we simply add a Private
+        // storage class global variable, using remapped versions of the result
+        // type and initializer ids for the global variable in the donor.
+        //
+        // We regard the added variable as having an irrelevant value.  This
+        // means that future passes can add stores to the variable in any
+        // way they wish, and pass them as pointer parameters to functions
+        // without worrying about whether their data might get modified.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        uint32_t remapped_pointer_type =
+            original_id_to_donated_id->at(type_or_value.type_id());
+        uint32_t initializer_id;
+        if (type_or_value.NumInOperands() == 1) {
+          // The variable did not have an initializer; initialize it to zero.
+          // This is to limit problems associated with uninitialized data.
+          initializer_id = FindOrCreateZeroConstant(
+              fuzzerutil::GetPointeeTypeIdFromPointerType(
+                  GetIRContext(), remapped_pointer_type));
+        } else {
+          // The variable already had an initializer; use its remapped id.
+          initializer_id = original_id_to_donated_id->at(
+              type_or_value.GetSingleWordInOperand(1));
+        }
+        ApplyTransformation(TransformationAddGlobalVariable(
+            new_result_id, remapped_pointer_type, initializer_id, true));
+      } break;
+      case SpvOpUndef: {
+        // It is fine to have multiple Undef instructions of the same type, so
+        // we just add this to the recipient module.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(TransformationAddGlobalUndef(
+            new_result_id,
+            original_id_to_donated_id->at(type_or_value.type_id())));
+      } break;
+      default: {
+        assert(0 && "Unknown type/value.");
+        new_result_id = 0;
+      } break;
+    }
+    // Update the id mapping to associate the instruction's result id with its
+    // corresponding id in the recipient.
+    original_id_to_donated_id->insert(
+        {type_or_value.result_id(), new_result_id});
+  }
+}
+
+void FuzzerPassDonateModules::HandleFunctions(
+    opt::IRContext* donor_ir_context,
+    std::map<uint32_t, uint32_t>* original_id_to_donated_id,
+    bool make_livesafe) {
+  // Get the ids of functions in the donor module, topologically sorted
+  // according to the donor's call graph.
+  auto topological_order =
+      GetFunctionsInCallGraphTopologicalOrder(donor_ir_context);
+
+  // Donate the functions in reverse topological order.  This ensures that a
+  // function gets donated before any function that depends on it.  This allows
+  // donation of the functions to be separated into a number of transformations,
+  // each adding one function, such that every prefix of transformations leaves
+  // the module valid.
+  for (auto function_id = topological_order.rbegin();
+       function_id != topological_order.rend(); ++function_id) {
+    // Find the function to be donated.
+    opt::Function* function_to_donate = nullptr;
+    for (auto& function : *donor_ir_context->module()) {
+      if (function.result_id() == *function_id) {
+        function_to_donate = &function;
+        break;
+      }
+    }
+    assert(function_to_donate && "Function to be donated was not found.");
+
+    // We will collect up protobuf messages representing the donor function's
+    // instructions here, and use them to create an AddFunction transformation.
+    std::vector<protobufs::Instruction> donated_instructions;
+
+    // Scan through the function, remapping each result id that it generates to
+    // a fresh id.  This is necessary because functions include forward
+    // references, e.g. to labels.
+    function_to_donate->ForEachInst([this, &original_id_to_donated_id](
+                                        const opt::Instruction* instruction) {
+      if (instruction->result_id()) {
+        original_id_to_donated_id->insert(
+            {instruction->result_id(), GetFuzzerContext()->GetFreshId()});
+      }
+    });
+
+    // Consider every instruction of the donor function.
+    function_to_donate->ForEachInst([this, &donated_instructions,
+                                     &original_id_to_donated_id](
+                                        const opt::Instruction* instruction) {
+      // Get the instruction's input operands into donation-ready form,
+      // remapping any id uses in the process.
+      opt::Instruction::OperandList input_operands;
+
+      // Consider each input operand in turn.
+      for (uint32_t in_operand_index = 0;
+           in_operand_index < instruction->NumInOperands();
+           in_operand_index++) {
+        std::vector<uint32_t> operand_data;
+        const opt::Operand& in_operand =
+            instruction->GetInOperand(in_operand_index);
+        switch (in_operand.type) {
+          case SPV_OPERAND_TYPE_ID:
+          case SPV_OPERAND_TYPE_TYPE_ID:
+          case SPV_OPERAND_TYPE_RESULT_ID:
+          case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
+          case SPV_OPERAND_TYPE_SCOPE_ID:
+            // This is an id operand - it consists of a single word of data,
+            // which needs to be remapped so that it is replaced with the
+            // donated form of the id.
+            operand_data.push_back(
+                original_id_to_donated_id->at(in_operand.words[0]));
+            break;
+          default:
+            // For non-id operands, we just add each of the data words.
+            for (auto word : in_operand.words) {
+              operand_data.push_back(word);
+            }
+            break;
+        }
+        input_operands.push_back({in_operand.type, operand_data});
+      }
+
+      if (instruction->opcode() == SpvOpVariable &&
+          instruction->NumInOperands() == 1) {
+        // This is an uninitialized local variable.  Initialize it to zero.
+        input_operands.push_back(
+            {SPV_OPERAND_TYPE_ID,
+             {FindOrCreateZeroConstant(
+                 fuzzerutil::GetPointeeTypeIdFromPointerType(
+                     GetIRContext(),
+                     original_id_to_donated_id->at(instruction->type_id())))}});
+      }
+
+      // Remap the result type and result id (if present) of the
+      // instruction, and turn it into a protobuf message.
+      donated_instructions.push_back(MakeInstructionMessage(
+          instruction->opcode(),
+          instruction->type_id()
+              ? original_id_to_donated_id->at(instruction->type_id())
+              : 0,
+          instruction->result_id()
+              ? original_id_to_donated_id->at(instruction->result_id())
+              : 0,
+          input_operands));
+    });
+
+    if (make_livesafe) {
+      // Various types and constants must be in place for a function to be made
+      // live-safe.  Add them if not already present.
+      FindOrCreateBoolType();  // Needed for comparisons
+      FindOrCreatePointerTo32BitIntegerType(
+          false, SpvStorageClassFunction);  // Needed for adding loop limiters
+      FindOrCreate32BitIntegerConstant(
+          0, false);  // Needed for initializing loop limiters
+      FindOrCreate32BitIntegerConstant(
+          1, false);  // Needed for incrementing loop limiters
+
+      // Get a fresh id for the variable that will be used as a loop limiter.
+      const uint32_t loop_limiter_variable_id =
+          GetFuzzerContext()->GetFreshId();
+      // Choose a random loop limit, and add the required constant to the
+      // module if not already there.
+      const uint32_t loop_limit = FindOrCreate32BitIntegerConstant(
+          GetFuzzerContext()->GetRandomLoopLimit(), false);
+
+      // Consider every loop header in the function to donate, and create a
+      // structure capturing the ids to be used for manipulating the loop
+      // limiter each time the loop is iterated.
+      std::vector<protobufs::LoopLimiterInfo> loop_limiters;
+      for (auto& block : *function_to_donate) {
+        if (block.IsLoopHeader()) {
+          protobufs::LoopLimiterInfo loop_limiter;
+          // Grab the loop header's id, mapped to its donated value.
+          loop_limiter.set_loop_header_id(
+              original_id_to_donated_id->at(block.id()));
+          // Get fresh ids that will be used to load the loop limiter, increment
+          // it, compare it with the loop limit, and an id for a new block that
+          // will contain the loop's original terminator.
+          loop_limiter.set_load_id(GetFuzzerContext()->GetFreshId());
+          loop_limiter.set_increment_id(GetFuzzerContext()->GetFreshId());
+          loop_limiter.set_compare_id(GetFuzzerContext()->GetFreshId());
+          loop_limiter.set_logical_op_id(GetFuzzerContext()->GetFreshId());
+          loop_limiters.emplace_back(loop_limiter);
+        }
+      }
+
+      // Consider every access chain in the function to donate, and create a
+      // structure containing the ids necessary to clamp the access chain
+      // indices to be in-bounds.
+      std::vector<protobufs::AccessChainClampingInfo>
+          access_chain_clamping_info;
+      for (auto& block : *function_to_donate) {
+        for (auto& inst : block) {
+          switch (inst.opcode()) {
+            case SpvOpAccessChain:
+            case SpvOpInBoundsAccessChain: {
+              protobufs::AccessChainClampingInfo clamping_info;
+              clamping_info.set_access_chain_id(
+                  original_id_to_donated_id->at(inst.result_id()));
+
+              auto base_object = donor_ir_context->get_def_use_mgr()->GetDef(
+                  inst.GetSingleWordInOperand(0));
+              assert(base_object && "The base object must exist.");
+              auto pointer_type = donor_ir_context->get_def_use_mgr()->GetDef(
+                  base_object->type_id());
+              assert(pointer_type &&
+                     pointer_type->opcode() == SpvOpTypePointer &&
+                     "The base object must have pointer type.");
+
+              auto should_be_composite_type =
+                  donor_ir_context->get_def_use_mgr()->GetDef(
+                      pointer_type->GetSingleWordInOperand(1));
+
+              // Walk the access chain, creating fresh ids to facilitate
+              // clamping each index.  For simplicity we do this for every
+              // index, even though constant indices will not end up being
+              // clamped.
+              for (uint32_t index = 1; index < inst.NumInOperands(); index++) {
+                auto compare_and_select_ids =
+                    clamping_info.add_compare_and_select_ids();
+                compare_and_select_ids->set_first(
+                    GetFuzzerContext()->GetFreshId());
+                compare_and_select_ids->set_second(
+                    GetFuzzerContext()->GetFreshId());
+
+                // Get the bound for the component being indexed into.
+                uint32_t bound =
+                    TransformationAddFunction::GetBoundForCompositeIndex(
+                        donor_ir_context, *should_be_composite_type);
+                const uint32_t index_id = inst.GetSingleWordInOperand(index);
+                auto index_inst =
+                    donor_ir_context->get_def_use_mgr()->GetDef(index_id);
+                auto index_type_inst =
+                    donor_ir_context->get_def_use_mgr()->GetDef(
+                        index_inst->type_id());
+                assert(index_type_inst->opcode() == SpvOpTypeInt);
+                assert(index_type_inst->GetSingleWordInOperand(0) == 32);
+                opt::analysis::Integer* index_int_type =
+                    donor_ir_context->get_type_mgr()
+                        ->GetType(index_type_inst->result_id())
+                        ->AsInteger();
+                if (index_inst->opcode() != SpvOpConstant) {
+                  // We will have to clamp this index, so we need a constant
+                  // whose value is one less than the bound, to compare
+                  // against and to use as the clamped value.
+                  FindOrCreate32BitIntegerConstant(bound - 1,
+                                                   index_int_type->IsSigned());
+                }
+                should_be_composite_type =
+                    TransformationAddFunction::FollowCompositeIndex(
+                        donor_ir_context, *should_be_composite_type, index_id);
+              }
+              access_chain_clamping_info.push_back(clamping_info);
+              break;
+            }
+            default:
+              break;
+          }
+        }
+      }
+
+      // If the function contains OpKill or OpUnreachable instructions, and has
+      // non-void return type, then we need a value %v to use in order to turn
+      // these into instructions of the form OpReturn %v.
+      uint32_t kill_unreachable_return_value_id;
+      auto function_return_type_inst =
+          donor_ir_context->get_def_use_mgr()->GetDef(
+              function_to_donate->type_id());
+      if (function_return_type_inst->opcode() == SpvOpTypeVoid) {
+        // The return type is void, so we don't need a return value.
+        kill_unreachable_return_value_id = 0;
+      } else {
+        // We do need a return value; we use zero.
+        assert(function_return_type_inst->opcode() != SpvOpTypePointer &&
+               "Function return type must not be a pointer.");
+        kill_unreachable_return_value_id =
+            FindOrCreateZeroConstant(original_id_to_donated_id->at(
+                function_return_type_inst->result_id()));
+      }
+      // Add the function in a livesafe manner.
+      ApplyTransformation(TransformationAddFunction(
+          donated_instructions, loop_limiter_variable_id, loop_limit,
+          loop_limiters, kill_unreachable_return_value_id,
+          access_chain_clamping_info));
+    } else {
+      // Add the function in a non-livesafe manner.
+      ApplyTransformation(TransformationAddFunction(donated_instructions));
+    }
+  }
+}
+
+std::vector<uint32_t>
+FuzzerPassDonateModules::GetFunctionsInCallGraphTopologicalOrder(
+    opt::IRContext* context) {
+  CallGraph call_graph(context);
+
+  // This is an implementation of Kahn’s algorithm for topological sorting.
+
+  // This is the sorted order of function ids that we will eventually return.
+  std::vector<uint32_t> result;
+
+  // Get a copy of the initial in-degrees of all functions.  The algorithm
+  // involves decrementing these values, hence why we work on a copy.
+  std::map<uint32_t, uint32_t> function_in_degree =
+      call_graph.GetFunctionInDegree();
+
+  // Populate a queue with all those function ids with in-degree zero.
+  std::queue<uint32_t> queue;
+  for (auto& entry : function_in_degree) {
+    if (entry.second == 0) {
+      queue.push(entry.first);
+    }
+  }
+
+  // Pop ids from the queue, adding them to the sorted order and decreasing the
+  // in-degrees of their successors.  A successor who's in-degree becomes zero
+  // gets added to the queue.
+  while (!queue.empty()) {
+    auto next = queue.front();
+    queue.pop();
+    result.push_back(next);
+    for (auto successor : call_graph.GetDirectCallees(next)) {
+      assert(function_in_degree.at(successor) > 0 &&
+             "The in-degree cannot be zero if the function is a successor.");
+      function_in_degree[successor] = function_in_degree.at(successor) - 1;
+      if (function_in_degree.at(successor) == 0) {
+        queue.push(successor);
+      }
+    }
+  }
+
+  assert(result.size() == function_in_degree.size() &&
+         "Every function should appear in the sort.");
+
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_donate_modules.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_donate_modules.h
new file mode 100644
index 0000000..ef529db
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_donate_modules.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_DONATE_MODULES_H_
+#define SOURCE_FUZZ_FUZZER_PASS_DONATE_MODULES_H_
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_pass.h"
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass that randomly adds code from other SPIR-V modules to the module
+// being transformed.
+class FuzzerPassDonateModules : public FuzzerPass {
+ public:
+  FuzzerPassDonateModules(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      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
+  // the fuzzer pass's IR context.  |make_livesafe| dictates whether the
+  // functions of the donated module will be made livesafe (see
+  // FactFunctionIsLivesafe).
+  void DonateSingleModule(opt::IRContext* donor_ir_context, bool make_livesafe);
+
+ private:
+  // Adapts a storage class coming from a donor module so that it will work
+  // in a recipient module, e.g. by changing Uniform to Private.
+  static SpvStorageClass AdaptStorageClass(SpvStorageClass donor_storage_class);
+
+  // Identifies all external instruction set imports in |donor_ir_context| and
+  // populates |original_id_to_donated_id| with a mapping from the donor's id
+  // for such an import to a corresponding import in the recipient.  Aborts if
+  // no such corresponding import is available.
+  void HandleExternalInstructionImports(
+      opt::IRContext* donor_ir_context,
+      std::map<uint32_t, uint32_t>* original_id_to_donated_id);
+
+  // Considers all types, globals, constants and undefs in |donor_ir_context|.
+  // For each instruction, uses |original_to_donated_id| to map its result id to
+  // either (1) the id of an existing identical instruction in the recipient, or
+  // (2) to a fresh id, in which case the instruction is also added to the
+  // recipient (with any operand ids that it uses being remapped via
+  // |original_id_to_donated_id|).
+  void HandleTypesAndValues(
+      opt::IRContext* donor_ir_context,
+      std::map<uint32_t, uint32_t>* original_id_to_donated_id);
+
+  // Assumes that |donor_ir_context| does not exhibit recursion.  Considers the
+  // functions in |donor_ir_context|'s call graph in a reverse-topologically-
+  // sorted order (leaves-to-root), adding each function to the recipient
+  // module, rewritten to use fresh ids and using |original_id_to_donated_id| to
+  // remap ids.  The |make_livesafe| argument captures whether the functions in
+  // the module are required to be made livesafe before being added to the
+  // recipient.
+  void HandleFunctions(opt::IRContext* donor_ir_context,
+                       std::map<uint32_t, uint32_t>* original_id_to_donated_id,
+                       bool make_livesafe);
+
+  // Returns the ids of all functions in |context| in a topological order in
+  // relation to the call graph of |context|, which is assumed to be recursion-
+  // free.
+  static std::vector<uint32_t> GetFunctionsInCallGraphTopologicalOrder(
+      opt::IRContext* context);
+
+  // Functions that supply SPIR-V modules
+  std::vector<fuzzerutil::ModuleSupplier> donor_suppliers_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_DONATE_MODULES_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
index 3df11ae..2caf0c6 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
@@ -416,13 +416,28 @@
           skipped_opcode_count.clear();
         }
 
-        // Consider each operand of the instruction, and add a constant id use
-        // for the operand if relevant.
-        for (uint32_t in_operand_index = 0;
-             in_operand_index < inst.NumInOperands(); in_operand_index++) {
-          MaybeAddConstantIdUse(inst, in_operand_index,
-                                base_instruction_result_id,
-                                skipped_opcode_count, &constant_uses);
+        switch (inst.opcode()) {
+          case SpvOpPhi:
+            // The instruction must not be an OpPhi, as we cannot insert
+            // instructions before an OpPhi.
+            // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902):
+            //  there is scope for being less conservative.
+            break;
+          case SpvOpVariable:
+            // The instruction must not be an OpVariable, the only id that an
+            // OpVariable uses is an initializer id, which has to remain
+            // constant.
+            break;
+          default:
+            // Consider each operand of the instruction, and add a constant id
+            // use for the operand if relevant.
+            for (uint32_t in_operand_index = 0;
+                 in_operand_index < inst.NumInOperands(); in_operand_index++) {
+              MaybeAddConstantIdUse(inst, in_operand_index,
+                                    base_instruction_result_id,
+                                    skipped_opcode_count, &constant_uses);
+            }
+            break;
         }
 
         if (!inst.HasResultId()) {
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
index 1c39da0..26961c8 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
@@ -103,10 +103,23 @@
     }
     phi_index++;
   }
-  // Return false if not all of the ids for extending OpPhi instructions are
-  // needed. This might turn out to be stricter than necessary; perhaps it would
-  // be OK just to not use the ids in this case.
-  return phi_index == static_cast<uint32_t>(phi_ids.size());
+  // We allow some of the ids provided for extending OpPhi instructions to be
+  // unused.  Their presence does no harm, and requiring a perfect match may
+  // make transformations less likely to cleanly apply.
+  return true;
+}
+
+uint32_t MaybeGetBoolConstantId(opt::IRContext* context, bool value) {
+  opt::analysis::Bool bool_type;
+  auto registered_bool_type =
+      context->get_type_mgr()->GetRegisteredType(&bool_type);
+  if (!registered_bool_type) {
+    return 0;
+  }
+  opt::analysis::BoolConstant bool_constant(registered_bool_type->AsBool(),
+                                            value);
+  return context->get_constant_mgr()->FindDeclaredConstant(
+      &bool_constant, context->get_type_mgr()->GetId(&bool_type));
 }
 
 void AddUnreachableEdgeAndUpdateOpPhis(
@@ -119,12 +132,10 @@
          "Precondition on terminator of bb_from is not satisfied");
 
   // Get the id of the boolean constant to be used as the condition.
-  opt::analysis::Bool bool_type;
-  opt::analysis::BoolConstant bool_constant(
-      context->get_type_mgr()->GetRegisteredType(&bool_type)->AsBool(),
-      condition_value);
-  uint32_t bool_id = context->get_constant_mgr()->FindDeclaredConstant(
-      &bool_constant, context->get_type_mgr()->GetId(&bool_type));
+  uint32_t bool_id = MaybeGetBoolConstantId(context, condition_value);
+  assert(
+      bool_id &&
+      "Precondition that condition value must be available is not satisfied");
 
   const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to);
   auto successor = bb_from->terminator()->GetSingleWordInOperand(0);
@@ -147,13 +158,11 @@
         break;
       }
       assert(phi_index < static_cast<uint32_t>(phi_ids.size()) &&
-             "There should be exactly one phi id per OpPhi instruction.");
+             "There should be at least one phi id per OpPhi instruction.");
       inst.AddOperand({SPV_OPERAND_TYPE_ID, {phi_ids[phi_index]}});
       inst.AddOperand({SPV_OPERAND_TYPE_ID, {bb_from->id()}});
       phi_index++;
     }
-    assert(phi_index == static_cast<uint32_t>(phi_ids.size()) &&
-           "There should be exactly one phi id per OpPhi instruction.");
   }
 }
 
@@ -217,6 +226,20 @@
     // We can only make a synonym of an instruction that has a type.
     return false;
   }
+  auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst->type_id());
+  if (type_inst->opcode() == SpvOpTypePointer) {
+    switch (inst->opcode()) {
+      case SpvOpConstantNull:
+      case SpvOpUndef:
+        // We disallow making synonyms of null or undefined pointers.  This is
+        // to provide the property that if the original shader exhibited no bad
+        // pointer accesses, the transformed shader will not either.
+        return false;
+      default:
+        break;
+    }
+  }
+
   // We do not make synonyms of objects that have decorations: if the synonym is
   // not decorated analogously, using the original object vs. its synonymous
   // form may not be equivalent.
@@ -239,40 +262,47 @@
   return result;
 }
 
+uint32_t WalkOneCompositeTypeIndex(opt::IRContext* context,
+                                   uint32_t base_object_type_id,
+                                   uint32_t index) {
+  auto should_be_composite_type =
+      context->get_def_use_mgr()->GetDef(base_object_type_id);
+  assert(should_be_composite_type && "The type should exist.");
+  switch (should_be_composite_type->opcode()) {
+    case SpvOpTypeArray: {
+      auto array_length = GetArraySize(*should_be_composite_type, context);
+      if (array_length == 0 || index >= array_length) {
+        return 0;
+      }
+      return should_be_composite_type->GetSingleWordInOperand(0);
+    }
+    case SpvOpTypeMatrix:
+    case SpvOpTypeVector: {
+      auto count = should_be_composite_type->GetSingleWordInOperand(1);
+      if (index >= count) {
+        return 0;
+      }
+      return should_be_composite_type->GetSingleWordInOperand(0);
+    }
+    case SpvOpTypeStruct: {
+      if (index >= GetNumberOfStructMembers(*should_be_composite_type)) {
+        return 0;
+      }
+      return should_be_composite_type->GetSingleWordInOperand(index);
+    }
+    default:
+      return 0;
+  }
+}
+
 uint32_t WalkCompositeTypeIndices(
     opt::IRContext* context, uint32_t base_object_type_id,
     const google::protobuf::RepeatedField<google::protobuf::uint32>& indices) {
   uint32_t sub_object_type_id = base_object_type_id;
   for (auto index : indices) {
-    auto should_be_composite_type =
-        context->get_def_use_mgr()->GetDef(sub_object_type_id);
-    assert(should_be_composite_type && "The type should exist.");
-    if (SpvOpTypeArray == should_be_composite_type->opcode()) {
-      auto array_length = GetArraySize(*should_be_composite_type, context);
-      if (array_length == 0 || index >= array_length) {
-        return 0;
-      }
-      sub_object_type_id = should_be_composite_type->GetSingleWordInOperand(0);
-    } else if (SpvOpTypeMatrix == should_be_composite_type->opcode()) {
-      auto matrix_column_count =
-          should_be_composite_type->GetSingleWordInOperand(1);
-      if (index >= matrix_column_count) {
-        return 0;
-      }
-      sub_object_type_id = should_be_composite_type->GetSingleWordInOperand(0);
-    } else if (SpvOpTypeStruct == should_be_composite_type->opcode()) {
-      if (index >= GetNumberOfStructMembers(*should_be_composite_type)) {
-        return 0;
-      }
-      sub_object_type_id =
-          should_be_composite_type->GetSingleWordInOperand(index);
-    } else if (SpvOpTypeVector == should_be_composite_type->opcode()) {
-      auto vector_length = should_be_composite_type->GetSingleWordInOperand(1);
-      if (index >= vector_length) {
-        return 0;
-      }
-      sub_object_type_id = should_be_composite_type->GetSingleWordInOperand(0);
-    } else {
+    sub_object_type_id =
+        WalkOneCompositeTypeIndex(context, sub_object_type_id, index);
+    if (!sub_object_type_id) {
       return 0;
     }
   }
@@ -302,7 +332,8 @@
 bool IsValid(opt::IRContext* context) {
   std::vector<uint32_t> binary;
   context->module()->ToBinary(&binary, false);
-  return SpirvTools(context->grammar().target_env()).Validate(binary);
+  SpirvTools tools(context->grammar().target_env());
+  return tools.Validate(binary);
 }
 
 std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context) {
@@ -317,6 +348,180 @@
   return type && !type->AsFunction();
 }
 
+bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id) {
+  bool result = false;
+  ir_context->get_def_use_mgr()->WhileEachUse(
+      block_id,
+      [&result](const opt::Instruction* use_instruction,
+                uint32_t /*unused*/) -> bool {
+        switch (use_instruction->opcode()) {
+          case SpvOpLoopMerge:
+          case SpvOpSelectionMerge:
+            result = true;
+            return false;
+          default:
+            return true;
+        }
+      });
+  return result;
+}
+
+uint32_t FindFunctionType(opt::IRContext* ir_context,
+                          const std::vector<uint32_t>& type_ids) {
+  // Look through the existing types for a match.
+  for (auto& type_or_value : ir_context->types_values()) {
+    if (type_or_value.opcode() != SpvOpTypeFunction) {
+      // We are only interested in function types.
+      continue;
+    }
+    if (type_or_value.NumInOperands() != type_ids.size()) {
+      // Not a match: different numbers of arguments.
+      continue;
+    }
+    // Check whether the return type and argument types match.
+    bool input_operands_match = true;
+    for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) {
+      if (type_ids[i] != type_or_value.GetSingleWordInOperand(i)) {
+        input_operands_match = false;
+        break;
+      }
+    }
+    if (input_operands_match) {
+      // Everything matches.
+      return type_or_value.result_id();
+    }
+  }
+  // No match was found.
+  return 0;
+}
+
+opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id) {
+  for (auto& function : *ir_context->module()) {
+    if (function.result_id() == function_id) {
+      return &function;
+    }
+  }
+  return nullptr;
+}
+
+bool IdIsAvailableAtUse(opt::IRContext* context,
+                        opt::Instruction* use_instruction,
+                        uint32_t use_input_operand_index, uint32_t id) {
+  auto defining_instruction = context->get_def_use_mgr()->GetDef(id);
+  auto enclosing_function =
+      context->get_instr_block(use_instruction)->GetParent();
+  // If the id a function parameter, it needs to be associated with the
+  // function containing the use.
+  if (defining_instruction->opcode() == SpvOpFunctionParameter) {
+    return InstructionIsFunctionParameter(defining_instruction,
+                                          enclosing_function);
+  }
+  if (!context->get_instr_block(id)) {
+    // The id must be at global scope.
+    return true;
+  }
+  if (defining_instruction == use_instruction) {
+    // It is not OK for a definition to use itself.
+    return false;
+  }
+  auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function);
+  if (use_instruction->opcode() == SpvOpPhi) {
+    // In the case where the use is an operand to OpPhi, it is actually the
+    // *parent* block associated with the operand that must be dominated by
+    // the synonym.
+    auto parent_block =
+        use_instruction->GetSingleWordInOperand(use_input_operand_index + 1);
+    return dominator_analysis->Dominates(
+        context->get_instr_block(defining_instruction)->id(), parent_block);
+  }
+  return dominator_analysis->Dominates(defining_instruction, use_instruction);
+}
+
+bool IdIsAvailableBeforeInstruction(opt::IRContext* context,
+                                    opt::Instruction* instruction,
+                                    uint32_t id) {
+  auto defining_instruction = context->get_def_use_mgr()->GetDef(id);
+  auto enclosing_function = context->get_instr_block(instruction)->GetParent();
+  // If the id a function parameter, it needs to be associated with the
+  // function containing the instruction.
+  if (defining_instruction->opcode() == SpvOpFunctionParameter) {
+    return InstructionIsFunctionParameter(defining_instruction,
+                                          enclosing_function);
+  }
+  if (!context->get_instr_block(id)) {
+    // The id is at global scope.
+    return true;
+  }
+  if (defining_instruction == instruction) {
+    // The instruction is not available right before its own definition.
+    return false;
+  }
+  return context->GetDominatorAnalysis(enclosing_function)
+      ->Dominates(defining_instruction, instruction);
+}
+
+bool InstructionIsFunctionParameter(opt::Instruction* instruction,
+                                    opt::Function* function) {
+  if (instruction->opcode() != SpvOpFunctionParameter) {
+    return false;
+  }
+  bool found_parameter = false;
+  function->ForEachParam(
+      [instruction, &found_parameter](opt::Instruction* param) {
+        if (param == instruction) {
+          found_parameter = true;
+        }
+      });
+  return found_parameter;
+}
+
+uint32_t GetTypeId(opt::IRContext* context, uint32_t result_id) {
+  return context->get_def_use_mgr()->GetDef(result_id)->type_id();
+}
+
+uint32_t GetPointeeTypeIdFromPointerType(opt::Instruction* pointer_type_inst) {
+  assert(pointer_type_inst && pointer_type_inst->opcode() == SpvOpTypePointer &&
+         "Precondition: |pointer_type_inst| must be OpTypePointer.");
+  return pointer_type_inst->GetSingleWordInOperand(1);
+}
+
+uint32_t GetPointeeTypeIdFromPointerType(opt::IRContext* context,
+                                         uint32_t pointer_type_id) {
+  return GetPointeeTypeIdFromPointerType(
+      context->get_def_use_mgr()->GetDef(pointer_type_id));
+}
+
+SpvStorageClass GetStorageClassFromPointerType(
+    opt::Instruction* pointer_type_inst) {
+  assert(pointer_type_inst && pointer_type_inst->opcode() == SpvOpTypePointer &&
+         "Precondition: |pointer_type_inst| must be OpTypePointer.");
+  return static_cast<SpvStorageClass>(
+      pointer_type_inst->GetSingleWordInOperand(0));
+}
+
+SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context,
+                                               uint32_t pointer_type_id) {
+  return GetStorageClassFromPointerType(
+      context->get_def_use_mgr()->GetDef(pointer_type_id));
+}
+
+uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
+                             SpvStorageClass storage_class) {
+  for (auto& inst : context->types_values()) {
+    switch (inst.opcode()) {
+      case SpvOpTypePointer:
+        if (inst.GetSingleWordInOperand(0) == storage_class &&
+            inst.GetSingleWordInOperand(1) == pointee_type_id) {
+          return inst.result_id();
+        }
+        break;
+      default:
+        break;
+    }
+  }
+  return 0;
+}
+
 }  // namespace fuzzerutil
 
 }  // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
index af3eb1b..daa836c 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
@@ -25,9 +25,12 @@
 namespace spvtools {
 namespace fuzz {
 
-// Provides global utility methods for use by the fuzzer
+// Provides types and global utility methods for use by the fuzzer
 namespace fuzzerutil {
 
+// Function type that produces a SPIR-V module.
+using ModuleSupplier = std::function<std::unique_ptr<opt::IRContext>()>;
+
 // Returns true if and only if the module does not define the given id.
 bool IsFreshId(opt::IRContext* context, uint32_t id);
 
@@ -49,8 +52,13 @@
     opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
     const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
 
-// Requires that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) holds,
-// and that bb_from ends with "OpBranch %some_block".  Turns OpBranch into
+// Returns the id of a boolean constant with value |value| if it exists in the
+// module, or 0 otherwise.
+uint32_t MaybeGetBoolConstantId(opt::IRContext* context, bool value);
+
+// Requires that a boolean constant with value |condition_value| is available,
+// that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) holds, and that
+// bb_from ends with "OpBranch %some_block".  Turns OpBranch into
 // "OpBranchConditional |condition_value| ...", such that control will branch
 // to %some_block, with |bb_to| being the unreachable alternative.  Updates
 // OpPhi instructions in |bb_to| using |phi_ids| so that the new edge is valid.
@@ -90,6 +98,21 @@
 std::vector<uint32_t> RepeatedFieldToVector(
     const google::protobuf::RepeatedField<uint32_t>& repeated_field);
 
+// Given a type id, |base_object_type_id|, returns 0 if the type is not a
+// composite type or if |index| is too large to be used as an index into the
+// composite.  Otherwise returns the type id of the type associated with the
+// composite's index.
+//
+// Example: if |base_object_type_id| is 10, and we have:
+//
+// %10 = OpTypeStruct %3 %4 %5
+//
+// then 3 will be returned if |index| is 0, 5 if |index| is 2, and 0 if index
+// is 3 or larger.
+uint32_t WalkOneCompositeTypeIndex(opt::IRContext* context,
+                                   uint32_t base_object_type_id,
+                                   uint32_t index);
+
 // Given a type id, |base_object_type_id|, checks that the given sequence of
 // |indices| is suitable for indexing into this type.  Returns the id of the
 // type of the final sub-object reached via the indices if they are valid, and
@@ -120,6 +143,64 @@
 // type.
 bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id);
 
+// Returns true if and only if |block_id| is a merge block or continue target
+bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id);
+
+// Returns the result id of an instruction of the form:
+//  %id = OpTypeFunction |type_ids|
+// or 0 if no such instruction exists.
+uint32_t FindFunctionType(opt::IRContext* ir_context,
+                          const std::vector<uint32_t>& type_ids);
+
+// Returns the function with result id |function_id|, or |nullptr| if no such
+// function exists.
+opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id);
+
+// Checks whether |id| is available (according to dominance rules) at the use
+// point defined by input operand |use_input_operand_index| of
+// |use_instruction|.
+bool IdIsAvailableAtUse(opt::IRContext* context,
+                        opt::Instruction* use_instruction,
+                        uint32_t use_input_operand_index, uint32_t id);
+
+// Checks whether |id| is available (according to dominance rules) at the
+// program point directly before |instruction|.
+bool IdIsAvailableBeforeInstruction(opt::IRContext* context,
+                                    opt::Instruction* instruction, uint32_t id);
+
+// Returns true if and only if |instruction| is an OpFunctionParameter
+// associated with |function|.
+bool InstructionIsFunctionParameter(opt::Instruction* instruction,
+                                    opt::Function* function);
+
+// Returns the type id of the instruction defined by |result_id|, or 0 if there
+// is no such result id.
+uint32_t GetTypeId(opt::IRContext* context, uint32_t result_id);
+
+// Given |pointer_type_inst|, which must be an OpTypePointer instruction,
+// returns the id of the associated pointee type.
+uint32_t GetPointeeTypeIdFromPointerType(opt::Instruction* pointer_type_inst);
+
+// Given |pointer_type_id|, which must be the id of a pointer type, returns the
+// id of the associated pointee type.
+uint32_t GetPointeeTypeIdFromPointerType(opt::IRContext* context,
+                                         uint32_t pointer_type_id);
+
+// Given |pointer_type_inst|, which must be an OpTypePointer instruction,
+// returns the associated storage class.
+SpvStorageClass GetStorageClassFromPointerType(
+    opt::Instruction* pointer_type_inst);
+
+// Given |pointer_type_id|, which must be the id of a pointer type, returns the
+// associated storage class.
+SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context,
+                                               uint32_t pointer_type_id);
+
+// Returns the id of a pointer with pointee type |pointee_type_id| and storage
+// class |storage_class|, if it exists, and 0 otherwise.
+uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
+                             SpvStorageClass storage_class);
+
 }  // namespace fuzzerutil
 
 }  // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/instruction_message.cpp b/third_party/SPIRV-Tools/source/fuzz/instruction_message.cpp
index b217a21..44777ae 100644
--- a/third_party/SPIRV-Tools/source/fuzz/instruction_message.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/instruction_message.cpp
@@ -21,16 +21,15 @@
 
 protobufs::Instruction MakeInstructionMessage(
     SpvOp opcode, uint32_t result_type_id, uint32_t result_id,
-    const std::vector<std::pair<uint32_t, std::vector<uint32_t>>>&
-        input_operands) {
+    const opt::Instruction::OperandList& input_operands) {
   protobufs::Instruction result;
   result.set_opcode(opcode);
   result.set_result_type_id(result_type_id);
   result.set_result_id(result_id);
   for (auto& operand : input_operands) {
     auto operand_message = result.add_input_operand();
-    operand_message->set_operand_type(operand.first);
-    for (auto operand_word : operand.second) {
+    operand_message->set_operand_type(static_cast<uint32_t>(operand.type));
+    for (auto operand_word : operand.words) {
       operand_message->add_operand_data(operand_word);
     }
   }
diff --git a/third_party/SPIRV-Tools/source/fuzz/instruction_message.h b/third_party/SPIRV-Tools/source/fuzz/instruction_message.h
index ed339aa..c010c2f 100644
--- a/third_party/SPIRV-Tools/source/fuzz/instruction_message.h
+++ b/third_party/SPIRV-Tools/source/fuzz/instruction_message.h
@@ -27,8 +27,7 @@
 // Creates an Instruction protobuf message from its component parts.
 protobufs::Instruction MakeInstructionMessage(
     SpvOp opcode, uint32_t result_type_id, uint32_t result_id,
-    const std::vector<std::pair<uint32_t, std::vector<uint32_t>>>&
-        input_operands);
+    const opt::Instruction::OperandList& input_operands);
 
 // Creates and returns an opt::Instruction from protobuf message
 // |instruction_message|, relative to |ir_context|.  In the process, the module
diff --git a/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
index dbd1fb8..9773b60 100644
--- a/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -166,6 +166,9 @@
     // Order the fact options by numeric id (rather than alphabetically).
     FactConstantUniform constant_uniform_fact = 1;
     FactDataSynonym data_synonym_fact = 2;
+    FactBlockIsDead block_is_dead_fact = 3;
+    FactFunctionIsLivesafe function_is_livesafe_fact = 4;
+    FactPointeeValueIsIrrelevant pointee_value_is_irrelevant = 5;
   }
 }
 
@@ -200,6 +203,97 @@
 
 }
 
+message FactBlockIsDead {
+
+  // Records the fact that a block is guaranteed to be dynamically unreachable.
+  // This is useful because it informs the fuzzer that rather arbitrary changes
+  // can be made to this block.
+
+  uint32 block_id = 1;
+}
+
+message FactFunctionIsLivesafe {
+
+  // Records the fact that a function is guaranteed to be "livesafe", meaning
+  // that it will not make out-of-bounds accesses, does not contain reachable
+  // OpKill or OpUnreachable instructions, does not contain loops that will
+  // execute for large numbers of iterations, and only invokes other livesafe
+  // functions.
+
+  uint32 function_id = 1;
+}
+
+message FactPointeeValueIsIrrelevant {
+
+  // Records the fact that value of the data pointed to by a pointer id does
+  // not influence the observable behaviour of the module.  This means that
+  // arbitrary stores can be made through the pointer, and that nothing can be
+  // guaranteed about the values that are loaded via the pointer.
+
+  // A result id of pointer type
+  uint32 pointer_id = 1;
+}
+
+message AccessChainClampingInfo {
+
+  // When making a function livesafe it is necessary to clamp the indices that
+  // occur as operands to access chain instructions so that they are guaranteed
+  // to be in bounds.  This message type allows an access chain instruction to
+  // have an associated sequence of ids that are reserved for comparing an
+  // access chain index with a bound (e.g. an array size), and selecting
+  // between the access chain index (if it is within bounds) and the bound (if
+  // it is not).
+  //
+  // This allows turning an instruction of the form:
+  //
+  // %result = OpAccessChain %type %object ... %index ...
+  //
+  // into:
+  //
+  // %t1 = OpULessThanEqual %bool %index %bound_minus_one
+  // %t2 = OpSelect %int_type %t1 %index %bound_minus_one
+  // %result = OpAccessChain %type %object ... %t2 ...
+
+  // The result id of an OpAccessChain or OpInBoundsAccessChain instruction.
+  uint32 access_chain_id = 1;
+
+  // A series of pairs of fresh ids, one per access chain index, for the results
+  // of a compare instruction and a select instruction, serving the roles of %t1
+  // and %t2 in the above example.
+  repeated UInt32Pair compare_and_select_ids = 2;
+
+}
+
+message LoopLimiterInfo {
+
+  // Structure capturing the information required to manipulate a loop limiter
+  // at a loop header.
+
+  // The header for the loop.
+  uint32 loop_header_id = 1;
+
+  // A fresh id into which the loop limiter's current value can be loaded.
+  uint32 load_id = 2;
+
+  // A fresh id that can be used to increment the loaded value by 1.
+  uint32 increment_id = 3;
+
+  // A fresh id that can be used to compare the loaded value with the loop
+  // limit.
+  uint32 compare_id = 4;
+
+  // A fresh id that can be used to compute the conjunction or disjunction of
+  // an original loop exit condition with |compare_id|, if the loop's back edge
+  // block can conditionally exit the loop.
+  uint32 logical_op_id = 5;
+
+  // A sequence of ids suitable for extending OpPhi instructions of the loop
+  // merge block if it did not previously have an incoming edge from the loop
+  // back edge block.
+  repeated uint32 phi_id = 6;
+
+}
+
 message TransformationSequence {
   repeated Transformation transformation = 1;
 }
@@ -242,12 +336,37 @@
     TransformationAddGlobalVariable add_global_variable = 31;
     TransformationAddGlobalUndef add_global_undef = 32;
     TransformationAddFunction add_function = 33;
+    TransformationAddDeadBlock add_dead_block = 34;
+    TransformationAddLocalVariable add_local_variable = 35;
+    TransformationLoad load = 36;
+    TransformationStore store = 37;
+    TransformationFunctionCall function_call = 38;
+    TransformationAccessChain access_chain = 39;
     // Add additional option using the next available number.
   }
 }
 
 // Keep transformation message types in alphabetical order:
 
+message TransformationAccessChain {
+
+  // Adds an access chain instruction based on a given pointer and indices.
+
+  // Result id for the access chain
+  uint32 fresh_id = 1;
+
+  // The pointer from which the access chain starts
+  uint32 pointer_id = 2;
+
+  // Zero or more access chain indices
+  repeated uint32 index_id = 3;
+
+  // A descriptor for an instruction in a block before which the new
+  // OpAccessChain instruction should be inserted
+  InstructionDescriptor instruction_to_insert_before = 4;
+
+}
+
 message TransformationAddConstantBoolean {
 
   // Supports adding the constants true and false to a module, which may be
@@ -275,7 +394,7 @@
 
 message TransformationAddConstantScalar {
 
-  // Adds a constant of the given scalar type
+  // Adds a constant of the given scalar type.
 
   // Id for the constant
   uint32 fresh_id = 1;
@@ -288,6 +407,25 @@
 
 }
 
+message TransformationAddDeadBlock {
+
+  // Adds a new block to the module that is statically reachable from an
+  // existing block, but dynamically unreachable.
+
+  // Fresh id for the dead block
+  uint32 fresh_id = 1;
+
+  // Id of an existing block terminated with OpBranch, such that this OpBranch
+  // can be replaced with an OpBranchConditional to its exiting successor or
+  // the dead block
+  uint32 existing_block = 2;
+
+  // Determines whether the condition associated with the OpBranchConditional
+  // is true or false
+  bool condition_value = 3;
+
+}
+
 message TransformationAddDeadBreak {
 
   // A transformation that turns a basic block that unconditionally branches to
@@ -336,6 +474,33 @@
   // The series of instructions that comprise the function.
   repeated Instruction instruction = 1;
 
+  // True if and only if the given function should be made livesafe (see
+  // FactFunctionIsLivesafe for definition).
+  bool is_livesafe = 2;
+
+  // Fresh id for a new variable that will serve as a "loop limiter" for the
+  // function; only relevant if |is_livesafe| holds.
+  uint32 loop_limiter_variable_id = 3;
+
+  // Id of an existing unsigned integer constant providing the maximum value
+  // that the loop limiter can reach before the loop is broken from; only
+  // relevant if |is_livesafe| holds.
+  uint32 loop_limit_constant_id = 4;
+
+  // Fresh ids for each loop in the function that allow the loop limiter to be
+  // manipulated; only relevant if |is_livesafe| holds.
+  repeated LoopLimiterInfo loop_limiter_info = 5;
+
+  // Id of an existing global value with the same return type as the function
+  // that can be used to replace OpKill and OpReachable instructions with
+  // ReturnValue instructions.  Ignored if the function has void return type.
+  uint32 kill_unreachable_return_value_id = 6;
+
+  // A mapping (represented as a sequence) from every access chain result id in
+  // the function to the ids required to clamp its indices to ensure they are in
+  // bounds.
+  repeated AccessChainClampingInfo access_chain_clamping_info = 7;
+
 }
 
 message TransformationAddGlobalUndef {
@@ -361,9 +526,39 @@
   // The type of the global variable
   uint32 type_id = 2;
 
-  // Optional initializer; 0 if there is no initializer
+  // Initial value of the variable
   uint32 initializer_id = 3;
 
+  // True if and only if the behaviour of the module should not depend on the
+  // value of the variable, in which case stores to the variable can be
+  // performed in an arbitrary fashion.
+  bool value_is_irrelevant = 4;
+
+}
+
+message TransformationAddLocalVariable {
+
+  // Adds a local variable of the given type (which must be a pointer with
+  // Function storage class) to the given function, initialized to the given
+  // id.
+
+  // Fresh id for the local variable
+  uint32 fresh_id = 1;
+
+  // The type of the local variable
+  uint32 type_id = 2;
+
+  // The id of the function to which the local variable should be added
+  uint32 function_id = 3;
+
+  // Initial value of the variable
+  uint32 initializer_id = 4;
+
+  // True if and only if the behaviour of the module should not depend on the
+  // value of the variable, in which case stores to the variable can be
+  // performed in an arbitrary fashion.
+  bool value_is_irrelevant = 5;
+
 }
 
 message TransformationAddNoContractionDecoration {
@@ -557,6 +752,45 @@
 
 }
 
+message TransformationFunctionCall {
+
+  // A transformation that introduces an OpFunctionCall instruction.  The call
+  // must not make the module's call graph cyclic.  Beyond that, if the call
+  // is in a dead block it can be to any function with arbitrary suitably-typed
+  // arguments; otherwise it must be to a livesafe function, with injected
+  // variables as pointer arguments and arbitrary non-pointer arguments.
+
+  // A fresh id for the result of the call
+  uint32 fresh_id = 1;
+
+  // Id of the function to be called
+  uint32 callee_id = 2;
+
+  // Ids for arguments to the function
+  repeated uint32 argument_id = 3;
+
+  // A descriptor for an instruction in a block before which the new
+  // OpFunctionCall instruction should be inserted
+  InstructionDescriptor instruction_to_insert_before = 4;
+
+}
+
+message TransformationLoad {
+
+  // Transformation that adds an OpLoad instruction from a pointer into an id.
+
+  // The result of the load instruction
+  uint32 fresh_id = 1;
+
+  // The pointer to be loaded from
+  uint32 pointer_id = 2;
+
+  // A descriptor for an instruction in a block before which the new OpLoad
+  // instruction should be inserted
+  InstructionDescriptor instruction_to_insert_before = 3;
+
+}
+
 message TransformationMergeBlocks {
 
   // A transformation that merges a block with its predecessor.
@@ -769,6 +1003,22 @@
 
 }
 
+message TransformationStore {
+
+  // Transformation that adds an OpStore instruction of an id to a pointer.
+
+  // The pointer to be stored to
+  uint32 pointer_id = 1;
+
+  // The value to be stored
+  uint32 value_id = 2;
+
+  // A descriptor for an instruction in a block before which the new OpStore
+  // instruction should be inserted
+  InstructionDescriptor instruction_to_insert_before = 3;
+
+}
+
 message TransformationVectorShuffle {
 
   // A transformation that adds a vector shuffle instruction.
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
index 1489f85..52fcfd7 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
@@ -16,14 +16,18 @@
 
 #include <cassert>
 
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_access_chain.h"
 #include "source/fuzz/transformation_add_constant_boolean.h"
 #include "source/fuzz/transformation_add_constant_composite.h"
 #include "source/fuzz/transformation_add_constant_scalar.h"
+#include "source/fuzz/transformation_add_dead_block.h"
 #include "source/fuzz/transformation_add_dead_break.h"
 #include "source/fuzz/transformation_add_dead_continue.h"
 #include "source/fuzz/transformation_add_function.h"
 #include "source/fuzz/transformation_add_global_undef.h"
 #include "source/fuzz/transformation_add_global_variable.h"
+#include "source/fuzz/transformation_add_local_variable.h"
 #include "source/fuzz/transformation_add_no_contraction_decoration.h"
 #include "source/fuzz/transformation_add_type_array.h"
 #include "source/fuzz/transformation_add_type_boolean.h"
@@ -37,6 +41,8 @@
 #include "source/fuzz/transformation_composite_construct.h"
 #include "source/fuzz/transformation_composite_extract.h"
 #include "source/fuzz/transformation_copy_object.h"
+#include "source/fuzz/transformation_function_call.h"
+#include "source/fuzz/transformation_load.h"
 #include "source/fuzz/transformation_merge_blocks.h"
 #include "source/fuzz/transformation_move_block_down.h"
 #include "source/fuzz/transformation_outline_function.h"
@@ -48,6 +54,7 @@
 #include "source/fuzz/transformation_set_memory_operands_mask.h"
 #include "source/fuzz/transformation_set_selection_control.h"
 #include "source/fuzz/transformation_split_block.h"
+#include "source/fuzz/transformation_store.h"
 #include "source/fuzz/transformation_vector_shuffle.h"
 #include "source/util/make_unique.h"
 
@@ -59,6 +66,8 @@
 std::unique_ptr<Transformation> Transformation::FromMessage(
     const protobufs::Transformation& message) {
   switch (message.transformation_case()) {
+    case protobufs::Transformation::TransformationCase::kAccessChain:
+      return MakeUnique<TransformationAccessChain>(message.access_chain());
     case protobufs::Transformation::TransformationCase::kAddConstantBoolean:
       return MakeUnique<TransformationAddConstantBoolean>(
           message.add_constant_boolean());
@@ -68,6 +77,8 @@
     case protobufs::Transformation::TransformationCase::kAddConstantScalar:
       return MakeUnique<TransformationAddConstantScalar>(
           message.add_constant_scalar());
+    case protobufs::Transformation::TransformationCase::kAddDeadBlock:
+      return MakeUnique<TransformationAddDeadBlock>(message.add_dead_block());
     case protobufs::Transformation::TransformationCase::kAddDeadBreak:
       return MakeUnique<TransformationAddDeadBreak>(message.add_dead_break());
     case protobufs::Transformation::TransformationCase::kAddDeadContinue:
@@ -81,6 +92,9 @@
     case protobufs::Transformation::TransformationCase::kAddGlobalVariable:
       return MakeUnique<TransformationAddGlobalVariable>(
           message.add_global_variable());
+    case protobufs::Transformation::TransformationCase::kAddLocalVariable:
+      return MakeUnique<TransformationAddLocalVariable>(
+          message.add_local_variable());
     case protobufs::Transformation::TransformationCase::
         kAddNoContractionDecoration:
       return MakeUnique<TransformationAddNoContractionDecoration>(
@@ -114,6 +128,10 @@
           message.composite_extract());
     case protobufs::Transformation::TransformationCase::kCopyObject:
       return MakeUnique<TransformationCopyObject>(message.copy_object());
+    case protobufs::Transformation::TransformationCase::kFunctionCall:
+      return MakeUnique<TransformationFunctionCall>(message.function_call());
+    case protobufs::Transformation::TransformationCase::kLoad:
+      return MakeUnique<TransformationLoad>(message.load());
     case protobufs::Transformation::TransformationCase::kMergeBlocks:
       return MakeUnique<TransformationMergeBlocks>(message.merge_blocks());
     case protobufs::Transformation::TransformationCase::kMoveBlockDown:
@@ -146,6 +164,8 @@
           message.set_selection_control());
     case protobufs::Transformation::TransformationCase::kSplitBlock:
       return MakeUnique<TransformationSplitBlock>(message.split_block());
+    case protobufs::Transformation::TransformationCase::kStore:
+      return MakeUnique<TransformationStore>(message.store());
     case protobufs::Transformation::TransformationCase::kVectorShuffle:
       return MakeUnique<TransformationVectorShuffle>(message.vector_shuffle());
     case protobufs::Transformation::TRANSFORMATION_NOT_SET:
@@ -156,5 +176,18 @@
   return nullptr;
 }
 
+bool Transformation::CheckIdIsFreshAndNotUsedByThisTransformation(
+    uint32_t id, opt::IRContext* context,
+    std::set<uint32_t>* ids_used_by_this_transformation) {
+  if (!fuzzerutil::IsFreshId(context, id)) {
+    return false;
+  }
+  if (ids_used_by_this_transformation->count(id) != 0) {
+    return false;
+  }
+  ids_used_by_this_transformation->insert(id);
+  return true;
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation.h b/third_party/SPIRV-Tools/source/fuzz/transformation.h
index c6b852f..dbe803f 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation.h
@@ -83,6 +83,15 @@
   // representation of a transformation given by |message|.
   static std::unique_ptr<Transformation> FromMessage(
       const protobufs::Transformation& message);
+
+  // Helper that returns true if and only if (a) |id| is a fresh id for the
+  // module, and (b) |id| is not in |ids_used_by_this_transformation|, a set of
+  // ids already known to be in use by a transformation.  This is useful when
+  // checking id freshness for a transformation that uses many ids, all of which
+  // must be distinct.
+  static bool CheckIdIsFreshAndNotUsedByThisTransformation(
+      uint32_t id, opt::IRContext* context,
+      std::set<uint32_t>* ids_used_by_this_transformation);
 };
 
 }  // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.cpp
new file mode 100644
index 0000000..8c31006
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.cpp
@@ -0,0 +1,215 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_access_chain.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAccessChain::TransformationAccessChain(
+    const spvtools::fuzz::protobufs::TransformationAccessChain& message)
+    : message_(message) {}
+
+TransformationAccessChain::TransformationAccessChain(
+    uint32_t fresh_id, uint32_t pointer_id,
+    const std::vector<uint32_t>& index_id,
+    const protobufs::InstructionDescriptor& instruction_to_insert_before) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_pointer_id(pointer_id);
+  for (auto id : index_id) {
+    message_.add_index_id(id);
+  }
+  *message_.mutable_instruction_to_insert_before() =
+      instruction_to_insert_before;
+}
+
+bool TransformationAccessChain::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // The result id must be fresh
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  // The pointer id must exist and have a type.
+  auto pointer = context->get_def_use_mgr()->GetDef(message_.pointer_id());
+  if (!pointer || !pointer->type_id()) {
+    return false;
+  }
+  // The type must indeed be a pointer
+  auto pointer_type = context->get_def_use_mgr()->GetDef(pointer->type_id());
+  if (pointer_type->opcode() != SpvOpTypePointer) {
+    return false;
+  }
+
+  // The described instruction to insert before must exist and be a suitable
+  // point where an OpAccessChain instruction could be inserted.
+  auto instruction_to_insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  if (!instruction_to_insert_before) {
+    return false;
+  }
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+          SpvOpAccessChain, instruction_to_insert_before)) {
+    return false;
+  }
+
+  // Do not allow making an access chain from a null or undefined pointer, as
+  // we do not want to allow accessing such pointers.  This might be acceptable
+  // in dead blocks, but we conservatively avoid it.
+  switch (pointer->opcode()) {
+    case SpvOpConstantNull:
+    case SpvOpUndef:
+      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3185): When
+      //  fuzzing for real we would like an 'assert(false)' here.  But we also
+      //  want to be able to write negative unit tests.
+      return false;
+    default:
+      break;
+  }
+
+  // The pointer on which the access chain is to be based needs to be available
+  // (according to dominance rules) at the insertion point.
+  if (!fuzzerutil::IdIsAvailableBeforeInstruction(
+          context, instruction_to_insert_before, message_.pointer_id())) {
+    return false;
+  }
+
+  // We now need to use the given indices to walk the type structure of the
+  // base type of the pointer, making sure that (a) the indices correspond to
+  // integers, and (b) these integer values are in-bounds.
+
+  // Start from the base type of the pointer.
+  uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
+
+  // Consider the given index ids in turn.
+  for (auto index_id : message_.index_id()) {
+    // Try to get the integer value associated with this index is.  The first
+    // component of the result will be false if the id did not correspond to an
+    // integer.  Otherwise, the integer with which the id is associated is the
+    // second component.
+    std::pair<bool, uint32_t> maybe_index_value =
+        GetIndexValue(context, index_id);
+    if (!maybe_index_value.first) {
+      // There was no integer: this index is no good.
+      return false;
+    }
+    // Try to walk down the type using this index.  This will yield 0 if the
+    // type is not a composite or the index is out of bounds, and the id of
+    // the next type otherwise.
+    subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
+        context, subobject_type_id, maybe_index_value.second);
+    if (!subobject_type_id) {
+      // Either the type was not a composite (so that too many indices were
+      // provided), or the index was out of bounds.
+      return false;
+    }
+  }
+  // At this point, |subobject_type_id| is the type of the value targeted by
+  // the new access chain.  The result type of the access chain should be a
+  // pointer to this type, with the same storage class as for the original
+  // pointer.  Such a pointer type needs to exist in the module.
+  //
+  // We do not use the type manager to look up this type, due to problems
+  // associated with pointers to isomorphic structs being regarded as the same.
+  return fuzzerutil::MaybeGetPointerType(
+             context, subobject_type_id,
+             static_cast<SpvStorageClass>(
+                 pointer_type->GetSingleWordInOperand(0))) != 0;
+}
+
+void TransformationAccessChain::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+  // The operands to the access chain are the pointer followed by the indices.
+  // The result type of the access chain is determined by where the indices
+  // lead.  We thus push the pointer to a sequence of operands, and then follow
+  // the indices, pushing each to the operand list and tracking the type
+  // obtained by following it.  Ultimately this yields the type of the
+  // component reached by following all the indices, and the result type is
+  // a pointer to this component type.
+  opt::Instruction::OperandList operands;
+
+  // Add the pointer id itself.
+  operands.push_back({SPV_OPERAND_TYPE_ID, {message_.pointer_id()}});
+
+  // Start walking the indices, starting with the pointer's base type.
+  auto pointer_type = context->get_def_use_mgr()->GetDef(
+      context->get_def_use_mgr()->GetDef(message_.pointer_id())->type_id());
+  uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
+
+  // Go through the index ids in turn.
+  for (auto index_id : message_.index_id()) {
+    // Add the index id to the operands.
+    operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}});
+    // Get the integer value associated with the index id.
+    uint32_t index_value = GetIndexValue(context, index_id).second;
+    // Walk to the next type in the composite object using this index.
+    subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
+        context, subobject_type_id, index_value);
+  }
+  // The access chain's result type is a pointer to the composite component that
+  // was reached after following all indices.  The storage class is that of the
+  // original pointer.
+  uint32_t result_type = fuzzerutil::MaybeGetPointerType(
+      context, subobject_type_id,
+      static_cast<SpvStorageClass>(pointer_type->GetSingleWordInOperand(0)));
+
+  // Add the access chain instruction to the module, and update the module's id
+  // bound.
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  FindInstruction(message_.instruction_to_insert_before(), context)
+      ->InsertBefore(
+          MakeUnique<opt::Instruction>(context, SpvOpAccessChain, result_type,
+                                       message_.fresh_id(), operands));
+
+  // Conservatively invalidate all analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+
+  // If the base pointer's pointee value was irrelevant, the same is true of the
+  // pointee value of the result of this access chain.
+  if (fact_manager->PointeeValueIsIrrelevant(message_.pointer_id())) {
+    fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id());
+  }
+}
+
+protobufs::Transformation TransformationAccessChain::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_access_chain() = message_;
+  return result;
+}
+
+std::pair<bool, uint32_t> TransformationAccessChain::GetIndexValue(
+    opt::IRContext* context, uint32_t index_id) const {
+  auto index_instruction = context->get_def_use_mgr()->GetDef(index_id);
+  if (!index_instruction || !spvOpcodeIsConstant(index_instruction->opcode())) {
+    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3179) We could
+    //  allow non-constant indices when looking up non-structs, using clamping
+    //  to ensure they are in-bounds.
+    return {false, 0};
+  }
+  auto index_type =
+      context->get_def_use_mgr()->GetDef(index_instruction->type_id());
+  if (index_type->opcode() != SpvOpTypeInt ||
+      index_type->GetSingleWordInOperand(0) != 32) {
+    return {false, 0};
+  }
+  return {true, index_instruction->GetSingleWordInOperand(0)};
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.h b/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.h
new file mode 100644
index 0000000..92d9e6a
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ACCESS_CHAIN_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ACCESS_CHAIN_H_
+
+#include <utility>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAccessChain : public Transformation {
+ public:
+  explicit TransformationAccessChain(
+      const protobufs::TransformationAccessChain& message);
+
+  TransformationAccessChain(
+      uint32_t fresh_id, uint32_t pointer_id,
+      const std::vector<uint32_t>& index_id,
+      const protobufs::InstructionDescriptor& instruction_to_insert_before);
+
+  // - |message_.fresh_id| must be fresh
+  // - |message_.instruction_to_insert_before| must identify an instruction
+  //   before which it is legitimate to insert an OpAccessChain instruction
+  // - |message_.pointer_id| must be a result id with pointer type that is
+  //   available (according to dominance rules) at the insertion point.
+  // - The pointer must not be OpConstantNull or OpUndef
+  // - |message_.index_id| must be a sequence of ids of 32-bit integer constants
+  //   such that it is possible to walk the pointee type of
+  //   |message_.pointer_id| using these indices, remaining in-bounds.
+  // - If type t is the final type reached by walking these indices, the module
+  //   must include an instruction "OpTypePointer SC %t" where SC is the storage
+  //   class associated with |message_.pointer_id|
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an instruction of the form:
+  //   |message_.fresh_id| = OpAccessChain %ptr |message_.index_id|
+  // where %ptr is the result if of an instruction declaring a pointer to the
+  // type reached by walking the pointee type of |message_.pointer_id| using
+  // the indices in |message_.index_id|, and with the same storage class as
+  // |message_.pointer_id|.
+  //
+  // If |fact_manager| reports that |message_.pointer_id| has an irrelevant
+  // pointee value, then the fact that |message_.fresh_id| (the result of the
+  // access chain) also has an irrelevant pointee value is also recorded.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  // Returns {false, 0} if |index_id| does not correspond to a 32-bit integer
+  // constant.  Otherwise, returns {true, value}, where value is the value of
+  // the 32-bit integer constant to which |index_id| corresponds.
+  std::pair<bool, uint32_t> GetIndexValue(opt::IRContext* context,
+                                          uint32_t index_id) const;
+
+  protobufs::TransformationAccessChain message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ACCESS_CHAIN_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_block.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_block.cpp
new file mode 100644
index 0000000..b58f75e
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_block.cpp
@@ -0,0 +1,169 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_dead_block.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddDeadBlock::TransformationAddDeadBlock(
+    const spvtools::fuzz::protobufs::TransformationAddDeadBlock& message)
+    : message_(message) {}
+
+TransformationAddDeadBlock::TransformationAddDeadBlock(uint32_t fresh_id,
+                                                       uint32_t existing_block,
+                                                       bool condition_value) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_existing_block(existing_block);
+  message_.set_condition_value(condition_value);
+}
+
+bool TransformationAddDeadBlock::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // The new block's id must be fresh.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+
+  // First, we check that a constant with the same value as
+  // |message_.condition_value| is present.
+  if (!fuzzerutil::MaybeGetBoolConstantId(context,
+                                          message_.condition_value())) {
+    // The required constant is not present, so the transformation cannot be
+    // applied.
+    return false;
+  }
+
+  // The existing block must indeed exist.
+  auto existing_block =
+      fuzzerutil::MaybeFindBlock(context, message_.existing_block());
+  if (!existing_block) {
+    return false;
+  }
+
+  // It must not head a loop.
+  if (existing_block->IsLoopHeader()) {
+    return false;
+  }
+
+  // It must end with OpBranch.
+  if (existing_block->terminator()->opcode() != SpvOpBranch) {
+    return false;
+  }
+
+  // Its successor must not be a merge block nor continue target.
+  auto successor_block_id =
+      existing_block->terminator()->GetSingleWordInOperand(0);
+  if (fuzzerutil::IsMergeOrContinue(context, successor_block_id)) {
+    return false;
+  }
+
+  // The successor must not be a loop header (i.e., |message_.existing_block|
+  // must not be a back-edge block.
+  if (context->cfg()->block(successor_block_id)->IsLoopHeader()) {
+    return false;
+  }
+
+  return true;
+}
+
+void TransformationAddDeadBlock::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+  // Update the module id bound so that it is at least the id of the new block.
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+
+  // Get the existing block and its successor.
+  auto existing_block = context->cfg()->block(message_.existing_block());
+  auto successor_block_id =
+      existing_block->terminator()->GetSingleWordInOperand(0);
+
+  // Get the id of the boolean value that will be used as the branch condition.
+  auto bool_id =
+      fuzzerutil::MaybeGetBoolConstantId(context, message_.condition_value());
+
+  // Make a new block that unconditionally branches to the original successor
+  // block.
+  auto enclosing_function = existing_block->GetParent();
+  std::unique_ptr<opt::BasicBlock> new_block = MakeUnique<opt::BasicBlock>(
+      MakeUnique<opt::Instruction>(context, SpvOpLabel, 0, message_.fresh_id(),
+                                   opt::Instruction::OperandList()));
+  new_block->AddInstruction(MakeUnique<opt::Instruction>(
+      context, SpvOpBranch, 0, 0,
+      opt::Instruction::OperandList(
+          {{SPV_OPERAND_TYPE_ID, {successor_block_id}}})));
+
+  // Turn the original block into a selection merge, with its original successor
+  // as the merge block.
+  existing_block->terminator()->InsertBefore(MakeUnique<opt::Instruction>(
+      context, SpvOpSelectionMerge, 0, 0,
+      opt::Instruction::OperandList(
+          {{SPV_OPERAND_TYPE_ID, {successor_block_id}},
+           {SPV_OPERAND_TYPE_SELECTION_CONTROL,
+            {SpvSelectionControlMaskNone}}})));
+
+  // Change the original block's terminator to be a conditional branch on the
+  // given boolean, with the original successor and the new successor as branch
+  // targets, and such that at runtime control will always transfer to the
+  // original successor.
+  existing_block->terminator()->SetOpcode(SpvOpBranchConditional);
+  existing_block->terminator()->SetInOperands(
+      {{SPV_OPERAND_TYPE_ID, {bool_id}},
+       {SPV_OPERAND_TYPE_ID,
+        {message_.condition_value() ? successor_block_id
+                                    : message_.fresh_id()}},
+       {SPV_OPERAND_TYPE_ID,
+        {message_.condition_value() ? message_.fresh_id()
+                                    : successor_block_id}}});
+
+  // Add the new block to the enclosing function.
+  new_block->SetParent(enclosing_function);
+  enclosing_function->InsertBasicBlockAfter(std::move(new_block),
+                                            existing_block);
+
+  // Record the fact that the new block is dead.
+  fact_manager->AddFactBlockIsDead(message_.fresh_id());
+
+  // Fix up OpPhi instructions in the successor block, so that the values they
+  // yield when control has transferred from the new block are the same as if
+  // control had transferred from |message_.existing_block|.  This is guaranteed
+  // to be valid since |message_.existing_block| dominates the new block by
+  // construction.  Other transformations can change these phi operands to more
+  // interesting values.
+  context->cfg()
+      ->block(successor_block_id)
+      ->ForEachPhiInst([this](opt::Instruction* phi_inst) {
+        // Copy the operand that provides the phi value for the first of any
+        // existing predecessors.
+        opt::Operand copy_of_existing_operand = phi_inst->GetInOperand(0);
+        // Use this as the value associated with the new predecessor.
+        phi_inst->AddOperand(std::move(copy_of_existing_operand));
+        phi_inst->AddOperand({SPV_OPERAND_TYPE_ID, {message_.fresh_id()}});
+      });
+
+  // Do not rely on any existing analysis results since the control flow graph
+  // of the module has changed.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddDeadBlock::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_dead_block() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_block.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_block.h
new file mode 100644
index 0000000..059daca9
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_block.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddDeadBlock : public Transformation {
+ public:
+  explicit TransformationAddDeadBlock(
+      const protobufs::TransformationAddDeadBlock& message);
+
+  TransformationAddDeadBlock(uint32_t fresh_id, uint32_t existing_block,
+                             bool condition_value);
+
+  // - |message_.fresh_id| must be a fresh id
+  // - A constant with the same value as |message_.condition_value| must be
+  //   available
+  // - |message_.existing_block| must be a block that is not a loop header,
+  //   and that ends with OpBranch to a block that is not a merge block nor
+  //   continue target - this is because the successor will become the merge
+  //   block of a selection construct headed at |message_.existing_block|
+  // - |message_.existing_block| must not be a back-edge block, since in this
+  //   case the newly-added block would lead to another back-edge to the
+  //   associated loop header
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Changes the OpBranch from |message_.existing_block| to its successor 's'
+  // to an OpBranchConditional to either 's' or a new block,
+  // |message_.fresh_id|, which itself unconditionally branches to 's'.  The
+  // conditional branch uses |message.condition_value| as its condition, and is
+  // arranged so that control will pass to 's' at runtime.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddDeadBlock message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.cpp
index a37100b..43847fa 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.cpp
@@ -111,15 +111,8 @@
     opt::IRContext* context, const FactManager& /*unused*/) const {
   // First, we check that a constant with the same value as
   // |message_.break_condition_value| is present.
-  opt::analysis::Bool bool_type;
-  auto registered_bool_type =
-      context->get_type_mgr()->GetRegisteredType(&bool_type);
-  if (!registered_bool_type) {
-    return false;
-  }
-  opt::analysis::BoolConstant bool_constant(registered_bool_type->AsBool(),
-                                            message_.break_condition_value());
-  if (!context->get_constant_mgr()->FindConstant(&bool_constant)) {
+  if (!fuzzerutil::MaybeGetBoolConstantId(context,
+                                          message_.break_condition_value())) {
     // The required constant is not present, so the transformation cannot be
     // applied.
     return false;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp
index 0aacc5b..ffa182e 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp
@@ -37,15 +37,8 @@
     opt::IRContext* context, const FactManager& /*unused*/) const {
   // First, we check that a constant with the same value as
   // |message_.continue_condition_value| is present.
-  opt::analysis::Bool bool_type;
-  auto registered_bool_type =
-      context->get_type_mgr()->GetRegisteredType(&bool_type);
-  if (!registered_bool_type) {
-    return false;
-  }
-  opt::analysis::BoolConstant bool_constant(
-      registered_bool_type->AsBool(), message_.continue_condition_value());
-  if (!context->get_constant_mgr()->FindConstant(&bool_constant)) {
+  if (!fuzzerutil::MaybeGetBoolConstantId(
+          context, message_.continue_condition_value())) {
     // The required constant is not present, so the transformation cannot be
     // applied.
     return false;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.cpp
index 5e53961..8f0d3c9 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.cpp
@@ -29,11 +29,95 @@
   for (auto& instruction : instructions) {
     *message_.add_instruction() = instruction;
   }
+  message_.set_is_livesafe(false);
+}
+
+TransformationAddFunction::TransformationAddFunction(
+    const std::vector<protobufs::Instruction>& instructions,
+    uint32_t loop_limiter_variable_id, uint32_t loop_limit_constant_id,
+    const std::vector<protobufs::LoopLimiterInfo>& loop_limiters,
+    uint32_t kill_unreachable_return_value_id,
+    const std::vector<protobufs::AccessChainClampingInfo>&
+        access_chain_clampers) {
+  for (auto& instruction : instructions) {
+    *message_.add_instruction() = instruction;
+  }
+  message_.set_is_livesafe(true);
+  message_.set_loop_limiter_variable_id(loop_limiter_variable_id);
+  message_.set_loop_limit_constant_id(loop_limit_constant_id);
+  for (auto& loop_limiter : loop_limiters) {
+    *message_.add_loop_limiter_info() = loop_limiter;
+  }
+  message_.set_kill_unreachable_return_value_id(
+      kill_unreachable_return_value_id);
+  for (auto& access_clamper : access_chain_clampers) {
+    *message_.add_access_chain_clamping_info() = access_clamper;
+  }
 }
 
 bool TransformationAddFunction::IsApplicable(
     opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    const spvtools::fuzz::FactManager& fact_manager) const {
+  // This transformation may use a lot of ids, all of which need to be fresh
+  // and distinct.  This set tracks them.
+  std::set<uint32_t> ids_used_by_this_transformation;
+
+  // Ensure that all result ids in the new function are fresh and distinct.
+  for (auto& instruction : message_.instruction()) {
+    if (instruction.result_id()) {
+      if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+              instruction.result_id(), context,
+              &ids_used_by_this_transformation)) {
+        return false;
+      }
+    }
+  }
+
+  if (message_.is_livesafe()) {
+    // Ensure that all ids provided for making the function livesafe are fresh
+    // and distinct.
+    if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+            message_.loop_limiter_variable_id(), context,
+            &ids_used_by_this_transformation)) {
+      return false;
+    }
+    for (auto& loop_limiter_info : message_.loop_limiter_info()) {
+      if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+              loop_limiter_info.load_id(), context,
+              &ids_used_by_this_transformation)) {
+        return false;
+      }
+      if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+              loop_limiter_info.increment_id(), context,
+              &ids_used_by_this_transformation)) {
+        return false;
+      }
+      if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+              loop_limiter_info.compare_id(), context,
+              &ids_used_by_this_transformation)) {
+        return false;
+      }
+      if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+              loop_limiter_info.logical_op_id(), context,
+              &ids_used_by_this_transformation)) {
+        return false;
+      }
+    }
+    for (auto& access_chain_clamping_info :
+         message_.access_chain_clamping_info()) {
+      for (auto& pair : access_chain_clamping_info.compare_and_select_ids()) {
+        if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+                pair.first(), context, &ids_used_by_this_transformation)) {
+          return false;
+        }
+        if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+                pair.second(), context, &ids_used_by_this_transformation)) {
+          return false;
+        }
+      }
+    }
+  }
+
   // Because checking all the conditions for a function to be valid is a big
   // job that the SPIR-V validator can already do, a "try it and see" approach
   // is taken here.
@@ -47,18 +131,83 @@
   if (!TryToAddFunction(cloned_module.get())) {
     return false;
   }
-  // Having managed to add the new function to the cloned module, we ascertain
-  // whether the cloned module is still valid.  If it is, the transformation is
-  // applicable.
-  return fuzzerutil::IsValid(cloned_module.get());
+
+  // Check whether the cloned module is still valid after adding the function.
+  // If it is not, the transformation is not applicable.
+  if (!fuzzerutil::IsValid(cloned_module.get())) {
+    return false;
+  }
+
+  if (message_.is_livesafe()) {
+    if (!TryToMakeFunctionLivesafe(cloned_module.get(), fact_manager)) {
+      return false;
+    }
+    // After making the function livesafe, we check validity of the module
+    // again.  This is because the turning of OpKill, OpUnreachable and OpReturn
+    // instructions into branches changes control flow graph reachability, which
+    // has the potential to make the module invalid when it was otherwise valid.
+    // It is simpler to rely on the validator to guard against this than to
+    // consider all scenarios when making a function livesafe.
+    if (!fuzzerutil::IsValid(cloned_module.get())) {
+      return false;
+    }
+  }
+  return true;
 }
 
 void TransformationAddFunction::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
-  auto success = TryToAddFunction(context);
+    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+  // Add the function to the module.  As the transformation is applicable, this
+  // should succeed.
+  bool success = TryToAddFunction(context);
   assert(success && "The function should be successfully added.");
   (void)(success);  // Keep release builds happy (otherwise they may complain
                     // that |success| is not used).
+
+  // Record the fact that all pointer parameters and variables declared in the
+  // function should be regarded as having irrelevant values.  This allows other
+  // passes to store arbitrarily to such variables, and to pass them freely as
+  // parameters to other functions knowing that it is OK if they get
+  // over-written.
+  for (auto& instruction : message_.instruction()) {
+    switch (instruction.opcode()) {
+      case SpvOpFunctionParameter:
+        if (context->get_def_use_mgr()
+                ->GetDef(instruction.result_type_id())
+                ->opcode() == SpvOpTypePointer) {
+          fact_manager->AddFactValueOfPointeeIsIrrelevant(
+              instruction.result_id());
+        }
+        break;
+      case SpvOpVariable:
+        fact_manager->AddFactValueOfPointeeIsIrrelevant(
+            instruction.result_id());
+        break;
+      default:
+        break;
+    }
+  }
+
+  if (message_.is_livesafe()) {
+    // Make the function livesafe, which also should succeed.
+    success = TryToMakeFunctionLivesafe(context, *fact_manager);
+    assert(success && "It should be possible to make the function livesafe.");
+    (void)(success);  // Keep release builds happy.
+
+    // Inform the fact manager that the function is livesafe.
+    assert(message_.instruction(0).opcode() == SpvOpFunction &&
+           "The first instruction of an 'add function' transformation must be "
+           "OpFunction.");
+    fact_manager->AddFactFunctionIsLivesafe(
+        message_.instruction(0).result_id());
+  } else {
+    // Inform the fact manager that all blocks in the function are dead.
+    for (auto& inst : message_.instruction()) {
+      if (inst.opcode() == SpvOpLabel) {
+        fact_manager->AddFactBlockIsDead(inst.result_id());
+      }
+    }
+  }
   context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 }
 
@@ -149,8 +298,634 @@
   new_function->SetFunctionEnd(
       InstructionFromMessage(context, message_.instruction(instruction_index)));
   context->AddFunction(std::move(new_function));
+
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+
   return true;
 }
 
+bool TransformationAddFunction::TryToMakeFunctionLivesafe(
+    opt::IRContext* context, const FactManager& fact_manager) const {
+  assert(message_.is_livesafe() && "Precondition: is_livesafe must hold.");
+
+  // Get a pointer to the added function.
+  opt::Function* added_function = nullptr;
+  for (auto& function : *context->module()) {
+    if (function.result_id() == message_.instruction(0).result_id()) {
+      added_function = &function;
+      break;
+    }
+  }
+  assert(added_function && "The added function should have been found.");
+
+  if (!TryToAddLoopLimiters(context, added_function)) {
+    // Adding loop limiters did not work; bail out.
+    return false;
+  }
+
+  // Consider all the instructions in the function, and:
+  // - attempt to replace OpKill and OpUnreachable with return instructions
+  // - attempt to clamp access chains to be within bounds
+  // - check that OpFunctionCall instructions are only to livesafe functions
+  for (auto& block : *added_function) {
+    for (auto& inst : block) {
+      switch (inst.opcode()) {
+        case SpvOpKill:
+        case SpvOpUnreachable:
+          if (!TryToTurnKillOrUnreachableIntoReturn(context, added_function,
+                                                    &inst)) {
+            return false;
+          }
+          break;
+        case SpvOpAccessChain:
+        case SpvOpInBoundsAccessChain:
+          if (!TryToClampAccessChainIndices(context, &inst)) {
+            return false;
+          }
+          break;
+        case SpvOpFunctionCall:
+          // A livesafe function my only call other livesafe functions.
+          if (!fact_manager.FunctionIsLivesafe(
+                  inst.GetSingleWordInOperand(0))) {
+            return false;
+          }
+        default:
+          break;
+      }
+    }
+  }
+  return true;
+}
+
+bool TransformationAddFunction::TryToAddLoopLimiters(
+    opt::IRContext* context, opt::Function* added_function) const {
+  // Collect up all the loop headers so that we can subsequently add loop
+  // limiting logic.
+  std::vector<opt::BasicBlock*> loop_headers;
+  for (auto& block : *added_function) {
+    if (block.IsLoopHeader()) {
+      loop_headers.push_back(&block);
+    }
+  }
+
+  if (loop_headers.empty()) {
+    // There are no loops, so no need to add any loop limiters.
+    return true;
+  }
+
+  // Check that the module contains appropriate ingredients for declaring and
+  // manipulating a loop limiter.
+
+  auto loop_limit_constant_id_instr =
+      context->get_def_use_mgr()->GetDef(message_.loop_limit_constant_id());
+  if (!loop_limit_constant_id_instr ||
+      loop_limit_constant_id_instr->opcode() != SpvOpConstant) {
+    // The loop limit constant id instruction must exist and have an
+    // appropriate opcode.
+    return false;
+  }
+
+  auto loop_limit_type = context->get_def_use_mgr()->GetDef(
+      loop_limit_constant_id_instr->type_id());
+  if (loop_limit_type->opcode() != SpvOpTypeInt ||
+      loop_limit_type->GetSingleWordInOperand(0) != 32) {
+    // The type of the loop limit constant must be 32-bit integer.  It
+    // doesn't actually matter whether the integer is signed or not.
+    return false;
+  }
+
+  // Find the id of the "unsigned int" type.
+  opt::analysis::Integer unsigned_int_type(32, false);
+  uint32_t unsigned_int_type_id =
+      context->get_type_mgr()->GetId(&unsigned_int_type);
+  if (!unsigned_int_type_id) {
+    // Unsigned int is not available; we need this type in order to add loop
+    // limiters.
+    return false;
+  }
+  auto registered_unsigned_int_type =
+      context->get_type_mgr()->GetRegisteredType(&unsigned_int_type);
+
+  // Look for 0 of type unsigned int.
+  opt::analysis::IntConstant zero(registered_unsigned_int_type->AsInteger(),
+                                  {0});
+  auto registered_zero = context->get_constant_mgr()->FindConstant(&zero);
+  if (!registered_zero) {
+    // We need 0 in order to be able to initialize loop limiters.
+    return false;
+  }
+  uint32_t zero_id = context->get_constant_mgr()
+                         ->GetDefiningInstruction(registered_zero)
+                         ->result_id();
+
+  // Look for 1 of type unsigned int.
+  opt::analysis::IntConstant one(registered_unsigned_int_type->AsInteger(),
+                                 {1});
+  auto registered_one = context->get_constant_mgr()->FindConstant(&one);
+  if (!registered_one) {
+    // We need 1 in order to be able to increment loop limiters.
+    return false;
+  }
+  uint32_t one_id = context->get_constant_mgr()
+                        ->GetDefiningInstruction(registered_one)
+                        ->result_id();
+
+  // Look for pointer-to-unsigned int type.
+  opt::analysis::Pointer pointer_to_unsigned_int_type(
+      registered_unsigned_int_type, SpvStorageClassFunction);
+  uint32_t pointer_to_unsigned_int_type_id =
+      context->get_type_mgr()->GetId(&pointer_to_unsigned_int_type);
+  if (!pointer_to_unsigned_int_type_id) {
+    // We need pointer-to-unsigned int in order to declare the loop limiter
+    // variable.
+    return false;
+  }
+
+  // Look for bool type.
+  opt::analysis::Bool bool_type;
+  uint32_t bool_type_id = context->get_type_mgr()->GetId(&bool_type);
+  if (!bool_type_id) {
+    // We need bool in order to compare the loop limiter's value with the loop
+    // limit constant.
+    return false;
+  }
+
+  // Declare the loop limiter variable at the start of the function's entry
+  // block, via an instruction of the form:
+  //   %loop_limiter_var = SpvOpVariable %ptr_to_uint Function %zero
+  added_function->begin()->begin()->InsertBefore(MakeUnique<opt::Instruction>(
+      context, SpvOpVariable, pointer_to_unsigned_int_type_id,
+      message_.loop_limiter_variable_id(),
+      opt::Instruction::OperandList(
+          {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
+           {SPV_OPERAND_TYPE_ID, {zero_id}}})));
+  // Update the module's id bound since we have added the loop limiter
+  // variable id.
+  fuzzerutil::UpdateModuleIdBound(context, message_.loop_limiter_variable_id());
+
+  // Consider each loop in turn.
+  for (auto loop_header : loop_headers) {
+    // Look for the loop's back-edge block.  This is a predecessor of the loop
+    // header that is dominated by the loop header.
+    uint32_t back_edge_block_id = 0;
+    for (auto pred : context->cfg()->preds(loop_header->id())) {
+      if (context->GetDominatorAnalysis(added_function)
+              ->Dominates(loop_header->id(), pred)) {
+        back_edge_block_id = pred;
+        break;
+      }
+    }
+    if (!back_edge_block_id) {
+      // The loop's back-edge block must be unreachable.  This means that the
+      // loop cannot iterate, so there is no need to make it lifesafe; we can
+      // move on from this loop.
+      continue;
+    }
+    auto back_edge_block = context->cfg()->block(back_edge_block_id);
+
+    // Go through the sequence of loop limiter infos and find the one
+    // corresponding to this loop.
+    bool found = false;
+    protobufs::LoopLimiterInfo loop_limiter_info;
+    for (auto& info : message_.loop_limiter_info()) {
+      if (info.loop_header_id() == loop_header->id()) {
+        loop_limiter_info = info;
+        found = true;
+        break;
+      }
+    }
+    if (!found) {
+      // We don't have loop limiter info for this loop header.
+      return false;
+    }
+
+    // The back-edge block either has the form:
+    //
+    // (1)
+    //
+    // %l = OpLabel
+    //      ... instructions ...
+    //      OpBranch %loop_header
+    //
+    // (2)
+    //
+    // %l = OpLabel
+    //      ... instructions ...
+    //      OpBranchConditional %c %loop_header %loop_merge
+    //
+    // (3)
+    //
+    // %l = OpLabel
+    //      ... instructions ...
+    //      OpBranchConditional %c %loop_merge %loop_header
+    //
+    // We turn these into the following:
+    //
+    // (1)
+    //
+    //  %l = OpLabel
+    //       ... instructions ...
+    // %t1 = OpLoad %uint32 %loop_limiter
+    // %t2 = OpIAdd %uint32 %t1 %one
+    //       OpStore %loop_limiter %t2
+    // %t3 = OpUGreaterThanEqual %bool %t1 %loop_limit
+    //       OpBranchConditional %t3 %loop_merge %loop_header
+    //
+    // (2)
+    //
+    //  %l = OpLabel
+    //       ... instructions ...
+    // %t1 = OpLoad %uint32 %loop_limiter
+    // %t2 = OpIAdd %uint32 %t1 %one
+    //       OpStore %loop_limiter %t2
+    // %t3 = OpULessThan %bool %t1 %loop_limit
+    // %t4 = OpLogicalAnd %bool %c %t3
+    //       OpBranchConditional %t4 %loop_header %loop_merge
+    //
+    // (3)
+    //
+    //  %l = OpLabel
+    //       ... instructions ...
+    // %t1 = OpLoad %uint32 %loop_limiter
+    // %t2 = OpIAdd %uint32 %t1 %one
+    //       OpStore %loop_limiter %t2
+    // %t3 = OpUGreaterThanEqual %bool %t1 %loop_limit
+    // %t4 = OpLogicalOr %bool %c %t3
+    //       OpBranchConditional %t4 %loop_merge %loop_header
+
+    auto back_edge_block_terminator = back_edge_block->terminator();
+    bool compare_using_greater_than_equal;
+    if (back_edge_block_terminator->opcode() == SpvOpBranch) {
+      compare_using_greater_than_equal = true;
+    } else {
+      assert(back_edge_block_terminator->opcode() == SpvOpBranchConditional);
+      assert(((back_edge_block_terminator->GetSingleWordInOperand(1) ==
+                   loop_header->id() &&
+               back_edge_block_terminator->GetSingleWordInOperand(2) ==
+                   loop_header->MergeBlockId()) ||
+              (back_edge_block_terminator->GetSingleWordInOperand(2) ==
+                   loop_header->id() &&
+               back_edge_block_terminator->GetSingleWordInOperand(1) ==
+                   loop_header->MergeBlockId())) &&
+             "A back edge edge block must branch to"
+             " either the loop header or merge");
+      compare_using_greater_than_equal =
+          back_edge_block_terminator->GetSingleWordInOperand(1) ==
+          loop_header->MergeBlockId();
+    }
+
+    std::vector<std::unique_ptr<opt::Instruction>> new_instructions;
+
+    // Add a load from the loop limiter variable, of the form:
+    //   %t1 = OpLoad %uint32 %loop_limiter
+    new_instructions.push_back(MakeUnique<opt::Instruction>(
+        context, SpvOpLoad, unsigned_int_type_id, loop_limiter_info.load_id(),
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}}})));
+
+    // Increment the loaded value:
+    //   %t2 = OpIAdd %uint32 %t1 %one
+    new_instructions.push_back(MakeUnique<opt::Instruction>(
+        context, SpvOpIAdd, unsigned_int_type_id,
+        loop_limiter_info.increment_id(),
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}},
+             {SPV_OPERAND_TYPE_ID, {one_id}}})));
+
+    // Store the incremented value back to the loop limiter variable:
+    //   OpStore %loop_limiter %t2
+    new_instructions.push_back(MakeUnique<opt::Instruction>(
+        context, SpvOpStore, 0, 0,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}},
+             {SPV_OPERAND_TYPE_ID, {loop_limiter_info.increment_id()}}})));
+
+    // Compare the loaded value with the loop limit; either:
+    //   %t3 = OpUGreaterThanEqual %bool %t1 %loop_limit
+    // or
+    //   %t3 = OpULessThan %bool %t1 %loop_limit
+    new_instructions.push_back(MakeUnique<opt::Instruction>(
+        context,
+        compare_using_greater_than_equal ? SpvOpUGreaterThanEqual
+                                         : SpvOpULessThan,
+        bool_type_id, loop_limiter_info.compare_id(),
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}},
+             {SPV_OPERAND_TYPE_ID, {message_.loop_limit_constant_id()}}})));
+
+    if (back_edge_block_terminator->opcode() == SpvOpBranchConditional) {
+      new_instructions.push_back(MakeUnique<opt::Instruction>(
+          context,
+          compare_using_greater_than_equal ? SpvOpLogicalOr : SpvOpLogicalAnd,
+          bool_type_id, loop_limiter_info.logical_op_id(),
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID,
+                {back_edge_block_terminator->GetSingleWordInOperand(0)}},
+               {SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}}})));
+    }
+
+    // Add the new instructions at the end of the back edge block, before the
+    // terminator and any loop merge instruction (as the back edge block can
+    // be the loop header).
+    if (back_edge_block->GetLoopMergeInst()) {
+      back_edge_block->GetLoopMergeInst()->InsertBefore(
+          std::move(new_instructions));
+    } else {
+      back_edge_block_terminator->InsertBefore(std::move(new_instructions));
+    }
+
+    if (back_edge_block_terminator->opcode() == SpvOpBranchConditional) {
+      back_edge_block_terminator->SetInOperand(
+          0, {loop_limiter_info.logical_op_id()});
+    } else {
+      assert(back_edge_block_terminator->opcode() == SpvOpBranch &&
+             "Back-edge terminator must be OpBranch or OpBranchConditional");
+
+      // Check that, if the merge block starts with OpPhi instructions, suitable
+      // ids have been provided to give these instructions a value corresponding
+      // to the new incoming edge from the back edge block.
+      auto merge_block = context->cfg()->block(loop_header->MergeBlockId());
+      if (!fuzzerutil::PhiIdsOkForNewEdge(context, back_edge_block, merge_block,
+                                          loop_limiter_info.phi_id())) {
+        return false;
+      }
+
+      // Augment OpPhi instructions at the loop merge with the given ids.
+      uint32_t phi_index = 0;
+      for (auto& inst : *merge_block) {
+        if (inst.opcode() != SpvOpPhi) {
+          break;
+        }
+        assert(phi_index <
+                   static_cast<uint32_t>(loop_limiter_info.phi_id().size()) &&
+               "There should be at least one phi id per OpPhi instruction.");
+        inst.AddOperand(
+            {SPV_OPERAND_TYPE_ID, {loop_limiter_info.phi_id(phi_index)}});
+        inst.AddOperand({SPV_OPERAND_TYPE_ID, {back_edge_block_id}});
+        phi_index++;
+      }
+
+      // Add the new edge, by changing OpBranch to OpBranchConditional.
+      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3162): This
+      //  could be a problem if the merge block was originally unreachable: it
+      //  might now be dominated by other blocks that it appears earlier than in
+      //  the module.
+      back_edge_block_terminator->SetOpcode(SpvOpBranchConditional);
+      back_edge_block_terminator->SetInOperands(opt::Instruction::OperandList(
+          {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}},
+           {SPV_OPERAND_TYPE_ID, {loop_header->MergeBlockId()}
+
+           },
+           {SPV_OPERAND_TYPE_ID, {loop_header->id()}}}));
+    }
+
+    // Update the module's id bound with respect to the various ids that
+    // have been used for loop limiter manipulation.
+    fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.load_id());
+    fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.increment_id());
+    fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.compare_id());
+    fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.logical_op_id());
+  }
+  return true;
+}
+
+bool TransformationAddFunction::TryToTurnKillOrUnreachableIntoReturn(
+    opt::IRContext* context, opt::Function* added_function,
+    opt::Instruction* kill_or_unreachable_inst) const {
+  assert((kill_or_unreachable_inst->opcode() == SpvOpKill ||
+          kill_or_unreachable_inst->opcode() == SpvOpUnreachable) &&
+         "Precondition: instruction must be OpKill or OpUnreachable.");
+
+  // Get the function's return type.
+  auto function_return_type_inst =
+      context->get_def_use_mgr()->GetDef(added_function->type_id());
+
+  if (function_return_type_inst->opcode() == SpvOpTypeVoid) {
+    // The function has void return type, so change this instruction to
+    // OpReturn.
+    kill_or_unreachable_inst->SetOpcode(SpvOpReturn);
+  } else {
+    // The function has non-void return type, so change this instruction
+    // to OpReturnValue, using the value id provided with the
+    // transformation.
+
+    // We first check that the id, %id, provided with the transformation
+    // specifically to turn OpKill and OpUnreachable instructions into
+    // OpReturnValue %id has the same type as the function's return type.
+    if (context->get_def_use_mgr()
+            ->GetDef(message_.kill_unreachable_return_value_id())
+            ->type_id() != function_return_type_inst->result_id()) {
+      return false;
+    }
+    kill_or_unreachable_inst->SetOpcode(SpvOpReturnValue);
+    kill_or_unreachable_inst->SetInOperands(
+        {{SPV_OPERAND_TYPE_ID, {message_.kill_unreachable_return_value_id()}}});
+  }
+  return true;
+}
+
+bool TransformationAddFunction::TryToClampAccessChainIndices(
+    opt::IRContext* context, opt::Instruction* access_chain_inst) const {
+  assert((access_chain_inst->opcode() == SpvOpAccessChain ||
+          access_chain_inst->opcode() == SpvOpInBoundsAccessChain) &&
+         "Precondition: instruction must be OpAccessChain or "
+         "OpInBoundsAccessChain.");
+
+  // Find the AccessChainClampingInfo associated with this access chain.
+  const protobufs::AccessChainClampingInfo* access_chain_clamping_info =
+      nullptr;
+  for (auto& clamping_info : message_.access_chain_clamping_info()) {
+    if (clamping_info.access_chain_id() == access_chain_inst->result_id()) {
+      access_chain_clamping_info = &clamping_info;
+      break;
+    }
+  }
+  if (!access_chain_clamping_info) {
+    // No access chain clamping information was found; the function cannot be
+    // made livesafe.
+    return false;
+  }
+
+  // Check that there is a (compare_id, select_id) pair for every
+  // index associated with the instruction.
+  if (static_cast<uint32_t>(
+          access_chain_clamping_info->compare_and_select_ids().size()) !=
+      access_chain_inst->NumInOperands() - 1) {
+    return false;
+  }
+
+  // Walk the access chain, clamping each index to be within bounds if it is
+  // not a constant.
+  auto base_object = context->get_def_use_mgr()->GetDef(
+      access_chain_inst->GetSingleWordInOperand(0));
+  assert(base_object && "The base object must exist.");
+  auto pointer_type =
+      context->get_def_use_mgr()->GetDef(base_object->type_id());
+  assert(pointer_type && pointer_type->opcode() == SpvOpTypePointer &&
+         "The base object must have pointer type.");
+  auto should_be_composite_type = context->get_def_use_mgr()->GetDef(
+      pointer_type->GetSingleWordInOperand(1));
+
+  // Consider each index input operand in turn (operand 0 is the base object).
+  for (uint32_t index = 1; index < access_chain_inst->NumInOperands();
+       index++) {
+    // We are going to turn:
+    //
+    // %result = OpAccessChain %type %object ... %index ...
+    //
+    // into:
+    //
+    // %t1 = OpULessThanEqual %bool %index %bound_minus_one
+    // %t2 = OpSelect %int_type %t1 %index %bound_minus_one
+    // %result = OpAccessChain %type %object ... %t2 ...
+    //
+    // ... unless %index is already a constant.
+
+    // Get the bound for the composite being indexed into; e.g. the number of
+    // columns of matrix or the size of an array.
+    uint32_t bound =
+        GetBoundForCompositeIndex(context, *should_be_composite_type);
+
+    // Get the instruction associated with the index and figure out its integer
+    // type.
+    const uint32_t index_id = access_chain_inst->GetSingleWordInOperand(index);
+    auto index_inst = context->get_def_use_mgr()->GetDef(index_id);
+    auto index_type_inst =
+        context->get_def_use_mgr()->GetDef(index_inst->type_id());
+    assert(index_type_inst->opcode() == SpvOpTypeInt);
+    assert(index_type_inst->GetSingleWordInOperand(0) == 32);
+    opt::analysis::Integer* index_int_type =
+        context->get_type_mgr()
+            ->GetType(index_type_inst->result_id())
+            ->AsInteger();
+
+    if (index_inst->opcode() != SpvOpConstant) {
+      // The index is non-constant so we need to clamp it.
+      assert(should_be_composite_type->opcode() != SpvOpTypeStruct &&
+             "Access chain indices into structures are required to be "
+             "constants.");
+      opt::analysis::IntConstant bound_minus_one(index_int_type, {bound - 1});
+      if (!context->get_constant_mgr()->FindConstant(&bound_minus_one)) {
+        // We do not have an integer constant whose value is |bound| -1.
+        return false;
+      }
+
+      opt::analysis::Bool bool_type;
+      uint32_t bool_type_id = context->get_type_mgr()->GetId(&bool_type);
+      if (!bool_type_id) {
+        // Bool type is not declared; we cannot do a comparison.
+        return false;
+      }
+
+      uint32_t bound_minus_one_id =
+          context->get_constant_mgr()
+              ->GetDefiningInstruction(&bound_minus_one)
+              ->result_id();
+
+      uint32_t compare_id =
+          access_chain_clamping_info->compare_and_select_ids(index - 1).first();
+      uint32_t select_id =
+          access_chain_clamping_info->compare_and_select_ids(index - 1)
+              .second();
+      std::vector<std::unique_ptr<opt::Instruction>> new_instructions;
+
+      // Compare the index with the bound via an instruction of the form:
+      //   %t1 = OpULessThanEqual %bool %index %bound_minus_one
+      new_instructions.push_back(MakeUnique<opt::Instruction>(
+          context, SpvOpULessThanEqual, bool_type_id, compare_id,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {index_inst->result_id()}},
+               {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
+
+      // Select the index if in-bounds, otherwise one less than the bound:
+      //   %t2 = OpSelect %int_type %t1 %index %bound_minus_one
+      new_instructions.push_back(MakeUnique<opt::Instruction>(
+          context, SpvOpSelect, index_type_inst->result_id(), select_id,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {compare_id}},
+               {SPV_OPERAND_TYPE_ID, {index_inst->result_id()}},
+               {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
+
+      // Add the new instructions before the access chain
+      access_chain_inst->InsertBefore(std::move(new_instructions));
+
+      // Replace %index with %t2.
+      access_chain_inst->SetInOperand(index, {select_id});
+      fuzzerutil::UpdateModuleIdBound(context, compare_id);
+      fuzzerutil::UpdateModuleIdBound(context, select_id);
+    } else {
+      // TODO(afd): At present the SPIR-V spec is not clear on whether
+      //  statically out-of-bounds indices mean that a module is invalid (so
+      //  that it should be rejected by the validator), or that such accesses
+      //  yield undefined results.  Via the following assertion, we assume that
+      //  functions added to the module do not feature statically out-of-bounds
+      //  accesses.
+      // Assert that the index is smaller (unsigned) than this value.
+      // Return false if it is not (to keep compilers happy).
+      if (index_inst->GetSingleWordInOperand(0) >= bound) {
+        assert(false &&
+               "The function has a statically out-of-bounds access; "
+               "this should not occur.");
+        return false;
+      }
+    }
+    should_be_composite_type =
+        FollowCompositeIndex(context, *should_be_composite_type, index_id);
+  }
+  return true;
+}
+
+uint32_t TransformationAddFunction::GetBoundForCompositeIndex(
+    opt::IRContext* context, const opt::Instruction& composite_type_inst) {
+  switch (composite_type_inst.opcode()) {
+    case SpvOpTypeArray:
+      return fuzzerutil::GetArraySize(composite_type_inst, context);
+    case SpvOpTypeMatrix:
+    case SpvOpTypeVector:
+      return composite_type_inst.GetSingleWordInOperand(1);
+    case SpvOpTypeStruct: {
+      return fuzzerutil::GetNumberOfStructMembers(composite_type_inst);
+    }
+    default:
+      assert(false && "Unknown composite type.");
+      return 0;
+  }
+}
+
+opt::Instruction* TransformationAddFunction::FollowCompositeIndex(
+    opt::IRContext* context, const opt::Instruction& composite_type_inst,
+    uint32_t index_id) {
+  uint32_t sub_object_type_id;
+  switch (composite_type_inst.opcode()) {
+    case SpvOpTypeArray:
+      sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0);
+      break;
+    case SpvOpTypeMatrix:
+    case SpvOpTypeVector:
+      sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0);
+      break;
+    case SpvOpTypeStruct: {
+      auto index_inst = context->get_def_use_mgr()->GetDef(index_id);
+      assert(index_inst->opcode() == SpvOpConstant);
+      assert(
+          context->get_def_use_mgr()->GetDef(index_inst->type_id())->opcode() ==
+          SpvOpTypeInt);
+      assert(context->get_def_use_mgr()
+                 ->GetDef(index_inst->type_id())
+                 ->GetSingleWordInOperand(0) == 32);
+      uint32_t index_value = index_inst->GetSingleWordInOperand(0);
+      sub_object_type_id =
+          composite_type_inst.GetSingleWordInOperand(index_value);
+      break;
+    }
+    default:
+      assert(false && "Unknown composite type.");
+      sub_object_type_id = 0;
+      break;
+  }
+  assert(sub_object_type_id && "No sub-object found.");
+  return context->get_def_use_mgr()->GetDef(sub_object_type_id);
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.h
index fee2732..848b799 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.h
@@ -28,26 +28,56 @@
   explicit TransformationAddFunction(
       const protobufs::TransformationAddFunction& message);
 
+  // Creates a transformation to add a non live-safe function.
   explicit TransformationAddFunction(
       const std::vector<protobufs::Instruction>& instructions);
 
+  // Creates a transformation to add a live-safe function.
+  TransformationAddFunction(
+      const std::vector<protobufs::Instruction>& instructions,
+      uint32_t loop_limiter_variable_id, uint32_t loop_limit_constant_id,
+      const std::vector<protobufs::LoopLimiterInfo>& loop_limiters,
+      uint32_t kill_unreachable_return_value_id,
+      const std::vector<protobufs::AccessChainClampingInfo>&
+          access_chain_clampers);
+
   // - |message_.instruction| must correspond to a sufficiently well-formed
   //   sequence of instructions that a function can be created from them
+  // - If |message_.is_livesafe| holds then |message_| must contain suitable
+  //   ingredients to make the function livesafe, and the function must only
+  //   invoke other livesafe functions
   // - Adding the created function to the module must lead to a valid module.
   bool IsApplicable(opt::IRContext* context,
                     const FactManager& fact_manager) const override;
 
-  // Adds the function defined by |message_.instruction| to the module
+  // Adds the function defined by |message_.instruction| to the module, making
+  // it livesafe if |message_.is_livesafe| holds.
   void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
 
   protobufs::Transformation ToMessage() const override;
 
+  // Helper method that returns the bound for indexing into a composite of type
+  // |composite_type_inst|, i.e. the number of fields of a struct, the size of
+  // an array, the number of components of a vector, or the number of columns of
+  // a matrix.
+  static uint32_t GetBoundForCompositeIndex(
+      opt::IRContext* context, const opt::Instruction& composite_type_inst);
+
+  // Helper method that, given composite type |composite_type_inst|, returns the
+  // type of the sub-object at index |index_id|, which is required to be in-
+  // bounds.
+  static opt::Instruction* FollowCompositeIndex(
+      opt::IRContext* context, const opt::Instruction& composite_type_inst,
+      uint32_t index_id);
+
  private:
   // Attempts to create a function from the series of instructions in
-  // |message_.instruction| and add it to |context|.  Returns false if this is
-  // not possible due to the messages not respecting the basic structure of a
-  // function, e.g. if there is no OpFunction instruction or no blocks; in this
-  // case |context| is left in an indeterminate state.
+  // |message_.instruction| and add it to |context|.
+  //
+  // Returns false if adding the function is not possible due to the messages
+  // not respecting the basic structure of a function, e.g. if there is no
+  // OpFunction instruction or no blocks; in this case |context| is left in an
+  // indeterminate state.
   //
   // Otherwise returns true.  Whether |context| is valid after addition of the
   // function depends on the contents of |message_.instruction|.
@@ -61,6 +91,30 @@
   //   to add the function.
   bool TryToAddFunction(opt::IRContext* context) const;
 
+  // Should only be called if |message_.is_livesafe| holds.  Attempts to make
+  // the function livesafe (see FactFunctionIsLivesafe for a definition).
+  // Returns false if this is not possible, due to |message_| or |context| not
+  // containing sufficient ingredients (such as types and fresh ids) to add
+  // the instrumentation necessary to make the function livesafe.
+  bool TryToMakeFunctionLivesafe(opt::IRContext* context,
+                                 const FactManager& fact_manager) const;
+
+  // A helper for TryToMakeFunctionLivesafe that tries to add loop-limiting
+  // logic.
+  bool TryToAddLoopLimiters(opt::IRContext* context,
+                            opt::Function* added_function) const;
+
+  // A helper for TryToMakeFunctionLivesafe that tries to replace OpKill and
+  // OpUnreachable instructions into return instructions.
+  bool TryToTurnKillOrUnreachableIntoReturn(
+      opt::IRContext* context, opt::Function* added_function,
+      opt::Instruction* kill_or_unreachable_inst) const;
+
+  // A helper for TryToMakeFunctionLivesafe that tries to clamp access chain
+  // indices so that they are guaranteed to be in-bounds.
+  bool TryToClampAccessChainIndices(opt::IRContext* context,
+                                    opt::Instruction* access_chain_inst) const;
+
   protobufs::TransformationAddFunction message_;
 };
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.cpp
index cea268c..e4f9f7a 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.cpp
@@ -24,10 +24,12 @@
     : message_(message) {}
 
 TransformationAddGlobalVariable::TransformationAddGlobalVariable(
-    uint32_t fresh_id, uint32_t type_id, uint32_t initializer_id) {
+    uint32_t fresh_id, uint32_t type_id, uint32_t initializer_id,
+    bool value_is_irrelevant) {
   message_.set_fresh_id(fresh_id);
   message_.set_type_id(type_id);
   message_.set_initializer_id(initializer_id);
+  message_.set_value_is_irrelevant(value_is_irrelevant);
 }
 
 bool TransformationAddGlobalVariable::IsApplicable(
@@ -51,27 +53,25 @@
   if (pointer_type->storage_class() != SpvStorageClassPrivate) {
     return false;
   }
-  if (message_.initializer_id()) {
-    // The initializer id must be the id of a constant.  Check this with the
-    // constant manager.
-    auto constant_id = context->get_constant_mgr()->GetConstantsFromIds(
-        {message_.initializer_id()});
-    if (constant_id.empty()) {
-      return false;
-    }
-    assert(constant_id.size() == 1 &&
-           "We asked for the constant associated with a single id; we should "
-           "get a single constant.");
-    // The type of the constant must match the pointee type of the pointer.
-    if (pointer_type->pointee_type() != constant_id[0]->type()) {
-      return false;
-    }
+  // The initializer id must be the id of a constant.  Check this with the
+  // constant manager.
+  auto constant_id = context->get_constant_mgr()->GetConstantsFromIds(
+      {message_.initializer_id()});
+  if (constant_id.empty()) {
+    return false;
+  }
+  assert(constant_id.size() == 1 &&
+         "We asked for the constant associated with a single id; we should "
+         "get a single constant.");
+  // The type of the constant must match the pointee type of the pointer.
+  if (pointer_type->pointee_type() != constant_id[0]->type()) {
+    return false;
   }
   return true;
 }
 
 void TransformationAddGlobalVariable::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
   opt::Instruction::OperandList input_operands;
   input_operands.push_back(
       {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassPrivate}});
@@ -99,6 +99,10 @@
     }
   }
 
+  if (message_.value_is_irrelevant()) {
+    fact_manager->AddFactValueOfPointeeIsIrrelevant(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);
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.h
index ca63e68..920ac45 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.h
@@ -29,7 +29,8 @@
       const protobufs::TransformationAddGlobalVariable& message);
 
   TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id,
-                                  uint32_t initializer_id);
+                                  uint32_t initializer_id,
+                                  bool value_is_irrelevant);
 
   // - |message_.fresh_id| must be fresh
   // - |message_.type_id| must be the id of a pointer type with Private storage
@@ -43,6 +44,9 @@
   // |message_.type_id| and either no initializer or |message_.initializer_id|
   // as an initializer, depending on whether |message_.initializer_id| is 0.
   // The global variable has result id |message_.fresh_id|.
+  //
+  // If |message_.value_is_irrelevant| holds, adds a corresponding fact to
+  // |fact_manager|.
   void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
 
   protobufs::Transformation ToMessage() const override;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.cpp
new file mode 100644
index 0000000..69e536d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.cpp
@@ -0,0 +1,98 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_local_variable.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddLocalVariable::TransformationAddLocalVariable(
+    const spvtools::fuzz::protobufs::TransformationAddLocalVariable& message)
+    : message_(message) {}
+
+TransformationAddLocalVariable::TransformationAddLocalVariable(
+    uint32_t fresh_id, uint32_t type_id, uint32_t function_id,
+    uint32_t initializer_id, bool value_is_irrelevant) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_type_id(type_id);
+  message_.set_function_id(function_id);
+  message_.set_initializer_id(initializer_id);
+  message_.set_value_is_irrelevant(value_is_irrelevant);
+}
+
+bool TransformationAddLocalVariable::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // The provided id must be fresh.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  // The pointer type id must indeed correspond to a pointer, and it must have
+  // function storage class.
+  auto type_instruction =
+      context->get_def_use_mgr()->GetDef(message_.type_id());
+  if (!type_instruction || type_instruction->opcode() != SpvOpTypePointer ||
+      type_instruction->GetSingleWordInOperand(0) != SpvStorageClassFunction) {
+    return false;
+  }
+  // The initializer must...
+  auto initializer_instruction =
+      context->get_def_use_mgr()->GetDef(message_.initializer_id());
+  // ... exist, ...
+  if (!initializer_instruction) {
+    return false;
+  }
+  // ... be a constant, ...
+  if (!spvOpcodeIsConstant(initializer_instruction->opcode())) {
+    return false;
+  }
+  // ... and have the same type as the pointee type.
+  if (initializer_instruction->type_id() !=
+      type_instruction->GetSingleWordInOperand(1)) {
+    return false;
+  }
+  // The function to which the local variable is to be added must exist.
+  return fuzzerutil::FindFunction(context, message_.function_id());
+}
+
+void TransformationAddLocalVariable::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  fuzzerutil::FindFunction(context, message_.function_id())
+      ->begin()
+      ->begin()
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          context, SpvOpVariable, message_.type_id(), message_.fresh_id(),
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_STORAGE_CLASS,
+                {
+
+                    SpvStorageClassFunction}},
+               {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}}})));
+  if (message_.value_is_irrelevant()) {
+    fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id());
+  }
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddLocalVariable::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_local_variable() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.h
new file mode 100644
index 0000000..b8e00dd
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddLocalVariable : public Transformation {
+ public:
+  explicit TransformationAddLocalVariable(
+      const protobufs::TransformationAddLocalVariable& message);
+
+  TransformationAddLocalVariable(uint32_t fresh_id, uint32_t type_id,
+                                 uint32_t function_id, uint32_t initializer_id,
+                                 bool value_is_irrelevant);
+
+  // - |message_.fresh_id| must not be used by the module
+  // - |message_.type_id| must be the id of a pointer type with Function
+  //   storage class
+  // - |message_.initializer_id| must be the id of a constant with the same
+  //   type as the pointer's pointee type
+  // - |message_.function_id| must be the id of a function
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an instruction to the start of |message_.function_id|, of the form:
+  //   |message_.fresh_id| = OpVariable |message_.type_id| Function
+  //                         |message_.initializer_id|
+  // If |message_.value_is_irrelevant| holds, adds a corresponding fact to
+  // |fact_manager|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddLocalVariable message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.h
index ee3caf7..69d6389 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.h
@@ -28,8 +28,8 @@
   explicit TransformationAddTypeMatrix(
       const protobufs::TransformationAddTypeMatrix& message);
 
-  TransformationAddTypeMatrix(uint32_t fresh_id, uint32_t base_type_id,
-                              uint32_t size);
+  TransformationAddTypeMatrix(uint32_t fresh_id, uint32_t column_type_id,
+                              uint32_t column_count);
 
   // - |message_.fresh_id| must be a fresh id
   // - |message_.column_type_id| must be the id of a floating-point vector type
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.h
index 7b50f6a..af840f5 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.h
@@ -28,8 +28,8 @@
   explicit TransformationAddTypeVector(
       const protobufs::TransformationAddTypeVector& message);
 
-  TransformationAddTypeVector(uint32_t fresh_id, uint32_t base_type_id,
-                              uint32_t size);
+  TransformationAddTypeVector(uint32_t fresh_id, uint32_t component_type_id,
+                              uint32_t component_count);
 
   // - |message_.fresh_id| must be a fresh id
   // - |message_.component_type_id| must be the id of a scalar type
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_construct.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_construct.cpp
index 7a3aff1..9c63c1d 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_construct.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_construct.cpp
@@ -84,27 +84,8 @@
   // Now check whether every component being used to initialize the composite is
   // available at the desired program point.
   for (auto& component : message_.component()) {
-    auto component_inst = context->get_def_use_mgr()->GetDef(component);
-    if (!context->get_instr_block(component)) {
-      // The component does not have a block; that means it is in global scope,
-      // which is OK. (Whether the component actually corresponds to an
-      // instruction is checked above when determining whether types are
-      // suitable.)
-      continue;
-    }
-    // Check whether the component is available.
-    if (insert_before->HasResultId() &&
-        insert_before->result_id() == component) {
-      // This constitutes trying to use an id right before it is defined.  The
-      // special case is needed due to an instruction always dominating itself.
-      return false;
-    }
-    if (!context
-             ->GetDominatorAnalysis(
-                 context->get_instr_block(&*insert_before)->GetParent())
-             ->Dominates(component_inst, &*insert_before)) {
-      // The instruction defining the component must dominate the instruction we
-      // wish to insert the composite before.
+    if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+                                                    component)) {
       return false;
     }
   }
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.cpp
index af1e81c..bfdced3 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.cpp
@@ -64,20 +64,10 @@
     return false;
   }
 
-  // |message_object| must be available at the point where we want to add the
-  // copy. It is available if it is at global scope (in which case it has no
-  // block), or if it dominates the point of insertion but is different from the
-  // point of insertion.
-  //
-  // The reason why the object needs to be different from the insertion point is
-  // that the copy will be added *before* this point, and we do not want to
-  // insert it before the object's defining instruction.
-  return !context->get_instr_block(object_inst) ||
-         (object_inst != &*insert_before &&
-          context
-              ->GetDominatorAnalysis(
-                  context->get_instr_block(insert_before)->GetParent())
-              ->Dominates(object_inst, &*insert_before));
+  // |message_object| must be available directly before the point where we want
+  // to add the copy.
+  return fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+                                                    message_.object());
 }
 
 void TransformationCopyObject::Apply(opt::IRContext* context,
@@ -105,6 +95,10 @@
   fact_manager->AddFactDataSynonym(MakeDataDescriptor(message_.object(), {}),
                                    MakeDataDescriptor(message_.fresh_id(), {}),
                                    context);
+
+  if (fact_manager->PointeeValueIsIrrelevant(message_.object())) {
+    fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id());
+  }
 }
 
 protobufs::Transformation TransformationCopyObject::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.h b/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.h
index 3a75ac9..9e9c26a 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.h
@@ -47,6 +47,8 @@
   // - It must be legal to insert an OpCopyObject instruction directly
   //   before 'inst'.
   // - |message_.object| must be available directly before 'inst'.
+  // - |message_.object| must not be a null pointer or undefined pointer (so as
+  //   to make it legal to load from copied pointers).
   bool IsApplicable(opt::IRContext* context,
                     const FactManager& fact_manager) const override;
 
@@ -55,7 +57,10 @@
   //   is added directly before the instruction at |message_.insert_after_id| +
   //   |message_|.offset, where %ty is the type of |message_.object|.
   // - The fact that |message_.fresh_id| and |message_.object| are synonyms
-  //   is added to the fact manager.
+  //   is added to |fact_manager|.
+  // - If |message_.object| is a pointer whose pointee value is known to be
+  //   irrelevant, the analogous fact is added to |fact_manager| about
+  //   |message_.fresh_id|.
   void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
 
   protobufs::Transformation ToMessage() const override;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.cpp
new file mode 100644
index 0000000..6988664
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.cpp
@@ -0,0 +1,195 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_function_call.h"
+
+#include "source/fuzz/call_graph.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationFunctionCall::TransformationFunctionCall(
+    const spvtools::fuzz::protobufs::TransformationFunctionCall& message)
+    : message_(message) {}
+
+TransformationFunctionCall::TransformationFunctionCall(
+    uint32_t fresh_id, uint32_t callee_id,
+    const std::vector<uint32_t>& argument_id,
+    const protobufs::InstructionDescriptor& instruction_to_insert_before) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_callee_id(callee_id);
+  for (auto argument : argument_id) {
+    message_.add_argument_id(argument);
+  }
+  *message_.mutable_instruction_to_insert_before() =
+      instruction_to_insert_before;
+}
+
+bool TransformationFunctionCall::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& fact_manager) const {
+  // The result id must be fresh
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+
+  // The function must exist
+  auto callee_inst = context->get_def_use_mgr()->GetDef(message_.callee_id());
+  if (!callee_inst || callee_inst->opcode() != SpvOpFunction) {
+    return false;
+  }
+
+  // The function must not be an entry point
+  if (FunctionIsEntryPoint(context, message_.callee_id())) {
+    return false;
+  }
+
+  auto callee_type_inst = context->get_def_use_mgr()->GetDef(
+      callee_inst->GetSingleWordInOperand(1));
+  assert(callee_type_inst->opcode() == SpvOpTypeFunction &&
+         "Bad function type.");
+
+  // The number of expected function arguments must match the number of given
+  // arguments.  The number of expected arguments is one less than the function
+  // type's number of input operands, as one operand is for the return type.
+  if (callee_type_inst->NumInOperands() - 1 !=
+      static_cast<uint32_t>(message_.argument_id().size())) {
+    return false;
+  }
+
+  // The instruction descriptor must refer to a position where it is valid to
+  // insert the call
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  if (!insert_before) {
+    return false;
+  }
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpFunctionCall,
+                                                    insert_before)) {
+    return false;
+  }
+
+  auto block = context->get_instr_block(insert_before);
+  auto enclosing_function = block->GetParent();
+
+  // If the block is not dead, the function must be livesafe
+  bool block_is_dead = fact_manager.BlockIsDead(block->id());
+  if (!block_is_dead &&
+      !fact_manager.FunctionIsLivesafe(message_.callee_id())) {
+    return false;
+  }
+
+  // The ids must all match and have the right types and satisfy rules on
+  // pointers.  If the block is not dead, pointers must be arbitrary.
+  for (uint32_t arg_index = 0;
+       arg_index < static_cast<uint32_t>(message_.argument_id().size());
+       arg_index++) {
+    opt::Instruction* arg_inst =
+        context->get_def_use_mgr()->GetDef(message_.argument_id(arg_index));
+    if (!arg_inst) {
+      // The given argument does not correspond to an instruction.
+      return false;
+    }
+    if (!arg_inst->type_id()) {
+      // The given argument does not have a type; it is thus not suitable.
+    }
+    if (arg_inst->type_id() !=
+        callee_type_inst->GetSingleWordInOperand(arg_index + 1)) {
+      // Argument type mismatch.
+      return false;
+    }
+    opt::Instruction* arg_type_inst =
+        context->get_def_use_mgr()->GetDef(arg_inst->type_id());
+    if (arg_type_inst->opcode() == SpvOpTypePointer) {
+      switch (arg_inst->opcode()) {
+        case SpvOpFunctionParameter:
+        case SpvOpVariable:
+          // These are OK
+          break;
+        default:
+          // Other pointer ids cannot be passed as parameters
+          return false;
+      }
+      if (!block_is_dead &&
+          !fact_manager.PointeeValueIsIrrelevant(arg_inst->result_id())) {
+        // This is not a dead block, so pointer parameters passed to the called
+        // function might really have their contents modified. We thus require
+        // such pointers to be to arbitrary-valued variables, which this is not.
+        return false;
+      }
+    }
+
+    // The argument id needs to be available (according to dominance rules) at
+    // the point where the call will occur.
+    if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+                                                    arg_inst->result_id())) {
+      return false;
+    }
+  }
+
+  // Introducing the call must not lead to recursion.
+  if (message_.callee_id() == enclosing_function->result_id()) {
+    // This would be direct recursion.
+    return false;
+  }
+  // Ensure the call would not lead to indirect recursion.
+  return !CallGraph(context)
+              .GetIndirectCallees(message_.callee_id())
+              .count(block->GetParent()->result_id());
+}
+
+void TransformationFunctionCall::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  // Update the module's bound to reflect the fresh id for the result of the
+  // function call.
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // Get the return type of the function being called.
+  uint32_t return_type =
+      context->get_def_use_mgr()->GetDef(message_.callee_id())->type_id();
+  // Populate the operands to the call instruction, with the function id and the
+  // arguments.
+  opt::Instruction::OperandList operands;
+  operands.push_back({SPV_OPERAND_TYPE_ID, {message_.callee_id()}});
+  for (auto arg : message_.argument_id()) {
+    operands.push_back({SPV_OPERAND_TYPE_ID, {arg}});
+  }
+  // Insert the function call before the instruction specified in the message.
+  FindInstruction(message_.instruction_to_insert_before(), context)
+      ->InsertBefore(
+          MakeUnique<opt::Instruction>(context, SpvOpFunctionCall, return_type,
+                                       message_.fresh_id(), operands));
+  // Invalidate all analyses since we have changed the module.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationFunctionCall::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_function_call() = message_;
+  return result;
+}
+
+bool TransformationFunctionCall::FunctionIsEntryPoint(opt::IRContext* context,
+                                                      uint32_t function_id) {
+  for (auto& entry_point : context->module()->entry_points()) {
+    if (entry_point.GetSingleWordInOperand(1) == function_id) {
+      return true;
+    }
+  }
+  return false;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.h b/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.h
new file mode 100644
index 0000000..e977e1d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_
+#define SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationFunctionCall : public Transformation {
+ public:
+  explicit TransformationFunctionCall(
+      const protobufs::TransformationFunctionCall& message);
+
+  TransformationFunctionCall(
+      uint32_t fresh_id, uint32_t callee_id,
+      const std::vector<uint32_t>& argument_id,
+      const protobufs::InstructionDescriptor& instruction_to_insert_before);
+
+  // - |message_.fresh_id| must be fresh
+  // - |message_.instruction_to_insert_before| must identify an instruction
+  //   before which an OpFunctionCall can be legitimately inserted
+  // - |message_.function_id| must be the id of a function, and calling the
+  //   function before the identified instruction must not introduce recursion
+  // - |message_.arg_id| must provide suitable arguments for the function call
+  //   (they must have the right types and be available according to dominance
+  //   rules)
+  // - If the insertion point is not in a dead block then |message_function_id|
+  //   must refer to a livesafe function, and every pointer argument in
+  //   |message_.arg_id| must refer to an arbitrary-valued variable
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an instruction of the form:
+  //   |fresh_id| = OpFunctionCall %type |callee_id| |arg_id...|
+  // before |instruction_to_insert_before|, where %type is the return type of
+  // |callee_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Helper to determine whether |function_id| is targeted by OpEntryPoint.
+  static bool FunctionIsEntryPoint(opt::IRContext* context,
+                                   uint32_t function_id);
+
+ private:
+  protobufs::TransformationFunctionCall message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_load.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_load.cpp
new file mode 100644
index 0000000..4cba37d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_load.cpp
@@ -0,0 +1,103 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_load.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationLoad::TransformationLoad(
+    const spvtools::fuzz::protobufs::TransformationLoad& message)
+    : message_(message) {}
+
+TransformationLoad::TransformationLoad(
+    uint32_t fresh_id, uint32_t pointer_id,
+    const protobufs::InstructionDescriptor& instruction_to_insert_before) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_pointer_id(pointer_id);
+  *message_.mutable_instruction_to_insert_before() =
+      instruction_to_insert_before;
+}
+
+bool TransformationLoad::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // The result id must be fresh.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+
+  // The pointer must exist and have a type.
+  auto pointer = context->get_def_use_mgr()->GetDef(message_.pointer_id());
+  if (!pointer || !pointer->type_id()) {
+    return false;
+  }
+  // The type must indeed be a pointer type.
+  auto pointer_type = context->get_def_use_mgr()->GetDef(pointer->type_id());
+  assert(pointer_type && "Type id must be defined.");
+  if (pointer_type->opcode() != SpvOpTypePointer) {
+    return false;
+  }
+  // We do not want to allow loading from null or undefined pointers, as it is
+  // not clear how punishing the consequences of doing so are from a semantics
+  // point of view.
+  switch (pointer->opcode()) {
+    case SpvOpConstantNull:
+    case SpvOpUndef:
+      return false;
+    default:
+      break;
+  }
+
+  // Determine which instruction we should be inserting before.
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  // It must exist, ...
+  if (!insert_before) {
+    return false;
+  }
+  // ... and it must be legitimate to insert a store before it.
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, insert_before)) {
+    return false;
+  }
+
+  // The pointer needs to be available at the insertion point.
+  return fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+                                                    message_.pointer_id());
+}
+
+void TransformationLoad::Apply(opt::IRContext* context,
+                               spvtools::fuzz::FactManager* /*unused*/) const {
+  uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
+      context, fuzzerutil::GetTypeId(context, message_.pointer_id()));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  FindInstruction(message_.instruction_to_insert_before(), context)
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          context, SpvOpLoad, result_type, message_.fresh_id(),
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}})));
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationLoad::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_load() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_load.h b/third_party/SPIRV-Tools/source/fuzz/transformation_load.h
new file mode 100644
index 0000000..ff99016
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_load.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_LOAD_H_
+#define SOURCE_FUZZ_TRANSFORMATION_LOAD_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationLoad : public Transformation {
+ public:
+  explicit TransformationLoad(const protobufs::TransformationLoad& message);
+
+  TransformationLoad(
+      uint32_t fresh_id, uint32_t pointer_id,
+      const protobufs::InstructionDescriptor& instruction_to_insert_before);
+
+  // - |message_.fresh_id| must be fresh
+  // - |message_.pointer_id| must be the id of a pointer
+  // - The pointer must not be OpConstantNull or OpUndef
+  // - |message_.instruction_to_insert_before| must identify an instruction
+  //   before which it is valid to insert an OpLoad, and where
+  //   |message_.pointer_id| is available (according to dominance rules)
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an instruction of the form:
+  //   |message_.fresh_id| = OpLoad %type |message_.pointer_id|
+  // before the instruction identified by
+  // |message_.instruction_to_insert_before|, where %type is the pointer's
+  // pointee type.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationLoad message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_LOAD_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.cpp
index 1d1d48e..01d1c45 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.cpp
@@ -151,22 +151,7 @@
 
   // For simplicity, we do not allow the exit block to be a merge block or
   // continue target.
-  bool exit_block_is_merge_or_continue = false;
-  context->get_def_use_mgr()->WhileEachUse(
-      exit_block->id(),
-      [&exit_block_is_merge_or_continue](
-          const opt::Instruction* use_instruction,
-          uint32_t /*unused*/) -> bool {
-        switch (use_instruction->opcode()) {
-          case SpvOpLoopMerge:
-          case SpvOpSelectionMerge:
-            exit_block_is_merge_or_continue = true;
-            return false;
-          default:
-            return true;
-        }
-      });
-  if (exit_block_is_merge_or_continue) {
+  if (fuzzerutil::IsMergeOrContinue(context, exit_block->id())) {
     return false;
   }
 
@@ -269,11 +254,21 @@
     if (input_id_to_fresh_id_map.count(id) == 0) {
       return false;
     }
-    // Furthermore, no region input id is allowed to be the result of an access
-    // chain.  This is because region input ids will become function parameters,
-    // and it is not legal to pass an access chain as a function parameter.
-    if (context->get_def_use_mgr()->GetDef(id)->opcode() == SpvOpAccessChain) {
-      return false;
+    // Furthermore, if the input id has pointer type it must be an OpVariable
+    // or OpFunctionParameter.
+    auto input_id_inst = context->get_def_use_mgr()->GetDef(id);
+    if (context->get_def_use_mgr()
+            ->GetDef(input_id_inst->type_id())
+            ->opcode() == SpvOpTypePointer) {
+      switch (input_id_inst->opcode()) {
+        case SpvOpFunctionParameter:
+        case SpvOpVariable:
+          // These are OK.
+          break;
+        default:
+          // Anything else is not OK.
+          return false;
+      }
     }
   }
 
@@ -292,7 +287,7 @@
 }
 
 void TransformationOutlineFunction::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
   // The entry block for the region before outlining.
   auto original_region_entry_block =
       context->cfg()->block(message_.entry_block());
@@ -345,8 +340,16 @@
 
   // Make a function prototype for the outlined function, which involves
   // figuring out its required type.
-  std::unique_ptr<opt::Function> outlined_function = PrepareFunctionPrototype(
-      context, region_input_ids, region_output_ids, input_id_to_fresh_id_map);
+  std::unique_ptr<opt::Function> outlined_function =
+      PrepareFunctionPrototype(region_input_ids, region_output_ids,
+                               input_id_to_fresh_id_map, context, fact_manager);
+
+  // If the original function was livesafe, the new function should also be
+  // livesafe.
+  if (fact_manager->FunctionIsLivesafe(
+          original_region_entry_block->GetParent()->result_id())) {
+    fact_manager->AddFactFunctionIsLivesafe(message_.new_function_id());
+  }
 
   // Adapt the region to be outlined so that its input ids are replaced with the
   // ids of the outlined function's input parameters, and so that output ids
@@ -357,10 +360,10 @@
 
   // Fill out the body of the outlined function according to the region that is
   // being outlined.
-  PopulateOutlinedFunction(context, *original_region_entry_block,
+  PopulateOutlinedFunction(*original_region_entry_block,
                            *original_region_exit_block, region_blocks,
                            region_output_ids, output_id_to_fresh_id_map,
-                           outlined_function.get());
+                           context, outlined_function.get(), fact_manager);
 
   // Collapse the region that has been outlined into a function down to a single
   // block that calls said function.
@@ -383,20 +386,6 @@
   return result;
 }
 
-bool TransformationOutlineFunction::
-    CheckIdIsFreshAndNotUsedByThisTransformation(
-        uint32_t id, opt::IRContext* context,
-        std::set<uint32_t>* ids_used_by_this_transformation) const {
-  if (!fuzzerutil::IsFreshId(context, id)) {
-    return false;
-  }
-  if (ids_used_by_this_transformation->count(id) != 0) {
-    return false;
-  }
-  ids_used_by_this_transformation->insert(id);
-  return true;
-}
-
 std::vector<uint32_t> TransformationOutlineFunction::GetRegionInputIds(
     opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
     opt::BasicBlock* region_exit_block) {
@@ -543,9 +532,10 @@
 
 std::unique_ptr<opt::Function>
 TransformationOutlineFunction::PrepareFunctionPrototype(
-    opt::IRContext* context, const std::vector<uint32_t>& region_input_ids,
+    const std::vector<uint32_t>& region_input_ids,
     const std::vector<uint32_t>& region_output_ids,
-    const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map) const {
+    const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
+    opt::IRContext* context, FactManager* fact_manager) const {
   uint32_t return_type_id = 0;
   uint32_t function_type_id = 0;
 
@@ -555,15 +545,16 @@
   // not exist there cannot already be a function type with this struct as its
   // return type.
   if (region_output_ids.empty()) {
+    std::vector<uint32_t> return_and_parameter_types;
     opt::analysis::Void void_type;
     return_type_id = context->get_type_mgr()->GetId(&void_type);
-    std::vector<const opt::analysis::Type*> argument_types;
+    return_and_parameter_types.push_back(return_type_id);
     for (auto id : region_input_ids) {
-      argument_types.push_back(context->get_type_mgr()->GetType(
-          context->get_def_use_mgr()->GetDef(id)->type_id()));
+      return_and_parameter_types.push_back(
+          context->get_def_use_mgr()->GetDef(id)->type_id());
     }
-    opt::analysis::Function function_type(&void_type, argument_types);
-    function_type_id = context->get_type_mgr()->GetId(&function_type);
+    function_type_id =
+        fuzzerutil::FindFunctionType(context, return_and_parameter_types);
   }
 
   // If no existing function type was found, we need to create one.
@@ -626,6 +617,12 @@
         context, SpvOpFunctionParameter,
         context->get_def_use_mgr()->GetDef(id)->type_id(),
         input_id_to_fresh_id_map.at(id), opt::Instruction::OperandList()));
+    // If the input id is an irrelevant-valued variable, the same should be true
+    // of the corresponding parameter.
+    if (fact_manager->PointeeValueIsIrrelevant(id)) {
+      fact_manager->AddFactValueOfPointeeIsIrrelevant(
+          input_id_to_fresh_id_map.at(id));
+    }
   }
 
   return outlined_function;
@@ -719,12 +716,13 @@
 }
 
 void TransformationOutlineFunction::PopulateOutlinedFunction(
-    opt::IRContext* context, const opt::BasicBlock& original_region_entry_block,
+    const opt::BasicBlock& original_region_entry_block,
     const opt::BasicBlock& original_region_exit_block,
     const std::set<opt::BasicBlock*>& region_blocks,
     const std::vector<uint32_t>& region_output_ids,
     const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map,
-    opt::Function* outlined_function) const {
+    opt::IRContext* context, opt::Function* outlined_function,
+    FactManager* fact_manager) const {
   // When we create the exit block for the outlined region, we use this pointer
   // to track of it so that we can manipulate it later.
   opt::BasicBlock* outlined_region_exit_block = nullptr;
@@ -737,6 +735,13 @@
           context, SpvOpLabel, 0, message_.new_function_region_entry_block(),
           opt::Instruction::OperandList()));
   outlined_region_entry_block->SetParent(outlined_function);
+
+  // If the original region's entry block was dead, the outlined region's entry
+  // block is also dead.
+  if (fact_manager->BlockIsDead(original_region_entry_block.id())) {
+    fact_manager->AddFactBlockIsDead(outlined_region_entry_block->id());
+  }
+
   if (&original_region_entry_block == &original_region_exit_block) {
     outlined_region_exit_block = outlined_region_entry_block.get();
   }
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.h b/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.h
index 784499d..5711790 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.h
@@ -128,15 +128,6 @@
       opt::BasicBlock* region_exit_block);
 
  private:
-  // A helper method for the applicability check.  Returns true if and only if
-  // |id| is (a) a fresh id for the module, and (b) an id that has not
-  // previously been subject to this check.  We use this to check whether the
-  // ids given for the transformation are not only fresh but also different from
-  // one another.
-  bool CheckIdIsFreshAndNotUsedByThisTransformation(
-      uint32_t id, opt::IRContext* context,
-      std::set<uint32_t>* ids_used_by_this_transformation) const;
-
   // Ensures that the module's id bound is at least the maximum of any fresh id
   // associated with the transformation.
   void UpdateModuleIdBoundForFreshIds(
@@ -167,10 +158,14 @@
   // A new struct type to represent the function return type, and a new function
   // type for the function, will be added to the module (unless suitable types
   // are already present).
+  //
+  // Facts about the function containing the outlined region that are relevant
+  // to the new function are propagated via |fact_manager|.
   std::unique_ptr<opt::Function> PrepareFunctionPrototype(
-      opt::IRContext* context, const std::vector<uint32_t>& region_input_ids,
+      const std::vector<uint32_t>& region_input_ids,
       const std::vector<uint32_t>& region_output_ids,
-      const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map) const;
+      const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
+      opt::IRContext* context, FactManager* fact_manager) const;
 
   // Creates the body of the outlined function by cloning blocks from the
   // original region, given by |region_blocks|, adapting the cloned version
@@ -178,14 +173,18 @@
   // and patching up branches to |original_region_entry_block| to refer to its
   // clone.  Parameters |region_output_ids| and |output_id_to_fresh_id_map| are
   // used to determine what the function should return.
+  //
+  // The |fact_manager| argument allow facts about blocks being outlined, e.g.
+  // whether they are dead blocks, to be asserted about blocks that get created
+  // during outlining.
   void PopulateOutlinedFunction(
-      opt::IRContext* context,
       const opt::BasicBlock& original_region_entry_block,
       const opt::BasicBlock& original_region_exit_block,
       const std::set<opt::BasicBlock*>& region_blocks,
       const std::vector<uint32_t>& region_output_ids,
       const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map,
-      opt::Function* outlined_function) const;
+      opt::IRContext* context, opt::Function* outlined_function,
+      FactManager* fact_manager) const;
 
   // Shrinks the outlined region, given by |region_blocks|, down to the single
   // block |original_region_entry_block|.  This block is itself shrunk to just
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
index b097767..72d9b22 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
@@ -243,11 +243,22 @@
     return false;
   }
 
-  // The instruction must not be an OpPhi, as we cannot insert a binary
-  // operator instruction before an OpPhi.
-  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): there is
-  //  scope for being less conservative.
-  return instruction->opcode() != SpvOpPhi;
+  switch (instruction->opcode()) {
+    case SpvOpPhi:
+      // The instruction must not be an OpPhi, as we cannot insert a binary
+      // operator instruction before an OpPhi.
+      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): there is
+      //  scope for being less conservative.
+      return false;
+    case SpvOpVariable:
+      // The instruction must not be an OpVariable, because (a) we cannot insert
+      // a binary operator before an OpVariable, but in any case (b) the
+      // constant we would be replacing is the initializer constant of the
+      // OpVariable, and this cannot be the result of a binary operation.
+      return false;
+    default:
+      return true;
+  }
 }
 
 void TransformationReplaceBooleanConstantWithConstantBinary::Apply(
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_constant_with_uniform.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_constant_with_uniform.cpp
index 405776e..8e0e4e5 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_constant_with_uniform.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_constant_with_uniform.cpp
@@ -154,6 +154,12 @@
     return false;
   }
 
+  // The use must not be a variable initializer; these are required to be
+  // constants, so it would be illegal to replace one with a uniform access.
+  if (instruction_using_constant->opcode() == SpvOpVariable) {
+    return false;
+  }
+
   // The module needs to have a uniform pointer type suitable for indexing into
   // the uniform variable, i.e. matching the type of the constant we wish to
   // replace with a uniform.
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_id_with_synonym.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_id_with_synonym.cpp
index 79ba012..88c977a 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_id_with_synonym.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_id_with_synonym.cpp
@@ -65,9 +65,9 @@
 
   // The transformation is applicable if the synonymous id is available at the
   // use point.
-  return IdsIsAvailableAtUse(context, use_instruction,
-                             message_.id_use_descriptor().in_operand_index(),
-                             message_.synonymous_id());
+  return fuzzerutil::IdIsAvailableAtUse(
+      context, use_instruction, message_.id_use_descriptor().in_operand_index(),
+      message_.synonymous_id());
 }
 
 void TransformationReplaceIdWithSynonym::Apply(
@@ -88,30 +88,6 @@
   return result;
 }
 
-bool TransformationReplaceIdWithSynonym::IdsIsAvailableAtUse(
-    opt::IRContext* context, opt::Instruction* use_instruction,
-    uint32_t use_input_operand_index, uint32_t id) {
-  if (!context->get_instr_block(id)) {
-    return true;
-  }
-  auto defining_instruction = context->get_def_use_mgr()->GetDef(id);
-  if (defining_instruction == use_instruction) {
-    return false;
-  }
-  auto dominator_analysis = context->GetDominatorAnalysis(
-      context->get_instr_block(use_instruction)->GetParent());
-  if (use_instruction->opcode() == SpvOpPhi) {
-    // In the case where the use is an operand to OpPhi, it is actually the
-    // *parent* block associated with the operand that must be dominated by
-    // the synonym.
-    auto parent_block =
-        use_instruction->GetSingleWordInOperand(use_input_operand_index + 1);
-    return dominator_analysis->Dominates(
-        context->get_instr_block(defining_instruction)->id(), parent_block);
-  }
-  return dominator_analysis->Dominates(defining_instruction, use_instruction);
-}
-
 bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
     opt::IRContext* context, opt::Instruction* use_instruction,
     uint32_t use_in_operand_index) {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_id_with_synonym.h b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_id_with_synonym.h
index c21673d..48132c1 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_id_with_synonym.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_id_with_synonym.h
@@ -51,14 +51,6 @@
 
   protobufs::Transformation ToMessage() const override;
 
-  // Checks whether the |id| is available (according to dominance rules) at the
-  // use point defined by input operand |use_input_operand_index| of
-  // |use_instruction|.
-  static bool IdsIsAvailableAtUse(opt::IRContext* context,
-                                  opt::Instruction* use_instruction,
-                                  uint32_t use_input_operand_index,
-                                  uint32_t id);
-
   // Checks whether various conditions hold related to the acceptability of
   // replacing the id use at |use_in_operand_index| of |use_instruction| with
   // a synonym.  In particular, this checks that:
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_split_block.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_split_block.cpp
index 9f6da7c..fc5229e 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_split_block.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_split_block.cpp
@@ -80,7 +80,7 @@
 }
 
 void TransformationSplitBlock::Apply(opt::IRContext* context,
-                                     FactManager* /*unused*/) const {
+                                     FactManager* fact_manager) const {
   opt::Instruction* instruction_to_split_before =
       FindInstruction(message_.instruction_to_split_before(), context);
   opt::BasicBlock* block_to_split =
@@ -114,6 +114,13 @@
            "one predecessor.");
     phi_inst->SetInOperand(1, {block_to_split->id()});
   });
+
+  // If the block being split was dead, the new block arising from the split is
+  // also dead.
+  if (fact_manager->BlockIsDead(block_to_split->id())) {
+    fact_manager->AddFactBlockIsDead(message_.fresh_id());
+  }
+
   // Invalidate all analyses
   context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
 }
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_split_block.h b/third_party/SPIRV-Tools/source/fuzz/transformation_split_block.h
index 63dc7f5..a193fc7 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_split_block.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_split_block.h
@@ -48,6 +48,7 @@
   // - All instructions of 'blk' from 'inst' onwards are moved into the new
   //   block.
   // - 'blk' is made to jump unconditionally to the new block.
+  // - If 'blk' was dead, the new block is also dead.
   void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
 
   protobufs::Transformation ToMessage() const override;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_store.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_store.cpp
new file mode 100644
index 0000000..7cb7611
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_store.cpp
@@ -0,0 +1,128 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_store.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationStore::TransformationStore(
+    const spvtools::fuzz::protobufs::TransformationStore& message)
+    : message_(message) {}
+
+TransformationStore::TransformationStore(
+    uint32_t pointer_id, uint32_t value_id,
+    const protobufs::InstructionDescriptor& instruction_to_insert_before) {
+  message_.set_pointer_id(pointer_id);
+  message_.set_value_id(value_id);
+  *message_.mutable_instruction_to_insert_before() =
+      instruction_to_insert_before;
+}
+
+bool TransformationStore::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& fact_manager) const {
+  // The pointer must exist and have a type.
+  auto pointer = context->get_def_use_mgr()->GetDef(message_.pointer_id());
+  if (!pointer || !pointer->type_id()) {
+    return false;
+  }
+
+  // The pointer type must indeed be a pointer.
+  auto pointer_type = context->get_def_use_mgr()->GetDef(pointer->type_id());
+  assert(pointer_type && "Type id must be defined.");
+  if (pointer_type->opcode() != SpvOpTypePointer) {
+    return false;
+  }
+
+  // The pointer must not be read only.
+  if (pointer_type->GetSingleWordInOperand(0) == SpvStorageClassInput) {
+    return false;
+  }
+
+  // We do not want to allow storing to null or undefined pointers.
+  switch (pointer->opcode()) {
+    case SpvOpConstantNull:
+    case SpvOpUndef:
+      return false;
+    default:
+      break;
+  }
+
+  // Determine which instruction we should be inserting before.
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  // It must exist, ...
+  if (!insert_before) {
+    return false;
+  }
+  // ... and it must be legitimate to insert a store before it.
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
+                                                    insert_before)) {
+    return false;
+  }
+
+  // The block we are inserting into needs to be dead, or else the pointee type
+  // of the pointer we are storing to needs to be irrelevant (otherwise the
+  // store could impact on the observable behaviour of the module).
+  if (!fact_manager.BlockIsDead(
+          context->get_instr_block(insert_before)->id()) &&
+      !fact_manager.PointeeValueIsIrrelevant(message_.pointer_id())) {
+    return false;
+  }
+
+  // The value being stored needs to exist and have a type.
+  auto value = context->get_def_use_mgr()->GetDef(message_.value_id());
+  if (!value || !value->type_id()) {
+    return false;
+  }
+
+  // The type of the value must match the pointee type.
+  if (pointer_type->GetSingleWordInOperand(1) != value->type_id()) {
+    return false;
+  }
+
+  // The pointer needs to be available at the insertion point.
+  if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+                                                  message_.pointer_id())) {
+    return false;
+  }
+
+  // The value needs to be available at the insertion point.
+  return fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+                                                    message_.value_id());
+}
+
+void TransformationStore::Apply(opt::IRContext* context,
+                                spvtools::fuzz::FactManager* /*unused*/) const {
+  FindInstruction(message_.instruction_to_insert_before(), context)
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          context, SpvOpStore, 0, 0,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
+               {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})));
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationStore::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_store() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_store.h b/third_party/SPIRV-Tools/source/fuzz/transformation_store.h
new file mode 100644
index 0000000..699afdd
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_store.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_STORE_H_
+#define SOURCE_FUZZ_TRANSFORMATION_STORE_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationStore : public Transformation {
+ public:
+  explicit TransformationStore(const protobufs::TransformationStore& message);
+
+  TransformationStore(
+      uint32_t pointer_id, uint32_t value_id,
+      const protobufs::InstructionDescriptor& instruction_to_insert_before);
+
+  // - |message_.pointer_id| must be the id of a pointer
+  // - The pointer type must not have read-only storage class
+  // - The pointer must not be OpConstantNull or OpUndef
+  // - |message_.value_id| must be an instruction result id that has the same
+  //   type as the pointee type of |message_.pointer_id|
+  // - |message_.instruction_to_insert_before| must identify an instruction
+  //   before which it is valid to insert an OpStore, and where both
+  //   |message_.pointer_id| and |message_.value_id| are available (according
+  //   to dominance rules)
+  // - Either the insertion point must be in a dead block, or it must be known
+  //   that the pointee value of |message_.pointer_id| is irrelevant
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an instruction of the form:
+  //   OpStore |pointer_id| |value_id|
+  // before the instruction identified by
+  // |message_.instruction_to_insert_before|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationStore message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_STORE_H_
diff --git a/third_party/SPIRV-Tools/source/link/linker.cpp b/third_party/SPIRV-Tools/source/link/linker.cpp
index d99a1e8..da6f0a7 100644
--- a/third_party/SPIRV-Tools/source/link/linker.cpp
+++ b/third_party/SPIRV-Tools/source/link/linker.cpp
@@ -137,8 +137,6 @@
 // TODO(pierremoreau): Linkage attributes applied by a group decoration are
 //                     currently not handled. (You could have a group being
 //                     applied to a single ID.)
-// TODO(pierremoreau): Run a pass for removing dead instructions, for example
-//                     OpName for prototypes of imported funcions.
 spv_result_t RemoveLinkageSpecificInstructions(
     const MessageConsumer& consumer, const LinkerOptions& options,
     const LinkageTable& linkings_to_do, DecorationManager* decoration_manager,
@@ -326,6 +324,11 @@
       linked_module->AddDebug3Inst(
           std::unique_ptr<Instruction>(inst.Clone(linked_context)));
 
+  for (const auto& module : input_modules)
+    for (const auto& inst : module->ext_inst_debuginfo())
+      linked_module->AddExtInstDebugInfo(
+          std::unique_ptr<Instruction>(inst.Clone(linked_context)));
+
   // If the generated module uses SPIR-V 1.1 or higher, add an
   // OpModuleProcessed instruction about the linking step.
   if (linked_module->version() >= 0x10100) {
@@ -531,24 +534,6 @@
   // TODO(pierremoreau): Remove FuncParamAttr decorations of imported
   // functions' return type.
 
-  // Remove FuncParamAttr decorations of imported functions' parameters.
-  // From the SPIR-V specification, Sec. 2.13:
-  //   When resolving imported functions, the Function Control and all Function
-  //   Parameter Attributes are taken from the function definition, and not
-  //   from the function declaration.
-  for (const auto& linking_entry : linkings_to_do) {
-    for (const auto parameter_id :
-         linking_entry.imported_symbol.parameter_ids) {
-      decoration_manager->RemoveDecorationsFrom(
-          parameter_id, [](const Instruction& inst) {
-            return (inst.opcode() == SpvOpDecorate ||
-                    inst.opcode() == SpvOpMemberDecorate) &&
-                   inst.GetSingleWordInOperand(1u) ==
-                       SpvDecorationFuncParamAttr;
-          });
-    }
-  }
-
   // Remove prototypes of imported functions
   for (const auto& linking_entry : linkings_to_do) {
     for (auto func_iter = linked_context->module()->begin();
@@ -746,24 +731,34 @@
   opt::Pass::Status pass_res = manager.Run(&linked_context);
   if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
 
-  // Phase 7: Rematch import variables/functions to export variables/functions
-  for (const auto& linking_entry : linkings_to_do)
+  // Phase 7: Remove all names and decorations of import variables/functions
+  for (const auto& linking_entry : linkings_to_do) {
+    linked_context.KillNamesAndDecorates(linking_entry.imported_symbol.id);
+    for (const auto parameter_id :
+         linking_entry.imported_symbol.parameter_ids) {
+      linked_context.KillNamesAndDecorates(parameter_id);
+    }
+  }
+
+  // Phase 8: Rematch import variables/functions to export variables/functions
+  for (const auto& linking_entry : linkings_to_do) {
     linked_context.ReplaceAllUsesWith(linking_entry.imported_symbol.id,
                                       linking_entry.exported_symbol.id);
+  }
 
-  // Phase 8: Remove linkage specific instructions, such as import/export
+  // Phase 9: Remove linkage specific instructions, such as import/export
   // attributes, linkage capability, etc. if applicable
   res = RemoveLinkageSpecificInstructions(consumer, options, linkings_to_do,
                                           linked_context.get_decoration_mgr(),
                                           &linked_context);
   if (res != SPV_SUCCESS) return res;
 
-  // Phase 9: Compact the IDs used in the module
+  // Phase 10: Compact the IDs used in the module
   manager.AddPass<opt::CompactIdsPass>();
   pass_res = manager.Run(&linked_context);
   if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
 
-  // Phase 10: Output the module
+  // Phase 11: Output the module
   linked_context.module()->ToBinary(linked_binary, true);
 
   return SPV_SUCCESS;
diff --git a/third_party/SPIRV-Tools/source/opcode.cpp b/third_party/SPIRV-Tools/source/opcode.cpp
index 7f91a0f..a837b95 100644
--- a/third_party/SPIRV-Tools/source/opcode.cpp
+++ b/third_party/SPIRV-Tools/source/opcode.cpp
@@ -181,11 +181,15 @@
   }
 }
 
-const char* spvOpcodeString(const SpvOp opcode) {
+const char* spvOpcodeString(const uint32_t opcode) {
   const auto beg = kOpcodeTableEntries;
   const auto end = kOpcodeTableEntries + ARRAY_SIZE(kOpcodeTableEntries);
-  spv_opcode_desc_t needle = {"",    opcode, 0, nullptr, 0,   {},
-                              false, false,  0, nullptr, ~0u, ~0u};
+  spv_opcode_desc_t needle = {"",    static_cast<SpvOp>(opcode),
+                              0,     nullptr,
+                              0,     {},
+                              false, false,
+                              0,     nullptr,
+                              ~0u,   ~0u};
   auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
     return lhs.opcode < rhs.opcode;
   };
diff --git a/third_party/SPIRV-Tools/source/opcode.h b/third_party/SPIRV-Tools/source/opcode.h
index ed64f1b..f79826f 100644
--- a/third_party/SPIRV-Tools/source/opcode.h
+++ b/third_party/SPIRV-Tools/source/opcode.h
@@ -56,9 +56,6 @@
                         const uint16_t word_count,
                         const spv_endianness_t endian, spv_instruction_t* inst);
 
-// Gets the name of an instruction, without the "Op" prefix.
-const char* spvOpcodeString(const SpvOp opcode);
-
 // Determine if the given opcode is a scalar type. Returns zero if false,
 // non-zero otherwise.
 int32_t spvOpcodeIsScalarType(const SpvOp opcode);
diff --git a/third_party/SPIRV-Tools/source/operand.cpp b/third_party/SPIRV-Tools/source/operand.cpp
index 39d17a6..3042606 100644
--- a/third_party/SPIRV-Tools/source/operand.cpp
+++ b/third_party/SPIRV-Tools/source/operand.cpp
@@ -19,6 +19,8 @@
 
 #include <algorithm>
 
+#include "DebugInfo.h"
+#include "OpenCLDebugInfo100.h"
 #include "source/macro.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_target_env.h"
@@ -512,3 +514,37 @@
   }
   return out;
 }
+
+std::function<bool(unsigned)> spvDbgInfoExtOperandCanBeForwardDeclaredFunction(
+    spv_ext_inst_type_t ext_type, uint32_t key) {
+  // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/532): Forward
+  // references for debug info instructions are still in discussion. We must
+  // update the following lines of code when we conclude the spec.
+  std::function<bool(unsigned index)> out;
+  if (ext_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+    switch (OpenCLDebugInfo100Instructions(key)) {
+      case OpenCLDebugInfo100DebugFunction:
+        out = [](unsigned index) { return index == 13; };
+        break;
+      case OpenCLDebugInfo100DebugTypeComposite:
+        out = [](unsigned index) { return index >= 13; };
+        break;
+      default:
+        out = [](unsigned) { return false; };
+        break;
+    }
+  } else {
+    switch (DebugInfoInstructions(key)) {
+      case DebugInfoDebugFunction:
+        out = [](unsigned index) { return index == 13; };
+        break;
+      case DebugInfoDebugTypeComposite:
+        out = [](unsigned index) { return index >= 12; };
+        break;
+      default:
+        out = [](unsigned) { return false; };
+        break;
+    }
+  }
+  return out;
+}
diff --git a/third_party/SPIRV-Tools/source/operand.h b/third_party/SPIRV-Tools/source/operand.h
index 15a1825..7c73c6f 100644
--- a/third_party/SPIRV-Tools/source/operand.h
+++ b/third_party/SPIRV-Tools/source/operand.h
@@ -141,4 +141,11 @@
 std::function<bool(unsigned)> spvOperandCanBeForwardDeclaredFunction(
     SpvOp opcode);
 
+// Takes the instruction key of a debug info extension instruction
+// and returns a function object that will return true if the index
+// of the operand can be forward declared. This function will
+// used in the SSA validation stage of the pipeline
+std::function<bool(unsigned)> spvDbgInfoExtOperandCanBeForwardDeclaredFunction(
+    spv_ext_inst_type_t ext_type, uint32_t key);
+
 #endif  // SOURCE_OPERAND_H_
diff --git a/third_party/SPIRV-Tools/source/opt/amd_ext_to_khr.cpp b/third_party/SPIRV-Tools/source/opt/amd_ext_to_khr.cpp
index e9b7f86..ccedc0b 100644
--- a/third_party/SPIRV-Tools/source/opt/amd_ext_to_khr.cpp
+++ b/third_party/SPIRV-Tools/source/opt/amd_ext_to_khr.cpp
@@ -53,12 +53,6 @@
   return ctx->get_type_mgr()->GetRegisteredType(&int_type);
 }
 
-bool NotImplementedYet(IRContext*, Instruction*,
-                       const std::vector<const analysis::Constant*>&) {
-  assert(false && "Not implemented.");
-  return false;
-}
-
 // Returns a folding rule that replaces |op(a,b,c)| by |op(op(a,b),c)|, where
 // |op| is either min or max. |opcode| is the binary opcode in the GLSLstd450
 // extended instruction set that corresponds to the trinary instruction being
@@ -686,13 +680,13 @@
   return true;
 }
 
-// A folding rule that will replace the CubeFaceCoordAMD extended
+// A folding rule that will replace the CubeFaceIndexAMD extended
 // instruction in the SPV_AMD_gcn_shader_ballot.  Returns true if the folding
 // is successful.
 //
 // The instruction
 //
-//  %result = OpExtInst %v2float %1 CubeFaceCoordAMD %input
+//  %result = OpExtInst %float %1 CubeFaceIndexAMD %input
 //
 // with
 //
@@ -705,7 +699,7 @@
 //      %is_z_neg = OpFOrdLessThan %bool %z %float_0
 //      %is_y_neg = OpFOrdLessThan %bool %y %float_0
 //      %is_x_neg = OpFOrdLessThan %bool %x %float_0
-//      %amax_x_y = OpExtInst %float %n_1 FMax %ay %ax
+//      %amax_x_y = OpExtInst %float %n_1 FMax %ax %ay
 //      %is_z_max = OpFOrdGreaterThanEqual %bool %az %amax_x_y
 //        %y_gt_x = OpFOrdGreaterThanEqual %bool %ay %ax
 //        %case_z = OpSelect %float %is_z_neg %float_5 %float4
@@ -800,6 +794,37 @@
   return true;
 }
 
+// A folding rule that will replace the TimeAMD extended instruction in the
+// SPV_AMD_gcn_shader_ballot.  It returns true if the folding is successful.
+// It returns False, otherwise.
+//
+// The instruction
+//
+//  %result = OpExtInst %uint64 %1 TimeAMD
+//
+// with
+//
+//  %result = OpReadClockKHR %uint64 %uint_3
+//
+// NOTE: TimeAMD uses subgroup scope (it is not a real time clock).
+bool ReplaceTimeAMD(IRContext* ctx, Instruction* inst,
+                    const std::vector<const analysis::Constant*>&) {
+  InstructionBuilder ir_builder(
+      ctx, inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  ctx->AddExtension("SPV_KHR_shader_clock");
+  ctx->AddCapability(SpvCapabilityShaderClockKHR);
+
+  inst->SetOpcode(SpvOpReadClockKHR);
+  Instruction::OperandList args;
+  uint32_t subgroup_scope_id = ir_builder.GetUintConstantId(SpvScopeSubgroup);
+  args.push_back({SPV_OPERAND_TYPE_ID, {subgroup_scope_id}});
+  inst->SetInOperands(std::move(args));
+  ctx->UpdateDefUse(inst);
+
+  return true;
+}
+
 class AmdExtFoldingRules : public FoldingRules {
  public:
   explicit AmdExtFoldingRules(IRContext* ctx) : FoldingRules(ctx) {}
@@ -869,7 +894,7 @@
           ReplaceCubeFaceCoord);
       ext_rules_[{extension_id, CubeFaceIndexAMD}].push_back(
           ReplaceCubeFaceIndex);
-      ext_rules_[{extension_id, TimeAMD}].push_back(NotImplementedYet);
+      ext_rules_[{extension_id, TimeAMD}].push_back(ReplaceTimeAMD);
     }
   }
 };
diff --git a/third_party/SPIRV-Tools/source/opt/basic_block.h b/third_party/SPIRV-Tools/source/opt/basic_block.h
index 0bab337..6741a50 100644
--- a/third_party/SPIRV-Tools/source/opt/basic_block.h
+++ b/third_party/SPIRV-Tools/source/opt/basic_block.h
@@ -214,7 +214,7 @@
   void KillAllInsts(bool killLabel);
 
   // Splits this basic block into two. Returns a new basic block with label
-  // |labelId| containing the instructions from |iter| onwards. Instructions
+  // |label_id| containing the instructions from |iter| onwards. Instructions
   // prior to |iter| remain in this basic block.  The new block will be added
   // to the function immediately after the original block.
   BasicBlock* SplitBasicBlock(IRContext* context, uint32_t label_id,
diff --git a/third_party/SPIRV-Tools/source/opt/block_merge_util.cpp b/third_party/SPIRV-Tools/source/opt/block_merge_util.cpp
index 263a069..14b5d36 100644
--- a/third_party/SPIRV-Tools/source/opt/block_merge_util.cpp
+++ b/third_party/SPIRV-Tools/source/opt/block_merge_util.cpp
@@ -171,8 +171,17 @@
       // flow declaration.
       context->KillInst(merge_inst);
     } else {
+      // Move OpLine/OpNoLine information to merge_inst. This solves
+      // the validation error that OpLine is placed between OpLoopMerge
+      // and OpBranchConditional.
+      auto terminator = bi->terminator();
+      auto& vec = terminator->dbg_line_insts();
+      auto& new_vec = merge_inst->dbg_line_insts();
+      new_vec.insert(new_vec.end(), vec.begin(), vec.end());
+      terminator->clear_dbg_line_insts();
+
       // Move the merge instruction to just before the terminator.
-      merge_inst->InsertBefore(bi->terminator());
+      merge_inst->InsertBefore(terminator);
     }
   }
   context->ReplaceAllUsesWith(lab_id, bi->id());
diff --git a/third_party/SPIRV-Tools/source/opt/const_folding_rules.cpp b/third_party/SPIRV-Tools/source/opt/const_folding_rules.cpp
index 2a2493f..d262a7e 100644
--- a/third_party/SPIRV-Tools/source/opt/const_folding_rules.cpp
+++ b/third_party/SPIRV-Tools/source/opt/const_folding_rules.cpp
@@ -265,7 +265,10 @@
       return nullptr;
     }
 
-    if (constants[0] == nullptr) {
+    const analysis::Constant* arg =
+        (inst->opcode() == SpvOpExtInst) ? constants[1] : constants[0];
+
+    if (arg == nullptr) {
       return nullptr;
     }
 
@@ -273,7 +276,7 @@
       std::vector<const analysis::Constant*> a_components;
       std::vector<const analysis::Constant*> results_components;
 
-      a_components = constants[0]->GetVectorComponents(const_mgr);
+      a_components = arg->GetVectorComponents(const_mgr);
 
       // Fold each component of the vector.
       for (uint32_t i = 0; i < a_components.size(); ++i) {
@@ -291,7 +294,7 @@
       }
       return const_mgr->GetConstant(vector_type, ids);
     } else {
-      return scalar_rule(result_type, constants[0], const_mgr);
+      return scalar_rule(result_type, arg, const_mgr);
     }
   };
 }
@@ -1070,6 +1073,60 @@
   return nullptr;
 }
 
+UnaryScalarFoldingRule FoldFTranscendentalUnary(double (*fp)(double)) {
+  return
+      [fp](const analysis::Type* result_type, const analysis::Constant* a,
+           analysis::ConstantManager* const_mgr) -> const analysis::Constant* {
+        assert(result_type != nullptr && a != nullptr);
+        const analysis::Float* float_type = a->type()->AsFloat();
+        assert(float_type != nullptr);
+        assert(float_type == result_type->AsFloat());
+        if (float_type->width() == 32) {
+          float fa = a->GetFloat();
+          float res = static_cast<float>(fp(fa));
+          utils::FloatProxy<float> result(res);
+          std::vector<uint32_t> words = result.GetWords();
+          return const_mgr->GetConstant(result_type, words);
+        } else if (float_type->width() == 64) {
+          double fa = a->GetDouble();
+          double res = fp(fa);
+          utils::FloatProxy<double> result(res);
+          std::vector<uint32_t> words = result.GetWords();
+          return const_mgr->GetConstant(result_type, words);
+        }
+        return nullptr;
+      };
+}
+
+BinaryScalarFoldingRule FoldFTranscendentalBinary(double (*fp)(double,
+                                                               double)) {
+  return
+      [fp](const analysis::Type* result_type, const analysis::Constant* a,
+           const analysis::Constant* b,
+           analysis::ConstantManager* const_mgr) -> const analysis::Constant* {
+        assert(result_type != nullptr && a != nullptr);
+        const analysis::Float* float_type = a->type()->AsFloat();
+        assert(float_type != nullptr);
+        assert(float_type == result_type->AsFloat());
+        assert(float_type == b->type()->AsFloat());
+        if (float_type->width() == 32) {
+          float fa = a->GetFloat();
+          float fb = b->GetFloat();
+          float res = static_cast<float>(fp(fa, fb));
+          utils::FloatProxy<float> result(res);
+          std::vector<uint32_t> words = result.GetWords();
+          return const_mgr->GetConstant(result_type, words);
+        } else if (float_type->width() == 64) {
+          double fa = a->GetDouble();
+          double fb = b->GetDouble();
+          double res = fp(fa, fb);
+          utils::FloatProxy<double> result(res);
+          std::vector<uint32_t> words = result.GetWords();
+          return const_mgr->GetConstant(result_type, words);
+        }
+        return nullptr;
+      };
+}
 }  // namespace
 
 void ConstantFoldingRules::AddFoldingRules() {
@@ -1175,6 +1232,45 @@
         FoldClamp2);
     ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FClamp}].push_back(
         FoldClamp3);
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Sin}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::sin)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Cos}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::cos)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Tan}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::tan)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Asin}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::asin)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Acos}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::acos)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Atan}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::atan)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Exp}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::exp)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Log}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::log)));
+
+#ifdef __ANDROID__
+    // Android NDK r15c tageting ABI 15 doesn't have full support for C++11
+    // (no std::exp2/log2). ::exp2 is available from C99 but ::log2 isn't
+    // available up until ABI 18 so we use a shim
+    auto log2_shim = [](double v) -> double { return log(v) / log(2.0); };
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Exp2}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(::exp2)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Log2}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(log2_shim)));
+#else
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Exp2}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::exp2)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Log2}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::log2)));
+#endif
+
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Sqrt}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::sqrt)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Atan2}].push_back(
+        FoldFPBinaryOp(FoldFTranscendentalBinary(std::atan2)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Pow}].push_back(
+        FoldFPBinaryOp(FoldFTranscendentalBinary(std::pow)));
   }
 }
 }  // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/constants.cpp b/third_party/SPIRV-Tools/source/opt/constants.cpp
index 0887ec2..6057356 100644
--- a/third_party/SPIRV-Tools/source/opt/constants.cpp
+++ b/third_party/SPIRV-Tools/source/opt/constants.cpp
@@ -156,7 +156,7 @@
 }
 
 std::vector<const Constant*> ConstantManager::GetOperandConstants(
-    Instruction* inst) const {
+    const Instruction* inst) const {
   std::vector<const Constant*> constants;
   for (uint32_t i = 0; i < inst->NumInOperands(); i++) {
     const Operand* operand = &inst->GetInOperand(i);
diff --git a/third_party/SPIRV-Tools/source/opt/constants.h b/third_party/SPIRV-Tools/source/opt/constants.h
index 5f2fdc7..9518b5b 100644
--- a/third_party/SPIRV-Tools/source/opt/constants.h
+++ b/third_party/SPIRV-Tools/source/opt/constants.h
@@ -597,7 +597,8 @@
 
   // Returns a vector of constants representing each in operand. If an operand
   // is not constant its entry is nullptr.
-  std::vector<const Constant*> GetOperandConstants(Instruction* inst) const;
+  std::vector<const Constant*> GetOperandConstants(
+      const Instruction* inst) const;
 
   // Records a mapping between |inst| and the constant value generated by it.
   // It returns true if a new Constant was successfully mapped, false if |inst|
diff --git a/third_party/SPIRV-Tools/source/opt/dead_branch_elim_pass.cpp b/third_party/SPIRV-Tools/source/opt/dead_branch_elim_pass.cpp
index 8425a39..16d9fd5 100644
--- a/third_party/SPIRV-Tools/source/opt/dead_branch_elim_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/dead_branch_elim_pass.cpp
@@ -167,7 +167,6 @@
     }
 
     if (simplify) {
-      modified = true;
       conditions_to_simplify.push_back({block, live_lab_id});
       stack.push_back(GetParentBlock(live_lab_id));
     } else {
@@ -179,24 +178,29 @@
     }
   }
 
-  // Traverse |conditions_to_simplify in reverse order.  This is done so that we
-  // simplify nested constructs before simplifying the constructs that contain
-  // them.
+  // Traverse |conditions_to_simplify| in reverse order.  This is done so that
+  // we simplify nested constructs before simplifying the constructs that
+  // contain them.
   for (auto b = conditions_to_simplify.rbegin();
        b != conditions_to_simplify.rend(); ++b) {
-    SimplifyBranch(b->first, b->second);
+    modified |= SimplifyBranch(b->first, b->second);
   }
 
   return modified;
 }
 
-void DeadBranchElimPass::SimplifyBranch(BasicBlock* block,
+bool DeadBranchElimPass::SimplifyBranch(BasicBlock* block,
                                         uint32_t live_lab_id) {
   Instruction* merge_inst = block->GetMergeInst();
   Instruction* terminator = block->terminator();
   if (merge_inst && merge_inst->opcode() == SpvOpSelectionMerge) {
     if (merge_inst->NextNode()->opcode() == SpvOpSwitch &&
         SwitchHasNestedBreak(block->id())) {
+      if (terminator->NumInOperands() == 2) {
+        // We cannot remove the branch, and it already has a single case, so no
+        // work to do.
+        return false;
+      }
       // We have to keep the switch because it has a nest break, so we
       // remove all cases except for the live one.
       Instruction::OperandList new_operands;
@@ -231,6 +235,7 @@
     AddBranch(live_lab_id, block);
     context()->KillInst(terminator);
   }
+  return true;
 }
 
 void DeadBranchElimPass::MarkUnreachableStructuredTargets(
@@ -643,7 +648,8 @@
         if (bb->id() == switch_header_id) {
           return true;
         }
-        return (cfg_analysis->ContainingConstruct(inst) == switch_header_id);
+        return (cfg_analysis->ContainingConstruct(inst) == switch_header_id &&
+                bb->GetMergeInst() == nullptr);
       });
 }
 
diff --git a/third_party/SPIRV-Tools/source/opt/dead_branch_elim_pass.h b/third_party/SPIRV-Tools/source/opt/dead_branch_elim_pass.h
index a50933f..7841bc4 100644
--- a/third_party/SPIRV-Tools/source/opt/dead_branch_elim_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/dead_branch_elim_pass.h
@@ -159,14 +159,15 @@
       std::unordered_set<BasicBlock*>* blocks_with_back_edges);
 
   // Returns true if there is a brach to the merge node of the selection
-  // construct |switch_header_id| that is inside a nested selection construct.
+  // construct |switch_header_id| that is inside a nested selection construct or
+  // in the header of the nested selection construct.
   bool SwitchHasNestedBreak(uint32_t switch_header_id);
 
-  // Replaces the terminator of |block| with a branch to |live_lab_id|.  The
-  // merge instruction is deleted or moved as needed to maintain structured
-  // control flow.  Assumes that the StructuredCFGAnalysis is valid for the
-  // constructs containing |block|.
-  void SimplifyBranch(BasicBlock* block, uint32_t live_lab_id);
+  // Return true of the terminator of |block| is successfully replaced with a
+  // branch to |live_lab_id|.  The merge instruction is deleted or moved as
+  // needed to maintain structured control flow.  Assumes that the
+  // StructuredCFGAnalysis is valid for the constructs containing |block|.
+  bool SimplifyBranch(BasicBlock* block, uint32_t live_lab_id);
 };
 
 }  // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/instruction.h b/third_party/SPIRV-Tools/source/opt/instruction.h
index d1c4ce1..322e0aa 100644
--- a/third_party/SPIRV-Tools/source/opt/instruction.h
+++ b/third_party/SPIRV-Tools/source/opt/instruction.h
@@ -172,6 +172,9 @@
     return dbg_line_insts_;
   }
 
+  // Clear line-related debug instructions attached to this instruction.
+  void clear_dbg_line_insts() { dbg_line_insts_.clear(); }
+
   // Same semantics as in the base class except the list the InstructionList
   // containing |pos| will now assume ownership of |this|.
   // inline void MoveBefore(Instruction* pos);
diff --git a/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp b/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
index dfcd164..b1a6edb 100644
--- a/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
@@ -988,6 +988,10 @@
     (void)i;
     ++module_offset;
   }
+  for (auto& i : module->ext_inst_debuginfo()) {
+    (void)i;
+    ++module_offset;
+  }
   for (auto& i : module->annotations()) {
     (void)i;
     ++module_offset;
diff --git a/third_party/SPIRV-Tools/source/opt/ir_context.h b/third_party/SPIRV-Tools/source/opt/ir_context.h
index 45bf129..723a2bb 100644
--- a/third_party/SPIRV-Tools/source/opt/ir_context.h
+++ b/third_party/SPIRV-Tools/source/opt/ir_context.h
@@ -187,6 +187,14 @@
   inline IteratorRange<Module::inst_iterator> debugs3();
   inline IteratorRange<Module::const_inst_iterator> debugs3() const;
 
+  // Iterators for debug info instructions (excluding OpLine & OpNoLine)
+  // contained in this module.  These are OpExtInst for OpenCL.DebugInfo.100
+  // or DebugInfo extension placed between section 9 and 10.
+  inline Module::inst_iterator ext_inst_debuginfo_begin();
+  inline Module::inst_iterator ext_inst_debuginfo_end();
+  inline IteratorRange<Module::inst_iterator> ext_inst_debuginfo();
+  inline IteratorRange<Module::const_inst_iterator> ext_inst_debuginfo() const;
+
   // Add |capability| to the module, if it is not already enabled.
   inline void AddCapability(SpvCapability capability);
 
@@ -215,6 +223,8 @@
   // Appends a debug 3 instruction (OpModuleProcessed) to this module.
   // This is due to decision by the SPIR Working Group, pending publication.
   inline void AddDebug3Inst(std::unique_ptr<Instruction>&& d);
+  // Appends a OpExtInst for DebugInfo to this module.
+  inline void AddExtInstDebugInfo(std::unique_ptr<Instruction>&& d);
   // Appends an annotation instruction to this module.
   inline void AddAnnotationInst(std::unique_ptr<Instruction>&& a);
   // Appends a type-declaration instruction to this module.
@@ -925,6 +935,23 @@
   return ((const Module*)module_.get())->debugs3();
 }
 
+Module::inst_iterator IRContext::ext_inst_debuginfo_begin() {
+  return module()->ext_inst_debuginfo_begin();
+}
+
+Module::inst_iterator IRContext::ext_inst_debuginfo_end() {
+  return module()->ext_inst_debuginfo_end();
+}
+
+IteratorRange<Module::inst_iterator> IRContext::ext_inst_debuginfo() {
+  return module()->ext_inst_debuginfo();
+}
+
+IteratorRange<Module::const_inst_iterator> IRContext::ext_inst_debuginfo()
+    const {
+  return ((const Module*)module_.get())->ext_inst_debuginfo();
+}
+
 void IRContext::AddCapability(SpvCapability capability) {
   if (!get_feature_mgr()->HasCapability(capability)) {
     std::unique_ptr<Instruction> capability_inst(new Instruction(
@@ -1018,6 +1045,10 @@
   module()->AddDebug3Inst(std::move(d));
 }
 
+void IRContext::AddExtInstDebugInfo(std::unique_ptr<Instruction>&& d) {
+  module()->AddExtInstDebugInfo(std::move(d));
+}
+
 void IRContext::AddAnnotationInst(std::unique_ptr<Instruction>&& a) {
   if (AreAnalysesValid(kAnalysisDecorations)) {
     get_decoration_mgr()->AddDecoration(a.get());
diff --git a/third_party/SPIRV-Tools/source/opt/ir_loader.cpp b/third_party/SPIRV-Tools/source/opt/ir_loader.cpp
index e21e680..836012f 100644
--- a/third_party/SPIRV-Tools/source/opt/ir_loader.cpp
+++ b/third_party/SPIRV-Tools/source/opt/ir_loader.cpp
@@ -16,6 +16,8 @@
 
 #include <utility>
 
+#include "DebugInfo.h"
+#include "OpenCLDebugInfo100.h"
 #include "source/ext_inst.h"
 #include "source/opt/log.h"
 #include "source/opt/reflect.h"
@@ -118,6 +120,9 @@
                  (opcode == SpvOpExtInst &&
                   spvExtInstIsNonSemantic(inst->ext_inst_type))) {
         module_->AddGlobalValue(std::move(spv_inst));
+      } else if (opcode == SpvOpExtInst &&
+                 spvExtInstIsDebugInfo(inst->ext_inst_type)) {
+        module_->AddExtInstDebugInfo(std::move(spv_inst));
       } else {
         Errorf(consumer_, src, loc,
                "Unhandled inst type (opcode: %d) found outside function "
@@ -136,6 +141,39 @@
         }
         function_->AddParameter(std::move(spv_inst));
       } else {
+        if (opcode == SpvOpExtInst &&
+            spvExtInstIsDebugInfo(inst->ext_inst_type)) {
+          const uint32_t ext_inst_index = inst->words[4];
+          if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+            const OpenCLDebugInfo100Instructions ext_inst_key =
+                OpenCLDebugInfo100Instructions(ext_inst_index);
+            if (ext_inst_key != OpenCLDebugInfo100DebugScope &&
+                ext_inst_key != OpenCLDebugInfo100DebugNoScope &&
+                ext_inst_key != OpenCLDebugInfo100DebugDeclare &&
+                ext_inst_key != OpenCLDebugInfo100DebugValue) {
+              Errorf(consumer_, src, loc,
+                     "Debug info extension instruction other than DebugScope, "
+                     "DebugNoScope, DebugDeclare, and DebugValue found inside "
+                     "function",
+                     opcode);
+              return false;
+            }
+          } else {
+            const DebugInfoInstructions ext_inst_key =
+                DebugInfoInstructions(ext_inst_index);
+            if (ext_inst_key != DebugInfoDebugScope &&
+                ext_inst_key != DebugInfoDebugNoScope &&
+                ext_inst_key != DebugInfoDebugDeclare &&
+                ext_inst_key != DebugInfoDebugValue) {
+              Errorf(consumer_, src, loc,
+                     "Debug info extension instruction other than DebugScope, "
+                     "DebugNoScope, DebugDeclare, and DebugValue found inside "
+                     "function",
+                     opcode);
+              return false;
+            }
+          }
+        }
         block_->AddInstruction(std::move(spv_inst));
       }
     }
diff --git a/third_party/SPIRV-Tools/source/opt/merge_return_pass.cpp b/third_party/SPIRV-Tools/source/opt/merge_return_pass.cpp
index 18c49f5..bbac4bb 100644
--- a/third_party/SPIRV-Tools/source/opt/merge_return_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/merge_return_pass.cpp
@@ -69,6 +69,32 @@
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
+void MergeReturnPass::GenerateState(BasicBlock* block) {
+  if (Instruction* mergeInst = block->GetMergeInst()) {
+    if (mergeInst->opcode() == SpvOpLoopMerge) {
+      // If new loop, break to this loop merge block
+      state_.emplace_back(mergeInst, mergeInst);
+    } else {
+      auto branchInst = mergeInst->NextNode();
+      if (branchInst->opcode() == SpvOpSwitch) {
+        // If switch inside of loop, break to innermost loop merge block.
+        // Otherwise need to break to this switch merge block.
+        auto lastMergeInst = state_.back().BreakMergeInst();
+        if (lastMergeInst && lastMergeInst->opcode() == SpvOpLoopMerge)
+          state_.emplace_back(lastMergeInst, mergeInst);
+        else
+          state_.emplace_back(mergeInst, mergeInst);
+      } else {
+        // If branch conditional inside loop, always break to innermost
+        // loop merge block. If branch conditional inside switch, break to
+        // innermost switch merge block.
+        auto lastMergeInst = state_.back().BreakMergeInst();
+        state_.emplace_back(lastMergeInst, mergeInst);
+      }
+    }
+  }
+}
+
 bool MergeReturnPass::ProcessStructured(
     Function* function, const std::vector<BasicBlock*>& return_blocks) {
   if (HasNontrivialUnreachableBlocks(function)) {
@@ -82,7 +108,7 @@
   }
 
   RecordImmediateDominators(function);
-  AddDummyLoopAroundFunction();
+  AddDummySwitchAroundFunction();
 
   std::list<BasicBlock*> order;
   cfg()->ComputeStructuredOrder(function, &*function->begin(), &order);
@@ -103,12 +129,8 @@
 
     ProcessStructuredBlock(block);
 
-    // Generate state for next block
-    if (Instruction* mergeInst = block->GetMergeInst()) {
-      Instruction* loopMergeInst = block->GetLoopMergeInst();
-      if (!loopMergeInst) loopMergeInst = state_.back().LoopMergeInst();
-      state_.emplace_back(loopMergeInst, mergeInst);
-    }
+    // Generate state for next block if warranted
+    GenerateState(block);
   }
 
   state_.clear();
@@ -133,12 +155,8 @@
       }
     }
 
-    // Generate state for next block
-    if (Instruction* mergeInst = block->GetMergeInst()) {
-      Instruction* loopMergeInst = block->GetLoopMergeInst();
-      if (!loopMergeInst) loopMergeInst = state_.back().LoopMergeInst();
-      state_.emplace_back(loopMergeInst, mergeInst);
-    }
+    // Generate state for next block if warranted
+    GenerateState(block);
   }
 
   // We have not kept the dominator tree up-to-date.
@@ -202,8 +220,8 @@
 
   if (tail_opcode == SpvOpReturn || tail_opcode == SpvOpReturnValue ||
       tail_opcode == SpvOpUnreachable) {
-    assert(CurrentState().InLoop() && "Should be in the dummy loop.");
-    BranchToBlock(block, CurrentState().LoopMergeId());
+    assert(CurrentState().InBreakable() && "Should be in the dummy construct.");
+    BranchToBlock(block, CurrentState().BreakMergeId());
     return_blocks_.insert(block->id());
   }
 }
@@ -337,8 +355,8 @@
   std::unordered_set<BasicBlock*> seen;
   if (block->id() == state->CurrentMergeId()) {
     state++;
-  } else if (block->id() == state->LoopMergeId()) {
-    while (state->LoopMergeId() == block->id()) {
+  } else if (block->id() == state->BreakMergeId()) {
+    while (state->BreakMergeId() == block->id()) {
       state++;
     }
   }
@@ -346,15 +364,14 @@
   while (block != nullptr && block != final_return_block_) {
     if (!predicated->insert(block).second) break;
     // Skip structured subgraphs.
-    assert(state->InLoop() && "Should be in the dummy loop at the very least.");
-    Instruction* current_loop_merge_inst = state->LoopMergeInst();
-    uint32_t merge_block_id =
-        current_loop_merge_inst->GetSingleWordInOperand(0);
-    while (state->LoopMergeId() == merge_block_id) {
+    assert(state->InBreakable() &&
+           "Should be in the dummy construct at the very least.");
+    Instruction* break_merge_inst = state->BreakMergeInst();
+    uint32_t merge_block_id = break_merge_inst->GetSingleWordInOperand(0);
+    while (state->BreakMergeId() == merge_block_id) {
       state++;
     }
-    if (!BreakFromConstruct(block, predicated, order,
-                            current_loop_merge_inst)) {
+    if (!BreakFromConstruct(block, predicated, order, break_merge_inst)) {
       return false;
     }
     block = context()->get_instr_block(merge_block_id);
@@ -364,9 +381,7 @@
 
 bool MergeReturnPass::BreakFromConstruct(
     BasicBlock* block, std::unordered_set<BasicBlock*>* predicated,
-    std::list<BasicBlock*>* order, Instruction* loop_merge_inst) {
-  assert(loop_merge_inst->opcode() == SpvOpLoopMerge &&
-         "loop_merge_inst must be a loop merge instruction.");
+    std::list<BasicBlock*>* order, Instruction* break_merge_inst) {
   // Make sure the CFG is build here.  If we don't then it becomes very hard
   // to know which new blocks need to be updated.
   context()->BuildInvalidAnalyses(IRContext::kAnalysisCFG);
@@ -388,7 +403,7 @@
     }
   }
 
-  uint32_t merge_block_id = loop_merge_inst->GetSingleWordInOperand(0);
+  uint32_t merge_block_id = break_merge_inst->GetSingleWordInOperand(0);
   BasicBlock* merge_block = context()->get_instr_block(merge_block_id);
   if (merge_block->GetLoopMergeInst()) {
     cfg()->SplitLoopHeader(merge_block);
@@ -416,9 +431,10 @@
 
   // If |block| was a continue target for a loop |old_body| is now the correct
   // continue target.
-  if (loop_merge_inst->GetSingleWordInOperand(1) == block->id()) {
-    loop_merge_inst->SetInOperand(1, {old_body->id()});
-    context()->UpdateDefUse(loop_merge_inst);
+  if (break_merge_inst->opcode() == SpvOpLoopMerge &&
+      break_merge_inst->GetSingleWordInOperand(1) == block->id()) {
+    break_merge_inst->SetInOperand(1, {old_body->id()});
+    context()->UpdateDefUse(break_merge_inst);
   }
 
   // Update |order| so old_block will be traversed.
@@ -430,8 +446,8 @@
   // 3. Update OpPhi instructions in |merge_block|.
   // 4. Update the CFG.
   //
-  // Sine we are branching to the merge block of the current construct, there is
-  // no need for an OpSelectionMerge.
+  // Since we are branching to the merge block of the current construct, there
+  // is no need for an OpSelectionMerge.
 
   InstructionBuilder builder(
       context(), block,
@@ -710,7 +726,7 @@
   list->insert(pos, new_element);
 }
 
-void MergeReturnPass::AddDummyLoopAroundFunction() {
+void MergeReturnPass::AddDummySwitchAroundFunction() {
   CreateReturnBlock();
   CreateReturn(final_return_block_);
 
@@ -718,7 +734,7 @@
     cfg()->RegisterBlock(final_return_block_);
   }
 
-  CreateDummyLoop(final_return_block_);
+  CreateDummySwitch(final_return_block_);
 }
 
 BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) {
@@ -753,14 +769,8 @@
   return new_block;
 }
 
-void MergeReturnPass::CreateDummyLoop(BasicBlock* merge_target) {
-  std::unique_ptr<Instruction> label(
-      new Instruction(context(), SpvOpLabel, 0u, TakeNextId(), {}));
-
-  // Create the new basic block
-  std::unique_ptr<BasicBlock> block(new BasicBlock(std::move(label)));
-
-  // Insert the new block before any code is run.  We have to split the entry
+void MergeReturnPass::CreateDummySwitch(BasicBlock* merge_target) {
+  // Insert the switch before any code is run.  We have to split the entry
   // block to make sure the OpVariable instructions remain in the entry block.
   BasicBlock* start_block = &*function_->begin();
   auto split_pos = start_block->begin();
@@ -771,38 +781,16 @@
   BasicBlock* old_block =
       start_block->SplitBasicBlock(context(), TakeNextId(), split_pos);
 
-  // The new block must be inserted after the entry block.  We cannot make the
-  // entry block the header for the dummy loop because it is not valid to have a
-  // branch to the entry block, and the continue target must branch back to the
-  // loop header.
-  auto pos = function_->begin();
-  pos++;
-  BasicBlock* header_block = &*pos.InsertBefore(std::move(block));
-  context()->AnalyzeDefUse(header_block->GetLabelInst());
-  header_block->SetParent(function_);
-
-  // We have to create the continue block before OpLoopMerge instruction.
-  // Otherwise the def-use manager will compalain that there is a use without a
-  // definition.
-  uint32_t continue_target = CreateContinueTarget(header_block->id())->id();
-
-  // Add the code the the header block.
+  // Add the switch to the end of the entry block.
   InstructionBuilder builder(
-      context(), header_block,
-      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-
-  builder.AddLoopMerge(merge_target->id(), continue_target);
-  builder.AddBranch(old_block->id());
-
-  // Fix up the entry block by adding a branch to the loop header.
-  InstructionBuilder builder2(
       context(), start_block,
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-  builder2.AddBranch(header_block->id());
+
+  builder.AddSwitch(builder.GetUintConstantId(0u), old_block->id(), {},
+                    merge_target->id());
 
   if (context()->AreAnalysesValid(IRContext::kAnalysisCFG)) {
     cfg()->RegisterBlock(old_block);
-    cfg()->RegisterBlock(header_block);
     cfg()->AddEdges(start_block);
   }
 }
diff --git a/third_party/SPIRV-Tools/source/opt/merge_return_pass.h b/third_party/SPIRV-Tools/source/opt/merge_return_pass.h
index f8edd27..fe85557 100644
--- a/third_party/SPIRV-Tools/source/opt/merge_return_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/merge_return_pass.h
@@ -33,7 +33,8 @@
  * Structured control flow guarantees that the CFG will converge at a given
  * point (the merge block). Within structured control flow, all blocks must be
  * post-dominated by the merge block, except return blocks and break blocks.
- * A break block is a block that branches to the innermost loop's merge block.
+ * A break block is a block that branches to a containing construct's merge
+ * block.
  *
  * Beyond this, we further assume that all unreachable blocks have been
  * cleaned up.  This means that the only unreachable blocks are those necessary
@@ -46,13 +47,14 @@
  * with a branch. If current block is not within structured control flow, this
  * is the final return. This block should branch to the new return block (its
  * direct successor). If the current block is within structured control flow,
- * the branch destination should be the innermost loop's merge.  This loop will
- * always exist because a dummy loop is added around the entire function.
- * If the merge block produces any live values it will need to be predicated.
- * While the merge is nested in structured control flow, the predication path
- *should branch to the merge block of the inner-most loop it is contained in.
- *Once structured control flow has been exited, it will be at the merge of the
- *dummy loop, with will simply return.
+ * the branch destination should be the innermost construct's merge.  This
+ * merge will always exist because a dummy switch is added around the
+ * entire function. If the merge block produces any live values it will need to
+ * be predicated. While the merge is nested in structured control flow, the
+ * predication path should branch to the merge block of the inner-most loop
+ * (or switch if no loop) it is contained in. Once structured control flow has
+ * been exited, it will be at the merge of the dummy switch, which will simply
+ * return.
  *
  * In the final return block, the return value should be loaded and returned.
  * Memory promotion passes should be able to promote the newly introduced
@@ -71,7 +73,7 @@
  *         ||
  *         \/
  *
- *          0 (dummy loop header)
+ *          0 (dummy switch header)
  *          |
  *          1 (loop header)
  *         / \
@@ -81,11 +83,11 @@
  *        / \
  *        |  3 (original code in 3)
  *        \ /
- *   (ret) 4 (dummy loop merge)
+ *   (ret) 4 (dummy switch merge)
  *
  * In the above (simple) example, the return originally in |2| is passed through
- * the merge. That merge is predicated such that the old body of the block is
- * the else branch. The branch condition is based on the value of the "has
+ * the loop merge. That merge is predicated such that the old body of the block
+ * is the else branch. The branch condition is based on the value of the "has
  * returned" variable.
  *
  ******************************************************************************/
@@ -108,17 +110,17 @@
   }
 
  private:
-  // This class is used to store the a loop merge instruction and a selection
-  // merge instruction.  The intended use is that is represent the inner most
-  // contain selection construct and the inner most loop construct.
+  // This class is used to store the a break merge instruction and a current
+  // merge instruction.  The intended use is to keep track of the block to
+  // break to and the current innermost control flow construct merge block.
   class StructuredControlState {
    public:
-    StructuredControlState(Instruction* loop, Instruction* merge)
-        : loop_merge_(loop), current_merge_(merge) {}
+    StructuredControlState(Instruction* break_merge, Instruction* merge)
+        : break_merge_(break_merge), current_merge_(merge) {}
 
     StructuredControlState(const StructuredControlState&) = default;
 
-    bool InLoop() const { return loop_merge_; }
+    bool InBreakable() const { return break_merge_; }
     bool InStructuredFlow() const { return CurrentMergeId() != 0; }
 
     uint32_t CurrentMergeId() const {
@@ -132,20 +134,14 @@
                             : 0;
     }
 
-    uint32_t LoopMergeId() const {
-      return loop_merge_ ? loop_merge_->GetSingleWordInOperand(0u) : 0u;
+    uint32_t BreakMergeId() const {
+      return break_merge_ ? break_merge_->GetSingleWordInOperand(0u) : 0u;
     }
 
-    uint32_t CurrentLoopHeader() const {
-      return loop_merge_
-                 ? loop_merge_->context()->get_instr_block(loop_merge_)->id()
-                 : 0;
-    }
-
-    Instruction* LoopMergeInst() const { return loop_merge_; }
+    Instruction* BreakMergeInst() const { return break_merge_; }
 
    private:
-    Instruction* loop_merge_;
+    Instruction* break_merge_;
     Instruction* current_merge_;
   };
 
@@ -159,6 +155,9 @@
   void MergeReturnBlocks(Function* function,
                          const std::vector<BasicBlock*>& returnBlocks);
 
+  // Generate and push new control flow state if |block| contains a merge.
+  void GenerateState(BasicBlock* block);
+
   // Merges the return instruction in |function| so that it has a single return
   // statement.  It is assumed that |function| has structured control flow, and
   // that |return_blocks| is a list of all of the basic blocks in |function|
@@ -219,9 +218,9 @@
                        std::list<BasicBlock*>* order);
 
   // Add a conditional branch at the start of |block| that either jumps to
-  // the merge block of |loop_merge_inst| or the original code in |block|
+  // the merge block of |break_merge_inst| or the original code in |block|
   // depending on the value in |return_flag_|.  The continue target in
-  // |loop_merge_inst| will be updated if needed.
+  // |break_merge_inst| will be updated if needed.
   //
   // If new blocks that are created will be added to |order|.  This way a call
   // can traverse these new block in structured order.
@@ -230,7 +229,7 @@
   bool BreakFromConstruct(BasicBlock* block,
                           std::unordered_set<BasicBlock*>* predicated,
                           std::list<BasicBlock*>* order,
-                          Instruction* loop_merge_inst);
+                          Instruction* break_merge_inst);
 
   // Add an |OpReturn| or |OpReturnValue| to the end of |block|.  If an
   // |OpReturnValue| is needed, the return value is loaded from |return_value_|.
@@ -274,27 +273,28 @@
   void InsertAfterElement(BasicBlock* element, BasicBlock* new_element,
                           std::list<BasicBlock*>* list);
 
-  // Creates a single iteration loop around all of the exectuable code of the
-  // current function and returns after the loop is done. Sets
+  // Creates a single case switch around all of the exectuable code of the
+  // current function where the switch and case value are both zero and the
+  // default is the merge block. Returns after the switch is executed. Sets
   // |final_return_block_|.
-  void AddDummyLoopAroundFunction();
+  void AddDummySwitchAroundFunction();
 
   // Creates a new basic block that branches to |header_label_id|.  Returns the
   // new basic block.  The block will be the second last basic block in the
   // function.
   BasicBlock* CreateContinueTarget(uint32_t header_label_id);
 
-  // Creates a loop around the executable code of the function with
+  // Creates a one case switch around the executable code of the function with
   // |merge_target| as the merge node.
-  void CreateDummyLoop(BasicBlock* merge_target);
+  void CreateDummySwitch(BasicBlock* merge_target);
 
   // Returns true if |function| has an unreachable block that is not a continue
   // target that simply branches back to the header, or a merge block containing
   // 1 instruction which is OpUnreachable.
   bool HasNontrivialUnreachableBlocks(Function* function);
 
-  // A stack used to keep track of the innermost contain loop and selection
-  // constructs.
+  // A stack used to keep track of the break and current control flow construct
+  // merge blocks.
   std::vector<StructuredControlState> state_;
 
   // The current function being transformed.
diff --git a/third_party/SPIRV-Tools/source/opt/module.cpp b/third_party/SPIRV-Tools/source/opt/module.cpp
index c7fc247..4403894 100644
--- a/third_party/SPIRV-Tools/source/opt/module.cpp
+++ b/third_party/SPIRV-Tools/source/opt/module.cpp
@@ -95,6 +95,7 @@
   DELEGATE(debugs1_);
   DELEGATE(debugs2_);
   DELEGATE(debugs3_);
+  DELEGATE(ext_inst_debuginfo_);
   DELEGATE(annotations_);
   DELEGATE(types_values_);
   for (auto& i : functions_) i->ForEachInst(f, run_on_debug_line_insts);
@@ -117,6 +118,7 @@
   for (auto& i : debugs3_) DELEGATE(i);
   for (auto& i : annotations_) DELEGATE(i);
   for (auto& i : types_values_) DELEGATE(i);
+  for (auto& i : ext_inst_debuginfo_) DELEGATE(i);
   for (auto& i : functions_) {
     static_cast<const Function*>(i.get())->ForEachInst(f,
                                                        run_on_debug_line_insts);
diff --git a/third_party/SPIRV-Tools/source/opt/module.h b/third_party/SPIRV-Tools/source/opt/module.h
index aefa2a5..fc53d35 100644
--- a/third_party/SPIRV-Tools/source/opt/module.h
+++ b/third_party/SPIRV-Tools/source/opt/module.h
@@ -102,6 +102,10 @@
   // This is due to decision by the SPIR Working Group, pending publication.
   inline void AddDebug3Inst(std::unique_ptr<Instruction> d);
 
+  // Appends a debug info extension (OpenCL.DebugInfo.100 or DebugInfo)
+  // instruction to this module.
+  inline void AddExtInstDebugInfo(std::unique_ptr<Instruction> d);
+
   // Appends an annotation instruction to this module.
   inline void AddAnnotationInst(std::unique_ptr<Instruction> a);
 
@@ -182,6 +186,14 @@
   inline IteratorRange<inst_iterator> debugs3();
   inline IteratorRange<const_inst_iterator> debugs3() const;
 
+  // Iterators for debug info instructions (excluding OpLine & OpNoLine)
+  // contained in this module.  These are OpExtInst for OpenCL.DebugInfo.100
+  // or DebugInfo extension placed between section 9 and 10.
+  inline inst_iterator ext_inst_debuginfo_begin();
+  inline inst_iterator ext_inst_debuginfo_end();
+  inline IteratorRange<inst_iterator> ext_inst_debuginfo();
+  inline IteratorRange<const_inst_iterator> ext_inst_debuginfo() const;
+
   // Iterators for entry point instructions contained in this module
   inline IteratorRange<inst_iterator> entry_points();
   inline IteratorRange<const_inst_iterator> entry_points() const;
@@ -274,6 +286,7 @@
   InstructionList debugs1_;
   InstructionList debugs2_;
   InstructionList debugs3_;
+  InstructionList ext_inst_debuginfo_;
   InstructionList annotations_;
   // Type declarations, constants, and global variable declarations.
   InstructionList types_values_;
@@ -323,6 +336,10 @@
   debugs3_.push_back(std::move(d));
 }
 
+inline void Module::AddExtInstDebugInfo(std::unique_ptr<Instruction> d) {
+  ext_inst_debuginfo_.push_back(std::move(d));
+}
+
 inline void Module::AddAnnotationInst(std::unique_ptr<Instruction> a) {
   annotations_.push_back(std::move(a));
 }
@@ -403,6 +420,22 @@
   return make_range(debugs3_.begin(), debugs3_.end());
 }
 
+inline Module::inst_iterator Module::ext_inst_debuginfo_begin() {
+  return ext_inst_debuginfo_.begin();
+}
+inline Module::inst_iterator Module::ext_inst_debuginfo_end() {
+  return ext_inst_debuginfo_.end();
+}
+
+inline IteratorRange<Module::inst_iterator> Module::ext_inst_debuginfo() {
+  return make_range(ext_inst_debuginfo_.begin(), ext_inst_debuginfo_.end());
+}
+
+inline IteratorRange<Module::const_inst_iterator> Module::ext_inst_debuginfo()
+    const {
+  return make_range(ext_inst_debuginfo_.begin(), ext_inst_debuginfo_.end());
+}
+
 inline IteratorRange<Module::inst_iterator> Module::entry_points() {
   return make_range(entry_points_.begin(), entry_points_.end());
 }
diff --git a/third_party/SPIRV-Tools/source/opt/strip_debug_info_pass.cpp b/third_party/SPIRV-Tools/source/opt/strip_debug_info_pass.cpp
index 936c966..c86ce57 100644
--- a/third_party/SPIRV-Tools/source/opt/strip_debug_info_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/strip_debug_info_pass.cpp
@@ -75,6 +75,7 @@
 
   for (auto& dbg : context()->debugs2()) to_kill.push_back(&dbg);
   for (auto& dbg : context()->debugs3()) to_kill.push_back(&dbg);
+  for (auto& dbg : context()->ext_inst_debuginfo()) to_kill.push_back(&dbg);
 
   // OpName must come first, since they may refer to other debug instructions.
   // If they are after the instructions that refer to, then they will be killed
diff --git a/third_party/SPIRV-Tools/source/opt/strip_reflect_info_pass.cpp b/third_party/SPIRV-Tools/source/opt/strip_reflect_info_pass.cpp
index c231ead..8b0f2db 100644
--- a/third_party/SPIRV-Tools/source/opt/strip_reflect_info_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/strip_reflect_info_pass.cpp
@@ -77,6 +77,7 @@
   for (auto& dbg : context()->debugs1()) to_remove.push_back(&dbg);
   for (auto& dbg : context()->debugs2()) to_remove.push_back(&dbg);
   for (auto& dbg : context()->debugs3()) to_remove.push_back(&dbg);
+  for (auto& dbg : context()->ext_inst_debuginfo()) to_remove.push_back(&dbg);
 
   // remove any extended inst imports that are non semantic
   std::unordered_set<uint32_t> non_semantic_sets;
diff --git a/third_party/SPIRV-Tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp b/third_party/SPIRV-Tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
index da61c8d..ce66691 100644
--- a/third_party/SPIRV-Tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
+++ b/third_party/SPIRV-Tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
@@ -52,6 +52,13 @@
     result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
   }
 
+  for (auto& inst : context->module()->ext_inst_debuginfo()) {
+    if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
+      continue;
+    }
+    result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
+  }
+
   for (auto& inst : context->module()->types_values()) {
     if (context->get_def_use_mgr()->NumUsers(&inst) > 0) {
       continue;
diff --git a/third_party/SPIRV-Tools/source/spirv_target_env.cpp b/third_party/SPIRV-Tools/source/spirv_target_env.cpp
index 86f2c8d..e2ff99c 100644
--- a/third_party/SPIRV-Tools/source/spirv_target_env.cpp
+++ b/third_party/SPIRV-Tools/source/spirv_target_env.cpp
@@ -68,6 +68,8 @@
       return "SPIR-V 1.4 (under Vulkan 1.1 semantics)";
     case SPV_ENV_UNIVERSAL_1_5:
       return "SPIR-V 1.5";
+    case SPV_ENV_VULKAN_1_2:
+      return "SPIR-V 1.5 (under Vulkan 1.2 semantics)";
   }
   return "";
 }
@@ -102,6 +104,7 @@
     case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
       return SPV_SPIRV_VERSION_WORD(1, 4);
     case SPV_ENV_UNIVERSAL_1_5:
+    case SPV_ENV_VULKAN_1_2:
       return SPV_SPIRV_VERSION_WORD(1, 5);
   }
   return SPV_SPIRV_VERSION_WORD(0, 0);
@@ -111,6 +114,7 @@
     {"vulkan1.1spv1.4", SPV_ENV_VULKAN_1_1_SPIRV_1_4},
     {"vulkan1.0", SPV_ENV_VULKAN_1_0},
     {"vulkan1.1", SPV_ENV_VULKAN_1_1},
+    {"vulkan1.2", SPV_ENV_VULKAN_1_2},
     {"spv1.0", SPV_ENV_UNIVERSAL_1_0},
     {"spv1.1", SPV_ENV_UNIVERSAL_1_1},
     {"spv1.2", SPV_ENV_UNIVERSAL_1_2},
@@ -149,6 +153,34 @@
   return false;
 }
 
+#define VULKAN_VER(MAJOR, MINOR) ((MAJOR << 22) | (MINOR << 12))
+#define SPIRV_VER(MAJOR, MINOR) ((MAJOR << 16) | (MINOR << 8))
+
+struct VulkanEnv {
+  spv_target_env vulkan_env;
+  uint32_t vulkan_ver;
+  uint32_t spirv_ver;
+};
+// Maps each Vulkan target environment enum to the Vulkan version, and the
+// maximum supported SPIR-V version for that Vulkan environment.
+// Keep this ordered from least capable to most capable.
+static const VulkanEnv ordered_vulkan_envs[] = {
+    {SPV_ENV_VULKAN_1_0, VULKAN_VER(1, 0), SPIRV_VER(1, 0)},
+    {SPV_ENV_VULKAN_1_1, VULKAN_VER(1, 1), SPIRV_VER(1, 3)},
+    {SPV_ENV_VULKAN_1_1_SPIRV_1_4, VULKAN_VER(1, 1), SPIRV_VER(1, 4)},
+    {SPV_ENV_VULKAN_1_2, VULKAN_VER(1, 2), SPIRV_VER(1, 5)}};
+
+bool spvParseVulkanEnv(uint32_t vulkan_ver, uint32_t spirv_ver,
+                       spv_target_env* env) {
+  for (auto triple : ordered_vulkan_envs) {
+    if (triple.vulkan_ver >= vulkan_ver && triple.spirv_ver >= spirv_ver) {
+      *env = triple.vulkan_env;
+      return true;
+    }
+  }
+  return false;
+}
+
 bool spvIsVulkanEnv(spv_target_env env) {
   switch (env) {
     case SPV_ENV_UNIVERSAL_1_0:
@@ -175,6 +207,7 @@
     case SPV_ENV_VULKAN_1_0:
     case SPV_ENV_VULKAN_1_1:
     case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
+    case SPV_ENV_VULKAN_1_2:
       return true;
   }
   return false;
@@ -197,6 +230,7 @@
     case SPV_ENV_UNIVERSAL_1_4:
     case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
     case SPV_ENV_UNIVERSAL_1_5:
+    case SPV_ENV_VULKAN_1_2:
       return false;
     case SPV_ENV_OPENCL_1_2:
     case SPV_ENV_OPENCL_EMBEDDED_1_2:
@@ -235,6 +269,7 @@
     case SPV_ENV_UNIVERSAL_1_4:
     case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
     case SPV_ENV_UNIVERSAL_1_5:
+    case SPV_ENV_VULKAN_1_2:
       return false;
     case SPV_ENV_WEBGPU_0:
       return true;
@@ -262,6 +297,7 @@
     case SPV_ENV_UNIVERSAL_1_4:
     case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
     case SPV_ENV_UNIVERSAL_1_5:
+    case SPV_ENV_VULKAN_1_2:
       return false;
     case SPV_ENV_OPENGL_4_0:
     case SPV_ENV_OPENGL_4_1:
@@ -299,7 +335,8 @@
     case SPV_ENV_VULKAN_1_0:
     case SPV_ENV_VULKAN_1_1:
     case SPV_ENV_VULKAN_1_1_SPIRV_1_4: {
-      return "Vulkan";
+      case SPV_ENV_VULKAN_1_2:
+        return "Vulkan";
     }
     case SPV_ENV_WEBGPU_0: {
       return "WebGPU";
diff --git a/third_party/SPIRV-Tools/source/table.cpp b/third_party/SPIRV-Tools/source/table.cpp
index b7a96cc..8340e8e 100644
--- a/third_party/SPIRV-Tools/source/table.cpp
+++ b/third_party/SPIRV-Tools/source/table.cpp
@@ -41,6 +41,7 @@
     case SPV_ENV_WEBGPU_0:
     case SPV_ENV_UNIVERSAL_1_4:
     case SPV_ENV_UNIVERSAL_1_5:
+    case SPV_ENV_VULKAN_1_2:
       break;
     default:
       return nullptr;
diff --git a/third_party/SPIRV-Tools/source/val/construct.cpp b/third_party/SPIRV-Tools/source/val/construct.cpp
index 1564449..733856c 100644
--- a/third_party/SPIRV-Tools/source/val/construct.cpp
+++ b/third_party/SPIRV-Tools/source/val/construct.cpp
@@ -169,9 +169,22 @@
       return true;
     }
 
+    // The next block in the traversal is either:
+    //  i.  The header block that declares |block| as its merge block.
+    //  ii. The immediate dominator of |block|.
+    auto NextBlock = [](const BasicBlock* block) -> const BasicBlock* {
+      for (auto& use : block->label()->uses()) {
+        if ((use.first->opcode() == SpvOpLoopMerge ||
+             use.first->opcode() == SpvOpSelectionMerge) &&
+            use.second == 1)
+          return use.first->block();
+      }
+      return block->immediate_dominator();
+    };
+
     bool seen_switch = false;
     auto header = entry_block();
-    auto block = header->immediate_dominator();
+    auto block = NextBlock(header);
     while (block) {
       auto terminator = block->terminator();
       auto index = terminator - &_.ordered_instructions()[0];
@@ -183,7 +196,7 @@
         auto merge_target = merge_inst->GetOperandAs<uint32_t>(0u);
         auto merge_block = merge_inst->function()->GetBlock(merge_target).first;
         if (merge_block->dominates(*header)) {
-          block = block->immediate_dominator();
+          block = NextBlock(block);
           continue;
         }
 
@@ -197,13 +210,15 @@
           }
         }
 
-        if (terminator->opcode() == SpvOpSwitch) seen_switch = true;
+        if (terminator->opcode() == SpvOpSwitch) {
+          seen_switch = true;
+        }
 
         // Hit an enclosing loop and didn't break or continue.
         if (merge_inst->opcode() == SpvOpLoopMerge) return false;
       }
 
-      block = block->immediate_dominator();
+      block = NextBlock(block);
     }
   }
 
diff --git a/third_party/SPIRV-Tools/source/val/instruction.h b/third_party/SPIRV-Tools/source/val/instruction.h
index e30bfbc..617cb06 100644
--- a/third_party/SPIRV-Tools/source/val/instruction.h
+++ b/third_party/SPIRV-Tools/source/val/instruction.h
@@ -91,6 +91,12 @@
            spvExtInstIsNonSemantic(inst_.ext_inst_type);
   }
 
+  /// True if this is an OpExtInst for debug info extension.
+  bool IsDebugInfo() const {
+    return opcode() == SpvOp::SpvOpExtInst &&
+           spvExtInstIsDebugInfo(inst_.ext_inst_type);
+  }
+
   // Casts the words belonging to the operand under |index| to |T| and returns.
   template <typename T>
   T GetOperandAs(size_t index) const {
diff --git a/third_party/SPIRV-Tools/source/val/validate.cpp b/third_party/SPIRV-Tools/source/val/validate.cpp
index 7f4b0dc..168968d 100644
--- a/third_party/SPIRV-Tools/source/val/validate.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate.cpp
@@ -142,7 +142,7 @@
   for (const auto other_id : _.entry_points()) {
     if (other_id == id) continue;
     const auto other_id_names = CalculateNamesForEntryPoint(_, other_id);
-    for (const auto other_id_name : other_id_names) {
+    for (const auto& other_id_name : other_id_names) {
       if (names.find(other_id_name) != names.end()) {
         return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(id))
                << "Entry point name \"" << other_id_name
@@ -431,7 +431,7 @@
   if (auto error = ValidateBuiltIns(*vstate)) return error;
   // These checks must be performed after individual opcode checks because
   // those checks register the limitation checked here.
-  for (const auto inst : vstate->ordered_instructions()) {
+  for (const auto& inst : vstate->ordered_instructions()) {
     if (auto error = ValidateExecutionLimitations(*vstate, &inst)) return error;
     if (auto error = ValidateSmallTypeUses(*vstate, &inst)) return error;
   }
diff --git a/third_party/SPIRV-Tools/source/val/validate_adjacency.cpp b/third_party/SPIRV-Tools/source/val/validate_adjacency.cpp
index 108e361..64655b0 100644
--- a/third_party/SPIRV-Tools/source/val/validate_adjacency.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_adjacency.cpp
@@ -54,6 +54,16 @@
         adjacency_status =
             adjacency_status == IN_NEW_FUNCTION ? IN_ENTRY_BLOCK : PHI_VALID;
         break;
+      case SpvOpExtInst:
+        // If it is a debug info instruction, we do not change the status to
+        // allow debug info instructions before OpVariable in a function.
+        // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We need
+        // to discuss the location of DebugScope, DebugNoScope, DebugDeclare,
+        // and DebugValue.
+        if (!spvExtInstIsDebugInfo(inst.ext_inst_type())) {
+          adjacency_status = PHI_AND_VAR_INVALID;
+        }
+        break;
       case SpvOpPhi:
         if (adjacency_status != PHI_VALID) {
           return _.diag(SPV_ERROR_INVALID_DATA, &inst)
diff --git a/third_party/SPIRV-Tools/source/val/validate_capability.cpp b/third_party/SPIRV-Tools/source/val/validate_capability.cpp
index ad6cb26..8a356bf 100644
--- a/third_party/SPIRV-Tools/source/val/validate_capability.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_capability.cpp
@@ -55,6 +55,15 @@
   return false;
 }
 
+bool IsSupportGuaranteedVulkan_1_2(uint32_t capability) {
+  if (IsSupportGuaranteedVulkan_1_1(capability)) return true;
+  switch (capability) {
+    case SpvCapabilityShaderNonUniform:
+      return true;
+  }
+  return false;
+}
+
 bool IsSupportOptionalVulkan_1_0(uint32_t capability) {
   switch (capability) {
     case SpvCapabilityGeometry:
@@ -121,6 +130,38 @@
   return false;
 }
 
+bool IsSupportOptionalVulkan_1_2(uint32_t capability) {
+  if (IsSupportOptionalVulkan_1_1(capability)) return true;
+
+  switch (capability) {
+    case SpvCapabilityDenormPreserve:
+    case SpvCapabilityDenormFlushToZero:
+    case SpvCapabilitySignedZeroInfNanPreserve:
+    case SpvCapabilityRoundingModeRTE:
+    case SpvCapabilityRoundingModeRTZ:
+    case SpvCapabilityVulkanMemoryModel:
+    case SpvCapabilityVulkanMemoryModelDeviceScope:
+    case SpvCapabilityStorageBuffer8BitAccess:
+    case SpvCapabilityUniformAndStorageBuffer8BitAccess:
+    case SpvCapabilityStoragePushConstant8:
+    case SpvCapabilityShaderViewportIndex:
+    case SpvCapabilityShaderLayer:
+    case SpvCapabilityPhysicalStorageBufferAddresses:
+    case SpvCapabilityRuntimeDescriptorArray:
+    case SpvCapabilityUniformTexelBufferArrayDynamicIndexing:
+    case SpvCapabilityStorageTexelBufferArrayDynamicIndexing:
+    case SpvCapabilityUniformBufferArrayNonUniformIndexing:
+    case SpvCapabilitySampledImageArrayNonUniformIndexing:
+    case SpvCapabilityStorageBufferArrayNonUniformIndexing:
+    case SpvCapabilityStorageImageArrayNonUniformIndexing:
+    case SpvCapabilityInputAttachmentArrayNonUniformIndexing:
+    case SpvCapabilityUniformTexelBufferArrayNonUniformIndexing:
+    case SpvCapabilityStorageTexelBufferArrayNonUniformIndexing:
+      return true;
+  }
+  return false;
+}
+
 bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) {
   switch (capability) {
     case SpvCapabilityAddresses:
@@ -284,6 +325,15 @@
              << " is not allowed by Vulkan 1.1 specification"
              << " (or requires extension)";
     }
+  } else if (env == SPV_ENV_VULKAN_1_2) {
+    if (!IsSupportGuaranteedVulkan_1_2(capability) &&
+        !IsSupportOptionalVulkan_1_2(capability) &&
+        !IsEnabledByExtension(_, capability)) {
+      return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
+             << "Capability " << capability_str()
+             << " is not allowed by Vulkan 1.2 specification"
+             << " (or requires extension)";
+    }
   } else if (env == SPV_ENV_OPENCL_1_2 || env == SPV_ENV_OPENCL_EMBEDDED_1_2) {
     if (!IsSupportGuaranteedOpenCL_1_2(capability, opencl_embedded) &&
         !IsSupportOptionalOpenCL_1_2(capability) &&
diff --git a/third_party/SPIRV-Tools/source/val/validate_cfg.cpp b/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
index 43a6af7..f3019d1 100644
--- a/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
@@ -728,10 +728,10 @@
     }
 
     Construct::ConstructBlockSet construct_blocks = construct.blocks(function);
+    std::string construct_name, header_name, exit_name;
+    std::tie(construct_name, header_name, exit_name) =
+        ConstructNames(construct.type());
     for (auto block : construct_blocks) {
-      std::string construct_name, header_name, exit_name;
-      std::tie(construct_name, header_name, exit_name) =
-          ConstructNames(construct.type());
       // Check that all exits from the construct are via structured exits.
       for (auto succ : *block->successors()) {
         if (block->reachable() && !construct_blocks.count(succ) &&
diff --git a/third_party/SPIRV-Tools/source/val/validate_function.cpp b/third_party/SPIRV-Tools/source/val/validate_function.cpp
index f995ab3..f130eac 100644
--- a/third_party/SPIRV-Tools/source/val/validate_function.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_function.cpp
@@ -88,7 +88,7 @@
     const auto* use = pair.first;
     if (std::find(acceptable.begin(), acceptable.end(), use->opcode()) ==
             acceptable.end() &&
-        !use->IsNonSemantic()) {
+        !use->IsNonSemantic() && !use->IsDebugInfo()) {
       return _.diag(SPV_ERROR_INVALID_ID, use)
              << "Invalid use of function result id " << _.getIdName(inst->id())
              << ".";
diff --git a/third_party/SPIRV-Tools/source/val/validate_id.cpp b/third_party/SPIRV-Tools/source/val/validate_id.cpp
index 7406330..c171d31 100644
--- a/third_party/SPIRV-Tools/source/val/validate_id.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_id.cpp
@@ -131,7 +131,11 @@
 // instruction operand's ID can be forward referenced.
 spv_result_t IdPass(ValidationState_t& _, Instruction* inst) {
   auto can_have_forward_declared_ids =
-      spvOperandCanBeForwardDeclaredFunction(inst->opcode());
+      inst->opcode() == SpvOpExtInst &&
+              spvExtInstIsDebugInfo(inst->ext_inst_type())
+          ? spvDbgInfoExtOperandCanBeForwardDeclaredFunction(
+                inst->ext_inst_type(), inst->word(4))
+          : spvOperandCanBeForwardDeclaredFunction(inst->opcode());
 
   // Keep track of a result id defined by this instruction.  0 means it
   // does not define an id.
diff --git a/third_party/SPIRV-Tools/source/val/validate_image.cpp b/third_party/SPIRV-Tools/source/val/validate_image.cpp
index bc2753c..ed960d1 100644
--- a/third_party/SPIRV-Tools/source/val/validate_image.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_image.cpp
@@ -149,6 +149,17 @@
   return false;
 }
 
+bool IsValidLodOperand(const ValidationState_t& _, SpvOp opcode) {
+  switch (opcode) {
+    case SpvOpImageRead:
+    case SpvOpImageWrite:
+    case SpvOpImageSparseRead:
+      return _.HasCapability(SpvCapabilityImageReadWriteLodAMD);
+    default:
+      return IsExplicitLod(opcode);
+  }
+}
+
 // Returns true if the opcode is a Image instruction which applies
 // homogenous projection to the coordinates.
 bool IsProj(SpvOp opcode) {
@@ -248,6 +259,7 @@
 
   const bool is_implicit_lod = IsImplicitLod(opcode);
   const bool is_explicit_lod = IsExplicitLod(opcode);
+  const bool is_valid_lod_operand = IsValidLodOperand(_, opcode);
 
   // The checks should be done in the order of definition of OperandImage.
 
@@ -277,7 +289,7 @@
   }
 
   if (mask & SpvImageOperandsLodMask) {
-    if (!is_explicit_lod && opcode != SpvOpImageFetch &&
+    if (!is_valid_lod_operand && opcode != SpvOpImageFetch &&
         opcode != SpvOpImageSparseFetch) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand Lod can only be used with ExplicitLod opcodes "
diff --git a/third_party/SPIRV-Tools/source/val/validate_layout.cpp b/third_party/SPIRV-Tools/source/val/validate_layout.cpp
index 259befe..707ad52 100644
--- a/third_party/SPIRV-Tools/source/val/validate_layout.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_layout.cpp
@@ -14,15 +14,16 @@
 
 // Source code for logical layout validation as described in section 2.4
 
-#include "source/val/validate.h"
-
 #include <cassert>
 
+#include "DebugInfo.h"
+#include "OpenCLDebugInfo100.h"
 #include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/operand.h"
 #include "source/val/function.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
@@ -46,6 +47,53 @@
                  << "Non-semantic OpExtInst must not appear before types "
                  << "section";
         }
+      } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) {
+        const uint32_t ext_inst_index = inst->word(4);
+        bool local_debug_info = false;
+        if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+          const OpenCLDebugInfo100Instructions ext_inst_key =
+              OpenCLDebugInfo100Instructions(ext_inst_index);
+          if (ext_inst_key == OpenCLDebugInfo100DebugScope ||
+              ext_inst_key == OpenCLDebugInfo100DebugNoScope ||
+              ext_inst_key == OpenCLDebugInfo100DebugDeclare ||
+              ext_inst_key == OpenCLDebugInfo100DebugValue) {
+            local_debug_info = true;
+          }
+        } else {
+          const DebugInfoInstructions ext_inst_key =
+              DebugInfoInstructions(ext_inst_index);
+          if (ext_inst_key == DebugInfoDebugScope ||
+              ext_inst_key == DebugInfoDebugNoScope ||
+              ext_inst_key == DebugInfoDebugDeclare ||
+              ext_inst_key == DebugInfoDebugValue) {
+            local_debug_info = true;
+          }
+        }
+
+        if (local_debug_info) {
+          if (_.in_function_body() == false) {
+            // DebugScope, DebugNoScope, DebugDeclare, DebugValue must
+            // appear in a function body.
+            return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                   << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
+                   << "of debug info extension must appear in a function "
+                   << "body";
+          }
+        } else {
+          // Debug info extinst opcodes other than DebugScope, DebugNoScope,
+          // DebugDeclare, DebugValue must be placed between section 9 (types,
+          // constants, global variables) and section 10 (function
+          // declarations).
+          if (_.current_layout_section() < kLayoutTypes ||
+              _.current_layout_section() >= kLayoutFunctionDeclarations) {
+            return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                   << "Debug info extension instructions other than "
+                   << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
+                   << "must appear between section 9 (types, constants, "
+                   << "global variables) and section 10 (function "
+                   << "declarations)";
+          }
+        }
       } else {
         // otherwise they must be used in a block
         if (_.current_layout_section() < kLayoutFunctionDefinitions) {
@@ -182,6 +230,53 @@
                    << "Non-semantic OpExtInst within function definition must "
                       "appear in a block";
           }
+        } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) {
+          const uint32_t ext_inst_index = inst->word(4);
+          bool local_debug_info = false;
+          if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+            const OpenCLDebugInfo100Instructions ext_inst_key =
+                OpenCLDebugInfo100Instructions(ext_inst_index);
+            if (ext_inst_key == OpenCLDebugInfo100DebugScope ||
+                ext_inst_key == OpenCLDebugInfo100DebugNoScope ||
+                ext_inst_key == OpenCLDebugInfo100DebugDeclare ||
+                ext_inst_key == OpenCLDebugInfo100DebugValue) {
+              local_debug_info = true;
+            }
+          } else {
+            const DebugInfoInstructions ext_inst_key =
+                DebugInfoInstructions(ext_inst_index);
+            if (ext_inst_key == DebugInfoDebugScope ||
+                ext_inst_key == DebugInfoDebugNoScope ||
+                ext_inst_key == DebugInfoDebugDeclare ||
+                ext_inst_key == DebugInfoDebugValue) {
+              local_debug_info = true;
+            }
+          }
+
+          if (local_debug_info) {
+            if (_.in_function_body() == false) {
+              // DebugScope, DebugNoScope, DebugDeclare, DebugValue must
+              // appear in a function body.
+              return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                     << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
+                     << "of debug info extension must appear in a function "
+                     << "body";
+            }
+          } else {
+            // Debug info extinst opcodes other than DebugScope, DebugNoScope,
+            // DebugDeclare, DebugValue must be placed between section 9 (types,
+            // constants, global variables) and section 10 (function
+            // declarations).
+            if (_.current_layout_section() < kLayoutTypes ||
+                _.current_layout_section() >= kLayoutFunctionDeclarations) {
+              return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                     << "Debug info extension instructions other than "
+                     << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
+                     << "must appear between section 9 (types, constants, "
+                     << "global variables) and section 10 (function "
+                     << "declarations)";
+            }
+          }
         } else {
           // otherwise they must be used in a block
           if (_.in_block() == false) {
diff --git a/third_party/SPIRV-Tools/source/val/validate_scopes.cpp b/third_party/SPIRV-Tools/source/val/validate_scopes.cpp
index 1b98786..320d828 100644
--- a/third_party/SPIRV-Tools/source/val/validate_scopes.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_scopes.cpp
@@ -212,13 +212,14 @@
              << "Device, Workgroup and Invocation";
     }
     // Vulkan 1.1 specifc rules
-    if (_.context()->target_env == SPV_ENV_VULKAN_1_1 &&
+    if ((_.context()->target_env == SPV_ENV_VULKAN_1_1 ||
+         _.context()->target_env == SPV_ENV_VULKAN_1_2) &&
         value != SpvScopeDevice && value != SpvScopeWorkgroup &&
         value != SpvScopeSubgroup && value != SpvScopeInvocation) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << spvOpcodeString(opcode)
-             << ": in Vulkan 1.1 environment Memory Scope is limited to "
-             << "Device, Workgroup and Invocation";
+             << ": in Vulkan 1.1 and 1.2 environment Memory Scope is limited "
+             << "to Device, Workgroup and Invocation";
     }
   }
 
diff --git a/third_party/SPIRV-Tools/source/val/validation_state.cpp b/third_party/SPIRV-Tools/source/val/validation_state.cpp
index 51aebbe..0739148 100644
--- a/third_party/SPIRV-Tools/source/val/validation_state.cpp
+++ b/third_party/SPIRV-Tools/source/val/validation_state.cpp
@@ -1052,7 +1052,7 @@
 }
 
 void ValidationState_t::ComputeRecursiveEntryPoints() {
-  for (const Function func : functions()) {
+  for (const Function& func : functions()) {
     std::stack<uint32_t> call_stack;
     std::set<uint32_t> visited;
 
diff --git a/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt b/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
index fb9e964..4211ff2 100644
--- a/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
@@ -22,14 +22,19 @@
           fact_manager_test.cpp
           fuzz_test_util.cpp
           fuzzer_pass_add_useful_constructs_test.cpp
+          fuzzer_pass_donate_modules_test.cpp
           instruction_descriptor_test.cpp
+          transformation_access_chain_test.cpp
           transformation_add_constant_boolean_test.cpp
           transformation_add_constant_composite_test.cpp
           transformation_add_constant_scalar_test.cpp
+          transformation_add_dead_block_test.cpp
           transformation_add_dead_break_test.cpp
           transformation_add_dead_continue_test.cpp
+          transformation_add_function_test.cpp
           transformation_add_global_undef_test.cpp
           transformation_add_global_variable_test.cpp
+          transformation_add_local_variable_test.cpp
           transformation_add_no_contraction_decoration_test.cpp
           transformation_add_type_array_test.cpp
           transformation_add_type_boolean_test.cpp
@@ -43,6 +48,8 @@
           transformation_composite_construct_test.cpp
           transformation_composite_extract_test.cpp
           transformation_copy_object_test.cpp
+          transformation_function_call_test.cpp
+          transformation_load_test.cpp
           transformation_merge_blocks_test.cpp
           transformation_move_block_down_test.cpp
           transformation_outline_function_test.cpp
@@ -54,6 +61,7 @@
           transformation_set_memory_operands_mask_test.cpp
           transformation_set_selection_control_test.cpp
           transformation_split_block_test.cpp
+          transformation_store_test.cpp
           transformation_vector_shuffle_test.cpp
           uniform_buffer_element_descriptor_test.cpp)
 
diff --git a/third_party/SPIRV-Tools/test/fuzz/fuzz_test_util.cpp b/third_party/SPIRV-Tools/test/fuzz/fuzz_test_util.cpp
index 1d15ad6..c717961 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fuzz_test_util.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fuzz_test_util.cpp
@@ -76,6 +76,7 @@
   std::vector<uint32_t> binary;
   ir->module()->ToBinary(&binary, false);
   SpirvTools t(env);
+  t.SetMessageConsumer(kConsoleMessageConsumer);
   return t.Validate(binary);
 }
 
diff --git a/third_party/SPIRV-Tools/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/third_party/SPIRV-Tools/test/fuzz/fuzzer_pass_donate_modules_test.cpp
new file mode 100644
index 0000000..dc7ba3a
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/fuzzer_pass_donate_modules_test.cpp
@@ -0,0 +1,678 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_donate_modules.h"
+#include "source/fuzz/pseudo_random_generator.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(FuzzerPassDonateModulesTest, BasicDonation) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %10 "m"
+               OpName %16 "v"
+               OpDecorate %16 RelaxedPrecision
+               OpDecorate %20 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 3
+          %8 = OpTypeMatrix %7 2
+          %9 = OpTypePointer Private %8
+         %10 = OpVariable %9 Private
+         %11 = OpTypeInt 32 1
+         %12 = OpConstant %11 0
+         %13 = OpTypeInt 32 0
+         %14 = OpTypeVector %13 4
+         %15 = OpTypePointer Private %14
+         %16 = OpVariable %15 Private
+         %17 = OpConstant %13 2
+         %18 = OpTypePointer Private %13
+         %22 = OpConstant %13 0
+         %23 = OpTypePointer Private %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %19 = OpAccessChain %18 %16 %17
+         %20 = OpLoad %13 %19
+         %21 = OpConvertUToF %6 %20
+         %24 = OpAccessChain %23 %10 %12 %22
+               OpStore %24 %21
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %12 "bar(mf24;"
+               OpName %11 "m"
+               OpName %20 "foo(vu4;"
+               OpName %19 "v"
+               OpName %23 "x"
+               OpName %26 "param"
+               OpName %29 "result"
+               OpName %31 "i"
+               OpName %81 "param"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypeMatrix %7 2
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeFunction %6 %9
+         %14 = OpTypeInt 32 0
+         %15 = OpTypeVector %14 4
+         %16 = OpTypePointer Function %15
+         %17 = OpTypeInt 32 1
+         %18 = OpTypeFunction %17 %16
+         %22 = OpTypePointer Function %17
+         %24 = OpConstant %14 2
+         %25 = OpConstantComposite %15 %24 %24 %24 %24
+         %28 = OpTypePointer Function %6
+         %30 = OpConstant %6 0
+         %32 = OpConstant %17 0
+         %39 = OpConstant %17 10
+         %40 = OpTypeBool
+         %43 = OpConstant %17 3
+         %50 = OpConstant %17 1
+         %55 = OpConstant %14 0
+         %56 = OpTypePointer Function %14
+         %59 = OpConstant %14 1
+         %65 = OpConstant %17 2
+         %68 = OpConstant %6 1
+         %69 = OpConstant %6 2
+         %70 = OpConstant %6 3
+         %71 = OpConstant %6 4
+         %72 = OpConstant %14 3
+         %76 = OpConstant %6 6
+         %77 = OpConstant %6 7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %23 = OpVariable %22 Function
+         %26 = OpVariable %16 Function
+               OpStore %26 %25
+         %27 = OpFunctionCall %17 %20 %26
+               OpStore %23 %27
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9
+         %13 = OpLabel
+         %29 = OpVariable %28 Function
+         %31 = OpVariable %22 Function
+               OpStore %29 %30
+               OpStore %31 %32
+               OpBranch %33
+         %33 = OpLabel
+               OpLoopMerge %35 %36 None
+               OpBranch %37
+         %37 = OpLabel
+         %38 = OpLoad %17 %31
+         %41 = OpSLessThan %40 %38 %39
+               OpBranchConditional %41 %34 %35
+         %34 = OpLabel
+         %42 = OpLoad %17 %31
+         %44 = OpExtInst %17 %1 SClamp %42 %32 %43
+         %45 = OpAccessChain %28 %11 %32 %44
+         %46 = OpLoad %6 %45
+         %47 = OpLoad %6 %29
+         %48 = OpFAdd %6 %47 %46
+               OpStore %29 %48
+               OpBranch %36
+         %36 = OpLabel
+         %49 = OpLoad %17 %31
+         %51 = OpIAdd %17 %49 %50
+               OpStore %31 %51
+               OpBranch %33
+         %35 = OpLabel
+         %52 = OpLoad %6 %29
+               OpReturnValue %52
+               OpFunctionEnd
+         %20 = OpFunction %17 None %18
+         %19 = OpFunctionParameter %16
+         %21 = OpLabel
+         %81 = OpVariable %9 Function
+         %57 = OpAccessChain %56 %19 %55
+         %58 = OpLoad %14 %57
+         %60 = OpAccessChain %56 %19 %59
+         %61 = OpLoad %14 %60
+         %62 = OpUGreaterThan %40 %58 %61
+               OpSelectionMerge %64 None
+               OpBranchConditional %62 %63 %67
+         %63 = OpLabel
+               OpReturnValue %65
+         %67 = OpLabel
+         %73 = OpAccessChain %56 %19 %72
+         %74 = OpLoad %14 %73
+         %75 = OpConvertUToF %6 %74
+         %78 = OpCompositeConstruct %7 %30 %68 %69 %70
+         %79 = OpCompositeConstruct %7 %71 %75 %76 %77
+         %80 = OpCompositeConstruct %8 %78 %79
+               OpStore %81 %80
+         %82 = OpFunctionCall %6 %12 %81
+         %83 = OpConvertFToS %17 %82
+               OpReturnValue %83
+         %64 = OpLabel
+         %85 = OpUndef %17
+               OpReturnValue %85
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto recipient_context =
+      BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  const auto donor_context =
+      BuildModule(env, consumer, donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+  FactManager fact_manager;
+
+  auto prng = MakeUnique<PseudoRandomGenerator>(0);
+  FuzzerContext fuzzer_context(prng.get(), 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager,
+                                      &fuzzer_context, &transformation_sequence,
+                                      {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), false);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonationWithUniforms) {
+  // This test checks that when donating a shader that contains uniforms,
+  // uniform variables and associated pointer types are demoted from having
+  // Uniform storage class to Private storage class.
+  std::string recipient_and_donor_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpMemberDecorate %9 0 Offset 0
+               OpDecorate %9 Block
+               OpDecorate %11 DescriptorSet 0
+               OpDecorate %11 Binding 0
+               OpMemberDecorate %19 0 Offset 0
+               OpDecorate %19 Block
+               OpDecorate %21 DescriptorSet 0
+               OpDecorate %21 Binding 1
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpTypeStruct %6
+         %10 = OpTypePointer Uniform %9
+         %11 = OpVariable %10 Uniform
+         %12 = OpTypeInt 32 1
+         %13 = OpConstant %12 0
+         %14 = OpTypePointer Uniform %6
+         %17 = OpTypePointer Function %12
+         %19 = OpTypeStruct %12
+         %20 = OpTypePointer Uniform %19
+         %21 = OpVariable %20 Uniform
+         %22 = OpTypePointer Uniform %12
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %18 = OpVariable %17 Function
+         %15 = OpAccessChain %14 %11 %13
+         %16 = OpLoad %6 %15
+               OpStore %8 %16
+         %23 = OpAccessChain %22 %21 %13
+         %24 = OpLoad %12 %23
+               OpStore %18 %24
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto recipient_context = BuildModule(
+      env, consumer, recipient_and_donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  const auto donor_context = BuildModule(
+      env, consumer, recipient_and_donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+  FactManager fact_manager;
+
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager,
+                                      &fuzzer_context, &transformation_sequence,
+                                      {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), false);
+
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpMemberDecorate %9 0 Offset 0
+               OpDecorate %9 Block
+               OpDecorate %11 DescriptorSet 0
+               OpDecorate %11 Binding 0
+               OpMemberDecorate %19 0 Offset 0
+               OpDecorate %19 Block
+               OpDecorate %21 DescriptorSet 0
+               OpDecorate %21 Binding 1
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpTypeStruct %6
+         %10 = OpTypePointer Uniform %9
+         %11 = OpVariable %10 Uniform
+         %12 = OpTypeInt 32 1
+         %13 = OpConstant %12 0
+         %14 = OpTypePointer Uniform %6
+         %17 = OpTypePointer Function %12
+         %19 = OpTypeStruct %12
+         %20 = OpTypePointer Uniform %19
+         %21 = OpVariable %20 Uniform
+         %22 = OpTypePointer Uniform %12
+        %100 = OpTypePointer Function %6
+        %101 = OpTypeStruct %6
+        %102 = OpTypePointer Private %101
+        %104 = OpConstant %6 0
+        %105 = OpConstantComposite %101 %104
+        %103 = OpVariable %102 Private %105
+        %106 = OpConstant %12 0
+        %107 = OpTypePointer Private %6
+        %108 = OpTypePointer Function %12
+        %109 = OpTypeStruct %12
+        %110 = OpTypePointer Private %109
+        %112 = OpConstantComposite %109 %13
+        %111 = OpVariable %110 Private %112
+        %113 = OpTypePointer Private %12
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %18 = OpVariable %17 Function
+         %15 = OpAccessChain %14 %11 %13
+         %16 = OpLoad %6 %15
+               OpStore %8 %16
+         %23 = OpAccessChain %22 %21 %13
+         %24 = OpLoad %12 %23
+               OpStore %18 %24
+               OpReturn
+               OpFunctionEnd
+        %114 = OpFunction %2 None %3
+        %115 = OpLabel
+        %116 = OpVariable %100 Function %104
+        %117 = OpVariable %108 Function %13
+        %118 = OpAccessChain %107 %103 %106
+        %119 = OpLoad %6 %118
+               OpStore %116 %119
+        %120 = OpAccessChain %113 %111 %106
+        %121 = OpLoad %12 %120
+               OpStore %117 %121
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonationWithInputAndOutputVariables) {
+  // This test checks that when donating a shader that contains input and output
+  // variables, such variables and associated pointer types are demoted to have
+  // the Private storage class.
+  std::string recipient_and_donor_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %9 %11
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %9 Location 0
+               OpDecorate %11 Location 1
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypePointer Output %7
+          %9 = OpVariable %8 Output
+         %10 = OpTypePointer Input %7
+         %11 = OpVariable %10 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %12 = OpLoad %7 %11
+               OpStore %9 %12
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto recipient_context = BuildModule(
+      env, consumer, recipient_and_donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  const auto donor_context = BuildModule(
+      env, consumer, recipient_and_donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+  FactManager fact_manager;
+
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager,
+                                      &fuzzer_context, &transformation_sequence,
+                                      {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), false);
+
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %9 %11
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %9 Location 0
+               OpDecorate %11 Location 1
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypePointer Output %7
+          %9 = OpVariable %8 Output
+         %10 = OpTypePointer Input %7
+         %11 = OpVariable %10 Input
+        %100 = OpTypePointer Private %7
+        %102 = OpConstant %6 0
+        %103 = OpConstantComposite %7 %102 %102 %102 %102
+        %101 = OpVariable %100 Private %103
+        %104 = OpTypePointer Private %7
+        %105 = OpVariable %104 Private %103
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %12 = OpLoad %7 %11
+               OpStore %9 %12
+               OpReturn
+               OpFunctionEnd
+        %106 = OpFunction %2 None %3
+        %107 = OpLabel
+        %108 = OpLoad %7 %105
+               OpStore %101 %108
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateFunctionTypeWithDifferentPointers) {
+  std::string recipient_and_donor_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 0
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %9 = OpVariable %7 Function
+         %10 = OpFunctionCall %2 %11 %9
+               OpReturn
+               OpFunctionEnd
+         %11 = OpFunction %2 None %8
+         %12 = OpFunctionParameter %7
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto recipient_context = BuildModule(
+      env, consumer, recipient_and_donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  const auto donor_context = BuildModule(
+      env, consumer, recipient_and_donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+  FactManager fact_manager;
+
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager,
+                                      &fuzzer_context, &transformation_sequence,
+                                      {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), false);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, Miscellaneous1) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %6 "foo("
+               OpName %10 "x"
+               OpName %12 "i"
+               OpName %33 "i"
+               OpName %42 "j"
+               OpDecorate %10 RelaxedPrecision
+               OpDecorate %12 RelaxedPrecision
+               OpDecorate %19 RelaxedPrecision
+               OpDecorate %23 RelaxedPrecision
+               OpDecorate %24 RelaxedPrecision
+               OpDecorate %25 RelaxedPrecision
+               OpDecorate %26 RelaxedPrecision
+               OpDecorate %27 RelaxedPrecision
+               OpDecorate %28 RelaxedPrecision
+               OpDecorate %30 RelaxedPrecision
+               OpDecorate %33 RelaxedPrecision
+               OpDecorate %39 RelaxedPrecision
+               OpDecorate %42 RelaxedPrecision
+               OpDecorate %49 RelaxedPrecision
+               OpDecorate %52 RelaxedPrecision
+               OpDecorate %53 RelaxedPrecision
+               OpDecorate %58 RelaxedPrecision
+               OpDecorate %59 RelaxedPrecision
+               OpDecorate %60 RelaxedPrecision
+               OpDecorate %63 RelaxedPrecision
+               OpDecorate %64 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %8 2
+         %13 = OpConstant %8 0
+         %20 = OpConstant %8 100
+         %21 = OpTypeBool
+         %29 = OpConstant %8 1
+         %40 = OpConstant %8 10
+         %43 = OpConstant %8 20
+         %61 = OpConstant %8 4
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %33 = OpVariable %9 Function
+         %42 = OpVariable %9 Function
+         %32 = OpFunctionCall %2 %6
+               OpStore %33 %13
+               OpBranch %34
+         %34 = OpLabel
+               OpLoopMerge %36 %37 None
+               OpBranch %38
+         %38 = OpLabel
+         %39 = OpLoad %8 %33
+         %41 = OpSLessThan %21 %39 %40
+               OpBranchConditional %41 %35 %36
+         %35 = OpLabel
+               OpStore %42 %43
+               OpBranch %44
+         %44 = OpLabel
+               OpLoopMerge %46 %47 None
+               OpBranch %48
+         %48 = OpLabel
+         %49 = OpLoad %8 %42
+         %50 = OpSGreaterThan %21 %49 %13
+               OpBranchConditional %50 %45 %46
+         %45 = OpLabel
+         %51 = OpFunctionCall %2 %6
+         %52 = OpLoad %8 %42
+         %53 = OpISub %8 %52 %29
+               OpStore %42 %53
+               OpBranch %47
+         %47 = OpLabel
+               OpBranch %44
+         %46 = OpLabel
+               OpBranch %54
+         %54 = OpLabel
+               OpLoopMerge %56 %57 None
+               OpBranch %55
+         %55 = OpLabel
+         %58 = OpLoad %8 %33
+         %59 = OpIAdd %8 %58 %29
+               OpStore %33 %59
+               OpBranch %57
+         %57 = OpLabel
+         %60 = OpLoad %8 %33
+         %62 = OpSLessThan %21 %60 %61
+               OpBranchConditional %62 %54 %56
+         %56 = OpLabel
+               OpBranch %37
+         %37 = OpLabel
+         %63 = OpLoad %8 %33
+         %64 = OpIAdd %8 %63 %29
+               OpStore %33 %64
+               OpBranch %34
+         %36 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+         %10 = OpVariable %9 Function
+         %12 = OpVariable %9 Function
+               OpStore %10 %11
+               OpStore %12 %13
+               OpBranch %14
+         %14 = OpLabel
+               OpLoopMerge %16 %17 None
+               OpBranch %18
+         %18 = OpLabel
+         %19 = OpLoad %8 %12
+         %22 = OpSLessThan %21 %19 %20
+               OpBranchConditional %22 %15 %16
+         %15 = OpLabel
+         %23 = OpLoad %8 %12
+         %24 = OpLoad %8 %10
+         %25 = OpIAdd %8 %24 %23
+               OpStore %10 %25
+         %26 = OpLoad %8 %10
+         %27 = OpIMul %8 %26 %11
+               OpStore %10 %27
+               OpBranch %17
+         %17 = OpLabel
+         %28 = OpLoad %8 %12
+         %30 = OpIAdd %8 %28 %29
+               OpStore %12 %30
+               OpBranch %14
+         %16 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto recipient_context =
+      BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  const auto donor_context =
+      BuildModule(env, consumer, donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+  FactManager fact_manager;
+
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager,
+                                      &fuzzer_context, &transformation_sequence,
+                                      {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), false);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp b/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
index fa1f096..f444695 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/fuzzer.h"
+#include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/replayer.h"
 #include "source/fuzz/uniform_buffer_element_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
@@ -23,109 +24,35 @@
 
 const uint32_t kNumFuzzerRuns = 20;
 
-void AddConstantUniformFact(protobufs::FactSequence* facts,
-                            uint32_t descriptor_set, uint32_t binding,
-                            std::vector<uint32_t>&& indices, uint32_t value) {
-  protobufs::FactConstantUniform fact;
-  *fact.mutable_uniform_buffer_element_descriptor() =
-      MakeUniformBufferElementDescriptor(descriptor_set, binding,
-                                         std::move(indices));
-  *fact.mutable_constant_word()->Add() = value;
-  protobufs::Fact temp;
-  *temp.mutable_constant_uniform_fact() = fact;
-  *facts->mutable_fact()->Add() = temp;
-}
+// The SPIR-V came from this GLSL:
+//
+// #version 310 es
+//
+// void foo() {
+//   int x;
+//   x = 2;
+//   for (int i = 0; i < 100; i++) {
+//     x += i;
+//     x = x * 2;
+//   }
+//   return;
+// }
+//
+// void main() {
+//   foo();
+//   for (int i = 0; i < 10; i++) {
+//     int j = 20;
+//     while(j > 0) {
+//       foo();
+//       j--;
+//     }
+//     do {
+//       i++;
+//     } while(i < 4);
+//   }
+// }
 
-// Reinterpret the bits of |value| as a 32-bit unsigned int
-uint32_t FloatBitsAsUint(float value) {
-  uint32_t result;
-  memcpy(&result, &value, sizeof(float));
-  return result;
-}
-
-// Assembles the given |shader| text, and then runs the fuzzer |num_runs|
-// times, using successive seeds starting from |initial_seed|.  Checks that
-// the binary produced after each fuzzer run is valid, and that replaying
-// the transformations that were applied during fuzzing leads to an
-// identical binary.
-void RunFuzzerAndReplayer(const std::string& shader,
-                          const protobufs::FactSequence& initial_facts,
-                          uint32_t initial_seed, uint32_t num_runs) {
-  const auto env = SPV_ENV_UNIVERSAL_1_5;
-
-  std::vector<uint32_t> binary_in;
-  SpirvTools t(env);
-  t.SetMessageConsumer(kConsoleMessageConsumer);
-  ASSERT_TRUE(t.Assemble(shader, &binary_in, kFuzzAssembleOption));
-  ASSERT_TRUE(t.Validate(binary_in));
-
-  for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) {
-    std::vector<uint32_t> fuzzer_binary_out;
-    protobufs::TransformationSequence fuzzer_transformation_sequence_out;
-
-    Fuzzer fuzzer(env, seed, true);
-    fuzzer.SetMessageConsumer(kSilentConsumer);
-    auto fuzzer_result_status =
-        fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out,
-                   &fuzzer_transformation_sequence_out);
-    ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status);
-    ASSERT_TRUE(t.Validate(fuzzer_binary_out));
-
-    std::vector<uint32_t> replayer_binary_out;
-    protobufs::TransformationSequence replayer_transformation_sequence_out;
-
-    Replayer replayer(env, false);
-    replayer.SetMessageConsumer(kSilentConsumer);
-    auto replayer_result_status = replayer.Run(
-        binary_in, initial_facts, fuzzer_transformation_sequence_out,
-        &replayer_binary_out, &replayer_transformation_sequence_out);
-    ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
-              replayer_result_status);
-
-    // After replaying the transformations applied by the fuzzer, exactly those
-    // transformations should have been applied, and the binary resulting from
-    // replay should be identical to that which resulted from fuzzing.
-    std::string fuzzer_transformations_string;
-    std::string replayer_transformations_string;
-    fuzzer_transformation_sequence_out.SerializeToString(
-        &fuzzer_transformations_string);
-    replayer_transformation_sequence_out.SerializeToString(
-        &replayer_transformations_string);
-    ASSERT_EQ(fuzzer_transformations_string, replayer_transformations_string);
-    ASSERT_EQ(fuzzer_binary_out, replayer_binary_out);
-  }
-}
-
-TEST(FuzzerReplayerTest, Miscellaneous1) {
-  // The SPIR-V came from this GLSL:
-  //
-  // #version 310 es
-  //
-  // void foo() {
-  //   int x;
-  //   x = 2;
-  //   for (int i = 0; i < 100; i++) {
-  //     x += i;
-  //     x = x * 2;
-  //   }
-  //   return;
-  // }
-  //
-  // void main() {
-  //   foo();
-  //   for (int i = 0; i < 10; i++) {
-  //     int j = 20;
-  //     while(j > 0) {
-  //       foo();
-  //       j--;
-  //     }
-  //     do {
-  //       i++;
-  //     } while(i < 4);
-  //   }
-  // }
-
-  std::string shader = R"(
+const std::string kTestShader1 = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
@@ -260,66 +187,60 @@
                OpFunctionEnd
   )";
 
-  // Do some fuzzer runs, starting from an initial seed of 0 (seed value chosen
-  // arbitrarily).
-  RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 0, kNumFuzzerRuns);
-}
+// The SPIR-V came from this GLSL, which was then optimized using spirv-opt
+// with the -O argument:
+//
+// #version 310 es
+//
+// precision highp float;
+//
+// layout(location = 0) out vec4 _GLF_color;
+//
+// layout(set = 0, binding = 0) uniform buf0 {
+//  vec2 injectionSwitch;
+// };
+// layout(set = 0, binding = 1) uniform buf1 {
+//  vec2 resolution;
+// };
+// bool checkSwap(float a, float b)
+// {
+//  return gl_FragCoord.y < resolution.y / 2.0 ? a > b : a < b;
+// }
+// void main()
+// {
+//  float data[10];
+//  for(int i = 0; i < 10; i++)
+//   {
+//    data[i] = float(10 - i) * injectionSwitch.y;
+//   }
+//  for(int i = 0; i < 9; i++)
+//   {
+//    for(int j = 0; j < 10; j++)
+//     {
+//      if(j < i + 1)
+//       {
+//        continue;
+//       }
+//      bool doSwap = checkSwap(data[i], data[j]);
+//      if(doSwap)
+//       {
+//        float temp = data[i];
+//        data[i] = data[j];
+//        data[j] = temp;
+//       }
+//     }
+//   }
+//  if(gl_FragCoord.x < resolution.x / 2.0)
+//   {
+//    _GLF_color = vec4(data[0] / 10.0, data[5] / 10.0, data[9] / 10.0, 1.0);
+//   }
+//  else
+//   {
+//    _GLF_color = vec4(data[5] / 10.0, data[9] / 10.0, data[0] / 10.0, 1.0);
+//   }
+// }
 
-TEST(FuzzerReplayerTest, Miscellaneous2) {
-  // The SPIR-V came from this GLSL, which was then optimized using spirv-opt
-  // with the -O argument:
-  //
-  // #version 310 es
-  //
-  // precision highp float;
-  //
-  // layout(location = 0) out vec4 _GLF_color;
-  //
-  // layout(set = 0, binding = 0) uniform buf0 {
-  //  vec2 injectionSwitch;
-  // };
-  // layout(set = 0, binding = 1) uniform buf1 {
-  //  vec2 resolution;
-  // };
-  // bool checkSwap(float a, float b)
-  // {
-  //  return gl_FragCoord.y < resolution.y / 2.0 ? a > b : a < b;
-  // }
-  // void main()
-  // {
-  //  float data[10];
-  //  for(int i = 0; i < 10; i++)
-  //   {
-  //    data[i] = float(10 - i) * injectionSwitch.y;
-  //   }
-  //  for(int i = 0; i < 9; i++)
-  //   {
-  //    for(int j = 0; j < 10; j++)
-  //     {
-  //      if(j < i + 1)
-  //       {
-  //        continue;
-  //       }
-  //      bool doSwap = checkSwap(data[i], data[j]);
-  //      if(doSwap)
-  //       {
-  //        float temp = data[i];
-  //        data[i] = data[j];
-  //        data[j] = temp;
-  //       }
-  //     }
-  //   }
-  //  if(gl_FragCoord.x < resolution.x / 2.0)
-  //   {
-  //    _GLF_color = vec4(data[0] / 10.0, data[5] / 10.0, data[9] / 10.0, 1.0);
-  //   }
-  //  else
-  //   {
-  //    _GLF_color = vec4(data[5] / 10.0, data[9] / 10.0, data[0] / 10.0, 1.0);
-  //   }
-  // }
-
-  std::string shader = R"(
+const std::string kTestShader2 = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
@@ -507,121 +428,115 @@
                OpFunctionEnd
   )";
 
-  // Do some fuzzer runs, starting from an initial seed of 10 (seed value chosen
-  // arbitrarily).
-  RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 10, kNumFuzzerRuns);
-}
+// The SPIR-V came from this GLSL, which was then optimized using spirv-opt
+// with the -O argument:
+//
+// #version 310 es
+//
+// precision highp float;
+//
+// layout(location = 0) out vec4 _GLF_color;
+//
+// layout(set = 0, binding = 0) uniform buf0 {
+//  vec2 resolution;
+// };
+// void main(void)
+// {
+//  float A[50];
+//  for(
+//      int i = 0;
+//      i < 200;
+//      i ++
+//  )
+//   {
+//    if(i >= int(resolution.x))
+//     {
+//      break;
+//     }
+//    if((4 * (i / 4)) == i)
+//     {
+//      A[i / 4] = float(i);
+//     }
+//   }
+//  for(
+//      int i = 0;
+//      i < 50;
+//      i ++
+//  )
+//   {
+//    if(i < int(gl_FragCoord.x))
+//     {
+//      break;
+//     }
+//    if(i > 0)
+//     {
+//      A[i] += A[i - 1];
+//     }
+//   }
+//  if(int(gl_FragCoord.x) < 20)
+//   {
+//    _GLF_color = vec4(A[0] / resolution.x, A[4] / resolution.y, 1.0, 1.0);
+//   }
+//  else
+//   if(int(gl_FragCoord.x) < 40)
+//    {
+//     _GLF_color = vec4(A[5] / resolution.x, A[9] / resolution.y, 1.0, 1.0);
+//    }
+//   else
+//    if(int(gl_FragCoord.x) < 60)
+//     {
+//      _GLF_color = vec4(A[10] / resolution.x, A[14] / resolution.y,
+//      1.0, 1.0);
+//     }
+//    else
+//     if(int(gl_FragCoord.x) < 80)
+//      {
+//       _GLF_color = vec4(A[15] / resolution.x, A[19] / resolution.y,
+//       1.0, 1.0);
+//      }
+//     else
+//      if(int(gl_FragCoord.x) < 100)
+//       {
+//        _GLF_color = vec4(A[20] / resolution.x, A[24] / resolution.y,
+//        1.0, 1.0);
+//       }
+//      else
+//       if(int(gl_FragCoord.x) < 120)
+//        {
+//         _GLF_color = vec4(A[25] / resolution.x, A[29] / resolution.y,
+//         1.0, 1.0);
+//        }
+//       else
+//        if(int(gl_FragCoord.x) < 140)
+//         {
+//          _GLF_color = vec4(A[30] / resolution.x, A[34] / resolution.y,
+//          1.0, 1.0);
+//         }
+//        else
+//         if(int(gl_FragCoord.x) < 160)
+//          {
+//           _GLF_color = vec4(A[35] / resolution.x, A[39] /
+//           resolution.y, 1.0, 1.0);
+//          }
+//         else
+//          if(int(gl_FragCoord.x) < 180)
+//           {
+//            _GLF_color = vec4(A[40] / resolution.x, A[44] /
+//            resolution.y, 1.0, 1.0);
+//           }
+//          else
+//           if(int(gl_FragCoord.x) < 180)
+//            {
+//             _GLF_color = vec4(A[45] / resolution.x, A[49] /
+//             resolution.y, 1.0, 1.0);
+//            }
+//           else
+//            {
+//             discard;
+//            }
+// }
 
-TEST(FuzzerReplayerTest, Miscellaneous3) {
-  // The SPIR-V came from this GLSL, which was then optimized using spirv-opt
-  // with the -O argument:
-  //
-  // #version 310 es
-  //
-  // precision highp float;
-  //
-  // layout(location = 0) out vec4 _GLF_color;
-  //
-  // layout(set = 0, binding = 0) uniform buf0 {
-  //  vec2 resolution;
-  // };
-  // void main(void)
-  // {
-  //  float A[50];
-  //  for(
-  //      int i = 0;
-  //      i < 200;
-  //      i ++
-  //  )
-  //   {
-  //    if(i >= int(resolution.x))
-  //     {
-  //      break;
-  //     }
-  //    if((4 * (i / 4)) == i)
-  //     {
-  //      A[i / 4] = float(i);
-  //     }
-  //   }
-  //  for(
-  //      int i = 0;
-  //      i < 50;
-  //      i ++
-  //  )
-  //   {
-  //    if(i < int(gl_FragCoord.x))
-  //     {
-  //      break;
-  //     }
-  //    if(i > 0)
-  //     {
-  //      A[i] += A[i - 1];
-  //     }
-  //   }
-  //  if(int(gl_FragCoord.x) < 20)
-  //   {
-  //    _GLF_color = vec4(A[0] / resolution.x, A[4] / resolution.y, 1.0, 1.0);
-  //   }
-  //  else
-  //   if(int(gl_FragCoord.x) < 40)
-  //    {
-  //     _GLF_color = vec4(A[5] / resolution.x, A[9] / resolution.y, 1.0, 1.0);
-  //    }
-  //   else
-  //    if(int(gl_FragCoord.x) < 60)
-  //     {
-  //      _GLF_color = vec4(A[10] / resolution.x, A[14] / resolution.y,
-  //      1.0, 1.0);
-  //     }
-  //    else
-  //     if(int(gl_FragCoord.x) < 80)
-  //      {
-  //       _GLF_color = vec4(A[15] / resolution.x, A[19] / resolution.y,
-  //       1.0, 1.0);
-  //      }
-  //     else
-  //      if(int(gl_FragCoord.x) < 100)
-  //       {
-  //        _GLF_color = vec4(A[20] / resolution.x, A[24] / resolution.y,
-  //        1.0, 1.0);
-  //       }
-  //      else
-  //       if(int(gl_FragCoord.x) < 120)
-  //        {
-  //         _GLF_color = vec4(A[25] / resolution.x, A[29] / resolution.y,
-  //         1.0, 1.0);
-  //        }
-  //       else
-  //        if(int(gl_FragCoord.x) < 140)
-  //         {
-  //          _GLF_color = vec4(A[30] / resolution.x, A[34] / resolution.y,
-  //          1.0, 1.0);
-  //         }
-  //        else
-  //         if(int(gl_FragCoord.x) < 160)
-  //          {
-  //           _GLF_color = vec4(A[35] / resolution.x, A[39] /
-  //           resolution.y, 1.0, 1.0);
-  //          }
-  //         else
-  //          if(int(gl_FragCoord.x) < 180)
-  //           {
-  //            _GLF_color = vec4(A[40] / resolution.x, A[44] /
-  //            resolution.y, 1.0, 1.0);
-  //           }
-  //          else
-  //           if(int(gl_FragCoord.x) < 180)
-  //            {
-  //             _GLF_color = vec4(A[45] / resolution.x, A[49] /
-  //             resolution.y, 1.0, 1.0);
-  //            }
-  //           else
-  //            {
-  //             discard;
-  //            }
-  // }
-
-  std::string shader = R"(
+const std::string kTestShader3 = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
@@ -969,21 +884,10 @@
                OpFunctionEnd
   )";
 
-  // Add the facts "resolution.x == 250" and "resolution.y == 100".
-  protobufs::FactSequence facts;
-  AddConstantUniformFact(&facts, 0, 0, {0, 0}, 250);
-  AddConstantUniformFact(&facts, 0, 0, {0, 1}, 100);
+// The SPIR-V comes from the 'matrices_smart_loops' GLSL shader that ships
+// with GraphicsFuzz.
 
-  // Do some fuzzer runs, starting from an initial seed of 94 (seed value chosen
-  // arbitrarily).
-  RunFuzzerAndReplayer(shader, facts, 94, kNumFuzzerRuns);
-}
-
-TEST(FuzzerReplayerTest, Miscellaneous4) {
-  // The SPIR-V comes from the 'matrices_smart_loops' GLSL shader that ships
-  // with GraphicsFuzz.
-
-  std::string shader = R"(
+const std::string kTestShader4 = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
@@ -1611,6 +1515,114 @@
                OpFunctionEnd
   )";
 
+void AddConstantUniformFact(protobufs::FactSequence* facts,
+                            uint32_t descriptor_set, uint32_t binding,
+                            std::vector<uint32_t>&& indices, uint32_t value) {
+  protobufs::FactConstantUniform fact;
+  *fact.mutable_uniform_buffer_element_descriptor() =
+      MakeUniformBufferElementDescriptor(descriptor_set, binding,
+                                         std::move(indices));
+  *fact.mutable_constant_word()->Add() = value;
+  protobufs::Fact temp;
+  *temp.mutable_constant_uniform_fact() = fact;
+  *facts->mutable_fact()->Add() = temp;
+}
+
+// Reinterpret the bits of |value| as a 32-bit unsigned int
+uint32_t FloatBitsAsUint(float value) {
+  uint32_t result;
+  memcpy(&result, &value, sizeof(float));
+  return result;
+}
+
+// Assembles the given |shader| text, and then runs the fuzzer |num_runs|
+// times, using successive seeds starting from |initial_seed|.  Checks that
+// the binary produced after each fuzzer run is valid, and that replaying
+// the transformations that were applied during fuzzing leads to an
+// identical binary.
+void RunFuzzerAndReplayer(const std::string& shader,
+                          const protobufs::FactSequence& initial_facts,
+                          uint32_t initial_seed, uint32_t num_runs) {
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+
+  std::vector<uint32_t> binary_in;
+  SpirvTools t(env);
+  t.SetMessageConsumer(kConsoleMessageConsumer);
+  ASSERT_TRUE(t.Assemble(shader, &binary_in, kFuzzAssembleOption));
+  ASSERT_TRUE(t.Validate(binary_in));
+
+  std::vector<fuzzerutil::ModuleSupplier> donor_suppliers;
+  for (auto donor :
+       {&kTestShader1, &kTestShader2, &kTestShader3, &kTestShader4}) {
+    donor_suppliers.emplace_back([donor]() {
+      return BuildModule(env, kConsoleMessageConsumer, *donor,
+                         kFuzzAssembleOption);
+    });
+  }
+
+  for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) {
+    std::vector<uint32_t> fuzzer_binary_out;
+    protobufs::TransformationSequence fuzzer_transformation_sequence_out;
+
+    Fuzzer fuzzer(env, seed, true);
+    fuzzer.SetMessageConsumer(kSilentConsumer);
+    auto fuzzer_result_status =
+        fuzzer.Run(binary_in, initial_facts, donor_suppliers,
+                   &fuzzer_binary_out, &fuzzer_transformation_sequence_out);
+    ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status);
+    ASSERT_TRUE(t.Validate(fuzzer_binary_out));
+
+    std::vector<uint32_t> replayer_binary_out;
+    protobufs::TransformationSequence replayer_transformation_sequence_out;
+
+    Replayer replayer(env, false);
+    replayer.SetMessageConsumer(kSilentConsumer);
+    auto replayer_result_status = replayer.Run(
+        binary_in, initial_facts, fuzzer_transformation_sequence_out,
+        &replayer_binary_out, &replayer_transformation_sequence_out);
+    ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
+              replayer_result_status);
+
+    // After replaying the transformations applied by the fuzzer, exactly those
+    // transformations should have been applied, and the binary resulting from
+    // replay should be identical to that which resulted from fuzzing.
+    std::string fuzzer_transformations_string;
+    std::string replayer_transformations_string;
+    fuzzer_transformation_sequence_out.SerializeToString(
+        &fuzzer_transformations_string);
+    replayer_transformation_sequence_out.SerializeToString(
+        &replayer_transformations_string);
+    ASSERT_EQ(fuzzer_transformations_string, replayer_transformations_string);
+    ASSERT_EQ(fuzzer_binary_out, replayer_binary_out);
+  }
+}
+
+TEST(FuzzerReplayerTest, Miscellaneous1) {
+  // Do some fuzzer runs, starting from an initial seed of 0 (seed value chosen
+  // arbitrarily).
+  RunFuzzerAndReplayer(kTestShader1, protobufs::FactSequence(), 0,
+                       kNumFuzzerRuns);
+}
+
+TEST(FuzzerReplayerTest, Miscellaneous2) {
+  // Do some fuzzer runs, starting from an initial seed of 10 (seed value chosen
+  // arbitrarily).
+  RunFuzzerAndReplayer(kTestShader2, protobufs::FactSequence(), 10,
+                       kNumFuzzerRuns);
+}
+
+TEST(FuzzerReplayerTest, Miscellaneous3) {
+  // Add the facts "resolution.x == 250" and "resolution.y == 100".
+  protobufs::FactSequence facts;
+  AddConstantUniformFact(&facts, 0, 0, {0, 0}, 250);
+  AddConstantUniformFact(&facts, 0, 0, {0, 1}, 100);
+
+  // Do some fuzzer runs, starting from an initial seed of 94 (seed value chosen
+  // arbitrarily).
+  RunFuzzerAndReplayer(kTestShader3, facts, 94, kNumFuzzerRuns);
+}
+
+TEST(FuzzerReplayerTest, Miscellaneous4) {
   // Add the facts:
   //  - "one == 1.0"
   //  - "resolution.y == 256.0",
@@ -1621,7 +1633,7 @@
 
   // Do some fuzzer runs, starting from an initial seed of 14 (seed value chosen
   // arbitrarily).
-  RunFuzzerAndReplayer(shader, facts, 14, kNumFuzzerRuns);
+  RunFuzzerAndReplayer(kTestShader4, facts, 14, kNumFuzzerRuns);
 }
 
 }  // namespace
diff --git a/third_party/SPIRV-Tools/test/fuzz/fuzzer_shrinker_test.cpp b/third_party/SPIRV-Tools/test/fuzz/fuzzer_shrinker_test.cpp
index 9af863e..c906a1e 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fuzzer_shrinker_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fuzzer_shrinker_test.cpp
@@ -16,6 +16,7 @@
 #include <vector>
 
 #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"
@@ -25,217 +26,35 @@
 namespace fuzz {
 namespace {
 
-// Abstract class exposing an interestingness function as a virtual method.
-class InterestingnessTest {
- public:
-  virtual ~InterestingnessTest() = default;
-
-  // Abstract method that subclasses should implement for specific notions of
-  // interestingness. Its signature matches Shrinker::InterestingnessFunction.
-  // Argument |binary| is the SPIR-V binary to be checked; |counter| is used for
-  // debugging purposes.
-  virtual bool Interesting(const std::vector<uint32_t>& binary,
-                           uint32_t counter) = 0;
-
-  // Yields the Interesting instance method wrapped in a function object.
-  Shrinker::InterestingnessFunction AsFunction() {
-    return std::bind(&InterestingnessTest::Interesting, this,
-                     std::placeholders::_1, std::placeholders::_2);
-  }
-};
-
-// A test that says all binaries are interesting.
-class AlwaysInteresting : public InterestingnessTest {
- public:
-  bool Interesting(const std::vector<uint32_t>&, uint32_t) override {
-    return true;
-  }
-};
-
-// A test that says a binary is interesting first time round, and uninteresting
-// thereafter.
-class OnlyInterestingFirstTime : public InterestingnessTest {
- public:
-  explicit OnlyInterestingFirstTime() : first_time_(true) {}
-
-  bool Interesting(const std::vector<uint32_t>&, uint32_t) override {
-    if (first_time_) {
-      first_time_ = false;
-      return true;
-    }
-    return false;
-  }
-
- private:
-  bool first_time_;
-};
-
-// A test that says a binary is interesting first time round, after which
-// interestingness ping pongs between false and true.
-class PingPong : public InterestingnessTest {
- public:
-  explicit PingPong() : interesting_(false) {}
-
-  bool Interesting(const std::vector<uint32_t>&, uint32_t) override {
-    interesting_ = !interesting_;
-    return interesting_;
-  }
-
- private:
-  bool interesting_;
-};
-
-// A test that says a binary is interesting first time round, thereafter
-// decides at random whether it is interesting.  This allows the logic of the
-// shrinker to be exercised quite a bit.
-class InterestingThenRandom : public InterestingnessTest {
- public:
-  InterestingThenRandom(const PseudoRandomGenerator& random_generator)
-      : first_time_(true), random_generator_(random_generator) {}
-
-  bool Interesting(const std::vector<uint32_t>&, uint32_t) override {
-    if (first_time_) {
-      first_time_ = false;
-      return true;
-    }
-    return random_generator_.RandomBool();
-  }
-
- private:
-  bool first_time_;
-  PseudoRandomGenerator random_generator_;
-};
-
-// |binary_in| and |initial_facts| are a SPIR-V binary and sequence of facts to
-// which |transformation_sequence_in| can be applied.  Shrinking of
-// |transformation_sequence_in| gets performed with respect to
-// |interestingness_function|.  If |expected_binary_out| is non-empty, it must
-// match the binary obtained by applying the final shrunk set of
-// transformations, in which case the number of such transformations should
-// equal |expected_transformations_out_size|.
+// The following SPIR-V came from this GLSL:
 //
-// The |step_limit| parameter restricts the number of steps that the shrinker
-// will try; it can be set to something small for a faster (but less thorough)
-// test.
-void RunAndCheckShrinker(
-    const spv_target_env& target_env, const std::vector<uint32_t>& binary_in,
-    const protobufs::FactSequence& initial_facts,
-    const protobufs::TransformationSequence& transformation_sequence_in,
-    const Shrinker::InterestingnessFunction& interestingness_function,
-    const std::vector<uint32_t>& expected_binary_out,
-    uint32_t expected_transformations_out_size, uint32_t step_limit) {
-  // Run the shrinker.
-  Shrinker shrinker(target_env, step_limit, false);
-  shrinker.SetMessageConsumer(kSilentConsumer);
+// #version 310 es
+//
+// void foo() {
+//   int x;
+//   x = 2;
+//   for (int i = 0; i < 100; i++) {
+//     x += i;
+//     x = x * 2;
+//   }
+//   return;
+// }
+//
+// void main() {
+//   foo();
+//   for (int i = 0; i < 10; i++) {
+//     int j = 20;
+//     while(j > 0) {
+//       foo();
+//       j--;
+//     }
+//     do {
+//       i++;
+//     } while(i < 4);
+//   }
+// }
 
-  std::vector<uint32_t> binary_out;
-  protobufs::TransformationSequence transformations_out;
-  Shrinker::ShrinkerResultStatus shrinker_result_status =
-      shrinker.Run(binary_in, initial_facts, transformation_sequence_in,
-                   interestingness_function, &binary_out, &transformations_out);
-  ASSERT_TRUE(Shrinker::ShrinkerResultStatus::kComplete ==
-                  shrinker_result_status ||
-              Shrinker::ShrinkerResultStatus::kStepLimitReached ==
-                  shrinker_result_status);
-
-  // If a non-empty expected binary was provided, check that it matches the
-  // result of shrinking and that the expected number of transformations remain.
-  if (!expected_binary_out.empty()) {
-    ASSERT_EQ(expected_binary_out, binary_out);
-    ASSERT_EQ(expected_transformations_out_size,
-              static_cast<uint32_t>(transformations_out.transformation_size()));
-  }
-}
-
-// Assembles the given |shader| text, and then:
-// - Runs the fuzzer with |seed| to yield a set of transformations
-// - Shrinks the transformation with various interestingness functions,
-//   asserting some properties about the result each time
-void RunFuzzerAndShrinker(const std::string& shader,
-                          const protobufs::FactSequence& initial_facts,
-                          uint32_t seed) {
-  const auto env = SPV_ENV_UNIVERSAL_1_5;
-
-  std::vector<uint32_t> binary_in;
-  SpirvTools t(env);
-  t.SetMessageConsumer(kConsoleMessageConsumer);
-  ASSERT_TRUE(t.Assemble(shader, &binary_in, kFuzzAssembleOption));
-  ASSERT_TRUE(t.Validate(binary_in));
-
-  // Run the fuzzer and check that it successfully yields a valid binary.
-  std::vector<uint32_t> fuzzer_binary_out;
-  protobufs::TransformationSequence fuzzer_transformation_sequence_out;
-  Fuzzer fuzzer(env, seed, true);
-  fuzzer.SetMessageConsumer(kSilentConsumer);
-  auto fuzzer_result_status =
-      fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out,
-                 &fuzzer_transformation_sequence_out);
-  ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status);
-  ASSERT_TRUE(t.Validate(fuzzer_binary_out));
-
-  const uint32_t kReasonableStepLimit = 50;
-  const uint32_t kSmallStepLimit = 20;
-
-  // With the AlwaysInteresting test, we should quickly shrink to the original
-  // binary with no transformations remaining.
-  RunAndCheckShrinker(
-      env, binary_in, initial_facts, fuzzer_transformation_sequence_out,
-      AlwaysInteresting().AsFunction(), binary_in, 0, kReasonableStepLimit);
-
-  // With the OnlyInterestingFirstTime test, no shrinking should be achieved.
-  RunAndCheckShrinker(
-      env, binary_in, initial_facts, fuzzer_transformation_sequence_out,
-      OnlyInterestingFirstTime().AsFunction(), fuzzer_binary_out,
-      static_cast<uint32_t>(
-          fuzzer_transformation_sequence_out.transformation_size()),
-      kReasonableStepLimit);
-
-  // 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_transformation_sequence_out,
-                      PingPong().AsFunction(), {}, 0, kSmallStepLimit);
-
-  // 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_transformation_sequence_out,
-      InterestingThenRandom(PseudoRandomGenerator(seed)).AsFunction(), {}, 0,
-      kSmallStepLimit);
-}
-
-TEST(FuzzerShrinkerTest, Miscellaneous1) {
-  // The following SPIR-V came from this GLSL:
-  //
-  // #version 310 es
-  //
-  // void foo() {
-  //   int x;
-  //   x = 2;
-  //   for (int i = 0; i < 100; i++) {
-  //     x += i;
-  //     x = x * 2;
-  //   }
-  //   return;
-  // }
-  //
-  // void main() {
-  //   foo();
-  //   for (int i = 0; i < 10; i++) {
-  //     int j = 20;
-  //     while(j > 0) {
-  //       foo();
-  //       j--;
-  //     }
-  //     do {
-  //       i++;
-  //     } while(i < 4);
-  //   }
-  // }
-
-  const std::string shader = R"(
+const std::string kTestShader1 = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
@@ -371,64 +190,60 @@
 
   )";
 
-  RunFuzzerAndShrinker(shader, protobufs::FactSequence(), 2);
-}
+// The following SPIR-V came from this GLSL, which was then optimized using
+// spirv-opt with the -O argument:
+//
+// #version 310 es
+//
+// precision highp float;
+//
+// layout(location = 0) out vec4 _GLF_color;
+//
+// layout(set = 0, binding = 0) uniform buf0 {
+//  vec2 injectionSwitch;
+// };
+// layout(set = 0, binding = 1) uniform buf1 {
+//  vec2 resolution;
+// };
+// bool checkSwap(float a, float b)
+// {
+//  return gl_FragCoord.y < resolution.y / 2.0 ? a > b : a < b;
+// }
+// void main()
+// {
+//  float data[10];
+//  for(int i = 0; i < 10; i++)
+//   {
+//    data[i] = float(10 - i) * injectionSwitch.y;
+//   }
+//  for(int i = 0; i < 9; i++)
+//   {
+//    for(int j = 0; j < 10; j++)
+//     {
+//      if(j < i + 1)
+//       {
+//        continue;
+//       }
+//      bool doSwap = checkSwap(data[i], data[j]);
+//      if(doSwap)
+//       {
+//        float temp = data[i];
+//        data[i] = data[j];
+//        data[j] = temp;
+//       }
+//     }
+//   }
+//  if(gl_FragCoord.x < resolution.x / 2.0)
+//   {
+//    _GLF_color = vec4(data[0] / 10.0, data[5] / 10.0, data[9] / 10.0, 1.0);
+//   }
+//  else
+//   {
+//    _GLF_color = vec4(data[5] / 10.0, data[9] / 10.0, data[0] / 10.0, 1.0);
+//   }
+// }
 
-TEST(FuzzerShrinkerTest, Miscellaneous2) {
-  // The following SPIR-V came from this GLSL, which was then optimized using
-  // spirv-opt with the -O argument:
-  //
-  // #version 310 es
-  //
-  // precision highp float;
-  //
-  // layout(location = 0) out vec4 _GLF_color;
-  //
-  // layout(set = 0, binding = 0) uniform buf0 {
-  //  vec2 injectionSwitch;
-  // };
-  // layout(set = 0, binding = 1) uniform buf1 {
-  //  vec2 resolution;
-  // };
-  // bool checkSwap(float a, float b)
-  // {
-  //  return gl_FragCoord.y < resolution.y / 2.0 ? a > b : a < b;
-  // }
-  // void main()
-  // {
-  //  float data[10];
-  //  for(int i = 0; i < 10; i++)
-  //   {
-  //    data[i] = float(10 - i) * injectionSwitch.y;
-  //   }
-  //  for(int i = 0; i < 9; i++)
-  //   {
-  //    for(int j = 0; j < 10; j++)
-  //     {
-  //      if(j < i + 1)
-  //       {
-  //        continue;
-  //       }
-  //      bool doSwap = checkSwap(data[i], data[j]);
-  //      if(doSwap)
-  //       {
-  //        float temp = data[i];
-  //        data[i] = data[j];
-  //        data[j] = temp;
-  //       }
-  //     }
-  //   }
-  //  if(gl_FragCoord.x < resolution.x / 2.0)
-  //   {
-  //    _GLF_color = vec4(data[0] / 10.0, data[5] / 10.0, data[9] / 10.0, 1.0);
-  //   }
-  //  else
-  //   {
-  //    _GLF_color = vec4(data[5] / 10.0, data[9] / 10.0, data[0] / 10.0, 1.0);
-  //   }
-  // }
-
-  const std::string shader = R"(
+const std::string kTestShader2 = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
@@ -616,119 +431,115 @@
                OpFunctionEnd
   )";
 
-  RunFuzzerAndShrinker(shader, protobufs::FactSequence(), 19);
-}
+// The following SPIR-V came from this GLSL, which was then optimized using
+// spirv-opt with the -O argument:
+//
+// #version 310 es
+//
+// precision highp float;
+//
+// layout(location = 0) out vec4 _GLF_color;
+//
+// layout(set = 0, binding = 0) uniform buf0 {
+//  vec2 resolution;
+// };
+// void main(void)
+// {
+//  float A[50];
+//  for(
+//      int i = 0;
+//      i < 200;
+//      i ++
+//  )
+//   {
+//    if(i >= int(resolution.x))
+//     {
+//      break;
+//     }
+//    if((4 * (i / 4)) == i)
+//     {
+//      A[i / 4] = float(i);
+//     }
+//   }
+//  for(
+//      int i = 0;
+//      i < 50;
+//      i ++
+//  )
+//   {
+//    if(i < int(gl_FragCoord.x))
+//     {
+//      break;
+//     }
+//    if(i > 0)
+//     {
+//      A[i] += A[i - 1];
+//     }
+//   }
+//  if(int(gl_FragCoord.x) < 20)
+//   {
+//    _GLF_color = vec4(A[0] / resolution.x, A[4] / resolution.y, 1.0, 1.0);
+//   }
+//  else
+//   if(int(gl_FragCoord.x) < 40)
+//    {
+//     _GLF_color = vec4(A[5] / resolution.x, A[9] / resolution.y, 1.0, 1.0);
+//    }
+//   else
+//    if(int(gl_FragCoord.x) < 60)
+//     {
+//      _GLF_color = vec4(A[10] / resolution.x, A[14] / resolution.y,
+//      1.0, 1.0);
+//     }
+//    else
+//     if(int(gl_FragCoord.x) < 80)
+//      {
+//       _GLF_color = vec4(A[15] / resolution.x, A[19] / resolution.y,
+//       1.0, 1.0);
+//      }
+//     else
+//      if(int(gl_FragCoord.x) < 100)
+//       {
+//        _GLF_color = vec4(A[20] / resolution.x, A[24] / resolution.y,
+//        1.0, 1.0);
+//       }
+//      else
+//       if(int(gl_FragCoord.x) < 120)
+//        {
+//         _GLF_color = vec4(A[25] / resolution.x, A[29] / resolution.y,
+//         1.0, 1.0);
+//        }
+//       else
+//        if(int(gl_FragCoord.x) < 140)
+//         {
+//          _GLF_color = vec4(A[30] / resolution.x, A[34] / resolution.y,
+//          1.0, 1.0);
+//         }
+//        else
+//         if(int(gl_FragCoord.x) < 160)
+//          {
+//           _GLF_color = vec4(A[35] / resolution.x, A[39] /
+//           resolution.y, 1.0, 1.0);
+//          }
+//         else
+//          if(int(gl_FragCoord.x) < 180)
+//           {
+//            _GLF_color = vec4(A[40] / resolution.x, A[44] /
+//            resolution.y, 1.0, 1.0);
+//           }
+//          else
+//           if(int(gl_FragCoord.x) < 180)
+//            {
+//             _GLF_color = vec4(A[45] / resolution.x, A[49] /
+//             resolution.y, 1.0, 1.0);
+//            }
+//           else
+//            {
+//             discard;
+//            }
+// }
 
-TEST(FuzzerShrinkerTest, Miscellaneous3) {
-  // The following SPIR-V came from this GLSL, which was then optimized using
-  // spirv-opt with the -O argument:
-  //
-  // #version 310 es
-  //
-  // precision highp float;
-  //
-  // layout(location = 0) out vec4 _GLF_color;
-  //
-  // layout(set = 0, binding = 0) uniform buf0 {
-  //  vec2 resolution;
-  // };
-  // void main(void)
-  // {
-  //  float A[50];
-  //  for(
-  //      int i = 0;
-  //      i < 200;
-  //      i ++
-  //  )
-  //   {
-  //    if(i >= int(resolution.x))
-  //     {
-  //      break;
-  //     }
-  //    if((4 * (i / 4)) == i)
-  //     {
-  //      A[i / 4] = float(i);
-  //     }
-  //   }
-  //  for(
-  //      int i = 0;
-  //      i < 50;
-  //      i ++
-  //  )
-  //   {
-  //    if(i < int(gl_FragCoord.x))
-  //     {
-  //      break;
-  //     }
-  //    if(i > 0)
-  //     {
-  //      A[i] += A[i - 1];
-  //     }
-  //   }
-  //  if(int(gl_FragCoord.x) < 20)
-  //   {
-  //    _GLF_color = vec4(A[0] / resolution.x, A[4] / resolution.y, 1.0, 1.0);
-  //   }
-  //  else
-  //   if(int(gl_FragCoord.x) < 40)
-  //    {
-  //     _GLF_color = vec4(A[5] / resolution.x, A[9] / resolution.y, 1.0, 1.0);
-  //    }
-  //   else
-  //    if(int(gl_FragCoord.x) < 60)
-  //     {
-  //      _GLF_color = vec4(A[10] / resolution.x, A[14] / resolution.y,
-  //      1.0, 1.0);
-  //     }
-  //    else
-  //     if(int(gl_FragCoord.x) < 80)
-  //      {
-  //       _GLF_color = vec4(A[15] / resolution.x, A[19] / resolution.y,
-  //       1.0, 1.0);
-  //      }
-  //     else
-  //      if(int(gl_FragCoord.x) < 100)
-  //       {
-  //        _GLF_color = vec4(A[20] / resolution.x, A[24] / resolution.y,
-  //        1.0, 1.0);
-  //       }
-  //      else
-  //       if(int(gl_FragCoord.x) < 120)
-  //        {
-  //         _GLF_color = vec4(A[25] / resolution.x, A[29] / resolution.y,
-  //         1.0, 1.0);
-  //        }
-  //       else
-  //        if(int(gl_FragCoord.x) < 140)
-  //         {
-  //          _GLF_color = vec4(A[30] / resolution.x, A[34] / resolution.y,
-  //          1.0, 1.0);
-  //         }
-  //        else
-  //         if(int(gl_FragCoord.x) < 160)
-  //          {
-  //           _GLF_color = vec4(A[35] / resolution.x, A[39] /
-  //           resolution.y, 1.0, 1.0);
-  //          }
-  //         else
-  //          if(int(gl_FragCoord.x) < 180)
-  //           {
-  //            _GLF_color = vec4(A[40] / resolution.x, A[44] /
-  //            resolution.y, 1.0, 1.0);
-  //           }
-  //          else
-  //           if(int(gl_FragCoord.x) < 180)
-  //            {
-  //             _GLF_color = vec4(A[45] / resolution.x, A[49] /
-  //             resolution.y, 1.0, 1.0);
-  //            }
-  //           else
-  //            {
-  //             discard;
-  //            }
-  // }
-
-  const std::string shader = R"(
+const std::string kTestShader3 = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
@@ -1076,6 +887,204 @@
                OpFunctionEnd
   )";
 
+// Abstract class exposing an interestingness function as a virtual method.
+class InterestingnessTest {
+ public:
+  virtual ~InterestingnessTest() = default;
+
+  // Abstract method that subclasses should implement for specific notions of
+  // interestingness. Its signature matches Shrinker::InterestingnessFunction.
+  // Argument |binary| is the SPIR-V binary to be checked; |counter| is used for
+  // debugging purposes.
+  virtual bool Interesting(const std::vector<uint32_t>& binary,
+                           uint32_t counter) = 0;
+
+  // Yields the Interesting instance method wrapped in a function object.
+  Shrinker::InterestingnessFunction AsFunction() {
+    return std::bind(&InterestingnessTest::Interesting, this,
+                     std::placeholders::_1, std::placeholders::_2);
+  }
+};
+
+// A test that says all binaries are interesting.
+class AlwaysInteresting : public InterestingnessTest {
+ public:
+  bool Interesting(const std::vector<uint32_t>&, uint32_t) override {
+    return true;
+  }
+};
+
+// A test that says a binary is interesting first time round, and uninteresting
+// thereafter.
+class OnlyInterestingFirstTime : public InterestingnessTest {
+ public:
+  explicit OnlyInterestingFirstTime() : first_time_(true) {}
+
+  bool Interesting(const std::vector<uint32_t>&, uint32_t) override {
+    if (first_time_) {
+      first_time_ = false;
+      return true;
+    }
+    return false;
+  }
+
+ private:
+  bool first_time_;
+};
+
+// A test that says a binary is interesting first time round, after which
+// interestingness ping pongs between false and true.
+class PingPong : public InterestingnessTest {
+ public:
+  explicit PingPong() : interesting_(false) {}
+
+  bool Interesting(const std::vector<uint32_t>&, uint32_t) override {
+    interesting_ = !interesting_;
+    return interesting_;
+  }
+
+ private:
+  bool interesting_;
+};
+
+// A test that says a binary is interesting first time round, thereafter
+// decides at random whether it is interesting.  This allows the logic of the
+// shrinker to be exercised quite a bit.
+class InterestingThenRandom : public InterestingnessTest {
+ public:
+  InterestingThenRandom(const PseudoRandomGenerator& random_generator)
+      : first_time_(true), random_generator_(random_generator) {}
+
+  bool Interesting(const std::vector<uint32_t>&, uint32_t) override {
+    if (first_time_) {
+      first_time_ = false;
+      return true;
+    }
+    return random_generator_.RandomBool();
+  }
+
+ private:
+  bool first_time_;
+  PseudoRandomGenerator random_generator_;
+};
+
+// |binary_in| and |initial_facts| are a SPIR-V binary and sequence of facts to
+// which |transformation_sequence_in| can be applied.  Shrinking of
+// |transformation_sequence_in| gets performed with respect to
+// |interestingness_function|.  If |expected_binary_out| is non-empty, it must
+// match the binary obtained by applying the final shrunk set of
+// transformations, in which case the number of such transformations should
+// equal |expected_transformations_out_size|.
+//
+// The |step_limit| parameter restricts the number of steps that the shrinker
+// will try; it can be set to something small for a faster (but less thorough)
+// test.
+void RunAndCheckShrinker(
+    const spv_target_env& target_env, const std::vector<uint32_t>& binary_in,
+    const protobufs::FactSequence& initial_facts,
+    const protobufs::TransformationSequence& transformation_sequence_in,
+    const Shrinker::InterestingnessFunction& interestingness_function,
+    const std::vector<uint32_t>& expected_binary_out,
+    uint32_t expected_transformations_out_size, uint32_t step_limit) {
+  // Run the shrinker.
+  Shrinker shrinker(target_env, step_limit, false);
+  shrinker.SetMessageConsumer(kSilentConsumer);
+
+  std::vector<uint32_t> binary_out;
+  protobufs::TransformationSequence transformations_out;
+  Shrinker::ShrinkerResultStatus shrinker_result_status =
+      shrinker.Run(binary_in, initial_facts, transformation_sequence_in,
+                   interestingness_function, &binary_out, &transformations_out);
+  ASSERT_TRUE(Shrinker::ShrinkerResultStatus::kComplete ==
+                  shrinker_result_status ||
+              Shrinker::ShrinkerResultStatus::kStepLimitReached ==
+                  shrinker_result_status);
+
+  // If a non-empty expected binary was provided, check that it matches the
+  // result of shrinking and that the expected number of transformations remain.
+  if (!expected_binary_out.empty()) {
+    ASSERT_EQ(expected_binary_out, binary_out);
+    ASSERT_EQ(expected_transformations_out_size,
+              static_cast<uint32_t>(transformations_out.transformation_size()));
+  }
+}
+
+// Assembles the given |shader| text, and then:
+// - Runs the fuzzer with |seed| to yield a set of transformations
+// - Shrinks the transformation with various interestingness functions,
+//   asserting some properties about the result each time
+void RunFuzzerAndShrinker(const std::string& shader,
+                          const protobufs::FactSequence& initial_facts,
+                          uint32_t seed) {
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+
+  std::vector<uint32_t> binary_in;
+  SpirvTools t(env);
+  t.SetMessageConsumer(kConsoleMessageConsumer);
+  ASSERT_TRUE(t.Assemble(shader, &binary_in, kFuzzAssembleOption));
+  ASSERT_TRUE(t.Validate(binary_in));
+
+  std::vector<fuzzerutil::ModuleSupplier> donor_suppliers;
+  for (auto donor : {&kTestShader1, &kTestShader2, &kTestShader3}) {
+    donor_suppliers.emplace_back([donor]() {
+      return BuildModule(env, kConsoleMessageConsumer, *donor,
+                         kFuzzAssembleOption);
+    });
+  }
+
+  // Run the fuzzer and check that it successfully yields a valid binary.
+  std::vector<uint32_t> fuzzer_binary_out;
+  protobufs::TransformationSequence fuzzer_transformation_sequence_out;
+  Fuzzer fuzzer(env, seed, true);
+  fuzzer.SetMessageConsumer(kSilentConsumer);
+  auto fuzzer_result_status =
+      fuzzer.Run(binary_in, initial_facts, donor_suppliers, &fuzzer_binary_out,
+                 &fuzzer_transformation_sequence_out);
+  ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status);
+  ASSERT_TRUE(t.Validate(fuzzer_binary_out));
+
+  const uint32_t kReasonableStepLimit = 50;
+  const uint32_t kSmallStepLimit = 20;
+
+  // With the AlwaysInteresting test, we should quickly shrink to the original
+  // binary with no transformations remaining.
+  RunAndCheckShrinker(
+      env, binary_in, initial_facts, fuzzer_transformation_sequence_out,
+      AlwaysInteresting().AsFunction(), binary_in, 0, kReasonableStepLimit);
+
+  // With the OnlyInterestingFirstTime test, no shrinking should be achieved.
+  RunAndCheckShrinker(
+      env, binary_in, initial_facts, fuzzer_transformation_sequence_out,
+      OnlyInterestingFirstTime().AsFunction(), fuzzer_binary_out,
+      static_cast<uint32_t>(
+          fuzzer_transformation_sequence_out.transformation_size()),
+      kReasonableStepLimit);
+
+  // 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_transformation_sequence_out,
+                      PingPong().AsFunction(), {}, 0, kSmallStepLimit);
+
+  // 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_transformation_sequence_out,
+      InterestingThenRandom(PseudoRandomGenerator(seed)).AsFunction(), {}, 0,
+      kSmallStepLimit);
+}
+
+TEST(FuzzerShrinkerTest, Miscellaneous1) {
+  RunFuzzerAndShrinker(kTestShader1, protobufs::FactSequence(), 2);
+}
+
+TEST(FuzzerShrinkerTest, Miscellaneous2) {
+  RunFuzzerAndShrinker(kTestShader2, protobufs::FactSequence(), 19);
+}
+
+TEST(FuzzerShrinkerTest, Miscellaneous3) {
   // Add the facts "resolution.x == 250" and "resolution.y == 100".
   protobufs::FactSequence facts;
   {
@@ -1099,7 +1108,7 @@
 
   // Do 2 fuzzer runs, starting from an initial seed of 194 (seed value chosen
   // arbitrarily).
-  RunFuzzerAndShrinker(shader, facts, 194);
+  RunFuzzerAndShrinker(kTestShader3, facts, 194);
 }
 
 }  // namespace
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_access_chain_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_access_chain_test.cpp
new file mode 100644
index 0000000..516d371
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_access_chain_test.cpp
@@ -0,0 +1,448 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_access_chain.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAccessChainTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %48 %54
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 2
+         %50 = OpTypeMatrix %7 2
+         %70 = OpTypePointer Function %7
+         %71 = OpTypePointer Function %50
+          %8 = OpTypeStruct %7 %6
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Function %10
+         %12 = OpTypeFunction %10 %9 %11
+         %17 = OpConstant %10 0
+         %18 = OpTypeInt 32 0
+         %19 = OpConstant %18 0
+         %20 = OpTypePointer Function %6
+         %99 = OpTypePointer Private %6
+         %29 = OpConstant %6 0
+         %30 = OpConstant %6 1
+         %31 = OpConstantComposite %7 %29 %30
+         %32 = OpConstant %6 2
+         %33 = OpConstantComposite %8 %31 %32
+         %35 = OpConstant %10 10
+         %51 = OpConstant %18 10
+         %80 = OpConstant %18 0
+         %81 = OpConstant %10 1
+         %82 = OpConstant %18 2
+         %83 = OpConstant %10 3
+         %84 = OpConstant %18 4
+         %85 = OpConstant %10 5
+         %52 = OpTypeArray %50 %51
+         %53 = OpTypePointer Private %52
+         %45 = OpUndef %9
+         %46 = OpConstantNull %9
+         %47 = OpTypePointer Private %8
+         %48 = OpVariable %47 Private
+         %54 = OpVariable %53 Private
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %28 = OpVariable %9 Function
+         %34 = OpVariable %11 Function
+         %36 = OpVariable %9 Function
+         %38 = OpVariable %11 Function
+         %44 = OpCopyObject %9 %36
+               OpStore %28 %33
+               OpStore %34 %35
+         %37 = OpLoad %8 %28
+               OpStore %36 %37
+         %39 = OpLoad %10 %34
+               OpStore %38 %39
+         %40 = OpFunctionCall %10 %15 %36 %38
+         %41 = OpLoad %10 %34
+         %42 = OpIAdd %10 %41 %40
+               OpStore %34 %42
+               OpReturn
+               OpFunctionEnd
+         %15 = OpFunction %10 None %12
+         %13 = OpFunctionParameter %9
+         %14 = OpFunctionParameter %11
+         %16 = OpLabel
+         %21 = OpAccessChain %20 %13 %17 %19
+         %43 = OpCopyObject %9 %13
+         %22 = OpLoad %6 %21
+         %23 = OpConvertFToS %10 %22
+         %24 = OpLoad %10 %14
+         %25 = OpIAdd %10 %23 %24
+               OpReturnValue %25
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Types:
+  // Ptr | Pointee | Storage class | GLSL for pointee    | Ids of this type
+  // ----+---------+---------------+---------------------+------------------
+  //  9  |    8    | Function      | struct(vec2, float) | 28, 36, 44, 13, 43
+  // 11  |   10    | Function      | int                 | 34, 38, 14
+  // 20  |    6    | Function      | float               | -
+  // 99  |    6    | Private       | float               | -
+  // 53  |   52    | Private       | mat2x2[10]          | 54
+  // 47  |    8    | Private       | struct(vec2, float) | 48
+  // 70  |    7    | Function      | vec2                | -
+  // 71  |   59    | Function      | mat2x2              | -
+
+  // Indices 0-5 are in ids 80-85
+
+  FactManager fact_manager;
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(54);
+
+  // Bad: id is not fresh
+  ASSERT_FALSE(TransformationAccessChain(
+                   43, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: pointer id does not exist
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 1000, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: pointer id is not a type
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 5, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: pointer id is not a pointer
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 23, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: index id does not exist
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 43, {1000}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: index id is not a constant
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 43, {24}, MakeInstructionDescriptor(25, SpvOpIAdd, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: too many indices
+  ASSERT_FALSE(
+      TransformationAccessChain(100, 43, {80, 80, 80},
+                                MakeInstructionDescriptor(24, SpvOpLoad, 0))
+          .IsApplicable(context.get(), fact_manager));
+
+  // Bad: index id is out of bounds
+  ASSERT_FALSE(
+      TransformationAccessChain(100, 43, {80, 83},
+                                MakeInstructionDescriptor(24, SpvOpLoad, 0))
+          .IsApplicable(context.get(), fact_manager));
+
+  // Bad: attempt to insert before variable
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 34, {}, MakeInstructionDescriptor(36, SpvOpVariable, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: pointer not available
+  ASSERT_FALSE(
+      TransformationAccessChain(
+          100, 43, {80}, MakeInstructionDescriptor(21, SpvOpAccessChain, 0))
+          .IsApplicable(context.get(), fact_manager));
+
+  // Bad: instruction descriptor does not identify anything
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 100))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: pointer is null
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 45, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: pointer is undef
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 46, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: pointer to result type does not exist
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 52, {0}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  {
+    TransformationAccessChain transformation(
+        100, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(100));
+  }
+
+  {
+    TransformationAccessChain transformation(
+        101, 28, {81}, MakeInstructionDescriptor(42, SpvOpReturn, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101));
+  }
+
+  {
+    TransformationAccessChain transformation(
+        102, 36, {80, 81}, MakeInstructionDescriptor(37, SpvOpStore, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(102));
+  }
+
+  {
+    TransformationAccessChain transformation(
+        103, 44, {}, MakeInstructionDescriptor(44, SpvOpStore, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(103));
+  }
+
+  {
+    TransformationAccessChain transformation(
+        104, 13, {80}, MakeInstructionDescriptor(21, SpvOpAccessChain, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(104));
+  }
+
+  {
+    TransformationAccessChain transformation(
+        105, 34, {}, MakeInstructionDescriptor(44, SpvOpStore, 1));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(105));
+  }
+
+  {
+    TransformationAccessChain transformation(
+        106, 38, {}, MakeInstructionDescriptor(40, SpvOpFunctionCall, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(106));
+  }
+
+  {
+    TransformationAccessChain transformation(
+        107, 14, {}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(107));
+  }
+
+  {
+    TransformationAccessChain transformation(
+        108, 54, {85, 81, 81}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+    ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(108));
+  }
+
+  {
+    TransformationAccessChain transformation(
+        109, 48, {80, 80}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(109));
+  }
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %48 %54
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 2
+         %50 = OpTypeMatrix %7 2
+         %70 = OpTypePointer Function %7
+         %71 = OpTypePointer Function %50
+          %8 = OpTypeStruct %7 %6
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Function %10
+         %12 = OpTypeFunction %10 %9 %11
+         %17 = OpConstant %10 0
+         %18 = OpTypeInt 32 0
+         %19 = OpConstant %18 0
+         %20 = OpTypePointer Function %6
+         %99 = OpTypePointer Private %6
+         %29 = OpConstant %6 0
+         %30 = OpConstant %6 1
+         %31 = OpConstantComposite %7 %29 %30
+         %32 = OpConstant %6 2
+         %33 = OpConstantComposite %8 %31 %32
+         %35 = OpConstant %10 10
+         %51 = OpConstant %18 10
+         %80 = OpConstant %18 0
+         %81 = OpConstant %10 1
+         %82 = OpConstant %18 2
+         %83 = OpConstant %10 3
+         %84 = OpConstant %18 4
+         %85 = OpConstant %10 5
+         %52 = OpTypeArray %50 %51
+         %53 = OpTypePointer Private %52
+         %45 = OpUndef %9
+         %46 = OpConstantNull %9
+         %47 = OpTypePointer Private %8
+         %48 = OpVariable %47 Private
+         %54 = OpVariable %53 Private
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %28 = OpVariable %9 Function
+         %34 = OpVariable %11 Function
+         %36 = OpVariable %9 Function
+         %38 = OpVariable %11 Function
+         %44 = OpCopyObject %9 %36
+        %103 = OpAccessChain %9 %44
+               OpStore %28 %33
+        %105 = OpAccessChain %11 %34
+               OpStore %34 %35
+         %37 = OpLoad %8 %28
+        %102 = OpAccessChain %20 %36 %80 %81
+               OpStore %36 %37
+         %39 = OpLoad %10 %34
+               OpStore %38 %39
+        %106 = OpAccessChain %11 %38
+         %40 = OpFunctionCall %10 %15 %36 %38
+         %41 = OpLoad %10 %34
+         %42 = OpIAdd %10 %41 %40
+               OpStore %34 %42
+        %101 = OpAccessChain %20 %28 %81
+               OpReturn
+               OpFunctionEnd
+         %15 = OpFunction %10 None %12
+         %13 = OpFunctionParameter %9
+         %14 = OpFunctionParameter %11
+         %16 = OpLabel
+        %104 = OpAccessChain %70 %13 %80
+         %21 = OpAccessChain %20 %13 %17 %19
+         %43 = OpCopyObject %9 %13
+         %22 = OpLoad %6 %21
+         %23 = OpConvertFToS %10 %22
+        %100 = OpAccessChain %70 %43 %80
+        %107 = OpAccessChain %11 %14
+        %108 = OpAccessChain %99 %54 %85 %81 %81
+        %109 = OpAccessChain %99 %48 %80 %80
+         %24 = OpLoad %10 %14
+         %25 = OpIAdd %10 %23 %24
+               OpReturnValue %25
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationAccessChainTest, IsomorphicStructs) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %11 %12
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeStruct %6
+          %8 = OpTypePointer Private %7
+          %9 = OpTypeStruct %6
+         %10 = OpTypePointer Private %9
+         %11 = OpVariable %8 Private
+         %12 = OpVariable %10 Private
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  {
+    TransformationAccessChain transformation(
+        100, 11, {}, MakeInstructionDescriptor(5, SpvOpReturn, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+  {
+    TransformationAccessChain transformation(
+        101, 12, {}, MakeInstructionDescriptor(5, SpvOpReturn, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %11 %12
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeStruct %6
+          %8 = OpTypePointer Private %7
+          %9 = OpTypeStruct %6
+         %10 = OpTypePointer Private %9
+         %11 = OpVariable %8 Private
+         %12 = OpVariable %10 Private
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %100 = OpAccessChain %8 %11
+        %101 = OpAccessChain %10 %12
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_block_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_block_test.cpp
new file mode 100644
index 0000000..f89140f
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_block_test.cpp
@@ -0,0 +1,321 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_dead_block.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddDeadBlockTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %8
+          %8 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Id 4 is already in use
+  ASSERT_FALSE(TransformationAddDeadBlock(4, 5, true)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Id 7 is not a block
+  ASSERT_FALSE(TransformationAddDeadBlock(100, 7, true)
+                   .IsApplicable(context.get(), fact_manager));
+
+  TransformationAddDeadBlock transformation(100, 5, true);
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  ASSERT_TRUE(fact_manager.BlockIsDead(100));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %8 None
+               OpBranchConditional %7 %8 %100
+        %100 = OpLabel
+               OpBranch %8
+          %8 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationAddDeadBlockTest, TargetBlockMustNotBeSelectionMerge) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %10 None
+               OpBranchConditional %7 %8 %9
+          %8 = OpLabel
+               OpBranch %10
+          %9 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  ASSERT_FALSE(TransformationAddDeadBlock(100, 9, true)
+                   .IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationAddDeadBlockTest, TargetBlockMustNotBeLoopMergeOrContinue) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %8
+          %8 = OpLabel
+               OpLoopMerge %11 %12 None
+               OpBranchConditional %7 %9 %10
+          %9 = OpLabel
+               OpBranch %12
+         %10 = OpLabel
+               OpBranch %11
+         %12 = OpLabel
+               OpBranch %8
+         %11 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Bad because 9's successor is the loop continue target.
+  ASSERT_FALSE(TransformationAddDeadBlock(100, 9, true)
+                   .IsApplicable(context.get(), fact_manager));
+  // Bad because 10's successor is the loop merge.
+  ASSERT_FALSE(TransformationAddDeadBlock(100, 10, true)
+                   .IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationAddDeadBlockTest, SourceBlockMustNotBeLoopHead) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %8
+          %8 = OpLabel
+               OpLoopMerge %11 %12 None
+               OpBranch %9
+          %9 = OpLabel
+               OpBranchConditional %7 %11 %12
+         %12 = OpLabel
+               OpBranch %8
+         %11 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Bad because 8 is a loop head.
+  ASSERT_FALSE(TransformationAddDeadBlock(100, 8, true)
+                   .IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationAddDeadBlockTest, OpPhiInTarget) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+          %9 = OpTypeInt 32 0
+         %10 = OpConstant %9 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %8
+          %8 = OpLabel
+         %12 = OpPhi %6 %7 %5
+         %13 = OpPhi %9 %10 %5
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationAddDeadBlock transformation(100, 5, true);
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  ASSERT_TRUE(fact_manager.BlockIsDead(100));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+          %9 = OpTypeInt 32 0
+         %10 = OpConstant %9 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %8 None
+               OpBranchConditional %7 %8 %100
+        %100 = OpLabel
+               OpBranch %8
+          %8 = OpLabel
+         %12 = OpPhi %6 %7 %5 %7 %100
+         %13 = OpPhi %9 %10 %5 %10 %100
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationAddDeadBlockTest, BackEdge) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %8
+          %8 = OpLabel
+               OpLoopMerge %10 %9 None
+               OpBranchConditional %7 %9 %10
+          %9 = OpLabel
+               OpBranch %8
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // 9 is a back edge block, so it would not be OK to add a dead block here,
+  // as then both 9 and the dead block would branch to the loop header, 8.
+  ASSERT_FALSE(TransformationAddDeadBlock(100, 9, true)
+                   .IsApplicable(context.get(), fact_manager));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_break_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_break_test.cpp
index 1dd0c9d..d60fc1f 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_break_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_break_test.cpp
@@ -1948,9 +1948,6 @@
   // Not applicable because two OpPhis (not just one) need to be updated at 20
   ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {13})
                    .IsApplicable(context.get(), fact_manager));
-  // Not applicable because only two OpPhis (not three) need to be updated at 20
-  ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {13, 21, 22})
-                   .IsApplicable(context.get(), fact_manager));
   // Not applicable because the given ids do not have types that match the
   // OpPhis at 20, in order
   ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {21, 13})
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_function_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_function_test.cpp
index 66130be..aed12dc 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_function_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_function_test.cpp
@@ -20,6 +20,94 @@
 namespace fuzz {
 namespace {
 
+protobufs::AccessChainClampingInfo MakeAccessClampingInfo(
+    uint32_t access_chain_id,
+    const std::vector<std::pair<uint32_t, uint32_t>>& compare_and_select_ids) {
+  protobufs::AccessChainClampingInfo result;
+  result.set_access_chain_id(access_chain_id);
+  for (auto& compare_and_select_id : compare_and_select_ids) {
+    auto pair = result.add_compare_and_select_ids();
+    pair->set_first(compare_and_select_id.first);
+    pair->set_second(compare_and_select_id.second);
+  }
+  return result;
+}
+
+std::vector<protobufs::Instruction> GetInstructionsForFunction(
+    spv_target_env env, const MessageConsumer& consumer,
+    const std::string& donor, uint32_t function_id) {
+  std::vector<protobufs::Instruction> result;
+  const auto donor_context =
+      BuildModule(env, consumer, donor, kFuzzAssembleOption);
+  assert(IsValid(env, donor_context.get()) && "The given donor must be valid.");
+  for (auto& function : *donor_context->module()) {
+    if (function.result_id() == function_id) {
+      function.ForEachInst([&result](opt::Instruction* inst) {
+        opt::Instruction::OperandList input_operands;
+        for (uint32_t i = 0; i < inst->NumInOperands(); i++) {
+          input_operands.push_back(inst->GetInOperand(i));
+        }
+        result.push_back(MakeInstructionMessage(inst->opcode(), inst->type_id(),
+                                                inst->result_id(),
+                                                input_operands));
+      });
+      break;
+    }
+  }
+  assert(!result.empty() && "The required function should have been found.");
+  return result;
+}
+
+// Returns true if and only if every pointer parameter and variable associated
+// with |function_id| in |context| is known by |fact_manager| to be irrelevant,
+// with the exception of |loop_limiter_id|, which must not be irrelevant.  (It
+// can be 0 if no loop limiter is expected, and 0 should not be deemed
+// irrelevant).
+bool AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
+    opt::IRContext* context, const FactManager& fact_manager,
+    uint32_t function_id, uint32_t loop_limiter_id) {
+  // Look at all the functions until the function of interest is found.
+  for (auto& function : *context->module()) {
+    if (function.result_id() != function_id) {
+      continue;
+    }
+    // Check that the parameters are all irrelevant.
+    bool found_non_irrelevant_parameter = false;
+    function.ForEachParam(
+        [context, &fact_manager,
+         &found_non_irrelevant_parameter](opt::Instruction* inst) {
+          if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() ==
+                  SpvOpTypePointer &&
+              !fact_manager.PointeeValueIsIrrelevant(inst->result_id())) {
+            found_non_irrelevant_parameter = true;
+          }
+        });
+    if (found_non_irrelevant_parameter) {
+      // A non-irrelevant parameter was found.
+      return false;
+    }
+    // Look through the instructions in the function's first block.
+    for (auto& inst : *function.begin()) {
+      if (inst.opcode() != SpvOpVariable) {
+        // We have found a non-variable instruction; this means we have gotten
+        // past all variables, so we are done.
+        return true;
+      }
+      // The variable should be irrelevant if and only if it is not the loop
+      // limiter.
+      if ((inst.result_id() == loop_limiter_id) ==
+          fact_manager.PointeeValueIsIrrelevant(inst.result_id())) {
+        return false;
+      }
+    }
+    assert(false &&
+           "We should have processed all variables and returned by "
+           "this point.");
+  }
+  assert(false && "We should have found the function of interest.");
+  return true;
+}
+
 TEST(TransformationAddFunctionTest, BasicTest) {
   std::string shader = R"(
                OpCapability Shader
@@ -190,6 +278,12 @@
                OpFunctionEnd
   )";
   ASSERT_TRUE(IsEqual(env, after_transformation1, context.get()));
+  ASSERT_TRUE(fact_manager.BlockIsDead(14));
+  ASSERT_TRUE(fact_manager.BlockIsDead(21));
+  ASSERT_TRUE(fact_manager.BlockIsDead(22));
+  ASSERT_TRUE(fact_manager.BlockIsDead(23));
+  ASSERT_TRUE(fact_manager.BlockIsDead(24));
+  ASSERT_TRUE(fact_manager.BlockIsDead(25));
 
   TransformationAddFunction transformation2(std::vector<protobufs::Instruction>(
       {MakeInstructionMessage(
@@ -320,6 +414,7 @@
                OpFunctionEnd
   )";
   ASSERT_TRUE(IsEqual(env, after_transformation2, context.get()));
+  ASSERT_TRUE(fact_manager.BlockIsDead(16));
 }
 
 TEST(TransformationAddFunctionTest, InapplicableTransformations) {
@@ -442,6 +537,2314 @@
           .IsApplicable(context.get(), fact_manager));
 }
 
+TEST(TransformationAddFunctionTest, LoopLimiters) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 0
+          %7 = OpTypePointer Function %6
+          %8 = OpConstant %6 0
+          %9 = OpConstant %6 1
+         %10 = OpConstant %6 5
+         %11 = OpTypeBool
+         %12 = OpConstantTrue %11
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+
+  std::vector<protobufs::Instruction> instructions;
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpFunction, 2, 30,
+      {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
+       {SPV_OPERAND_TYPE_TYPE_ID, {3}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 31, {}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {20}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 20, {}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpLoopMerge, 0, 0,
+      {{SPV_OPERAND_TYPE_ID, {21}},
+       {SPV_OPERAND_TYPE_ID, {22}},
+       {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
+                                                {{SPV_OPERAND_TYPE_ID, {12}},
+                                                 {SPV_OPERAND_TYPE_ID, {23}},
+                                                 {SPV_OPERAND_TYPE_ID, {21}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 23, {}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpLoopMerge, 0, 0,
+      {{SPV_OPERAND_TYPE_ID, {25}},
+       {SPV_OPERAND_TYPE_ID, {26}},
+       {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {28}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 28, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
+                                                {{SPV_OPERAND_TYPE_ID, {12}},
+                                                 {SPV_OPERAND_TYPE_ID, {26}},
+                                                 {SPV_OPERAND_TYPE_ID, {25}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 26, {}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {23}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 25, {}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpLoopMerge, 0, 0,
+      {{SPV_OPERAND_TYPE_ID, {24}},
+       {SPV_OPERAND_TYPE_ID, {27}},
+       {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
+                                                {{SPV_OPERAND_TYPE_ID, {12}},
+                                                 {SPV_OPERAND_TYPE_ID, {24}},
+                                                 {SPV_OPERAND_TYPE_ID, {27}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 27, {}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {25}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 24, {}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {22}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 22, {}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {20}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 21, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
+
+  FactManager fact_manager1;
+  FactManager fact_manager2;
+
+  const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context1.get()));
+
+  TransformationAddFunction add_dead_function(instructions);
+  ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1));
+  add_dead_function.Apply(context1.get(), &fact_manager1);
+  ASSERT_TRUE(IsValid(env, context1.get()));
+  // The added function should not be deemed livesafe.
+  ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(30));
+  // All variables/parameters in the function should be deemed irrelevant.
+  ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
+      context1.get(), fact_manager1, 30, 0));
+
+  std::string added_as_dead_code = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 0
+          %7 = OpTypePointer Function %6
+          %8 = OpConstant %6 0
+          %9 = OpConstant %6 1
+         %10 = OpConstant %6 5
+         %11 = OpTypeBool
+         %12 = OpConstantTrue %11
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %30 = OpFunction %2 None %3
+         %31 = OpLabel
+               OpBranch %20
+         %20 = OpLabel
+               OpLoopMerge %21 %22 None
+               OpBranchConditional %12 %23 %21
+         %23 = OpLabel
+               OpLoopMerge %25 %26 None
+               OpBranch %28
+         %28 = OpLabel
+               OpBranchConditional %12 %26 %25
+         %26 = OpLabel
+               OpBranch %23
+         %25 = OpLabel
+               OpLoopMerge %24 %27 None
+               OpBranchConditional %12 %24 %27
+         %27 = OpLabel
+               OpBranch %25
+         %24 = OpLabel
+               OpBranch %22
+         %22 = OpLabel
+               OpBranch %20
+         %21 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get()));
+
+  protobufs::LoopLimiterInfo loop_limiter1;
+  loop_limiter1.set_loop_header_id(20);
+  loop_limiter1.set_load_id(101);
+  loop_limiter1.set_increment_id(102);
+  loop_limiter1.set_compare_id(103);
+  loop_limiter1.set_logical_op_id(104);
+
+  protobufs::LoopLimiterInfo loop_limiter2;
+  loop_limiter2.set_loop_header_id(23);
+  loop_limiter2.set_load_id(105);
+  loop_limiter2.set_increment_id(106);
+  loop_limiter2.set_compare_id(107);
+  loop_limiter2.set_logical_op_id(108);
+
+  protobufs::LoopLimiterInfo loop_limiter3;
+  loop_limiter3.set_loop_header_id(25);
+  loop_limiter3.set_load_id(109);
+  loop_limiter3.set_increment_id(110);
+  loop_limiter3.set_compare_id(111);
+  loop_limiter3.set_logical_op_id(112);
+
+  std::vector<protobufs::LoopLimiterInfo> loop_limiters = {
+      loop_limiter1, loop_limiter2, loop_limiter3};
+
+  TransformationAddFunction add_livesafe_function(instructions, 100, 10,
+                                                  loop_limiters, 0, {});
+  ASSERT_TRUE(
+      add_livesafe_function.IsApplicable(context2.get(), fact_manager2));
+  add_livesafe_function.Apply(context2.get(), &fact_manager2);
+  ASSERT_TRUE(IsValid(env, context2.get()));
+  // The added function should indeed be deemed livesafe.
+  ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(30));
+  // All variables/parameters in the function should be deemed irrelevant,
+  // except the loop limiter.
+  ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
+      context2.get(), fact_manager2, 30, 100));
+  std::string added_as_livesafe_code = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 0
+          %7 = OpTypePointer Function %6
+          %8 = OpConstant %6 0
+          %9 = OpConstant %6 1
+         %10 = OpConstant %6 5
+         %11 = OpTypeBool
+         %12 = OpConstantTrue %11
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %30 = OpFunction %2 None %3
+         %31 = OpLabel
+        %100 = OpVariable %7 Function %8
+               OpBranch %20
+         %20 = OpLabel
+               OpLoopMerge %21 %22 None
+               OpBranchConditional %12 %23 %21
+         %23 = OpLabel
+               OpLoopMerge %25 %26 None
+               OpBranch %28
+         %28 = OpLabel
+               OpBranchConditional %12 %26 %25
+         %26 = OpLabel
+        %105 = OpLoad %6 %100
+        %106 = OpIAdd %6 %105 %9
+               OpStore %100 %106
+        %107 = OpUGreaterThanEqual %11 %105 %10
+               OpBranchConditional %107 %25 %23
+         %25 = OpLabel
+               OpLoopMerge %24 %27 None
+               OpBranchConditional %12 %24 %27
+         %27 = OpLabel
+        %109 = OpLoad %6 %100
+        %110 = OpIAdd %6 %109 %9
+               OpStore %100 %110
+        %111 = OpUGreaterThanEqual %11 %109 %10
+               OpBranchConditional %111 %24 %25
+         %24 = OpLabel
+               OpBranch %22
+         %22 = OpLabel
+        %101 = OpLoad %6 %100
+        %102 = OpIAdd %6 %101 %9
+               OpStore %100 %102
+        %103 = OpUGreaterThanEqual %11 %101 %10
+               OpBranchConditional %103 %21 %20
+         %21 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get()));
+}
+
+TEST(TransformationAddFunctionTest, KillAndUnreachableInVoidFunction) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %7
+         %13 = OpConstant %6 2
+         %14 = OpTypeBool
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+
+  std::vector<protobufs::Instruction> instructions;
+
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpFunction, 2, 10,
+      {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
+       {SPV_OPERAND_TYPE_TYPE_ID, {8}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 11, {}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 6, 12, {{SPV_OPERAND_TYPE_ID, {9}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpIEqual, 14, 15,
+      {{SPV_OPERAND_TYPE_ID, {12}}, {SPV_OPERAND_TYPE_ID, {13}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpSelectionMerge, 0, 0,
+      {{SPV_OPERAND_TYPE_ID, {17}},
+       {SPV_OPERAND_TYPE_SELECTION_CONTROL, {SpvSelectionControlMaskNone}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
+                                                {{SPV_OPERAND_TYPE_ID, {15}},
+                                                 {SPV_OPERAND_TYPE_ID, {16}},
+                                                 {SPV_OPERAND_TYPE_ID, {17}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 16, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpUnreachable, 0, 0, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 17, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpKill, 0, 0, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
+
+  FactManager fact_manager1;
+  FactManager fact_manager2;
+
+  const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context1.get()));
+
+  TransformationAddFunction add_dead_function(instructions);
+  ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1));
+  add_dead_function.Apply(context1.get(), &fact_manager1);
+  ASSERT_TRUE(IsValid(env, context1.get()));
+  // The added function should not be deemed livesafe.
+  ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(10));
+  // All variables/parameters in the function should be deemed irrelevant.
+  ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
+      context1.get(), fact_manager1, 10, 0));
+
+  std::string added_as_dead_code = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %7
+         %13 = OpConstant %6 2
+         %14 = OpTypeBool
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %2 None %8
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+         %12 = OpLoad %6 %9
+         %15 = OpIEqual %14 %12 %13
+               OpSelectionMerge %17 None
+               OpBranchConditional %15 %16 %17
+         %16 = OpLabel
+               OpUnreachable
+         %17 = OpLabel
+               OpKill
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get()));
+
+  TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0,
+                                                  {});
+  ASSERT_TRUE(
+      add_livesafe_function.IsApplicable(context2.get(), fact_manager2));
+  add_livesafe_function.Apply(context2.get(), &fact_manager2);
+  ASSERT_TRUE(IsValid(env, context2.get()));
+  // The added function should indeed be deemed livesafe.
+  ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(10));
+  // All variables/parameters in the function should be deemed irrelevant.
+  ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
+      context2.get(), fact_manager2, 10, 0));
+  std::string added_as_livesafe_code = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %7
+         %13 = OpConstant %6 2
+         %14 = OpTypeBool
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %2 None %8
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+         %12 = OpLoad %6 %9
+         %15 = OpIEqual %14 %12 %13
+               OpSelectionMerge %17 None
+               OpBranchConditional %15 %16 %17
+         %16 = OpLabel
+               OpReturn
+         %17 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get()));
+}
+
+TEST(TransformationAddFunctionTest, KillAndUnreachableInNonVoidFunction) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %7
+         %50 = OpTypeFunction %6 %7
+         %13 = OpConstant %6 2
+         %14 = OpTypeBool
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+
+  std::vector<protobufs::Instruction> instructions;
+
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpFunction, 6, 10,
+      {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
+       {SPV_OPERAND_TYPE_TYPE_ID, {50}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 11, {}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 6, 12, {{SPV_OPERAND_TYPE_ID, {9}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpIEqual, 14, 15,
+      {{SPV_OPERAND_TYPE_ID, {12}}, {SPV_OPERAND_TYPE_ID, {13}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpSelectionMerge, 0, 0,
+      {{SPV_OPERAND_TYPE_ID, {17}},
+       {SPV_OPERAND_TYPE_SELECTION_CONTROL, {SpvSelectionControlMaskNone}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
+                                                {{SPV_OPERAND_TYPE_ID, {15}},
+                                                 {SPV_OPERAND_TYPE_ID, {16}},
+                                                 {SPV_OPERAND_TYPE_ID, {17}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 16, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpUnreachable, 0, 0, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 17, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpKill, 0, 0, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
+
+  FactManager fact_manager1;
+  FactManager fact_manager2;
+
+  const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context1.get()));
+
+  TransformationAddFunction add_dead_function(instructions);
+  ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1));
+  add_dead_function.Apply(context1.get(), &fact_manager1);
+  ASSERT_TRUE(IsValid(env, context1.get()));
+  // The added function should not be deemed livesafe.
+  ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(10));
+  // All variables/parameters in the function should be deemed irrelevant.
+  ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
+      context1.get(), fact_manager1, 10, 0));
+
+  std::string added_as_dead_code = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %7
+         %50 = OpTypeFunction %6 %7
+         %13 = OpConstant %6 2
+         %14 = OpTypeBool
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %6 None %50
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+         %12 = OpLoad %6 %9
+         %15 = OpIEqual %14 %12 %13
+               OpSelectionMerge %17 None
+               OpBranchConditional %15 %16 %17
+         %16 = OpLabel
+               OpUnreachable
+         %17 = OpLabel
+               OpKill
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get()));
+
+  TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13,
+                                                  {});
+  ASSERT_TRUE(
+      add_livesafe_function.IsApplicable(context2.get(), fact_manager2));
+  add_livesafe_function.Apply(context2.get(), &fact_manager2);
+  ASSERT_TRUE(IsValid(env, context2.get()));
+  // The added function should indeed be deemed livesafe.
+  ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(10));
+  // All variables/parameters in the function should be deemed irrelevant.
+  ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
+      context2.get(), fact_manager2, 10, 0));
+  std::string added_as_livesafe_code = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %7
+         %50 = OpTypeFunction %6 %7
+         %13 = OpConstant %6 2
+         %14 = OpTypeBool
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %6 None %50
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+         %12 = OpLoad %6 %9
+         %15 = OpIEqual %14 %12 %13
+               OpSelectionMerge %17 None
+               OpBranchConditional %15 %16 %17
+         %16 = OpLabel
+               OpReturnValue %13
+         %17 = OpLabel
+               OpReturnValue %13
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get()));
+}
+
+TEST(TransformationAddFunctionTest, ClampedAccessChains) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+        %100 = OpTypeBool
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+         %15 = OpTypeInt 32 0
+        %102 = OpTypePointer Function %15
+          %8 = OpTypeFunction %2 %7 %102 %7
+         %16 = OpConstant %15 5
+         %17 = OpTypeArray %6 %16
+         %18 = OpTypeArray %17 %16
+         %19 = OpTypePointer Private %18
+         %20 = OpVariable %19 Private
+         %21 = OpConstant %6 0
+         %23 = OpTypePointer Private %6
+         %26 = OpTypePointer Function %17
+         %29 = OpTypePointer Private %17
+         %33 = OpConstant %6 4
+        %200 = OpConstant %15 4
+         %35 = OpConstant %15 10
+         %36 = OpTypeArray %6 %35
+         %37 = OpTypePointer Private %36
+         %38 = OpVariable %37 Private
+         %54 = OpTypeFloat 32
+         %55 = OpTypeVector %54 4
+         %56 = OpTypePointer Private %55
+         %57 = OpVariable %56 Private
+         %59 = OpTypeVector %54 3
+         %60 = OpTypeMatrix %59 2
+         %61 = OpTypePointer Private %60
+         %62 = OpVariable %61 Private
+         %64 = OpTypePointer Private %54
+         %69 = OpConstant %54 2
+         %71 = OpConstant %6 1
+         %72 = OpConstant %6 2
+        %201 = OpConstant %15 2
+         %73 = OpConstant %6 3
+        %202 = OpConstant %15 3
+        %203 = OpConstant %6 1
+        %204 = OpConstant %6 9
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+
+  std::vector<protobufs::Instruction> instructions;
+
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpFunction, 2, 12,
+      {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
+       {SPV_OPERAND_TYPE_TYPE_ID, {8}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpFunctionParameter, 102, 10, {}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 13, {}));
+
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpVariable, 7, 14,
+      {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpVariable, 26, 27,
+      {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 6, 22, {{SPV_OPERAND_TYPE_ID, {11}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 23, 24,
+                                                {{SPV_OPERAND_TYPE_ID, {20}},
+                                                 {SPV_OPERAND_TYPE_ID, {21}},
+                                                 {SPV_OPERAND_TYPE_ID, {22}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 6, 25, {{SPV_OPERAND_TYPE_ID, {24}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpStore, 0, 0,
+      {{SPV_OPERAND_TYPE_ID, {14}}, {SPV_OPERAND_TYPE_ID, {25}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 15, 28, {{SPV_OPERAND_TYPE_ID, {10}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpAccessChain, 29, 30,
+      {{SPV_OPERAND_TYPE_ID, {20}}, {SPV_OPERAND_TYPE_ID, {28}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 17, 31, {{SPV_OPERAND_TYPE_ID, {30}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpStore, 0, 0,
+      {{SPV_OPERAND_TYPE_ID, {27}}, {SPV_OPERAND_TYPE_ID, {31}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 6, 32, {{SPV_OPERAND_TYPE_ID, {9}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpInBoundsAccessChain, 7, 34,
+      {{SPV_OPERAND_TYPE_ID, {27}}, {SPV_OPERAND_TYPE_ID, {32}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpStore, 0, 0,
+      {{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 6, 39, {{SPV_OPERAND_TYPE_ID, {9}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpAccessChain, 23, 40,
+      {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {33}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 6, 41, {{SPV_OPERAND_TYPE_ID, {40}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpInBoundsAccessChain, 23, 42,
+      {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {39}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpStore, 0, 0,
+      {{SPV_OPERAND_TYPE_ID, {42}}, {SPV_OPERAND_TYPE_ID, {41}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 15, 43, {{SPV_OPERAND_TYPE_ID, {10}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 6, 44, {{SPV_OPERAND_TYPE_ID, {11}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 6, 45, {{SPV_OPERAND_TYPE_ID, {9}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 15, 46, {{SPV_OPERAND_TYPE_ID, {10}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpIAdd, 6, 47,
+      {{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {46}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpAccessChain, 23, 48,
+      {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {47}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 6, 49, {{SPV_OPERAND_TYPE_ID, {48}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpInBoundsAccessChain, 23,
+                                                50,
+                                                {{SPV_OPERAND_TYPE_ID, {20}},
+                                                 {SPV_OPERAND_TYPE_ID, {43}},
+                                                 {SPV_OPERAND_TYPE_ID, {44}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 6, 51, {{SPV_OPERAND_TYPE_ID, {50}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpIAdd, 6, 52,
+      {{SPV_OPERAND_TYPE_ID, {51}}, {SPV_OPERAND_TYPE_ID, {49}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 23, 53,
+                                                {{SPV_OPERAND_TYPE_ID, {20}},
+                                                 {SPV_OPERAND_TYPE_ID, {43}},
+                                                 {SPV_OPERAND_TYPE_ID, {44}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpStore, 0, 0,
+      {{SPV_OPERAND_TYPE_ID, {53}}, {SPV_OPERAND_TYPE_ID, {52}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 15, 58, {{SPV_OPERAND_TYPE_ID, {10}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 6, 63, {{SPV_OPERAND_TYPE_ID, {11}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 64, 65,
+                                                {{SPV_OPERAND_TYPE_ID, {62}},
+                                                 {SPV_OPERAND_TYPE_ID, {21}},
+                                                 {SPV_OPERAND_TYPE_ID, {63}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 64, 101,
+                                                {{SPV_OPERAND_TYPE_ID, {62}},
+                                                 {SPV_OPERAND_TYPE_ID, {45}},
+                                                 {SPV_OPERAND_TYPE_ID, {46}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 54, 66, {{SPV_OPERAND_TYPE_ID, {65}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpAccessChain, 64, 67,
+      {{SPV_OPERAND_TYPE_ID, {57}}, {SPV_OPERAND_TYPE_ID, {58}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpStore, 0, 0,
+      {{SPV_OPERAND_TYPE_ID, {67}}, {SPV_OPERAND_TYPE_ID, {66}}}));
+  instructions.push_back(
+      MakeInstructionMessage(SpvOpLoad, 6, 68, {{SPV_OPERAND_TYPE_ID, {9}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpInBoundsAccessChain, 64, 70,
+      {{SPV_OPERAND_TYPE_ID, {57}}, {SPV_OPERAND_TYPE_ID, {68}}}));
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpStore, 0, 0,
+      {{SPV_OPERAND_TYPE_ID, {70}}, {SPV_OPERAND_TYPE_ID, {69}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
+
+  FactManager fact_manager1;
+  FactManager fact_manager2;
+
+  const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context1.get()));
+
+  TransformationAddFunction add_dead_function(instructions);
+  ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1));
+  add_dead_function.Apply(context1.get(), &fact_manager1);
+  ASSERT_TRUE(IsValid(env, context1.get()));
+  // The function should not be deemed livesafe
+  ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(12));
+  // All variables/parameters in the function should be deemed irrelevant.
+  ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
+      context1.get(), fact_manager1, 12, 0));
+
+  std::string added_as_dead_code = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+        %100 = OpTypeBool
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+         %15 = OpTypeInt 32 0
+        %102 = OpTypePointer Function %15
+          %8 = OpTypeFunction %2 %7 %102 %7
+         %16 = OpConstant %15 5
+         %17 = OpTypeArray %6 %16
+         %18 = OpTypeArray %17 %16
+         %19 = OpTypePointer Private %18
+         %20 = OpVariable %19 Private
+         %21 = OpConstant %6 0
+         %23 = OpTypePointer Private %6
+         %26 = OpTypePointer Function %17
+         %29 = OpTypePointer Private %17
+         %33 = OpConstant %6 4
+        %200 = OpConstant %15 4
+         %35 = OpConstant %15 10
+         %36 = OpTypeArray %6 %35
+         %37 = OpTypePointer Private %36
+         %38 = OpVariable %37 Private
+         %54 = OpTypeFloat 32
+         %55 = OpTypeVector %54 4
+         %56 = OpTypePointer Private %55
+         %57 = OpVariable %56 Private
+         %59 = OpTypeVector %54 3
+         %60 = OpTypeMatrix %59 2
+         %61 = OpTypePointer Private %60
+         %62 = OpVariable %61 Private
+         %64 = OpTypePointer Private %54
+         %69 = OpConstant %54 2
+         %71 = OpConstant %6 1
+         %72 = OpConstant %6 2
+        %201 = OpConstant %15 2
+         %73 = OpConstant %6 3
+        %202 = OpConstant %15 3
+        %203 = OpConstant %6 1
+        %204 = OpConstant %6 9
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %2 None %8
+          %9 = OpFunctionParameter %7
+         %10 = OpFunctionParameter %102
+         %11 = OpFunctionParameter %7
+         %13 = OpLabel
+         %14 = OpVariable %7 Function
+         %27 = OpVariable %26 Function
+         %22 = OpLoad %6 %11
+         %24 = OpAccessChain %23 %20 %21 %22
+         %25 = OpLoad %6 %24
+               OpStore %14 %25
+         %28 = OpLoad %15 %10
+         %30 = OpAccessChain %29 %20 %28
+         %31 = OpLoad %17 %30
+               OpStore %27 %31
+         %32 = OpLoad %6 %9
+         %34 = OpInBoundsAccessChain %7 %27 %32
+               OpStore %34 %33
+         %39 = OpLoad %6 %9
+         %40 = OpAccessChain %23 %38 %33
+         %41 = OpLoad %6 %40
+         %42 = OpInBoundsAccessChain %23 %38 %39
+               OpStore %42 %41
+         %43 = OpLoad %15 %10
+         %44 = OpLoad %6 %11
+         %45 = OpLoad %6 %9
+         %46 = OpLoad %15 %10
+         %47 = OpIAdd %6 %45 %46
+         %48 = OpAccessChain %23 %38 %47
+         %49 = OpLoad %6 %48
+         %50 = OpInBoundsAccessChain %23 %20 %43 %44
+         %51 = OpLoad %6 %50
+         %52 = OpIAdd %6 %51 %49
+         %53 = OpAccessChain %23 %20 %43 %44
+               OpStore %53 %52
+         %58 = OpLoad %15 %10
+         %63 = OpLoad %6 %11
+         %65 = OpAccessChain %64 %62 %21 %63
+        %101 = OpAccessChain %64 %62 %45 %46
+         %66 = OpLoad %54 %65
+         %67 = OpAccessChain %64 %57 %58
+               OpStore %67 %66
+         %68 = OpLoad %6 %9
+         %70 = OpInBoundsAccessChain %64 %57 %68
+               OpStore %70 %69
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get()));
+
+  std::vector<protobufs::AccessChainClampingInfo> access_chain_clamping_info;
+  access_chain_clamping_info.push_back(
+      MakeAccessClampingInfo(24, {{1001, 2001}, {1002, 2002}}));
+  access_chain_clamping_info.push_back(
+      MakeAccessClampingInfo(30, {{1003, 2003}}));
+  access_chain_clamping_info.push_back(
+      MakeAccessClampingInfo(34, {{1004, 2004}}));
+  access_chain_clamping_info.push_back(
+      MakeAccessClampingInfo(40, {{1005, 2005}}));
+  access_chain_clamping_info.push_back(
+      MakeAccessClampingInfo(42, {{1006, 2006}}));
+  access_chain_clamping_info.push_back(
+      MakeAccessClampingInfo(48, {{1007, 2007}}));
+  access_chain_clamping_info.push_back(
+      MakeAccessClampingInfo(50, {{1008, 2008}, {1009, 2009}}));
+  access_chain_clamping_info.push_back(
+      MakeAccessClampingInfo(53, {{1010, 2010}, {1011, 2011}}));
+  access_chain_clamping_info.push_back(
+      MakeAccessClampingInfo(65, {{1012, 2012}, {1013, 2013}}));
+  access_chain_clamping_info.push_back(
+      MakeAccessClampingInfo(101, {{1014, 2014}, {1015, 2015}}));
+  access_chain_clamping_info.push_back(
+      MakeAccessClampingInfo(67, {{1016, 2016}}));
+  access_chain_clamping_info.push_back(
+      MakeAccessClampingInfo(70, {{1017, 2017}}));
+
+  TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13,
+                                                  access_chain_clamping_info);
+  ASSERT_TRUE(
+      add_livesafe_function.IsApplicable(context2.get(), fact_manager2));
+  add_livesafe_function.Apply(context2.get(), &fact_manager2);
+  ASSERT_TRUE(IsValid(env, context2.get()));
+  // The function should be deemed livesafe
+  ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(12));
+  // All variables/parameters in the function should be deemed irrelevant.
+  ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
+      context2.get(), fact_manager2, 12, 0));
+  std::string added_as_livesafe_code = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+        %100 = OpTypeBool
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+         %15 = OpTypeInt 32 0
+        %102 = OpTypePointer Function %15
+          %8 = OpTypeFunction %2 %7 %102 %7
+         %16 = OpConstant %15 5
+         %17 = OpTypeArray %6 %16
+         %18 = OpTypeArray %17 %16
+         %19 = OpTypePointer Private %18
+         %20 = OpVariable %19 Private
+         %21 = OpConstant %6 0
+         %23 = OpTypePointer Private %6
+         %26 = OpTypePointer Function %17
+         %29 = OpTypePointer Private %17
+         %33 = OpConstant %6 4
+        %200 = OpConstant %15 4
+         %35 = OpConstant %15 10
+         %36 = OpTypeArray %6 %35
+         %37 = OpTypePointer Private %36
+         %38 = OpVariable %37 Private
+         %54 = OpTypeFloat 32
+         %55 = OpTypeVector %54 4
+         %56 = OpTypePointer Private %55
+         %57 = OpVariable %56 Private
+         %59 = OpTypeVector %54 3
+         %60 = OpTypeMatrix %59 2
+         %61 = OpTypePointer Private %60
+         %62 = OpVariable %61 Private
+         %64 = OpTypePointer Private %54
+         %69 = OpConstant %54 2
+         %71 = OpConstant %6 1
+         %72 = OpConstant %6 2
+        %201 = OpConstant %15 2
+         %73 = OpConstant %6 3
+        %202 = OpConstant %15 3
+        %203 = OpConstant %6 1
+        %204 = OpConstant %6 9
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %2 None %8
+          %9 = OpFunctionParameter %7
+         %10 = OpFunctionParameter %102
+         %11 = OpFunctionParameter %7
+         %13 = OpLabel
+         %14 = OpVariable %7 Function
+         %27 = OpVariable %26 Function
+         %22 = OpLoad %6 %11
+       %1002 = OpULessThanEqual %100 %22 %33
+       %2002 = OpSelect %6 %1002 %22 %33
+         %24 = OpAccessChain %23 %20 %21 %2002
+         %25 = OpLoad %6 %24
+               OpStore %14 %25
+         %28 = OpLoad %15 %10
+       %1003 = OpULessThanEqual %100 %28 %200
+       %2003 = OpSelect %15 %1003 %28 %200
+         %30 = OpAccessChain %29 %20 %2003
+         %31 = OpLoad %17 %30
+               OpStore %27 %31
+         %32 = OpLoad %6 %9
+       %1004 = OpULessThanEqual %100 %32 %33
+       %2004 = OpSelect %6 %1004 %32 %33
+         %34 = OpInBoundsAccessChain %7 %27 %2004
+               OpStore %34 %33
+         %39 = OpLoad %6 %9
+         %40 = OpAccessChain %23 %38 %33
+         %41 = OpLoad %6 %40
+       %1006 = OpULessThanEqual %100 %39 %204
+       %2006 = OpSelect %6 %1006 %39 %204
+         %42 = OpInBoundsAccessChain %23 %38 %2006
+               OpStore %42 %41
+         %43 = OpLoad %15 %10
+         %44 = OpLoad %6 %11
+         %45 = OpLoad %6 %9
+         %46 = OpLoad %15 %10
+         %47 = OpIAdd %6 %45 %46
+       %1007 = OpULessThanEqual %100 %47 %204
+       %2007 = OpSelect %6 %1007 %47 %204
+         %48 = OpAccessChain %23 %38 %2007
+         %49 = OpLoad %6 %48
+       %1008 = OpULessThanEqual %100 %43 %200
+       %2008 = OpSelect %15 %1008 %43 %200
+       %1009 = OpULessThanEqual %100 %44 %33
+       %2009 = OpSelect %6 %1009 %44 %33
+         %50 = OpInBoundsAccessChain %23 %20 %2008 %2009
+         %51 = OpLoad %6 %50
+         %52 = OpIAdd %6 %51 %49
+       %1010 = OpULessThanEqual %100 %43 %200
+       %2010 = OpSelect %15 %1010 %43 %200
+       %1011 = OpULessThanEqual %100 %44 %33
+       %2011 = OpSelect %6 %1011 %44 %33
+         %53 = OpAccessChain %23 %20 %2010 %2011
+               OpStore %53 %52
+         %58 = OpLoad %15 %10
+         %63 = OpLoad %6 %11
+       %1013 = OpULessThanEqual %100 %63 %72
+       %2013 = OpSelect %6 %1013 %63 %72
+         %65 = OpAccessChain %64 %62 %21 %2013
+       %1014 = OpULessThanEqual %100 %45 %71
+       %2014 = OpSelect %6 %1014 %45 %71
+       %1015 = OpULessThanEqual %100 %46 %201
+       %2015 = OpSelect %15 %1015 %46 %201
+        %101 = OpAccessChain %64 %62 %2014 %2015
+         %66 = OpLoad %54 %65
+       %1016 = OpULessThanEqual %100 %58 %202
+       %2016 = OpSelect %15 %1016 %58 %202
+         %67 = OpAccessChain %64 %57 %2016
+               OpStore %67 %66
+         %68 = OpLoad %6 %9
+       %1017 = OpULessThanEqual %100 %68 %73
+       %2017 = OpSelect %6 %1017 %68 %73
+         %70 = OpInBoundsAccessChain %64 %57 %2017
+               OpStore %70 %69
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get()));
+}
+
+TEST(TransformationAddFunctionTest, LivesafeCanCallLivesafe) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+
+  std::vector<protobufs::Instruction> instructions;
+
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpFunction, 2, 8,
+      {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
+       {SPV_OPERAND_TYPE_TYPE_ID, {3}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 9, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpFunctionCall, 2, 11,
+                                                {{SPV_OPERAND_TYPE_ID, {6}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
+
+  FactManager fact_manager1;
+  FactManager fact_manager2;
+
+  // Mark function 6 as livesafe.
+  fact_manager2.AddFactFunctionIsLivesafe(6);
+
+  const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context1.get()));
+
+  TransformationAddFunction add_dead_function(instructions);
+  ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1));
+  add_dead_function.Apply(context1.get(), &fact_manager1);
+  ASSERT_TRUE(IsValid(env, context1.get()));
+  // The function should not be deemed livesafe
+  ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(8));
+  // All variables/parameters in the function should be deemed irrelevant.
+  ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
+      context1.get(), fact_manager1, 8, 0));
+
+  std::string added_as_live_or_dead_code = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %8 = OpFunction %2 None %3
+          %9 = OpLabel
+         %11 = OpFunctionCall %2 %6
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, added_as_live_or_dead_code, context1.get()));
+
+  TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0,
+                                                  {});
+  ASSERT_TRUE(
+      add_livesafe_function.IsApplicable(context2.get(), fact_manager2));
+  add_livesafe_function.Apply(context2.get(), &fact_manager2);
+  ASSERT_TRUE(IsValid(env, context2.get()));
+  // The function should be deemed livesafe
+  ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(8));
+  // All variables/parameters in the function should be deemed irrelevant.
+  ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
+      context2.get(), fact_manager2, 8, 0));
+  ASSERT_TRUE(IsEqual(env, added_as_live_or_dead_code, context2.get()));
+}
+
+TEST(TransformationAddFunctionTest, LivesafeOnlyCallsLivesafe) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+               OpKill
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+
+  std::vector<protobufs::Instruction> instructions;
+
+  instructions.push_back(MakeInstructionMessage(
+      SpvOpFunction, 2, 8,
+      {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
+       {SPV_OPERAND_TYPE_TYPE_ID, {3}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 9, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpFunctionCall, 2, 11,
+                                                {{SPV_OPERAND_TYPE_ID, {6}}}));
+  instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {}));
+  instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
+
+  FactManager fact_manager1;
+  FactManager fact_manager2;
+
+  const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context1.get()));
+
+  TransformationAddFunction add_dead_function(instructions);
+  ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1));
+  add_dead_function.Apply(context1.get(), &fact_manager1);
+  ASSERT_TRUE(IsValid(env, context1.get()));
+  // The function should not be deemed livesafe
+  ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(8));
+  // All variables/parameters in the function should be deemed irrelevant.
+  ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
+      context1.get(), fact_manager1, 8, 0));
+
+  std::string added_as_dead_code = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+               OpKill
+               OpFunctionEnd
+          %8 = OpFunction %2 None %3
+          %9 = OpLabel
+         %11 = OpFunctionCall %2 %6
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get()));
+
+  TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0,
+                                                  {});
+  ASSERT_FALSE(
+      add_livesafe_function.IsApplicable(context2.get(), fact_manager2));
+}
+
+TEST(TransformationAddFunctionTest,
+     LoopLimitersBackEdgeBlockEndsWithConditional1) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %8 0
+         %18 = OpConstant %8 10
+         %19 = OpTypeBool
+         %26 = OpConstantTrue %19
+         %27 = OpConstantFalse %19
+         %28 = OpTypeInt 32 0
+         %29 = OpTypePointer Function %28
+         %30 = OpConstant %28 0
+         %31 = OpConstant %28 1
+         %32 = OpConstant %28 5
+         %22 = OpConstant %8 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %8 0
+         %18 = OpConstant %8 10
+         %19 = OpTypeBool
+         %26 = OpConstantTrue %19
+         %27 = OpConstantFalse %19
+         %28 = OpTypeInt 32 0
+         %29 = OpTypePointer Function %28
+         %30 = OpConstant %28 0
+         %31 = OpConstant %28 1
+         %32 = OpConstant %28 5
+         %22 = OpConstant %8 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+         %10 = OpVariable %9 Function
+               OpStore %10 %11
+               OpBranch %12
+         %12 = OpLabel
+               OpLoopMerge %14 %15 None
+               OpBranch %15
+         %15 = OpLabel
+         %17 = OpLoad %8 %10
+         %20 = OpSLessThan %19 %17 %18
+         %21 = OpLoad %8 %10
+         %23 = OpIAdd %8 %21 %22
+               OpStore %10 %23
+               OpBranchConditional %20 %12 %14
+         %14 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+
+  FactManager fact_manager;
+
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Make a sequence of instruction messages corresponding to function %8 in
+  // |donor|.
+  std::vector<protobufs::Instruction> instructions =
+      GetInstructionsForFunction(env, consumer, donor, 6);
+
+  protobufs::LoopLimiterInfo loop_limiter_info;
+  loop_limiter_info.set_loop_header_id(12);
+  loop_limiter_info.set_load_id(102);
+  loop_limiter_info.set_increment_id(103);
+  loop_limiter_info.set_compare_id(104);
+  loop_limiter_info.set_logical_op_id(105);
+  TransformationAddFunction add_livesafe_function(instructions, 100, 32,
+                                                  {loop_limiter_info}, 0, {});
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
+  add_livesafe_function.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  std::string expected = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %8 0
+         %18 = OpConstant %8 10
+         %19 = OpTypeBool
+         %26 = OpConstantTrue %19
+         %27 = OpConstantFalse %19
+         %28 = OpTypeInt 32 0
+         %29 = OpTypePointer Function %28
+         %30 = OpConstant %28 0
+         %31 = OpConstant %28 1
+         %32 = OpConstant %28 5
+         %22 = OpConstant %8 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+        %100 = OpVariable %29 Function %30
+         %10 = OpVariable %9 Function
+               OpStore %10 %11
+               OpBranch %12
+         %12 = OpLabel
+               OpLoopMerge %14 %15 None
+               OpBranch %15
+         %15 = OpLabel
+         %17 = OpLoad %8 %10
+         %20 = OpSLessThan %19 %17 %18
+         %21 = OpLoad %8 %10
+         %23 = OpIAdd %8 %21 %22
+               OpStore %10 %23
+        %102 = OpLoad %28 %100
+        %103 = OpIAdd %28 %102 %31
+               OpStore %100 %103
+        %104 = OpULessThan %19 %102 %32
+        %105 = OpLogicalAnd %19 %20 %104
+               OpBranchConditional %105 %12 %14
+         %14 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, expected, context.get()));
+}
+
+TEST(TransformationAddFunctionTest,
+     LoopLimitersBackEdgeBlockEndsWithConditional2) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %8 0
+         %18 = OpConstant %8 10
+         %19 = OpTypeBool
+         %26 = OpConstantTrue %19
+         %27 = OpConstantFalse %19
+         %28 = OpTypeInt 32 0
+         %29 = OpTypePointer Function %28
+         %30 = OpConstant %28 0
+         %31 = OpConstant %28 1
+         %32 = OpConstant %28 5
+         %22 = OpConstant %8 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %8 0
+         %18 = OpConstant %8 10
+         %19 = OpTypeBool
+         %26 = OpConstantTrue %19
+         %27 = OpConstantFalse %19
+         %28 = OpTypeInt 32 0
+         %29 = OpTypePointer Function %28
+         %30 = OpConstant %28 0
+         %31 = OpConstant %28 1
+         %32 = OpConstant %28 5
+         %22 = OpConstant %8 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+         %10 = OpVariable %9 Function
+               OpStore %10 %11
+               OpBranch %12
+         %12 = OpLabel
+               OpLoopMerge %14 %15 None
+               OpBranch %15
+         %15 = OpLabel
+         %17 = OpLoad %8 %10
+         %20 = OpSLessThan %19 %17 %18
+         %21 = OpLoad %8 %10
+         %23 = OpIAdd %8 %21 %22
+               OpStore %10 %23
+         %50 = OpLogicalNot %19 %20
+               OpBranchConditional %50 %14 %12
+         %14 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+
+  FactManager fact_manager;
+
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Make a sequence of instruction messages corresponding to function %8 in
+  // |donor|.
+  std::vector<protobufs::Instruction> instructions =
+      GetInstructionsForFunction(env, consumer, donor, 6);
+
+  protobufs::LoopLimiterInfo loop_limiter_info;
+  loop_limiter_info.set_loop_header_id(12);
+  loop_limiter_info.set_load_id(102);
+  loop_limiter_info.set_increment_id(103);
+  loop_limiter_info.set_compare_id(104);
+  loop_limiter_info.set_logical_op_id(105);
+  TransformationAddFunction add_livesafe_function(instructions, 100, 32,
+                                                  {loop_limiter_info}, 0, {});
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
+  add_livesafe_function.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  std::string expected = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %8 0
+         %18 = OpConstant %8 10
+         %19 = OpTypeBool
+         %26 = OpConstantTrue %19
+         %27 = OpConstantFalse %19
+         %28 = OpTypeInt 32 0
+         %29 = OpTypePointer Function %28
+         %30 = OpConstant %28 0
+         %31 = OpConstant %28 1
+         %32 = OpConstant %28 5
+         %22 = OpConstant %8 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+        %100 = OpVariable %29 Function %30
+         %10 = OpVariable %9 Function
+               OpStore %10 %11
+               OpBranch %12
+         %12 = OpLabel
+               OpLoopMerge %14 %15 None
+               OpBranch %15
+         %15 = OpLabel
+         %17 = OpLoad %8 %10
+         %20 = OpSLessThan %19 %17 %18
+         %21 = OpLoad %8 %10
+         %23 = OpIAdd %8 %21 %22
+               OpStore %10 %23
+         %50 = OpLogicalNot %19 %20
+        %102 = OpLoad %28 %100
+        %103 = OpIAdd %28 %102 %31
+               OpStore %100 %103
+        %104 = OpUGreaterThanEqual %19 %102 %32
+        %105 = OpLogicalOr %19 %50 %104
+               OpBranchConditional %105 %14 %12
+         %14 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, expected, context.get()));
+}
+
+TEST(TransformationAddFunctionTest, LoopLimitersHeaderIsBackEdgeBlock) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %8 0
+         %18 = OpConstant %8 10
+         %19 = OpTypeBool
+         %26 = OpConstantTrue %19
+         %27 = OpConstantFalse %19
+         %28 = OpTypeInt 32 0
+         %29 = OpTypePointer Function %28
+         %30 = OpConstant %28 0
+         %31 = OpConstant %28 1
+         %32 = OpConstant %28 5
+         %22 = OpConstant %8 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %8 0
+         %18 = OpConstant %8 10
+         %19 = OpTypeBool
+         %26 = OpConstantTrue %19
+         %27 = OpConstantFalse %19
+         %28 = OpTypeInt 32 0
+         %29 = OpTypePointer Function %28
+         %30 = OpConstant %28 0
+         %31 = OpConstant %28 1
+         %32 = OpConstant %28 5
+         %22 = OpConstant %8 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+         %10 = OpVariable %9 Function
+               OpStore %10 %11
+               OpBranch %12
+         %12 = OpLabel
+         %17 = OpLoad %8 %10
+         %20 = OpSLessThan %19 %17 %18
+         %21 = OpLoad %8 %10
+         %23 = OpIAdd %8 %21 %22
+               OpStore %10 %23
+         %50 = OpLogicalNot %19 %20
+               OpLoopMerge %14 %12 None
+               OpBranchConditional %50 %14 %12
+         %14 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+
+  FactManager fact_manager;
+
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Make a sequence of instruction messages corresponding to function %8 in
+  // |donor|.
+  std::vector<protobufs::Instruction> instructions =
+      GetInstructionsForFunction(env, consumer, donor, 6);
+
+  protobufs::LoopLimiterInfo loop_limiter_info;
+  loop_limiter_info.set_loop_header_id(12);
+  loop_limiter_info.set_load_id(102);
+  loop_limiter_info.set_increment_id(103);
+  loop_limiter_info.set_compare_id(104);
+  loop_limiter_info.set_logical_op_id(105);
+  TransformationAddFunction add_livesafe_function(instructions, 100, 32,
+                                                  {loop_limiter_info}, 0, {});
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
+  add_livesafe_function.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  std::string expected = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %8 0
+         %18 = OpConstant %8 10
+         %19 = OpTypeBool
+         %26 = OpConstantTrue %19
+         %27 = OpConstantFalse %19
+         %28 = OpTypeInt 32 0
+         %29 = OpTypePointer Function %28
+         %30 = OpConstant %28 0
+         %31 = OpConstant %28 1
+         %32 = OpConstant %28 5
+         %22 = OpConstant %8 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+        %100 = OpVariable %29 Function %30
+         %10 = OpVariable %9 Function
+               OpStore %10 %11
+               OpBranch %12
+         %12 = OpLabel
+         %17 = OpLoad %8 %10
+         %20 = OpSLessThan %19 %17 %18
+         %21 = OpLoad %8 %10
+         %23 = OpIAdd %8 %21 %22
+               OpStore %10 %23
+         %50 = OpLogicalNot %19 %20
+        %102 = OpLoad %28 %100
+        %103 = OpIAdd %28 %102 %31
+               OpStore %100 %103
+        %104 = OpUGreaterThanEqual %19 %102 %32
+        %105 = OpLogicalOr %19 %50 %104
+               OpLoopMerge %14 %12 None
+               OpBranchConditional %105 %14 %12
+         %14 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, expected, context.get()));
+}
+
+TEST(TransformationAddFunctionTest, InfiniteLoop1) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %8 0
+         %18 = OpConstant %8 10
+         %19 = OpTypeBool
+         %26 = OpConstantTrue %19
+         %27 = OpConstantFalse %19
+         %28 = OpTypeInt 32 0
+         %29 = OpTypePointer Function %28
+         %30 = OpConstant %28 0
+         %31 = OpConstant %28 1
+         %32 = OpConstant %28 5
+         %22 = OpConstant %8 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %8 0
+         %18 = OpConstant %8 10
+         %19 = OpTypeBool
+         %26 = OpConstantTrue %19
+         %27 = OpConstantFalse %19
+         %28 = OpTypeInt 32 0
+         %29 = OpTypePointer Function %28
+         %30 = OpConstant %28 0
+         %31 = OpConstant %28 1
+         %32 = OpConstant %28 5
+         %22 = OpConstant %8 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+         %10 = OpVariable %9 Function
+               OpStore %10 %11
+               OpBranch %12
+         %12 = OpLabel
+               OpLoopMerge %14 %12 None
+               OpBranch %12
+         %14 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+
+  FactManager fact_manager;
+
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Make a sequence of instruction messages corresponding to function %8 in
+  // |donor|.
+  std::vector<protobufs::Instruction> instructions =
+      GetInstructionsForFunction(env, consumer, donor, 6);
+
+  protobufs::LoopLimiterInfo loop_limiter_info;
+  loop_limiter_info.set_loop_header_id(12);
+  loop_limiter_info.set_load_id(102);
+  loop_limiter_info.set_increment_id(103);
+  loop_limiter_info.set_compare_id(104);
+  loop_limiter_info.set_logical_op_id(105);
+  TransformationAddFunction add_livesafe_function(instructions, 100, 32,
+                                                  {loop_limiter_info}, 0, {});
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
+  add_livesafe_function.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  std::string expected = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %8 0
+         %18 = OpConstant %8 10
+         %19 = OpTypeBool
+         %26 = OpConstantTrue %19
+         %27 = OpConstantFalse %19
+         %28 = OpTypeInt 32 0
+         %29 = OpTypePointer Function %28
+         %30 = OpConstant %28 0
+         %31 = OpConstant %28 1
+         %32 = OpConstant %28 5
+         %22 = OpConstant %8 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+        %100 = OpVariable %29 Function %30
+         %10 = OpVariable %9 Function
+               OpStore %10 %11
+               OpBranch %12
+         %12 = OpLabel
+        %102 = OpLoad %28 %100
+        %103 = OpIAdd %28 %102 %31
+               OpStore %100 %103
+        %104 = OpUGreaterThanEqual %19 %102 %32
+               OpLoopMerge %14 %12 None
+               OpBranchConditional %104 %14 %12
+         %14 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, expected, context.get()));
+}
+
+TEST(TransformationAddFunctionTest, UnreachableContinueConstruct) {
+  // This captures the case where the loop's continue construct is statically
+  // unreachable.  In this case the loop cannot iterate and so we do not add
+  // a loop limiter.  (The reason we do not just add one anyway is that
+  // detecting which block would be the back-edge block is difficult in the
+  // absence of reliable dominance information.)
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %8 0
+         %18 = OpConstant %8 10
+         %19 = OpTypeBool
+         %23 = OpConstant %8 1
+         %26 = OpConstantTrue %19
+         %27 = OpConstantFalse %19
+         %28 = OpTypeInt 32 0
+         %29 = OpTypePointer Function %28
+         %30 = OpConstant %28 0
+         %31 = OpConstant %28 1
+         %32 = OpConstant %28 5
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %8 0
+         %18 = OpConstant %8 10
+         %19 = OpTypeBool
+         %23 = OpConstant %8 1
+         %26 = OpConstantTrue %19
+         %27 = OpConstantFalse %19
+         %28 = OpTypeInt 32 0
+         %29 = OpTypePointer Function %28
+         %30 = OpConstant %28 0
+         %31 = OpConstant %28 1
+         %32 = OpConstant %28 5
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+         %10 = OpVariable %9 Function
+               OpStore %10 %11
+               OpBranch %12
+         %12 = OpLabel
+               OpLoopMerge %14 %15 None
+               OpBranch %16
+         %16 = OpLabel
+         %17 = OpLoad %8 %10
+         %20 = OpSLessThan %19 %17 %18
+               OpBranchConditional %20 %13 %14
+         %13 = OpLabel
+               OpBranch %14
+         %15 = OpLabel
+         %22 = OpLoad %8 %10
+         %24 = OpIAdd %8 %22 %23
+               OpStore %10 %24
+               OpBranch %12
+         %14 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+
+  FactManager fact_manager;
+
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Make a sequence of instruction messages corresponding to function %8 in
+  // |donor|.
+  std::vector<protobufs::Instruction> instructions =
+      GetInstructionsForFunction(env, consumer, donor, 6);
+
+  protobufs::LoopLimiterInfo loop_limiter_info;
+  loop_limiter_info.set_loop_header_id(12);
+  loop_limiter_info.set_load_id(102);
+  loop_limiter_info.set_increment_id(103);
+  loop_limiter_info.set_compare_id(104);
+  loop_limiter_info.set_logical_op_id(105);
+  TransformationAddFunction add_livesafe_function(instructions, 100, 32,
+                                                  {loop_limiter_info}, 0, {});
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
+  add_livesafe_function.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  std::string expected = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %8 0
+         %18 = OpConstant %8 10
+         %19 = OpTypeBool
+         %23 = OpConstant %8 1
+         %26 = OpConstantTrue %19
+         %27 = OpConstantFalse %19
+         %28 = OpTypeInt 32 0
+         %29 = OpTypePointer Function %28
+         %30 = OpConstant %28 0
+         %31 = OpConstant %28 1
+         %32 = OpConstant %28 5
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+        %100 = OpVariable %29 Function %30
+         %10 = OpVariable %9 Function
+               OpStore %10 %11
+               OpBranch %12
+         %12 = OpLabel
+               OpLoopMerge %14 %15 None
+               OpBranch %16
+         %16 = OpLabel
+         %17 = OpLoad %8 %10
+         %20 = OpSLessThan %19 %17 %18
+               OpBranchConditional %20 %13 %14
+         %13 = OpLabel
+               OpBranch %14
+         %15 = OpLabel
+         %22 = OpLoad %8 %10
+         %24 = OpIAdd %8 %22 %23
+               OpStore %10 %24
+               OpBranch %12
+         %14 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, expected, context.get()));
+}
+
+TEST(TransformationAddFunctionTest, LoopLimitersAndOpPhi1) {
+  // This captures the scenario where breaking a loop due to a loop limiter
+  // requires patching up OpPhi instructions occurring at the loop merge block.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %50 = OpTypeInt 32 0
+         %51 = OpConstant %50 0
+         %52 = OpConstant %50 1
+         %53 = OpTypePointer Function %50
+          %7 = OpTypeFunction %6
+         %10 = OpTypePointer Function %6
+         %12 = OpConstant %6 0
+         %19 = OpConstant %6 100
+         %20 = OpTypeBool
+         %23 = OpConstant %6 20
+         %28 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeFunction %6
+         %10 = OpTypePointer Function %6
+         %12 = OpConstant %6 0
+         %19 = OpConstant %6 100
+         %20 = OpTypeBool
+         %23 = OpConstant %6 20
+         %28 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %36 = OpFunctionCall %6 %8
+               OpReturn
+               OpFunctionEnd
+          %8 = OpFunction %6 None %7
+          %9 = OpLabel
+         %11 = OpVariable %10 Function
+               OpStore %11 %12
+               OpBranch %13
+         %13 = OpLabel
+         %37 = OpPhi %6 %12 %9 %32 %16
+               OpLoopMerge %15 %16 None
+               OpBranch %17
+         %17 = OpLabel
+         %21 = OpSLessThan %20 %37 %19
+               OpBranchConditional %21 %14 %15
+         %14 = OpLabel
+         %24 = OpSGreaterThan %20 %37 %23
+               OpSelectionMerge %26 None
+               OpBranchConditional %24 %25 %26
+         %25 = OpLabel
+         %29 = OpIAdd %6 %37 %28
+               OpStore %11 %29
+               OpBranch %15
+         %26 = OpLabel
+               OpBranch %16
+         %16 = OpLabel
+         %32 = OpIAdd %6 %37 %28
+               OpStore %11 %32
+               OpBranch %13
+         %15 = OpLabel
+         %38 = OpPhi %6 %37 %17 %29 %25
+               OpReturnValue %38
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+
+  FactManager fact_manager;
+
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Make a sequence of instruction messages corresponding to function %8 in
+  // |donor|.
+  std::vector<protobufs::Instruction> instructions =
+      GetInstructionsForFunction(env, consumer, donor, 8);
+
+  protobufs::LoopLimiterInfo loop_limiter_info;
+  loop_limiter_info.set_loop_header_id(13);
+  loop_limiter_info.set_load_id(102);
+  loop_limiter_info.set_increment_id(103);
+  loop_limiter_info.set_compare_id(104);
+  loop_limiter_info.set_logical_op_id(105);
+
+  TransformationAddFunction no_op_phi_data(instructions, 100, 28,
+                                           {loop_limiter_info}, 0, {});
+  // The loop limiter info is not good enough; it does not include ids to patch
+  // up the OpPhi at the loop merge.
+  ASSERT_FALSE(no_op_phi_data.IsApplicable(context.get(), fact_manager));
+
+  // Add a phi id for the new edge from the loop back edge block to the loop
+  // merge.
+  loop_limiter_info.add_phi_id(28);
+  TransformationAddFunction with_op_phi_data(instructions, 100, 28,
+                                             {loop_limiter_info}, 0, {});
+  ASSERT_TRUE(with_op_phi_data.IsApplicable(context.get(), fact_manager));
+  with_op_phi_data.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  std::string expected = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %50 = OpTypeInt 32 0
+         %51 = OpConstant %50 0
+         %52 = OpConstant %50 1
+         %53 = OpTypePointer Function %50
+          %7 = OpTypeFunction %6
+         %10 = OpTypePointer Function %6
+         %12 = OpConstant %6 0
+         %19 = OpConstant %6 100
+         %20 = OpTypeBool
+         %23 = OpConstant %6 20
+         %28 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %8 = OpFunction %6 None %7
+          %9 = OpLabel
+        %100 = OpVariable %53 Function %51
+         %11 = OpVariable %10 Function
+               OpStore %11 %12
+               OpBranch %13
+         %13 = OpLabel
+         %37 = OpPhi %6 %12 %9 %32 %16
+               OpLoopMerge %15 %16 None
+               OpBranch %17
+         %17 = OpLabel
+         %21 = OpSLessThan %20 %37 %19
+               OpBranchConditional %21 %14 %15
+         %14 = OpLabel
+         %24 = OpSGreaterThan %20 %37 %23
+               OpSelectionMerge %26 None
+               OpBranchConditional %24 %25 %26
+         %25 = OpLabel
+         %29 = OpIAdd %6 %37 %28
+               OpStore %11 %29
+               OpBranch %15
+         %26 = OpLabel
+               OpBranch %16
+         %16 = OpLabel
+         %32 = OpIAdd %6 %37 %28
+               OpStore %11 %32
+        %102 = OpLoad %50 %100
+        %103 = OpIAdd %50 %102 %52
+               OpStore %100 %103
+        %104 = OpUGreaterThanEqual %20 %102 %28
+               OpBranchConditional %104 %15 %13
+         %15 = OpLabel
+         %38 = OpPhi %6 %37 %17 %29 %25 %28 %16
+               OpReturnValue %38
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, expected, context.get()));
+}
+
+TEST(TransformationAddFunctionTest, LoopLimitersAndOpPhi2) {
+  // This captures the scenario where the loop merge block already has an OpPhi
+  // with the loop back edge block as a 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
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %50 = OpTypeInt 32 0
+         %51 = OpConstant %50 0
+         %52 = OpConstant %50 1
+         %53 = OpTypePointer Function %50
+          %7 = OpTypeFunction %6
+         %10 = OpTypePointer Function %6
+         %12 = OpConstant %6 0
+         %19 = OpConstant %6 100
+         %20 = OpTypeBool
+         %60 = OpConstantTrue %20
+         %23 = OpConstant %6 20
+         %28 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %50 = OpTypeInt 32 0
+         %51 = OpConstant %50 0
+         %52 = OpConstant %50 1
+         %53 = OpTypePointer Function %50
+          %7 = OpTypeFunction %6
+         %10 = OpTypePointer Function %6
+         %12 = OpConstant %6 0
+         %19 = OpConstant %6 100
+         %20 = OpTypeBool
+         %60 = OpConstantTrue %20
+         %23 = OpConstant %6 20
+         %28 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %8 = OpFunction %6 None %7
+          %9 = OpLabel
+         %11 = OpVariable %10 Function
+               OpStore %11 %12
+               OpBranch %13
+         %13 = OpLabel
+         %37 = OpPhi %6 %12 %9 %32 %16
+               OpLoopMerge %15 %16 None
+               OpBranch %17
+         %17 = OpLabel
+         %21 = OpSLessThan %20 %37 %19
+               OpBranchConditional %21 %14 %15
+         %14 = OpLabel
+         %24 = OpSGreaterThan %20 %37 %23
+               OpSelectionMerge %26 None
+               OpBranchConditional %24 %25 %26
+         %25 = OpLabel
+         %29 = OpIAdd %6 %37 %28
+               OpStore %11 %29
+               OpBranch %15
+         %26 = OpLabel
+               OpBranch %16
+         %16 = OpLabel
+         %32 = OpIAdd %6 %37 %28
+               OpStore %11 %32
+               OpBranchConditional %60 %15 %13
+         %15 = OpLabel
+         %38 = OpPhi %6 %37 %17 %29 %25 %23 %16
+               OpReturnValue %38
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+
+  FactManager fact_manager;
+
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Make a sequence of instruction messages corresponding to function %8 in
+  // |donor|.
+  std::vector<protobufs::Instruction> instructions =
+      GetInstructionsForFunction(env, consumer, donor, 8);
+
+  protobufs::LoopLimiterInfo loop_limiter_info;
+  loop_limiter_info.set_loop_header_id(13);
+  loop_limiter_info.set_load_id(102);
+  loop_limiter_info.set_increment_id(103);
+  loop_limiter_info.set_compare_id(104);
+  loop_limiter_info.set_logical_op_id(105);
+
+  TransformationAddFunction transformation(instructions, 100, 28,
+                                           {loop_limiter_info}, 0, {});
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  std::string expected = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %50 = OpTypeInt 32 0
+         %51 = OpConstant %50 0
+         %52 = OpConstant %50 1
+         %53 = OpTypePointer Function %50
+          %7 = OpTypeFunction %6
+         %10 = OpTypePointer Function %6
+         %12 = OpConstant %6 0
+         %19 = OpConstant %6 100
+         %20 = OpTypeBool
+         %60 = OpConstantTrue %20
+         %23 = OpConstant %6 20
+         %28 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %8 = OpFunction %6 None %7
+          %9 = OpLabel
+        %100 = OpVariable %53 Function %51
+         %11 = OpVariable %10 Function
+               OpStore %11 %12
+               OpBranch %13
+         %13 = OpLabel
+         %37 = OpPhi %6 %12 %9 %32 %16
+               OpLoopMerge %15 %16 None
+               OpBranch %17
+         %17 = OpLabel
+         %21 = OpSLessThan %20 %37 %19
+               OpBranchConditional %21 %14 %15
+         %14 = OpLabel
+         %24 = OpSGreaterThan %20 %37 %23
+               OpSelectionMerge %26 None
+               OpBranchConditional %24 %25 %26
+         %25 = OpLabel
+         %29 = OpIAdd %6 %37 %28
+               OpStore %11 %29
+               OpBranch %15
+         %26 = OpLabel
+               OpBranch %16
+         %16 = OpLabel
+         %32 = OpIAdd %6 %37 %28
+               OpStore %11 %32
+        %102 = OpLoad %50 %100
+        %103 = OpIAdd %50 %102 %52
+               OpStore %100 %103
+        %104 = OpUGreaterThanEqual %20 %102 %28
+        %105 = OpLogicalOr %20 %60 %104
+               OpBranchConditional %105 %15 %13
+         %15 = OpLabel
+         %38 = OpPhi %6 %37 %17 %29 %25 %23 %16
+               OpReturnValue %38
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, expected, context.get()));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_variable_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_variable_test.cpp
index eda6828..619f068 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_variable_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_variable_test.cpp
@@ -30,8 +30,10 @@
           %2 = OpTypeVoid
           %3 = OpTypeFunction %2
           %6 = OpTypeFloat 32
+         %40 = OpConstant %6 0
           %7 = OpTypeInt 32 1
           %8 = OpTypeVector %6 2
+         %41 = OpConstantComposite %8 %40 %40
           %9 = OpTypePointer Function %6
          %10 = OpTypePointer Private %6
          %20 = OpTypePointer Uniform %6
@@ -60,71 +62,78 @@
   FactManager fact_manager;
 
   // Id already in use
-  ASSERT_FALSE(TransformationAddGlobalVariable(4, 10, 0).IsApplicable(
-      context.get(), fact_manager));
+  ASSERT_FALSE(TransformationAddGlobalVariable(4, 10, 0, true)
+                   .IsApplicable(context.get(), fact_manager));
   // %1 is not a type
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 1, 0).IsApplicable(
-      context.get(), fact_manager));
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 1, 0, false)
+                   .IsApplicable(context.get(), fact_manager));
 
   // %7 is not a pointer type
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 7, 0).IsApplicable(
-      context.get(), fact_manager));
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 7, 0, true)
+                   .IsApplicable(context.get(), fact_manager));
 
   // %9 does not have Private storage class
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 9, 0).IsApplicable(
-      context.get(), fact_manager));
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 9, 0, false)
+                   .IsApplicable(context.get(), fact_manager));
 
   // %15 does not have Private storage class
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 15, 0)
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 15, 0, true)
                    .IsApplicable(context.get(), fact_manager));
 
   // %10 is a pointer to float, while %16 is an int constant
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 16)
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 16, false)
                    .IsApplicable(context.get(), fact_manager));
 
   // %10 is a Private pointer to float, while %15 is a variable with type
   // Uniform float pointer
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 15)
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 15, true)
                    .IsApplicable(context.get(), fact_manager));
 
   // %12 is a Private pointer to int, while %10 is a variable with type
   // Private float pointer
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 12, 10)
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 12, 10, false)
                    .IsApplicable(context.get(), fact_manager));
 
   // %10 is pointer-to-float, and %14 has type pointer-to-float; that's not OK
   // since the initializer's type should be the *pointee* type.
-  ASSERT_FALSE(TransformationAddGlobalVariable(104, 10, 14)
+  ASSERT_FALSE(TransformationAddGlobalVariable(104, 10, 14, true)
                    .IsApplicable(context.get(), fact_manager));
 
   // This would work in principle, but logical addressing does not allow
   // a pointer to a pointer.
-  ASSERT_FALSE(TransformationAddGlobalVariable(104, 17, 14)
+  ASSERT_FALSE(TransformationAddGlobalVariable(104, 17, 14, false)
                    .IsApplicable(context.get(), fact_manager));
 
   TransformationAddGlobalVariable transformations[] = {
       // %100 = OpVariable %12 Private
-      TransformationAddGlobalVariable(100, 12, 0),
+      TransformationAddGlobalVariable(100, 12, 16, true),
 
       // %101 = OpVariable %10 Private
-      TransformationAddGlobalVariable(101, 10, 0),
+      TransformationAddGlobalVariable(101, 10, 40, false),
 
       // %102 = OpVariable %13 Private
-      TransformationAddGlobalVariable(102, 13, 0),
+      TransformationAddGlobalVariable(102, 13, 41, true),
 
       // %103 = OpVariable %12 Private %16
-      TransformationAddGlobalVariable(103, 12, 16),
+      TransformationAddGlobalVariable(103, 12, 16, false),
 
       // %104 = OpVariable %19 Private %21
-      TransformationAddGlobalVariable(104, 19, 21),
+      TransformationAddGlobalVariable(104, 19, 21, true),
 
       // %105 = OpVariable %19 Private %22
-      TransformationAddGlobalVariable(105, 19, 22)};
+      TransformationAddGlobalVariable(105, 19, 22, false)};
 
   for (auto& transformation : transformations) {
     ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
     transformation.Apply(context.get(), &fact_manager);
   }
+  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(100));
+  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(102));
+  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(104));
+  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101));
+  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(103));
+  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(105));
+
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -137,8 +146,10 @@
           %2 = OpTypeVoid
           %3 = OpTypeFunction %2
           %6 = OpTypeFloat 32
+         %40 = OpConstant %6 0
           %7 = OpTypeInt 32 1
           %8 = OpTypeVector %6 2
+         %41 = OpConstantComposite %8 %40 %40
           %9 = OpTypePointer Function %6
          %10 = OpTypePointer Private %6
          %20 = OpTypePointer Uniform %6
@@ -153,9 +164,9 @@
          %19 = OpTypePointer Private %18
          %21 = OpConstantTrue %18
          %22 = OpConstantFalse %18
-        %100 = OpVariable %12 Private
-        %101 = OpVariable %10 Private
-        %102 = OpVariable %13 Private
+        %100 = OpVariable %12 Private %16
+        %101 = OpVariable %10 Private %40
+        %102 = OpVariable %13 Private %41
         %103 = OpVariable %12 Private %16
         %104 = OpVariable %19 Private %21
         %105 = OpVariable %19 Private %22
@@ -215,18 +226,21 @@
 
   TransformationAddGlobalVariable transformations[] = {
       // %100 = OpVariable %12 Private
-      TransformationAddGlobalVariable(100, 12, 0),
+      TransformationAddGlobalVariable(100, 12, 16, true),
 
       // %101 = OpVariable %12 Private %16
-      TransformationAddGlobalVariable(101, 12, 16),
+      TransformationAddGlobalVariable(101, 12, 16, false),
 
       // %102 = OpVariable %19 Private %21
-      TransformationAddGlobalVariable(102, 19, 21)};
+      TransformationAddGlobalVariable(102, 19, 21, true)};
 
   for (auto& transformation : transformations) {
     ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
     transformation.Apply(context.get(), &fact_manager);
   }
+  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(100));
+  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(102));
+  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101));
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -255,7 +269,7 @@
          %18 = OpTypeBool
          %19 = OpTypePointer Private %18
          %21 = OpConstantTrue %18
-        %100 = OpVariable %12 Private
+        %100 = OpVariable %12 Private %16
         %101 = OpVariable %12 Private %16
         %102 = OpVariable %19 Private %21
           %4 = OpFunction %2 None %3
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_local_variable_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_local_variable_test.cpp
new file mode 100644
index 0000000..fd7047f
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_local_variable_test.cpp
@@ -0,0 +1,206 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_local_variable.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddLocalVariableTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeStruct %6 %6
+          %8 = OpTypePointer Function %7
+         %10 = OpConstant %6 1
+         %11 = OpConstant %6 2
+         %12 = OpConstantComposite %7 %10 %11
+         %13 = OpTypeFloat 32
+         %14 = OpTypeInt 32 0
+         %15 = OpConstant %14 3
+         %16 = OpTypeArray %13 %15
+         %17 = OpTypeBool
+         %18 = OpTypeStruct %16 %7 %17
+         %19 = OpTypePointer Function %18
+         %21 = OpConstant %13 1
+         %22 = OpConstant %13 2
+         %23 = OpConstant %13 4
+         %24 = OpConstantComposite %16 %21 %22 %23
+         %25 = OpConstant %6 5
+         %26 = OpConstant %6 6
+         %27 = OpConstantComposite %7 %25 %26
+         %28 = OpConstantFalse %17
+         %29 = OpConstantComposite %18 %24 %27 %28
+         %30 = OpTypeVector %13 2
+         %31 = OpTypePointer Function %30
+         %33 = OpConstantComposite %30 %21 %21
+         %34 = OpTypeVector %17 3
+         %35 = OpTypePointer Function %34
+         %37 = OpConstantTrue %17
+         %38 = OpConstantComposite %34 %37 %28 %28
+         %39 = OpTypeVector %13 4
+         %40 = OpTypeMatrix %39 3
+         %41 = OpTypePointer Function %40
+         %43 = OpConstantComposite %39 %21 %22 %23 %21
+         %44 = OpConstantComposite %39 %22 %23 %21 %22
+         %45 = OpConstantComposite %39 %23 %21 %22 %23
+         %46 = OpConstantComposite %40 %43 %44 %45
+         %50 = OpTypePointer Function %14
+         %51 = OpConstantNull %14
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // A few cases of inapplicable transformations:
+  // Id 4 is already in use
+  ASSERT_FALSE(TransformationAddLocalVariable(4, 50, 4, 51, true)
+                   .IsApplicable(context.get(), fact_manager));
+  // Type mismatch between initializer and pointer
+  ASSERT_FALSE(TransformationAddLocalVariable(105, 46, 4, 51, true)
+                   .IsApplicable(context.get(), fact_manager));
+  // Id 5 is not a function
+  ASSERT_FALSE(TransformationAddLocalVariable(105, 50, 5, 51, true)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // %105 = OpVariable %50 Function %51
+  {
+    TransformationAddLocalVariable transformation(105, 50, 4, 51, true);
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+  }
+
+  // %104 = OpVariable %41 Function %46
+  {
+    TransformationAddLocalVariable transformation(104, 41, 4, 46, false);
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+  }
+
+  // %103 = OpVariable %35 Function %38
+  {
+    TransformationAddLocalVariable transformation(103, 35, 4, 38, true);
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+  }
+
+  // %102 = OpVariable %31 Function %33
+  {
+    TransformationAddLocalVariable transformation(102, 31, 4, 33, false);
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+  }
+
+  // %101 = OpVariable %19 Function %29
+  {
+    TransformationAddLocalVariable transformation(101, 19, 4, 29, true);
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+  }
+
+  // %100 = OpVariable %8 Function %12
+  {
+    TransformationAddLocalVariable transformation(100, 8, 4, 12, false);
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+  }
+
+  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(100));
+  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(101));
+  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(102));
+  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(103));
+  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(104));
+  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(105));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeStruct %6 %6
+          %8 = OpTypePointer Function %7
+         %10 = OpConstant %6 1
+         %11 = OpConstant %6 2
+         %12 = OpConstantComposite %7 %10 %11
+         %13 = OpTypeFloat 32
+         %14 = OpTypeInt 32 0
+         %15 = OpConstant %14 3
+         %16 = OpTypeArray %13 %15
+         %17 = OpTypeBool
+         %18 = OpTypeStruct %16 %7 %17
+         %19 = OpTypePointer Function %18
+         %21 = OpConstant %13 1
+         %22 = OpConstant %13 2
+         %23 = OpConstant %13 4
+         %24 = OpConstantComposite %16 %21 %22 %23
+         %25 = OpConstant %6 5
+         %26 = OpConstant %6 6
+         %27 = OpConstantComposite %7 %25 %26
+         %28 = OpConstantFalse %17
+         %29 = OpConstantComposite %18 %24 %27 %28
+         %30 = OpTypeVector %13 2
+         %31 = OpTypePointer Function %30
+         %33 = OpConstantComposite %30 %21 %21
+         %34 = OpTypeVector %17 3
+         %35 = OpTypePointer Function %34
+         %37 = OpConstantTrue %17
+         %38 = OpConstantComposite %34 %37 %28 %28
+         %39 = OpTypeVector %13 4
+         %40 = OpTypeMatrix %39 3
+         %41 = OpTypePointer Function %40
+         %43 = OpConstantComposite %39 %21 %22 %23 %21
+         %44 = OpConstantComposite %39 %22 %23 %21 %22
+         %45 = OpConstantComposite %39 %23 %21 %22 %23
+         %46 = OpConstantComposite %40 %43 %44 %45
+         %50 = OpTypePointer Function %14
+         %51 = OpConstantNull %14
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %100 = OpVariable %8 Function %12
+        %101 = OpVariable %19 Function %29
+        %102 = OpVariable %31 Function %33
+        %103 = OpVariable %35 Function %38
+        %104 = OpVariable %41 Function %46
+        %105 = OpVariable %50 Function %51
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_copy_object_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_copy_object_test.cpp
index b489f71..b85f75b 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_copy_object_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_copy_object_test.cpp
@@ -588,6 +588,96 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationCopyObjectTest, DoNotCopyNullOrUndefPointers) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpConstantNull %7
+          %9 = OpUndef %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Illegal to copy null.
+  ASSERT_FALSE(TransformationCopyObject(
+                   8, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Illegal to copy an OpUndef of pointer type.
+  ASSERT_FALSE(TransformationCopyObject(
+                   9, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100)
+                   .IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationCopyObjectTest, PropagateIrrelevantPointeeFact) {
+  // Checks that if a pointer is known to have an irrelevant value, the same
+  // holds after the pointer is copied.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+          %9 = OpVariable %7 Function
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(8);
+
+  TransformationCopyObject transformation1(
+      8, MakeInstructionDescriptor(9, SpvOpReturn, 0), 100);
+  TransformationCopyObject transformation2(
+      9, MakeInstructionDescriptor(9, SpvOpReturn, 0), 101);
+  TransformationCopyObject transformation3(
+      100, MakeInstructionDescriptor(9, SpvOpReturn, 0), 102);
+
+  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
+  transformation3.Apply(context.get(), &fact_manager);
+
+  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(8));
+  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(100));
+  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(102));
+  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(9));
+  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_function_call_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_function_call_test.cpp
new file mode 100644
index 0000000..9bd971e
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_function_call_test.cpp
@@ -0,0 +1,443 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_function_call.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationFunctionCallTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %6 %7
+         %12 = OpTypeFloat 32
+         %13 = OpTypePointer Function %12
+         %14 = OpTypeFunction %6 %7 %13
+         %27 = OpConstant %6 1
+         %50 = OpConstant %12 1
+         %57 = OpTypeBool
+         %58 = OpConstantFalse %57
+        %204 = OpUndef %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %61 = OpVariable %7 Function
+         %62 = OpVariable %7 Function
+         %65 = OpVariable %13 Function
+         %66 = OpVariable %7 Function
+         %68 = OpVariable %13 Function
+         %71 = OpVariable %7 Function
+         %72 = OpVariable %13 Function
+         %73 = OpVariable %7 Function
+         %75 = OpVariable %13 Function
+         %78 = OpVariable %7 Function
+         %98 = OpAccessChain %7 %71
+         %99 = OpCopyObject %7 %71
+               OpSelectionMerge %60 None
+               OpBranchConditional %58 %59 %60
+         %59 = OpLabel
+               OpBranch %60
+         %60 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %6 None %8
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+         %26 = OpLoad %6 %9
+         %28 = OpIAdd %6 %26 %27
+               OpSelectionMerge %97 None
+               OpBranchConditional %58 %96 %97
+         %96 = OpLabel
+               OpBranch %97
+         %97 = OpLabel
+               OpReturnValue %28
+               OpFunctionEnd
+         %17 = OpFunction %6 None %14
+         %15 = OpFunctionParameter %7
+         %16 = OpFunctionParameter %13
+         %18 = OpLabel
+         %31 = OpVariable %7 Function
+         %32 = OpLoad %6 %15
+               OpStore %31 %32
+         %33 = OpFunctionCall %6 %10 %31
+               OpReturnValue %33
+               OpFunctionEnd
+         %21 = OpFunction %6 None %14
+         %19 = OpFunctionParameter %7
+         %20 = OpFunctionParameter %13
+         %22 = OpLabel
+         %36 = OpLoad %6 %19
+         %37 = OpLoad %12 %20
+         %38 = OpConvertFToS %6 %37
+         %39 = OpIAdd %6 %36 %38
+               OpReturnValue %39
+               OpFunctionEnd
+         %24 = OpFunction %6 None %8
+         %23 = OpFunctionParameter %7
+         %25 = OpLabel
+         %44 = OpVariable %7 Function
+         %46 = OpVariable %13 Function
+         %51 = OpVariable %7 Function
+         %52 = OpVariable %13 Function
+         %42 = OpLoad %6 %23
+         %43 = OpConvertSToF %12 %42
+         %45 = OpLoad %6 %23
+               OpStore %44 %45
+               OpStore %46 %43
+         %47 = OpFunctionCall %6 %17 %44 %46
+         %48 = OpLoad %6 %23
+         %49 = OpIAdd %6 %48 %27
+               OpStore %51 %49
+               OpStore %52 %50
+         %53 = OpFunctionCall %6 %17 %51 %52
+         %54 = OpIAdd %6 %47 %53
+               OpReturnValue %54
+               OpFunctionEnd
+        %200 = OpFunction %6 None %14
+        %201 = OpFunctionParameter %7
+        %202 = OpFunctionParameter %13
+        %203 = OpLabel
+               OpSelectionMerge %206 None
+               OpBranchConditional %58 %205 %206
+        %205 = OpLabel
+               OpBranch %206
+        %206 = OpLabel
+               OpReturnValue %204
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  fact_manager.AddFactBlockIsDead(59);
+  fact_manager.AddFactBlockIsDead(11);
+  fact_manager.AddFactBlockIsDead(18);
+  fact_manager.AddFactBlockIsDead(25);
+  fact_manager.AddFactBlockIsDead(96);
+  fact_manager.AddFactBlockIsDead(205);
+  fact_manager.AddFactFunctionIsLivesafe(21);
+  fact_manager.AddFactFunctionIsLivesafe(200);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(71);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(72);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(19);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(20);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(23);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(44);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(46);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(51);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(52);
+
+  // Livesafe functions with argument types: 21(7, 13), 200(7, 13)
+  // Non-livesafe functions with argument types: 4(), 10(7), 17(7, 13), 24(7)
+  // Call graph edges:
+  //    17 -> 10
+  //    24 -> 17
+
+  // Bad transformations
+  // Too many arguments
+  ASSERT_FALSE(
+      TransformationFunctionCall(100, 21, {71, 72, 71},
+                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
+          .IsApplicable(context.get(), fact_manager));
+  // Too few arguments
+  ASSERT_FALSE(TransformationFunctionCall(
+                   100, 21, {71}, MakeInstructionDescriptor(59, SpvOpBranch, 0))
+                   .IsApplicable(context.get(), fact_manager));
+  // Arguments are the wrong way around (types do not match)
+  ASSERT_FALSE(
+      TransformationFunctionCall(100, 21, {72, 71},
+                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
+          .IsApplicable(context.get(), fact_manager));
+  // 21 is not an appropriate argument
+  ASSERT_FALSE(
+      TransformationFunctionCall(100, 21, {21, 72},
+                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
+          .IsApplicable(context.get(), fact_manager));
+  // 300 does not exist
+  ASSERT_FALSE(
+      TransformationFunctionCall(100, 21, {300, 72},
+                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
+          .IsApplicable(context.get(), fact_manager));
+  // 71 is not a function
+  ASSERT_FALSE(
+      TransformationFunctionCall(100, 71, {71, 72},
+                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
+          .IsApplicable(context.get(), fact_manager));
+  // 500 does not exist
+  ASSERT_FALSE(
+      TransformationFunctionCall(100, 500, {71, 72},
+                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
+          .IsApplicable(context.get(), fact_manager));
+  // Id is not fresh
+  ASSERT_FALSE(
+      TransformationFunctionCall(21, 21, {71, 72},
+                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
+          .IsApplicable(context.get(), fact_manager));
+  // Access chain as pointer parameter
+  ASSERT_FALSE(
+      TransformationFunctionCall(100, 21, {98, 72},
+                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
+          .IsApplicable(context.get(), fact_manager));
+  // Copied object as pointer parameter
+  ASSERT_FALSE(
+      TransformationFunctionCall(100, 21, {99, 72},
+                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
+          .IsApplicable(context.get(), fact_manager));
+  // Non-livesafe called from original live block
+  ASSERT_FALSE(
+      TransformationFunctionCall(
+          100, 10, {71}, MakeInstructionDescriptor(99, SpvOpSelectionMerge, 0))
+          .IsApplicable(context.get(), fact_manager));
+  // Non-livesafe called from livesafe function
+  ASSERT_FALSE(
+      TransformationFunctionCall(
+          100, 10, {19}, MakeInstructionDescriptor(38, SpvOpConvertFToS, 0))
+          .IsApplicable(context.get(), fact_manager));
+  // Livesafe function called with pointer to non-arbitrary local variable
+  ASSERT_FALSE(
+      TransformationFunctionCall(
+          100, 21, {61, 72}, MakeInstructionDescriptor(38, SpvOpConvertFToS, 0))
+          .IsApplicable(context.get(), fact_manager));
+  // Direct recursion
+  ASSERT_FALSE(TransformationFunctionCall(
+                   100, 4, {}, MakeInstructionDescriptor(59, SpvOpBranch, 0))
+                   .IsApplicable(context.get(), fact_manager));
+  // Indirect recursion
+  ASSERT_FALSE(TransformationFunctionCall(
+                   100, 24, {9}, MakeInstructionDescriptor(96, SpvOpBranch, 0))
+                   .IsApplicable(context.get(), fact_manager));
+  // Parameter 23 is not available at the call site
+  ASSERT_FALSE(
+      TransformationFunctionCall(104, 10, {23},
+                                 MakeInstructionDescriptor(205, SpvOpBranch, 0))
+          .IsApplicable(context.get(), fact_manager));
+
+  // Good transformations
+  {
+    // Livesafe called from dead block: fine
+    TransformationFunctionCall transformation(
+        100, 21, {71, 72}, MakeInstructionDescriptor(59, SpvOpBranch, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+  {
+    // Livesafe called from original live block: fine
+    TransformationFunctionCall transformation(
+        101, 21, {71, 72}, MakeInstructionDescriptor(98, SpvOpAccessChain, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+  {
+    // Livesafe called from livesafe function: fine
+    TransformationFunctionCall transformation(
+        102, 200, {19, 20}, MakeInstructionDescriptor(36, SpvOpLoad, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+  {
+    // Dead called from dead block in injected function: fine
+    TransformationFunctionCall transformation(
+        103, 10, {23}, MakeInstructionDescriptor(45, SpvOpLoad, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+  {
+    // Non-livesafe called from dead block in livesafe function: OK
+    TransformationFunctionCall transformation(
+        104, 10, {201}, MakeInstructionDescriptor(205, SpvOpBranch, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+  {
+    // Livesafe called from dead block with non-arbitrary parameter
+    TransformationFunctionCall transformation(
+        105, 21, {62, 65}, MakeInstructionDescriptor(59, SpvOpBranch, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %6 %7
+         %12 = OpTypeFloat 32
+         %13 = OpTypePointer Function %12
+         %14 = OpTypeFunction %6 %7 %13
+         %27 = OpConstant %6 1
+         %50 = OpConstant %12 1
+         %57 = OpTypeBool
+         %58 = OpConstantFalse %57
+        %204 = OpUndef %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %61 = OpVariable %7 Function
+         %62 = OpVariable %7 Function
+         %65 = OpVariable %13 Function
+         %66 = OpVariable %7 Function
+         %68 = OpVariable %13 Function
+         %71 = OpVariable %7 Function
+         %72 = OpVariable %13 Function
+         %73 = OpVariable %7 Function
+         %75 = OpVariable %13 Function
+         %78 = OpVariable %7 Function
+        %101 = OpFunctionCall %6 %21 %71 %72
+         %98 = OpAccessChain %7 %71
+         %99 = OpCopyObject %7 %71
+               OpSelectionMerge %60 None
+               OpBranchConditional %58 %59 %60
+         %59 = OpLabel
+        %100 = OpFunctionCall %6 %21 %71 %72
+        %105 = OpFunctionCall %6 %21 %62 %65
+               OpBranch %60
+         %60 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %6 None %8
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+         %26 = OpLoad %6 %9
+         %28 = OpIAdd %6 %26 %27
+               OpSelectionMerge %97 None
+               OpBranchConditional %58 %96 %97
+         %96 = OpLabel
+               OpBranch %97
+         %97 = OpLabel
+               OpReturnValue %28
+               OpFunctionEnd
+         %17 = OpFunction %6 None %14
+         %15 = OpFunctionParameter %7
+         %16 = OpFunctionParameter %13
+         %18 = OpLabel
+         %31 = OpVariable %7 Function
+         %32 = OpLoad %6 %15
+               OpStore %31 %32
+         %33 = OpFunctionCall %6 %10 %31
+               OpReturnValue %33
+               OpFunctionEnd
+         %21 = OpFunction %6 None %14
+         %19 = OpFunctionParameter %7
+         %20 = OpFunctionParameter %13
+         %22 = OpLabel
+        %102 = OpFunctionCall %6 %200 %19 %20
+         %36 = OpLoad %6 %19
+         %37 = OpLoad %12 %20
+         %38 = OpConvertFToS %6 %37
+         %39 = OpIAdd %6 %36 %38
+               OpReturnValue %39
+               OpFunctionEnd
+         %24 = OpFunction %6 None %8
+         %23 = OpFunctionParameter %7
+         %25 = OpLabel
+         %44 = OpVariable %7 Function
+         %46 = OpVariable %13 Function
+         %51 = OpVariable %7 Function
+         %52 = OpVariable %13 Function
+         %42 = OpLoad %6 %23
+         %43 = OpConvertSToF %12 %42
+        %103 = OpFunctionCall %6 %10 %23
+         %45 = OpLoad %6 %23
+               OpStore %44 %45
+               OpStore %46 %43
+         %47 = OpFunctionCall %6 %17 %44 %46
+         %48 = OpLoad %6 %23
+         %49 = OpIAdd %6 %48 %27
+               OpStore %51 %49
+               OpStore %52 %50
+         %53 = OpFunctionCall %6 %17 %51 %52
+         %54 = OpIAdd %6 %47 %53
+               OpReturnValue %54
+               OpFunctionEnd
+        %200 = OpFunction %6 None %14
+        %201 = OpFunctionParameter %7
+        %202 = OpFunctionParameter %13
+        %203 = OpLabel
+               OpSelectionMerge %206 None
+               OpBranchConditional %58 %205 %206
+        %205 = OpLabel
+        %104 = OpFunctionCall %6 %10 %201
+               OpBranch %206
+        %206 = OpLabel
+               OpReturnValue %204
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationFunctionCallTest, DoNotInvokeEntryPoint) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %2 None %3
+         %11 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  fact_manager.AddFactBlockIsDead(11);
+
+  // 4 is an entry point, so it is not legal for it to be the target of a call.
+  ASSERT_FALSE(TransformationFunctionCall(
+                   100, 4, {}, MakeInstructionDescriptor(11, SpvOpReturn, 0))
+                   .IsApplicable(context.get(), fact_manager));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_load_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_load_test.cpp
new file mode 100644
index 0000000..1f728ff
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_load_test.cpp
@@ -0,0 +1,277 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_load.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationLoadTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 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
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %20 = OpVariable %9 Function
+         %27 = OpVariable %9 Function ; irrelevant
+         %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
+         %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
+               OpBranch %37
+         %37 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9 ; irrelevant
+         %13 = OpLabel
+         %46 = OpCopyObject %9 %11 ; irrelevant
+         %16 = OpAccessChain %15 %11 %14 ; irrelevant
+               OpReturnValue %21
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(27);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(11);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(46);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(16);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(52);
+
+  fact_manager.AddFactBlockIsDead(36);
+
+  // Variables with pointee types:
+  //  52 - ptr_to(7)
+  //  53 - ptr_to(6)
+  //  20 - ptr_to(8)
+  //  27 - ptr_to(8) - irrelevant
+
+  // Access chains with pointee type:
+  //  22 - ptr_to(6)
+  //  26 - ptr_to(6)
+  //  30 - ptr_to(6)
+  //  33 - ptr_to(6)
+  //  38 - ptr_to(6)
+  //  40 - ptr_to(6)
+  //  43 - ptr_to(6)
+  //  16 - ptr_to(6) - irrelevant
+
+  // Copied object with pointee type:
+  //  44 - ptr_to(8)
+  //  45 - ptr_to(6)
+  //  46 - ptr_to(8) - irrelevant
+
+  // Function parameters with pointee type:
+  //  11 - ptr_to(8) - irrelevant
+
+  // Pointers that cannot be used:
+  //  60 - null
+  //  61 - undefined
+
+  // Bad: id is not fresh
+  ASSERT_FALSE(TransformationLoad(
+                   33, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+  // Bad: attempt to load from 11 from outside its function
+  ASSERT_FALSE(TransformationLoad(
+                   100, 11, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: pointer is not available
+  ASSERT_FALSE(TransformationLoad(
+                   100, 33, MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: attempt to insert before OpVariable
+  ASSERT_FALSE(TransformationLoad(
+                   100, 27, MakeInstructionDescriptor(27, SpvOpVariable, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: pointer id does not exist
+  ASSERT_FALSE(
+      TransformationLoad(100, 1000,
+                         MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+          .IsApplicable(context.get(), fact_manager));
+
+  // Bad: pointer id exists but does not have a type
+  ASSERT_FALSE(TransformationLoad(
+                   100, 5, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: pointer id exists and has a type, but is not a pointer
+  ASSERT_FALSE(TransformationLoad(
+                   100, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: attempt to load from null pointer
+  ASSERT_FALSE(TransformationLoad(
+                   100, 60, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: attempt to load from undefined pointer
+  ASSERT_FALSE(TransformationLoad(
+                   100, 61, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+  // Bad: %40 is not available at the program point
+  ASSERT_FALSE(
+      TransformationLoad(100, 40, MakeInstructionDescriptor(37, SpvOpReturn, 0))
+          .IsApplicable(context.get(), fact_manager));
+
+  // Bad: The described instruction does not exist
+  ASSERT_FALSE(TransformationLoad(
+                   100, 33, MakeInstructionDescriptor(1000, SpvOpReturn, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  {
+    TransformationLoad transformation(
+        100, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  {
+    TransformationLoad transformation(
+        101, 46, MakeInstructionDescriptor(16, SpvOpReturnValue, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  {
+    TransformationLoad transformation(
+        102, 16, MakeInstructionDescriptor(16, SpvOpReturnValue, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  {
+    TransformationLoad transformation(
+        103, 40, MakeInstructionDescriptor(43, SpvOpAccessChain, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %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
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %20 = OpVariable %9 Function
+         %27 = OpVariable %9 Function ; irrelevant
+         %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
+         %33 = OpAccessChain %15 %20 %14
+               OpSelectionMerge %37 None
+               OpBranchConditional %35 %36 %37
+         %36 = OpLabel
+        %100 = OpLoad %6 %33
+         %38 = OpAccessChain %15 %20 %14
+         %40 = OpAccessChain %15 %20 %14
+        %103 = OpLoad %6 %40
+         %43 = OpAccessChain %15 %20 %14
+               OpBranch %37
+         %37 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9 ; irrelevant
+         %13 = OpLabel
+         %46 = OpCopyObject %9 %11 ; irrelevant
+         %16 = OpAccessChain %15 %11 %14 ; irrelevant
+        %101 = OpLoad %8 %46
+        %102 = OpLoad %6 %16
+               OpReturnValue %21
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_outline_function_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_outline_function_test.cpp
index 4f828b6..40aaebc 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_outline_function_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_outline_function_test.cpp
@@ -1656,6 +1656,665 @@
   ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
 }
 
+TEST(TransformationOutlineFunctionTest,
+     DoNotOutlineRegionThatUsesCopiedObject) {
+  // Copying a variable leads to a pointer, but one that cannot be passed as a
+  // function parameter, as it is not a memory object.
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypePointer Function %7
+          %9 = OpTypePointer Function %6
+         %18 = OpTypeInt 32 0
+         %19 = OpConstant %18 0
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %10 = OpVariable %8 Function
+               OpBranch %11
+         %11 = OpLabel
+         %20 = OpCopyObject %8 %10
+               OpBranch %13
+         %13 = OpLabel
+         %12 = OpAccessChain %9 %20 %19
+         %14 = OpLoad %6 %12
+               OpBranch %15
+         %15 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 13,
+      /*exit_block*/ 15,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 204,
+      /*new_caller_result_id*/ 205,
+      /*new_callee_result_id*/ 206,
+      /*input_id_to_fresh_id*/ {{20, 207}},
+      /*output_id_to_fresh_id*/ {});
+
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest,
+     DoOutlineRegionThatUsesPointerParameter) {
+  // The region being outlined reads from a function parameter of pointer type.
+  // This is OK: the function parameter can itself be passed on as a function
+  // parameter.
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %7
+         %13 = OpConstant %6 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %15 = OpVariable %7 Function
+         %16 = OpVariable %7 Function
+         %17 = OpLoad %6 %15
+               OpStore %16 %17
+         %18 = OpFunctionCall %2 %10 %16
+         %19 = OpLoad %6 %16
+               OpStore %15 %19
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %2 None %8
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+         %12 = OpLoad %6 %9
+         %14 = OpIAdd %6 %12 %13
+               OpBranch %20
+         %20 = OpLabel
+               OpStore %9 %14
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 11,
+      /*exit_block*/ 11,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 204,
+      /*new_caller_result_id*/ 205,
+      /*new_callee_result_id*/ 206,
+      /*input_id_to_fresh_id*/ {{9, 207}},
+      /*output_id_to_fresh_id*/ {{14, 208}});
+
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %7
+         %13 = OpConstant %6 2
+        %200 = OpTypeStruct %6
+        %201 = OpTypeFunction %200 %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %15 = OpVariable %7 Function
+         %16 = OpVariable %7 Function
+         %17 = OpLoad %6 %15
+               OpStore %16 %17
+         %18 = OpFunctionCall %2 %10 %16
+         %19 = OpLoad %6 %16
+               OpStore %15 %19
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %2 None %8
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+        %205 = OpFunctionCall %200 %202 %9
+         %14 = OpCompositeExtract %6 %205 0
+               OpBranch %20
+         %20 = OpLabel
+               OpStore %9 %14
+               OpReturn
+               OpFunctionEnd
+        %202 = OpFunction %200 None %201
+        %207 = OpFunctionParameter %7
+        %204 = OpLabel
+         %12 = OpLoad %6 %207
+        %208 = OpIAdd %6 %12 %13
+        %206 = OpCompositeConstruct %200 %208
+               OpReturnValue %206
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineLivesafe) {
+  // In the following, %30 is a livesafe function, with irrelevant parameter
+  // %200 and irrelevant local variable %201.  Variable %100 is a loop limiter,
+  // which is not irrelevant.  The test checks that the outlined function is
+  // livesafe, and that the parameters corresponding to %200 and %201 have the
+  // irrelevant fact associated with them.
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 0
+          %7 = OpTypePointer Function %6
+        %199 = OpTypeFunction %2 %7
+          %8 = OpConstant %6 0
+          %9 = OpConstant %6 1
+         %10 = OpConstant %6 5
+         %11 = OpTypeBool
+         %12 = OpConstantTrue %11
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %30 = OpFunction %2 None %199
+        %200 = OpFunctionParameter %7
+         %31 = OpLabel
+        %100 = OpVariable %7 Function %8
+        %201 = OpVariable %7 Function %8
+               OpBranch %198
+        %198 = OpLabel
+               OpBranch %20
+         %20 = OpLabel
+        %101 = OpLoad %6 %100
+        %102 = OpIAdd %6 %101 %9
+        %202 = OpLoad %6 %200
+               OpStore %201 %202
+               OpStore %100 %102
+        %103 = OpUGreaterThanEqual %11 %101 %10
+               OpLoopMerge %21 %22 None
+               OpBranchConditional %103 %21 %104
+        %104 = OpLabel
+               OpBranchConditional %12 %23 %21
+         %23 = OpLabel
+        %105 = OpLoad %6 %100
+        %106 = OpIAdd %6 %105 %9
+               OpStore %100 %106
+        %107 = OpUGreaterThanEqual %11 %105 %10
+               OpLoopMerge %25 %26 None
+               OpBranchConditional %107 %25 %108
+        %108 = OpLabel
+               OpBranch %28
+         %28 = OpLabel
+               OpBranchConditional %12 %26 %25
+         %26 = OpLabel
+               OpBranch %23
+         %25 = OpLabel
+        %109 = OpLoad %6 %100
+        %110 = OpIAdd %6 %109 %9
+               OpStore %100 %110
+        %111 = OpUGreaterThanEqual %11 %109 %10
+               OpLoopMerge %24 %27 None
+               OpBranchConditional %111 %24 %112
+        %112 = OpLabel
+               OpBranchConditional %12 %24 %27
+         %27 = OpLabel
+               OpBranch %25
+         %24 = OpLabel
+               OpBranch %22
+         %22 = OpLabel
+               OpBranch %20
+         %21 = OpLabel
+               OpBranch %197
+        %197 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  fact_manager.AddFactFunctionIsLivesafe(30);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(200);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(201);
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 198,
+      /*exit_block*/ 197,
+      /*new_function_struct_return_type_id*/ 400,
+      /*new_function_type_id*/ 401,
+      /*new_function_id*/ 402,
+      /*new_function_region_entry_block*/ 404,
+      /*new_caller_result_id*/ 405,
+      /*new_callee_result_id*/ 406,
+      /*input_id_to_fresh_id*/ {{100, 407}, {200, 408}, {201, 409}},
+      /*output_id_to_fresh_id*/ {});
+
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // The original function should still be livesafe.
+  ASSERT_TRUE(fact_manager.FunctionIsLivesafe(30));
+  // The outlined function should be livesafe.
+  ASSERT_TRUE(fact_manager.FunctionIsLivesafe(402));
+  // The variable and parameter that were originally irrelevant should still be.
+  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(200));
+  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(201));
+  // The loop limiter should still be non-irrelevant.
+  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(100));
+  // The parameters for the original irrelevant variables should be irrelevant.
+  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(408));
+  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(409));
+  // The parameter for the loop limiter should not be irrelevant.
+  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(407));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 0
+          %7 = OpTypePointer Function %6
+        %199 = OpTypeFunction %2 %7
+          %8 = OpConstant %6 0
+          %9 = OpConstant %6 1
+         %10 = OpConstant %6 5
+         %11 = OpTypeBool
+         %12 = OpConstantTrue %11
+        %401 = OpTypeFunction %2 %7 %7 %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %30 = OpFunction %2 None %199
+        %200 = OpFunctionParameter %7
+         %31 = OpLabel
+        %100 = OpVariable %7 Function %8
+        %201 = OpVariable %7 Function %8
+               OpBranch %198
+        %198 = OpLabel
+        %405 = OpFunctionCall %2 %402 %200 %100 %201
+               OpReturn
+               OpFunctionEnd
+        %402 = OpFunction %2 None %401
+        %408 = OpFunctionParameter %7
+        %407 = OpFunctionParameter %7
+        %409 = OpFunctionParameter %7
+        %404 = OpLabel
+               OpBranch %20
+         %20 = OpLabel
+        %101 = OpLoad %6 %407
+        %102 = OpIAdd %6 %101 %9
+        %202 = OpLoad %6 %408
+               OpStore %409 %202
+               OpStore %407 %102
+        %103 = OpUGreaterThanEqual %11 %101 %10
+               OpLoopMerge %21 %22 None
+               OpBranchConditional %103 %21 %104
+        %104 = OpLabel
+               OpBranchConditional %12 %23 %21
+         %23 = OpLabel
+        %105 = OpLoad %6 %407
+        %106 = OpIAdd %6 %105 %9
+               OpStore %407 %106
+        %107 = OpUGreaterThanEqual %11 %105 %10
+               OpLoopMerge %25 %26 None
+               OpBranchConditional %107 %25 %108
+        %108 = OpLabel
+               OpBranch %28
+         %28 = OpLabel
+               OpBranchConditional %12 %26 %25
+         %26 = OpLabel
+               OpBranch %23
+         %25 = OpLabel
+        %109 = OpLoad %6 %407
+        %110 = OpIAdd %6 %109 %9
+               OpStore %407 %110
+        %111 = OpUGreaterThanEqual %11 %109 %10
+               OpLoopMerge %24 %27 None
+               OpBranchConditional %111 %24 %112
+        %112 = OpLabel
+               OpBranchConditional %12 %24 %27
+         %27 = OpLabel
+               OpBranch %25
+         %24 = OpLabel
+               OpBranch %22
+         %22 = OpLabel
+               OpBranch %20
+         %21 = OpLabel
+               OpBranch %197
+        %197 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineWithDeadBlocks1) {
+  // This checks that if all blocks in the region being outlined were dead, all
+  // blocks in the outlined function will be dead.
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %10 "foo(i1;"
+               OpName %9 "x"
+               OpName %12 "y"
+               OpName %21 "i"
+               OpName %46 "param"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %7
+         %13 = OpConstant %6 2
+         %14 = OpTypeBool
+         %15 = OpConstantFalse %14
+         %22 = OpConstant %6 0
+         %29 = OpConstant %6 10
+         %41 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %46 = OpVariable %7 Function
+               OpStore %46 %13
+         %47 = OpFunctionCall %2 %10 %46
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %2 None %8
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+         %12 = OpVariable %7 Function
+         %21 = OpVariable %7 Function
+               OpStore %12 %13
+               OpSelectionMerge %17 None
+               OpBranchConditional %15 %16 %17
+         %16 = OpLabel
+         %18 = OpLoad %6 %9
+               OpStore %12 %18
+         %19 = OpLoad %6 %9
+         %20 = OpIAdd %6 %19 %13
+               OpStore %9 %20
+               OpStore %21 %22
+               OpBranch %23
+         %23 = OpLabel
+               OpLoopMerge %25 %26 None
+               OpBranch %27
+         %27 = OpLabel
+         %28 = OpLoad %6 %21
+         %30 = OpSLessThan %14 %28 %29
+               OpBranchConditional %30 %24 %25
+         %24 = OpLabel
+         %31 = OpLoad %6 %9
+         %32 = OpLoad %6 %21
+         %33 = OpSGreaterThan %14 %31 %32
+               OpSelectionMerge %35 None
+               OpBranchConditional %33 %34 %35
+         %34 = OpLabel
+               OpBranch %26
+         %35 = OpLabel
+         %37 = OpLoad %6 %9
+         %38 = OpLoad %6 %12
+         %39 = OpIAdd %6 %38 %37
+               OpStore %12 %39
+               OpBranch %26
+         %26 = OpLabel
+         %40 = OpLoad %6 %21
+         %42 = OpIAdd %6 %40 %41
+               OpStore %21 %42
+               OpBranch %23
+         %25 = OpLabel
+               OpBranch %50
+         %50 = OpLabel
+               OpBranch %17
+         %17 = OpLabel
+         %43 = OpLoad %6 %9
+         %44 = OpLoad %6 %12
+         %45 = OpIAdd %6 %44 %43
+               OpStore %12 %45
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  for (uint32_t block_id : {16u, 23u, 24u, 26u, 27u, 34u, 35u, 50u}) {
+    fact_manager.AddFactBlockIsDead(block_id);
+  }
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 16,
+      /*exit_block*/ 50,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 203,
+      /*new_caller_result_id*/ 204,
+      /*new_callee_result_id*/ 205,
+      /*input_id_to_fresh_id*/ {{9, 206}, {12, 207}, {21, 208}},
+      /*output_id_to_fresh_id*/ {});
+
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  // All the original blocks, plus the new function entry block, should be dead.
+  for (uint32_t block_id : {16u, 23u, 24u, 26u, 27u, 34u, 35u, 50u, 203u}) {
+    ASSERT_TRUE(fact_manager.BlockIsDead(block_id));
+  }
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineWithDeadBlocks2) {
+  // This checks that if some, but not all, blocks in the outlined region are
+  // dead, those (but not others) will be dead in the outlined function.
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %8
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypePointer Private %6
+          %8 = OpVariable %7 Private
+          %9 = OpConstantFalse %6
+         %10 = OpTypePointer Function %6
+         %12 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+               OpBranch %30
+         %30 = OpLabel
+               OpStore %8 %9
+               OpBranch %31
+         %31 = OpLabel
+               OpStore %11 %12
+               OpSelectionMerge %36 None
+               OpBranchConditional %9 %32 %33
+         %32 = OpLabel
+               OpBranch %34
+         %33 = OpLabel
+               OpBranch %36
+         %34 = OpLabel
+               OpBranch %35
+         %35 = OpLabel
+               OpBranch %36
+         %36 = OpLabel
+               OpBranch %37
+         %37 = OpLabel
+         %13 = OpLoad %6 %8
+               OpStore %11 %13
+         %14 = OpLoad %6 %11
+               OpStore %8 %14
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  for (uint32_t block_id : {32u, 34u, 35u}) {
+    fact_manager.AddFactBlockIsDead(block_id);
+  }
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 30,
+      /*exit_block*/ 37,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 203,
+      /*new_caller_result_id*/ 204,
+      /*new_callee_result_id*/ 205,
+      /*input_id_to_fresh_id*/ {{11, 206}},
+      /*output_id_to_fresh_id*/ {});
+
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  // The blocks that were originally dead, but not others, should be dead.
+  for (uint32_t block_id : {32u, 34u, 35u}) {
+    ASSERT_TRUE(fact_manager.BlockIsDead(block_id));
+  }
+  for (uint32_t block_id : {5u, 30u, 31u, 33u, 36u, 37u, 203u}) {
+    ASSERT_FALSE(fact_manager.BlockIsDead(block_id));
+  }
+}
+
+TEST(TransformationOutlineFunctionTest,
+     OutlineWithIrrelevantVariablesAndParameters) {
+  // This checks that if the outlined region uses a mixture of irrelevant and
+  // non-irrelevant variables and parameters, these properties are preserved
+  // during outlining.
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %7 %7
+         %13 = OpConstant %6 2
+         %15 = OpConstant %6 3
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %11 = OpFunction %2 None %8
+          %9 = OpFunctionParameter %7
+         %10 = OpFunctionParameter %7
+         %12 = OpLabel
+         %14 = OpVariable %7 Function
+         %20 = OpVariable %7 Function
+               OpBranch %50
+         %50 = OpLabel
+               OpStore %9 %13
+               OpStore %14 %15
+         %16 = OpLoad %6 %14
+               OpStore %10 %16
+         %17 = OpLoad %6 %9
+         %18 = OpLoad %6 %10
+         %19 = OpIAdd %6 %17 %18
+               OpStore %14 %19
+         %21 = OpLoad %6 %9
+               OpStore %20 %21
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(9);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(14);
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 50,
+      /*exit_block*/ 50,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 203,
+      /*new_caller_result_id*/ 204,
+      /*new_callee_result_id*/ 205,
+      /*input_id_to_fresh_id*/ {{9, 206}, {10, 207}, {14, 208}, {20, 209}},
+      /*output_id_to_fresh_id*/ {});
+
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  // The variables that were originally irrelevant, plus input parameters
+  // corresponding to them, should be irrelevant.  The rest should not be.
+  for (uint32_t variable_id : {9u, 14u, 206u, 208u}) {
+    ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(variable_id));
+  }
+  for (uint32_t variable_id : {10u, 20u, 207u, 209u}) {
+    ASSERT_FALSE(fact_manager.BlockIsDead(variable_id));
+  }
+}
+
 TEST(TransformationOutlineFunctionTest, Miscellaneous1) {
   // This tests outlining of some non-trivial code.
 
@@ -2040,6 +2699,91 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationOutlineFunctionTest, Miscellaneous4) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %6 "main"
+               OpExecutionMode %6 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %21 = OpTypeBool
+        %100 = OpTypeInt 32 0
+        %101 = OpTypePointer Function %100
+        %102 = OpTypePointer Function %100
+        %103 = OpTypeFunction %2 %101
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+        %104 = OpVariable %102 Function
+               OpBranch %80
+         %80 = OpLabel
+        %105 = OpLoad %100 %104
+               OpBranch %106
+        %106 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 80,
+      /*exit_block*/ 106,
+      /*new_function_struct_return_type_id*/ 300,
+      /*new_function_type_id*/ 301,
+      /*new_function_id*/ 302,
+      /*new_function_region_entry_block*/ 304,
+      /*new_caller_result_id*/ 305,
+      /*new_callee_result_id*/ 306,
+      /*input_id_to_fresh_id*/ {{104, 307}},
+      /*output_id_to_fresh_id*/ {});
+
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %6 "main"
+               OpExecutionMode %6 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %21 = OpTypeBool
+        %100 = OpTypeInt 32 0
+        %101 = OpTypePointer Function %100
+        %102 = OpTypePointer Function %100
+        %103 = OpTypeFunction %2 %101
+        %301 = OpTypeFunction %2 %102
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+        %104 = OpVariable %102 Function
+               OpBranch %80
+         %80 = OpLabel
+        %305 = OpFunctionCall %2 %302 %104
+               OpReturn
+               OpFunctionEnd
+        %302 = OpFunction %2 None %301
+        %307 = OpFunctionParameter %102
+        %304 = OpLabel
+        %105 = OpLoad %100 %307
+               OpBranch %106
+        %106 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp
index bfc7fa7..527a7b7 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp
@@ -650,6 +650,45 @@
   ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
 }
 
+TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
+     DoNotReplaceVariableInitializer) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypePointer Function %6
+          %9 = OpConstantTrue %6
+         %10 = OpTypeInt 32 1
+         %13 = OpConstant %10 0
+         %15 = OpConstant %10 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %50 = OpVariable %7 Function %9
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
+                   MakeIdUseDescriptor(
+                       9, MakeInstructionDescriptor(50, SpvOpVariable, 0), 1),
+                   13, 15, SpvOpSLessThan, 100)
+                   .IsApplicable(context.get(), fact_manager));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
index ac2e3f9..58d4a89 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
@@ -1442,6 +1442,56 @@
   ASSERT_TRUE(IsEqual(env, after, context.get()));
 }
 
+TEST(TransformationReplaceConstantWithUniformTest,
+     DoNotReplaceVariableInitializer) {
+  // If a local variable has a constant initializer, this cannot be replaced
+  // by a uniform.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpMemberDecorate %16 0 Offset 0
+               OpDecorate %16 Block
+               OpDecorate %18 DescriptorSet 0
+               OpDecorate %18 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+         %50 = OpConstant %6 0
+         %16 = OpTypeStruct %6
+         %17 = OpTypePointer Uniform %16
+         %51 = OpTypePointer Uniform %6
+         %18 = OpVariable %17 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function %50
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  protobufs::UniformBufferElementDescriptor blockname_a =
+      MakeUniformBufferElementDescriptor(0, 0, {0});
+
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 0, blockname_a));
+
+  ASSERT_FALSE(TransformationReplaceConstantWithUniform(
+                   MakeIdUseDescriptor(
+                       50, MakeInstructionDescriptor(8, SpvOpVariable, 0), 1),
+                   blockname_a, 100, 101)
+                   .IsApplicable(context.get(), fact_manager));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_split_block_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_split_block_test.cpp
index d162e07..09007a5 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_split_block_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_split_block_test.cpp
@@ -774,6 +774,77 @@
   ASSERT_TRUE(IsEqual(env, after_split, context.get()));
 }
 
+TEST(TransformationSplitBlockTest, DeadBlockShouldSplitToTwoDeadBlocks) {
+  // This checks that if a block B is marked as dead, it should split into a
+  // pair of dead blocks.
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantFalse %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %9 None
+               OpBranchConditional %7 %8 %9
+          %8 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+
+  // Record the fact that block 8 is dead.
+  fact_manager.AddFactBlockIsDead(8);
+
+  auto split = TransformationSplitBlock(
+      MakeInstructionDescriptor(8, SpvOpBranch, 0), 100);
+  ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
+  split.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  ASSERT_TRUE(fact_manager.BlockIsDead(8));
+  ASSERT_TRUE(fact_manager.BlockIsDead(100));
+
+  std::string after_split = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantFalse %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %9 None
+               OpBranchConditional %7 %8 %9
+          %8 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_split, context.get()));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_store_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_store_test.cpp
new file mode 100644
index 0000000..3fb9b61
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_store_test.cpp
@@ -0,0 +1,341 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_store.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationStoreTest, BasicTest) {
+  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 ; irrelevant
+         %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 ; irrelevant
+         %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 ; irrelevant
+               OpBranch %37
+         %37 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9 ; irrelevant
+         %13 = OpLabel
+         %46 = OpCopyObject %9 %11 ; irrelevant
+         %16 = OpAccessChain %15 %11 %14 ; irrelevant
+         %95 = OpCopyObject %8 %80
+               OpReturnValue %21
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(27);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(11);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(46);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(16);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(52);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(81);
+  fact_manager.AddFactValueOfPointeeIsIrrelevant(82);
+
+  fact_manager.AddFactBlockIsDead(36);
+
+  // Variables with pointee types:
+  //  52 - ptr_to(7)
+  //  53 - ptr_to(6)
+  //  20 - ptr_to(8)
+  //  27 - ptr_to(8) - irrelevant
+  //  92 - ptr_to(90) - read only
+
+  // Access chains with pointee type:
+  //  22 - ptr_to(6)
+  //  26 - ptr_to(6)
+  //  30 - ptr_to(6)
+  //  33 - ptr_to(6)
+  //  38 - ptr_to(6)
+  //  40 - ptr_to(6)
+  //  43 - ptr_to(6)
+  //  16 - ptr_to(6) - irrelevant
+
+  // Copied object with pointee type:
+  //  44 - ptr_to(8)
+  //  45 - ptr_to(6)
+  //  46 - ptr_to(8) - irrelevant
+  //  81 - ptr_to(8) - irrelevant
+  //  82 - ptr_to(8) - irrelevant
+
+  // Function parameters with pointee type:
+  //  11 - ptr_to(8) - irrelevant
+
+  // Pointers that cannot be used:
+  //  60 - null
+  //  61 - undefined
+
+  // Bad: attempt to store to 11 from outside its function
+  ASSERT_FALSE(TransformationStore(
+                   11, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: pointer is not available
+  ASSERT_FALSE(TransformationStore(
+                   81, 80, MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: attempt to insert before OpVariable
+  ASSERT_FALSE(TransformationStore(
+                   52, 24, MakeInstructionDescriptor(27, SpvOpVariable, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: pointer id does not exist
+  ASSERT_FALSE(TransformationStore(
+                   1000, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: pointer id exists but does not have a type
+  ASSERT_FALSE(TransformationStore(
+                   5, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: pointer id exists and has a type, but is not a pointer
+  ASSERT_FALSE(TransformationStore(
+                   24, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: attempt to store to a null pointer
+  ASSERT_FALSE(TransformationStore(
+                   60, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: attempt to store to an undefined pointer
+  ASSERT_FALSE(TransformationStore(
+                   61, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: %82 is not available at the program point
+  ASSERT_FALSE(
+      TransformationStore(82, 80, MakeInstructionDescriptor(37, SpvOpReturn, 0))
+          .IsApplicable(context.get(), fact_manager));
+
+  // Bad: value id does not exist
+  ASSERT_FALSE(TransformationStore(
+                   27, 1000, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: value id exists but does not have a type
+  ASSERT_FALSE(TransformationStore(
+                   27, 15, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: value id exists but has the wrong type
+  ASSERT_FALSE(TransformationStore(
+                   27, 14, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: attempt to store to read-only variable
+  ASSERT_FALSE(TransformationStore(
+                   92, 93, MakeInstructionDescriptor(40, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: value is not available
+  ASSERT_FALSE(TransformationStore(
+                   27, 95, MakeInstructionDescriptor(40, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: variable being stored to does not have an irrelevant pointee value,
+  // and the store is not in a dead block.
+  ASSERT_FALSE(TransformationStore(
+                   20, 95, MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // The described instruction does not exist.
+  ASSERT_FALSE(TransformationStore(
+                   27, 80, MakeInstructionDescriptor(1000, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  {
+    // Store to irrelevant variable from dead block.
+    TransformationStore transformation(
+        27, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  {
+    // Store to irrelevant variable from live block.
+    TransformationStore transformation(
+        11, 95, MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  {
+    // Store to irrelevant variable from live block.
+    TransformationStore transformation(
+        46, 80, MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  {
+    // Store to irrelevant variable from live block.
+    TransformationStore transformation(
+        16, 21, MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  {
+    // Store to non-irrelevant variable from dead block.
+    TransformationStore transformation(
+        53, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %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 ; irrelevant
+         %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 ; irrelevant
+         %33 = OpAccessChain %15 %20 %14
+               OpSelectionMerge %37 None
+               OpBranchConditional %35 %36 %37
+         %36 = OpLabel
+               OpStore %27 %80
+               OpStore %53 %21
+         %38 = OpAccessChain %15 %20 %14
+         %40 = OpAccessChain %15 %20 %14
+         %43 = OpAccessChain %15 %20 %14
+         %82 = OpCopyObject %9 %27 ; irrelevant
+               OpBranch %37
+         %37 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9 ; irrelevant
+         %13 = OpLabel
+         %46 = OpCopyObject %9 %11 ; irrelevant
+         %16 = OpAccessChain %15 %11 %14 ; irrelevant
+         %95 = OpCopyObject %8 %80
+               OpStore %11 %95
+               OpStore %46 %80
+               OpStore %16 %21
+               OpReturnValue %21
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/link/matching_imports_to_exports_test.cpp b/third_party/SPIRV-Tools/test/link/matching_imports_to_exports_test.cpp
index 59e62d5..e76c69f 100644
--- a/third_party/SPIRV-Tools/test/link/matching_imports_to_exports_test.cpp
+++ b/third_party/SPIRV-Tools/test/link/matching_imports_to_exports_test.cpp
@@ -399,5 +399,78 @@
   EXPECT_EQ(expected_res, res_body);
 }
 
+TEST_F(MatchingImportsToExports, NamesAndDecorations) {
+  const std::string body1 = R"(
+OpCapability Kernel
+OpCapability Linkage
+OpName %1 "foo"
+OpName %3 "param"
+OpDecorate %1 LinkageAttributes "foo" Import
+OpDecorate %2 Restrict
+OpDecorate %4 NonWritable
+%2 = OpDecorationGroup
+OpGroupDecorate %2 %3 %4
+%5 = OpTypeVoid
+%6 = OpTypeInt 32 0
+%9 = OpTypePointer Function %6
+%7 = OpTypeFunction %5 %9
+%1 = OpFunction %5 None %7
+%3 = OpFunctionParameter %9
+OpFunctionEnd
+%8 = OpFunction %5 None %7
+%4 = OpFunctionParameter %9
+OpFunctionEnd
+)";
+  const std::string body2 = R"(
+OpCapability Kernel
+OpCapability Linkage
+OpName %1 "foo"
+OpName %2 "param"
+OpDecorate %1 LinkageAttributes "foo" Export
+OpDecorate %2 Restrict
+%3 = OpTypeVoid
+%4 = OpTypeInt 32 0
+%7 = OpTypePointer Function %4
+%5 = OpTypeFunction %3 %7
+%1 = OpFunction %3 None %5
+%2 = OpFunctionParameter %7
+%6 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  spvtest::Binary linked_binary;
+  EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
+      << GetErrorMessage();
+
+  const std::string expected_res = R"(OpCapability Kernel
+OpName %1 "foo"
+OpName %2 "param"
+OpModuleProcessed "Linked by SPIR-V Tools Linker"
+OpDecorate %3 Restrict
+OpDecorate %4 NonWritable
+%3 = OpDecorationGroup
+OpGroupDecorate %3 %4
+OpDecorate %2 Restrict
+%5 = OpTypeVoid
+%6 = OpTypeInt 32 0
+%7 = OpTypePointer Function %6
+%8 = OpTypeFunction %5 %7
+%9 = OpFunction %5 None %8
+%4 = OpFunctionParameter %7
+OpFunctionEnd
+%1 = OpFunction %5 None %8
+%2 = OpFunctionParameter %7
+%10 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+  std::string res_body;
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
+      << GetErrorMessage();
+  EXPECT_EQ(expected_res, res_body);
+}
+
 }  // namespace
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/amd_ext_to_khr.cpp b/third_party/SPIRV-Tools/test/opt/amd_ext_to_khr.cpp
index d943d34..3340e89 100644
--- a/third_party/SPIRV-Tools/test/opt/amd_ext_to_khr.cpp
+++ b/third_party/SPIRV-Tools/test/opt/amd_ext_to_khr.cpp
@@ -15,7 +15,6 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
@@ -913,6 +912,42 @@
   EXPECT_THAT(output, HasSubstr("Version: 1.4"));
 }
 
+TEST_F(AmdExtToKhrTest, TimeAMD) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpCapability Int64
+               OpExtension "SPV_AMD_gcn_shader"
+; CHECK-NOT: OpExtension "SPV_AMD_gcn_shader"
+; CHECK: OpExtension "SPV_KHR_shader_clock"
+          %1 = OpExtInstImport "GLSL.std.450"
+          %2 = OpExtInstImport "SPV_AMD_gcn_shader"
+; CHECK-NOT: OpExtInstImport "SPV_AMD_gcn_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpSourceExtension "GL_AMD_gcn_shader"
+               OpSourceExtension "GL_ARB_gpu_shader_int64"
+               OpName %main "main"
+               OpName %time "time"
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+      %ulong = OpTypeInt 64 0
+%_ptr_Function_ulong = OpTypePointer Function %ulong
+       %main = OpFunction %void None %6
+          %9 = OpLabel
+       %time = OpVariable %_ptr_Function_ulong Function
+; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
+; CHECK: [[uint_3:%\w+]] = OpConstant [[uint]] 3
+         %10 = OpExtInst %ulong %2 TimeAMD
+; CHECK: %10 = OpReadClockKHR %ulong [[uint_3]]
+               OpStore %time %10
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
+}
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/block_merge_test.cpp b/third_party/SPIRV-Tools/test/opt/block_merge_test.cpp
index 11fba73..f1460c5 100644
--- a/third_party/SPIRV-Tools/test/opt/block_merge_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/block_merge_test.cpp
@@ -423,6 +423,42 @@
   SinglePassRunAndMatch<BlockMergePass>(text, true);
 }
 
+TEST_F(BlockMergeTest, MergeContinueWithOpLine) {
+  const std::string text = R"(
+; CHECK: OpBranch [[header:%\w+]]
+; CHECK: [[header]] = OpLabel
+; CHECK-NEXT: OpLogicalAnd
+; CHECK-NEXT: OpLine {{%\w+}} 1 1
+; CHECK-NEXT: OpLoopMerge {{%\w+}} [[header]] None
+; CHECK-NEXT: OpBranch [[header]]
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %func "func"
+OpExecutionMode %func OriginUpperLeft
+%src = OpString "test.shader"
+%void = OpTypeVoid
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%false = OpConstantFalse  %bool
+%functy = OpTypeFunction %void
+%func = OpFunction %void None %functy
+%entry = OpLabel
+OpBranch %header
+%header = OpLabel
+OpLoopMerge %merge %continue None
+OpBranch %continue
+%continue = OpLabel
+%op = OpLogicalAnd %bool %true %false
+OpLine %src 1 1
+OpBranch %header
+%merge = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<BlockMergePass>(text, true);
+}
+
 TEST_F(BlockMergeTest, TwoHeadersCannotBeMerged) {
   const std::string text = R"(
 ; CHECK: OpBranch [[loop_header:%\w+]]
diff --git a/third_party/SPIRV-Tools/test/opt/dead_branch_elim_test.cpp b/third_party/SPIRV-Tools/test/opt/dead_branch_elim_test.cpp
index e612867..3dcc0f7 100644
--- a/third_party/SPIRV-Tools/test/opt/dead_branch_elim_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/dead_branch_elim_test.cpp
@@ -1378,6 +1378,7 @@
 
   SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
 }
+
 TEST_F(DeadBranchElimTest, LeaveContinueBackedgeExtraBlock) {
   const std::string text = R"(
 ; CHECK: OpBranch [[header:%\w+]]
@@ -3161,6 +3162,73 @@
   SinglePassRunAndCheck<DeadBranchElimPass>(before, after, true, true);
 }
 
+TEST_F(DeadBranchElimTest, BreakInNestedHeaderWithSingleCase) {
+  const std::string text = R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpName %main "main"
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%bool = OpTypeBool
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%8 = OpUndef %bool
+%main = OpFunction %void None %4
+%9 = OpLabel
+OpSelectionMerge %10 None
+OpSwitch %uint_0 %11
+%11 = OpLabel
+OpSelectionMerge %12 None
+OpBranchConditional %8 %10 %12
+%12 = OpLabel
+OpBranch %10
+%10 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndCheck<DeadBranchElimPass>(text, text, true, true);
+}
+
+TEST_F(DeadBranchElimTest, BreakInNestedHeaderWithTwoCases) {
+  const std::string text = R"(
+; CHECK: OpSelectionMerge [[merge:%\w+]] None
+; CHECK-NEXT: OpSwitch %uint_0 [[bb:%\w+\n]]
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpName %main "main"
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%bool = OpTypeBool
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%8 = OpUndef %bool
+%main = OpFunction %void None %4
+%9 = OpLabel
+OpSelectionMerge %10 None
+OpSwitch %uint_0 %11 1 %12
+%11 = OpLabel
+OpSelectionMerge %13 None
+OpBranchConditional %8 %10 %13
+%13 = OpLabel
+OpBranch %10
+%12 = OpLabel
+OpBranch %10
+%10 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    More complex control flow
diff --git a/third_party/SPIRV-Tools/test/opt/fold_test.cpp b/third_party/SPIRV-Tools/test/opt/fold_test.cpp
index 26d1220..db01924 100644
--- a/third_party/SPIRV-Tools/test/opt/fold_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/fold_test.cpp
@@ -1693,7 +1693,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0.2f),
-    // Test case 21: FMax 1.0 4.0
+    // Test case 23: FMax 1.0 4.0
     InstructionFoldingCase<float>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -1701,7 +1701,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 4.0f),
-    // Test case 22: FMax 1.0 0.2
+    // Test case 24: FMax 1.0 0.2
     InstructionFoldingCase<float>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -1709,7 +1709,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 1.0f),
-    // Test case 23: FClamp 1.0 0.2 4.0
+    // Test case 25: FClamp 1.0 0.2 4.0
     InstructionFoldingCase<float>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -1717,7 +1717,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 1.0f),
-    // Test case 24: FClamp 0.2 2.0 4.0
+    // Test case 26: FClamp 0.2 2.0 4.0
     InstructionFoldingCase<float>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -1725,7 +1725,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 2.0f),
-    // Test case 25: FClamp 2049.0 2.0 4.0
+    // Test case 27: FClamp 2049.0 2.0 4.0
     InstructionFoldingCase<float>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -1733,7 +1733,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 4.0f),
-    // Test case 26: FClamp 1.0 2.0 x
+    // Test case 28: FClamp 1.0 2.0 x
     InstructionFoldingCase<float>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -1742,7 +1742,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 2.0),
-    // Test case 27: FClamp 1.0 x 0.5
+    // Test case 29: FClamp 1.0 x 0.5
     InstructionFoldingCase<float>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -1750,7 +1750,111 @@
             "%2 = OpExtInst %float %1 FClamp %float_1 %undef %float_0p5\n" +
             "OpReturn\n" +
             "OpFunctionEnd",
-        2, 0.5)
+        2, 0.5),
+    // Test case 30: Sin 0.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 Sin %float_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0.0),
+    // Test case 31: Cos 0.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 Cos %float_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 1.0),
+    // Test case 32: Tan 0.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 Tan %float_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0.0),
+    // Test case 33: Asin 0.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 Asin %float_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0.0),
+    // Test case 34: Acos 1.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 Acos %float_1\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0.0),
+    // Test case 35: Atan 0.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 Atan %float_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0.0),
+    // Test case 36: Exp 0.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 Exp %float_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 1.0),
+    // Test case 37: Log 1.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 Log %float_1\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0.0),
+    // Test case 38: Exp2 2.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 Exp2 %float_2\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 4.0),
+    // Test case 39: Log2 4.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 Log2 %float_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 2.0),
+    // Test case 40: Sqrt 4.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 Sqrt %float_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 2.0),
+    // Test case 41: Atan2 0.0 1.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 Atan2 %float_0 %float_1\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0.0),
+    // Test case 42: Pow 2.0 3.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 Pow %float_2 %float_3\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 8.0)
 ));
 // clang-format on
 
@@ -1967,7 +2071,25 @@
                 "%2 = OpExtInst %double %1 FClamp %double_1 %undef %double_0p5\n" +
                 "OpReturn\n" +
                 "OpFunctionEnd",
-            2, 0.5)
+            2, 0.5),
+        // Test case 21: Sqrt 4.0
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+                "%main_lab = OpLabel\n" +
+                "%undef = OpUndef %double\n" +
+                "%2 = OpExtInst %double %1 Sqrt %double_4\n" +
+                "OpReturn\n" +
+                "OpFunctionEnd",
+            2, 2.0),
+        // Test case 22: Pow 2.0 3.0
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+                "%main_lab = OpLabel\n" +
+                "%undef = OpUndef %double\n" +
+                "%2 = OpExtInst %double %1 Pow %double_2 %double_3\n" +
+                "OpReturn\n" +
+                "OpFunctionEnd",
+            2, 8.0)
 ));
 // clang-format on
 
diff --git a/third_party/SPIRV-Tools/test/opt/ir_loader_test.cpp b/third_party/SPIRV-Tools/test/opt/ir_loader_test.cpp
index ac5c520..c60e853 100644
--- a/third_party/SPIRV-Tools/test/opt/ir_loader_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/ir_loader_test.cpp
@@ -129,6 +129,621 @@
   // clang-format on
 }
 
+TEST(IrBuilder, ConsumeDebugInfoInst) {
+  // /* HLSL */
+  //
+  // struct VS_OUTPUT {
+  //   float4 pos : SV_POSITION;
+  //   float4 color : COLOR;
+  // };
+  //
+  // VS_OUTPUT main(float4 pos : POSITION,
+  //                float4 color : COLOR) {
+  //   VS_OUTPUT vout;
+  //   vout.pos = pos;
+  //   vout.color = color;
+  //   return vout;
+  // }
+  DoRoundTripCheck(R"(OpCapability Shader
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main" %pos %color %gl_Position %out_var_COLOR
+%7 = OpString "simple_vs.hlsl"
+%8 = OpString "#line 1 \"simple_vs.hlsl\"
+struct VS_OUTPUT {
+  float4 pos : SV_POSITION;
+  float4 color : COLOR;
+};
+
+VS_OUTPUT main(float4 pos : POSITION,
+               float4 color : COLOR) {
+  VS_OUTPUT vout;
+  vout.pos = pos;
+  vout.color = color;
+  return vout;
+}
+"
+OpSource HLSL 600 %7 "#line 1 \"simple_vs.hlsl\"
+struct VS_OUTPUT {
+  float4 pos : SV_POSITION;
+  float4 color : COLOR;
+};
+
+VS_OUTPUT main(float4 pos : POSITION,
+               float4 color : COLOR) {
+  VS_OUTPUT vout;
+  vout.pos = pos;
+  vout.color = color;
+  return vout;
+}
+"
+%9 = OpString "struct VS_OUTPUT"
+%10 = OpString "float"
+%11 = OpString "pos : SV_POSITION"
+%12 = OpString "color : COLOR"
+%13 = OpString "VS_OUTPUT"
+%14 = OpString "main"
+%15 = OpString "VS_OUTPUT_main_v4f_v4f"
+%16 = OpString "pos : POSITION"
+%17 = OpString "color : COLOR"
+%18 = OpString "vout"
+OpName %out_var_COLOR "out.var.COLOR"
+OpName %main "main"
+OpName %VS_OUTPUT "VS_OUTPUT"
+OpMemberName %VS_OUTPUT 0 "pos"
+OpMemberName %VS_OUTPUT 1 "color"
+OpName %pos "pos"
+OpName %color "color"
+OpName %vout "vout"
+OpDecorate %gl_Position BuiltIn Position
+OpDecorate %pos Location 0
+OpDecorate %color Location 1
+OpDecorate %out_var_COLOR Location 0
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_32 = OpConstant %int 32
+%int_128 = OpConstant %int 128
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%31 = OpTypeFunction %void
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%VS_OUTPUT = OpTypeStruct %v4float %v4float
+%_ptr_Function_VS_OUTPUT = OpTypePointer Function %VS_OUTPUT
+OpLine %7 6 23
+%pos = OpVariable %_ptr_Input_v4float Input
+OpLine %7 7 23
+%color = OpVariable %_ptr_Input_v4float Input
+OpLine %7 2 16
+%gl_Position = OpVariable %_ptr_Output_v4float Output
+OpLine %7 3 18
+%out_var_COLOR = OpVariable %_ptr_Output_v4float Output
+%34 = OpExtInst %void %1 DebugSource %7 %8
+%35 = OpExtInst %void %1 DebugCompilationUnit 2 4 %34 HLSL
+%36 = OpExtInst %void %1 DebugTypeComposite %9 Structure %34 1 1 %35 %13 %int_128 FlagIsProtected|FlagIsPrivate %37 %38
+%39 = OpExtInst %void %1 DebugTypeBasic %10 %int_32 Float
+%40 = OpExtInst %void %1 DebugTypeVector %39 4
+%37 = OpExtInst %void %1 DebugTypeMember %11 %40 %34 2 3 %36 %int_0 %int_128 FlagIsProtected|FlagIsPrivate
+%38 = OpExtInst %void %1 DebugTypeMember %12 %40 %34 3 3 %36 %int_128 %int_128 FlagIsProtected|FlagIsPrivate
+%41 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %36 %40 %40
+%42 = OpExtInst %void %1 DebugExpression
+%43 = OpExtInst %void %1 DebugFunction %14 %41 %34 6 1 %35 %15 FlagIsProtected|FlagIsPrivate 7 %main
+%44 = OpExtInst %void %1 DebugLocalVariable %16 %40 %34 6 16 %43 FlagIsLocal 0
+%45 = OpExtInst %void %1 DebugLocalVariable %17 %40 %34 7 16 %43 FlagIsLocal 1
+%46 = OpExtInst %void %1 DebugLocalVariable %18 %36 %34 8 3 %43 FlagIsLocal
+%47 = OpExtInst %void %1 DebugDeclare %44 %pos %42
+%48 = OpExtInst %void %1 DebugDeclare %45 %color %42
+OpLine %7 6 1
+%main = OpFunction %void None %31
+%49 = OpLabel
+%50 = OpExtInst %void %1 DebugScope %43
+OpLine %7 8 13
+%vout = OpVariable %_ptr_Function_VS_OUTPUT Function
+%51 = OpExtInst %void %1 DebugDeclare %46 %vout %42
+OpLine %7 9 14
+%52 = OpLoad %v4float %pos
+OpLine %7 9 3
+%53 = OpAccessChain %_ptr_Function_v4float %vout %int_0
+%54 = OpExtInst %void %1 DebugValue %46 %53 %42 %int_0
+OpStore %53 %52
+OpLine %7 10 16
+%55 = OpLoad %v4float %color
+OpLine %7 10 3
+%56 = OpAccessChain %_ptr_Function_v4float %vout %int_1
+%57 = OpExtInst %void %1 DebugValue %46 %56 %42 %int_1
+OpStore %56 %55
+OpLine %7 11 10
+%58 = OpLoad %VS_OUTPUT %vout
+OpLine %7 11 3
+%59 = OpCompositeExtract %v4float %58 0
+OpStore %gl_Position %59
+%60 = OpCompositeExtract %v4float %58 1
+OpStore %out_var_COLOR %60
+OpReturn
+OpFunctionEnd
+)");
+}
+
+TEST(IrBuilder, ConsumeDebugInfoLexicalScopeInst) {
+  // /* HLSL */
+  //
+  // float4 func2(float arg2) {   // func2_block
+  //   return float4(arg2, 0, 0, 0);
+  // }
+  //
+  // float4 func1(float arg1) {   // func1_block
+  //   if (arg1 > 1) {       // if_true_block
+  //     return float4(0, 0, 0, 0);
+  //   }
+  //   return func2(arg1);   // if_merge_block
+  // }
+  //
+  // float4 main(float pos : POSITION) : SV_POSITION {  // main
+  //   return func1(pos);
+  // }
+  DoRoundTripCheck(R"(OpCapability Shader
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main" %pos %gl_Position
+%5 = OpString "block/block.hlsl"
+%6 = OpString "#line 1 \"block/block.hlsl\"
+float4 func2(float arg2) {
+  return float4(arg2, 0, 0, 0);
+}
+
+float4 func1(float arg1) {
+  if (arg1 > 1) {
+    return float4(0, 0, 0, 0);
+  }
+  return func2(arg1);
+}
+
+float4 main(float pos : POSITION) : SV_POSITION {
+  return func1(pos);
+}
+"
+OpSource HLSL 600 %5 "#line 1 \"block/block.hlsl\"
+float4 func2(float arg2) {
+  return float4(arg2, 0, 0, 0);
+}
+
+float4 func1(float arg1) {
+  if (arg1 > 1) {
+    return float4(0, 0, 0, 0);
+  }
+  return func2(arg1);
+}
+
+float4 main(float pos : POSITION) : SV_POSITION {
+  return func1(pos);
+}
+"
+%7 = OpString "float"
+%8 = OpString "main"
+%9 = OpString "v4f_main_f"
+%10 = OpString "v4f_func1_f"
+%11 = OpString "v4f_func2_f"
+%12 = OpString "pos : POSITION"
+%13 = OpString "func1"
+%14 = OpString "func2"
+OpName %main "main"
+OpName %pos "pos"
+OpName %bb_entry "bb.entry"
+OpName %param_var_arg1 "param.var.arg1"
+OpName %func1 "func1"
+OpName %arg1 "arg1"
+OpName %bb_entry_0 "bb.entry"
+OpName %param_var_arg2 "param.var.arg2"
+OpName %if_true "if.true"
+OpName %if_merge "if.merge"
+OpName %func2 "func2"
+OpName %arg2 "arg2"
+OpName %bb_entry_1 "bb.entry"
+OpDecorate %gl_Position BuiltIn Position
+OpDecorate %pos Location 0
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 1
+%float_1 = OpConstant %float 1
+%float_0 = OpConstant %float 0
+%int_32 = OpConstant %int 32
+%v4float = OpTypeVector %float 4
+%32 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%36 = OpTypeFunction %void
+%_ptr_Function_float = OpTypePointer Function %float
+%38 = OpTypeFunction %v4float %_ptr_Function_float
+%bool = OpTypeBool
+OpLine %5 12 25
+%pos = OpVariable %_ptr_Input_float Input
+OpLine %5 12 37
+%gl_Position = OpVariable %_ptr_Output_v4float Output
+%40 = OpExtInst %void %1 DebugSource %5 %6
+%41 = OpExtInst %void %1 DebugCompilationUnit 2 4 %40 HLSL
+%42 = OpExtInst %void %1 DebugTypeBasic %7 %int_32 Float
+%43 = OpExtInst %void %1 DebugTypeVector %42 4
+%44 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42
+%45 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42
+%46 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42
+%47 = OpExtInst %void %1 DebugFunction %8 %44 %40 12 1 %41 %9 FlagIsProtected|FlagIsPrivate 13 %main
+%48 = OpExtInst %void %1 DebugFunction %13 %45 %40 5 1 %41 %10 FlagIsProtected|FlagIsPrivate 13 %func1
+%49 = OpExtInst %void %1 DebugFunction %14 %46 %40 1 1 %41 %11 FlagIsProtected|FlagIsPrivate 13 %func2
+%50 = OpExtInst %void %1 DebugLexicalBlock %40 6 17 %48
+%51 = OpExtInst %void %1 DebugLexicalBlock %40 9 3 %48
+OpLine %5 12 1
+%main = OpFunction %void None %36
+%bb_entry = OpLabel
+%52 = OpExtInst %void %1 DebugScope %47
+OpLine %5 13 16
+%param_var_arg1 = OpVariable %_ptr_Function_float Function
+%53 = OpLoad %float %pos
+OpStore %param_var_arg1 %53
+OpLine %5 13 10
+%54 = OpFunctionCall %v4float %func1 %param_var_arg1
+OpLine %5 13 3
+OpStore %gl_Position %54
+OpReturn
+OpFunctionEnd
+OpLine %5 5 1
+%func1 = OpFunction %v4float None %38
+OpLine %5 5 20
+%arg1 = OpFunctionParameter %_ptr_Function_float
+%bb_entry_0 = OpLabel
+%55 = OpExtInst %void %1 DebugScope %48
+OpLine %5 9 16
+%param_var_arg2 = OpVariable %_ptr_Function_float Function
+OpLine %5 6 7
+%56 = OpLoad %float %arg1
+OpLine %5 6 12
+%57 = OpFOrdGreaterThan %bool %56 %float_1
+OpLine %5 6 17
+OpSelectionMerge %if_merge None
+OpBranchConditional %57 %if_true %if_merge
+%if_true = OpLabel
+%58 = OpExtInst %void %1 DebugScope %50
+OpLine %5 7 5
+OpReturnValue %32
+%if_merge = OpLabel
+%59 = OpExtInst %void %1 DebugScope %51
+OpLine %5 9 16
+%60 = OpLoad %float %arg1
+OpStore %param_var_arg2 %60
+OpLine %5 9 10
+%61 = OpFunctionCall %v4float %func2 %param_var_arg2
+OpLine %5 9 3
+OpReturnValue %61
+OpFunctionEnd
+OpLine %5 1 1
+%func2 = OpFunction %v4float None %38
+OpLine %5 1 20
+%arg2 = OpFunctionParameter %_ptr_Function_float
+%bb_entry_1 = OpLabel
+%62 = OpExtInst %void %1 DebugScope %49
+OpLine %5 2 17
+%63 = OpLoad %float %arg2
+%64 = OpCompositeConstruct %v4float %63 %float_0 %float_0 %float_0
+OpLine %5 2 3
+OpReturnValue %64
+OpFunctionEnd
+)");
+}
+
+TEST(IrBuilder, ConsumeDebugInlinedAt) {
+  // /* HLSL */
+  //
+  // float4 func2(float arg2) {   // func2_block
+  //   return float4(arg2, 0, 0, 0);
+  // }
+  //
+  // float4 func1(float arg1) {   // func1_block
+  //   if (arg1 > 1) {       // if_true_block
+  //     return float4(0, 0, 0, 0);
+  //   }
+  //   return func2(arg1);   // if_merge_block
+  // }
+  //
+  // float4 main(float pos : POSITION) : SV_POSITION {  // main
+  //   return func1(pos);
+  // }
+  //
+  // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): In the following
+  // SPIRV code, we use DebugInfoNone to reference opted-out function from
+  // DebugFunction similar to opted-out global variable for DebugGlobalVariable,
+  // but this is not a part of the spec yet. We are still in discussion and we
+  // must correct it if our decision is different.
+  DoRoundTripCheck(R"(OpCapability Shader
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main" %pos %gl_Position
+%5 = OpString "block/block.hlsl"
+%6 = OpString "#line 1 \"block/block.hlsl\"
+float4 func2(float arg2) {
+  return float4(arg2, 0, 0, 0);
+}
+
+float4 func1(float arg1) {
+  if (arg1 > 1) {
+    return float4(0, 0, 0, 0);
+  }
+  return func2(arg1);
+}
+
+float4 main(float pos : POSITION) : SV_POSITION {
+  return func1(pos);
+}
+"
+OpSource HLSL 600 %5 "#line 1 \"block/block.hlsl\"
+float4 func2(float arg2) {
+  return float4(arg2, 0, 0, 0);
+}
+
+float4 func1(float arg1) {
+  if (arg1 > 1) {
+    return float4(0, 0, 0, 0);
+  }
+  return func2(arg1);
+}
+
+float4 main(float pos : POSITION) : SV_POSITION {
+  return func1(pos);
+}
+"
+%7 = OpString "float"
+%8 = OpString "main"
+%9 = OpString "v4f_main_f"
+%10 = OpString "v4f_func1_f"
+%11 = OpString "v4f_func2_f"
+%12 = OpString "pos : POSITION"
+%13 = OpString "func1"
+%14 = OpString "func2"
+OpName %main "main"
+OpName %pos "pos"
+OpName %bb_entry "bb.entry"
+OpName %if_true "if.true"
+OpName %if_merge "if.merge"
+OpDecorate %gl_Position BuiltIn Position
+OpDecorate %pos Location 0
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 1
+%float_1 = OpConstant %float 1
+%float_0 = OpConstant %float 0
+%int_32 = OpConstant %int 32
+%v4float = OpTypeVector %float 4
+%24 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%28 = OpTypeFunction %void
+%_ptr_Function_float = OpTypePointer Function %float
+%30 = OpTypeFunction %v4float %_ptr_Function_float
+%bool = OpTypeBool
+OpLine %5 12 25
+%pos = OpVariable %_ptr_Input_float Input
+OpLine %5 12 37
+%gl_Position = OpVariable %_ptr_Output_v4float Output
+%32 = OpExtInst %void %1 DebugInfoNone
+%33 = OpExtInst %void %1 DebugSource %5 %6
+%34 = OpExtInst %void %1 DebugCompilationUnit 2 4 %33 HLSL
+%35 = OpExtInst %void %1 DebugTypeBasic %7 %int_32 Float
+%36 = OpExtInst %void %1 DebugTypeVector %35 4
+%37 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %36 %35
+%38 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %36 %35
+%39 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %36 %35
+%40 = OpExtInst %void %1 DebugFunction %8 %37 %33 12 1 %34 %9 FlagIsProtected|FlagIsPrivate 13 %main
+%41 = OpExtInst %void %1 DebugFunction %13 %38 %33 5 1 %34 %10 FlagIsProtected|FlagIsPrivate 13 %32
+%42 = OpExtInst %void %1 DebugFunction %14 %39 %33 1 1 %34 %11 FlagIsProtected|FlagIsPrivate 13 %32
+%43 = OpExtInst %void %1 DebugLexicalBlock %33 12 49 %40
+%44 = OpExtInst %void %1 DebugLexicalBlock %33 5 26 %41
+%45 = OpExtInst %void %1 DebugLexicalBlock %33 1 26 %42
+%46 = OpExtInst %void %1 DebugLexicalBlock %33 6 17 %44
+%47 = OpExtInst %void %1 DebugLexicalBlock %33 9 3 %44
+%48 = OpExtInst %void %1 DebugInlinedAt 9 %47
+%49 = OpExtInst %void %1 DebugInlinedAt 13 %43
+%50 = OpExtInst %void %1 DebugInlinedAt 13 %43 %48
+OpLine %5 12 1
+%main = OpFunction %void None %28
+%bb_entry = OpLabel
+%51 = OpExtInst %void %1 DebugScope %44 %49
+OpLine %5 6 7
+%52 = OpLoad %float %pos
+OpLine %5 6 12
+%53 = OpFOrdGreaterThan %bool %52 %float_1
+OpLine %5 6 17
+OpSelectionMerge %if_merge None
+OpBranchConditional %53 %if_true %if_merge
+%if_true = OpLabel
+%54 = OpExtInst %void %1 DebugScope %46 %49
+OpLine %5 7 5
+OpStore %gl_Position %24
+OpReturn
+%if_merge = OpLabel
+%55 = OpExtInst %void %1 DebugScope %45 %50
+OpLine %5 2 17
+%56 = OpLoad %float %pos
+OpLine %5 2 10
+%57 = OpCompositeConstruct %v4float %56 %float_0 %float_0 %float_0
+%58 = OpExtInst %void %1 DebugScope %43
+OpLine %5 13 3
+OpStore %gl_Position %57
+OpReturn
+OpFunctionEnd
+)");
+}
+
+TEST(IrBuilder, DebugInfoInstInFunctionOutOfBlock) {
+  // /* HLSL */
+  //
+  // float4 func2(float arg2) {   // func2_block
+  //   return float4(arg2, 0, 0, 0);
+  // }
+  //
+  // float4 func1(float arg1) {   // func1_block
+  //   if (arg1 > 1) {       // if_true_block
+  //     return float4(0, 0, 0, 0);
+  //   }
+  //   return func2(arg1);   // if_merge_block
+  // }
+  //
+  // float4 main(float pos : POSITION) : SV_POSITION {  // main
+  //   return func1(pos);
+  // }
+  const std::string text = R"(OpCapability Shader
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main" %pos %gl_Position
+%5 = OpString "block/block.hlsl"
+%6 = OpString "#line 1 \"block/block.hlsl\"
+float4 func2(float arg2) {
+  return float4(arg2, 0, 0, 0);
+}
+
+float4 func1(float arg1) {
+  if (arg1 > 1) {
+    return float4(0, 0, 0, 0);
+  }
+  return func2(arg1);
+}
+
+float4 main(float pos : POSITION) : SV_POSITION {
+  return func1(pos);
+}
+"
+OpSource HLSL 600 %5 "#line 1 \"block/block.hlsl\"
+float4 func2(float arg2) {
+  return float4(arg2, 0, 0, 0);
+}
+
+float4 func1(float arg1) {
+  if (arg1 > 1) {
+    return float4(0, 0, 0, 0);
+  }
+  return func2(arg1);
+}
+
+float4 main(float pos : POSITION) : SV_POSITION {
+  return func1(pos);
+}
+"
+%7 = OpString "float"
+%8 = OpString "main"
+%9 = OpString "v4f_main_f"
+%10 = OpString "v4f_func1_f"
+%11 = OpString "v4f_func2_f"
+%12 = OpString "pos : POSITION"
+%13 = OpString "func1"
+%14 = OpString "func2"
+OpName %main "main"
+OpName %pos "pos"
+OpName %bb_entry "bb.entry"
+OpName %param_var_arg1 "param.var.arg1"
+OpName %func1 "func1"
+OpName %arg1 "arg1"
+OpName %bb_entry_0 "bb.entry"
+OpName %param_var_arg2 "param.var.arg2"
+OpName %if_true "if.true"
+OpName %if_merge "if.merge"
+OpName %func2 "func2"
+OpName %arg2 "arg2"
+OpName %bb_entry_1 "bb.entry"
+OpDecorate %gl_Position BuiltIn Position
+OpDecorate %pos Location 0
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 1
+%float_1 = OpConstant %float 1
+%float_0 = OpConstant %float 0
+%int_32 = OpConstant %int 32
+%v4float = OpTypeVector %float 4
+%32 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%36 = OpTypeFunction %void
+%_ptr_Function_float = OpTypePointer Function %float
+%38 = OpTypeFunction %v4float %_ptr_Function_float
+%bool = OpTypeBool
+OpLine %5 12 25
+%pos = OpVariable %_ptr_Input_float Input
+OpLine %5 12 37
+%gl_Position = OpVariable %_ptr_Output_v4float Output
+%40 = OpExtInst %void %1 DebugSource %5 %6
+%41 = OpExtInst %void %1 DebugCompilationUnit 2 4 %40 HLSL
+%42 = OpExtInst %void %1 DebugTypeBasic %7 %int_32 Float
+%43 = OpExtInst %void %1 DebugTypeVector %42 4
+%44 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42
+%45 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42
+%46 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42
+%47 = OpExtInst %void %1 DebugFunction %8 %44 %40 12 1 %41 %9 FlagIsProtected|FlagIsPrivate 13 %main
+%48 = OpExtInst %void %1 DebugFunction %13 %45 %40 5 1 %41 %10 FlagIsProtected|FlagIsPrivate 13 %func1
+%49 = OpExtInst %void %1 DebugFunction %14 %46 %40 1 1 %41 %11 FlagIsProtected|FlagIsPrivate 13 %func2
+%50 = OpExtInst %void %1 DebugLexicalBlock %40 6 17 %48
+%51 = OpExtInst %void %1 DebugLexicalBlock %40 9 3 %48
+OpLine %5 12 1
+%main = OpFunction %void None %36
+%52 = OpExtInst %void %1 DebugScope %47
+%bb_entry = OpLabel
+OpLine %5 13 16
+%param_var_arg1 = OpVariable %_ptr_Function_float Function
+%53 = OpLoad %float %pos
+OpStore %param_var_arg1 %53
+OpLine %5 13 10
+%54 = OpFunctionCall %v4float %func1 %param_var_arg1
+OpLine %5 13 3
+OpStore %gl_Position %54
+OpReturn
+OpFunctionEnd
+OpLine %5 5 1
+%func1 = OpFunction %v4float None %38
+OpLine %5 5 20
+%arg1 = OpFunctionParameter %_ptr_Function_float
+%bb_entry_0 = OpLabel
+%55 = OpExtInst %void %1 DebugScope %48
+OpLine %5 9 16
+%param_var_arg2 = OpVariable %_ptr_Function_float Function
+OpLine %5 6 7
+%56 = OpLoad %float %arg1
+OpLine %5 6 12
+%57 = OpFOrdGreaterThan %bool %56 %float_1
+OpLine %5 6 17
+OpSelectionMerge %if_merge None
+OpBranchConditional %57 %if_true %if_merge
+%if_true = OpLabel
+%58 = OpExtInst %void %1 DebugScope %50
+OpLine %5 7 5
+OpReturnValue %32
+%if_merge = OpLabel
+%59 = OpExtInst %void %1 DebugScope %51
+OpLine %5 9 16
+%60 = OpLoad %float %arg1
+OpStore %param_var_arg2 %60
+OpLine %5 9 10
+%61 = OpFunctionCall %v4float %func2 %param_var_arg2
+OpLine %5 9 3
+OpReturnValue %61
+OpFunctionEnd
+OpLine %5 1 1
+%func2 = OpFunction %v4float None %38
+OpLine %5 1 20
+%arg2 = OpFunctionParameter %_ptr_Function_float
+%bb_entry_1 = OpLabel
+%62 = OpExtInst %void %1 DebugScope %49
+OpLine %5 2 17
+%63 = OpLoad %float %arg2
+%64 = OpCompositeConstruct %v4float %63 %float_0 %float_0 %float_0
+OpLine %5 2 3
+OpReturnValue %64
+OpFunctionEnd
+)";
+
+  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text);
+  ASSERT_EQ(nullptr, context);
+}
+
 TEST(IrBuilder, LocalGlobalVariables) {
   // #version 310 es
   //
diff --git a/third_party/SPIRV-Tools/test/opt/pass_merge_return_test.cpp b/third_party/SPIRV-Tools/test/opt/pass_merge_return_test.cpp
index 4118169..d16b65c 100644
--- a/third_party/SPIRV-Tools/test/opt/pass_merge_return_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/pass_merge_return_test.cpp
@@ -268,7 +268,7 @@
 ; CHECK: [[true:%\w+]] = OpConstantTrue
 ; CHECK: OpFunction
 ; CHECK: [[var:%\w+]] = OpVariable [[:%\w+]] Function [[false]]
-; CHECK: OpLoopMerge [[return_block:%\w+]]
+; CHECK: OpSelectionMerge [[return_block:%\w+]]
 ; CHECK: OpSelectionMerge [[merge_lab:%\w+]]
 ; CHECK: OpBranchConditional [[cond:%\w+]] [[if_lab:%\w+]] [[then_lab:%\w+]]
 ; CHECK: [[if_lab]] = OpLabel
@@ -314,7 +314,7 @@
 ; CHECK: [[true:%\w+]] = OpConstantTrue
 ; CHECK: OpFunction
 ; CHECK: [[var:%\w+]] = OpVariable [[:%\w+]] Function [[false]]
-; CHECK: OpLoopMerge [[dummy_loop_merge:%\w+]]
+; CHECK: OpSelectionMerge [[dummy_loop_merge:%\w+]]
 ; CHECK: OpSelectionMerge [[merge_lab:%\w+]]
 ; CHECK: OpBranchConditional [[cond:%\w+]] [[if_lab:%\w+]] [[then_lab:%\w+]]
 ; CHECK: [[if_lab]] = OpLabel
@@ -364,7 +364,7 @@
 ; CHECK: [[true:%\w+]] = OpConstantTrue
 ; CHECK: OpFunction
 ; CHECK: [[var:%\w+]] = OpVariable [[:%\w+]] Function [[false]]
-; CHECK: OpLoopMerge [[return_block:%\w+]]
+; CHECK: OpSelectionMerge [[return_block:%\w+]]
 ; CHECK: OpSelectionMerge [[merge_lab:%\w+]]
 ; CHECK: OpBranchConditional [[cond:%\w+]] [[if_lab:%\w+]] [[then_lab:%\w+]]
 ; CHECK: [[if_lab]] = OpLabel
@@ -411,7 +411,7 @@
   const std::string before =
       R"(
 ; CHECK: OpFunction
-; CHECK: OpLoopMerge [[dummy_loop_merge:%\w+]]
+; CHECK: OpSelectionMerge [[dummy_loop_merge:%\w+]]
 ; CHECK: OpLoopMerge [[loop_merge:%\w+]]
 ; CHECK: [[loop_merge]] = OpLabel
 ; CHECK: OpBranchConditional {{%\w+}} [[dummy_loop_merge]] [[old_code_path:%\w+]]
@@ -525,7 +525,7 @@
       R"(
 ; CHECK: OpFunction
 ; CHECK: [[ret_flag:%\w+]] = OpVariable %_ptr_Function_bool Function %false
-; CHECK: OpLoopMerge [[dummy_loop_merge:%\w+]]
+; CHECK: OpSelectionMerge [[dummy_loop_merge:%\w+]]
 ; CHECK: OpLoopMerge [[loop1_merge:%\w+]] {{%\w+}}
 ; CHECK-NEXT: OpBranchConditional {{%\w+}} [[if_lab:%\w+]] {{%\w+}}
 ; CHECK: [[if_lab]] = OpLabel
@@ -914,7 +914,7 @@
   const std::string test =
       R"(
 ; CHECK: OpFunction
-; CHECK: OpLoopMerge [[dummy_loop_merge:%\w+]]
+; CHECK: OpSelectionMerge [[dummy_loop_merge:%\w+]]
 ; CHECK: OpLoopMerge [[outer_loop_merge:%\w+]]
 ; CHECK: OpLoopMerge [[inner_loop_merge:%\w+]]
 ; CHECK: OpSelectionMerge
@@ -1150,7 +1150,6 @@
       R"(
 ; CHECK: OpFunction %void
 ; CHECK: OpLabel
-; CHECK: OpLabel
 ; CHECK: [[pre_header:%\w+]] = OpLabel
 ; CHECK: [[header:%\w+]] = OpLabel
 ; CHECK-NEXT: OpPhi %bool {{%\w+}} [[pre_header]] [[iv:%\w+]] [[continue:%\w+]]
@@ -1196,7 +1195,6 @@
       R"(
 ; CHECK: OpFunction %void
 ; CHECK: OpLabel
-; CHECK: OpLabel
 ; CHECK: [[pre_header:%\w+]] = OpLabel
 ; CHECK: [[header:%\w+]] = OpLabel
 ; CHECK-NEXT: OpPhi
@@ -1258,7 +1256,9 @@
 TEST_F(MergeReturnPassTest, GeneratePhiInOuterLoop) {
   const std::string before =
       R"(
-      ; CHECK: OpLoopMerge
+      ; CHECK: OpSelectionMerge
+      ; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]]
+      ; CHECK-NEXT: [[def_bb1]] = OpLabel
       ; CHECK: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]]
       ; CHECK: [[continue]] = OpLabel
       ; CHECK-NEXT: [[undef:%\w+]] = OpUndef
@@ -1322,7 +1322,9 @@
   // #2455: This test case triggers phi insertions that use previously inserted
   // phis. Without the fix, it fails to validate.
   const std::string spirv = R"(
-; CHECK: OpLoopMerge
+; CHECK: OpSelectionMerge
+; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]]
+; CHECK-NEXT: [[def_bb1]] = OpLabel
 ; CHECK: OpLoopMerge
 ; CHECK: OpLoopMerge
 ; CHECK: OpLoopMerge [[merge:%\w+]]
@@ -1430,9 +1432,9 @@
 TEST_F(MergeReturnPassTest, InnerLoopMergeIsOuterLoopContinue) {
   const std::string before =
       R"(
-      ; CHECK: OpLoopMerge
-      ; CHECK-NEXT: OpBranch [[bb1:%\w+]]
-      ; CHECK: [[bb1]] = OpLabel
+      ; CHECK: OpSelectionMerge
+      ; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]]
+      ; CHECK-NEXT: [[def_bb1]] = OpLabel
       ; CHECK-NEXT: OpBranch [[outer_loop_header:%\w+]]
       ; CHECK: [[outer_loop_header]] = OpLabel
       ; CHECK-NEXT: OpLoopMerge [[outer_loop_merge:%\w+]] [[outer_loop_continue:%\w+]] None
@@ -1481,7 +1483,9 @@
 TEST_F(MergeReturnPassTest, BreakFromLoopUseNoLongerDominated) {
   const std::string spirv = R"(
 ; CHECK: [[undef:%\w+]] = OpUndef
-; CHECK: OpLoopMerge
+; CHECK: OpSelectionMerge
+; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]]
+; CHECK-NEXT: [[def_bb1]] = OpLabel
 ; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]]
 ; CHECK-NEXT: OpBranch [[body:%\w+]]
 ; CHECK: [[body]] = OpLabel
@@ -1541,7 +1545,9 @@
 TEST_F(MergeReturnPassTest, TwoBreaksFromLoopUsesNoLongerDominated) {
   const std::string spirv = R"(
 ; CHECK: [[undef:%\w+]] = OpUndef
-; CHECK: OpLoopMerge
+; CHECK: OpSelectionMerge
+; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]]
+; CHECK-NEXT: [[def_bb1]] = OpLabel
 ; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]]
 ; CHECK-NEXT: OpBranch [[body:%\w+]]
 ; CHECK: [[body]] = OpLabel
@@ -1725,7 +1731,9 @@
   const std::string text =
       R"(
 ; CHECK: [[new_undef:%\w+]] = OpUndef %uint
-; CHECK: OpLoopMerge
+; CHECK: OpSelectionMerge
+; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]]
+; CHECK-NEXT: [[def_bb1]] = OpLabel
 ; CHECK: OpLoopMerge [[merge1:%\w+]]
 ; CHECK: OpLoopMerge [[merge2:%\w+]]
 ; CHECK: [[merge1]] = OpLabel
@@ -1781,7 +1789,9 @@
   //  Add and use a phi in the second merge block from the return.
   const std::string text =
       R"(
-; CHECK: OpLoopMerge
+; CHECK: OpSelectionMerge
+; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]]
+; CHECK-NEXT: [[def_bb1]] = OpLabel
 ; CHECK: OpLoopMerge [[merge_bb:%\w+]] [[continue_bb:%\w+]]
 ; CHECK: [[continue_bb]] = OpLabel
 ; CHECK-NEXT: [[val:%\w+]] = OpUndef %float
@@ -1831,6 +1841,91 @@
   SinglePassRunAndMatch<MergeReturnPass>(text, true);
 }
 
+TEST_F(MergeReturnPassTest, ReturnsInSwitch) {
+  //  Cannot branch directly to dummy switch merge block from original switch.
+  //  Must branch to merge block of original switch and then do predicated
+  //  branch to merge block of dummy switch.
+  const std::string text =
+      R"(
+; CHECK: OpSelectionMerge [[dummy_merge_bb:%\w+]]
+; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]]
+; CHECK-NEXT: [[def_bb1]] = OpLabel
+; CHECK: OpSelectionMerge
+; CHECK-NEXT: OpSwitch {{%\w+}} [[inner_merge_bb:%\w+]] 0 {{%\w+}} 1 {{%\w+}}
+; CHECK: OpBranch [[inner_merge_bb]]
+; CHECK: OpBranch [[inner_merge_bb]]
+; CHECK-NEXT: [[inner_merge_bb]] = OpLabel
+; CHECK: OpBranchConditional {{%\w+}} [[dummy_merge_bb]] {{%\w+}}
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %PSMain "PSMain" %_entryPointOutput_color
+               OpExecutionMode %PSMain OriginUpperLeft
+               OpSource HLSL 500
+               OpMemberDecorate %cb 0 Offset 0
+               OpMemberDecorate %cb 1 Offset 16
+               OpMemberDecorate %cb 2 Offset 32
+               OpMemberDecorate %cb 3 Offset 48
+               OpDecorate %cb Block
+               OpDecorate %_ DescriptorSet 0
+               OpDecorate %_ Binding 0
+               OpDecorate %_entryPointOutput_color Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+          %8 = OpTypeFunction %v4float
+        %int = OpTypeInt 32 1
+         %cb = OpTypeStruct %v4float %v4float %v4float %int
+%_ptr_Uniform_cb = OpTypePointer Uniform %cb
+          %_ = OpVariable %_ptr_Uniform_cb Uniform
+      %int_3 = OpConstant %int 3
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+      %int_0 = OpConstant %int 0
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+    %float_0 = OpConstant %float 0
+    %float_1 = OpConstant %float 1
+         %45 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_entryPointOutput_color = OpVariable %_ptr_Output_v4float Output
+     %PSMain = OpFunction %void None %3
+          %5 = OpLabel
+         %50 = OpFunctionCall %v4float %BlendValue_
+               OpStore %_entryPointOutput_color %50
+               OpReturn
+               OpFunctionEnd
+%BlendValue_ = OpFunction %v4float None %8
+         %10 = OpLabel
+         %21 = OpAccessChain %_ptr_Uniform_int %_ %int_3
+         %22 = OpLoad %int %21
+               OpSelectionMerge %25 None
+               OpSwitch %22 %25 0 %23 1 %24
+         %23 = OpLabel
+         %28 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0
+         %29 = OpLoad %v4float %28
+               OpReturnValue %29
+         %24 = OpLabel
+         %31 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0
+         %32 = OpLoad %v4float %31
+         %34 = OpAccessChain %_ptr_Uniform_v4float %_ %int_1
+         %35 = OpLoad %v4float %34
+         %37 = OpAccessChain %_ptr_Uniform_v4float %_ %int_2
+         %38 = OpLoad %v4float %37
+         %39 = OpFMul %v4float %35 %38
+         %40 = OpFAdd %v4float %32 %39
+               OpReturnValue %40
+         %25 = OpLabel
+               OpReturnValue %45
+               OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<MergeReturnPass>(text, true);
+}
+
 TEST_F(MergeReturnPassTest, UnreachableMergeAndContinue) {
   // Make sure that the pass can handle a single block that is both a merge and
   // a continue.
diff --git a/third_party/SPIRV-Tools/test/target_env_test.cpp b/third_party/SPIRV-Tools/test/target_env_test.cpp
index 9f80e83..9c86e2d 100644
--- a/third_party/SPIRV-Tools/test/target_env_test.cpp
+++ b/third_party/SPIRV-Tools/test/target_env_test.cpp
@@ -63,11 +63,13 @@
 
 using TargetParseTest = ::testing::TestWithParam<ParseCase>;
 
-TEST_P(TargetParseTest, InvalidTargetEnvProducesNull) {
+TEST_P(TargetParseTest, Samples) {
   spv_target_env env;
   bool parsed = spvParseTargetEnv(GetParam().input, &env);
   EXPECT_THAT(parsed, Eq(GetParam().success));
-  EXPECT_THAT(env, Eq(GetParam().env));
+  if (parsed) {
+    EXPECT_THAT(env, Eq(GetParam().env));
+  }
 }
 
 INSTANTIATE_TEST_SUITE_P(
@@ -79,6 +81,7 @@
         {"spv1.3", true, SPV_ENV_UNIVERSAL_1_3},
         {"vulkan1.0", true, SPV_ENV_VULKAN_1_0},
         {"vulkan1.1", true, SPV_ENV_VULKAN_1_1},
+        {"vulkan1.2", true, SPV_ENV_VULKAN_1_2},
         {"opencl2.1", true, SPV_ENV_OPENCL_2_1},
         {"opencl2.2", true, SPV_ENV_OPENCL_2_2},
         {"opengl4.0", true, SPV_ENV_OPENGL_4_0},
@@ -95,12 +98,68 @@
         {"webgpu0", true, SPV_ENV_WEBGPU_0},
         {"opencl2.3", false, SPV_ENV_UNIVERSAL_1_0},
         {"opencl3.0", false, SPV_ENV_UNIVERSAL_1_0},
-        {"vulkan1.2", false, SPV_ENV_UNIVERSAL_1_0},
+        {"vulkan1.9", false, SPV_ENV_UNIVERSAL_1_0},
         {"vulkan2.0", false, SPV_ENV_UNIVERSAL_1_0},
         {nullptr, false, SPV_ENV_UNIVERSAL_1_0},
         {"", false, SPV_ENV_UNIVERSAL_1_0},
         {"abc", false, SPV_ENV_UNIVERSAL_1_0},
     }));
 
+// A test case for parsing an environment string.
+struct ParseVulkanCase {
+  uint32_t vulkan;
+  uint32_t spirv;
+  bool success;        // Expect to successfully parse?
+  spv_target_env env;  // The parsed environment, if successful.
+};
+
+using TargetParseVulkanTest = ::testing::TestWithParam<ParseVulkanCase>;
+
+TEST_P(TargetParseVulkanTest, Samples) {
+  spv_target_env env;
+  bool parsed = spvParseVulkanEnv(GetParam().vulkan, GetParam().spirv, &env);
+  EXPECT_THAT(parsed, Eq(GetParam().success));
+  if (parsed) {
+    EXPECT_THAT(env, Eq(GetParam().env));
+  }
+}
+
+#define VK(MAJ, MIN) ((MAJ << 22) | (MIN << 12))
+#define SPV(MAJ, MIN) ((MAJ << 16) | (MIN << 8))
+INSTANTIATE_TEST_SUITE_P(
+    TargetVulkanParsing, TargetParseVulkanTest,
+    ValuesIn(std::vector<ParseVulkanCase>{
+        // Vulkan 1.0 cases
+        {VK(1, 0), SPV(1, 0), true, SPV_ENV_VULKAN_1_0},
+        {VK(1, 0), SPV(1, 1), true, SPV_ENV_VULKAN_1_1},
+        {VK(1, 0), SPV(1, 2), true, SPV_ENV_VULKAN_1_1},
+        {VK(1, 0), SPV(1, 3), true, SPV_ENV_VULKAN_1_1},
+        {VK(1, 0), SPV(1, 4), true, SPV_ENV_VULKAN_1_1_SPIRV_1_4},
+        {VK(1, 0), SPV(1, 5), true, SPV_ENV_VULKAN_1_2},
+        {VK(1, 0), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0},
+        // Vulkan 1.1 cases
+        {VK(1, 1), SPV(1, 0), true, SPV_ENV_VULKAN_1_1},
+        {VK(1, 1), SPV(1, 1), true, SPV_ENV_VULKAN_1_1},
+        {VK(1, 1), SPV(1, 2), true, SPV_ENV_VULKAN_1_1},
+        {VK(1, 1), SPV(1, 3), true, SPV_ENV_VULKAN_1_1},
+        {VK(1, 1), SPV(1, 4), true, SPV_ENV_VULKAN_1_1_SPIRV_1_4},
+        {VK(1, 1), SPV(1, 5), true, SPV_ENV_VULKAN_1_2},
+        {VK(1, 1), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0},
+        // Vulkan 1.2 cases
+        {VK(1, 2), SPV(1, 0), true, SPV_ENV_VULKAN_1_2},
+        {VK(1, 2), SPV(1, 1), true, SPV_ENV_VULKAN_1_2},
+        {VK(1, 2), SPV(1, 2), true, SPV_ENV_VULKAN_1_2},
+        {VK(1, 2), SPV(1, 3), true, SPV_ENV_VULKAN_1_2},
+        {VK(1, 2), SPV(1, 4), true, SPV_ENV_VULKAN_1_2},
+        {VK(1, 2), SPV(1, 5), true, SPV_ENV_VULKAN_1_2},
+        {VK(1, 2), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0},
+        // Vulkan 1.3 cases
+        {VK(1, 3), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0},
+        // Vulkan 2.0 cases
+        {VK(2, 0), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0},
+        // Vulkan 99.0 cases
+        {VK(99, 0), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0},
+    }));
+
 }  // namespace
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_capability_test.cpp b/third_party/SPIRV-Tools/test/val/val_capability_test.cpp
index ae1d62b..098fa2f 100644
--- a/third_party/SPIRV-Tools/test/val/val_capability_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_capability_test.cpp
@@ -114,6 +114,8 @@
 using ValidateCapabilityOpenGL40 = spvtest::ValidateBase<CapTestParameter>;
 // Always assembles using Vulkan 1.1.
 using ValidateCapabilityVulkan11 = spvtest::ValidateBase<CapTestParameter>;
+// Always assembles using Vulkan 1.2.
+using ValidateCapabilityVulkan12 = spvtest::ValidateBase<CapTestParameter>;
 // Always assembles using WebGPU.
 using ValidateCapabilityWebGPU = spvtest::ValidateBase<CapTestParameter>;
 
@@ -217,6 +219,114 @@
   return *r;
 }
 
+const std::vector<std::string>& AllSpirV15Capabilities() {
+  static const auto r = new std::vector<std::string>{
+    "",
+    "Matrix",
+    "Shader",
+    "Geometry",
+    "Tessellation",
+    "Addresses",
+    "Linkage",
+    "Kernel",
+    "Vector16",
+    "Float16Buffer",
+    "Float16",
+    "Float64",
+    "Int64",
+    "Int64Atomics",
+    "ImageBasic",
+    "ImageReadWrite",
+    "ImageMipmap",
+    "Pipes",
+    "Groups",
+    "DeviceEnqueue",
+    "LiteralSampler",
+    "AtomicStorage",
+    "Int16",
+    "TessellationPointSize",
+    "GeometryPointSize",
+    "ImageGatherExtended",
+    "StorageImageMultisample",
+    "UniformBufferArrayDynamicIndexing",
+    "SampledImageArrayDynamicIndexing",
+    "StorageBufferArrayDynamicIndexing",
+    "StorageImageArrayDynamicIndexing",
+    "ClipDistance",
+    "CullDistance",
+    "ImageCubeArray",
+    "SampleRateShading",
+    "ImageRect",
+    "SampledRect",
+    "GenericPointer",
+    "Int8",
+    "InputAttachment",
+    "SparseResidency",
+    "MinLod",
+    "Sampled1D",
+    "Image1D",
+    "SampledCubeArray",
+    "SampledBuffer",
+    "ImageBuffer",
+    "ImageMSArray",
+    "StorageImageExtendedFormats",
+    "ImageQuery",
+    "DerivativeControl",
+    "InterpolationFunction",
+    "TransformFeedback",
+    "GeometryStreams",
+    "StorageImageReadWithoutFormat",
+    "StorageImageWriteWithoutFormat",
+    "MultiViewport",
+    "SubgroupDispatch",
+    "NamedBarrier",
+    "PipeStorage",
+    "GroupNonUniform",
+    "GroupNonUniformVote",
+    "GroupNonUniformArithmetic",
+    "GroupNonUniformBallot",
+    "GroupNonUniformShuffle",
+    "GroupNonUniformShuffleRelative",
+    "GroupNonUniformClustered",
+    "GroupNonUniformQuad",
+    "DrawParameters",
+    "StorageBuffer16BitAccess",
+    "StorageUniformBufferBlock16",
+    "UniformAndStorageBuffer16BitAccess",
+    "StorageUniform16",
+    "StoragePushConstant16",
+    "StorageInputOutput16",
+    "DeviceGroup",
+    "MultiView",
+    "VariablePointersStorageBuffer",
+    "VariablePointers",
+    "DenormPreserve",
+    "DenormFlushToZero",
+    "SignedZeroInfNanPreserve",
+    "RoundingModeRTE",
+    "RoundingModeRTZ",
+    // Omitted due to extra validation requirements on memory model.
+    //"VulkanMemoryModel",
+    //"VulkanMemoryModelDeviceScope",
+    "StorageBuffer8BitAccess",
+    "UniformAndStorageBuffer8BitAccess",
+    "StoragePushConstant8",
+    "ShaderViewportIndex",
+    "ShaderLayer",
+    "PhysicalStorageBufferAddresses",
+    "RuntimeDescriptorArray",
+    "UniformTexelBufferArrayDynamicIndexing",
+    "StorageTexelBufferArrayDynamicIndexing",
+    "UniformBufferArrayNonUniformIndexing",
+    "SampledImageArrayNonUniformIndexing",
+    "StorageBufferArrayNonUniformIndexing",
+    "StorageImageArrayNonUniformIndexing",
+    "InputAttachmentArrayNonUniformIndexing",
+    "UniformTexelBufferArrayNonUniformIndexing",
+    "StorageTexelBufferArrayNonUniformIndexing"};
+  return *r;
+}
+
 const std::vector<std::string>& AllSpirV10Capabilities() {
   static const auto r = new std::vector<std::string>{
     "",
@@ -390,6 +500,94 @@
   return *r;
 }
 
+const std::vector<std::string>& AllVulkan12Capabilities() {
+  static const auto r = new std::vector<std::string>{
+    "",
+    "Matrix",
+    "Shader",
+    "InputAttachment",
+    "Sampled1D",
+    "Image1D",
+    "SampledBuffer",
+    "ImageBuffer",
+    "ImageQuery",
+    "DerivativeControl",
+    "Geometry",
+    "Tessellation",
+    "Float16",
+    "Float64",
+    "Int64",
+    "Int64Atomics",
+    "Int16",
+    "TessellationPointSize",
+    "GeometryPointSize",
+    "ImageGatherExtended",
+    "StorageImageMultisample",
+    "UniformBufferArrayDynamicIndexing",
+    "SampledImageArrayDynamicIndexing",
+    "StorageBufferArrayDynamicIndexing",
+    "StorageImageArrayDynamicIndexing",
+    "ClipDistance",
+    "CullDistance",
+    "ImageCubeArray",
+    "SampleRateShading",
+    "Int8",
+    "SparseResidency",
+    "MinLod",
+    "SampledCubeArray",
+    "ImageMSArray",
+    "StorageImageExtendedFormats",
+    "InterpolationFunction",
+    "StorageImageReadWithoutFormat",
+    "StorageImageWriteWithoutFormat",
+    "MultiViewport",
+    "GroupNonUniform",
+    "GroupNonUniformVote",
+    "GroupNonUniformArithmetic",
+    "GroupNonUniformBallot",
+    "GroupNonUniformShuffle",
+    "GroupNonUniformShuffleRelative",
+    "GroupNonUniformClustered",
+    "GroupNonUniformQuad",
+    "DrawParameters",
+    "StorageBuffer16BitAccess",
+    "StorageUniformBufferBlock16",
+    "UniformAndStorageBuffer16BitAccess",
+    "StorageUniform16",
+    "StoragePushConstant16",
+    "StorageInputOutput16",
+    "DeviceGroup",
+    "MultiView",
+    "VariablePointersStorageBuffer",
+    "VariablePointers",
+    "TransformFeedback",
+    "GeometryStreams",
+    "DenormPreserve",
+    "DenormFlushToZero",
+    "SignedZeroInfNanPreserve",
+    "RoundingModeRTE",
+    "RoundingModeRTZ",
+    "VulkanMemoryModel",
+    "VulkanMemoryModelDeviceScope",
+    "StorageBuffer8BitAccess",
+    "UniformAndStorageBuffer8BitAccess",
+    "StoragePushConstant8",
+    "ShaderViewportIndex",
+    "ShaderLayer",
+    "PhysicalStorageBufferAddresses",
+    "RuntimeDescriptorArray",
+    "UniformTexelBufferArrayDynamicIndexing",
+    "StorageTexelBufferArrayDynamicIndexing",
+    "UniformBufferArrayNonUniformIndexing",
+    "SampledImageArrayNonUniformIndexing",
+    "StorageBufferArrayNonUniformIndexing",
+    "StorageImageArrayNonUniformIndexing",
+    "InputAttachmentArrayNonUniformIndexing",
+    "UniformTexelBufferArrayNonUniformIndexing",
+    "StorageTexelBufferArrayNonUniformIndexing"};
+  return *r;
+}
+
 const std::vector<std::string>& AllWebGPUCapabilities() {
   static const auto r = new std::vector<std::string>{
     "",
@@ -1640,6 +1838,25 @@
           AllVulkan11Capabilities())
 )));
 
+INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan12,
+                        Combine(
+                            // All capabilities to try.
+                            ValuesIn(AllSpirV15Capabilities()),
+                            Values(
+std::make_pair(std::string(kGLSL450MemoryModel) +
+          "OpEntryPoint Vertex %func \"shader\" \n" +
+          "OpDecorate %int0 BuiltIn PointSize\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+          AllVulkan12Capabilities()),
+std::make_pair(std::string(kGLSL450MemoryModel) +
+          "OpEntryPoint Vertex %func \"shader\" \n" +
+          "OpDecorate %int0 BuiltIn CullDistance\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+          AllVulkan12Capabilities())
+)));
+
 // TODO(umar): Selection Control
 // TODO(umar): Loop Control
 // TODO(umar): Function Control
@@ -1803,6 +2020,17 @@
   }
 }
 
+TEST_P(ValidateCapabilityVulkan12, Capability) {
+  const std::string capability = Capability(GetParam());
+  if (Exists(capability, SPV_ENV_VULKAN_1_2)) {
+    const std::string test_code = MakeAssembly(GetParam());
+    CompileSuccessfully(test_code, SPV_ENV_VULKAN_1_2);
+    ASSERT_EQ(ExpectedResult(GetParam()),
+              ValidateInstructions(SPV_ENV_VULKAN_1_2))
+        << test_code;
+  }
+}
+
 TEST_P(ValidateCapabilityOpenGL40, Capability) {
   const std::string capability = Capability(GetParam());
   if (Exists(capability, SPV_ENV_OPENGL_4_0)) {
diff --git a/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp b/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
index 8d8de37..4cf4029 100644
--- a/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
@@ -4187,6 +4187,115 @@
           "1[%loop], but its merge block 2[%continue] is not"));
 }
 
+TEST_F(ValidateCFG, ExitFromConstructWhoseHeaderIsAMerge) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%2 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%4 = OpUndef %int
+%bool = OpTypeBool
+%6 = OpUndef %bool
+%7 = OpFunction %void None %2
+%8 = OpLabel
+OpSelectionMerge %9 None
+OpSwitch %4 %10 0 %11
+%10 = OpLabel
+OpBranch %9
+%11 = OpLabel
+OpBranch %12
+%12 = OpLabel
+OpLoopMerge %13 %14 None
+OpBranch %15
+%15 = OpLabel
+OpSelectionMerge %16 None
+OpSwitch %4 %17 1 %18 2 %19
+%17 = OpLabel
+OpBranch %16
+%18 = OpLabel
+OpBranch %14
+%19 = OpLabel
+OpBranch %16
+%16 = OpLabel
+OpBranch %14
+%14 = OpLabel
+OpBranchConditional %6 %12 %13
+%13 = OpLabel
+OpSelectionMerge %20 None
+OpBranchConditional %6 %21 %20
+%21 = OpLabel
+OpBranch %9
+%20 = OpLabel
+OpBranch %10
+%9 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, ExitFromConstructWhoseHeaderIsAMerge2) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %6 = OpUndef %int
+       %bool = OpTypeBool
+          %8 = OpUndef %bool
+          %2 = OpFunction %void None %4
+          %9 = OpLabel
+               OpSelectionMerge %10 None
+               OpSwitch %6 %11 0 %12
+         %11 = OpLabel
+               OpBranch %10
+         %12 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+               OpLoopMerge %14 %15 None
+               OpBranch %16
+         %16 = OpLabel
+               OpSelectionMerge %17 None
+               OpSwitch %6 %18 1 %19 2 %20
+         %18 = OpLabel
+               OpBranch %17
+         %19 = OpLabel
+               OpBranch %15
+         %20 = OpLabel
+               OpBranch %17
+         %17 = OpLabel
+               OpBranch %15
+         %15 = OpLabel
+               OpBranchConditional %8 %13 %14
+         %14 = OpLabel
+               OpSelectionMerge %21 None
+               OpBranchConditional %8 %22 %21
+         %22 = OpLabel
+               OpSelectionMerge %23 None
+               OpBranchConditional %8 %24 %23
+         %24 = OpLabel
+               OpBranch %10
+         %23 = OpLabel
+               OpBranch %21
+         %21 = OpLabel
+               OpBranch %11
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_ext_inst_test.cpp b/third_party/SPIRV-Tools/test/val/val_ext_inst_test.cpp
index 67df43d..01df796 100644
--- a/third_party/SPIRV-Tools/test/val/val_ext_inst_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_ext_inst_test.cpp
@@ -33,6 +33,9 @@
 using ::testing::Not;
 
 using ValidateExtInst = spvtest::ValidateBase<bool>;
+using ValidateOldDebugInfo = spvtest::ValidateBase<std::string>;
+using ValidateOpenCL100DebugInfo = spvtest::ValidateBase<std::string>;
+using ValidateLocalDebugInfoOutOfFunction = spvtest::ValidateBase<std::string>;
 using ValidateGlslStd450SqrtLike = spvtest::ValidateBase<std::string>;
 using ValidateGlslStd450FMinLike = spvtest::ValidateBase<std::string>;
 using ValidateGlslStd450FClampLike = spvtest::ValidateBase<std::string>;
@@ -460,6 +463,462 @@
   return ss.str();
 }
 
+std::string GenerateShaderCodeForDebugInfo(
+    const std::string& op_string_instructions,
+    const std::string& op_const_instructions,
+    const std::string& debug_instructions_before_main, const std::string& body,
+    const std::string& capabilities_and_extensions = "",
+    const std::string& execution_model = "Fragment") {
+  std::ostringstream ss;
+  ss << R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Float64
+OpCapability Int16
+OpCapability Int64
+)";
+
+  ss << capabilities_and_extensions;
+  ss << "%extinst = OpExtInstImport \"GLSL.std.450\"\n";
+  ss << "OpMemoryModel Logical GLSL450\n";
+  ss << "OpEntryPoint " << execution_model << " %main \"main\""
+     << " %f32_output"
+     << " %f32vec2_output"
+     << " %u32_output"
+     << " %u32vec2_output"
+     << " %u64_output"
+     << " %f32_input"
+     << " %f32vec2_input"
+     << " %u32_input"
+     << " %u32vec2_input"
+     << " %u64_input"
+     << "\n";
+  if (execution_model == "Fragment") {
+    ss << "OpExecutionMode %main OriginUpperLeft\n";
+  }
+
+  ss << op_string_instructions;
+
+  ss << R"(
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bool = OpTypeBool
+%f16 = OpTypeFloat 16
+%f32 = OpTypeFloat 32
+%f64 = OpTypeFloat 64
+%u32 = OpTypeInt 32 0
+%s32 = OpTypeInt 32 1
+%u64 = OpTypeInt 64 0
+%s64 = OpTypeInt 64 1
+%u16 = OpTypeInt 16 0
+%s16 = OpTypeInt 16 1
+%f32vec2 = OpTypeVector %f32 2
+%f32vec3 = OpTypeVector %f32 3
+%f32vec4 = OpTypeVector %f32 4
+%f64vec2 = OpTypeVector %f64 2
+%f64vec3 = OpTypeVector %f64 3
+%f64vec4 = OpTypeVector %f64 4
+%u32vec2 = OpTypeVector %u32 2
+%u32vec3 = OpTypeVector %u32 3
+%s32vec2 = OpTypeVector %s32 2
+%u32vec4 = OpTypeVector %u32 4
+%s32vec4 = OpTypeVector %s32 4
+%u64vec2 = OpTypeVector %u64 2
+%s64vec2 = OpTypeVector %s64 2
+%f64mat22 = OpTypeMatrix %f64vec2 2
+%f32mat22 = OpTypeMatrix %f32vec2 2
+%f32mat23 = OpTypeMatrix %f32vec2 3
+%f32mat32 = OpTypeMatrix %f32vec3 2
+%f32mat33 = OpTypeMatrix %f32vec3 3
+
+%f32_0 = OpConstant %f32 0
+%f32_1 = OpConstant %f32 1
+%f32_2 = OpConstant %f32 2
+%f32_3 = OpConstant %f32 3
+%f32_4 = OpConstant %f32 4
+%f32_h = OpConstant %f32 0.5
+%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1
+%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2
+%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2
+%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3
+%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3
+%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4
+
+%f64_0 = OpConstant %f64 0
+%f64_1 = OpConstant %f64 1
+%f64_2 = OpConstant %f64 2
+%f64_3 = OpConstant %f64 3
+%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1
+%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2
+%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3
+
+%f16_0 = OpConstant %f16 0
+%f16_1 = OpConstant %f16 1
+%f16_h = OpConstant %f16 0.5
+
+%u32_0 = OpConstant %u32 0
+%u32_1 = OpConstant %u32 1
+%u32_2 = OpConstant %u32 2
+%u32_3 = OpConstant %u32 3
+
+%s32_0 = OpConstant %s32 0
+%s32_1 = OpConstant %s32 1
+%s32_2 = OpConstant %s32 2
+%s32_3 = OpConstant %s32 3
+
+%u64_0 = OpConstant %u64 0
+%u64_1 = OpConstant %u64 1
+%u64_2 = OpConstant %u64 2
+%u64_3 = OpConstant %u64 3
+
+%s64_0 = OpConstant %s64 0
+%s64_1 = OpConstant %s64 1
+%s64_2 = OpConstant %s64 2
+%s64_3 = OpConstant %s64 3
+)";
+
+  ss << op_const_instructions;
+
+  ss << R"(
+%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1
+%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1
+
+%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2
+%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2
+
+%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3
+%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3
+
+%s64vec2_01 = OpConstantComposite %s64vec2 %s64_0 %s64_1
+%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1
+
+%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12
+%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12
+
+%f32_ptr_output = OpTypePointer Output %f32
+%f32vec2_ptr_output = OpTypePointer Output %f32vec2
+
+%u32_ptr_output = OpTypePointer Output %u32
+%u32vec2_ptr_output = OpTypePointer Output %u32vec2
+
+%u64_ptr_output = OpTypePointer Output %u64
+
+%f32_output = OpVariable %f32_ptr_output Output
+%f32vec2_output = OpVariable %f32vec2_ptr_output Output
+
+%u32_output = OpVariable %u32_ptr_output Output
+%u32vec2_output = OpVariable %u32vec2_ptr_output Output
+
+%u64_output = OpVariable %u64_ptr_output Output
+
+%f32_ptr_input = OpTypePointer Input %f32
+%f32vec2_ptr_input = OpTypePointer Input %f32vec2
+
+%u32_ptr_input = OpTypePointer Input %u32
+%u32vec2_ptr_input = OpTypePointer Input %u32vec2
+
+%u64_ptr_input = OpTypePointer Input %u64
+
+%f32_ptr_function = OpTypePointer Function %f32
+
+%f32_input = OpVariable %f32_ptr_input Input
+%f32vec2_input = OpVariable %f32vec2_ptr_input Input
+
+%u32_input = OpVariable %u32_ptr_input Input
+%u32vec2_input = OpVariable %u32vec2_ptr_input Input
+
+%u64_input = OpVariable %u64_ptr_input Input
+
+%u32_ptr_function = OpTypePointer Function %u32
+
+%struct_f16_u16 = OpTypeStruct %f16 %u16
+%struct_f32_f32 = OpTypeStruct %f32 %f32
+%struct_f32_f32_f32 = OpTypeStruct %f32 %f32 %f32
+%struct_f32_u32 = OpTypeStruct %f32 %u32
+%struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32
+%struct_u32_f32 = OpTypeStruct %u32 %f32
+%struct_u32_u32 = OpTypeStruct %u32 %u32
+%struct_f32_f64 = OpTypeStruct %f32 %f64
+%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2
+%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2
+)";
+
+  ss << debug_instructions_before_main;
+
+  ss << R"(
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+)";
+
+  ss << body;
+
+  ss << R"(
+OpReturn
+OpFunctionEnd)";
+
+  return ss.str();
+}
+
+TEST_F(ValidateOldDebugInfo, UseDebugInstructionOutOfFunction) {
+  const std::string src = R"(
+%code = OpString "main() {}"
+)";
+
+  const std::string dbg_inst = R"(
+%cu = OpExtInst %void %DbgExt DebugCompilationUnit %code 1 1
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "DebugInfo"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+                                                     extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, UseDebugInstructionOutOfFunction) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+  const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+                                                     extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugSourceInFunction) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+  const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst,
+                                                     extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Debug info extension instructions other than DebugScope, "
+                "DebugNoScope, DebugDeclare, DebugValue must appear between "
+                "section 9 (types, constants, global variables) and section 10 "
+                "(function declarations)"));
+}
+
+TEST_P(ValidateLocalDebugInfoOutOfFunction, OpenCLDebugInfo100DebugScope) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%void_name = OpString "void"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+%int_name = OpString "int"
+%foo_name = OpString "foo"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%void_info = OpExtInst %void %DbgExt DebugTypeBasic %void_name %u32_0 Unspecified
+%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_0 Signed
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void_info %void_info
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %int_info %dbg_src 1 1 %main_info FlagIsLocal
+%expr = OpExtInst %void %DbgExt DebugExpression
+)";
+
+  const std::string body = R"(
+%foo = OpVariable %u32_ptr_function Function
+%foo_val = OpLoad %u32 %foo
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header + GetParam(), body, extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue "
+                        "of debug info extension must appear in a function "
+                        "body"));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AllLocalDebugInfo, ValidateLocalDebugInfoOutOfFunction,
+    ::testing::ValuesIn(std::vector<std::string>{
+        "%main_scope = OpExtInst %void %DbgExt DebugScope %main_info",
+        "%no_scope = OpExtInst %void %DbgExt DebugNoScope",
+    }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionForwardReference) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%void_name = OpString "void"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%void_info = OpExtInst %void %DbgExt DebugTypeBasic %void_name %u32_0 Unspecified
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void_info %void_info
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main
+)";
+
+  const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %main_info
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugScopeBeforeOpVariableInFunction) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+  float foo;
+  return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main
+)";
+
+  const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %main_info
+%foo = OpVariable %f32_ptr_function Function
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, body, extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeForwardReference) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+  float4 pos : SV_POSITION;
+  float4 color : COLOR;
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_color_name = OpString "color : COLOR"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
+%VS_OUTPUT_color_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_color_name %v4float_info %dbg_src 3 3 %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeMissingReference) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+  float4 pos : SV_POSITION;
+  float4 color : COLOR;
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_color_name = OpString "color : COLOR"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("forward referenced IDs have not been defined"));
+}
+
 TEST_P(ValidateGlslStd450SqrtLike, Success) {
   const std::string ext_inst_name = GetParam();
   std::ostringstream ss;
diff --git a/third_party/SPIRV-Tools/test/val/val_image_test.cpp b/third_party/SPIRV-Tools/test/val/val_image_test.cpp
index 433c9fa..570dd16 100644
--- a/third_party/SPIRV-Tools/test/val/val_image_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_image_test.cpp
@@ -4841,6 +4841,169 @@
   EXPECT_THAT(getDiagnosticString(), Eq(""));
 }
 
+TEST_F(ValidateImage, ReadLodAMDSuccess1) {
+  const std::string body = R"(
+%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
+%res1 = OpImageRead %u32vec4 %img %u32vec2_01 Lod %u32_0
+)";
+
+  const std::string extra =
+      "\nOpCapability StorageImageReadWithoutFormat\n"
+      "OpCapability ImageReadWriteLodAMD\n"
+      "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n";
+  CompileSuccessfully(
+      GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1),
+      SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
+}
+
+TEST_F(ValidateImage, ReadLodAMDSuccess2) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_1d_0002_rgba32f %uniform_image_f32_1d_0002_rgba32f
+%res1 = OpImageRead %f32vec4 %img %u32vec2_01 Lod %u32_0
+)";
+
+  const std::string extra =
+      "\nOpCapability Image1D\n"
+      "OpCapability ImageReadWriteLodAMD\n"
+      "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n";
+  CompileSuccessfully(
+      GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1),
+      SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
+}
+
+TEST_F(ValidateImage, ReadLodAMDSuccess3) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f
+%res1 = OpImageRead %f32vec4 %img %u32vec3_012 Lod %u32_0
+)";
+
+  const std::string extra =
+      "\nOpCapability ImageCubeArray\n"
+      "OpCapability ImageReadWriteLodAMD\n"
+      "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n";
+  CompileSuccessfully(
+      GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1),
+      SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
+}
+
+TEST_F(ValidateImage, ReadLodAMDNeedCapability) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f
+%res1 = OpImageRead %f32vec4 %img %u32vec3_012 Lod %u32_0
+)";
+
+  const std::string extra = "\nOpCapability ImageCubeArray\n";
+  CompileSuccessfully(
+      GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1),
+      SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Image Operand Lod can only be used with ExplicitLod "
+                        "opcodes and OpImageFetch"));
+}
+
+TEST_F(ValidateImage, WriteLodAMDSuccess1) {
+  const std::string body = R"(
+%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
+OpImageWrite %img %u32vec2_01 %u32vec4_0123 Lod %u32_0
+)";
+
+  const std::string extra =
+      "\nOpCapability StorageImageWriteWithoutFormat\n"
+      "OpCapability ImageReadWriteLodAMD\n"
+      "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n";
+  CompileSuccessfully(
+      GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1),
+      SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
+}
+
+TEST_F(ValidateImage, WriteLodAMDSuccess2) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_1d_0002_rgba32f %uniform_image_f32_1d_0002_rgba32f
+OpImageWrite %img %u32_1 %f32vec4_0000 Lod %u32_0
+)";
+
+  const std::string extra =
+      "\nOpCapability Image1D\n"
+      "OpCapability ImageReadWriteLodAMD\n"
+      "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n";
+  CompileSuccessfully(
+      GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1),
+      SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
+}
+
+TEST_F(ValidateImage, WriteLodAMDSuccess3) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f
+OpImageWrite %img %u32vec3_012 %f32vec4_0000 Lod %u32_0
+)";
+
+  const std::string extra =
+      "\nOpCapability ImageCubeArray\n"
+      "OpCapability ImageReadWriteLodAMD\n"
+      "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n";
+  CompileSuccessfully(
+      GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1),
+      SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
+}
+
+TEST_F(ValidateImage, WriteLodAMDNeedCapability) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f
+OpImageWrite %img %u32vec3_012 %f32vec4_0000 Lod %u32_0
+)";
+
+  const std::string extra = "\nOpCapability ImageCubeArray\n";
+  CompileSuccessfully(
+      GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1),
+      SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Image Operand Lod can only be used with ExplicitLod "
+                        "opcodes and OpImageFetch"));
+}
+
+TEST_F(ValidateImage, SparseReadLodAMDSuccess) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002
+%res1 = OpImageSparseRead %struct_u32_f32vec4 %img %u32vec2_01 Lod %u32_0
+)";
+
+  const std::string extra =
+      "\nOpCapability StorageImageReadWithoutFormat\n"
+      "OpCapability ImageReadWriteLodAMD\n"
+      "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n";
+  CompileSuccessfully(
+      GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1),
+      SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
+}
+
+TEST_F(ValidateImage, SparseReadLodAMDNeedCapability) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002
+%res1 = OpImageSparseRead %struct_u32_f32vec4 %img %u32vec2_01 Lod %u32_0
+)";
+
+  const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
+  CompileSuccessfully(
+      GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1),
+      SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Image Operand Lod can only be used with ExplicitLod "
+                        "opcodes and OpImageFetch"));
+}
+
 // No negative tests for ZeroExtend since we don't truly know the
 // texel format.
 
diff --git a/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp b/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp
index cb98914..718d038 100644
--- a/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp
+++ b/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp
@@ -23,6 +23,7 @@
 
 #include "source/fuzz/force_render_red.h"
 #include "source/fuzz/fuzzer.h"
+#include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/replayer.h"
 #include "source/fuzz/shrinker.h"
@@ -75,7 +76,8 @@
   printf(
       R"(%s - Fuzzes an equivalent SPIR-V binary based on a given binary.
 
-USAGE: %s [options] <input.spv> -o <output.spv>
+USAGE: %s [options] <input.spv> -o <output.spv> \
+  --donors=<donors.txt>
 USAGE: %s [options] <input.spv> -o <output.spv> \
   --shrink=<input.transformations> -- <interestingness_test> [args...]
 
@@ -100,6 +102,11 @@
 
   -h, --help
                Print this help.
+  --donors=
+               File specifying a series of donor files, one per line.  Must be
+               provided if the tool is invoked in fuzzing mode; incompatible
+               with replay and shrink modes.  The file should be empty if no
+               donors are to be used.
   --force-render-red
                Transforms the input shader into a shader that writes red to the
                output buffer, and then captures the original shader as the body
@@ -154,7 +161,7 @@
 }
 
 FuzzStatus ParseFlags(int argc, const char** argv, std::string* in_binary_file,
-                      std::string* out_binary_file,
+                      std::string* out_binary_file, std::string* donors_file,
                       std::string* replay_transformations_file,
                       std::vector<std::string>* interestingness_test,
                       std::string* shrink_transformations_file,
@@ -181,6 +188,9 @@
           PrintUsage(argv[0]);
           return {FuzzActions::STOP, 1};
         }
+      } else if (0 == strncmp(cur_arg, "--donors=", sizeof("--donors=") - 1)) {
+        const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
+        *donors_file = std::string(split_flag.second);
       } else if (0 == strncmp(cur_arg, "--force-render-red",
                               sizeof("--force-render-red") - 1)) {
         force_render_red = true;
@@ -285,6 +295,18 @@
     return {FuzzActions::STOP, 1};
   }
 
+  if (!replay_transformations_file->empty() ||
+      !shrink_transformations_file->empty()) {
+    // Donors should not be provided when replaying or shrinking: they only make
+    // sense during fuzzing.
+    if (!donors_file->empty()) {
+      spvtools::Error(FuzzDiagnostic, nullptr, {},
+                      "The --donors argument is not compatible with --replay "
+                      "nor --shrink.");
+      return {FuzzActions::STOP, 1};
+    }
+  }
+
   if (!replay_transformations_file->empty()) {
     // A replay transformations file was given, thus the tool is being invoked
     // in replay mode.
@@ -305,6 +327,12 @@
     return {FuzzActions::SHRINK, 0};
   }
 
+  // The tool is being invoked in fuzz mode.
+  if (donors_file->empty()) {
+    spvtools::Error(FuzzDiagnostic, nullptr, {},
+                    "Fuzzing requires that the --donors option is used.");
+    return {FuzzActions::STOP, 1};
+  }
   return {FuzzActions::FUZZ, 0};
 }
 
@@ -408,18 +436,44 @@
           spv_const_fuzzer_options fuzzer_options,
           const std::vector<uint32_t>& binary_in,
           const spvtools::fuzz::protobufs::FactSequence& initial_facts,
-          std::vector<uint32_t>* binary_out,
+          const std::string& donors, std::vector<uint32_t>* binary_out,
           spvtools::fuzz::protobufs::TransformationSequence*
               transformations_applied) {
+  auto message_consumer = spvtools::utils::CLIMessageConsumer;
+
+  std::vector<spvtools::fuzz::fuzzerutil::ModuleSupplier> donor_suppliers;
+
+  std::ifstream donors_file(donors);
+  if (!donors_file) {
+    spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error opening donors file");
+    return false;
+  }
+  std::string donor_filename;
+  while (std::getline(donors_file, donor_filename)) {
+    donor_suppliers.emplace_back(
+        [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)) {
+            return nullptr;
+          }
+          return spvtools::BuildModule(target_env, message_consumer,
+                                       donor_binary.data(),
+                                       donor_binary.size());
+        });
+  }
+
   spvtools::fuzz::Fuzzer fuzzer(
       target_env,
       fuzzer_options->has_random_seed
           ? fuzzer_options->random_seed
           : static_cast<uint32_t>(std::random_device()()),
       fuzzer_options->fuzzer_pass_validation_enabled);
-  fuzzer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
+  fuzzer.SetMessageConsumer(message_consumer);
   auto fuzz_result_status =
-      fuzzer.Run(binary_in, initial_facts, binary_out, transformations_applied);
+      fuzzer.Run(binary_in, initial_facts, donor_suppliers, binary_out,
+                 transformations_applied);
   if (fuzz_result_status !=
       spvtools::fuzz::Fuzzer::FuzzerResultStatus::kComplete) {
     spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error running fuzzer");
@@ -452,6 +506,7 @@
 int main(int argc, const char** argv) {
   std::string in_binary_file;
   std::string out_binary_file;
+  std::string donors_file;
   std::string replay_transformations_file;
   std::vector<std::string> interestingness_test;
   std::string shrink_transformations_file;
@@ -460,7 +515,7 @@
   spvtools::FuzzerOptions fuzzer_options;
 
   FuzzStatus status = ParseFlags(
-      argc, argv, &in_binary_file, &out_binary_file,
+      argc, argv, &in_binary_file, &out_binary_file, &donors_file,
       &replay_transformations_file, &interestingness_test,
       &shrink_transformations_file, &shrink_temp_file_prefix, &fuzzer_options);
 
@@ -507,7 +562,7 @@
       break;
     case FuzzActions::FUZZ:
       if (!Fuzz(target_env, fuzzer_options, binary_in, initial_facts,
-                &binary_out, &transformations_applied)) {
+                donors_file, &binary_out, &transformations_applied)) {
         return 1;
       }
       break;
diff --git a/third_party/SPIRV-Tools/tools/opt/opt.cpp b/third_party/SPIRV-Tools/tools/opt/opt.cpp
index 0ff937a..658bd5b 100644
--- a/third_party/SPIRV-Tools/tools/opt/opt.cpp
+++ b/third_party/SPIRV-Tools/tools/opt/opt.cpp
@@ -111,7 +111,7 @@
   printf(R"(
   --amd-ext-to-khr
                Replaces the extensions VK_AMD_shader_ballot, VK_AMD_gcn_shader,
-               and VK_AMD_shader_trinary_minmax with equivalant code using core
+               and VK_AMD_shader_trinary_minmax with equivalent code using core
                instructions and capabilities.)");
   printf(R"(
   --ccp
@@ -173,7 +173,7 @@
   printf(R"(
   --eliminate-dead-branches
                Convert conditional branches with constant condition to the
-               indicated unconditional brranch. Delete all resulting dead
+               indicated unconditional branch. Delete all resulting dead
                code. Performed only on entry point call tree functions.)");
   printf(R"(
   --eliminate-dead-code-aggressive
@@ -302,7 +302,7 @@
                the optimization is above the threshold.)");
   printf(R"(
   --max-id-bound=<n>
-               Sets the maximum value for the id bound for the moudle.  The
+               Sets the maximum value for the id bound for the module.  The
                default is the minimum value for this limit, 0x3FFFFF.  See
                section 2.17 of the Spir-V specification.)");
   printf(R"(
@@ -428,7 +428,7 @@
   --scalar-replacement[=<n>]
                Replace aggregate function scope variables that are only accessed
                via their elements with new function variables representing each
-               element.  <n> is a limit on the size of the aggragates that will
+               element.  <n> is a limit on the size of the aggregates that will
                be replaced.  0 means there is no limit.  The default value is
                100.)");
   printf(R"(
@@ -447,7 +447,7 @@
   --split-invalid-unreachable
                Attempts to legalize for WebGPU cases where an unreachable
                merge-block is also a continue-target by splitting it into two
-               seperate blocks. There exist legal, for Vulkan, instances of this
+               separate blocks. There exist legal, for Vulkan, instances of this
                pattern that cannot be converted into legal WebGPU, so this
                conversion may not succeed.)");
   printf(R"(
@@ -472,7 +472,7 @@
   printf(R"(
   --target-env=<env>
                Set the target environment. Without this flag the target
-               enviroment defaults to spv1.3. <env> must be one of
+               environment defaults to spv1.5. <env> must be one of
                {%s})",
          target_env_list.c_str());
   printf(R"(
diff --git a/third_party/SPIRV-Tools/utils/check_copyright.py b/third_party/SPIRV-Tools/utils/check_copyright.py
index 969371d..2d288a1 100755
--- a/third_party/SPIRV-Tools/utils/check_copyright.py
+++ b/third_party/SPIRV-Tools/utils/check_copyright.py
@@ -32,9 +32,9 @@
            'Google LLC',
            'Pierre Moreau',
            'Samsung Inc']
-CURRENT_YEAR='2019'
+CURRENT_YEAR='2020'
 
-YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2017-2019|2018|2019)'
+YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2017-2019|2018|2019|2020)'
 COPYRIGHT_RE = re.compile(
         'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS)))
 
diff --git a/third_party/SPIRV-Tools/utils/generate_grammar_tables.py b/third_party/SPIRV-Tools/utils/generate_grammar_tables.py
index 06fcf94..2a67733 100755
--- a/third_party/SPIRV-Tools/utils/generate_grammar_tables.py
+++ b/third_party/SPIRV-Tools/utils/generate_grammar_tables.py
@@ -765,6 +765,11 @@
                         help='prefix for operand kinds (to disambiguate operand type enums)')
     args = parser.parse_args()
 
+    # The GN build system needs this because it doesn't handle quoting
+    # empty string arguments well.
+    if args.vendor_operand_kind_prefix == "...nil...":
+        args.vendor_operand_kind_prefix = ""
+
     if (args.core_insts_output is None) != \
             (args.operand_kinds_output is None):
         print('error: --core-insts-output and --operand-kinds-output '
@@ -774,8 +779,8 @@
          args.extinst_debuginfo_grammar and
          args.extinst_cldebuginfo100_grammar):
         print('error: --operand-kinds-output requires --spirv-core-grammar '
-              'and --exinst-debuginfo-grammar '
-              'and --exinst-cldebuginfo100-grammar')
+              'and --extinst-debuginfo-grammar '
+              'and --extinst-cldebuginfo100-grammar')
         exit(1)
     if (args.glsl_insts_output is None) != \
             (args.extinst_glsl_grammar is None):
diff --git a/third_party/SPIRV-Tools/utils/vscode/spirv.json b/third_party/SPIRV-Tools/utils/vscode/spirv.json
index 30573d4..7999b52 100644
--- a/third_party/SPIRV-Tools/utils/vscode/spirv.json
+++ b/third_party/SPIRV-Tools/utils/vscode/spirv.json
@@ -33,7 +33,14 @@
 		{ "include": "#ValueEnum_GroupOperation" },
 		{ "include": "#ValueEnum_KernelEnqueueFlags" },
 		{ "include": "#ValueEnum_Capability" },
+		{ "include": "#BitEnum_DebugInfoFlags" },
+		{ "include": "#ValueEnum_DebugBaseTypeAttributeEncoding" },
+		{ "include": "#ValueEnum_DebugCompositeType" },
+		{ "include": "#ValueEnum_DebugTypeQualifier" },
+		{ "include": "#ValueEnum_DebugOperation" },
+		{ "include": "#ValueEnum_DebugImportedEntity" },
 		{ "include": "#opcode" },
+		{ "include": "#extopcode" },
 		{ "include": "#identifier" },
 		{ "include": "#number" },
 		{ "include": "#string" },
@@ -161,10 +168,38 @@
 			"match": "\\b(Matrix|Shader|Geometry|Tessellation|Addresses|Linkage|Kernel|Vector16|Float16Buffer|Float16|Float64|Int64|Int64Atomics|ImageBasic|ImageReadWrite|ImageMipmap|Pipes|Groups|DeviceEnqueue|LiteralSampler|AtomicStorage|Int16|TessellationPointSize|GeometryPointSize|ImageGatherExtended|StorageImageMultisample|UniformBufferArrayDynamicIndexing|SampledImageArrayDynamicIndexing|StorageBufferArrayDynamicIndexing|StorageImageArrayDynamicIndexing|ClipDistance|CullDistance|ImageCubeArray|SampleRateShading|ImageRect|SampledRect|GenericPointer|Int8|InputAttachment|SparseResidency|MinLod|Sampled1D|Image1D|SampledCubeArray|SampledBuffer|ImageBuffer|ImageMSArray|StorageImageExtendedFormats|ImageQuery|DerivativeControl|InterpolationFunction|TransformFeedback|GeometryStreams|StorageImageReadWithoutFormat|StorageImageWriteWithoutFormat|MultiViewport|SubgroupDispatch|NamedBarrier|PipeStorage|GroupNonUniform|GroupNonUniformVote|GroupNonUniformArithmetic|GroupNonUniformBallot|GroupNonUniformShuffle|GroupNonUniformShuffleRelative|GroupNonUniformClustered|GroupNonUniformQuad|ShaderLayer|ShaderViewportIndex|SubgroupBallotKHR|DrawParameters|SubgroupVoteKHR|StorageBuffer16BitAccess|StorageUniformBufferBlock16|UniformAndStorageBuffer16BitAccess|StorageUniform16|StoragePushConstant16|StorageInputOutput16|DeviceGroup|MultiView|VariablePointersStorageBuffer|VariablePointers|AtomicStorageOps|SampleMaskPostDepthCoverage|StorageBuffer8BitAccess|UniformAndStorageBuffer8BitAccess|StoragePushConstant8|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|Float16ImageAMD|ImageGatherBiasLodAMD|FragmentMaskAMD|StencilExportEXT|ImageReadWriteLodAMD|ShaderClockKHR|SampleMaskOverrideCoverageNV|GeometryShaderPassthroughNV|ShaderViewportIndexLayerEXT|ShaderViewportIndexLayerNV|ShaderViewportMaskNV|ShaderStereoViewNV|PerViewAttributesNV|FragmentFullyCoveredEXT|MeshShadingNV|ImageFootprintNV|FragmentBarycentricNV|ComputeDerivativeGroupQuadsNV|FragmentDensityEXT|ShadingRateNV|GroupNonUniformPartitionedNV|ShaderNonUniform|ShaderNonUniformEXT|RuntimeDescriptorArray|RuntimeDescriptorArrayEXT|InputAttachmentArrayDynamicIndexing|InputAttachmentArrayDynamicIndexingEXT|UniformTexelBufferArrayDynamicIndexing|UniformTexelBufferArrayDynamicIndexingEXT|StorageTexelBufferArrayDynamicIndexing|StorageTexelBufferArrayDynamicIndexingEXT|UniformBufferArrayNonUniformIndexing|UniformBufferArrayNonUniformIndexingEXT|SampledImageArrayNonUniformIndexing|SampledImageArrayNonUniformIndexingEXT|StorageBufferArrayNonUniformIndexing|StorageBufferArrayNonUniformIndexingEXT|StorageImageArrayNonUniformIndexing|StorageImageArrayNonUniformIndexingEXT|InputAttachmentArrayNonUniformIndexing|InputAttachmentArrayNonUniformIndexingEXT|UniformTexelBufferArrayNonUniformIndexing|UniformTexelBufferArrayNonUniformIndexingEXT|StorageTexelBufferArrayNonUniformIndexing|StorageTexelBufferArrayNonUniformIndexingEXT|RayTracingNV|VulkanMemoryModel|VulkanMemoryModelKHR|VulkanMemoryModelDeviceScope|VulkanMemoryModelDeviceScopeKHR|PhysicalStorageBufferAddresses|PhysicalStorageBufferAddressesEXT|ComputeDerivativeGroupLinearNV|CooperativeMatrixNV|FragmentShaderSampleInterlockEXT|FragmentShaderShadingRateInterlockEXT|ShaderSMBuiltinsNV|FragmentShaderPixelInterlockEXT|DemoteToHelperInvocationEXT|SubgroupShuffleINTEL|SubgroupBufferBlockIOINTEL|SubgroupImageBlockIOINTEL|SubgroupImageMediaBlockIOINTEL|IntegerFunctions2INTEL|SubgroupAvcMotionEstimationINTEL|SubgroupAvcMotionEstimationIntraINTEL|SubgroupAvcMotionEstimationChromaINTEL)\\b",
 			"name": "keyword.spirv"
 		},
+		"BitEnum_DebugInfoFlags": {
+			"match": "\\b(FlagIsProtected|FlagIsPrivate|FlagIsPublic|FlagIsLocal|FlagIsDefinition|FlagFwdDecl|FlagArtificial|FlagExplicit|FlagPrototyped|FlagObjectPointer|FlagStaticMember|FlagIndirectVariable|FlagLValueReference|FlagRValueReference|FlagIsOptimized|FlagIsEnumClass|FlagTypePassByValue|FlagTypePassByReference)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_DebugBaseTypeAttributeEncoding": {
+			"match": "\\b(Unspecified|Address|Boolean|Float|Signed|SignedChar|Unsigned|UnsignedChar)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_DebugCompositeType": {
+			"match": "\\b(Class|Structure|Union)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_DebugTypeQualifier": {
+			"match": "\\b(ConstType|VolatileType|RestrictType|AtomicType)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_DebugOperation": {
+			"match": "\\b(Deref|Plus|Minus|PlusUconst|BitPiece|Swap|Xderef|StackValue|Constu|Fragment)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_DebugImportedEntity": {
+			"match": "\\b(ImportedModule|ImportedDeclaration)\\b",
+			"name": "keyword.spirv"
+		},
 		"opcode": {
 			"match": "(Op[a-zA-Z]+)",
 			"name": "entity.name.function.spirv"
 		},
+		"extopcode": {
+			"match": "(Round|RoundEven|Trunc|FAbs|SAbs|FSign|SSign|Floor|Ceil|Fract|Radians|Degrees|Sin|Cos|Tan|Asin|Acos|Atan|Sinh|Cosh|Tanh|Asinh|Acosh|Atanh|Atan2|Pow|Exp|Log|Exp2|Log2|Sqrt|InverseSqrt|Determinant|MatrixInverse|Modf|ModfStruct|FMin|UMin|SMin|FMax|UMax|SMax|FClamp|UClamp|SClamp|FMix|IMix|Step|SmoothStep|Fma|Frexp|FrexpStruct|Ldexp|PackSnorm4x8|PackUnorm4x8|PackSnorm2x16|PackUnorm2x16|PackHalf2x16|PackDouble2x32|UnpackSnorm2x16|UnpackUnorm2x16|UnpackHalf2x16|UnpackSnorm4x8|UnpackUnorm4x8|UnpackDouble2x32|Length|Distance|Cross|Normalize|FaceForward|Reflect|Refract|FindILsb|FindSMsb|FindUMsb|InterpolateAtCentroid|InterpolateAtSample|InterpolateAtOffset|NMin|NMax|NClamp|acos|acosh|acospi|asin|asinh|asinpi|atan|atan2|atanh|atanpi|atan2pi|cbrt|ceil|copysign|cos|cosh|cospi|erfc|erf|exp|exp2|exp10|expm1|fabs|fdim|floor|fma|fmax|fmin|fmod|fract|frexp|hypot|ilogb|ldexp|lgamma|lgamma_r|log|log2|log10|log1p|logb|mad|maxmag|minmag|modf|nan|nextafter|pow|pown|powr|remainder|remquo|rint|rootn|round|rsqrt|sin|sincos|sinh|sinpi|sqrt|tan|tanh|tanpi|tgamma|trunc|half_cos|half_divide|half_exp|half_exp2|half_exp10|half_log|half_log2|half_log10|half_powr|half_recip|half_rsqrt|half_sin|half_sqrt|half_tan|native_cos|native_divide|native_exp|native_exp2|native_exp10|native_log|native_log2|native_log10|native_powr|native_recip|native_rsqrt|native_sin|native_sqrt|native_tan|s_abs|s_abs_diff|s_add_sat|u_add_sat|s_hadd|u_hadd|s_rhadd|u_rhadd|s_clamp|u_clamp|clz|ctz|s_mad_hi|u_mad_sat|s_mad_sat|s_max|u_max|s_min|u_min|s_mul_hi|rotate|s_sub_sat|u_sub_sat|u_upsample|s_upsample|popcount|s_mad24|u_mad24|s_mul24|u_mul24|u_abs|u_abs_diff|u_mul_hi|u_mad_hi|fclamp|degrees|fmax_common|fmin_common|mix|radians|step|smoothstep|sign|cross|distance|length|normalize|fast_distance|fast_length|fast_normalize|bitselect|select|vloadn|vstoren|vload_half|vload_halfn|vstore_half|vstore_half_r|vstore_halfn|vstore_halfn_r|vloada_halfn|vstorea_halfn|vstorea_halfn_r|shuffle|shuffle2|printf|prefetch|DebugInfoNone|DebugCompilationUnit|DebugTypeBasic|DebugTypePointer|DebugTypeQualifier|DebugTypeArray|DebugTypeVector|DebugTypedef|DebugTypeFunction|DebugTypeEnum|DebugTypeComposite|DebugTypeMember|DebugTypeInheritance|DebugTypePtrToMember|DebugTypeTemplate|DebugTypeTemplateParameter|DebugTypeTemplateTemplateParameter|DebugTypeTemplateParameterPack|DebugGlobalVariable|DebugFunctionDeclaration|DebugFunction|DebugLexicalBlock|DebugLexicalBlockDiscriminator|DebugScope|DebugNoScope|DebugInlinedAt|DebugLocalVariable|DebugInlinedVariable|DebugDeclare|DebugValue|DebugOperation|DebugExpression|DebugMacroDef|DebugMacroUndef|DebugImportedEntity|DebugSource)",
+			"name": "entity.name.function.ext"
+		},
 		"identifier": {
 			"match": "%[a-zA-Z0-9_]+",
 			"name": "variable.spirv"
diff --git a/third_party/SPIRV-Tools/utils/vscode/spirv.json.tmpl b/third_party/SPIRV-Tools/utils/vscode/spirv.json.tmpl
index 8582d03..f655e52 100644
--- a/third_party/SPIRV-Tools/utils/vscode/spirv.json.tmpl
+++ b/third_party/SPIRV-Tools/utils/vscode/spirv.json.tmpl
@@ -3,15 +3,16 @@
 	"name": "SPIR-V",
 	"comment": "Generated by {{GenerateArguments}}. Do not modify this file directly.",
 	"patterns": [
-{{range $o := .OperandKinds}}{{if len $o.Enumerants}}		{ "include": "#{{$o.Category}}_{{$o.Kind}}" },
+{{range $o := .All.OperandKinds}}{{if len $o.Enumerants}}		{ "include": "#{{$o.Category}}_{{$o.Kind}}" },
 {{end}}{{end}}		{ "include": "#opcode" },
+		{ "include": "#extopcode" },
 		{ "include": "#identifier" },
 		{ "include": "#number" },
 		{ "include": "#string" },
 		{ "include": "#comment" },
 		{ "include": "#operator" }
 	],
-	"repository": { {{range $o := .OperandKinds}}{{if len $o.Enumerants}}
+	"repository": { {{range $o := .All.OperandKinds}}{{if len $o.Enumerants}}
 		"{{$o.Category}}_{{$o.Kind}}": {
 			"match": "\\b({{OperandKindsMatch $o}})\\b",
 			"name": "keyword.spirv"
@@ -20,6 +21,10 @@
 			"match": "(Op[a-zA-Z]+)",
 			"name": "entity.name.function.spirv"
 		},
+		"extopcode": {
+			"match": "({{AllExtOpcodes}})",
+			"name": "entity.name.function.ext"
+		},
 		"identifier": {
 			"match": "%[a-zA-Z0-9_]+",
 			"name": "variable.spirv"
diff --git a/third_party/SPIRV-Tools/utils/vscode/src/parser/parser.go b/third_party/SPIRV-Tools/utils/vscode/src/parser/parser.go
index 64ba462..9f5691b 100644
--- a/third_party/SPIRV-Tools/utils/vscode/src/parser/parser.go
+++ b/third_party/SPIRV-Tools/utils/vscode/src/parser/parser.go
@@ -396,12 +396,13 @@
 func isAlphaNumeric(r rune) bool { return isAlpha(r) || isNumeric(r) }
 
 type parser struct {
-	lines    []string               // all source lines
-	toks     []*Token               // all tokens
-	diags    []Diagnostic           // parser emitted diagnostics
-	idents   map[string]*Identifier // identifiers by name
-	mappings map[*Token]interface{} // tokens to semantic map
-	insts    []*Instruction         // all instructions
+	lines          []string                    // all source lines
+	toks           []*Token                    // all tokens
+	diags          []Diagnostic                // parser emitted diagnostics
+	idents         map[string]*Identifier      // identifiers by name
+	mappings       map[*Token]interface{}      // tokens to semantic map
+	extInstImports map[string]schema.OpcodeMap // extension imports by identifier
+	insts          []*Instruction              // all instructions
 }
 
 func (p *parser) parse() error {
@@ -459,9 +460,9 @@
 		p.addIdentDef(inst.Result.Text(p.lines), inst, p.tok(i))
 	}
 
-	for _, o := range operands {
+	processOperand := func(o schema.Operand) bool {
 		if p.newline(i + n) {
-			break
+			return false
 		}
 
 		switch o.Quantifier {
@@ -481,10 +482,37 @@
 					inst.Tokens = append(inst.Tokens, op.Tokens...)
 					n += c
 				} else {
-					break
+					return false
 				}
 			}
 		}
+		return true
+	}
+
+	for _, o := range operands {
+		if !processOperand(o) {
+			break
+		}
+
+		if inst.Opcode == schema.OpExtInst && n == 4 {
+			extImportTok, extNameTok := p.tok(i+n), p.tok(i+n+1)
+			extImport := extImportTok.Text(p.lines)
+			if extOpcodes, ok := p.extInstImports[extImport]; ok {
+				extName := extNameTok.Text(p.lines)
+				if extOpcode, ok := extOpcodes[extName]; ok {
+					n += 2 // skip ext import, ext name
+					for _, o := range extOpcode.Operands {
+						if !processOperand(o) {
+							break
+						}
+					}
+				} else {
+					p.err(extNameTok, "Unknown extension opcode '%s'", extName)
+				}
+			} else {
+				p.err(extImportTok, "Expected identifier to OpExtInstImport")
+			}
+		}
 	}
 
 	for _, t := range inst.Tokens {
@@ -492,6 +520,19 @@
 	}
 
 	p.insts = append(p.insts, inst)
+
+	if inst.Opcode == schema.OpExtInstImport && len(inst.Tokens) >= 4 {
+		// Instruction is a OpExtInstImport. Keep track of this.
+		extTok := inst.Tokens[3]
+		extName := strings.Trim(extTok.Text(p.lines), `"`)
+		extOpcodes, ok := schema.ExtOpcodes[extName]
+		if !ok {
+			p.err(extTok, "Unknown extension '%s'", extName)
+		}
+		extImport := inst.Result.Text(p.lines)
+		p.extInstImports[extImport] = extOpcodes
+	}
+
 	return
 }
 
@@ -545,7 +586,7 @@
 
 	case schema.OperandCategoryLiteral:
 		switch tok.Type {
-		case String, Integer, Float:
+		case String, Integer, Float, Ident:
 			return op, 1
 		}
 		if !optional {
@@ -683,10 +724,11 @@
 	}
 	lines := strings.SplitAfter(source, "\n")
 	p := parser{
-		lines:    lines,
-		toks:     toks,
-		idents:   map[string]*Identifier{},
-		mappings: map[*Token]interface{}{},
+		lines:          lines,
+		toks:           toks,
+		idents:         map[string]*Identifier{},
+		mappings:       map[*Token]interface{}{},
+		extInstImports: map[string]schema.OpcodeMap{},
 	}
 	if err := p.parse(); err != nil {
 		return Results{}, err
diff --git a/third_party/SPIRV-Tools/utils/vscode/src/schema/schema.go b/third_party/SPIRV-Tools/utils/vscode/src/schema/schema.go
index c7e1ca4..66bd7dc 100755
--- a/third_party/SPIRV-Tools/utils/vscode/src/schema/schema.go
+++ b/third_party/SPIRV-Tools/utils/vscode/src/schema/schema.go
@@ -97,9 +97,11 @@
 	OperandCategoryComposite = "Composite"
 )
 
+type OpcodeMap map[string]*Opcode
+
 var (
 	// Opcodes is a map of opcode name to Opcode description.
-	Opcodes = map[string]*Opcode {
+	Opcodes = OpcodeMap {
 		"OpNop": OpNop,
 		"OpUndef": OpUndef,
 		"OpSourceContinued": OpSourceContinued,
@@ -627,13 +629,306 @@
 		"OpSubgroupAvcSicGetInterRawSadsINTEL": OpSubgroupAvcSicGetInterRawSadsINTEL,
 	}
 
+	// ExtOpcodes is a map of extension name to Opcode description list.
+	ExtOpcodes = map[string]OpcodeMap {
+		"GLSL.std.450": {
+			"Round": GLSLStd450_Round,
+			"RoundEven": GLSLStd450_RoundEven,
+			"Trunc": GLSLStd450_Trunc,
+			"FAbs": GLSLStd450_FAbs,
+			"SAbs": GLSLStd450_SAbs,
+			"FSign": GLSLStd450_FSign,
+			"SSign": GLSLStd450_SSign,
+			"Floor": GLSLStd450_Floor,
+			"Ceil": GLSLStd450_Ceil,
+			"Fract": GLSLStd450_Fract,
+			"Radians": GLSLStd450_Radians,
+			"Degrees": GLSLStd450_Degrees,
+			"Sin": GLSLStd450_Sin,
+			"Cos": GLSLStd450_Cos,
+			"Tan": GLSLStd450_Tan,
+			"Asin": GLSLStd450_Asin,
+			"Acos": GLSLStd450_Acos,
+			"Atan": GLSLStd450_Atan,
+			"Sinh": GLSLStd450_Sinh,
+			"Cosh": GLSLStd450_Cosh,
+			"Tanh": GLSLStd450_Tanh,
+			"Asinh": GLSLStd450_Asinh,
+			"Acosh": GLSLStd450_Acosh,
+			"Atanh": GLSLStd450_Atanh,
+			"Atan2": GLSLStd450_Atan2,
+			"Pow": GLSLStd450_Pow,
+			"Exp": GLSLStd450_Exp,
+			"Log": GLSLStd450_Log,
+			"Exp2": GLSLStd450_Exp2,
+			"Log2": GLSLStd450_Log2,
+			"Sqrt": GLSLStd450_Sqrt,
+			"InverseSqrt": GLSLStd450_InverseSqrt,
+			"Determinant": GLSLStd450_Determinant,
+			"MatrixInverse": GLSLStd450_MatrixInverse,
+			"Modf": GLSLStd450_Modf,
+			"ModfStruct": GLSLStd450_ModfStruct,
+			"FMin": GLSLStd450_FMin,
+			"UMin": GLSLStd450_UMin,
+			"SMin": GLSLStd450_SMin,
+			"FMax": GLSLStd450_FMax,
+			"UMax": GLSLStd450_UMax,
+			"SMax": GLSLStd450_SMax,
+			"FClamp": GLSLStd450_FClamp,
+			"UClamp": GLSLStd450_UClamp,
+			"SClamp": GLSLStd450_SClamp,
+			"FMix": GLSLStd450_FMix,
+			"IMix": GLSLStd450_IMix,
+			"Step": GLSLStd450_Step,
+			"SmoothStep": GLSLStd450_SmoothStep,
+			"Fma": GLSLStd450_Fma,
+			"Frexp": GLSLStd450_Frexp,
+			"FrexpStruct": GLSLStd450_FrexpStruct,
+			"Ldexp": GLSLStd450_Ldexp,
+			"PackSnorm4x8": GLSLStd450_PackSnorm4x8,
+			"PackUnorm4x8": GLSLStd450_PackUnorm4x8,
+			"PackSnorm2x16": GLSLStd450_PackSnorm2x16,
+			"PackUnorm2x16": GLSLStd450_PackUnorm2x16,
+			"PackHalf2x16": GLSLStd450_PackHalf2x16,
+			"PackDouble2x32": GLSLStd450_PackDouble2x32,
+			"UnpackSnorm2x16": GLSLStd450_UnpackSnorm2x16,
+			"UnpackUnorm2x16": GLSLStd450_UnpackUnorm2x16,
+			"UnpackHalf2x16": GLSLStd450_UnpackHalf2x16,
+			"UnpackSnorm4x8": GLSLStd450_UnpackSnorm4x8,
+			"UnpackUnorm4x8": GLSLStd450_UnpackUnorm4x8,
+			"UnpackDouble2x32": GLSLStd450_UnpackDouble2x32,
+			"Length": GLSLStd450_Length,
+			"Distance": GLSLStd450_Distance,
+			"Cross": GLSLStd450_Cross,
+			"Normalize": GLSLStd450_Normalize,
+			"FaceForward": GLSLStd450_FaceForward,
+			"Reflect": GLSLStd450_Reflect,
+			"Refract": GLSLStd450_Refract,
+			"FindILsb": GLSLStd450_FindILsb,
+			"FindSMsb": GLSLStd450_FindSMsb,
+			"FindUMsb": GLSLStd450_FindUMsb,
+			"InterpolateAtCentroid": GLSLStd450_InterpolateAtCentroid,
+			"InterpolateAtSample": GLSLStd450_InterpolateAtSample,
+			"InterpolateAtOffset": GLSLStd450_InterpolateAtOffset,
+			"NMin": GLSLStd450_NMin,
+			"NMax": GLSLStd450_NMax,
+			"NClamp": GLSLStd450_NClamp,
+		},
+		"OpenCL.std": {
+			"acos": OpenCLStd_acos,
+			"acosh": OpenCLStd_acosh,
+			"acospi": OpenCLStd_acospi,
+			"asin": OpenCLStd_asin,
+			"asinh": OpenCLStd_asinh,
+			"asinpi": OpenCLStd_asinpi,
+			"atan": OpenCLStd_atan,
+			"atan2": OpenCLStd_atan2,
+			"atanh": OpenCLStd_atanh,
+			"atanpi": OpenCLStd_atanpi,
+			"atan2pi": OpenCLStd_atan2pi,
+			"cbrt": OpenCLStd_cbrt,
+			"ceil": OpenCLStd_ceil,
+			"copysign": OpenCLStd_copysign,
+			"cos": OpenCLStd_cos,
+			"cosh": OpenCLStd_cosh,
+			"cospi": OpenCLStd_cospi,
+			"erfc": OpenCLStd_erfc,
+			"erf": OpenCLStd_erf,
+			"exp": OpenCLStd_exp,
+			"exp2": OpenCLStd_exp2,
+			"exp10": OpenCLStd_exp10,
+			"expm1": OpenCLStd_expm1,
+			"fabs": OpenCLStd_fabs,
+			"fdim": OpenCLStd_fdim,
+			"floor": OpenCLStd_floor,
+			"fma": OpenCLStd_fma,
+			"fmax": OpenCLStd_fmax,
+			"fmin": OpenCLStd_fmin,
+			"fmod": OpenCLStd_fmod,
+			"fract": OpenCLStd_fract,
+			"frexp": OpenCLStd_frexp,
+			"hypot": OpenCLStd_hypot,
+			"ilogb": OpenCLStd_ilogb,
+			"ldexp": OpenCLStd_ldexp,
+			"lgamma": OpenCLStd_lgamma,
+			"lgamma_r": OpenCLStd_lgamma_r,
+			"log": OpenCLStd_log,
+			"log2": OpenCLStd_log2,
+			"log10": OpenCLStd_log10,
+			"log1p": OpenCLStd_log1p,
+			"logb": OpenCLStd_logb,
+			"mad": OpenCLStd_mad,
+			"maxmag": OpenCLStd_maxmag,
+			"minmag": OpenCLStd_minmag,
+			"modf": OpenCLStd_modf,
+			"nan": OpenCLStd_nan,
+			"nextafter": OpenCLStd_nextafter,
+			"pow": OpenCLStd_pow,
+			"pown": OpenCLStd_pown,
+			"powr": OpenCLStd_powr,
+			"remainder": OpenCLStd_remainder,
+			"remquo": OpenCLStd_remquo,
+			"rint": OpenCLStd_rint,
+			"rootn": OpenCLStd_rootn,
+			"round": OpenCLStd_round,
+			"rsqrt": OpenCLStd_rsqrt,
+			"sin": OpenCLStd_sin,
+			"sincos": OpenCLStd_sincos,
+			"sinh": OpenCLStd_sinh,
+			"sinpi": OpenCLStd_sinpi,
+			"sqrt": OpenCLStd_sqrt,
+			"tan": OpenCLStd_tan,
+			"tanh": OpenCLStd_tanh,
+			"tanpi": OpenCLStd_tanpi,
+			"tgamma": OpenCLStd_tgamma,
+			"trunc": OpenCLStd_trunc,
+			"half_cos": OpenCLStd_half_cos,
+			"half_divide": OpenCLStd_half_divide,
+			"half_exp": OpenCLStd_half_exp,
+			"half_exp2": OpenCLStd_half_exp2,
+			"half_exp10": OpenCLStd_half_exp10,
+			"half_log": OpenCLStd_half_log,
+			"half_log2": OpenCLStd_half_log2,
+			"half_log10": OpenCLStd_half_log10,
+			"half_powr": OpenCLStd_half_powr,
+			"half_recip": OpenCLStd_half_recip,
+			"half_rsqrt": OpenCLStd_half_rsqrt,
+			"half_sin": OpenCLStd_half_sin,
+			"half_sqrt": OpenCLStd_half_sqrt,
+			"half_tan": OpenCLStd_half_tan,
+			"native_cos": OpenCLStd_native_cos,
+			"native_divide": OpenCLStd_native_divide,
+			"native_exp": OpenCLStd_native_exp,
+			"native_exp2": OpenCLStd_native_exp2,
+			"native_exp10": OpenCLStd_native_exp10,
+			"native_log": OpenCLStd_native_log,
+			"native_log2": OpenCLStd_native_log2,
+			"native_log10": OpenCLStd_native_log10,
+			"native_powr": OpenCLStd_native_powr,
+			"native_recip": OpenCLStd_native_recip,
+			"native_rsqrt": OpenCLStd_native_rsqrt,
+			"native_sin": OpenCLStd_native_sin,
+			"native_sqrt": OpenCLStd_native_sqrt,
+			"native_tan": OpenCLStd_native_tan,
+			"s_abs": OpenCLStd_s_abs,
+			"s_abs_diff": OpenCLStd_s_abs_diff,
+			"s_add_sat": OpenCLStd_s_add_sat,
+			"u_add_sat": OpenCLStd_u_add_sat,
+			"s_hadd": OpenCLStd_s_hadd,
+			"u_hadd": OpenCLStd_u_hadd,
+			"s_rhadd": OpenCLStd_s_rhadd,
+			"u_rhadd": OpenCLStd_u_rhadd,
+			"s_clamp": OpenCLStd_s_clamp,
+			"u_clamp": OpenCLStd_u_clamp,
+			"clz": OpenCLStd_clz,
+			"ctz": OpenCLStd_ctz,
+			"s_mad_hi": OpenCLStd_s_mad_hi,
+			"u_mad_sat": OpenCLStd_u_mad_sat,
+			"s_mad_sat": OpenCLStd_s_mad_sat,
+			"s_max": OpenCLStd_s_max,
+			"u_max": OpenCLStd_u_max,
+			"s_min": OpenCLStd_s_min,
+			"u_min": OpenCLStd_u_min,
+			"s_mul_hi": OpenCLStd_s_mul_hi,
+			"rotate": OpenCLStd_rotate,
+			"s_sub_sat": OpenCLStd_s_sub_sat,
+			"u_sub_sat": OpenCLStd_u_sub_sat,
+			"u_upsample": OpenCLStd_u_upsample,
+			"s_upsample": OpenCLStd_s_upsample,
+			"popcount": OpenCLStd_popcount,
+			"s_mad24": OpenCLStd_s_mad24,
+			"u_mad24": OpenCLStd_u_mad24,
+			"s_mul24": OpenCLStd_s_mul24,
+			"u_mul24": OpenCLStd_u_mul24,
+			"u_abs": OpenCLStd_u_abs,
+			"u_abs_diff": OpenCLStd_u_abs_diff,
+			"u_mul_hi": OpenCLStd_u_mul_hi,
+			"u_mad_hi": OpenCLStd_u_mad_hi,
+			"fclamp": OpenCLStd_fclamp,
+			"degrees": OpenCLStd_degrees,
+			"fmax_common": OpenCLStd_fmax_common,
+			"fmin_common": OpenCLStd_fmin_common,
+			"mix": OpenCLStd_mix,
+			"radians": OpenCLStd_radians,
+			"step": OpenCLStd_step,
+			"smoothstep": OpenCLStd_smoothstep,
+			"sign": OpenCLStd_sign,
+			"cross": OpenCLStd_cross,
+			"distance": OpenCLStd_distance,
+			"length": OpenCLStd_length,
+			"normalize": OpenCLStd_normalize,
+			"fast_distance": OpenCLStd_fast_distance,
+			"fast_length": OpenCLStd_fast_length,
+			"fast_normalize": OpenCLStd_fast_normalize,
+			"bitselect": OpenCLStd_bitselect,
+			"select": OpenCLStd_select,
+			"vloadn": OpenCLStd_vloadn,
+			"vstoren": OpenCLStd_vstoren,
+			"vload_half": OpenCLStd_vload_half,
+			"vload_halfn": OpenCLStd_vload_halfn,
+			"vstore_half": OpenCLStd_vstore_half,
+			"vstore_half_r": OpenCLStd_vstore_half_r,
+			"vstore_halfn": OpenCLStd_vstore_halfn,
+			"vstore_halfn_r": OpenCLStd_vstore_halfn_r,
+			"vloada_halfn": OpenCLStd_vloada_halfn,
+			"vstorea_halfn": OpenCLStd_vstorea_halfn,
+			"vstorea_halfn_r": OpenCLStd_vstorea_halfn_r,
+			"shuffle": OpenCLStd_shuffle,
+			"shuffle2": OpenCLStd_shuffle2,
+			"printf": OpenCLStd_printf,
+			"prefetch": OpenCLStd_prefetch,
+		},
+		"OpenCL.DebugInfo.100": {
+			"DebugInfoNone": OpenCLDebugInfo100_DebugInfoNone,
+			"DebugCompilationUnit": OpenCLDebugInfo100_DebugCompilationUnit,
+			"DebugTypeBasic": OpenCLDebugInfo100_DebugTypeBasic,
+			"DebugTypePointer": OpenCLDebugInfo100_DebugTypePointer,
+			"DebugTypeQualifier": OpenCLDebugInfo100_DebugTypeQualifier,
+			"DebugTypeArray": OpenCLDebugInfo100_DebugTypeArray,
+			"DebugTypeVector": OpenCLDebugInfo100_DebugTypeVector,
+			"DebugTypedef": OpenCLDebugInfo100_DebugTypedef,
+			"DebugTypeFunction": OpenCLDebugInfo100_DebugTypeFunction,
+			"DebugTypeEnum": OpenCLDebugInfo100_DebugTypeEnum,
+			"DebugTypeComposite": OpenCLDebugInfo100_DebugTypeComposite,
+			"DebugTypeMember": OpenCLDebugInfo100_DebugTypeMember,
+			"DebugTypeInheritance": OpenCLDebugInfo100_DebugTypeInheritance,
+			"DebugTypePtrToMember": OpenCLDebugInfo100_DebugTypePtrToMember,
+			"DebugTypeTemplate": OpenCLDebugInfo100_DebugTypeTemplate,
+			"DebugTypeTemplateParameter": OpenCLDebugInfo100_DebugTypeTemplateParameter,
+			"DebugTypeTemplateTemplateParameter": OpenCLDebugInfo100_DebugTypeTemplateTemplateParameter,
+			"DebugTypeTemplateParameterPack": OpenCLDebugInfo100_DebugTypeTemplateParameterPack,
+			"DebugGlobalVariable": OpenCLDebugInfo100_DebugGlobalVariable,
+			"DebugFunctionDeclaration": OpenCLDebugInfo100_DebugFunctionDeclaration,
+			"DebugFunction": OpenCLDebugInfo100_DebugFunction,
+			"DebugLexicalBlock": OpenCLDebugInfo100_DebugLexicalBlock,
+			"DebugLexicalBlockDiscriminator": OpenCLDebugInfo100_DebugLexicalBlockDiscriminator,
+			"DebugScope": OpenCLDebugInfo100_DebugScope,
+			"DebugNoScope": OpenCLDebugInfo100_DebugNoScope,
+			"DebugInlinedAt": OpenCLDebugInfo100_DebugInlinedAt,
+			"DebugLocalVariable": OpenCLDebugInfo100_DebugLocalVariable,
+			"DebugInlinedVariable": OpenCLDebugInfo100_DebugInlinedVariable,
+			"DebugDeclare": OpenCLDebugInfo100_DebugDeclare,
+			"DebugValue": OpenCLDebugInfo100_DebugValue,
+			"DebugOperation": OpenCLDebugInfo100_DebugOperation,
+			"DebugExpression": OpenCLDebugInfo100_DebugExpression,
+			"DebugMacroDef": OpenCLDebugInfo100_DebugMacroDef,
+			"DebugMacroUndef": OpenCLDebugInfo100_DebugMacroUndef,
+			"DebugImportedEntity": OpenCLDebugInfo100_DebugImportedEntity,
+			"DebugSource": OpenCLDebugInfo100_DebugSource,
+		},
+	}
+
 	OpNop = &Opcode {
 		Opname:   "OpNop",
+		Class:    "Miscellaneous",
+		Opcode:   0,
 		Operands: []Operand {
 		},
 	}
 	OpUndef = &Opcode {
 		Opname:   "OpUndef",
+		Class:    "Miscellaneous",
+		Opcode:   1,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -649,6 +944,8 @@
 	}
 	OpSourceContinued = &Opcode {
 		Opname:   "OpSourceContinued",
+		Class:    "Debug",
+		Opcode:   2,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindLiteralString,
@@ -659,6 +956,8 @@
 	}
 	OpSource = &Opcode {
 		Opname:   "OpSource",
+		Class:    "Debug",
+		Opcode:   3,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindSourceLanguage,
@@ -684,6 +983,8 @@
 	}
 	OpSourceExtension = &Opcode {
 		Opname:   "OpSourceExtension",
+		Class:    "Debug",
+		Opcode:   4,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindLiteralString,
@@ -694,6 +995,8 @@
 	}
 	OpName = &Opcode {
 		Opname:   "OpName",
+		Class:    "Debug",
+		Opcode:   5,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -709,6 +1012,8 @@
 	}
 	OpMemberName = &Opcode {
 		Opname:   "OpMemberName",
+		Class:    "Debug",
+		Opcode:   6,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -729,6 +1034,8 @@
 	}
 	OpString = &Opcode {
 		Opname:   "OpString",
+		Class:    "Debug",
+		Opcode:   7,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -744,6 +1051,8 @@
 	}
 	OpLine = &Opcode {
 		Opname:   "OpLine",
+		Class:    "Debug",
+		Opcode:   8,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -764,6 +1073,8 @@
 	}
 	OpExtension = &Opcode {
 		Opname:   "OpExtension",
+		Class:    "Extension",
+		Opcode:   10,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindLiteralString,
@@ -774,6 +1085,8 @@
 	}
 	OpExtInstImport = &Opcode {
 		Opname:   "OpExtInstImport",
+		Class:    "Extension",
+		Opcode:   11,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -789,6 +1102,8 @@
 	}
 	OpExtInst = &Opcode {
 		Opname:   "OpExtInst",
+		Class:    "Extension",
+		Opcode:   12,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -819,6 +1134,8 @@
 	}
 	OpMemoryModel = &Opcode {
 		Opname:   "OpMemoryModel",
+		Class:    "Mode-Setting",
+		Opcode:   14,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindAddressingModel,
@@ -834,6 +1151,8 @@
 	}
 	OpEntryPoint = &Opcode {
 		Opname:   "OpEntryPoint",
+		Class:    "Mode-Setting",
+		Opcode:   15,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindExecutionModel,
@@ -859,6 +1178,8 @@
 	}
 	OpExecutionMode = &Opcode {
 		Opname:   "OpExecutionMode",
+		Class:    "Mode-Setting",
+		Opcode:   16,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -874,6 +1195,8 @@
 	}
 	OpCapability = &Opcode {
 		Opname:   "OpCapability",
+		Class:    "Mode-Setting",
+		Opcode:   17,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindCapability,
@@ -884,6 +1207,8 @@
 	}
 	OpTypeVoid = &Opcode {
 		Opname:   "OpTypeVoid",
+		Class:    "Type-Declaration",
+		Opcode:   19,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -894,6 +1219,8 @@
 	}
 	OpTypeBool = &Opcode {
 		Opname:   "OpTypeBool",
+		Class:    "Type-Declaration",
+		Opcode:   20,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -904,6 +1231,8 @@
 	}
 	OpTypeInt = &Opcode {
 		Opname:   "OpTypeInt",
+		Class:    "Type-Declaration",
+		Opcode:   21,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -924,6 +1253,8 @@
 	}
 	OpTypeFloat = &Opcode {
 		Opname:   "OpTypeFloat",
+		Class:    "Type-Declaration",
+		Opcode:   22,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -939,6 +1270,8 @@
 	}
 	OpTypeVector = &Opcode {
 		Opname:   "OpTypeVector",
+		Class:    "Type-Declaration",
+		Opcode:   23,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -959,6 +1292,8 @@
 	}
 	OpTypeMatrix = &Opcode {
 		Opname:   "OpTypeMatrix",
+		Class:    "Type-Declaration",
+		Opcode:   24,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -979,6 +1314,8 @@
 	}
 	OpTypeImage = &Opcode {
 		Opname:   "OpTypeImage",
+		Class:    "Type-Declaration",
+		Opcode:   25,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -1029,6 +1366,8 @@
 	}
 	OpTypeSampler = &Opcode {
 		Opname:   "OpTypeSampler",
+		Class:    "Type-Declaration",
+		Opcode:   26,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -1039,6 +1378,8 @@
 	}
 	OpTypeSampledImage = &Opcode {
 		Opname:   "OpTypeSampledImage",
+		Class:    "Type-Declaration",
+		Opcode:   27,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -1054,6 +1395,8 @@
 	}
 	OpTypeArray = &Opcode {
 		Opname:   "OpTypeArray",
+		Class:    "Type-Declaration",
+		Opcode:   28,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -1074,6 +1417,8 @@
 	}
 	OpTypeRuntimeArray = &Opcode {
 		Opname:   "OpTypeRuntimeArray",
+		Class:    "Type-Declaration",
+		Opcode:   29,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -1089,6 +1434,8 @@
 	}
 	OpTypeStruct = &Opcode {
 		Opname:   "OpTypeStruct",
+		Class:    "Type-Declaration",
+		Opcode:   30,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -1104,6 +1451,8 @@
 	}
 	OpTypeOpaque = &Opcode {
 		Opname:   "OpTypeOpaque",
+		Class:    "Type-Declaration",
+		Opcode:   31,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -1119,6 +1468,8 @@
 	}
 	OpTypePointer = &Opcode {
 		Opname:   "OpTypePointer",
+		Class:    "Type-Declaration",
+		Opcode:   32,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -1139,6 +1490,8 @@
 	}
 	OpTypeFunction = &Opcode {
 		Opname:   "OpTypeFunction",
+		Class:    "Type-Declaration",
+		Opcode:   33,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -1159,6 +1512,8 @@
 	}
 	OpTypeEvent = &Opcode {
 		Opname:   "OpTypeEvent",
+		Class:    "Type-Declaration",
+		Opcode:   34,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -1169,6 +1524,8 @@
 	}
 	OpTypeDeviceEvent = &Opcode {
 		Opname:   "OpTypeDeviceEvent",
+		Class:    "Type-Declaration",
+		Opcode:   35,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -1179,6 +1536,8 @@
 	}
 	OpTypeReserveId = &Opcode {
 		Opname:   "OpTypeReserveId",
+		Class:    "Type-Declaration",
+		Opcode:   36,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -1189,6 +1548,8 @@
 	}
 	OpTypeQueue = &Opcode {
 		Opname:   "OpTypeQueue",
+		Class:    "Type-Declaration",
+		Opcode:   37,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -1199,6 +1560,8 @@
 	}
 	OpTypePipe = &Opcode {
 		Opname:   "OpTypePipe",
+		Class:    "Type-Declaration",
+		Opcode:   38,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -1214,6 +1577,8 @@
 	}
 	OpTypeForwardPointer = &Opcode {
 		Opname:   "OpTypeForwardPointer",
+		Class:    "Type-Declaration",
+		Opcode:   39,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -1229,6 +1594,8 @@
 	}
 	OpConstantTrue = &Opcode {
 		Opname:   "OpConstantTrue",
+		Class:    "Constant-Creation",
+		Opcode:   41,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1244,6 +1611,8 @@
 	}
 	OpConstantFalse = &Opcode {
 		Opname:   "OpConstantFalse",
+		Class:    "Constant-Creation",
+		Opcode:   42,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1259,6 +1628,8 @@
 	}
 	OpConstant = &Opcode {
 		Opname:   "OpConstant",
+		Class:    "Constant-Creation",
+		Opcode:   43,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1279,6 +1650,8 @@
 	}
 	OpConstantComposite = &Opcode {
 		Opname:   "OpConstantComposite",
+		Class:    "Constant-Creation",
+		Opcode:   44,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1299,6 +1672,8 @@
 	}
 	OpConstantSampler = &Opcode {
 		Opname:   "OpConstantSampler",
+		Class:    "Constant-Creation",
+		Opcode:   45,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1329,6 +1704,8 @@
 	}
 	OpConstantNull = &Opcode {
 		Opname:   "OpConstantNull",
+		Class:    "Constant-Creation",
+		Opcode:   46,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1344,6 +1721,8 @@
 	}
 	OpSpecConstantTrue = &Opcode {
 		Opname:   "OpSpecConstantTrue",
+		Class:    "Constant-Creation",
+		Opcode:   48,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1359,6 +1738,8 @@
 	}
 	OpSpecConstantFalse = &Opcode {
 		Opname:   "OpSpecConstantFalse",
+		Class:    "Constant-Creation",
+		Opcode:   49,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1374,6 +1755,8 @@
 	}
 	OpSpecConstant = &Opcode {
 		Opname:   "OpSpecConstant",
+		Class:    "Constant-Creation",
+		Opcode:   50,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1394,6 +1777,8 @@
 	}
 	OpSpecConstantComposite = &Opcode {
 		Opname:   "OpSpecConstantComposite",
+		Class:    "Constant-Creation",
+		Opcode:   51,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1414,6 +1799,8 @@
 	}
 	OpSpecConstantOp = &Opcode {
 		Opname:   "OpSpecConstantOp",
+		Class:    "Constant-Creation",
+		Opcode:   52,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1434,6 +1821,8 @@
 	}
 	OpFunction = &Opcode {
 		Opname:   "OpFunction",
+		Class:    "Function",
+		Opcode:   54,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1459,6 +1848,8 @@
 	}
 	OpFunctionParameter = &Opcode {
 		Opname:   "OpFunctionParameter",
+		Class:    "Function",
+		Opcode:   55,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1474,11 +1865,15 @@
 	}
 	OpFunctionEnd = &Opcode {
 		Opname:   "OpFunctionEnd",
+		Class:    "Function",
+		Opcode:   56,
 		Operands: []Operand {
 		},
 	}
 	OpFunctionCall = &Opcode {
 		Opname:   "OpFunctionCall",
+		Class:    "Function",
+		Opcode:   57,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1504,6 +1899,8 @@
 	}
 	OpVariable = &Opcode {
 		Opname:   "OpVariable",
+		Class:    "Memory",
+		Opcode:   59,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1529,6 +1926,8 @@
 	}
 	OpImageTexelPointer = &Opcode {
 		Opname:   "OpImageTexelPointer",
+		Class:    "Memory",
+		Opcode:   60,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1559,6 +1958,8 @@
 	}
 	OpLoad = &Opcode {
 		Opname:   "OpLoad",
+		Class:    "Memory",
+		Opcode:   61,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1584,6 +1985,8 @@
 	}
 	OpStore = &Opcode {
 		Opname:   "OpStore",
+		Class:    "Memory",
+		Opcode:   62,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -1604,6 +2007,8 @@
 	}
 	OpCopyMemory = &Opcode {
 		Opname:   "OpCopyMemory",
+		Class:    "Memory",
+		Opcode:   63,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -1629,6 +2034,8 @@
 	}
 	OpCopyMemorySized = &Opcode {
 		Opname:   "OpCopyMemorySized",
+		Class:    "Memory",
+		Opcode:   64,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -1659,6 +2066,8 @@
 	}
 	OpAccessChain = &Opcode {
 		Opname:   "OpAccessChain",
+		Class:    "Memory",
+		Opcode:   65,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1684,6 +2093,8 @@
 	}
 	OpInBoundsAccessChain = &Opcode {
 		Opname:   "OpInBoundsAccessChain",
+		Class:    "Memory",
+		Opcode:   66,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1709,6 +2120,8 @@
 	}
 	OpPtrAccessChain = &Opcode {
 		Opname:   "OpPtrAccessChain",
+		Class:    "Memory",
+		Opcode:   67,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1739,6 +2152,8 @@
 	}
 	OpArrayLength = &Opcode {
 		Opname:   "OpArrayLength",
+		Class:    "Memory",
+		Opcode:   68,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1764,6 +2179,8 @@
 	}
 	OpGenericPtrMemSemantics = &Opcode {
 		Opname:   "OpGenericPtrMemSemantics",
+		Class:    "Memory",
+		Opcode:   69,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1784,6 +2201,8 @@
 	}
 	OpInBoundsPtrAccessChain = &Opcode {
 		Opname:   "OpInBoundsPtrAccessChain",
+		Class:    "Memory",
+		Opcode:   70,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1814,6 +2233,8 @@
 	}
 	OpDecorate = &Opcode {
 		Opname:   "OpDecorate",
+		Class:    "Annotation",
+		Opcode:   71,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -1829,6 +2250,8 @@
 	}
 	OpMemberDecorate = &Opcode {
 		Opname:   "OpMemberDecorate",
+		Class:    "Annotation",
+		Opcode:   72,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -1849,6 +2272,8 @@
 	}
 	OpDecorationGroup = &Opcode {
 		Opname:   "OpDecorationGroup",
+		Class:    "Annotation",
+		Opcode:   73,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -1859,6 +2284,8 @@
 	}
 	OpGroupDecorate = &Opcode {
 		Opname:   "OpGroupDecorate",
+		Class:    "Annotation",
+		Opcode:   74,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -1874,6 +2301,8 @@
 	}
 	OpGroupMemberDecorate = &Opcode {
 		Opname:   "OpGroupMemberDecorate",
+		Class:    "Annotation",
+		Opcode:   75,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -1889,6 +2318,8 @@
 	}
 	OpVectorExtractDynamic = &Opcode {
 		Opname:   "OpVectorExtractDynamic",
+		Class:    "Composite",
+		Opcode:   77,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1914,6 +2345,8 @@
 	}
 	OpVectorInsertDynamic = &Opcode {
 		Opname:   "OpVectorInsertDynamic",
+		Class:    "Composite",
+		Opcode:   78,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1944,6 +2377,8 @@
 	}
 	OpVectorShuffle = &Opcode {
 		Opname:   "OpVectorShuffle",
+		Class:    "Composite",
+		Opcode:   79,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1974,6 +2409,8 @@
 	}
 	OpCompositeConstruct = &Opcode {
 		Opname:   "OpCompositeConstruct",
+		Class:    "Composite",
+		Opcode:   80,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -1994,6 +2431,8 @@
 	}
 	OpCompositeExtract = &Opcode {
 		Opname:   "OpCompositeExtract",
+		Class:    "Composite",
+		Opcode:   81,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2019,6 +2458,8 @@
 	}
 	OpCompositeInsert = &Opcode {
 		Opname:   "OpCompositeInsert",
+		Class:    "Composite",
+		Opcode:   82,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2049,6 +2490,8 @@
 	}
 	OpCopyObject = &Opcode {
 		Opname:   "OpCopyObject",
+		Class:    "Composite",
+		Opcode:   83,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2069,6 +2512,8 @@
 	}
 	OpTranspose = &Opcode {
 		Opname:   "OpTranspose",
+		Class:    "Composite",
+		Opcode:   84,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2089,6 +2534,8 @@
 	}
 	OpSampledImage = &Opcode {
 		Opname:   "OpSampledImage",
+		Class:    "Image",
+		Opcode:   86,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2114,6 +2561,8 @@
 	}
 	OpImageSampleImplicitLod = &Opcode {
 		Opname:   "OpImageSampleImplicitLod",
+		Class:    "Image",
+		Opcode:   87,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2144,6 +2593,8 @@
 	}
 	OpImageSampleExplicitLod = &Opcode {
 		Opname:   "OpImageSampleExplicitLod",
+		Class:    "Image",
+		Opcode:   88,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2174,6 +2625,8 @@
 	}
 	OpImageSampleDrefImplicitLod = &Opcode {
 		Opname:   "OpImageSampleDrefImplicitLod",
+		Class:    "Image",
+		Opcode:   89,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2209,6 +2662,8 @@
 	}
 	OpImageSampleDrefExplicitLod = &Opcode {
 		Opname:   "OpImageSampleDrefExplicitLod",
+		Class:    "Image",
+		Opcode:   90,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2244,6 +2699,8 @@
 	}
 	OpImageSampleProjImplicitLod = &Opcode {
 		Opname:   "OpImageSampleProjImplicitLod",
+		Class:    "Image",
+		Opcode:   91,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2274,6 +2731,8 @@
 	}
 	OpImageSampleProjExplicitLod = &Opcode {
 		Opname:   "OpImageSampleProjExplicitLod",
+		Class:    "Image",
+		Opcode:   92,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2304,6 +2763,8 @@
 	}
 	OpImageSampleProjDrefImplicitLod = &Opcode {
 		Opname:   "OpImageSampleProjDrefImplicitLod",
+		Class:    "Image",
+		Opcode:   93,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2339,6 +2800,8 @@
 	}
 	OpImageSampleProjDrefExplicitLod = &Opcode {
 		Opname:   "OpImageSampleProjDrefExplicitLod",
+		Class:    "Image",
+		Opcode:   94,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2374,6 +2837,8 @@
 	}
 	OpImageFetch = &Opcode {
 		Opname:   "OpImageFetch",
+		Class:    "Image",
+		Opcode:   95,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2404,6 +2869,8 @@
 	}
 	OpImageGather = &Opcode {
 		Opname:   "OpImageGather",
+		Class:    "Image",
+		Opcode:   96,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2439,6 +2906,8 @@
 	}
 	OpImageDrefGather = &Opcode {
 		Opname:   "OpImageDrefGather",
+		Class:    "Image",
+		Opcode:   97,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2474,6 +2943,8 @@
 	}
 	OpImageRead = &Opcode {
 		Opname:   "OpImageRead",
+		Class:    "Image",
+		Opcode:   98,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2504,6 +2975,8 @@
 	}
 	OpImageWrite = &Opcode {
 		Opname:   "OpImageWrite",
+		Class:    "Image",
+		Opcode:   99,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -2529,6 +3002,8 @@
 	}
 	OpImage = &Opcode {
 		Opname:   "OpImage",
+		Class:    "Image",
+		Opcode:   100,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2549,6 +3024,8 @@
 	}
 	OpImageQueryFormat = &Opcode {
 		Opname:   "OpImageQueryFormat",
+		Class:    "Image",
+		Opcode:   101,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2569,6 +3046,8 @@
 	}
 	OpImageQueryOrder = &Opcode {
 		Opname:   "OpImageQueryOrder",
+		Class:    "Image",
+		Opcode:   102,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2589,6 +3068,8 @@
 	}
 	OpImageQuerySizeLod = &Opcode {
 		Opname:   "OpImageQuerySizeLod",
+		Class:    "Image",
+		Opcode:   103,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2614,6 +3095,8 @@
 	}
 	OpImageQuerySize = &Opcode {
 		Opname:   "OpImageQuerySize",
+		Class:    "Image",
+		Opcode:   104,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2634,6 +3117,8 @@
 	}
 	OpImageQueryLod = &Opcode {
 		Opname:   "OpImageQueryLod",
+		Class:    "Image",
+		Opcode:   105,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2659,6 +3144,8 @@
 	}
 	OpImageQueryLevels = &Opcode {
 		Opname:   "OpImageQueryLevels",
+		Class:    "Image",
+		Opcode:   106,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2679,6 +3166,8 @@
 	}
 	OpImageQuerySamples = &Opcode {
 		Opname:   "OpImageQuerySamples",
+		Class:    "Image",
+		Opcode:   107,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2699,6 +3188,8 @@
 	}
 	OpConvertFToU = &Opcode {
 		Opname:   "OpConvertFToU",
+		Class:    "Conversion",
+		Opcode:   109,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2719,6 +3210,8 @@
 	}
 	OpConvertFToS = &Opcode {
 		Opname:   "OpConvertFToS",
+		Class:    "Conversion",
+		Opcode:   110,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2739,6 +3232,8 @@
 	}
 	OpConvertSToF = &Opcode {
 		Opname:   "OpConvertSToF",
+		Class:    "Conversion",
+		Opcode:   111,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2759,6 +3254,8 @@
 	}
 	OpConvertUToF = &Opcode {
 		Opname:   "OpConvertUToF",
+		Class:    "Conversion",
+		Opcode:   112,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2779,6 +3276,8 @@
 	}
 	OpUConvert = &Opcode {
 		Opname:   "OpUConvert",
+		Class:    "Conversion",
+		Opcode:   113,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2799,6 +3298,8 @@
 	}
 	OpSConvert = &Opcode {
 		Opname:   "OpSConvert",
+		Class:    "Conversion",
+		Opcode:   114,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2819,6 +3320,8 @@
 	}
 	OpFConvert = &Opcode {
 		Opname:   "OpFConvert",
+		Class:    "Conversion",
+		Opcode:   115,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2839,6 +3342,8 @@
 	}
 	OpQuantizeToF16 = &Opcode {
 		Opname:   "OpQuantizeToF16",
+		Class:    "Conversion",
+		Opcode:   116,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2859,6 +3364,8 @@
 	}
 	OpConvertPtrToU = &Opcode {
 		Opname:   "OpConvertPtrToU",
+		Class:    "Conversion",
+		Opcode:   117,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2879,6 +3386,8 @@
 	}
 	OpSatConvertSToU = &Opcode {
 		Opname:   "OpSatConvertSToU",
+		Class:    "Conversion",
+		Opcode:   118,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2899,6 +3408,8 @@
 	}
 	OpSatConvertUToS = &Opcode {
 		Opname:   "OpSatConvertUToS",
+		Class:    "Conversion",
+		Opcode:   119,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2919,6 +3430,8 @@
 	}
 	OpConvertUToPtr = &Opcode {
 		Opname:   "OpConvertUToPtr",
+		Class:    "Conversion",
+		Opcode:   120,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2939,6 +3452,8 @@
 	}
 	OpPtrCastToGeneric = &Opcode {
 		Opname:   "OpPtrCastToGeneric",
+		Class:    "Conversion",
+		Opcode:   121,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2959,6 +3474,8 @@
 	}
 	OpGenericCastToPtr = &Opcode {
 		Opname:   "OpGenericCastToPtr",
+		Class:    "Conversion",
+		Opcode:   122,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -2979,6 +3496,8 @@
 	}
 	OpGenericCastToPtrExplicit = &Opcode {
 		Opname:   "OpGenericCastToPtrExplicit",
+		Class:    "Conversion",
+		Opcode:   123,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3004,6 +3523,8 @@
 	}
 	OpBitcast = &Opcode {
 		Opname:   "OpBitcast",
+		Class:    "Conversion",
+		Opcode:   124,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3024,6 +3545,8 @@
 	}
 	OpSNegate = &Opcode {
 		Opname:   "OpSNegate",
+		Class:    "Arithmetic",
+		Opcode:   126,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3044,6 +3567,8 @@
 	}
 	OpFNegate = &Opcode {
 		Opname:   "OpFNegate",
+		Class:    "Arithmetic",
+		Opcode:   127,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3064,6 +3589,8 @@
 	}
 	OpIAdd = &Opcode {
 		Opname:   "OpIAdd",
+		Class:    "Arithmetic",
+		Opcode:   128,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3089,6 +3616,8 @@
 	}
 	OpFAdd = &Opcode {
 		Opname:   "OpFAdd",
+		Class:    "Arithmetic",
+		Opcode:   129,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3114,6 +3643,8 @@
 	}
 	OpISub = &Opcode {
 		Opname:   "OpISub",
+		Class:    "Arithmetic",
+		Opcode:   130,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3139,6 +3670,8 @@
 	}
 	OpFSub = &Opcode {
 		Opname:   "OpFSub",
+		Class:    "Arithmetic",
+		Opcode:   131,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3164,6 +3697,8 @@
 	}
 	OpIMul = &Opcode {
 		Opname:   "OpIMul",
+		Class:    "Arithmetic",
+		Opcode:   132,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3189,6 +3724,8 @@
 	}
 	OpFMul = &Opcode {
 		Opname:   "OpFMul",
+		Class:    "Arithmetic",
+		Opcode:   133,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3214,6 +3751,8 @@
 	}
 	OpUDiv = &Opcode {
 		Opname:   "OpUDiv",
+		Class:    "Arithmetic",
+		Opcode:   134,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3239,6 +3778,8 @@
 	}
 	OpSDiv = &Opcode {
 		Opname:   "OpSDiv",
+		Class:    "Arithmetic",
+		Opcode:   135,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3264,6 +3805,8 @@
 	}
 	OpFDiv = &Opcode {
 		Opname:   "OpFDiv",
+		Class:    "Arithmetic",
+		Opcode:   136,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3289,6 +3832,8 @@
 	}
 	OpUMod = &Opcode {
 		Opname:   "OpUMod",
+		Class:    "Arithmetic",
+		Opcode:   137,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3314,6 +3859,8 @@
 	}
 	OpSRem = &Opcode {
 		Opname:   "OpSRem",
+		Class:    "Arithmetic",
+		Opcode:   138,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3339,6 +3886,8 @@
 	}
 	OpSMod = &Opcode {
 		Opname:   "OpSMod",
+		Class:    "Arithmetic",
+		Opcode:   139,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3364,6 +3913,8 @@
 	}
 	OpFRem = &Opcode {
 		Opname:   "OpFRem",
+		Class:    "Arithmetic",
+		Opcode:   140,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3389,6 +3940,8 @@
 	}
 	OpFMod = &Opcode {
 		Opname:   "OpFMod",
+		Class:    "Arithmetic",
+		Opcode:   141,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3414,6 +3967,8 @@
 	}
 	OpVectorTimesScalar = &Opcode {
 		Opname:   "OpVectorTimesScalar",
+		Class:    "Arithmetic",
+		Opcode:   142,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3439,6 +3994,8 @@
 	}
 	OpMatrixTimesScalar = &Opcode {
 		Opname:   "OpMatrixTimesScalar",
+		Class:    "Arithmetic",
+		Opcode:   143,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3464,6 +4021,8 @@
 	}
 	OpVectorTimesMatrix = &Opcode {
 		Opname:   "OpVectorTimesMatrix",
+		Class:    "Arithmetic",
+		Opcode:   144,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3489,6 +4048,8 @@
 	}
 	OpMatrixTimesVector = &Opcode {
 		Opname:   "OpMatrixTimesVector",
+		Class:    "Arithmetic",
+		Opcode:   145,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3514,6 +4075,8 @@
 	}
 	OpMatrixTimesMatrix = &Opcode {
 		Opname:   "OpMatrixTimesMatrix",
+		Class:    "Arithmetic",
+		Opcode:   146,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3539,6 +4102,8 @@
 	}
 	OpOuterProduct = &Opcode {
 		Opname:   "OpOuterProduct",
+		Class:    "Arithmetic",
+		Opcode:   147,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3564,6 +4129,8 @@
 	}
 	OpDot = &Opcode {
 		Opname:   "OpDot",
+		Class:    "Arithmetic",
+		Opcode:   148,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3589,6 +4156,8 @@
 	}
 	OpIAddCarry = &Opcode {
 		Opname:   "OpIAddCarry",
+		Class:    "Arithmetic",
+		Opcode:   149,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3614,6 +4183,8 @@
 	}
 	OpISubBorrow = &Opcode {
 		Opname:   "OpISubBorrow",
+		Class:    "Arithmetic",
+		Opcode:   150,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3639,6 +4210,8 @@
 	}
 	OpUMulExtended = &Opcode {
 		Opname:   "OpUMulExtended",
+		Class:    "Arithmetic",
+		Opcode:   151,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3664,6 +4237,8 @@
 	}
 	OpSMulExtended = &Opcode {
 		Opname:   "OpSMulExtended",
+		Class:    "Arithmetic",
+		Opcode:   152,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3689,6 +4264,8 @@
 	}
 	OpAny = &Opcode {
 		Opname:   "OpAny",
+		Class:    "Relational_and_Logical",
+		Opcode:   154,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3709,6 +4286,8 @@
 	}
 	OpAll = &Opcode {
 		Opname:   "OpAll",
+		Class:    "Relational_and_Logical",
+		Opcode:   155,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3729,6 +4308,8 @@
 	}
 	OpIsNan = &Opcode {
 		Opname:   "OpIsNan",
+		Class:    "Relational_and_Logical",
+		Opcode:   156,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3749,6 +4330,8 @@
 	}
 	OpIsInf = &Opcode {
 		Opname:   "OpIsInf",
+		Class:    "Relational_and_Logical",
+		Opcode:   157,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3769,6 +4352,8 @@
 	}
 	OpIsFinite = &Opcode {
 		Opname:   "OpIsFinite",
+		Class:    "Relational_and_Logical",
+		Opcode:   158,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3789,6 +4374,8 @@
 	}
 	OpIsNormal = &Opcode {
 		Opname:   "OpIsNormal",
+		Class:    "Relational_and_Logical",
+		Opcode:   159,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3809,6 +4396,8 @@
 	}
 	OpSignBitSet = &Opcode {
 		Opname:   "OpSignBitSet",
+		Class:    "Relational_and_Logical",
+		Opcode:   160,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3829,6 +4418,8 @@
 	}
 	OpLessOrGreater = &Opcode {
 		Opname:   "OpLessOrGreater",
+		Class:    "Relational_and_Logical",
+		Opcode:   161,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3854,6 +4445,8 @@
 	}
 	OpOrdered = &Opcode {
 		Opname:   "OpOrdered",
+		Class:    "Relational_and_Logical",
+		Opcode:   162,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3879,6 +4472,8 @@
 	}
 	OpUnordered = &Opcode {
 		Opname:   "OpUnordered",
+		Class:    "Relational_and_Logical",
+		Opcode:   163,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3904,6 +4499,8 @@
 	}
 	OpLogicalEqual = &Opcode {
 		Opname:   "OpLogicalEqual",
+		Class:    "Relational_and_Logical",
+		Opcode:   164,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3929,6 +4526,8 @@
 	}
 	OpLogicalNotEqual = &Opcode {
 		Opname:   "OpLogicalNotEqual",
+		Class:    "Relational_and_Logical",
+		Opcode:   165,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3954,6 +4553,8 @@
 	}
 	OpLogicalOr = &Opcode {
 		Opname:   "OpLogicalOr",
+		Class:    "Relational_and_Logical",
+		Opcode:   166,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -3979,6 +4580,8 @@
 	}
 	OpLogicalAnd = &Opcode {
 		Opname:   "OpLogicalAnd",
+		Class:    "Relational_and_Logical",
+		Opcode:   167,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4004,6 +4607,8 @@
 	}
 	OpLogicalNot = &Opcode {
 		Opname:   "OpLogicalNot",
+		Class:    "Relational_and_Logical",
+		Opcode:   168,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4024,6 +4629,8 @@
 	}
 	OpSelect = &Opcode {
 		Opname:   "OpSelect",
+		Class:    "Relational_and_Logical",
+		Opcode:   169,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4054,6 +4661,8 @@
 	}
 	OpIEqual = &Opcode {
 		Opname:   "OpIEqual",
+		Class:    "Relational_and_Logical",
+		Opcode:   170,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4079,6 +4688,8 @@
 	}
 	OpINotEqual = &Opcode {
 		Opname:   "OpINotEqual",
+		Class:    "Relational_and_Logical",
+		Opcode:   171,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4104,6 +4715,8 @@
 	}
 	OpUGreaterThan = &Opcode {
 		Opname:   "OpUGreaterThan",
+		Class:    "Relational_and_Logical",
+		Opcode:   172,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4129,6 +4742,8 @@
 	}
 	OpSGreaterThan = &Opcode {
 		Opname:   "OpSGreaterThan",
+		Class:    "Relational_and_Logical",
+		Opcode:   173,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4154,6 +4769,8 @@
 	}
 	OpUGreaterThanEqual = &Opcode {
 		Opname:   "OpUGreaterThanEqual",
+		Class:    "Relational_and_Logical",
+		Opcode:   174,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4179,6 +4796,8 @@
 	}
 	OpSGreaterThanEqual = &Opcode {
 		Opname:   "OpSGreaterThanEqual",
+		Class:    "Relational_and_Logical",
+		Opcode:   175,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4204,6 +4823,8 @@
 	}
 	OpULessThan = &Opcode {
 		Opname:   "OpULessThan",
+		Class:    "Relational_and_Logical",
+		Opcode:   176,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4229,6 +4850,8 @@
 	}
 	OpSLessThan = &Opcode {
 		Opname:   "OpSLessThan",
+		Class:    "Relational_and_Logical",
+		Opcode:   177,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4254,6 +4877,8 @@
 	}
 	OpULessThanEqual = &Opcode {
 		Opname:   "OpULessThanEqual",
+		Class:    "Relational_and_Logical",
+		Opcode:   178,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4279,6 +4904,8 @@
 	}
 	OpSLessThanEqual = &Opcode {
 		Opname:   "OpSLessThanEqual",
+		Class:    "Relational_and_Logical",
+		Opcode:   179,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4304,6 +4931,8 @@
 	}
 	OpFOrdEqual = &Opcode {
 		Opname:   "OpFOrdEqual",
+		Class:    "Relational_and_Logical",
+		Opcode:   180,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4329,6 +4958,8 @@
 	}
 	OpFUnordEqual = &Opcode {
 		Opname:   "OpFUnordEqual",
+		Class:    "Relational_and_Logical",
+		Opcode:   181,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4354,6 +4985,8 @@
 	}
 	OpFOrdNotEqual = &Opcode {
 		Opname:   "OpFOrdNotEqual",
+		Class:    "Relational_and_Logical",
+		Opcode:   182,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4379,6 +5012,8 @@
 	}
 	OpFUnordNotEqual = &Opcode {
 		Opname:   "OpFUnordNotEqual",
+		Class:    "Relational_and_Logical",
+		Opcode:   183,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4404,6 +5039,8 @@
 	}
 	OpFOrdLessThan = &Opcode {
 		Opname:   "OpFOrdLessThan",
+		Class:    "Relational_and_Logical",
+		Opcode:   184,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4429,6 +5066,8 @@
 	}
 	OpFUnordLessThan = &Opcode {
 		Opname:   "OpFUnordLessThan",
+		Class:    "Relational_and_Logical",
+		Opcode:   185,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4454,6 +5093,8 @@
 	}
 	OpFOrdGreaterThan = &Opcode {
 		Opname:   "OpFOrdGreaterThan",
+		Class:    "Relational_and_Logical",
+		Opcode:   186,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4479,6 +5120,8 @@
 	}
 	OpFUnordGreaterThan = &Opcode {
 		Opname:   "OpFUnordGreaterThan",
+		Class:    "Relational_and_Logical",
+		Opcode:   187,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4504,6 +5147,8 @@
 	}
 	OpFOrdLessThanEqual = &Opcode {
 		Opname:   "OpFOrdLessThanEqual",
+		Class:    "Relational_and_Logical",
+		Opcode:   188,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4529,6 +5174,8 @@
 	}
 	OpFUnordLessThanEqual = &Opcode {
 		Opname:   "OpFUnordLessThanEqual",
+		Class:    "Relational_and_Logical",
+		Opcode:   189,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4554,6 +5201,8 @@
 	}
 	OpFOrdGreaterThanEqual = &Opcode {
 		Opname:   "OpFOrdGreaterThanEqual",
+		Class:    "Relational_and_Logical",
+		Opcode:   190,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4579,6 +5228,8 @@
 	}
 	OpFUnordGreaterThanEqual = &Opcode {
 		Opname:   "OpFUnordGreaterThanEqual",
+		Class:    "Relational_and_Logical",
+		Opcode:   191,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4604,6 +5255,8 @@
 	}
 	OpShiftRightLogical = &Opcode {
 		Opname:   "OpShiftRightLogical",
+		Class:    "Bit",
+		Opcode:   194,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4629,6 +5282,8 @@
 	}
 	OpShiftRightArithmetic = &Opcode {
 		Opname:   "OpShiftRightArithmetic",
+		Class:    "Bit",
+		Opcode:   195,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4654,6 +5309,8 @@
 	}
 	OpShiftLeftLogical = &Opcode {
 		Opname:   "OpShiftLeftLogical",
+		Class:    "Bit",
+		Opcode:   196,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4679,6 +5336,8 @@
 	}
 	OpBitwiseOr = &Opcode {
 		Opname:   "OpBitwiseOr",
+		Class:    "Bit",
+		Opcode:   197,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4704,6 +5363,8 @@
 	}
 	OpBitwiseXor = &Opcode {
 		Opname:   "OpBitwiseXor",
+		Class:    "Bit",
+		Opcode:   198,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4729,6 +5390,8 @@
 	}
 	OpBitwiseAnd = &Opcode {
 		Opname:   "OpBitwiseAnd",
+		Class:    "Bit",
+		Opcode:   199,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4754,6 +5417,8 @@
 	}
 	OpNot = &Opcode {
 		Opname:   "OpNot",
+		Class:    "Bit",
+		Opcode:   200,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4774,6 +5439,8 @@
 	}
 	OpBitFieldInsert = &Opcode {
 		Opname:   "OpBitFieldInsert",
+		Class:    "Bit",
+		Opcode:   201,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4809,6 +5476,8 @@
 	}
 	OpBitFieldSExtract = &Opcode {
 		Opname:   "OpBitFieldSExtract",
+		Class:    "Bit",
+		Opcode:   202,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4839,6 +5508,8 @@
 	}
 	OpBitFieldUExtract = &Opcode {
 		Opname:   "OpBitFieldUExtract",
+		Class:    "Bit",
+		Opcode:   203,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4869,6 +5540,8 @@
 	}
 	OpBitReverse = &Opcode {
 		Opname:   "OpBitReverse",
+		Class:    "Bit",
+		Opcode:   204,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4889,6 +5562,8 @@
 	}
 	OpBitCount = &Opcode {
 		Opname:   "OpBitCount",
+		Class:    "Bit",
+		Opcode:   205,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4909,6 +5584,8 @@
 	}
 	OpDPdx = &Opcode {
 		Opname:   "OpDPdx",
+		Class:    "Derivative",
+		Opcode:   207,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4929,6 +5606,8 @@
 	}
 	OpDPdy = &Opcode {
 		Opname:   "OpDPdy",
+		Class:    "Derivative",
+		Opcode:   208,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4949,6 +5628,8 @@
 	}
 	OpFwidth = &Opcode {
 		Opname:   "OpFwidth",
+		Class:    "Derivative",
+		Opcode:   209,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4969,6 +5650,8 @@
 	}
 	OpDPdxFine = &Opcode {
 		Opname:   "OpDPdxFine",
+		Class:    "Derivative",
+		Opcode:   210,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -4989,6 +5672,8 @@
 	}
 	OpDPdyFine = &Opcode {
 		Opname:   "OpDPdyFine",
+		Class:    "Derivative",
+		Opcode:   211,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5009,6 +5694,8 @@
 	}
 	OpFwidthFine = &Opcode {
 		Opname:   "OpFwidthFine",
+		Class:    "Derivative",
+		Opcode:   212,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5029,6 +5716,8 @@
 	}
 	OpDPdxCoarse = &Opcode {
 		Opname:   "OpDPdxCoarse",
+		Class:    "Derivative",
+		Opcode:   213,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5049,6 +5738,8 @@
 	}
 	OpDPdyCoarse = &Opcode {
 		Opname:   "OpDPdyCoarse",
+		Class:    "Derivative",
+		Opcode:   214,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5069,6 +5760,8 @@
 	}
 	OpFwidthCoarse = &Opcode {
 		Opname:   "OpFwidthCoarse",
+		Class:    "Derivative",
+		Opcode:   215,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5089,16 +5782,22 @@
 	}
 	OpEmitVertex = &Opcode {
 		Opname:   "OpEmitVertex",
+		Class:    "Primitive",
+		Opcode:   218,
 		Operands: []Operand {
 		},
 	}
 	OpEndPrimitive = &Opcode {
 		Opname:   "OpEndPrimitive",
+		Class:    "Primitive",
+		Opcode:   219,
 		Operands: []Operand {
 		},
 	}
 	OpEmitStreamVertex = &Opcode {
 		Opname:   "OpEmitStreamVertex",
+		Class:    "Primitive",
+		Opcode:   220,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -5109,6 +5808,8 @@
 	}
 	OpEndStreamPrimitive = &Opcode {
 		Opname:   "OpEndStreamPrimitive",
+		Class:    "Primitive",
+		Opcode:   221,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -5119,6 +5820,8 @@
 	}
 	OpControlBarrier = &Opcode {
 		Opname:   "OpControlBarrier",
+		Class:    "Barrier",
+		Opcode:   224,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdScope,
@@ -5139,6 +5842,8 @@
 	}
 	OpMemoryBarrier = &Opcode {
 		Opname:   "OpMemoryBarrier",
+		Class:    "Barrier",
+		Opcode:   225,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdScope,
@@ -5154,6 +5859,8 @@
 	}
 	OpAtomicLoad = &Opcode {
 		Opname:   "OpAtomicLoad",
+		Class:    "Atomic",
+		Opcode:   227,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5184,6 +5891,8 @@
 	}
 	OpAtomicStore = &Opcode {
 		Opname:   "OpAtomicStore",
+		Class:    "Atomic",
+		Opcode:   228,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -5209,6 +5918,8 @@
 	}
 	OpAtomicExchange = &Opcode {
 		Opname:   "OpAtomicExchange",
+		Class:    "Atomic",
+		Opcode:   229,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5244,6 +5955,8 @@
 	}
 	OpAtomicCompareExchange = &Opcode {
 		Opname:   "OpAtomicCompareExchange",
+		Class:    "Atomic",
+		Opcode:   230,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5289,6 +6002,8 @@
 	}
 	OpAtomicCompareExchangeWeak = &Opcode {
 		Opname:   "OpAtomicCompareExchangeWeak",
+		Class:    "Atomic",
+		Opcode:   231,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5334,6 +6049,8 @@
 	}
 	OpAtomicIIncrement = &Opcode {
 		Opname:   "OpAtomicIIncrement",
+		Class:    "Atomic",
+		Opcode:   232,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5364,6 +6081,8 @@
 	}
 	OpAtomicIDecrement = &Opcode {
 		Opname:   "OpAtomicIDecrement",
+		Class:    "Atomic",
+		Opcode:   233,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5394,6 +6113,8 @@
 	}
 	OpAtomicIAdd = &Opcode {
 		Opname:   "OpAtomicIAdd",
+		Class:    "Atomic",
+		Opcode:   234,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5429,6 +6150,8 @@
 	}
 	OpAtomicISub = &Opcode {
 		Opname:   "OpAtomicISub",
+		Class:    "Atomic",
+		Opcode:   235,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5464,6 +6187,8 @@
 	}
 	OpAtomicSMin = &Opcode {
 		Opname:   "OpAtomicSMin",
+		Class:    "Atomic",
+		Opcode:   236,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5499,6 +6224,8 @@
 	}
 	OpAtomicUMin = &Opcode {
 		Opname:   "OpAtomicUMin",
+		Class:    "Atomic",
+		Opcode:   237,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5534,6 +6261,8 @@
 	}
 	OpAtomicSMax = &Opcode {
 		Opname:   "OpAtomicSMax",
+		Class:    "Atomic",
+		Opcode:   238,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5569,6 +6298,8 @@
 	}
 	OpAtomicUMax = &Opcode {
 		Opname:   "OpAtomicUMax",
+		Class:    "Atomic",
+		Opcode:   239,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5604,6 +6335,8 @@
 	}
 	OpAtomicAnd = &Opcode {
 		Opname:   "OpAtomicAnd",
+		Class:    "Atomic",
+		Opcode:   240,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5639,6 +6372,8 @@
 	}
 	OpAtomicOr = &Opcode {
 		Opname:   "OpAtomicOr",
+		Class:    "Atomic",
+		Opcode:   241,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5674,6 +6409,8 @@
 	}
 	OpAtomicXor = &Opcode {
 		Opname:   "OpAtomicXor",
+		Class:    "Atomic",
+		Opcode:   242,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5709,6 +6446,8 @@
 	}
 	OpPhi = &Opcode {
 		Opname:   "OpPhi",
+		Class:    "Control-Flow",
+		Opcode:   245,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5729,6 +6468,8 @@
 	}
 	OpLoopMerge = &Opcode {
 		Opname:   "OpLoopMerge",
+		Class:    "Control-Flow",
+		Opcode:   246,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -5749,6 +6490,8 @@
 	}
 	OpSelectionMerge = &Opcode {
 		Opname:   "OpSelectionMerge",
+		Class:    "Control-Flow",
+		Opcode:   247,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -5764,6 +6507,8 @@
 	}
 	OpLabel = &Opcode {
 		Opname:   "OpLabel",
+		Class:    "Control-Flow",
+		Opcode:   248,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -5774,6 +6519,8 @@
 	}
 	OpBranch = &Opcode {
 		Opname:   "OpBranch",
+		Class:    "Control-Flow",
+		Opcode:   249,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -5784,6 +6531,8 @@
 	}
 	OpBranchConditional = &Opcode {
 		Opname:   "OpBranchConditional",
+		Class:    "Control-Flow",
+		Opcode:   250,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -5809,6 +6558,8 @@
 	}
 	OpSwitch = &Opcode {
 		Opname:   "OpSwitch",
+		Class:    "Control-Flow",
+		Opcode:   251,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -5829,16 +6580,22 @@
 	}
 	OpKill = &Opcode {
 		Opname:   "OpKill",
+		Class:    "Control-Flow",
+		Opcode:   252,
 		Operands: []Operand {
 		},
 	}
 	OpReturn = &Opcode {
 		Opname:   "OpReturn",
+		Class:    "Control-Flow",
+		Opcode:   253,
 		Operands: []Operand {
 		},
 	}
 	OpReturnValue = &Opcode {
 		Opname:   "OpReturnValue",
+		Class:    "Control-Flow",
+		Opcode:   254,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -5849,11 +6606,15 @@
 	}
 	OpUnreachable = &Opcode {
 		Opname:   "OpUnreachable",
+		Class:    "Control-Flow",
+		Opcode:   255,
 		Operands: []Operand {
 		},
 	}
 	OpLifetimeStart = &Opcode {
 		Opname:   "OpLifetimeStart",
+		Class:    "Control-Flow",
+		Opcode:   256,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -5869,6 +6630,8 @@
 	}
 	OpLifetimeStop = &Opcode {
 		Opname:   "OpLifetimeStop",
+		Class:    "Control-Flow",
+		Opcode:   257,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -5884,6 +6647,8 @@
 	}
 	OpGroupAsyncCopy = &Opcode {
 		Opname:   "OpGroupAsyncCopy",
+		Class:    "Group",
+		Opcode:   259,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5929,6 +6694,8 @@
 	}
 	OpGroupWaitEvents = &Opcode {
 		Opname:   "OpGroupWaitEvents",
+		Class:    "Group",
+		Opcode:   260,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdScope,
@@ -5949,6 +6716,8 @@
 	}
 	OpGroupAll = &Opcode {
 		Opname:   "OpGroupAll",
+		Class:    "Group",
+		Opcode:   261,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5974,6 +6743,8 @@
 	}
 	OpGroupAny = &Opcode {
 		Opname:   "OpGroupAny",
+		Class:    "Group",
+		Opcode:   262,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -5999,6 +6770,8 @@
 	}
 	OpGroupBroadcast = &Opcode {
 		Opname:   "OpGroupBroadcast",
+		Class:    "Group",
+		Opcode:   263,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6029,6 +6802,8 @@
 	}
 	OpGroupIAdd = &Opcode {
 		Opname:   "OpGroupIAdd",
+		Class:    "Group",
+		Opcode:   264,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6059,6 +6834,8 @@
 	}
 	OpGroupFAdd = &Opcode {
 		Opname:   "OpGroupFAdd",
+		Class:    "Group",
+		Opcode:   265,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6089,6 +6866,8 @@
 	}
 	OpGroupFMin = &Opcode {
 		Opname:   "OpGroupFMin",
+		Class:    "Group",
+		Opcode:   266,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6119,6 +6898,8 @@
 	}
 	OpGroupUMin = &Opcode {
 		Opname:   "OpGroupUMin",
+		Class:    "Group",
+		Opcode:   267,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6149,6 +6930,8 @@
 	}
 	OpGroupSMin = &Opcode {
 		Opname:   "OpGroupSMin",
+		Class:    "Group",
+		Opcode:   268,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6179,6 +6962,8 @@
 	}
 	OpGroupFMax = &Opcode {
 		Opname:   "OpGroupFMax",
+		Class:    "Group",
+		Opcode:   269,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6209,6 +6994,8 @@
 	}
 	OpGroupUMax = &Opcode {
 		Opname:   "OpGroupUMax",
+		Class:    "Group",
+		Opcode:   270,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6239,6 +7026,8 @@
 	}
 	OpGroupSMax = &Opcode {
 		Opname:   "OpGroupSMax",
+		Class:    "Group",
+		Opcode:   271,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6269,6 +7058,8 @@
 	}
 	OpReadPipe = &Opcode {
 		Opname:   "OpReadPipe",
+		Class:    "Pipe",
+		Opcode:   274,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6304,6 +7095,8 @@
 	}
 	OpWritePipe = &Opcode {
 		Opname:   "OpWritePipe",
+		Class:    "Pipe",
+		Opcode:   275,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6339,6 +7132,8 @@
 	}
 	OpReservedReadPipe = &Opcode {
 		Opname:   "OpReservedReadPipe",
+		Class:    "Pipe",
+		Opcode:   276,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6384,6 +7179,8 @@
 	}
 	OpReservedWritePipe = &Opcode {
 		Opname:   "OpReservedWritePipe",
+		Class:    "Pipe",
+		Opcode:   277,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6429,6 +7226,8 @@
 	}
 	OpReserveReadPipePackets = &Opcode {
 		Opname:   "OpReserveReadPipePackets",
+		Class:    "Pipe",
+		Opcode:   278,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6464,6 +7263,8 @@
 	}
 	OpReserveWritePipePackets = &Opcode {
 		Opname:   "OpReserveWritePipePackets",
+		Class:    "Pipe",
+		Opcode:   279,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6499,6 +7300,8 @@
 	}
 	OpCommitReadPipe = &Opcode {
 		Opname:   "OpCommitReadPipe",
+		Class:    "Pipe",
+		Opcode:   280,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -6524,6 +7327,8 @@
 	}
 	OpCommitWritePipe = &Opcode {
 		Opname:   "OpCommitWritePipe",
+		Class:    "Pipe",
+		Opcode:   281,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -6549,6 +7354,8 @@
 	}
 	OpIsValidReserveId = &Opcode {
 		Opname:   "OpIsValidReserveId",
+		Class:    "Pipe",
+		Opcode:   282,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6569,6 +7376,8 @@
 	}
 	OpGetNumPipePackets = &Opcode {
 		Opname:   "OpGetNumPipePackets",
+		Class:    "Pipe",
+		Opcode:   283,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6599,6 +7408,8 @@
 	}
 	OpGetMaxPipePackets = &Opcode {
 		Opname:   "OpGetMaxPipePackets",
+		Class:    "Pipe",
+		Opcode:   284,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6629,6 +7440,8 @@
 	}
 	OpGroupReserveReadPipePackets = &Opcode {
 		Opname:   "OpGroupReserveReadPipePackets",
+		Class:    "Pipe",
+		Opcode:   285,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6669,6 +7482,8 @@
 	}
 	OpGroupReserveWritePipePackets = &Opcode {
 		Opname:   "OpGroupReserveWritePipePackets",
+		Class:    "Pipe",
+		Opcode:   286,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6709,6 +7524,8 @@
 	}
 	OpGroupCommitReadPipe = &Opcode {
 		Opname:   "OpGroupCommitReadPipe",
+		Class:    "Pipe",
+		Opcode:   287,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdScope,
@@ -6739,6 +7556,8 @@
 	}
 	OpGroupCommitWritePipe = &Opcode {
 		Opname:   "OpGroupCommitWritePipe",
+		Class:    "Pipe",
+		Opcode:   288,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdScope,
@@ -6769,6 +7588,8 @@
 	}
 	OpEnqueueMarker = &Opcode {
 		Opname:   "OpEnqueueMarker",
+		Class:    "Device-Side_Enqueue",
+		Opcode:   291,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6804,6 +7625,8 @@
 	}
 	OpEnqueueKernel = &Opcode {
 		Opname:   "OpEnqueueKernel",
+		Class:    "Device-Side_Enqueue",
+		Opcode:   292,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6874,6 +7697,8 @@
 	}
 	OpGetKernelNDrangeSubGroupCount = &Opcode {
 		Opname:   "OpGetKernelNDrangeSubGroupCount",
+		Class:    "Device-Side_Enqueue",
+		Opcode:   293,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6914,6 +7739,8 @@
 	}
 	OpGetKernelNDrangeMaxSubGroupSize = &Opcode {
 		Opname:   "OpGetKernelNDrangeMaxSubGroupSize",
+		Class:    "Device-Side_Enqueue",
+		Opcode:   294,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6954,6 +7781,8 @@
 	}
 	OpGetKernelWorkGroupSize = &Opcode {
 		Opname:   "OpGetKernelWorkGroupSize",
+		Class:    "Device-Side_Enqueue",
+		Opcode:   295,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -6989,6 +7818,8 @@
 	}
 	OpGetKernelPreferredWorkGroupSizeMultiple = &Opcode {
 		Opname:   "OpGetKernelPreferredWorkGroupSizeMultiple",
+		Class:    "Device-Side_Enqueue",
+		Opcode:   296,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7024,6 +7855,8 @@
 	}
 	OpRetainEvent = &Opcode {
 		Opname:   "OpRetainEvent",
+		Class:    "Device-Side_Enqueue",
+		Opcode:   297,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -7034,6 +7867,8 @@
 	}
 	OpReleaseEvent = &Opcode {
 		Opname:   "OpReleaseEvent",
+		Class:    "Device-Side_Enqueue",
+		Opcode:   298,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -7044,6 +7879,8 @@
 	}
 	OpCreateUserEvent = &Opcode {
 		Opname:   "OpCreateUserEvent",
+		Class:    "Device-Side_Enqueue",
+		Opcode:   299,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7059,6 +7896,8 @@
 	}
 	OpIsValidEvent = &Opcode {
 		Opname:   "OpIsValidEvent",
+		Class:    "Device-Side_Enqueue",
+		Opcode:   300,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7079,6 +7918,8 @@
 	}
 	OpSetUserEventStatus = &Opcode {
 		Opname:   "OpSetUserEventStatus",
+		Class:    "Device-Side_Enqueue",
+		Opcode:   301,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -7094,6 +7935,8 @@
 	}
 	OpCaptureEventProfilingInfo = &Opcode {
 		Opname:   "OpCaptureEventProfilingInfo",
+		Class:    "Device-Side_Enqueue",
+		Opcode:   302,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -7114,6 +7957,8 @@
 	}
 	OpGetDefaultQueue = &Opcode {
 		Opname:   "OpGetDefaultQueue",
+		Class:    "Device-Side_Enqueue",
+		Opcode:   303,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7129,6 +7974,8 @@
 	}
 	OpBuildNDRange = &Opcode {
 		Opname:   "OpBuildNDRange",
+		Class:    "Device-Side_Enqueue",
+		Opcode:   304,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7159,6 +8006,8 @@
 	}
 	OpImageSparseSampleImplicitLod = &Opcode {
 		Opname:   "OpImageSparseSampleImplicitLod",
+		Class:    "Image",
+		Opcode:   305,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7189,6 +8038,8 @@
 	}
 	OpImageSparseSampleExplicitLod = &Opcode {
 		Opname:   "OpImageSparseSampleExplicitLod",
+		Class:    "Image",
+		Opcode:   306,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7219,6 +8070,8 @@
 	}
 	OpImageSparseSampleDrefImplicitLod = &Opcode {
 		Opname:   "OpImageSparseSampleDrefImplicitLod",
+		Class:    "Image",
+		Opcode:   307,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7254,6 +8107,8 @@
 	}
 	OpImageSparseSampleDrefExplicitLod = &Opcode {
 		Opname:   "OpImageSparseSampleDrefExplicitLod",
+		Class:    "Image",
+		Opcode:   308,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7289,6 +8144,8 @@
 	}
 	OpImageSparseSampleProjImplicitLod = &Opcode {
 		Opname:   "OpImageSparseSampleProjImplicitLod",
+		Class:    "Image",
+		Opcode:   309,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7319,6 +8176,8 @@
 	}
 	OpImageSparseSampleProjExplicitLod = &Opcode {
 		Opname:   "OpImageSparseSampleProjExplicitLod",
+		Class:    "Image",
+		Opcode:   310,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7349,6 +8208,8 @@
 	}
 	OpImageSparseSampleProjDrefImplicitLod = &Opcode {
 		Opname:   "OpImageSparseSampleProjDrefImplicitLod",
+		Class:    "Image",
+		Opcode:   311,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7384,6 +8245,8 @@
 	}
 	OpImageSparseSampleProjDrefExplicitLod = &Opcode {
 		Opname:   "OpImageSparseSampleProjDrefExplicitLod",
+		Class:    "Image",
+		Opcode:   312,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7419,6 +8282,8 @@
 	}
 	OpImageSparseFetch = &Opcode {
 		Opname:   "OpImageSparseFetch",
+		Class:    "Image",
+		Opcode:   313,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7449,6 +8314,8 @@
 	}
 	OpImageSparseGather = &Opcode {
 		Opname:   "OpImageSparseGather",
+		Class:    "Image",
+		Opcode:   314,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7484,6 +8351,8 @@
 	}
 	OpImageSparseDrefGather = &Opcode {
 		Opname:   "OpImageSparseDrefGather",
+		Class:    "Image",
+		Opcode:   315,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7519,6 +8388,8 @@
 	}
 	OpImageSparseTexelsResident = &Opcode {
 		Opname:   "OpImageSparseTexelsResident",
+		Class:    "Image",
+		Opcode:   316,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7539,11 +8410,15 @@
 	}
 	OpNoLine = &Opcode {
 		Opname:   "OpNoLine",
+		Class:    "Debug",
+		Opcode:   317,
 		Operands: []Operand {
 		},
 	}
 	OpAtomicFlagTestAndSet = &Opcode {
 		Opname:   "OpAtomicFlagTestAndSet",
+		Class:    "Atomic",
+		Opcode:   318,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7574,6 +8449,8 @@
 	}
 	OpAtomicFlagClear = &Opcode {
 		Opname:   "OpAtomicFlagClear",
+		Class:    "Atomic",
+		Opcode:   319,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -7594,6 +8471,8 @@
 	}
 	OpImageSparseRead = &Opcode {
 		Opname:   "OpImageSparseRead",
+		Class:    "Image",
+		Opcode:   320,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7624,6 +8503,8 @@
 	}
 	OpSizeOf = &Opcode {
 		Opname:   "OpSizeOf",
+		Class:    "Miscellaneous",
+		Opcode:   321,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7644,6 +8525,8 @@
 	}
 	OpTypePipeStorage = &Opcode {
 		Opname:   "OpTypePipeStorage",
+		Class:    "Type-Declaration",
+		Opcode:   322,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -7654,6 +8537,8 @@
 	}
 	OpConstantPipeStorage = &Opcode {
 		Opname:   "OpConstantPipeStorage",
+		Class:    "Pipe",
+		Opcode:   323,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7684,6 +8569,8 @@
 	}
 	OpCreatePipeFromPipeStorage = &Opcode {
 		Opname:   "OpCreatePipeFromPipeStorage",
+		Class:    "Pipe",
+		Opcode:   324,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7704,6 +8591,8 @@
 	}
 	OpGetKernelLocalSizeForSubgroupCount = &Opcode {
 		Opname:   "OpGetKernelLocalSizeForSubgroupCount",
+		Class:    "Device-Side_Enqueue",
+		Opcode:   325,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7744,6 +8633,8 @@
 	}
 	OpGetKernelMaxNumSubgroups = &Opcode {
 		Opname:   "OpGetKernelMaxNumSubgroups",
+		Class:    "Device-Side_Enqueue",
+		Opcode:   326,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7779,6 +8670,8 @@
 	}
 	OpTypeNamedBarrier = &Opcode {
 		Opname:   "OpTypeNamedBarrier",
+		Class:    "Type-Declaration",
+		Opcode:   327,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -7789,6 +8682,8 @@
 	}
 	OpNamedBarrierInitialize = &Opcode {
 		Opname:   "OpNamedBarrierInitialize",
+		Class:    "Barrier",
+		Opcode:   328,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7809,6 +8704,8 @@
 	}
 	OpMemoryNamedBarrier = &Opcode {
 		Opname:   "OpMemoryNamedBarrier",
+		Class:    "Barrier",
+		Opcode:   329,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -7829,6 +8726,8 @@
 	}
 	OpModuleProcessed = &Opcode {
 		Opname:   "OpModuleProcessed",
+		Class:    "Debug",
+		Opcode:   330,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindLiteralString,
@@ -7839,6 +8738,8 @@
 	}
 	OpExecutionModeId = &Opcode {
 		Opname:   "OpExecutionModeId",
+		Class:    "Mode-Setting",
+		Opcode:   331,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -7854,6 +8755,8 @@
 	}
 	OpDecorateId = &Opcode {
 		Opname:   "OpDecorateId",
+		Class:    "Annotation",
+		Opcode:   332,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -7869,6 +8772,8 @@
 	}
 	OpGroupNonUniformElect = &Opcode {
 		Opname:   "OpGroupNonUniformElect",
+		Class:    "Non-Uniform",
+		Opcode:   333,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7889,6 +8794,8 @@
 	}
 	OpGroupNonUniformAll = &Opcode {
 		Opname:   "OpGroupNonUniformAll",
+		Class:    "Non-Uniform",
+		Opcode:   334,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7914,6 +8821,8 @@
 	}
 	OpGroupNonUniformAny = &Opcode {
 		Opname:   "OpGroupNonUniformAny",
+		Class:    "Non-Uniform",
+		Opcode:   335,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7939,6 +8848,8 @@
 	}
 	OpGroupNonUniformAllEqual = &Opcode {
 		Opname:   "OpGroupNonUniformAllEqual",
+		Class:    "Non-Uniform",
+		Opcode:   336,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7964,6 +8875,8 @@
 	}
 	OpGroupNonUniformBroadcast = &Opcode {
 		Opname:   "OpGroupNonUniformBroadcast",
+		Class:    "Non-Uniform",
+		Opcode:   337,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -7994,6 +8907,8 @@
 	}
 	OpGroupNonUniformBroadcastFirst = &Opcode {
 		Opname:   "OpGroupNonUniformBroadcastFirst",
+		Class:    "Non-Uniform",
+		Opcode:   338,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8019,6 +8934,8 @@
 	}
 	OpGroupNonUniformBallot = &Opcode {
 		Opname:   "OpGroupNonUniformBallot",
+		Class:    "Non-Uniform",
+		Opcode:   339,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8044,6 +8961,8 @@
 	}
 	OpGroupNonUniformInverseBallot = &Opcode {
 		Opname:   "OpGroupNonUniformInverseBallot",
+		Class:    "Non-Uniform",
+		Opcode:   340,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8069,6 +8988,8 @@
 	}
 	OpGroupNonUniformBallotBitExtract = &Opcode {
 		Opname:   "OpGroupNonUniformBallotBitExtract",
+		Class:    "Non-Uniform",
+		Opcode:   341,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8099,6 +9020,8 @@
 	}
 	OpGroupNonUniformBallotBitCount = &Opcode {
 		Opname:   "OpGroupNonUniformBallotBitCount",
+		Class:    "Non-Uniform",
+		Opcode:   342,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8129,6 +9052,8 @@
 	}
 	OpGroupNonUniformBallotFindLSB = &Opcode {
 		Opname:   "OpGroupNonUniformBallotFindLSB",
+		Class:    "Non-Uniform",
+		Opcode:   343,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8154,6 +9079,8 @@
 	}
 	OpGroupNonUniformBallotFindMSB = &Opcode {
 		Opname:   "OpGroupNonUniformBallotFindMSB",
+		Class:    "Non-Uniform",
+		Opcode:   344,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8179,6 +9106,8 @@
 	}
 	OpGroupNonUniformShuffle = &Opcode {
 		Opname:   "OpGroupNonUniformShuffle",
+		Class:    "Non-Uniform",
+		Opcode:   345,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8209,6 +9138,8 @@
 	}
 	OpGroupNonUniformShuffleXor = &Opcode {
 		Opname:   "OpGroupNonUniformShuffleXor",
+		Class:    "Non-Uniform",
+		Opcode:   346,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8239,6 +9170,8 @@
 	}
 	OpGroupNonUniformShuffleUp = &Opcode {
 		Opname:   "OpGroupNonUniformShuffleUp",
+		Class:    "Non-Uniform",
+		Opcode:   347,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8269,6 +9202,8 @@
 	}
 	OpGroupNonUniformShuffleDown = &Opcode {
 		Opname:   "OpGroupNonUniformShuffleDown",
+		Class:    "Non-Uniform",
+		Opcode:   348,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8299,6 +9234,8 @@
 	}
 	OpGroupNonUniformIAdd = &Opcode {
 		Opname:   "OpGroupNonUniformIAdd",
+		Class:    "Non-Uniform",
+		Opcode:   349,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8334,6 +9271,8 @@
 	}
 	OpGroupNonUniformFAdd = &Opcode {
 		Opname:   "OpGroupNonUniformFAdd",
+		Class:    "Non-Uniform",
+		Opcode:   350,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8369,6 +9308,8 @@
 	}
 	OpGroupNonUniformIMul = &Opcode {
 		Opname:   "OpGroupNonUniformIMul",
+		Class:    "Non-Uniform",
+		Opcode:   351,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8404,6 +9345,8 @@
 	}
 	OpGroupNonUniformFMul = &Opcode {
 		Opname:   "OpGroupNonUniformFMul",
+		Class:    "Non-Uniform",
+		Opcode:   352,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8439,6 +9382,8 @@
 	}
 	OpGroupNonUniformSMin = &Opcode {
 		Opname:   "OpGroupNonUniformSMin",
+		Class:    "Non-Uniform",
+		Opcode:   353,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8474,6 +9419,8 @@
 	}
 	OpGroupNonUniformUMin = &Opcode {
 		Opname:   "OpGroupNonUniformUMin",
+		Class:    "Non-Uniform",
+		Opcode:   354,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8509,6 +9456,8 @@
 	}
 	OpGroupNonUniformFMin = &Opcode {
 		Opname:   "OpGroupNonUniformFMin",
+		Class:    "Non-Uniform",
+		Opcode:   355,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8544,6 +9493,8 @@
 	}
 	OpGroupNonUniformSMax = &Opcode {
 		Opname:   "OpGroupNonUniformSMax",
+		Class:    "Non-Uniform",
+		Opcode:   356,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8579,6 +9530,8 @@
 	}
 	OpGroupNonUniformUMax = &Opcode {
 		Opname:   "OpGroupNonUniformUMax",
+		Class:    "Non-Uniform",
+		Opcode:   357,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8614,6 +9567,8 @@
 	}
 	OpGroupNonUniformFMax = &Opcode {
 		Opname:   "OpGroupNonUniformFMax",
+		Class:    "Non-Uniform",
+		Opcode:   358,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8649,6 +9604,8 @@
 	}
 	OpGroupNonUniformBitwiseAnd = &Opcode {
 		Opname:   "OpGroupNonUniformBitwiseAnd",
+		Class:    "Non-Uniform",
+		Opcode:   359,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8684,6 +9641,8 @@
 	}
 	OpGroupNonUniformBitwiseOr = &Opcode {
 		Opname:   "OpGroupNonUniformBitwiseOr",
+		Class:    "Non-Uniform",
+		Opcode:   360,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8719,6 +9678,8 @@
 	}
 	OpGroupNonUniformBitwiseXor = &Opcode {
 		Opname:   "OpGroupNonUniformBitwiseXor",
+		Class:    "Non-Uniform",
+		Opcode:   361,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8754,6 +9715,8 @@
 	}
 	OpGroupNonUniformLogicalAnd = &Opcode {
 		Opname:   "OpGroupNonUniformLogicalAnd",
+		Class:    "Non-Uniform",
+		Opcode:   362,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8789,6 +9752,8 @@
 	}
 	OpGroupNonUniformLogicalOr = &Opcode {
 		Opname:   "OpGroupNonUniformLogicalOr",
+		Class:    "Non-Uniform",
+		Opcode:   363,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8824,6 +9789,8 @@
 	}
 	OpGroupNonUniformLogicalXor = &Opcode {
 		Opname:   "OpGroupNonUniformLogicalXor",
+		Class:    "Non-Uniform",
+		Opcode:   364,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8859,6 +9826,8 @@
 	}
 	OpGroupNonUniformQuadBroadcast = &Opcode {
 		Opname:   "OpGroupNonUniformQuadBroadcast",
+		Class:    "Non-Uniform",
+		Opcode:   365,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8889,6 +9858,8 @@
 	}
 	OpGroupNonUniformQuadSwap = &Opcode {
 		Opname:   "OpGroupNonUniformQuadSwap",
+		Class:    "Non-Uniform",
+		Opcode:   366,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8919,6 +9890,8 @@
 	}
 	OpCopyLogical = &Opcode {
 		Opname:   "OpCopyLogical",
+		Class:    "Composite",
+		Opcode:   400,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8939,6 +9912,8 @@
 	}
 	OpPtrEqual = &Opcode {
 		Opname:   "OpPtrEqual",
+		Class:    "Memory",
+		Opcode:   401,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8964,6 +9939,8 @@
 	}
 	OpPtrNotEqual = &Opcode {
 		Opname:   "OpPtrNotEqual",
+		Class:    "Memory",
+		Opcode:   402,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -8989,6 +9966,8 @@
 	}
 	OpPtrDiff = &Opcode {
 		Opname:   "OpPtrDiff",
+		Class:    "Memory",
+		Opcode:   403,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9014,6 +9993,8 @@
 	}
 	OpSubgroupBallotKHR = &Opcode {
 		Opname:   "OpSubgroupBallotKHR",
+		Class:    "Group",
+		Opcode:   4421,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9034,6 +10015,8 @@
 	}
 	OpSubgroupFirstInvocationKHR = &Opcode {
 		Opname:   "OpSubgroupFirstInvocationKHR",
+		Class:    "Group",
+		Opcode:   4422,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9054,6 +10037,8 @@
 	}
 	OpSubgroupAllKHR = &Opcode {
 		Opname:   "OpSubgroupAllKHR",
+		Class:    "Group",
+		Opcode:   4428,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9074,6 +10059,8 @@
 	}
 	OpSubgroupAnyKHR = &Opcode {
 		Opname:   "OpSubgroupAnyKHR",
+		Class:    "Group",
+		Opcode:   4429,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9094,6 +10081,8 @@
 	}
 	OpSubgroupAllEqualKHR = &Opcode {
 		Opname:   "OpSubgroupAllEqualKHR",
+		Class:    "Group",
+		Opcode:   4430,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9114,6 +10103,8 @@
 	}
 	OpSubgroupReadInvocationKHR = &Opcode {
 		Opname:   "OpSubgroupReadInvocationKHR",
+		Class:    "Group",
+		Opcode:   4432,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9139,6 +10130,8 @@
 	}
 	OpGroupIAddNonUniformAMD = &Opcode {
 		Opname:   "OpGroupIAddNonUniformAMD",
+		Class:    "Group",
+		Opcode:   5000,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9169,6 +10162,8 @@
 	}
 	OpGroupFAddNonUniformAMD = &Opcode {
 		Opname:   "OpGroupFAddNonUniformAMD",
+		Class:    "Group",
+		Opcode:   5001,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9199,6 +10194,8 @@
 	}
 	OpGroupFMinNonUniformAMD = &Opcode {
 		Opname:   "OpGroupFMinNonUniformAMD",
+		Class:    "Group",
+		Opcode:   5002,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9229,6 +10226,8 @@
 	}
 	OpGroupUMinNonUniformAMD = &Opcode {
 		Opname:   "OpGroupUMinNonUniformAMD",
+		Class:    "Group",
+		Opcode:   5003,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9259,6 +10258,8 @@
 	}
 	OpGroupSMinNonUniformAMD = &Opcode {
 		Opname:   "OpGroupSMinNonUniformAMD",
+		Class:    "Group",
+		Opcode:   5004,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9289,6 +10290,8 @@
 	}
 	OpGroupFMaxNonUniformAMD = &Opcode {
 		Opname:   "OpGroupFMaxNonUniformAMD",
+		Class:    "Group",
+		Opcode:   5005,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9319,6 +10322,8 @@
 	}
 	OpGroupUMaxNonUniformAMD = &Opcode {
 		Opname:   "OpGroupUMaxNonUniformAMD",
+		Class:    "Group",
+		Opcode:   5006,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9349,6 +10354,8 @@
 	}
 	OpGroupSMaxNonUniformAMD = &Opcode {
 		Opname:   "OpGroupSMaxNonUniformAMD",
+		Class:    "Group",
+		Opcode:   5007,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9379,6 +10386,8 @@
 	}
 	OpFragmentMaskFetchAMD = &Opcode {
 		Opname:   "OpFragmentMaskFetchAMD",
+		Class:    "Reserved",
+		Opcode:   5011,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9404,6 +10413,8 @@
 	}
 	OpFragmentFetchAMD = &Opcode {
 		Opname:   "OpFragmentFetchAMD",
+		Class:    "Reserved",
+		Opcode:   5012,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9434,6 +10445,8 @@
 	}
 	OpReadClockKHR = &Opcode {
 		Opname:   "OpReadClockKHR",
+		Class:    "Reserved",
+		Opcode:   5056,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9454,6 +10467,8 @@
 	}
 	OpImageSampleFootprintNV = &Opcode {
 		Opname:   "OpImageSampleFootprintNV",
+		Class:    "Image",
+		Opcode:   5283,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9494,6 +10509,8 @@
 	}
 	OpGroupNonUniformPartitionNV = &Opcode {
 		Opname:   "OpGroupNonUniformPartitionNV",
+		Class:    "Non-Uniform",
+		Opcode:   5296,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9514,6 +10531,8 @@
 	}
 	OpWritePackedPrimitiveIndices4x8NV = &Opcode {
 		Opname:   "OpWritePackedPrimitiveIndices4x8NV",
+		Class:    "Reserved",
+		Opcode:   5299,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -9529,6 +10548,8 @@
 	}
 	OpReportIntersectionNV = &Opcode {
 		Opname:   "OpReportIntersectionNV",
+		Class:    "Reserved",
+		Opcode:   5334,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9554,16 +10575,22 @@
 	}
 	OpIgnoreIntersectionNV = &Opcode {
 		Opname:   "OpIgnoreIntersectionNV",
+		Class:    "Reserved",
+		Opcode:   5335,
 		Operands: []Operand {
 		},
 	}
 	OpTerminateRayNV = &Opcode {
 		Opname:   "OpTerminateRayNV",
+		Class:    "Reserved",
+		Opcode:   5336,
 		Operands: []Operand {
 		},
 	}
 	OpTraceNV = &Opcode {
 		Opname:   "OpTraceNV",
+		Class:    "Reserved",
+		Opcode:   5337,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -9624,6 +10651,8 @@
 	}
 	OpTypeAccelerationStructureNV = &Opcode {
 		Opname:   "OpTypeAccelerationStructureNV",
+		Class:    "Reserved",
+		Opcode:   5341,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -9634,6 +10663,8 @@
 	}
 	OpExecuteCallableNV = &Opcode {
 		Opname:   "OpExecuteCallableNV",
+		Class:    "Reserved",
+		Opcode:   5344,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -9649,6 +10680,8 @@
 	}
 	OpTypeCooperativeMatrixNV = &Opcode {
 		Opname:   "OpTypeCooperativeMatrixNV",
+		Class:    "Reserved",
+		Opcode:   5358,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -9679,6 +10712,8 @@
 	}
 	OpCooperativeMatrixLoadNV = &Opcode {
 		Opname:   "OpCooperativeMatrixLoadNV",
+		Class:    "Reserved",
+		Opcode:   5359,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9714,6 +10749,8 @@
 	}
 	OpCooperativeMatrixStoreNV = &Opcode {
 		Opname:   "OpCooperativeMatrixStoreNV",
+		Class:    "Reserved",
+		Opcode:   5360,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -9744,6 +10781,8 @@
 	}
 	OpCooperativeMatrixMulAddNV = &Opcode {
 		Opname:   "OpCooperativeMatrixMulAddNV",
+		Class:    "Reserved",
+		Opcode:   5361,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9774,6 +10813,8 @@
 	}
 	OpCooperativeMatrixLengthNV = &Opcode {
 		Opname:   "OpCooperativeMatrixLengthNV",
+		Class:    "Reserved",
+		Opcode:   5362,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9794,21 +10835,29 @@
 	}
 	OpBeginInvocationInterlockEXT = &Opcode {
 		Opname:   "OpBeginInvocationInterlockEXT",
+		Class:    "Reserved",
+		Opcode:   5364,
 		Operands: []Operand {
 		},
 	}
 	OpEndInvocationInterlockEXT = &Opcode {
 		Opname:   "OpEndInvocationInterlockEXT",
+		Class:    "Reserved",
+		Opcode:   5365,
 		Operands: []Operand {
 		},
 	}
 	OpDemoteToHelperInvocationEXT = &Opcode {
 		Opname:   "OpDemoteToHelperInvocationEXT",
+		Class:    "Reserved",
+		Opcode:   5380,
 		Operands: []Operand {
 		},
 	}
 	OpIsHelperInvocationEXT = &Opcode {
 		Opname:   "OpIsHelperInvocationEXT",
+		Class:    "Reserved",
+		Opcode:   5381,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9824,6 +10873,8 @@
 	}
 	OpSubgroupShuffleINTEL = &Opcode {
 		Opname:   "OpSubgroupShuffleINTEL",
+		Class:    "Group",
+		Opcode:   5571,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9849,6 +10900,8 @@
 	}
 	OpSubgroupShuffleDownINTEL = &Opcode {
 		Opname:   "OpSubgroupShuffleDownINTEL",
+		Class:    "Group",
+		Opcode:   5572,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9879,6 +10932,8 @@
 	}
 	OpSubgroupShuffleUpINTEL = &Opcode {
 		Opname:   "OpSubgroupShuffleUpINTEL",
+		Class:    "Group",
+		Opcode:   5573,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9909,6 +10964,8 @@
 	}
 	OpSubgroupShuffleXorINTEL = &Opcode {
 		Opname:   "OpSubgroupShuffleXorINTEL",
+		Class:    "Group",
+		Opcode:   5574,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9934,6 +10991,8 @@
 	}
 	OpSubgroupBlockReadINTEL = &Opcode {
 		Opname:   "OpSubgroupBlockReadINTEL",
+		Class:    "Group",
+		Opcode:   5575,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9954,6 +11013,8 @@
 	}
 	OpSubgroupBlockWriteINTEL = &Opcode {
 		Opname:   "OpSubgroupBlockWriteINTEL",
+		Class:    "Group",
+		Opcode:   5576,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -9969,6 +11030,8 @@
 	}
 	OpSubgroupImageBlockReadINTEL = &Opcode {
 		Opname:   "OpSubgroupImageBlockReadINTEL",
+		Class:    "Group",
+		Opcode:   5577,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -9994,6 +11057,8 @@
 	}
 	OpSubgroupImageBlockWriteINTEL = &Opcode {
 		Opname:   "OpSubgroupImageBlockWriteINTEL",
+		Class:    "Group",
+		Opcode:   5578,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -10014,6 +11079,8 @@
 	}
 	OpSubgroupImageMediaBlockReadINTEL = &Opcode {
 		Opname:   "OpSubgroupImageMediaBlockReadINTEL",
+		Class:    "Group",
+		Opcode:   5580,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10049,6 +11116,8 @@
 	}
 	OpSubgroupImageMediaBlockWriteINTEL = &Opcode {
 		Opname:   "OpSubgroupImageMediaBlockWriteINTEL",
+		Class:    "Group",
+		Opcode:   5581,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -10079,6 +11148,8 @@
 	}
 	OpUCountLeadingZerosINTEL = &Opcode {
 		Opname:   "OpUCountLeadingZerosINTEL",
+		Class:    "Reserved",
+		Opcode:   5585,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10099,6 +11170,8 @@
 	}
 	OpUCountTrailingZerosINTEL = &Opcode {
 		Opname:   "OpUCountTrailingZerosINTEL",
+		Class:    "Reserved",
+		Opcode:   5586,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10119,6 +11192,8 @@
 	}
 	OpAbsISubINTEL = &Opcode {
 		Opname:   "OpAbsISubINTEL",
+		Class:    "Reserved",
+		Opcode:   5587,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10144,6 +11219,8 @@
 	}
 	OpAbsUSubINTEL = &Opcode {
 		Opname:   "OpAbsUSubINTEL",
+		Class:    "Reserved",
+		Opcode:   5588,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10169,6 +11246,8 @@
 	}
 	OpIAddSatINTEL = &Opcode {
 		Opname:   "OpIAddSatINTEL",
+		Class:    "Reserved",
+		Opcode:   5589,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10194,6 +11273,8 @@
 	}
 	OpUAddSatINTEL = &Opcode {
 		Opname:   "OpUAddSatINTEL",
+		Class:    "Reserved",
+		Opcode:   5590,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10219,6 +11300,8 @@
 	}
 	OpIAverageINTEL = &Opcode {
 		Opname:   "OpIAverageINTEL",
+		Class:    "Reserved",
+		Opcode:   5591,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10244,6 +11327,8 @@
 	}
 	OpUAverageINTEL = &Opcode {
 		Opname:   "OpUAverageINTEL",
+		Class:    "Reserved",
+		Opcode:   5592,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10269,6 +11354,8 @@
 	}
 	OpIAverageRoundedINTEL = &Opcode {
 		Opname:   "OpIAverageRoundedINTEL",
+		Class:    "Reserved",
+		Opcode:   5593,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10294,6 +11381,8 @@
 	}
 	OpUAverageRoundedINTEL = &Opcode {
 		Opname:   "OpUAverageRoundedINTEL",
+		Class:    "Reserved",
+		Opcode:   5594,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10319,6 +11408,8 @@
 	}
 	OpISubSatINTEL = &Opcode {
 		Opname:   "OpISubSatINTEL",
+		Class:    "Reserved",
+		Opcode:   5595,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10344,6 +11435,8 @@
 	}
 	OpUSubSatINTEL = &Opcode {
 		Opname:   "OpUSubSatINTEL",
+		Class:    "Reserved",
+		Opcode:   5596,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10369,6 +11462,8 @@
 	}
 	OpIMul32x16INTEL = &Opcode {
 		Opname:   "OpIMul32x16INTEL",
+		Class:    "Reserved",
+		Opcode:   5597,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10394,6 +11489,8 @@
 	}
 	OpUMul32x16INTEL = &Opcode {
 		Opname:   "OpUMul32x16INTEL",
+		Class:    "Reserved",
+		Opcode:   5598,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10419,6 +11516,8 @@
 	}
 	OpDecorateString = &Opcode {
 		Opname:   "OpDecorateString",
+		Class:    "Annotation",
+		Opcode:   5632,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -10434,6 +11533,8 @@
 	}
 	OpDecorateStringGOOGLE = &Opcode {
 		Opname:   "OpDecorateStringGOOGLE",
+		Class:    "Annotation",
+		Opcode:   5632,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -10449,6 +11550,8 @@
 	}
 	OpMemberDecorateString = &Opcode {
 		Opname:   "OpMemberDecorateString",
+		Class:    "Annotation",
+		Opcode:   5633,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -10469,6 +11572,8 @@
 	}
 	OpMemberDecorateStringGOOGLE = &Opcode {
 		Opname:   "OpMemberDecorateStringGOOGLE",
+		Class:    "Annotation",
+		Opcode:   5633,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -10489,6 +11594,8 @@
 	}
 	OpVmeImageINTEL = &Opcode {
 		Opname:   "OpVmeImageINTEL",
+		Class:    "@exclude",
+		Opcode:   5699,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10514,6 +11621,8 @@
 	}
 	OpTypeVmeImageINTEL = &Opcode {
 		Opname:   "OpTypeVmeImageINTEL",
+		Class:    "@exclude",
+		Opcode:   5700,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -10529,6 +11638,8 @@
 	}
 	OpTypeAvcImePayloadINTEL = &Opcode {
 		Opname:   "OpTypeAvcImePayloadINTEL",
+		Class:    "@exclude",
+		Opcode:   5701,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -10539,6 +11650,8 @@
 	}
 	OpTypeAvcRefPayloadINTEL = &Opcode {
 		Opname:   "OpTypeAvcRefPayloadINTEL",
+		Class:    "@exclude",
+		Opcode:   5702,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -10549,6 +11662,8 @@
 	}
 	OpTypeAvcSicPayloadINTEL = &Opcode {
 		Opname:   "OpTypeAvcSicPayloadINTEL",
+		Class:    "@exclude",
+		Opcode:   5703,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -10559,6 +11674,8 @@
 	}
 	OpTypeAvcMcePayloadINTEL = &Opcode {
 		Opname:   "OpTypeAvcMcePayloadINTEL",
+		Class:    "@exclude",
+		Opcode:   5704,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -10569,6 +11686,8 @@
 	}
 	OpTypeAvcMceResultINTEL = &Opcode {
 		Opname:   "OpTypeAvcMceResultINTEL",
+		Class:    "@exclude",
+		Opcode:   5705,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -10579,6 +11698,8 @@
 	}
 	OpTypeAvcImeResultINTEL = &Opcode {
 		Opname:   "OpTypeAvcImeResultINTEL",
+		Class:    "@exclude",
+		Opcode:   5706,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -10589,6 +11710,8 @@
 	}
 	OpTypeAvcImeResultSingleReferenceStreamoutINTEL = &Opcode {
 		Opname:   "OpTypeAvcImeResultSingleReferenceStreamoutINTEL",
+		Class:    "@exclude",
+		Opcode:   5707,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -10599,6 +11722,8 @@
 	}
 	OpTypeAvcImeResultDualReferenceStreamoutINTEL = &Opcode {
 		Opname:   "OpTypeAvcImeResultDualReferenceStreamoutINTEL",
+		Class:    "@exclude",
+		Opcode:   5708,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -10609,6 +11734,8 @@
 	}
 	OpTypeAvcImeSingleReferenceStreaminINTEL = &Opcode {
 		Opname:   "OpTypeAvcImeSingleReferenceStreaminINTEL",
+		Class:    "@exclude",
+		Opcode:   5709,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -10619,6 +11746,8 @@
 	}
 	OpTypeAvcImeDualReferenceStreaminINTEL = &Opcode {
 		Opname:   "OpTypeAvcImeDualReferenceStreaminINTEL",
+		Class:    "@exclude",
+		Opcode:   5710,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -10629,6 +11758,8 @@
 	}
 	OpTypeAvcRefResultINTEL = &Opcode {
 		Opname:   "OpTypeAvcRefResultINTEL",
+		Class:    "@exclude",
+		Opcode:   5711,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -10639,6 +11770,8 @@
 	}
 	OpTypeAvcSicResultINTEL = &Opcode {
 		Opname:   "OpTypeAvcSicResultINTEL",
+		Class:    "@exclude",
+		Opcode:   5712,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResult,
@@ -10649,6 +11782,8 @@
 	}
 	OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL",
+		Class:    "@exclude",
+		Opcode:   5713,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10674,6 +11809,8 @@
 	}
 	OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL",
+		Class:    "@exclude",
+		Opcode:   5714,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10699,6 +11836,8 @@
 	}
 	OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL",
+		Class:    "@exclude",
+		Opcode:   5715,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10724,6 +11863,8 @@
 	}
 	OpSubgroupAvcMceSetInterShapePenaltyINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceSetInterShapePenaltyINTEL",
+		Class:    "@exclude",
+		Opcode:   5716,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10749,6 +11890,8 @@
 	}
 	OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL",
+		Class:    "@exclude",
+		Opcode:   5717,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10774,6 +11917,8 @@
 	}
 	OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL",
+		Class:    "@exclude",
+		Opcode:   5718,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10799,6 +11944,8 @@
 	}
 	OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL",
+		Class:    "@exclude",
+		Opcode:   5719,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10824,6 +11971,8 @@
 	}
 	OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL",
+		Class:    "@exclude",
+		Opcode:   5720,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10849,6 +11998,8 @@
 	}
 	OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL",
+		Class:    "@exclude",
+		Opcode:   5721,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10864,6 +12015,8 @@
 	}
 	OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL",
+		Class:    "@exclude",
+		Opcode:   5722,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10879,6 +12032,8 @@
 	}
 	OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL",
+		Class:    "@exclude",
+		Opcode:   5723,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10894,6 +12049,8 @@
 	}
 	OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL",
+		Class:    "@exclude",
+		Opcode:   5724,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10929,6 +12086,8 @@
 	}
 	OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL",
+		Class:    "@exclude",
+		Opcode:   5725,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10954,6 +12113,8 @@
 	}
 	OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL",
+		Class:    "@exclude",
+		Opcode:   5726,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10969,6 +12130,8 @@
 	}
 	OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL",
+		Class:    "@exclude",
+		Opcode:   5727,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10984,6 +12147,8 @@
 	}
 	OpSubgroupAvcMceSetAcOnlyHaarINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceSetAcOnlyHaarINTEL",
+		Class:    "@exclude",
+		Opcode:   5728,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11004,6 +12169,8 @@
 	}
 	OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL",
+		Class:    "@exclude",
+		Opcode:   5729,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11029,6 +12196,8 @@
 	}
 	OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL",
+		Class:    "@exclude",
+		Opcode:   5730,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11054,6 +12223,8 @@
 	}
 	OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL",
+		Class:    "@exclude",
+		Opcode:   5731,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11084,6 +12255,8 @@
 	}
 	OpSubgroupAvcMceConvertToImePayloadINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceConvertToImePayloadINTEL",
+		Class:    "@exclude",
+		Opcode:   5732,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11104,6 +12277,8 @@
 	}
 	OpSubgroupAvcMceConvertToImeResultINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceConvertToImeResultINTEL",
+		Class:    "@exclude",
+		Opcode:   5733,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11124,6 +12299,8 @@
 	}
 	OpSubgroupAvcMceConvertToRefPayloadINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceConvertToRefPayloadINTEL",
+		Class:    "@exclude",
+		Opcode:   5734,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11144,6 +12321,8 @@
 	}
 	OpSubgroupAvcMceConvertToRefResultINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceConvertToRefResultINTEL",
+		Class:    "@exclude",
+		Opcode:   5735,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11164,6 +12343,8 @@
 	}
 	OpSubgroupAvcMceConvertToSicPayloadINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceConvertToSicPayloadINTEL",
+		Class:    "@exclude",
+		Opcode:   5736,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11184,6 +12365,8 @@
 	}
 	OpSubgroupAvcMceConvertToSicResultINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceConvertToSicResultINTEL",
+		Class:    "@exclude",
+		Opcode:   5737,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11204,6 +12387,8 @@
 	}
 	OpSubgroupAvcMceGetMotionVectorsINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetMotionVectorsINTEL",
+		Class:    "@exclude",
+		Opcode:   5738,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11224,6 +12409,8 @@
 	}
 	OpSubgroupAvcMceGetInterDistortionsINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetInterDistortionsINTEL",
+		Class:    "@exclude",
+		Opcode:   5739,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11244,6 +12431,8 @@
 	}
 	OpSubgroupAvcMceGetBestInterDistortionsINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetBestInterDistortionsINTEL",
+		Class:    "@exclude",
+		Opcode:   5740,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11264,6 +12453,8 @@
 	}
 	OpSubgroupAvcMceGetInterMajorShapeINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetInterMajorShapeINTEL",
+		Class:    "@exclude",
+		Opcode:   5741,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11284,6 +12475,8 @@
 	}
 	OpSubgroupAvcMceGetInterMinorShapeINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetInterMinorShapeINTEL",
+		Class:    "@exclude",
+		Opcode:   5742,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11304,6 +12497,8 @@
 	}
 	OpSubgroupAvcMceGetInterDirectionsINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetInterDirectionsINTEL",
+		Class:    "@exclude",
+		Opcode:   5743,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11324,6 +12519,8 @@
 	}
 	OpSubgroupAvcMceGetInterMotionVectorCountINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetInterMotionVectorCountINTEL",
+		Class:    "@exclude",
+		Opcode:   5744,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11344,6 +12541,8 @@
 	}
 	OpSubgroupAvcMceGetInterReferenceIdsINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetInterReferenceIdsINTEL",
+		Class:    "@exclude",
+		Opcode:   5745,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11364,6 +12563,8 @@
 	}
 	OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL",
+		Class:    "@exclude",
+		Opcode:   5746,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11394,6 +12595,8 @@
 	}
 	OpSubgroupAvcImeInitializeINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeInitializeINTEL",
+		Class:    "@exclude",
+		Opcode:   5747,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11424,6 +12627,8 @@
 	}
 	OpSubgroupAvcImeSetSingleReferenceINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeSetSingleReferenceINTEL",
+		Class:    "@exclude",
+		Opcode:   5748,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11454,6 +12659,8 @@
 	}
 	OpSubgroupAvcImeSetDualReferenceINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeSetDualReferenceINTEL",
+		Class:    "@exclude",
+		Opcode:   5749,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11489,6 +12696,8 @@
 	}
 	OpSubgroupAvcImeRefWindowSizeINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeRefWindowSizeINTEL",
+		Class:    "@exclude",
+		Opcode:   5750,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11514,6 +12723,8 @@
 	}
 	OpSubgroupAvcImeAdjustRefOffsetINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeAdjustRefOffsetINTEL",
+		Class:    "@exclude",
+		Opcode:   5751,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11549,6 +12760,8 @@
 	}
 	OpSubgroupAvcImeConvertToMcePayloadINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeConvertToMcePayloadINTEL",
+		Class:    "@exclude",
+		Opcode:   5752,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11569,6 +12782,8 @@
 	}
 	OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL",
+		Class:    "@exclude",
+		Opcode:   5753,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11594,6 +12809,8 @@
 	}
 	OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL",
+		Class:    "@exclude",
+		Opcode:   5754,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11614,6 +12831,8 @@
 	}
 	OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL",
+		Class:    "@exclude",
+		Opcode:   5755,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11639,6 +12858,8 @@
 	}
 	OpSubgroupAvcImeSetWeightedSadINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeSetWeightedSadINTEL",
+		Class:    "@exclude",
+		Opcode:   5756,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11664,6 +12885,8 @@
 	}
 	OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL",
+		Class:    "@exclude",
+		Opcode:   5757,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11694,6 +12917,8 @@
 	}
 	OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL",
+		Class:    "@exclude",
+		Opcode:   5758,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11729,6 +12954,8 @@
 	}
 	OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL",
+		Class:    "@exclude",
+		Opcode:   5759,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11764,6 +12991,8 @@
 	}
 	OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL",
+		Class:    "@exclude",
+		Opcode:   5760,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11804,6 +13033,8 @@
 	}
 	OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL",
+		Class:    "@exclude",
+		Opcode:   5761,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11834,6 +13065,8 @@
 	}
 	OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL",
+		Class:    "@exclude",
+		Opcode:   5762,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11869,6 +13102,8 @@
 	}
 	OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL",
+		Class:    "@exclude",
+		Opcode:   5763,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11904,6 +13139,8 @@
 	}
 	OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL",
+		Class:    "@exclude",
+		Opcode:   5764,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11944,6 +13181,8 @@
 	}
 	OpSubgroupAvcImeConvertToMceResultINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeConvertToMceResultINTEL",
+		Class:    "@exclude",
+		Opcode:   5765,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11964,6 +13203,8 @@
 	}
 	OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL",
+		Class:    "@exclude",
+		Opcode:   5766,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -11984,6 +13225,8 @@
 	}
 	OpSubgroupAvcImeGetDualReferenceStreaminINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeGetDualReferenceStreaminINTEL",
+		Class:    "@exclude",
+		Opcode:   5767,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12004,6 +13247,8 @@
 	}
 	OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL",
+		Class:    "@exclude",
+		Opcode:   5768,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12024,6 +13269,8 @@
 	}
 	OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL",
+		Class:    "@exclude",
+		Opcode:   5769,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12044,6 +13291,8 @@
 	}
 	OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL",
+		Class:    "@exclude",
+		Opcode:   5770,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12069,6 +13318,8 @@
 	}
 	OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL",
+		Class:    "@exclude",
+		Opcode:   5771,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12094,6 +13345,8 @@
 	}
 	OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL",
+		Class:    "@exclude",
+		Opcode:   5772,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12119,6 +13372,8 @@
 	}
 	OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL",
+		Class:    "@exclude",
+		Opcode:   5773,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12149,6 +13404,8 @@
 	}
 	OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL",
+		Class:    "@exclude",
+		Opcode:   5774,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12179,6 +13436,8 @@
 	}
 	OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL",
+		Class:    "@exclude",
+		Opcode:   5775,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12209,6 +13468,8 @@
 	}
 	OpSubgroupAvcImeGetBorderReachedINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeGetBorderReachedINTEL",
+		Class:    "@exclude",
+		Opcode:   5776,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12234,6 +13495,8 @@
 	}
 	OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL",
+		Class:    "@exclude",
+		Opcode:   5777,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12254,6 +13517,8 @@
 	}
 	OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL",
+		Class:    "@exclude",
+		Opcode:   5778,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12274,6 +13539,8 @@
 	}
 	OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL",
+		Class:    "@exclude",
+		Opcode:   5779,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12294,6 +13561,8 @@
 	}
 	OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL",
+		Class:    "@exclude",
+		Opcode:   5780,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12314,6 +13583,8 @@
 	}
 	OpSubgroupAvcFmeInitializeINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcFmeInitializeINTEL",
+		Class:    "@exclude",
+		Opcode:   5781,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12364,6 +13635,8 @@
 	}
 	OpSubgroupAvcBmeInitializeINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcBmeInitializeINTEL",
+		Class:    "@exclude",
+		Opcode:   5782,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12419,6 +13692,8 @@
 	}
 	OpSubgroupAvcRefConvertToMcePayloadINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcRefConvertToMcePayloadINTEL",
+		Class:    "@exclude",
+		Opcode:   5783,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12439,6 +13714,8 @@
 	}
 	OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL",
+		Class:    "@exclude",
+		Opcode:   5784,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12459,6 +13736,8 @@
 	}
 	OpSubgroupAvcRefSetBilinearFilterEnableINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcRefSetBilinearFilterEnableINTEL",
+		Class:    "@exclude",
+		Opcode:   5785,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12479,6 +13758,8 @@
 	}
 	OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL",
+		Class:    "@exclude",
+		Opcode:   5786,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12509,6 +13790,8 @@
 	}
 	OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL",
+		Class:    "@exclude",
+		Opcode:   5787,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12544,6 +13827,8 @@
 	}
 	OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL",
+		Class:    "@exclude",
+		Opcode:   5788,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12574,6 +13859,8 @@
 	}
 	OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL",
+		Class:    "@exclude",
+		Opcode:   5789,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12609,6 +13896,8 @@
 	}
 	OpSubgroupAvcRefConvertToMceResultINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcRefConvertToMceResultINTEL",
+		Class:    "@exclude",
+		Opcode:   5790,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12629,6 +13918,8 @@
 	}
 	OpSubgroupAvcSicInitializeINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicInitializeINTEL",
+		Class:    "@exclude",
+		Opcode:   5791,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12649,6 +13940,8 @@
 	}
 	OpSubgroupAvcSicConfigureSkcINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicConfigureSkcINTEL",
+		Class:    "@exclude",
+		Opcode:   5792,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12694,6 +13987,8 @@
 	}
 	OpSubgroupAvcSicConfigureIpeLumaINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicConfigureIpeLumaINTEL",
+		Class:    "@exclude",
+		Opcode:   5793,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12749,6 +14044,8 @@
 	}
 	OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL",
+		Class:    "@exclude",
+		Opcode:   5794,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12819,6 +14116,8 @@
 	}
 	OpSubgroupAvcSicGetMotionVectorMaskINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicGetMotionVectorMaskINTEL",
+		Class:    "@exclude",
+		Opcode:   5795,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12844,6 +14143,8 @@
 	}
 	OpSubgroupAvcSicConvertToMcePayloadINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicConvertToMcePayloadINTEL",
+		Class:    "@exclude",
+		Opcode:   5796,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12864,6 +14165,8 @@
 	}
 	OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL",
+		Class:    "@exclude",
+		Opcode:   5797,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12889,6 +14192,8 @@
 	}
 	OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL",
+		Class:    "@exclude",
+		Opcode:   5798,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12924,6 +14229,8 @@
 	}
 	OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL",
+		Class:    "@exclude",
+		Opcode:   5799,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12949,6 +14256,8 @@
 	}
 	OpSubgroupAvcSicSetBilinearFilterEnableINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicSetBilinearFilterEnableINTEL",
+		Class:    "@exclude",
+		Opcode:   5800,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12969,6 +14278,8 @@
 	}
 	OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL",
+		Class:    "@exclude",
+		Opcode:   5801,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -12994,6 +14305,8 @@
 	}
 	OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL",
+		Class:    "@exclude",
+		Opcode:   5802,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -13019,6 +14332,8 @@
 	}
 	OpSubgroupAvcSicEvaluateIpeINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicEvaluateIpeINTEL",
+		Class:    "@exclude",
+		Opcode:   5803,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -13044,6 +14359,8 @@
 	}
 	OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL",
+		Class:    "@exclude",
+		Opcode:   5804,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -13074,6 +14391,8 @@
 	}
 	OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL",
+		Class:    "@exclude",
+		Opcode:   5805,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -13109,6 +14428,8 @@
 	}
 	OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL",
+		Class:    "@exclude",
+		Opcode:   5806,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -13139,6 +14460,8 @@
 	}
 	OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL",
+		Class:    "@exclude",
+		Opcode:   5807,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -13174,6 +14497,8 @@
 	}
 	OpSubgroupAvcSicConvertToMceResultINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicConvertToMceResultINTEL",
+		Class:    "@exclude",
+		Opcode:   5808,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -13194,6 +14519,8 @@
 	}
 	OpSubgroupAvcSicGetIpeLumaShapeINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicGetIpeLumaShapeINTEL",
+		Class:    "@exclude",
+		Opcode:   5809,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -13214,6 +14541,8 @@
 	}
 	OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL",
+		Class:    "@exclude",
+		Opcode:   5810,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -13234,6 +14563,8 @@
 	}
 	OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL",
+		Class:    "@exclude",
+		Opcode:   5811,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -13254,6 +14585,8 @@
 	}
 	OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL",
+		Class:    "@exclude",
+		Opcode:   5812,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -13274,6 +14607,8 @@
 	}
 	OpSubgroupAvcSicGetIpeChromaModeINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicGetIpeChromaModeINTEL",
+		Class:    "@exclude",
+		Opcode:   5813,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -13294,6 +14629,8 @@
 	}
 	OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL",
+		Class:    "@exclude",
+		Opcode:   5814,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -13314,6 +14651,8 @@
 	}
 	OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL",
+		Class:    "@exclude",
+		Opcode:   5815,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -13334,6 +14673,8 @@
 	}
 	OpSubgroupAvcSicGetInterRawSadsINTEL = &Opcode {
 		Opname:   "OpSubgroupAvcSicGetInterRawSadsINTEL",
+		Class:    "@exclude",
+		Opcode:   5816,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -13353,6 +14694,4167 @@
 		},
 	}
 
+	GLSLStd450_Round = &Opcode {
+		Opname:   "Round",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_RoundEven = &Opcode {
+		Opname:   "RoundEven",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Trunc = &Opcode {
+		Opname:   "Trunc",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_FAbs = &Opcode {
+		Opname:   "FAbs",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_SAbs = &Opcode {
+		Opname:   "SAbs",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_FSign = &Opcode {
+		Opname:   "FSign",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_SSign = &Opcode {
+		Opname:   "SSign",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Floor = &Opcode {
+		Opname:   "Floor",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Ceil = &Opcode {
+		Opname:   "Ceil",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Fract = &Opcode {
+		Opname:   "Fract",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Radians = &Opcode {
+		Opname:   "Radians",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'degrees'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Degrees = &Opcode {
+		Opname:   "Degrees",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'radians'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Sin = &Opcode {
+		Opname:   "Sin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Cos = &Opcode {
+		Opname:   "Cos",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Tan = &Opcode {
+		Opname:   "Tan",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Asin = &Opcode {
+		Opname:   "Asin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Acos = &Opcode {
+		Opname:   "Acos",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Atan = &Opcode {
+		Opname:   "Atan",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y_over_x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Sinh = &Opcode {
+		Opname:   "Sinh",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Cosh = &Opcode {
+		Opname:   "Cosh",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Tanh = &Opcode {
+		Opname:   "Tanh",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Asinh = &Opcode {
+		Opname:   "Asinh",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Acosh = &Opcode {
+		Opname:   "Acosh",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Atanh = &Opcode {
+		Opname:   "Atanh",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Atan2 = &Opcode {
+		Opname:   "Atan2",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Pow = &Opcode {
+		Opname:   "Pow",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Exp = &Opcode {
+		Opname:   "Exp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Log = &Opcode {
+		Opname:   "Log",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Exp2 = &Opcode {
+		Opname:   "Exp2",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Log2 = &Opcode {
+		Opname:   "Log2",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Sqrt = &Opcode {
+		Opname:   "Sqrt",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_InverseSqrt = &Opcode {
+		Opname:   "InverseSqrt",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Determinant = &Opcode {
+		Opname:   "Determinant",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_MatrixInverse = &Opcode {
+		Opname:   "MatrixInverse",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Modf = &Opcode {
+		Opname:   "Modf",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'i'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_ModfStruct = &Opcode {
+		Opname:   "ModfStruct",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_FMin = &Opcode {
+		Opname:   "FMin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_UMin = &Opcode {
+		Opname:   "UMin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_SMin = &Opcode {
+		Opname:   "SMin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_FMax = &Opcode {
+		Opname:   "FMax",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_UMax = &Opcode {
+		Opname:   "UMax",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_SMax = &Opcode {
+		Opname:   "SMax",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_FClamp = &Opcode {
+		Opname:   "FClamp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'minVal'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'maxVal'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_UClamp = &Opcode {
+		Opname:   "UClamp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'minVal'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'maxVal'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_SClamp = &Opcode {
+		Opname:   "SClamp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'minVal'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'maxVal'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_FMix = &Opcode {
+		Opname:   "FMix",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'a'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_IMix = &Opcode {
+		Opname:   "IMix",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'a'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Step = &Opcode {
+		Opname:   "Step",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'edge'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_SmoothStep = &Opcode {
+		Opname:   "SmoothStep",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'edge0'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'edge1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Fma = &Opcode {
+		Opname:   "Fma",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'a'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'b'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'c'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Frexp = &Opcode {
+		Opname:   "Frexp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'exp'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_FrexpStruct = &Opcode {
+		Opname:   "FrexpStruct",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Ldexp = &Opcode {
+		Opname:   "Ldexp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'exp'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_PackSnorm4x8 = &Opcode {
+		Opname:   "PackSnorm4x8",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'v'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_PackUnorm4x8 = &Opcode {
+		Opname:   "PackUnorm4x8",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'v'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_PackSnorm2x16 = &Opcode {
+		Opname:   "PackSnorm2x16",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'v'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_PackUnorm2x16 = &Opcode {
+		Opname:   "PackUnorm2x16",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'v'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_PackHalf2x16 = &Opcode {
+		Opname:   "PackHalf2x16",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'v'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_PackDouble2x32 = &Opcode {
+		Opname:   "PackDouble2x32",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'v'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_UnpackSnorm2x16 = &Opcode {
+		Opname:   "UnpackSnorm2x16",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_UnpackUnorm2x16 = &Opcode {
+		Opname:   "UnpackUnorm2x16",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_UnpackHalf2x16 = &Opcode {
+		Opname:   "UnpackHalf2x16",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'v'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_UnpackSnorm4x8 = &Opcode {
+		Opname:   "UnpackSnorm4x8",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_UnpackUnorm4x8 = &Opcode {
+		Opname:   "UnpackUnorm4x8",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_UnpackDouble2x32 = &Opcode {
+		Opname:   "UnpackDouble2x32",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'v'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Length = &Opcode {
+		Opname:   "Length",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Distance = &Opcode {
+		Opname:   "Distance",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p0'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p1'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Cross = &Opcode {
+		Opname:   "Cross",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Normalize = &Opcode {
+		Opname:   "Normalize",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_FaceForward = &Opcode {
+		Opname:   "FaceForward",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'N'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'I'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Nref'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Reflect = &Opcode {
+		Opname:   "Reflect",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'I'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'N'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_Refract = &Opcode {
+		Opname:   "Refract",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'I'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'N'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'eta'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_FindILsb = &Opcode {
+		Opname:   "FindILsb",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_FindSMsb = &Opcode {
+		Opname:   "FindSMsb",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_FindUMsb = &Opcode {
+		Opname:   "FindUMsb",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_InterpolateAtCentroid = &Opcode {
+		Opname:   "InterpolateAtCentroid",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'interpolant'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_InterpolateAtSample = &Opcode {
+		Opname:   "InterpolateAtSample",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'interpolant'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'sample'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_InterpolateAtOffset = &Opcode {
+		Opname:   "InterpolateAtOffset",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'interpolant'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'offset'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_NMin = &Opcode {
+		Opname:   "NMin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_NMax = &Opcode {
+		Opname:   "NMax",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	GLSLStd450_NClamp = &Opcode {
+		Opname:   "NClamp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'minVal'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'maxVal'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_acos = &Opcode {
+		Opname:   "acos",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_acosh = &Opcode {
+		Opname:   "acosh",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_acospi = &Opcode {
+		Opname:   "acospi",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_asin = &Opcode {
+		Opname:   "asin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_asinh = &Opcode {
+		Opname:   "asinh",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_asinpi = &Opcode {
+		Opname:   "asinpi",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_atan = &Opcode {
+		Opname:   "atan",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_atan2 = &Opcode {
+		Opname:   "atan2",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_atanh = &Opcode {
+		Opname:   "atanh",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_atanpi = &Opcode {
+		Opname:   "atanpi",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_atan2pi = &Opcode {
+		Opname:   "atan2pi",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_cbrt = &Opcode {
+		Opname:   "cbrt",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_ceil = &Opcode {
+		Opname:   "ceil",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_copysign = &Opcode {
+		Opname:   "copysign",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_cos = &Opcode {
+		Opname:   "cos",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_cosh = &Opcode {
+		Opname:   "cosh",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_cospi = &Opcode {
+		Opname:   "cospi",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_erfc = &Opcode {
+		Opname:   "erfc",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_erf = &Opcode {
+		Opname:   "erf",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_exp = &Opcode {
+		Opname:   "exp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_exp2 = &Opcode {
+		Opname:   "exp2",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_exp10 = &Opcode {
+		Opname:   "exp10",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_expm1 = &Opcode {
+		Opname:   "expm1",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_fabs = &Opcode {
+		Opname:   "fabs",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_fdim = &Opcode {
+		Opname:   "fdim",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_floor = &Opcode {
+		Opname:   "floor",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_fma = &Opcode {
+		Opname:   "fma",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'a'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'b'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'c'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_fmax = &Opcode {
+		Opname:   "fmax",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_fmin = &Opcode {
+		Opname:   "fmin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_fmod = &Opcode {
+		Opname:   "fmod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_fract = &Opcode {
+		Opname:   "fract",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ptr'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_frexp = &Opcode {
+		Opname:   "frexp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'exp'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_hypot = &Opcode {
+		Opname:   "hypot",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_ilogb = &Opcode {
+		Opname:   "ilogb",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_ldexp = &Opcode {
+		Opname:   "ldexp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'k'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_lgamma = &Opcode {
+		Opname:   "lgamma",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_lgamma_r = &Opcode {
+		Opname:   "lgamma_r",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'signp'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_log = &Opcode {
+		Opname:   "log",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_log2 = &Opcode {
+		Opname:   "log2",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_log10 = &Opcode {
+		Opname:   "log10",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_log1p = &Opcode {
+		Opname:   "log1p",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_logb = &Opcode {
+		Opname:   "logb",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_mad = &Opcode {
+		Opname:   "mad",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'a'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'b'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'c'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_maxmag = &Opcode {
+		Opname:   "maxmag",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_minmag = &Opcode {
+		Opname:   "minmag",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_modf = &Opcode {
+		Opname:   "modf",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'iptr'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_nan = &Opcode {
+		Opname:   "nan",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'nancode'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_nextafter = &Opcode {
+		Opname:   "nextafter",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_pow = &Opcode {
+		Opname:   "pow",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_pown = &Opcode {
+		Opname:   "pown",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_powr = &Opcode {
+		Opname:   "powr",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_remainder = &Opcode {
+		Opname:   "remainder",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_remquo = &Opcode {
+		Opname:   "remquo",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'quo'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_rint = &Opcode {
+		Opname:   "rint",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_rootn = &Opcode {
+		Opname:   "rootn",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_round = &Opcode {
+		Opname:   "round",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_rsqrt = &Opcode {
+		Opname:   "rsqrt",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_sin = &Opcode {
+		Opname:   "sin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_sincos = &Opcode {
+		Opname:   "sincos",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'cosval'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_sinh = &Opcode {
+		Opname:   "sinh",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_sinpi = &Opcode {
+		Opname:   "sinpi",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_sqrt = &Opcode {
+		Opname:   "sqrt",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_tan = &Opcode {
+		Opname:   "tan",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_tanh = &Opcode {
+		Opname:   "tanh",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_tanpi = &Opcode {
+		Opname:   "tanpi",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_tgamma = &Opcode {
+		Opname:   "tgamma",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_trunc = &Opcode {
+		Opname:   "trunc",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_half_cos = &Opcode {
+		Opname:   "half_cos",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_half_divide = &Opcode {
+		Opname:   "half_divide",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_half_exp = &Opcode {
+		Opname:   "half_exp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_half_exp2 = &Opcode {
+		Opname:   "half_exp2",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_half_exp10 = &Opcode {
+		Opname:   "half_exp10",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_half_log = &Opcode {
+		Opname:   "half_log",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_half_log2 = &Opcode {
+		Opname:   "half_log2",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_half_log10 = &Opcode {
+		Opname:   "half_log10",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_half_powr = &Opcode {
+		Opname:   "half_powr",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_half_recip = &Opcode {
+		Opname:   "half_recip",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_half_rsqrt = &Opcode {
+		Opname:   "half_rsqrt",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_half_sin = &Opcode {
+		Opname:   "half_sin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_half_sqrt = &Opcode {
+		Opname:   "half_sqrt",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_half_tan = &Opcode {
+		Opname:   "half_tan",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_native_cos = &Opcode {
+		Opname:   "native_cos",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_native_divide = &Opcode {
+		Opname:   "native_divide",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_native_exp = &Opcode {
+		Opname:   "native_exp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_native_exp2 = &Opcode {
+		Opname:   "native_exp2",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_native_exp10 = &Opcode {
+		Opname:   "native_exp10",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_native_log = &Opcode {
+		Opname:   "native_log",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_native_log2 = &Opcode {
+		Opname:   "native_log2",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_native_log10 = &Opcode {
+		Opname:   "native_log10",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_native_powr = &Opcode {
+		Opname:   "native_powr",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_native_recip = &Opcode {
+		Opname:   "native_recip",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_native_rsqrt = &Opcode {
+		Opname:   "native_rsqrt",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_native_sin = &Opcode {
+		Opname:   "native_sin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_native_sqrt = &Opcode {
+		Opname:   "native_sqrt",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_native_tan = &Opcode {
+		Opname:   "native_tan",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_s_abs = &Opcode {
+		Opname:   "s_abs",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_s_abs_diff = &Opcode {
+		Opname:   "s_abs_diff",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_s_add_sat = &Opcode {
+		Opname:   "s_add_sat",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_u_add_sat = &Opcode {
+		Opname:   "u_add_sat",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_s_hadd = &Opcode {
+		Opname:   "s_hadd",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_u_hadd = &Opcode {
+		Opname:   "u_hadd",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_s_rhadd = &Opcode {
+		Opname:   "s_rhadd",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_u_rhadd = &Opcode {
+		Opname:   "u_rhadd",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_s_clamp = &Opcode {
+		Opname:   "s_clamp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'minval'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'maxval'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_u_clamp = &Opcode {
+		Opname:   "u_clamp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'minval'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'maxval'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_clz = &Opcode {
+		Opname:   "clz",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_ctz = &Opcode {
+		Opname:   "ctz",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_s_mad_hi = &Opcode {
+		Opname:   "s_mad_hi",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'a'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'b'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'c'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_u_mad_sat = &Opcode {
+		Opname:   "u_mad_sat",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'z'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_s_mad_sat = &Opcode {
+		Opname:   "s_mad_sat",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'z'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_s_max = &Opcode {
+		Opname:   "s_max",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_u_max = &Opcode {
+		Opname:   "u_max",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_s_min = &Opcode {
+		Opname:   "s_min",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_u_min = &Opcode {
+		Opname:   "u_min",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_s_mul_hi = &Opcode {
+		Opname:   "s_mul_hi",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_rotate = &Opcode {
+		Opname:   "rotate",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'v'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'i'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_s_sub_sat = &Opcode {
+		Opname:   "s_sub_sat",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_u_sub_sat = &Opcode {
+		Opname:   "u_sub_sat",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_u_upsample = &Opcode {
+		Opname:   "u_upsample",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'hi'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'lo'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_s_upsample = &Opcode {
+		Opname:   "s_upsample",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'hi'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'lo'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_popcount = &Opcode {
+		Opname:   "popcount",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_s_mad24 = &Opcode {
+		Opname:   "s_mad24",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'z'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_u_mad24 = &Opcode {
+		Opname:   "u_mad24",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'z'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_s_mul24 = &Opcode {
+		Opname:   "s_mul24",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_u_mul24 = &Opcode {
+		Opname:   "u_mul24",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_u_abs = &Opcode {
+		Opname:   "u_abs",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_u_abs_diff = &Opcode {
+		Opname:   "u_abs_diff",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_u_mul_hi = &Opcode {
+		Opname:   "u_mul_hi",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_u_mad_hi = &Opcode {
+		Opname:   "u_mad_hi",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'a'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'b'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'c'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_fclamp = &Opcode {
+		Opname:   "fclamp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'minval'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'maxval'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_degrees = &Opcode {
+		Opname:   "degrees",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'radians'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_fmax_common = &Opcode {
+		Opname:   "fmax_common",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_fmin_common = &Opcode {
+		Opname:   "fmin_common",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_mix = &Opcode {
+		Opname:   "mix",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'a'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_radians = &Opcode {
+		Opname:   "radians",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'degrees'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_step = &Opcode {
+		Opname:   "step",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'edge'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_smoothstep = &Opcode {
+		Opname:   "smoothstep",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'edge0'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'edge1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_sign = &Opcode {
+		Opname:   "sign",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_cross = &Opcode {
+		Opname:   "cross",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p0'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p1'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_distance = &Opcode {
+		Opname:   "distance",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p0'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p1'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_length = &Opcode {
+		Opname:   "length",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_normalize = &Opcode {
+		Opname:   "normalize",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_fast_distance = &Opcode {
+		Opname:   "fast_distance",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p0'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p1'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_fast_length = &Opcode {
+		Opname:   "fast_length",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_fast_normalize = &Opcode {
+		Opname:   "fast_normalize",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_bitselect = &Opcode {
+		Opname:   "bitselect",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'a'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'b'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'c'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_select = &Opcode {
+		Opname:   "select",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'a'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'b'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'c'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_vloadn = &Opcode {
+		Opname:   "vloadn",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'n'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_vstoren = &Opcode {
+		Opname:   "vstoren",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'data'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_vload_half = &Opcode {
+		Opname:   "vload_half",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_vload_halfn = &Opcode {
+		Opname:   "vload_halfn",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'n'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_vstore_half = &Opcode {
+		Opname:   "vstore_half",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'data'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_vstore_half_r = &Opcode {
+		Opname:   "vstore_half_r",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'data'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindFPRoundingMode,
+				Name:       "'mode'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_vstore_halfn = &Opcode {
+		Opname:   "vstore_halfn",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'data'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_vstore_halfn_r = &Opcode {
+		Opname:   "vstore_halfn_r",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'data'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindFPRoundingMode,
+				Name:       "'mode'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_vloada_halfn = &Opcode {
+		Opname:   "vloada_halfn",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'n'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_vstorea_halfn = &Opcode {
+		Opname:   "vstorea_halfn",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'data'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_vstorea_halfn_r = &Opcode {
+		Opname:   "vstorea_halfn_r",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'data'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindFPRoundingMode,
+				Name:       "'mode'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_shuffle = &Opcode {
+		Opname:   "shuffle",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'shuffle mask'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_shuffle2 = &Opcode {
+		Opname:   "shuffle2",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'shuffle mask'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLStd_printf = &Opcode {
+		Opname:   "printf",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'format'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'additional arguments'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpenCLStd_prefetch = &Opcode {
+		Opname:   "prefetch",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ptr'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'num elements'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugInfoNone = &Opcode {
+		Opname:   "DebugInfoNone",
+		Operands: []Operand {
+		},
+	}
+	OpenCLDebugInfo100_DebugCompilationUnit = &Opcode {
+		Opname:   "DebugCompilationUnit",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Version'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'DWARF Version'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindSourceLanguage,
+				Name:       "'Language'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugTypeBasic = &Opcode {
+		Opname:   "DebugTypeBasic",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDebugBaseTypeAttributeEncoding,
+				Name:       "'Encoding'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugTypePointer = &Opcode {
+		Opname:   "DebugTypePointer",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindStorageClass,
+				Name:       "'Storage Class'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDebugInfoFlags,
+				Name:       "'Flags'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugTypeQualifier = &Opcode {
+		Opname:   "DebugTypeQualifier",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDebugTypeQualifier,
+				Name:       "'Type Qualifier'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugTypeArray = &Opcode {
+		Opname:   "DebugTypeArray",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Component Counts'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugTypeVector = &Opcode {
+		Opname:   "DebugTypeVector",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Component Count'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugTypedef = &Opcode {
+		Opname:   "DebugTypedef",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Column'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parent'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugTypeFunction = &Opcode {
+		Opname:   "DebugTypeFunction",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindDebugInfoFlags,
+				Name:       "'Flags'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Return Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parameter Types'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugTypeEnum = &Opcode {
+		Opname:   "DebugTypeEnum",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Underlying Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Column'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parent'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDebugInfoFlags,
+				Name:       "'Flags'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindPairIdRefIdRef,
+				Name:       "'Value, Name, Value, Name, ...'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugTypeComposite = &Opcode {
+		Opname:   "DebugTypeComposite",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDebugCompositeType,
+				Name:       "'Tag'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Column'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parent'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Linkage Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDebugInfoFlags,
+				Name:       "'Flags'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Members'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugTypeMember = &Opcode {
+		Opname:   "DebugTypeMember",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Column'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parent'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDebugInfoFlags,
+				Name:       "'Flags'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugTypeInheritance = &Opcode {
+		Opname:   "DebugTypeInheritance",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Child'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parent'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDebugInfoFlags,
+				Name:       "'Flags'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugTypePtrToMember = &Opcode {
+		Opname:   "DebugTypePtrToMember",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Member Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parent'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugTypeTemplate = &Opcode {
+		Opname:   "DebugTypeTemplate",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parameters'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugTypeTemplateParameter = &Opcode {
+		Opname:   "DebugTypeTemplateParameter",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Actual Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Column'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugTypeTemplateTemplateParameter = &Opcode {
+		Opname:   "DebugTypeTemplateTemplateParameter",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Template Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Column'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugTypeTemplateParameterPack = &Opcode {
+		Opname:   "DebugTypeTemplateParameterPack",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Column'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Template Parameters'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugGlobalVariable = &Opcode {
+		Opname:   "DebugGlobalVariable",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Column'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parent'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Linkage Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Variable'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDebugInfoFlags,
+				Name:       "'Flags'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Static Member Declaration'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugFunctionDeclaration = &Opcode {
+		Opname:   "DebugFunctionDeclaration",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Column'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parent'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Linkage Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDebugInfoFlags,
+				Name:       "'Flags'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugFunction = &Opcode {
+		Opname:   "DebugFunction",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Column'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parent'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Linkage Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDebugInfoFlags,
+				Name:       "'Flags'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Scope Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Function'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Declaration'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugLexicalBlock = &Opcode {
+		Opname:   "DebugLexicalBlock",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Column'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parent'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugLexicalBlockDiscriminator = &Opcode {
+		Opname:   "DebugLexicalBlockDiscriminator",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Discriminator'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parent'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugScope = &Opcode {
+		Opname:   "DebugScope",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Scope'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Inlined At'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugNoScope = &Opcode {
+		Opname:   "DebugNoScope",
+		Operands: []Operand {
+		},
+	}
+	OpenCLDebugInfo100_DebugInlinedAt = &Opcode {
+		Opname:   "DebugInlinedAt",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Scope'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Inlined'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugLocalVariable = &Opcode {
+		Opname:   "DebugLocalVariable",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Column'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parent'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDebugInfoFlags,
+				Name:       "'Flags'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Arg Number'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugInlinedVariable = &Opcode {
+		Opname:   "DebugInlinedVariable",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Variable'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Inlined'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugDeclare = &Opcode {
+		Opname:   "DebugDeclare",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Local Variable'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Variable'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Expression'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugValue = &Opcode {
+		Opname:   "DebugValue",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Local Variable'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Expression'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Indexes'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugOperation = &Opcode {
+		Opname:   "DebugOperation",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindDebugOperation,
+				Name:       "'OpCode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Operands ...'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugExpression = &Opcode {
+		Opname:   "DebugExpression",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operands ...'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugMacroDef = &Opcode {
+		Opname:   "DebugMacroDef",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugMacroUndef = &Opcode {
+		Opname:   "DebugMacroUndef",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Macro'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugImportedEntity = &Opcode {
+		Opname:   "DebugImportedEntity",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDebugImportedEntity,
+				Name:       "'Tag'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Entity'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Column'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parent'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpenCLDebugInfo100_DebugSource = &Opcode {
+		Opname:   "DebugSource",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'File'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Text'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+
 
 	OperandKindImageOperands = &OperandKind {
 		Kind:       "ImageOperands",
@@ -18114,5 +23616,362 @@
 		},
 		Bases:      []*OperandKind {OperandKindIdRef,OperandKindIdRef,},
 	}
+	OperandKindDebugInfoFlags = &OperandKind {
+		Kind:       "DebugInfoFlags",
+		Category:   "BitEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "FlagIsProtected",
+				Value:        0x01,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagIsPrivate",
+				Value:        0x02,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagIsPublic",
+				Value:        0x03,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagIsLocal",
+				Value:        0x04,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagIsDefinition",
+				Value:        0x08,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagFwdDecl",
+				Value:        0x10,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagArtificial",
+				Value:        0x20,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagExplicit",
+				Value:        0x40,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagPrototyped",
+				Value:        0x80,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagObjectPointer",
+				Value:        0x100,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagStaticMember",
+				Value:        0x200,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagIndirectVariable",
+				Value:        0x400,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagLValueReference",
+				Value:        0x800,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagRValueReference",
+				Value:        0x1000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagIsOptimized",
+				Value:        0x2000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagIsEnumClass",
+				Value:        0x4000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagTypePassByValue",
+				Value:        0x8000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FlagTypePassByReference",
+				Value:        0x10000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindDebugBaseTypeAttributeEncoding = &OperandKind {
+		Kind:       "DebugBaseTypeAttributeEncoding",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Unspecified",
+				Value:        0,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Address",
+				Value:        1,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Boolean",
+				Value:        2,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Float",
+				Value:        3,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Signed",
+				Value:        4,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SignedChar",
+				Value:        5,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Unsigned",
+				Value:        6,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "UnsignedChar",
+				Value:        7,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindDebugCompositeType = &OperandKind {
+		Kind:       "DebugCompositeType",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Class",
+				Value:        0,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Structure",
+				Value:        1,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Union",
+				Value:        2,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindDebugTypeQualifier = &OperandKind {
+		Kind:       "DebugTypeQualifier",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "ConstType",
+				Value:        0,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "VolatileType",
+				Value:        1,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "RestrictType",
+				Value:        2,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "AtomicType",
+				Value:        3,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindDebugOperation = &OperandKind {
+		Kind:       "DebugOperation",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Deref",
+				Value:        0,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Plus",
+				Value:        1,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Minus",
+				Value:        2,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "PlusUconst",
+				Value:        3,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "BitPiece",
+				Value:        4,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},{OperandKindLiteralInteger, ""},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Swap",
+				Value:        5,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Xderef",
+				Value:        6,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "StackValue",
+				Value:        7,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Constu",
+				Value:        8,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Fragment",
+				Value:        9,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},{OperandKindLiteralInteger, ""},},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindDebugImportedEntity = &OperandKind {
+		Kind:       "DebugImportedEntity",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "ImportedModule",
+				Value:        0,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ImportedDeclaration",
+				Value:        1,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
 
 )
diff --git a/third_party/SPIRV-Tools/utils/vscode/src/schema/schema.go.tmpl b/third_party/SPIRV-Tools/utils/vscode/src/schema/schema.go.tmpl
index b584058..987fcd0 100644
--- a/third_party/SPIRV-Tools/utils/vscode/src/schema/schema.go.tmpl
+++ b/third_party/SPIRV-Tools/utils/vscode/src/schema/schema.go.tmpl
@@ -97,14 +97,26 @@
 	OperandCategoryComposite = "Composite"
 )
 
+// OpcodeMap is a map of opcode name to Opcode type.
+type OpcodeMap map[string]*Opcode
+
 var (
 	// Opcodes is a map of opcode name to Opcode description.
-	Opcodes = map[string]*Opcode {•{{range $i := .Instructions}}
-		"{{$i.Opname}}": {{$i.Opname}},{{end}}
+	Opcodes = OpcodeMap {•{{range $i := .SPIRV.Instructions}}
+		"{{$i.Opname}}": {{Title $i.Opname}},{{end}}
 	}
 
-{{range $i := .Instructions}}	{{$i.Opname}} = &Opcode {
+	// ExtOpcodes is a map of extension name to Opcode description list.
+	ExtOpcodes = map[string]OpcodeMap {•{{range $ext := .Extensions}}
+		"{{$ext.Name}}": {•{{range $i := $ext.Instructions}}
+			"{{$i.Opname}}": {{Global $ext.Name}}_{{$i.Opname}},{{end}}
+		},{{end}}
+	}
+
+{{range $i := .SPIRV.Instructions}}	{{Title $i.Opname}} = &Opcode {
 		Opname:   "{{$i.Opname}}",
+		Class:    "{{$i.Class}}",
+		Opcode:   {{$i.Opcode}},
 		Operands: []Operand {•{{range $i := $i.Operands}}
 			Operand {
 				Kind:       OperandKind{{$i.Kind}},
@@ -114,8 +126,19 @@
 		},
 	}
 {{end}}
+{{range $ext := .Extensions}}{{range $i := $ext.Instructions}}	{{Global $ext.Name}}_{{$i.Opname}} = &Opcode {
+		Opname:   "{{$i.Opname}}",
+		Operands: []Operand {•{{range $i := $i.Operands}}
+			Operand {
+				Kind:       OperandKind{{$i.Kind}},
+				Name:       "{{Replace $i.Name "\n" " "}}",
+				Quantifier: "{{$i.Quantifier}}",
+			}, {{end}}
+		},
+	}
+{{end}}{{end}}
 
-{{range $o := .OperandKinds}}	OperandKind{{$o.Kind}} = &OperandKind {
+{{range $o := .All.OperandKinds}}	OperandKind{{$o.Kind}} = &OperandKind {
 		Kind:       "{{$o.Kind}}",
 		Category:   "{{$o.Category}}",
 		Enumerants: []Enumerant {•{{range $e := $o.Enumerants}}
diff --git a/third_party/SPIRV-Tools/utils/vscode/src/tools/gen-grammar.go b/third_party/SPIRV-Tools/utils/vscode/src/tools/gen-grammar.go
index 42cbbe9..200f695 100644
--- a/third_party/SPIRV-Tools/utils/vscode/src/tools/gen-grammar.go
+++ b/third_party/SPIRV-Tools/utils/vscode/src/tools/gen-grammar.go
@@ -34,12 +34,30 @@
 	"../grammar"
 )
 
-const (
-	spirvGrammarURL  = "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/spirv.core.grammar.json"
-	spirvGrammarName = "spirv.core.grammar.json"
-)
+type grammarDefinition struct {
+	name string
+	url  string
+}
 
 var (
+	spirvGrammar = grammarDefinition{
+		name: "SPIR-V",
+		url:  "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/spirv.core.grammar.json",
+	}
+
+	extensionGrammars = []grammarDefinition{
+		{
+			name: "GLSL.std.450",
+			url:  "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/extinst.glsl.std.450.grammar.json",
+		}, {
+			name: "OpenCL.std",
+			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",
+		},
+	}
+
 	templatePath = flag.String("template", "", "Path to input template file (required)")
 	outputPath   = flag.String("out", "", "Path to output generated file (required)")
 	cachePath    = flag.String("cache", "", "Cache directory for downloaded files (optional)")
@@ -67,6 +85,34 @@
 	if err != nil {
 		return errors.Wrap(err, "Could not open template file")
 	}
+
+	type extension struct {
+		grammar.Root
+		Name string
+	}
+
+	args := struct {
+		SPIRV      grammar.Root
+		Extensions []extension
+		All        grammar.Root // Combination of SPIRV + Extensions
+	}{}
+
+	if args.SPIRV, err = parseGrammar(spirvGrammar); err != nil {
+		return errors.Wrap(err, "Failed to parse SPIR-V grammar file")
+	}
+	args.All.Instructions = append(args.All.Instructions, args.SPIRV.Instructions...)
+	args.All.OperandKinds = append(args.All.OperandKinds, args.SPIRV.OperandKinds...)
+
+	for _, ext := range extensionGrammars {
+		root, err := parseGrammar(ext)
+		if err != nil {
+			return errors.Wrap(err, "Failed to parse extension grammar file")
+		}
+		args.Extensions = append(args.Extensions, extension{Root: root, Name: ext.name})
+		args.All.Instructions = append(args.All.Instructions, root.Instructions...)
+		args.All.OperandKinds = append(args.All.OperandKinds, root.OperandKinds...)
+	}
+
 	t, err := template.New("tmpl").
 		Funcs(template.FuncMap{
 			"GenerateArguments": func() string {
@@ -96,24 +142,30 @@
 				}
 				return sb.String()
 			},
+			"AllExtOpcodes": func() string {
+				sb := strings.Builder{}
+				for _, ext := range args.Extensions {
+					for _, inst := range ext.Root.Instructions {
+						if sb.Len() > 0 {
+							sb.WriteString("|")
+						}
+						sb.WriteString(inst.Opname)
+					}
+				}
+				return sb.String()
+			},
+			"Title":   strings.Title,
 			"Replace": strings.ReplaceAll,
+			"Global": func(s string) string {
+				return strings.ReplaceAll(strings.Title(s), ".", "")
+			},
 		}).Parse(string(tf))
 	if err != nil {
 		return errors.Wrap(err, "Failed to parse template")
 	}
 
-	file, err := getOrDownload(spirvGrammarName, spirvGrammarURL)
-	if err != nil {
-		return errors.Wrap(err, "Failed to load grammar file")
-	}
-
-	g := grammar.Root{}
-	if err := json.NewDecoder(bytes.NewReader(file)).Decode(&g); err != nil {
-		return errors.Wrap(err, "Failed to parse grammar file")
-	}
-
 	buf := bytes.Buffer{}
-	if err := t.Execute(&buf, g); err != nil {
+	if err := t.Execute(&buf, args); err != nil {
 		return errors.Wrap(err, "Failed to execute template")
 	}
 
@@ -127,6 +179,22 @@
 	return nil
 }
 
+// parseGrammar downloads (or loads from the cache) the grammar file and returns
+// the parsed grammar.Root.
+func parseGrammar(def grammarDefinition) (grammar.Root, error) {
+	file, err := getOrDownload(def.name, def.url)
+	if err != nil {
+		return grammar.Root{}, errors.Wrap(err, "Failed to load grammar file")
+	}
+
+	g := grammar.Root{}
+	if err := json.NewDecoder(bytes.NewReader(file)).Decode(&g); err != nil {
+		return grammar.Root{}, errors.Wrap(err, "Failed to parse grammar file")
+	}
+
+	return g, nil
+}
+
 // getOrDownload loads the specific file from the cache, or downloads the file
 // from the given url.
 func getOrDownload(name, url string) ([]byte, error) {