Merge changes Id54668de,I200b4417

* changes:
  Update SPIR-V Tools to 8a5500656
  Squashed 'third_party/SPIRV-Tools/' changes from 55af3902f..8a5500656
diff --git a/third_party/SPIRV-Tools/CHANGES b/third_party/SPIRV-Tools/CHANGES
index fe6641e..d10f55b 100644
--- a/third_party/SPIRV-Tools/CHANGES
+++ b/third_party/SPIRV-Tools/CHANGES
@@ -1,7 +1,77 @@
 Revision history for SPIRV-Tools
 
-v2020.3-dev 2020-03-26
- - Start v2020.3-dev
+v2020.5 2020-07-22
+ - Start SPIRV-Tools v2020.5
+
+v2020.4 2020-07-22
+ - General
+   - Changed variable names to be more descriptive (#3433)
+   - Add support to GPU-AV instrumentation for Task and Mesh shaders (#3512)
+   - Permit Simple and GLSL450 memory model in WEBGPU_0 (#3463)
+   - Support SPV_KHR_terminate_invocation (#3568)
+ - Optimizer
+   - Preserving debug information in optimizations
+     (#3389,#3420,#3425,#3356,#3459,#3444,#3492,#3451,#3497i,#3498,#3542)
+   - Eliminate branches with condition of OpConstantNull (#3438)
+   - Use structured order to unroll loops. (#3443)
+   - Updated desc_sroa to support flattening structures (#3448)
+   - Support OpCompositeExtract pattern in desc_sroa (#3456)
+   - Fix ADCE pass bug for mulitple entries (#3470)
+     - Sink pointer instructions in merge return (#3569)
+ - Validator
+   - Validate location assignments (#3308)
+     - Fix reachability in the validator (#3541)
+ - Reduce
+ - Fuzz
+   - Add support for OpSpecConstant* (#3373)
+   - Add replace linear algebra instruction transformation (#3402)
+   - Implement vector shuffle fuzzer pass (#3412)
+   - Swap operands in OpBranchConditional (#3423)
+   - Permute OpPhi instruction operands (#3421)
+   - Add FuzzerPassAddCopyMemoryInstructions (#3391)
+   - TransformationInvertComparisonOperator (#3475)
+   - Add variables with workgroup storage class (#3485)
+   - Add image sample unused components transformation (#3439)
+   - TransformationReplaceParameterWithGlobal (#3434)
+     - Support adding dead break from back-edge block (#3519)
+       - Fuzzer pass to interchange zero-like constants (#3524)
+ - Linker
+
+v2020.3 2020-05-27
+ - General
+   - Prevent Effcee install his things when build spirv-tools with testing enabled (#3256)
+   - Update acorn version (#3294)
+   - If SPIRV-Headers is in our tree, include it as subproject (#3299)
+   - allow cross compiling for Windows Store, UWP, etc. (#3330)
+ - Optimizer
+   - Remove deprecated interfaces from instrument passes (#3361)
+   - Preserve debug info in inline pass (#3349)
+   - Handle more cases in dead member elim (#3289)
+   - Preserve debug info in eliminate-dead-functions (#3251)
+   - Fix Struct CFG analysis for single block loop (#3293)
+   - Add tests for recently added command line option (#3297)
+   - Consider sampled images as read-only storage (#3295)
+   - Allow various validation options to be passed to spirv-opt (#3314)
+   - Add debug information analysis (#3305)
+   - Preserve debug info for wrap-opkill (#3331)
+   - refactor inlining pass (#3328)
+   - Add unrolling to performance passes (#3082)
+ - Validator
+   - Add validation support for ImageGatherBiasLodAMD (#3363)
+   - Validate ShaderCallKHR memory scope (#3332)
+   - Validate Buffer and BufferBlock apply only to struct types (#3259)
+ - Reduce
+   - increase default step limit (#3327)
+   - Remove unused uniforms and similar (#3321)
+ - Fuzz
+   - Add support for StorageBuffer (#3348)
+   - Add validator options (#3254)
+   - Limit adding of new variables to 'basic' types (#3257)
+   - Transformation to add OpConstantNull (#3273)
+   - Handling of more fuzzing opportunities (#3277, #3280, #3281, #3290, #3292)
+   - Respect rules for OpSampledImage (#3287)
+   - Do not outline regions that produce pointer outputs (#3291)
+ - Linker
 
 v2020.2 2020-03-26
  - General:
diff --git a/third_party/SPIRV-Tools/DEPS b/third_party/SPIRV-Tools/DEPS
index c3e78a2..a27b921 100644
--- a/third_party/SPIRV-Tools/DEPS
+++ b/third_party/SPIRV-Tools/DEPS
@@ -3,10 +3,10 @@
 vars = {
   'github': 'https://github.com',
 
-  'effcee_revision': 'cd25ec17e9382f99a895b9ef53ff3c277464d07d',
-  'googletest_revision': 'f2fb48c3b3d79a75a88a99fba6576b25d42ec528',
-  're2_revision': '5bd613749fd530b576b890283bfb6bc6ea6246cb',
-  'spirv_headers_revision': 'f8bf11a0253a32375c32cad92c841237b96696c0',
+  'effcee_revision': '5af957bbfc7da4e9f7aa8cac11379fa36dd79b84',
+  'googletest_revision': '011959aafddcd30611003de96cfd8d7a7685c700',
+  're2_revision': 'aecba11114cf1fac5497aeb844b6966106de3eb6',
+  'spirv_headers_revision': 'ac638f1815425403e946d0ab78bac71d2bdbf3be',
 }
 
 deps = {
diff --git a/third_party/SPIRV-Tools/external/CMakeLists.txt b/third_party/SPIRV-Tools/external/CMakeLists.txt
index 56dd54f..5b34159 100644
--- a/third_party/SPIRV-Tools/external/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/external/CMakeLists.txt
@@ -47,7 +47,7 @@
   if (TARGET gmock)
     message(STATUS "Google Mock already configured")
   else()
-    set(GMOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/googletest/googlemock)
+    set(GMOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/googletest)
     if(EXISTS ${GMOCK_DIR})
       if(MSVC)
         # Our tests use ::testing::Combine.  Work around a compiler
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp b/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp
index ef5136a..b4f3355 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp
+++ b/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp
@@ -110,6 +110,16 @@
 static const int kInstRayTracingOutLaunchIdY = kInstCommonOutCnt + 1;
 static const int kInstRayTracingOutLaunchIdZ = kInstCommonOutCnt + 2;
 
+// Mesh Shader Output Record Offsets
+static const int kInstMeshOutGlobalInvocationIdX = kInstCommonOutCnt;
+static const int kInstMeshOutGlobalInvocationIdY = kInstCommonOutCnt + 1;
+static const int kInstMeshOutGlobalInvocationIdZ = kInstCommonOutCnt + 2;
+
+// Task Shader Output Record Offsets
+static const int kInstTaskOutGlobalInvocationIdX = kInstCommonOutCnt;
+static const int kInstTaskOutGlobalInvocationIdY = kInstCommonOutCnt + 1;
+static const int kInstTaskOutGlobalInvocationIdZ = kInstCommonOutCnt + 2;
+
 // Size of Common and Stage-specific Members
 static const int kInstStageOutCnt = kInstCommonOutCnt + 3;
 
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h
index 03c7d1b..68afd64 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h
+++ b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h
@@ -676,6 +676,12 @@
 SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed(
     spv_fuzzer_options options, uint32_t seed);
 
+// Sets the range of transformations that should be applied during replay: 0
+// means all transformations, +N means the first N transformations, -N means all
+// except the final N transformations.
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetReplayRange(
+    spv_fuzzer_options options, int32_t replay_range);
+
 // Sets the maximum number of steps that the shrinker should take before giving
 // up.
 SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp
index 5e1819e..6b31a07 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp
+++ b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp
@@ -227,6 +227,11 @@
     spvFuzzerOptionsSetRandomSeed(options_, seed);
   }
 
+  // See spvFuzzerOptionsSetReplayRange.
+  void set_replay_range(int32_t replay_range) {
+    spvFuzzerOptionsSetReplayRange(options_, replay_range);
+  }
+
   // See spvFuzzerOptionsSetShrinkerStepLimit.
   void set_shrinker_step_limit(uint32_t shrinker_step_limit) {
     spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit);
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp b/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
index d393495..741f947 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
+++ b/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
@@ -876,8 +876,10 @@
 // for the first index.
 Optimizer::PassToken CreateDescriptorScalarReplacementPass();
 
-// Create a pass to replace all OpKill instruction with a function call to a
-// function that has a single OpKill.  This allows more code to be inlined.
+// Create a pass to replace each OpKill instruction with a function call to a
+// function that has a single OpKill.  Also replace each OpTerminateInvocation
+// instruction  with a function call to a function that has a single
+// OpTerminateInvocation.  This allows more code to be inlined.
 Optimizer::PassToken CreateWrapOpKillPass();
 
 // Replaces the extensions VK_AMD_shader_ballot,VK_AMD_gcn_shader, and
diff --git a/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt b/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
index 6582927..c413614 100644
--- a/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
@@ -39,16 +39,22 @@
         fuzzer_pass.h
         fuzzer_pass_add_access_chains.h
         fuzzer_pass_add_composite_types.h
+        fuzzer_pass_add_copy_memory.h
         fuzzer_pass_add_dead_blocks.h
         fuzzer_pass_add_dead_breaks.h
         fuzzer_pass_add_dead_continues.h
         fuzzer_pass_add_equation_instructions.h
         fuzzer_pass_add_function_calls.h
         fuzzer_pass_add_global_variables.h
+        fuzzer_pass_add_image_sample_unused_components.h
+        fuzzer_pass_add_synonyms.h
         fuzzer_pass_add_loads.h
         fuzzer_pass_add_local_variables.h
         fuzzer_pass_add_no_contraction_decorations.h
+        fuzzer_pass_add_parameters.h
+        fuzzer_pass_add_relaxed_decorations.h
         fuzzer_pass_add_stores.h
+        fuzzer_pass_add_vector_shuffle_instructions.h
         fuzzer_pass_adjust_branch_weights.h
         fuzzer_pass_adjust_function_controls.h
         fuzzer_pass_adjust_loop_controls.h
@@ -58,13 +64,24 @@
         fuzzer_pass_construct_composites.h
         fuzzer_pass_copy_objects.h
         fuzzer_pass_donate_modules.h
+        fuzzer_pass_invert_comparison_operators.h
+        fuzzer_pass_interchange_zero_like_constants.h
         fuzzer_pass_merge_blocks.h
         fuzzer_pass_obfuscate_constants.h
         fuzzer_pass_outline_functions.h
         fuzzer_pass_permute_blocks.h
         fuzzer_pass_permute_function_parameters.h
+        fuzzer_pass_permute_phi_operands.h
+        fuzzer_pass_push_ids_through_variables.h
+        fuzzer_pass_replace_copy_memories_with_loads_stores.h
+        fuzzer_pass_replace_copy_objects_with_stores_loads.h
+        fuzzer_pass_replace_linear_algebra_instructions.h
+        fuzzer_pass_replace_loads_stores_with_copy_memories.h
+        fuzzer_pass_replace_parameter_with_global.h
+        fuzzer_pass_replace_params_with_struct.h
         fuzzer_pass_split_blocks.h
         fuzzer_pass_swap_commutable_operands.h
+        fuzzer_pass_swap_conditional_branch_operands.h
         fuzzer_pass_toggle_access_chain_instruction.h
         fuzzer_util.h
         id_use_descriptor.h
@@ -81,14 +98,20 @@
         transformation_add_constant_composite.h
         transformation_add_constant_null.h
         transformation_add_constant_scalar.h
+        transformation_add_copy_memory.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_image_sample_unused_components.h
         transformation_add_local_variable.h
         transformation_add_no_contraction_decoration.h
+        transformation_add_parameter.h
+        transformation_add_relaxed_decoration.h
+        transformation_add_spec_constant_op.h
+        transformation_add_synonym.h
         transformation_add_type_array.h
         transformation_add_type_boolean.h
         transformation_add_type_float.h
@@ -103,17 +126,26 @@
         transformation_composite_extract.h
         transformation_compute_data_synonym_fact_closure.h
         transformation_context.h
-        transformation_copy_object.h
         transformation_equation_instruction.h
         transformation_function_call.h
+        transformation_invert_comparison_operator.h
         transformation_load.h
         transformation_merge_blocks.h
         transformation_move_block_down.h
         transformation_outline_function.h
         transformation_permute_function_parameters.h
+        transformation_permute_phi_operands.h
+        transformation_push_id_through_variable.h
+        transformation_record_synonymous_constants.h
         transformation_replace_boolean_constant_with_constant_binary.h
         transformation_replace_constant_with_uniform.h
+        transformation_replace_copy_memory_with_load_store.h
+        transformation_replace_copy_object_with_store_load.h
         transformation_replace_id_with_synonym.h
+        transformation_replace_linear_algebra_instruction.h
+        transformation_replace_load_store_with_copy_memory.h
+        transformation_replace_parameter_with_global.h
+        transformation_replace_params_with_struct.h
         transformation_set_function_control.h
         transformation_set_loop_control.h
         transformation_set_memory_operands_mask.h
@@ -121,6 +153,7 @@
         transformation_split_block.h
         transformation_store.h
         transformation_swap_commutable_operands.h
+        transformation_swap_conditional_branch_operands.h
         transformation_toggle_access_chain_instruction.h
         transformation_vector_shuffle.h
         uniform_buffer_element_descriptor.h
@@ -135,16 +168,22 @@
         fuzzer_pass.cpp
         fuzzer_pass_add_access_chains.cpp
         fuzzer_pass_add_composite_types.cpp
+        fuzzer_pass_add_copy_memory.cpp
         fuzzer_pass_add_dead_blocks.cpp
         fuzzer_pass_add_dead_breaks.cpp
         fuzzer_pass_add_dead_continues.cpp
         fuzzer_pass_add_equation_instructions.cpp
         fuzzer_pass_add_function_calls.cpp
         fuzzer_pass_add_global_variables.cpp
+        fuzzer_pass_add_image_sample_unused_components.cpp
+        fuzzer_pass_add_synonyms.cpp
         fuzzer_pass_add_loads.cpp
         fuzzer_pass_add_local_variables.cpp
         fuzzer_pass_add_no_contraction_decorations.cpp
+        fuzzer_pass_add_parameters.cpp
+        fuzzer_pass_add_relaxed_decorations.cpp
         fuzzer_pass_add_stores.cpp
+        fuzzer_pass_add_vector_shuffle_instructions.cpp
         fuzzer_pass_adjust_branch_weights.cpp
         fuzzer_pass_adjust_function_controls.cpp
         fuzzer_pass_adjust_loop_controls.cpp
@@ -154,13 +193,24 @@
         fuzzer_pass_construct_composites.cpp
         fuzzer_pass_copy_objects.cpp
         fuzzer_pass_donate_modules.cpp
+        fuzzer_pass_invert_comparison_operators.cpp
+        fuzzer_pass_interchange_zero_like_constants.cpp
         fuzzer_pass_merge_blocks.cpp
         fuzzer_pass_obfuscate_constants.cpp
         fuzzer_pass_outline_functions.cpp
         fuzzer_pass_permute_blocks.cpp
         fuzzer_pass_permute_function_parameters.cpp
+        fuzzer_pass_permute_phi_operands.cpp
+        fuzzer_pass_push_ids_through_variables.cpp
+        fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
+        fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
+        fuzzer_pass_replace_linear_algebra_instructions.cpp
+        fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
+        fuzzer_pass_replace_parameter_with_global.cpp
+        fuzzer_pass_replace_params_with_struct.cpp
         fuzzer_pass_split_blocks.cpp
         fuzzer_pass_swap_commutable_operands.cpp
+        fuzzer_pass_swap_conditional_branch_operands.cpp
         fuzzer_pass_toggle_access_chain_instruction.cpp
         fuzzer_util.cpp
         id_use_descriptor.cpp
@@ -176,14 +226,20 @@
         transformation_add_constant_composite.cpp
         transformation_add_constant_null.cpp
         transformation_add_constant_scalar.cpp
+        transformation_add_copy_memory.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_image_sample_unused_components.cpp
         transformation_add_local_variable.cpp
         transformation_add_no_contraction_decoration.cpp
+        transformation_add_parameter.cpp
+        transformation_add_relaxed_decoration.cpp
+        transformation_add_spec_constant_op.cpp
+        transformation_add_synonym.cpp
         transformation_add_type_array.cpp
         transformation_add_type_boolean.cpp
         transformation_add_type_float.cpp
@@ -198,17 +254,26 @@
         transformation_composite_extract.cpp
         transformation_compute_data_synonym_fact_closure.cpp
         transformation_context.cpp
-        transformation_copy_object.cpp
         transformation_equation_instruction.cpp
         transformation_function_call.cpp
+        transformation_invert_comparison_operator.cpp
         transformation_load.cpp
         transformation_merge_blocks.cpp
         transformation_move_block_down.cpp
         transformation_outline_function.cpp
         transformation_permute_function_parameters.cpp
+        transformation_permute_phi_operands.cpp
+        transformation_push_id_through_variable.cpp
+        transformation_record_synonymous_constants.cpp
         transformation_replace_boolean_constant_with_constant_binary.cpp
         transformation_replace_constant_with_uniform.cpp
+        transformation_replace_copy_memory_with_load_store.cpp
+        transformation_replace_copy_object_with_store_load.cpp
         transformation_replace_id_with_synonym.cpp
+        transformation_replace_linear_algebra_instruction.cpp
+        transformation_replace_load_store_with_copy_memory.cpp
+        transformation_replace_parameter_with_global.cpp
+        transformation_replace_params_with_struct.cpp
         transformation_set_function_control.cpp
         transformation_set_loop_control.cpp
         transformation_set_memory_operands_mask.cpp
@@ -216,6 +281,7 @@
         transformation_split_block.cpp
         transformation_store.cpp
         transformation_swap_commutable_operands.cpp
+        transformation_swap_conditional_branch_operands.cpp
         transformation_toggle_access_chain_instruction.cpp
         transformation_vector_shuffle.cpp
         uniform_buffer_element_descriptor.cpp
diff --git a/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp b/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
index 0b41eeb..6dff669 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
@@ -457,6 +457,18 @@
                                    const protobufs::DataDescriptor& dd2,
                                    opt::IRContext* context);
 
+  // Computes various corollary facts from the data descriptor |dd| if members
+  // of its equivalence class participate in equation facts with OpConvert*
+  // opcodes. The descriptor should be registered in the equivalence relation.
+  void ComputeConversionDataSynonymFacts(const protobufs::DataDescriptor& dd,
+                                         opt::IRContext* context);
+
+  // Recurses into sub-components of the data descriptors, if they are
+  // composites, to record that their components are pairwise-synonymous.
+  void ComputeCompositeDataSynonymFacts(const protobufs::DataDescriptor& dd1,
+                                        const protobufs::DataDescriptor& dd2,
+                                        opt::IRContext* context);
+
   // Records the fact that |dd1| and |dd2| are equivalent, and merges the sets
   // of equations that are known about them.
   void MakeEquivalent(const protobufs::DataDescriptor& dd1,
@@ -588,9 +600,13 @@
   // Now try to work out corollaries implied by the new equation and existing
   // facts.
   switch (opcode) {
+    case SpvOpConvertSToF:
+    case SpvOpConvertUToF:
+      ComputeConversionDataSynonymFacts(*rhs_dds[0], context);
+      break;
     case SpvOpIAdd: {
       // Equation form: "a = b + c"
-      for (auto equation : GetEquations(rhs_dds[0])) {
+      for (const auto& equation : GetEquations(rhs_dds[0])) {
         if (equation.opcode == SpvOpISub) {
           // Equation form: "a = (d - e) + c"
           if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) {
@@ -606,7 +622,7 @@
           }
         }
       }
-      for (auto equation : GetEquations(rhs_dds[1])) {
+      for (const auto& equation : GetEquations(rhs_dds[1])) {
         if (equation.opcode == SpvOpISub) {
           // Equation form: "a = b + (d - e)"
           if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) {
@@ -620,7 +636,7 @@
     }
     case SpvOpISub: {
       // Equation form: "a = b - c"
-      for (auto equation : GetEquations(rhs_dds[0])) {
+      for (const auto& equation : GetEquations(rhs_dds[0])) {
         if (equation.opcode == SpvOpIAdd) {
           // Equation form: "a = (d + e) - c"
           if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
@@ -646,7 +662,7 @@
         }
       }
 
-      for (auto equation : GetEquations(rhs_dds[1])) {
+      for (const auto& equation : GetEquations(rhs_dds[1])) {
         if (equation.opcode == SpvOpIAdd) {
           // Equation form: "a = b - (d + e)"
           if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) {
@@ -676,7 +692,7 @@
     case SpvOpLogicalNot:
     case SpvOpSNegate: {
       // Equation form: "a = !b" or "a = -b"
-      for (auto equation : GetEquations(rhs_dds[0])) {
+      for (const auto& equation : GetEquations(rhs_dds[0])) {
         if (equation.opcode == opcode) {
           // Equation form: "a = !!b" or "a = -(-b)"
           // We can thus infer "a = b"
@@ -698,7 +714,69 @@
   // Record that the data descriptors provided in the fact are equivalent.
   MakeEquivalent(dd1, dd2);
 
-  // We now check whether this is a synonym about composite objects.  If it is,
+  // Compute various corollary facts.
+  ComputeConversionDataSynonymFacts(dd1, context);
+  ComputeCompositeDataSynonymFacts(dd1, dd2, context);
+}
+
+void FactManager::DataSynonymAndIdEquationFacts::
+    ComputeConversionDataSynonymFacts(const protobufs::DataDescriptor& dd,
+                                      opt::IRContext* context) {
+  assert(synonymous_.Exists(dd) &&
+         "|dd| should've been registered in the equivalence relation");
+
+  const auto* representative = synonymous_.Find(&dd);
+  assert(representative &&
+         "Representative can't be null for a registered descriptor");
+
+  const auto* type =
+      context->get_type_mgr()->GetType(fuzzerutil::WalkCompositeTypeIndices(
+          context, fuzzerutil::GetTypeId(context, representative->object()),
+          representative->index()));
+  assert(type && "Data descriptor has invalid type");
+
+  if ((type->AsVector() && type->AsVector()->element_type()->AsInteger()) ||
+      type->AsInteger()) {
+    // If there exist equation facts of the form |%a = opcode %representative|
+    // and |%b = opcode %representative| where |opcode| is either OpConvertSToF
+    // or OpConvertUToF, then |a| and |b| are synonymous.
+    std::vector<const protobufs::DataDescriptor*> convert_s_to_f_lhs;
+    std::vector<const protobufs::DataDescriptor*> convert_u_to_f_lhs;
+
+    for (const auto& fact : id_equations_) {
+      for (const auto& equation : fact.second) {
+        if (synonymous_.IsEquivalent(*equation.operands[0], *representative)) {
+          if (equation.opcode == SpvOpConvertSToF) {
+            convert_s_to_f_lhs.push_back(fact.first);
+          } else if (equation.opcode == SpvOpConvertUToF) {
+            convert_u_to_f_lhs.push_back(fact.first);
+          }
+        }
+      }
+    }
+
+    for (const auto& synonyms :
+         {std::move(convert_s_to_f_lhs), std::move(convert_u_to_f_lhs)}) {
+      for (const auto* synonym_a : synonyms) {
+        for (const auto* synonym_b : synonyms) {
+          if (!synonymous_.IsEquivalent(*synonym_a, *synonym_b) &&
+              DataDescriptorsAreWellFormedAndComparable(context, *synonym_a,
+                                                        *synonym_b)) {
+            // |synonym_a| and |synonym_b| have compatible types - they are
+            // synonymous.
+            AddDataSynonymFactRecursive(*synonym_a, *synonym_b, context);
+          }
+        }
+      }
+    }
+  }
+}
+
+void FactManager::DataSynonymAndIdEquationFacts::
+    ComputeCompositeDataSynonymFacts(const protobufs::DataDescriptor& dd1,
+                                     const protobufs::DataDescriptor& dd2,
+                                     opt::IRContext* context) {
+  // Check whether this is a synonym about composite objects.  If it is,
   // we can recursively add synonym facts about their associated sub-components.
 
   // Get the type of the object referred to by the first data descriptor in the
@@ -813,8 +891,10 @@
   struct DataDescriptorPairEquals {
     bool operator()(const DataDescriptorPair& first,
                     const DataDescriptorPair& second) const {
-      return DataDescriptorEquals()(&first.first, &second.first) &&
-             DataDescriptorEquals()(&first.second, &second.second);
+      return (DataDescriptorEquals()(&first.first, &second.first) &&
+              DataDescriptorEquals()(&first.second, &second.second)) ||
+             (DataDescriptorEquals()(&first.first, &second.second) &&
+              DataDescriptorEquals()(&first.second, &second.first));
     }
   };
 
@@ -1263,32 +1343,49 @@
 //==============================
 
 //==============================
-// Irrelevant pointee value facts
+// Irrelevant 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 {
+// facts about various irrelevant values in the module.
+class FactManager::IrrelevantValueFacts {
  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.
+  void AddFact(const protobufs::FactIdIsIrrelevant& fact);
+
+  // See method in FactManager which delegates to this method.
   bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
 
+  // See method in FactManager which delegates to this method.
+  bool IdIsIrrelevant(uint32_t pointer_id) const;
+
  private:
-  std::set<uint32_t> pointers_to_irrelevant_pointees_ids_;
+  std::unordered_set<uint32_t> pointers_to_irrelevant_pointees_ids_;
+  std::unordered_set<uint32_t> irrelevant_ids_;
 };
 
-void FactManager::IrrelevantPointeeValueFacts::AddFact(
+void FactManager::IrrelevantValueFacts::AddFact(
     const protobufs::FactPointeeValueIsIrrelevant& fact) {
   pointers_to_irrelevant_pointees_ids_.insert(fact.pointer_id());
 }
 
-bool FactManager::IrrelevantPointeeValueFacts::PointeeValueIsIrrelevant(
+void FactManager::IrrelevantValueFacts::AddFact(
+    const protobufs::FactIdIsIrrelevant& fact) {
+  irrelevant_ids_.insert(fact.result_id());
+}
+
+bool FactManager::IrrelevantValueFacts::PointeeValueIsIrrelevant(
     uint32_t pointer_id) const {
   return pointers_to_irrelevant_pointees_ids_.count(pointer_id) != 0;
 }
 
+bool FactManager::IrrelevantValueFacts::IdIsIrrelevant(
+    uint32_t pointer_id) const {
+  return irrelevant_ids_.count(pointer_id) != 0;
+}
+
 // End of arbitrarily-valued variable facts
 //==============================
 
@@ -1298,8 +1395,7 @@
           MakeUnique<DataSynonymAndIdEquationFacts>()),
       dead_block_facts_(MakeUnique<DeadBlockFacts>()),
       livesafe_function_facts_(MakeUnique<LivesafeFunctionFacts>()),
-      irrelevant_pointee_value_facts_(
-          MakeUnique<IrrelevantPointeeValueFacts>()) {}
+      irrelevant_value_facts_(MakeUnique<IrrelevantValueFacts>()) {}
 
 FactManager::~FactManager() = default;
 
@@ -1340,6 +1436,8 @@
 void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1,
                                      const protobufs::DataDescriptor& data2,
                                      opt::IRContext* context) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550):
+  //  assert that neither |data1| nor |data2| are irrelevant.
   protobufs::FactDataSynonym fact;
   *fact.mutable_data1() = data1;
   *fact.mutable_data2() = data2;
@@ -1420,18 +1518,32 @@
 }
 
 bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const {
-  return irrelevant_pointee_value_facts_->PointeeValueIsIrrelevant(pointer_id);
+  return irrelevant_value_facts_->PointeeValueIsIrrelevant(pointer_id);
+}
+
+bool FactManager::IdIsIrrelevant(uint32_t result_id) const {
+  return irrelevant_value_facts_->IdIsIrrelevant(result_id);
 }
 
 void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) {
   protobufs::FactPointeeValueIsIrrelevant fact;
   fact.set_pointer_id(pointer_id);
-  irrelevant_pointee_value_facts_->AddFact(fact);
+  irrelevant_value_facts_->AddFact(fact);
+}
+
+void FactManager::AddFactIdIsIrrelevant(uint32_t result_id) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550):
+  //  assert that |result_id| is not a part of any DataSynonym fact.
+  protobufs::FactIdIsIrrelevant fact;
+  fact.set_result_id(result_id);
+  irrelevant_value_facts_->AddFact(fact);
 }
 
 void FactManager::AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
                                     const std::vector<uint32_t>& rhs_id,
                                     opt::IRContext* context) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550):
+  //  assert that elements of |rhs_id| and |lhs_id| are not irrelevant.
   protobufs::FactIdEquation fact;
   fact.set_lhs_id(lhs_id);
   fact.set_opcode(opcode);
diff --git a/third_party/SPIRV-Tools/source/fuzz/fact_manager.h b/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
index f520e42..f83e2ff 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
@@ -68,6 +68,10 @@
   // is irrelevant: it does not affect the observable behaviour of the module.
   void AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id);
 
+  // Records a fact that the |result_id| is irrelevant (i.e. it doesn't affect
+  // the semantics of the module)
+  void AddFactIdIsIrrelevant(uint32_t result_id);
+
   // Records the fact that |lhs_id| is defined by the equation:
   //
   //   |lhs_id| = |opcode| |rhs_id[0]| ... |rhs_id[N-1]|
@@ -181,13 +185,16 @@
   //==============================
 
   //==============================
-  // Querying facts about pointers with irrelevant pointee values
+  // Querying facts about irrelevant 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
+  // Returns true iff there exists a fact that the |result_id| is irrelevant.
+  bool IdIsIrrelevant(uint32_t result_id) const;
+
+  // End of irrelevant value facts
   //==============================
 
  private:
@@ -213,10 +220,10 @@
   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.
+  class IrrelevantValueFacts;  // Opaque class for management of
+  // facts about various irrelevant values in the module.
+  std::unique_ptr<IrrelevantValueFacts>
+      irrelevant_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 3343abc..f549590 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
@@ -18,36 +18,49 @@
 #include <memory>
 #include <sstream>
 
-#include "fuzzer_pass_adjust_memory_operands_masks.h"
 #include "source/fuzz/fact_manager.h"
 #include "source/fuzz/fuzzer_context.h"
 #include "source/fuzz/fuzzer_pass_add_access_chains.h"
 #include "source/fuzz/fuzzer_pass_add_composite_types.h"
+#include "source/fuzz/fuzzer_pass_add_copy_memory.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_equation_instructions.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_image_sample_unused_components.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_parameters.h"
 #include "source/fuzz/fuzzer_pass_add_stores.h"
+#include "source/fuzz/fuzzer_pass_add_synonyms.h"
+#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h"
 #include "source/fuzz/fuzzer_pass_adjust_branch_weights.h"
 #include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
 #include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
+#include "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h"
 #include "source/fuzz/fuzzer_pass_adjust_selection_controls.h"
 #include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
 #include "source/fuzz/fuzzer_pass_construct_composites.h"
 #include "source/fuzz/fuzzer_pass_copy_objects.h"
 #include "source/fuzz/fuzzer_pass_donate_modules.h"
+#include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h"
+#include "source/fuzz/fuzzer_pass_invert_comparison_operators.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"
 #include "source/fuzz/fuzzer_pass_permute_blocks.h"
 #include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
+#include "source/fuzz/fuzzer_pass_permute_phi_operands.h"
+#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h"
+#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
+#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h"
+#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h"
 #include "source/fuzz/fuzzer_pass_split_blocks.h"
 #include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
+#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
 #include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/pseudo_random_generator.h"
@@ -195,6 +208,9 @@
     MaybeAddPass<FuzzerPassAddCompositeTypes>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddCopyMemory>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassAddDeadBlocks>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
@@ -213,15 +229,27 @@
     MaybeAddPass<FuzzerPassAddGlobalVariables>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddImageSampleUnusedComponents>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassAddLoads>(&passes, ir_context.get(),
                                      &transformation_context, &fuzzer_context,
                                      transformation_sequence_out);
     MaybeAddPass<FuzzerPassAddLocalVariables>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddParameters>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(),
                                       &transformation_context, &fuzzer_context,
                                       transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddSynonyms>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddVectorShuffleInstructions>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassApplyIdSynonyms>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
@@ -234,6 +262,9 @@
     MaybeAddPass<FuzzerPassDonateModules>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out, donor_suppliers);
+    MaybeAddPass<FuzzerPassInvertComparisonOperators>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassMergeBlocks>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
@@ -249,9 +280,24 @@
     MaybeAddPass<FuzzerPassPermuteFunctionParameters>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
+    MaybeAddPass<FuzzerPassPushIdsThroughVariables>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassReplaceParameterWithGlobal>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassReplaceLinearAlgebraInstructions>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassReplaceParamsWithStruct>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassSplitBlocks>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
+    MaybeAddPass<FuzzerPassSwapBranchConditionalOperands>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
   }
 
   bool is_first = true;
@@ -289,6 +335,12 @@
   MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
       transformation_sequence_out);
+  MaybeAddPass<FuzzerPassInterchangeZeroLikeConstants>(
+      &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
+      transformation_sequence_out);
+  MaybeAddPass<FuzzerPassPermutePhiOperands>(
+      &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
+      transformation_sequence_out);
   MaybeAddPass<FuzzerPassSwapCommutableOperands>(
       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
       transformation_sequence_out);
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
index 1779709..3f1fe16 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
@@ -27,19 +27,26 @@
 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> kChanceOfAddingCopyMemory = {20, 50};
 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> kChanceOfAddingEquationInstruction = {5,
                                                                           90};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingGlobalVariable = {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingImageSampleUnusedComponents =
+    {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> kChanceOfAddingParameters = {5, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingRelaxedDecoration = {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingStore = {5, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingSynonyms = {20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorType = {20, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorShuffle = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingBranchWeights = {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingFunctionControl = {20,
                                                                          70};
@@ -51,19 +58,41 @@
 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> kChanceOfChoosingWorkgroupStorageClass = {
+    50, 50};
 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> kChanceOfInterchangingZeroLikeConstants = {
+    10, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfInvertingComparisonOperators = {
+    20, 50};
 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};
 const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfPermutingPhiOperands = {30, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfPushingIdThroughVariable = {5, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyMemoryWithLoadStore =
+    {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyObjectWithStoreLoad =
+    {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
+const std::pair<uint32_t, uint32_t>
+    kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfReplacingLoadStoreWithCopyMemory =
+    {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithGlobals = {
+    30, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithStruct = {
+    20, 40};
 const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
+const std::pair<uint32_t, uint32_t> kChanceOfSwappingConditionalBranchOperands =
+    {10, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfTogglingAccessChainInstruction = {
     20, 90};
 
@@ -74,6 +103,11 @@
 const uint32_t kDefaultMaxLoopControlPeelCount = 100;
 const uint32_t kDefaultMaxLoopLimit = 20;
 const uint32_t kDefaultMaxNewArraySizeLimit = 100;
+// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3424):
+//  think whether there is a better limit on the maximum number of parameters.
+const uint32_t kDefaultMaxNumberOfFunctionParameters = 128;
+const uint32_t kDefaultMaxNumberOfNewParameters = 15;
+const uint32_t kGetDefaultMaxNumberOfParametersReplacedWithStruct = 5;
 
 // Default functions for controlling how deep to go during recursive
 // generation/transformation. Keep them in alphabetical order.
@@ -97,6 +131,10 @@
       max_loop_control_peel_count_(kDefaultMaxLoopControlPeelCount),
       max_loop_limit_(kDefaultMaxLoopLimit),
       max_new_array_size_limit_(kDefaultMaxNewArraySizeLimit),
+      max_number_of_function_parameters_(kDefaultMaxNumberOfFunctionParameters),
+      max_number_of_new_parameters_(kDefaultMaxNumberOfNewParameters),
+      max_number_of_parameters_replaced_with_struct_(
+          kGetDefaultMaxNumberOfParametersReplacedWithStruct),
       go_deeper_in_constant_obfuscation_(
           kDefaultGoDeeperInConstantObfuscation) {
   chance_of_adding_access_chain_ =
@@ -105,6 +143,8 @@
       ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField);
   chance_of_adding_array_or_struct_type_ =
       ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType);
+  chance_of_adding_copy_memory_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingCopyMemory);
   chance_of_adding_dead_block_ =
       ChooseBetweenMinAndMax(kChanceOfAddingDeadBlock);
   chance_of_adding_dead_break_ =
@@ -116,19 +156,28 @@
   chance_of_adding_global_variable_ =
       ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable);
   chance_of_adding_load_ = ChooseBetweenMinAndMax(kChanceOfAddingLoad);
+  chance_of_adding_image_sample_unused_components_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingImageSampleUnusedComponents);
   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_parameters =
+      ChooseBetweenMinAndMax(kChanceOfAddingParameters);
+  chance_of_adding_relaxed_decoration_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingRelaxedDecoration);
   chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore);
+  chance_of_adding_vector_shuffle_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingVectorShuffle);
   chance_of_adding_vector_type_ =
       ChooseBetweenMinAndMax(kChanceOfAddingVectorType);
   chance_of_adjusting_branch_weights_ =
       ChooseBetweenMinAndMax(kChanceOfAdjustingBranchWeights);
   chance_of_adjusting_function_control_ =
       ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl);
+  chance_of_adding_synonyms_ = ChooseBetweenMinAndMax(kChanceOfAddingSynonyms);
   chance_of_adjusting_loop_control_ =
       ChooseBetweenMinAndMax(kChanceOfAdjustingLoopControl);
   chance_of_adjusting_memory_operands_mask_ =
@@ -139,6 +188,8 @@
       ChooseBetweenMinAndMax(kChanceOfCallingFunction);
   chance_of_choosing_struct_type_vs_array_type_ =
       ChooseBetweenMinAndMax(kChanceOfChoosingStructTypeVsArrayType);
+  chance_of_choosing_workgroup_storage_class_ =
+      ChooseBetweenMinAndMax(kChanceOfChoosingWorkgroupStorageClass);
   chance_of_constructing_composite_ =
       ChooseBetweenMinAndMax(kChanceOfConstructingComposite);
   chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
@@ -146,6 +197,10 @@
       ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
   chance_of_going_deeper_when_making_access_chain_ =
       ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain);
+  chance_of_interchanging_zero_like_constants_ =
+      ChooseBetweenMinAndMax(kChanceOfInterchangingZeroLikeConstants);
+  chance_of_inverting_comparison_operators_ =
+      ChooseBetweenMinAndMax(kChanceOfInvertingComparisonOperators);
   chance_of_making_donor_livesafe_ =
       ChooseBetweenMinAndMax(kChanceOfMakingDonorLivesafe);
   chance_of_merging_blocks_ = ChooseBetweenMinAndMax(kChanceOfMergingBlocks);
@@ -157,9 +212,27 @@
       ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
   chance_of_permuting_parameters_ =
       ChooseBetweenMinAndMax(kChanceOfPermutingParameters);
+  chance_of_permuting_phi_operands_ =
+      ChooseBetweenMinAndMax(kChanceOfPermutingPhiOperands);
+  chance_of_pushing_id_through_variable_ =
+      ChooseBetweenMinAndMax(kChanceOfPushingIdThroughVariable);
+  chance_of_replacing_copy_memory_with_load_store_ =
+      ChooseBetweenMinAndMax(kChanceOfReplacingCopyMemoryWithLoadStore);
+  chance_of_replacing_copyobject_with_store_load_ =
+      ChooseBetweenMinAndMax(kChanceOfReplacingCopyObjectWithStoreLoad);
   chance_of_replacing_id_with_synonym_ =
       ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
+  chance_of_replacing_linear_algebra_instructions_ =
+      ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
+  chance_of_replacing_load_store_with_copy_memory_ =
+      ChooseBetweenMinAndMax(kChanceOfReplacingLoadStoreWithCopyMemory);
+  chance_of_replacing_parameters_with_globals_ =
+      ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals);
+  chance_of_replacing_parameters_with_struct_ =
+      ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithStruct);
   chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
+  chance_of_swapping_conditional_branch_operands_ =
+      ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands);
   chance_of_toggling_access_chain_instruction_ =
       ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction);
 }
@@ -168,6 +241,16 @@
 
 uint32_t FuzzerContext::GetFreshId() { return next_fresh_id_++; }
 
+std::vector<uint32_t> FuzzerContext::GetFreshIds(const uint32_t count) {
+  std::vector<uint32_t> fresh_ids(count);
+
+  for (uint32_t& fresh_id : fresh_ids) {
+    fresh_id = next_fresh_id_++;
+  }
+
+  return fresh_ids;
+}
+
 bool FuzzerContext::ChooseEven() { return random_generator_->RandomBool(); }
 
 bool FuzzerContext::ChoosePercentage(uint32_t percentage_chance) {
@@ -182,5 +265,21 @@
          random_generator_->RandomUint32(min_max.second - min_max.first + 1);
 }
 
+protobufs::TransformationAddSynonym::SynonymType
+FuzzerContext::GetRandomSynonymType() {
+  // value_count method is guaranteed to return a value greater than 0.
+  auto result_index = ChooseBetweenMinAndMax(
+      {0, static_cast<uint32_t>(
+              protobufs::TransformationAddSynonym::SynonymType_descriptor()
+                  ->value_count() -
+              1)});
+  auto result = protobufs::TransformationAddSynonym::SynonymType_descriptor()
+                    ->value(result_index)
+                    ->number();
+  assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(result) &&
+         "|result| is not a value of SynonymType");
+  return static_cast<protobufs::TransformationAddSynonym::SynonymType>(result);
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
index dd19d9a..3376c1e 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
@@ -18,6 +18,7 @@
 #include <functional>
 #include <utility>
 
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/random_generator.h"
 #include "source/opt/function.h"
 
@@ -100,6 +101,9 @@
   // or to have been issued before.
   uint32_t GetFreshId();
 
+  // Returns a vector of |count| fresh ids.
+  std::vector<uint32_t> GetFreshIds(const uint32_t count);
+
   // Probabilities associated with applying various transformations.
   // Keep them in alphabetical order.
   uint32_t GetChanceOfAddingAccessChain() {
@@ -111,6 +115,9 @@
   uint32_t GetChanceOfAddingArrayOrStructType() {
     return chance_of_adding_array_or_struct_type_;
   }
+  uint32_t GetChanceOfAddingCopyMemory() {
+    return chance_of_adding_copy_memory_;
+  }
   uint32_t GetChanceOfAddingDeadBlock() { return chance_of_adding_dead_block_; }
   uint32_t GetChanceOfAddingDeadBreak() { return chance_of_adding_dead_break_; }
   uint32_t GetChanceOfAddingDeadContinue() {
@@ -122,6 +129,9 @@
   uint32_t GetChanceOfAddingGlobalVariable() {
     return chance_of_adding_global_variable_;
   }
+  uint32_t GetChanceOfAddingImageSampleUnusedComponents() {
+    return chance_of_adding_image_sample_unused_components_;
+  }
   uint32_t GetChanceOfAddingLoad() { return chance_of_adding_load_; }
   uint32_t GetChanceOfAddingLocalVariable() {
     return chance_of_adding_local_variable_;
@@ -132,7 +142,15 @@
   uint32_t GetChanceOfAddingNoContractionDecoration() {
     return chance_of_adding_no_contraction_decoration_;
   }
+  uint32_t GetChanceOfAddingParameters() { return chance_of_adding_parameters; }
+  uint32_t GetChanceOfAddingRelaxedDecoration() {
+    return chance_of_adding_relaxed_decoration_;
+  }
   uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
+  uint32_t GetChanceOfAddingSynonyms() { return chance_of_adding_synonyms_; }
+  uint32_t GetChanceOfAddingVectorShuffle() {
+    return chance_of_adding_vector_shuffle_;
+  }
   uint32_t GetChanceOfAddingVectorType() {
     return chance_of_adding_vector_type_;
   }
@@ -155,6 +173,9 @@
   uint32_t GetChanceOfChoosingStructTypeVsArrayType() {
     return chance_of_choosing_struct_type_vs_array_type_;
   }
+  uint32_t GetChanceOfChoosingWorkgroupStorageClass() {
+    return chance_of_choosing_workgroup_storage_class_;
+  }
   uint32_t GetChanceOfConstructingComposite() {
     return chance_of_constructing_composite_;
   }
@@ -165,6 +186,12 @@
   uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() {
     return chance_of_going_deeper_when_making_access_chain_;
   }
+  uint32_t GetChanceOfInterchangingZeroLikeConstants() {
+    return chance_of_interchanging_zero_like_constants_;
+  }
+  uint32_t GetChanceOfInvertingComparisonOperators() {
+    return chance_of_inverting_comparison_operators_;
+  }
   uint32_t ChanceOfMakingDonorLivesafe() {
     return chance_of_making_donor_livesafe_;
   }
@@ -179,10 +206,37 @@
   uint32_t GetChanceOfPermutingParameters() {
     return chance_of_permuting_parameters_;
   }
+  uint32_t GetChanceOfPermutingPhiOperands() {
+    return chance_of_permuting_phi_operands_;
+  }
+  uint32_t GetChanceOfPushingIdThroughVariable() {
+    return chance_of_pushing_id_through_variable_;
+  }
+  uint32_t GetChanceOfReplacingCopyMemoryWithLoadStore() {
+    return chance_of_replacing_copy_memory_with_load_store_;
+  }
+  uint32_t GetChanceOfReplacingCopyObjectWithStoreLoad() {
+    return chance_of_replacing_copyobject_with_store_load_;
+  }
   uint32_t GetChanceOfReplacingIdWithSynonym() {
     return chance_of_replacing_id_with_synonym_;
   }
+  uint32_t GetChanceOfReplacingLinearAlgebraInstructions() {
+    return chance_of_replacing_linear_algebra_instructions_;
+  }
+  uint32_t GetChanceOfReplacingLoadStoreWithCopyMemory() {
+    return chance_of_replacing_load_store_with_copy_memory_;
+  }
+  uint32_t GetChanceOfReplacingParametersWithGlobals() {
+    return chance_of_replacing_parameters_with_globals_;
+  }
+  uint32_t GetChanceOfReplacingParametersWithStruct() {
+    return chance_of_replacing_parameters_with_struct_;
+  }
   uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
+  uint32_t GetChanceOfSwappingConditionalBranchOperands() {
+    return chance_of_swapping_conditional_branch_operands_;
+  }
   uint32_t GetChanceOfTogglingAccessChainInstruction() {
     return chance_of_toggling_access_chain_instruction_;
   }
@@ -192,17 +246,11 @@
   uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() {
     return max_equivalence_class_size_for_data_synonym_fact_closure_;
   }
-  uint32_t GetRandomIndexForAccessChain(uint32_t composite_size_bound) {
-    return random_generator_->RandomUint32(composite_size_bound);
+  uint32_t GetMaximumNumberOfFunctionParameters() {
+    return max_number_of_function_parameters_;
   }
-  uint32_t GetRandomLoopControlPartialCount() {
-    return random_generator_->RandomUint32(max_loop_control_partial_count_);
-  }
-  uint32_t GetRandomLoopControlPeelCount() {
-    return random_generator_->RandomUint32(max_loop_control_peel_count_);
-  }
-  uint32_t GetRandomLoopLimit() {
-    return random_generator_->RandomUint32(max_loop_limit_);
+  uint32_t GetMaximumNumberOfParametersReplacedWithStruct() {
+    return max_number_of_parameters_replaced_with_struct_;
   }
   std::pair<uint32_t, uint32_t> GetRandomBranchWeights() {
     std::pair<uint32_t, uint32_t> branch_weights = {0, 0};
@@ -216,10 +264,51 @@
 
     return branch_weights;
   }
+  std::vector<uint32_t> GetRandomComponentsForVectorShuffle(
+      uint32_t max_component_index) {
+    // Component count must be in range [2, 4].
+    std::vector<uint32_t> components(random_generator_->RandomUint32(2) + 2);
+
+    for (uint32_t& component : components) {
+      component = random_generator_->RandomUint32(max_component_index);
+    }
+
+    return components;
+  }
+  uint32_t GetRandomIndexForAccessChain(uint32_t composite_size_bound) {
+    return random_generator_->RandomUint32(composite_size_bound);
+  }
+  uint32_t GetRandomLoopControlPartialCount() {
+    return random_generator_->RandomUint32(max_loop_control_partial_count_);
+  }
+  uint32_t GetRandomLoopControlPeelCount() {
+    return random_generator_->RandomUint32(max_loop_control_peel_count_);
+  }
+  uint32_t GetRandomLoopLimit() {
+    return random_generator_->RandomUint32(max_loop_limit_);
+  }
+  uint32_t GetRandomNumberOfNewParameters(uint32_t num_of_params) {
+    assert(num_of_params < GetMaximumNumberOfFunctionParameters());
+    return ChooseBetweenMinAndMax(
+        {1, std::min(max_number_of_new_parameters_,
+                     GetMaximumNumberOfFunctionParameters() - num_of_params)});
+  }
+  uint32_t GetRandomNumberOfParametersReplacedWithStruct(uint32_t num_params) {
+    assert(num_params != 0 && "A function must have parameters to replace");
+    return ChooseBetweenMinAndMax(
+        {1, std::min(num_params,
+                     GetMaximumNumberOfParametersReplacedWithStruct())});
+  }
   uint32_t GetRandomSizeForNewArray() {
     // Ensure that the array size is non-zero.
     return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
   }
+  protobufs::TransformationAddSynonym::SynonymType GetRandomSynonymType();
+  uint32_t GetRandomUnusedComponentCountForImageSample(
+      uint32_t max_unused_component_count) {
+    // Ensure that the number of unused components is non-zero.
+    return random_generator_->RandomUint32(max_unused_component_count) + 1;
+  }
   bool GoDeeperInConstantObfuscation(uint32_t depth) {
     return go_deeper_in_constant_obfuscation_(depth, random_generator_);
   }
@@ -235,16 +324,22 @@
   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_copy_memory_;
   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_equation_instruction_;
   uint32_t chance_of_adding_global_variable_;
+  uint32_t chance_of_adding_image_sample_unused_components_;
   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_parameters;
+  uint32_t chance_of_adding_relaxed_decoration_;
   uint32_t chance_of_adding_store_;
+  uint32_t chance_of_adding_synonyms_;
+  uint32_t chance_of_adding_vector_shuffle_;
   uint32_t chance_of_adding_vector_type_;
   uint32_t chance_of_adjusting_branch_weights_;
   uint32_t chance_of_adjusting_function_control_;
@@ -253,18 +348,30 @@
   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_choosing_workgroup_storage_class_;
   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_interchanging_zero_like_constants_;
+  uint32_t chance_of_inverting_comparison_operators_;
   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_;
   uint32_t chance_of_outlining_function_;
   uint32_t chance_of_permuting_parameters_;
+  uint32_t chance_of_permuting_phi_operands_;
+  uint32_t chance_of_pushing_id_through_variable_;
+  uint32_t chance_of_replacing_copy_memory_with_load_store_;
+  uint32_t chance_of_replacing_copyobject_with_store_load_;
   uint32_t chance_of_replacing_id_with_synonym_;
+  uint32_t chance_of_replacing_linear_algebra_instructions_;
+  uint32_t chance_of_replacing_load_store_with_copy_memory_;
+  uint32_t chance_of_replacing_parameters_with_globals_;
+  uint32_t chance_of_replacing_parameters_with_struct_;
   uint32_t chance_of_splitting_block_;
+  uint32_t chance_of_swapping_conditional_branch_operands_;
   uint32_t chance_of_toggling_access_chain_instruction_;
 
   // Limits associated with various quantities for which random values are
@@ -275,6 +382,9 @@
   uint32_t max_loop_control_peel_count_;
   uint32_t max_loop_limit_;
   uint32_t max_new_array_size_limit_;
+  uint32_t max_number_of_function_parameters_;
+  uint32_t max_number_of_new_parameters_;
+  uint32_t max_number_of_parameters_replaced_with_struct_;
 
   // 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 cd94e4e..ebd88d0 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
@@ -20,6 +20,7 @@
 #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_null.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"
@@ -28,6 +29,7 @@
 #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 {
@@ -151,9 +153,7 @@
 }
 
 uint32_t FuzzerPass::FindOrCreateBoolType() {
-  opt::analysis::Bool bool_type;
-  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&bool_type);
-  if (existing_id) {
+  if (auto existing_id = fuzzerutil::MaybeGetBoolType(GetIRContext())) {
     return existing_id;
   }
   auto result = GetFuzzerContext()->GetFreshId();
@@ -161,25 +161,25 @@
   return result;
 }
 
-uint32_t FuzzerPass::FindOrCreate32BitIntegerType(bool is_signed) {
-  opt::analysis::Integer int_type(32, is_signed);
+uint32_t FuzzerPass::FindOrCreateIntegerType(uint32_t width, bool is_signed) {
+  opt::analysis::Integer int_type(width, 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));
+  ApplyTransformation(TransformationAddTypeInt(result, width, is_signed));
   return result;
 }
 
-uint32_t FuzzerPass::FindOrCreate32BitFloatType() {
-  opt::analysis::Float float_type(32);
+uint32_t FuzzerPass::FindOrCreateFloatType(uint32_t width) {
+  opt::analysis::Float float_type(width);
   auto existing_id = GetIRContext()->get_type_mgr()->GetId(&float_type);
   if (existing_id) {
     return existing_id;
   }
   auto result = GetFuzzerContext()->GetFreshId();
-  ApplyTransformation(TransformationAddTypeFloat(result, 32));
+  ApplyTransformation(TransformationAddTypeFloat(result, width));
   return result;
 }
 
@@ -228,7 +228,7 @@
   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);
+      FindOrCreateVectorType(FindOrCreateFloatType(32), row_count);
   opt::analysis::Type* column_type =
       GetIRContext()->get_type_mgr()->GetType(column_type_id);
   opt::analysis::Matrix matrix_type(column_type, column_count);
@@ -242,6 +242,17 @@
   return result;
 }
 
+uint32_t FuzzerPass::FindOrCreateStructType(
+    const std::vector<uint32_t>& component_type_ids) {
+  if (auto existing_id =
+          fuzzerutil::MaybeGetStructType(GetIRContext(), component_type_ids)) {
+    return existing_id;
+  }
+  auto new_id = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddTypeStruct(new_id, component_type_ids));
+  return new_id;
+}
+
 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
@@ -257,70 +268,59 @@
   return result;
 }
 
-uint32_t FuzzerPass::FindOrCreatePointerTo32BitIntegerType(
-    bool is_signed, SpvStorageClass storage_class) {
-  return FindOrCreatePointerType(FindOrCreate32BitIntegerType(is_signed),
+uint32_t FuzzerPass::FindOrCreatePointerToIntegerType(
+    uint32_t width, bool is_signed, SpvStorageClass storage_class) {
+  return FindOrCreatePointerType(FindOrCreateIntegerType(width, 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();
+uint32_t FuzzerPass::FindOrCreateIntegerConstant(
+    const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
+    bool is_irrelevant) {
+  auto int_type_id = FindOrCreateIntegerType(width, is_signed);
+  if (auto constant_id = fuzzerutil::MaybeGetScalarConstant(
+          GetIRContext(), *GetTransformationContext(), words, int_type_id,
+          is_irrelevant)) {
+    return constant_id;
   }
   auto result = GetFuzzerContext()->GetFreshId();
-  ApplyTransformation(
-      TransformationAddConstantScalar(result, uint32_type_id, {word}));
+  ApplyTransformation(TransformationAddConstantScalar(result, int_type_id,
+                                                      words, is_irrelevant));
   return result;
 }
 
-uint32_t FuzzerPass::FindOrCreate32BitFloatConstant(uint32_t word) {
-  auto float_type_id = FindOrCreate32BitFloatType();
+uint32_t FuzzerPass::FindOrCreateFloatConstant(
+    const std::vector<uint32_t>& words, uint32_t width, bool is_irrelevant) {
+  auto float_type_id = FindOrCreateFloatType(width);
   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();
+      GetIRContext()->get_type_mgr()->GetType(float_type_id)->AsFloat(), words);
+  if (auto constant_id = fuzzerutil::MaybeGetScalarConstant(
+          GetIRContext(), *GetTransformationContext(), words, float_type_id,
+          is_irrelevant)) {
+    return constant_id;
   }
   auto result = GetFuzzerContext()->GetFreshId();
-  ApplyTransformation(
-      TransformationAddConstantScalar(result, float_type_id, {word}));
+  ApplyTransformation(TransformationAddConstantScalar(result, float_type_id,
+                                                      words, is_irrelevant));
   return result;
 }
 
-uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value) {
+uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value, bool is_irrelevant) {
   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();
+  if (auto constant_id = fuzzerutil::MaybeGetScalarConstant(
+          GetIRContext(), *GetTransformationContext(), {value ? 1u : 0u},
+          bool_type_id, is_irrelevant)) {
+    return constant_id;
   }
   auto result = GetFuzzerContext()->GetFreshId();
-  ApplyTransformation(TransformationAddConstantBoolean(result, value));
+  ApplyTransformation(
+      TransformationAddConstantBoolean(result, value, is_irrelevant));
   return result;
 }
 
 uint32_t FuzzerPass::FindOrCreateConstant(const std::vector<uint32_t>& words,
-                                          uint32_t type_id) {
+                                          uint32_t type_id,
+                                          bool is_irrelevant) {
   assert(type_id && "Constant's type id can't be 0.");
 
   const auto* type = GetIRContext()->get_type_mgr()->GetType(type_id);
@@ -328,18 +328,12 @@
 
   if (type->AsBool()) {
     assert(words.size() == 1);
-    return FindOrCreateBoolConstant(words[0]);
+    return FindOrCreateBoolConstant(words[0], is_irrelevant);
   } else if (const auto* integer = type->AsInteger()) {
-    assert(integer->width() == 32 && words.size() == 1 &&
-           "Integer must have 32-bit width");
-    return FindOrCreate32BitIntegerConstant(words[0], integer->IsSigned());
+    return FindOrCreateIntegerConstant(words, integer->width(),
+                                       integer->IsSigned(), is_irrelevant);
   } else if (const auto* floating = type->AsFloat()) {
-    // Assertions are not evaluated in release builds so |floating|
-    // variable will be unused.
-    (void)floating;
-    assert(floating->width() == 32 && words.size() == 1 &&
-           "Floating point number must have 32-bit width");
-    return FindOrCreate32BitFloatConstant(words[0]);
+    return FindOrCreateFloatConstant(words, floating->width(), is_irrelevant);
   }
 
   // This assertion will fail in debug build but not in release build
@@ -348,6 +342,20 @@
   return 0;
 }
 
+uint32_t FuzzerPass::FindOrCreateCompositeConstant(
+    const std::vector<uint32_t>& component_ids, uint32_t type_id,
+    bool is_irrelevant) {
+  if (auto existing_constant = fuzzerutil::MaybeGetCompositeConstant(
+          GetIRContext(), *GetTransformationContext(), component_ids, type_id,
+          is_irrelevant)) {
+    return existing_constant;
+  }
+  uint32_t result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddConstantComposite(
+      result, type_id, component_ids, is_irrelevant));
+  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) {
@@ -359,6 +367,27 @@
   return result;
 }
 
+uint32_t FuzzerPass::FindOrCreateNullConstant(uint32_t type_id) {
+  // Find existing declaration
+  opt::analysis::NullConstant null_constant(
+      GetIRContext()->get_type_mgr()->GetType(type_id));
+  auto existing_constant =
+      GetIRContext()->get_constant_mgr()->FindConstant(&null_constant);
+
+  // Return if found
+  if (existing_constant) {
+    return GetIRContext()
+        ->get_constant_mgr()
+        ->GetDefiningInstruction(existing_constant)
+        ->result_id();
+  }
+
+  // Create new if not found
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddConstantNull(result, type_id));
+  return result;
+}
+
 std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
 FuzzerPass::GetAvailableBasicTypesAndPointers(
     SpvStorageClass storage_class) const {
@@ -429,43 +458,55 @@
 }
 
 uint32_t FuzzerPass::FindOrCreateZeroConstant(
-    uint32_t scalar_or_composite_type_id) {
+    uint32_t scalar_or_composite_type_id, bool is_irrelevant) {
   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);
+      return FindOrCreateBoolConstant(false, is_irrelevant);
+    case SpvOpTypeFloat: {
+      auto width = type_instruction->GetSingleWordInOperand(0);
+      auto num_words = (width + 32 - 1) / 32;
+      return FindOrCreateFloatConstant(std::vector<uint32_t>(num_words, 0),
+                                       width, is_irrelevant);
+    }
+    case SpvOpTypeInt: {
+      auto width = type_instruction->GetSingleWordInOperand(0);
+      auto num_words = (width + 32 - 1) / 32;
+      return FindOrCreateIntegerConstant(
+          std::vector<uint32_t>(num_words, 0), width,
+          type_instruction->GetSingleWordInOperand(1), is_irrelevant);
+    }
     case SpvOpTypeArray: {
-      return GetZeroConstantForHomogeneousComposite(
-          *type_instruction, type_instruction->GetSingleWordInOperand(0),
-          fuzzerutil::GetArraySize(*type_instruction, GetIRContext()));
+      auto component_type_id = type_instruction->GetSingleWordInOperand(0);
+      auto num_components =
+          fuzzerutil::GetArraySize(*type_instruction, GetIRContext());
+      return FindOrCreateCompositeConstant(
+          std::vector<uint32_t>(
+              num_components,
+              FindOrCreateZeroConstant(component_type_id, is_irrelevant)),
+          scalar_or_composite_type_id, is_irrelevant);
     }
     case SpvOpTypeMatrix:
     case SpvOpTypeVector: {
-      return GetZeroConstantForHomogeneousComposite(
-          *type_instruction, type_instruction->GetSingleWordInOperand(0),
-          type_instruction->GetSingleWordInOperand(1));
+      auto component_type_id = type_instruction->GetSingleWordInOperand(0);
+      auto num_components = type_instruction->GetSingleWordInOperand(1);
+      return FindOrCreateCompositeConstant(
+          std::vector<uint32_t>(
+              num_components,
+              FindOrCreateZeroConstant(component_type_id, is_irrelevant)),
+          scalar_or_composite_type_id, is_irrelevant);
     }
     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));
+        field_zero_ids.push_back(FindOrCreateZeroConstant(
+            type_instruction->GetSingleWordInOperand(index), is_irrelevant));
       }
       return FindOrCreateCompositeConstant(
-          *type_instruction, field_zero_constants, field_zero_ids);
+          field_zero_ids, scalar_or_composite_type_id, is_irrelevant);
     }
     default:
       assert(false && "Unknown type.");
@@ -473,62 +514,5 @@
   }
 }
 
-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 800b888..4731b74 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
@@ -103,18 +103,33 @@
     *GetTransformations()->add_transformation() = transformation.ToMessage();
   }
 
+  // A generic helper for applying a transformation only if it is applicable.
+  // If it is applicable, the transformation is applied and then added to the
+  // sequence of applied transformations and the function returns true.
+  // Otherwise, the function returns false.
+  bool MaybeApplyTransformation(const Transformation& transformation) {
+    if (transformation.IsApplicable(GetIRContext(),
+                                    *GetTransformationContext())) {
+      transformation.Apply(GetIRContext(), GetTransformationContext());
+      *GetTransformations()->add_transformation() = transformation.ToMessage();
+      return true;
+    }
+    return false;
+  }
+
   // 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 OpTypeInt instruction, with width and signedness
+  // specified by |width| and |is_signed|, respectively.  If such an instruction
+  // does not exist, a transformation is applied to add it.
+  uint32_t FindOrCreateIntegerType(uint32_t width, 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 OpTypeFloat instruction, with width specified by
+  // |width|.  If such an instruction does not exist, a transformation is
+  // applied to add it.
+  uint32_t FindOrCreateFloatType(uint32_t width);
 
   // Returns the id of an OpTypeFunction %<return_type_id> %<...argument_id>
   // instruction. If such an instruction doesn't exist, a transformation
@@ -135,48 +150,83 @@
   // 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 an OpTypeStruct instruction with |component_type_ids| as
+  // type ids for struct's components. If no such a struct type exists,
+  // transformations are applied to add it. |component_type_ids| may not contain
+  // a result id of an OpTypeFunction.
+  uint32_t FindOrCreateStructType(
+      const std::vector<uint32_t>& component_type_ids);
+
   // 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 OpTypePointer instruction, with a integer base
+  // type of width and signedness specified by |width| and |is_signed|,
+  // respectively.  If the pointer type or required integer base type do not
+  // exist, transformations are applied to add them.
+  uint32_t FindOrCreatePointerToIntegerType(uint32_t width, bool is_signed,
+                                            SpvStorageClass storage_class);
 
-  // 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 a integer type of
+  // width and signedness specified by |width| and |is_signed|, respectively,
+  // with |words| as its value.  If either the required integer type or the
+  // constant do not exist, transformations are applied to add them.
+  // The returned id either participates in IdIsIrrelevant fact or not,
+  // depending on the |is_irrelevant| parameter.
+  uint32_t FindOrCreateIntegerConstant(const std::vector<uint32_t>& words,
+                                       uint32_t width, bool is_signed,
+                                       bool is_irrelevant);
 
-  // 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 OpConstant instruction, with a floating-point
+  // type of width specified by |width|, with |words| as its value.  If either
+  // the required floating-point type or the constant do not exist,
+  // transformations are applied to add them. The returned id either
+  // participates in IdIsIrrelevant fact or not, depending on the
+  // |is_irrelevant| parameter.
+  uint32_t FindOrCreateFloatConstant(const std::vector<uint32_t>& words,
+                                     uint32_t width, bool is_irrelevant);
 
   // 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);
+  // The returned id either participates in IdIsIrrelevant fact or not,
+  // depending on the |is_irrelevant| parameter.
+  uint32_t FindOrCreateBoolConstant(bool value, bool is_irrelevant);
 
   // Returns the id of an OpConstant instruction of type with |type_id|
   // that consists of |words|. If that instruction doesn't exist,
   // transformations are applied to add it. |type_id| must be a valid
   // result id of either scalar or boolean OpType* instruction that exists
-  // in the module.
+  // in the module. The returned id either participates in IdIsIrrelevant fact
+  // or not, depending on the |is_irrelevant| parameter.
   uint32_t FindOrCreateConstant(const std::vector<uint32_t>& words,
-                                uint32_t type_id);
+                                uint32_t type_id, bool is_irrelevant);
+
+  // Returns the id of an OpConstantComposite instruction of type with |type_id|
+  // that consists of |component_ids|. If that instruction doesn't exist,
+  // transformations are applied to add it. |type_id| must be a valid
+  // result id of an OpType* instruction that represents a composite type
+  // (i.e. a vector, matrix, struct or array).
+  // The returned id either participates in IdIsIrrelevant fact or not,
+  // depending on the |is_irrelevant| parameter.
+  uint32_t FindOrCreateCompositeConstant(
+      const std::vector<uint32_t>& component_ids, uint32_t type_id,
+      bool is_irrelevant);
 
   // 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);
 
+  // Returns the id of an OpNullConstant instruction of type |type_id|. If
+  // that instruction doesn't exist, it is added through a transformation.
+  // |type_id| must be a valid result id of an OpType* instruction that exists
+  // in the module.
+  uint32_t FindOrCreateNullConstant(uint32_t type_id);
+
   // Define a *basic type* to be an integer, boolean or floating-point type,
   // or a matrix, vector, struct or fixed-size array built from basic types.  In
   // particular, a basic type cannot contain an opaque type (such as an image),
@@ -197,7 +247,8 @@
   // 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.
+  // add it. The returned id either participates in IdIsIrrelevant fact or not,
+  // depending on the |is_irrelevant| parameter.
   //
   // Examples:
   // --------------+-------------------------------
@@ -219,31 +270,10 @@
   //     uint2 u;  |
   //   }           |
   // --------------+-------------------------------
-  uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id);
+  uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id,
+                                    bool is_irrelevant);
 
  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_;
   TransformationContext* transformation_context_;
   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
index b9c1eed..11155f2 100644
--- 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
@@ -92,6 +92,11 @@
             relevant_pointer_instructions[GetFuzzerContext()->RandomIndex(
                 relevant_pointer_instructions)];
         std::vector<uint32_t> index_ids;
+
+        // Each index accessing a non-struct composite will be clamped, thus
+        // needing a pair of fresh ids
+        std::vector<std::pair<uint32_t, uint32_t>> fresh_ids_for_clamping;
+
         auto pointer_type = GetIRContext()->get_def_use_mgr()->GetDef(
             chosen_pointer->type_id());
         uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
@@ -132,20 +137,37 @@
             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:
+            case SpvOpTypeVector: {
+              // The index will be clamped
+
+              bool is_signed = GetFuzzerContext()->ChooseEven();
+
+              // Make the constant ready for clamping. We need:
+              // - an OpTypeBool to be present in the module
+              // - an OpConstant with the same type as the index and value
+              //   the maximum value for an index
+              // - a new pair of fresh ids for the clamping instructions
+              FindOrCreateBoolType();
+              FindOrCreateIntegerConstant({bound - 1}, 32, is_signed, false);
+              std::pair<uint32_t, uint32_t> fresh_pair_of_ids = {
+                  GetFuzzerContext()->GetFreshId(),
+                  GetFuzzerContext()->GetFreshId()};
+              fresh_ids_for_clamping.emplace_back(fresh_pair_of_ids);
+
+              index_ids.push_back(FindOrCreateIntegerConstant(
+                  {index_value}, 32, is_signed, false));
               subobject_type_id = subobject_type->GetSingleWordInOperand(0);
-              break;
+
+            } break;
             case SpvOpTypeStruct:
+              index_ids.push_back(FindOrCreateIntegerConstant(
+                  {index_value}, 32, GetFuzzerContext()->ChooseEven(), false));
               subobject_type_id =
                   subobject_type->GetSingleWordInOperand(index_value);
               break;
@@ -162,7 +184,7 @@
         // Apply the transformation to add an access chain.
         ApplyTransformation(TransformationAccessChain(
             GetFuzzerContext()->GetFreshId(), chosen_pointer->result_id(),
-            index_ids, instruction_descriptor));
+            index_ids, instruction_descriptor, fresh_ids_for_clamping));
       });
 }
 
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
index 9b0dda8..653d784 100644
--- 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
@@ -53,13 +53,13 @@
     return FindOrCreateBoolType();
   };
   std::function<uint32_t()> float_type_supplier = [this]() -> uint32_t {
-    return FindOrCreate32BitFloatType();
+    return FindOrCreateFloatType(32);
   };
   std::function<uint32_t()> int_type_supplier = [this]() -> uint32_t {
-    return FindOrCreate32BitIntegerType(true);
+    return FindOrCreateIntegerType(32, true);
   };
   std::function<uint32_t()> uint_type_supplier = [this]() -> uint32_t {
-    return FindOrCreate32BitIntegerType(false);
+    return FindOrCreateIntegerType(32, false);
   };
 
   // Consider each of the base types with which we can make vectors.
@@ -96,8 +96,8 @@
 void FuzzerPassAddCompositeTypes::AddNewArrayType() {
   ApplyTransformation(TransformationAddTypeArray(
       GetFuzzerContext()->GetFreshId(), ChooseScalarOrCompositeType(),
-      FindOrCreate32BitIntegerConstant(
-          GetFuzzerContext()->GetRandomSizeForNewArray(), false)));
+      FindOrCreateIntegerConstant(
+          {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false, false)));
 }
 
 void FuzzerPassAddCompositeTypes::AddNewStructType() {
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp
new file mode 100644
index 0000000..d98619c
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp
@@ -0,0 +1,82 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_copy_memory.h"
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_add_copy_memory.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddCopyMemory::FuzzerPassAddCopyMemory(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassAddCopyMemory::~FuzzerPassAddCopyMemory() = default;
+
+void FuzzerPassAddCopyMemory::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor) {
+        // Check that we can insert an OpCopyMemory before this instruction.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyMemory,
+                                                          inst_it)) {
+          return;
+        }
+
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAddingCopyMemory())) {
+          return;
+        }
+
+        // Get all instructions available before |inst_it| according to the
+        // domination rules.
+        auto instructions = FindAvailableInstructions(
+            function, block, inst_it,
+            TransformationAddCopyMemory::IsInstructionSupported);
+
+        if (instructions.empty()) {
+          return;
+        }
+
+        const auto* inst =
+            instructions[GetFuzzerContext()->RandomIndex(instructions)];
+
+        // Decide whether to create global or local variable.
+        auto storage_class = GetFuzzerContext()->ChooseEven()
+                                 ? SpvStorageClassPrivate
+                                 : SpvStorageClassFunction;
+
+        auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
+            GetIRContext(), inst->type_id());
+
+        // Create a pointer type with |storage_class| if needed.
+        FindOrCreatePointerType(pointee_type_id, storage_class);
+
+        ApplyTransformation(TransformationAddCopyMemory(
+            instruction_descriptor, GetFuzzerContext()->GetFreshId(),
+            inst->result_id(), storage_class,
+            FindOrCreateZeroConstant(pointee_type_id, false)));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_copy_memory.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_copy_memory.h
new file mode 100644
index 0000000..321e4a1
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_copy_memory.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_COPY_MEMORY_INSTRUCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Randomly decides whether to add OpCopyMemory before some instruction in the
+// module.
+class FuzzerPassAddCopyMemory : public FuzzerPass {
+ public:
+  FuzzerPassAddCopyMemory(opt::IRContext* ir_context,
+                          TransformationContext* transformation_context,
+                          FuzzerContext* fuzzer_context,
+                          protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddCopyMemory() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_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
index 30a4145..84ed1fb 100644
--- 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
@@ -45,7 +45,7 @@
       // Make sure the module contains a boolean constant equal to
       // |condition_value|.
       bool condition_value = GetFuzzerContext()->ChooseEven();
-      FindOrCreateBoolConstant(condition_value);
+      FindOrCreateBoolConstant(condition_value, false);
 
       // We speculatively create a transformation, and then apply it (below) if
       // it turns out to be applicable.  This avoids duplicating the logic for
@@ -59,11 +59,7 @@
   }
   // Apply all those transformations that are in fact applicable.
   for (auto& transformation : candidate_transformations) {
-    if (transformation.IsApplicable(GetIRContext(),
-                                    *GetTransformationContext())) {
-      transformation.Apply(GetIRContext(), GetTransformationContext());
-      *GetTransformations()->add_transformation() = transformation.ToMessage();
-    }
+    MaybeApplyTransformation(transformation);
   }
 }
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
index f3900aa..cf4ecee 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
@@ -68,19 +68,16 @@
           merge_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) {
             // Add an additional operand for OpPhi instruction.
             //
-            // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
-            // If we have a way to communicate to the fact manager
-            // that a specific id use is irrelevant and could be replaced with
-            // something else, we should add such a fact about the zero
-            // provided as an OpPhi operand
-            phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id()));
+            // We mark the constant as irrelevant so that we can replace it with
+            // a more interesting value later.
+            phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id(), true));
           });
         }
 
         // Make sure the module has a required boolean constant to be used in
         // OpBranchConditional instruction.
         auto break_condition = GetFuzzerContext()->ChooseEven();
-        FindOrCreateBoolConstant(break_condition);
+        FindOrCreateBoolConstant(break_condition, false);
 
         auto candidate_transformation = TransformationAddDeadBreak(
             block.id(), merge_block->id(), break_condition, std::move(phi_ids));
@@ -114,12 +111,9 @@
     candidate_transformations.erase(candidate_transformations.begin() + index);
     // Probabilistically decide whether to try to apply it vs. ignore it, in the
     // case that it is applicable.
-    if (transformation.IsApplicable(GetIRContext(),
-                                    *GetTransformationContext()) &&
-        GetFuzzerContext()->ChoosePercentage(
+    if (GetFuzzerContext()->ChoosePercentage(
             GetFuzzerContext()->GetChanceOfAddingDeadBreak())) {
-      transformation.Apply(GetIRContext(), GetTransformationContext());
-      *GetTransformations()->add_transformation() = transformation.ToMessage();
+      MaybeApplyTransformation(transformation);
     }
   }
 }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp
index 56a7fd1..61a7c6d 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp
@@ -59,19 +59,16 @@
         continue_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) {
           // Add an additional operand for OpPhi instruction.
           //
-          // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
-          // If we have a way to communicate to the fact manager
-          // that a specific id use is irrelevant and could be replaced with
-          // something else, we should add such a fact about the zero
-          // provided as an OpPhi operand
-          phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id()));
+          // We mark the constant as irrelevant so that we can replace it with a
+          // more interesting value later.
+          phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id(), true));
         });
       }
 
       // Make sure the module contains a boolean constant equal to
       // |condition_value|.
       bool condition_value = GetFuzzerContext()->ChooseEven();
-      FindOrCreateBoolConstant(condition_value);
+      FindOrCreateBoolConstant(condition_value, false);
 
       // Make a transformation to add a dead continue from this node; if the
       // node turns out to be inappropriate (e.g. by not being in a loop) the
@@ -80,14 +77,9 @@
           block.id(), condition_value, std::move(phi_ids));
       // Probabilistically decide whether to apply the transformation in the
       // case that it is applicable.
-      if (candidate_transformation.IsApplicable(GetIRContext(),
-                                                *GetTransformationContext()) &&
-          GetFuzzerContext()->ChoosePercentage(
+      if (GetFuzzerContext()->ChoosePercentage(
               GetFuzzerContext()->GetChanceOfAddingDeadContinue())) {
-        candidate_transformation.Apply(GetIRContext(),
-                                       GetTransformationContext());
-        *GetTransformations()->add_transformation() =
-            candidate_transformation.ToMessage();
+        MaybeApplyTransformation(candidate_transformation);
       }
     }
   }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
index 49c4a8a..dc811e6 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
@@ -57,19 +57,116 @@
         std::vector<opt::Instruction*> available_instructions =
             FindAvailableInstructions(
                 function, block, inst_it,
-                [](opt::IRContext*, opt::Instruction* instruction) -> bool {
+                [this](opt::IRContext*, opt::Instruction* instruction) -> bool {
                   return instruction->result_id() && instruction->type_id() &&
-                         instruction->opcode() != SpvOpUndef;
+                         instruction->opcode() != SpvOpUndef &&
+                         !GetTransformationContext()
+                              ->GetFactManager()
+                              ->IdIsIrrelevant(instruction->result_id());
                 });
 
         // Try the opcodes for which we know how to make ids at random until
         // something works.
-        std::vector<SpvOp> candidate_opcodes = {SpvOpIAdd, SpvOpISub,
-                                                SpvOpLogicalNot, SpvOpSNegate};
+        std::vector<SpvOp> candidate_opcodes = {
+            SpvOpIAdd,        SpvOpISub,        SpvOpLogicalNot, SpvOpSNegate,
+            SpvOpConvertUToF, SpvOpConvertSToF, SpvOpBitcast};
         do {
           auto opcode =
               GetFuzzerContext()->RemoveAtRandomIndex(&candidate_opcodes);
           switch (opcode) {
+            case SpvOpConvertSToF:
+            case SpvOpConvertUToF: {
+              auto candidate_instructions =
+                  GetIntegerInstructions(available_instructions);
+
+              if (candidate_instructions.empty()) {
+                break;
+              }
+
+              const auto* operand =
+                  candidate_instructions[GetFuzzerContext()->RandomIndex(
+                      candidate_instructions)];
+
+              const auto* type =
+                  GetIRContext()->get_type_mgr()->GetType(operand->type_id());
+              assert(type && "Operand has invalid type");
+
+              // Make sure a result type exists in the module.
+              if (const auto* vector = type->AsVector()) {
+                FindOrCreateVectorType(
+                    FindOrCreateFloatType(
+                        vector->element_type()->AsInteger()->width()),
+                    vector->element_count());
+              } else {
+                FindOrCreateFloatType(type->AsInteger()->width());
+              }
+
+              ApplyTransformation(TransformationEquationInstruction(
+                  GetFuzzerContext()->GetFreshId(), opcode,
+                  {operand->result_id()}, instruction_descriptor));
+              return;
+            }
+            case SpvOpBitcast: {
+              std::vector<const opt::Instruction*> candidate_instructions;
+              for (const auto* inst : available_instructions) {
+                const auto* type =
+                    GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+                assert(type && "Instruction has invalid type");
+                if ((type->AsVector() &&
+                     (type->AsVector()->element_type()->AsInteger() ||
+                      type->AsVector()->element_type()->AsFloat())) ||
+                    type->AsInteger() || type->AsFloat()) {
+                  // We support OpBitcast for only scalars or vectors of
+                  // numerical type.
+                  candidate_instructions.push_back(inst);
+                }
+              }
+
+              if (!candidate_instructions.empty()) {
+                const auto* operand_inst =
+                    candidate_instructions[GetFuzzerContext()->RandomIndex(
+                        candidate_instructions)];
+                const auto* operand_type =
+                    GetIRContext()->get_type_mgr()->GetType(
+                        operand_inst->type_id());
+                assert(operand_type && "Operand instruction has invalid type");
+
+                // Make sure a result type exists in the module.
+                //
+                // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3539):
+                //  The only constraint on the types of OpBitcast's parameters
+                //  is that they must have the same number of bits. Consider
+                //  improving the code below to support this in full.
+                if (const auto* vector = operand_type->AsVector()) {
+                  uint32_t element_type_id;
+                  if (const auto* int_type =
+                          vector->element_type()->AsInteger()) {
+                    element_type_id = FindOrCreateFloatType(int_type->width());
+                  } else {
+                    assert(vector->element_type()->AsFloat() &&
+                           "Vector must have numerical elements");
+                    element_type_id = FindOrCreateIntegerType(
+                        vector->element_type()->AsFloat()->width(),
+                        GetFuzzerContext()->ChooseEven());
+                  }
+
+                  FindOrCreateVectorType(element_type_id,
+                                         vector->element_count());
+                } else if (const auto* int_type = operand_type->AsInteger()) {
+                  FindOrCreateFloatType(int_type->width());
+                } else {
+                  assert(operand_type->AsFloat() &&
+                         "Operand is not a scalar of numerical type");
+                  FindOrCreateIntegerType(operand_type->AsFloat()->width(),
+                                          GetFuzzerContext()->ChooseEven());
+                }
+
+                ApplyTransformation(TransformationEquationInstruction(
+                    GetFuzzerContext()->GetFreshId(), opcode,
+                    {operand_inst->result_id()}, instruction_descriptor));
+                return;
+              }
+            } break;
             case SpvOpIAdd:
             case SpvOpISub: {
               // Instructions of integer (scalar or vector) result type are
@@ -182,6 +279,20 @@
 }
 
 std::vector<opt::Instruction*>
+FuzzerPassAddEquationInstructions::GetFloatInstructions(
+    const std::vector<opt::Instruction*>& instructions) const {
+  std::vector<opt::Instruction*> result;
+  for (auto& inst : instructions) {
+    auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+    if (type->AsFloat() ||
+        (type->AsVector() && type->AsVector()->element_type()->AsFloat())) {
+      result.push_back(inst);
+    }
+  }
+  return result;
+}
+
+std::vector<opt::Instruction*>
 FuzzerPassAddEquationInstructions::GetBooleanInstructions(
     const std::vector<opt::Instruction*>& instructions) const {
   std::vector<opt::Instruction*> result;
@@ -225,10 +336,11 @@
     if (type->AsVector()) {
       type = type->AsVector()->element_type();
     }
-    assert(type->AsInteger() &&
+    assert((type->AsInteger() || type->AsFloat()) &&
            "Precondition: all input instructions must "
-           "have integer scalar or vector type.");
-    if (type->AsInteger()->width() == bit_width) {
+           "have integer or float scalar or vector type.");
+    if ((type->AsInteger() && type->AsInteger()->width() == bit_width) ||
+        (type->AsFloat() && type->AsFloat()->width() == bit_width)) {
       result.push_back(inst);
     }
   }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.h
index 6e64977..8328b6b 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.h
@@ -41,6 +41,11 @@
   std::vector<opt::Instruction*> GetIntegerInstructions(
       const std::vector<opt::Instruction*>& instructions) const;
 
+  // Returns only instructions, that have either a scalar floating-point or a
+  // vector type.
+  std::vector<opt::Instruction*> GetFloatInstructions(
+      const std::vector<opt::Instruction*>& instructions) const;
+
   // Yields those instructions in |instructions| that have boolean scalar or
   // vector result type.
   std::vector<opt::Instruction*> GetBooleanInstructions(
@@ -53,9 +58,9 @@
       const std::vector<opt::Instruction*>& instructions,
       uint32_t vector_width) const;
 
-  // Requires that |instructions| are integer scalars or vectors.  Returns only
-  // those instructions for which the bit-width of the underlying integer type
-  // is |bit_width|.
+  // Requires that |instructions| are integer or float scalars or vectors.
+  // Returns only those instructions for which the bit-width of the underlying
+  // integer or floating-point type is |bit_width|.
   std::vector<opt::Instruction*> RestrictToElementBitWidth(
       const std::vector<opt::Instruction*>& instructions,
       uint32_t bit_width) const;
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
index 569df10..b6f4c85 100644
--- 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
@@ -106,148 +106,92 @@
       });
 }
 
-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 (!GetTransformationContext()->GetFactManager()->BlockIsDead(
-                      block->id()) &&
-                  !GetTransformationContext()
-                       ->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.
-        auto storage_class = static_cast<SpvStorageClass>(
-            type_instruction->GetSingleWordInOperand(0));
-        if (storage_class == 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((storage_class == SpvStorageClassPrivate ||
-                  storage_class == SpvStorageClassWorkgroup) &&
-                 "Only Function, Private and Workgroup storage classes are "
-                 "supported at present.");
-          // Add a new global variable to the module, zero-initializing it if
-          // it has Private storage class, and noting that its pointee value is
-          // irrelevant.
-          ApplyTransformation(TransformationAddGlobalVariable(
-              fresh_variable_id, arg_type_id, storage_class,
-              storage_class == SpvStorageClassPrivate
-                  ? FindOrCreateZeroConstant(
-                        type_instruction->GetSingleWordInOperand(1))
-                  : 0,
-              true));
+  auto available_pointers = FindAvailableInstructions(
+      caller_function, caller_block, caller_inst_it,
+      [this, caller_block](opt::IRContext* /*unused*/, opt::Instruction* inst) {
+        if (inst->opcode() != SpvOpVariable ||
+            inst->opcode() != SpvOpFunctionParameter) {
+          // Function parameters and variables are the only
+          // kinds of pointer that can be used as actual
+          // parameters.
+          return false;
         }
-      } 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 GetTransformationContext()->GetFactManager()->BlockIsDead(
+                   caller_block->id()) ||
+               GetTransformationContext()
+                   ->GetFactManager()
+                   ->PointeeValueIsIrrelevant(inst->result_id());
+      });
+
+  std::unordered_map<uint32_t, std::vector<uint32_t>> type_id_to_result_id;
+  for (const auto* inst : available_pointers) {
+    type_id_to_result_id[inst->type_id()].push_back(inst->result_id());
+  }
+
+  std::vector<uint32_t> result;
+  for (const auto* param :
+       fuzzerutil::GetParameters(GetIRContext(), callee.result_id())) {
+    const auto* param_type =
+        GetIRContext()->get_type_mgr()->GetType(param->type_id());
+    assert(param_type && "Parameter has invalid type");
+
+    if (!param_type->AsPointer()) {
+      // We mark the constant as irrelevant so that we can replace it with a
+      // more interesting value later.
+      result.push_back(FindOrCreateZeroConstant(param->type_id(), true));
+      continue;
+    }
+
+    if (type_id_to_result_id.count(param->type_id())) {
+      // Use an existing pointer if there are any.
+      const auto& candidates = type_id_to_result_id[param->type_id()];
+      result.push_back(candidates[GetFuzzerContext()->RandomIndex(candidates)]);
+      continue;
+    }
+
+    // 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);
+    type_id_to_result_id[param->type_id()].push_back(fresh_variable_id);
+
+    // Now bring the variable into existence.
+    auto storage_class = param_type->AsPointer()->storage_class();
+    auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
+        GetIRContext(), param->type_id());
+    if (storage_class == SpvStorageClassFunction) {
+      // Add a new zero-initialized local variable to the current
+      // function, noting that its pointee value is irrelevant.
+      ApplyTransformation(TransformationAddLocalVariable(
+          fresh_variable_id, param->type_id(), caller_function->result_id(),
+          FindOrCreateZeroConstant(pointee_type_id, false), true));
+    } else {
+      assert((storage_class == SpvStorageClassPrivate ||
+              storage_class == SpvStorageClassWorkgroup) &&
+             "Only Function, Private and Workgroup storage classes are "
+             "supported at present.");
+      // Add a new global variable to the module, zero-initializing it if
+      // it has Private storage class, and noting that its pointee value is
+      // irrelevant.
+      ApplyTransformation(TransformationAddGlobalVariable(
+          fresh_variable_id, param->type_id(), storage_class,
+          storage_class == SpvStorageClassPrivate
+              ? FindOrCreateZeroConstant(pointee_type_id, false)
+              : 0,
+          true));
     }
   }
+
   return result;
 }
 
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
index 8f75e8c..4ed8791 100644
--- 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
@@ -34,14 +34,6 @@
   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
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
index 4023b22..9a45a37 100644
--- 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
@@ -30,8 +30,22 @@
 FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default;
 
 void FuzzerPassAddGlobalVariables::Apply() {
+  SpvStorageClass variable_storage_class = SpvStorageClassPrivate;
+  for (auto& entry_point : GetIRContext()->module()->entry_points()) {
+    // If the execution model of some entry point is GLCompute,
+    // then the variable storage class may be Workgroup.
+    if (entry_point.GetSingleWordInOperand(0) == SpvExecutionModelGLCompute) {
+      variable_storage_class =
+          GetFuzzerContext()->ChoosePercentage(
+              GetFuzzerContext()->GetChanceOfChoosingWorkgroupStorageClass())
+              ? SpvStorageClassWorkgroup
+              : SpvStorageClassPrivate;
+      break;
+    }
+  }
+
   auto basic_type_ids_and_pointers =
-      GetAvailableBasicTypesAndPointers(SpvStorageClassPrivate);
+      GetAvailableBasicTypesAndPointers(variable_storage_class);
 
   // These are the basic types that are available to this fuzzer pass.
   auto& basic_types = basic_type_ids_and_pointers.first;
@@ -59,18 +73,21 @@
       pointer_type_id = GetFuzzerContext()->GetFreshId();
       available_pointers_to_basic_type.push_back(pointer_type_id);
       ApplyTransformation(TransformationAddTypePointer(
-          pointer_type_id, SpvStorageClassPrivate, basic_type));
+          pointer_type_id, variable_storage_class, basic_type));
     } else {
       // There is - grab one.
       pointer_type_id =
           available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex(
               available_pointers_to_basic_type)];
     }
-    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3274):  We could
-    //  add new variables with Workgroup storage class in compute shaders.
+
     ApplyTransformation(TransformationAddGlobalVariable(
         GetFuzzerContext()->GetFreshId(), pointer_type_id,
-        SpvStorageClassPrivate, FindOrCreateZeroConstant(basic_type), true));
+        variable_storage_class,
+        variable_storage_class == SpvStorageClassPrivate
+            ? FindOrCreateZeroConstant(basic_type, false)
+            : 0,
+        true));
   }
 }
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp
new file mode 100644
index 0000000..313bb0d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp
@@ -0,0 +1,200 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_image_sample_unused_components.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_add_image_sample_unused_components.h"
+#include "source/fuzz/transformation_composite_construct.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddImageSampleUnusedComponents::
+    FuzzerPassAddImageSampleUnusedComponents(
+        opt::IRContext* ir_context,
+        TransformationContext* transformation_context,
+        FuzzerContext* fuzzer_context,
+        protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassAddImageSampleUnusedComponents::
+    ~FuzzerPassAddImageSampleUnusedComponents() = default;
+
+void FuzzerPassAddImageSampleUnusedComponents::Apply() {
+  // SPIR-V module to help understand the transformation.
+  //
+  //       OpCapability Shader
+  //  %1 = OpExtInstImport "GLSL.std.450"
+  //       OpMemoryModel Logical GLSL450
+  //       OpEntryPoint Fragment %15 "main" %12 %14
+  //       OpExecutionMode %15 OriginUpperLeft
+  //
+  // ; Decorations
+  //        OpDecorate %12 Location 0 ; Input color variable location
+  //        OpDecorate %13 DescriptorSet 0 ; Image coordinate variable
+  //        descriptor set OpDecorate %13 Binding 0 ; Image coordinate
+  //        variable binding OpDecorate %14 Location 0 ; Fragment color
+  //        variable location
+  //
+  // ; Types
+  //  %2 = OpTypeVoid
+  //  %3 = OpTypeFunction %2
+  //  %4 = OpTypeFloat 32
+  //  %5 = OpTypeVector %4 2
+  //  %6 = OpTypeVector %4 4
+  //  %7 = OpTypeImage %4 2D 0 0 0 1 Rgba32f
+  //  %8 = OpTypeSampledImage %7
+  //  %9 = OpTypePointer Input %5
+  // %10 = OpTypePointer UniformConstant %8
+  // %11 = OpTypePointer Output %6
+  //
+  // ; Variables
+  // %12 = OpVariable %9 Input ; Input image coordinate variable
+  // %13 = OpVariable %10 UniformConstant ; Image variable
+  // %14 = OpVariable %11 Output ; Fragment color variable
+  //
+  // ; main function
+  // %15 = OpFunction %2 None %3
+  // %16 = OpLabel
+  // %17 = OpLoad %5 %12
+  // %18 = OpLoad %8 %13
+  // %19 = OpImageSampleImplicitLod %6 %18 %17
+  //       OpStore %14 %19
+  //       OpReturn
+  //       OpFunctionEnd
+
+  GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
+    // |instruction| %19 = OpImageSampleImplicitLod %6 %18 %17
+    if (!spvOpcodeIsImageSample(instruction->opcode())) {
+      return;
+    }
+
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()
+                ->GetChanceOfAddingImageSampleUnusedComponents())) {
+      return;
+    }
+
+    // Gets image sample coordinate information.
+    // |coordinate_instruction| %17 = OpLoad %5 %12
+    uint32_t coordinate_id = instruction->GetSingleWordInOperand(1);
+    auto coordinate_instruction =
+        GetIRContext()->get_def_use_mgr()->GetDef(coordinate_id);
+    auto coordinate_type = GetIRContext()->get_type_mgr()->GetType(
+        coordinate_instruction->type_id());
+
+    // If the coordinate is a 4-dimensional vector, then no unused components
+    // may be added.
+    if (coordinate_type->AsVector() &&
+        coordinate_type->AsVector()->element_count() == 4) {
+      return;
+    }
+
+    // If the coordinate is a scalar, then at most 3 unused components may be
+    // added. If the coordinate is a vector, then the maximum number of unused
+    // components depends on the vector size.
+    // For the sample module, the coordinate type instruction is %5 =
+    // OpTypeVector %4 2, thus |max_unused_component_count| = 4 - 2 = 2.
+    uint32_t max_unused_component_count =
+        coordinate_type->AsInteger() || coordinate_type->AsFloat()
+            ? 3
+            : 4 - coordinate_type->AsVector()->element_count();
+
+    // |unused_component_count| may be 1 or 2.
+    uint32_t unused_component_count =
+        GetFuzzerContext()->GetRandomUnusedComponentCountForImageSample(
+            max_unused_component_count);
+
+    // Gets a type for the zero-unused components.
+    uint32_t zero_constant_type_id;
+    switch (unused_component_count) {
+      case 1:
+        // If the coordinate is an integer or float, then the unused components
+        // type is the same as the coordinate. If the coordinate is a vector,
+        // then the unused components type is the same as the vector components
+        // type.
+        zero_constant_type_id =
+            coordinate_type->AsInteger() || coordinate_type->AsFloat()
+                ? coordinate_instruction->type_id()
+                : GetIRContext()->get_type_mgr()->GetId(
+                      coordinate_type->AsVector()->element_type());
+        break;
+      case 2:
+      case 3:
+        // If the coordinate is an integer or float, then the unused components
+        // type is the same as the coordinate. If the coordinate is a vector,
+        // then the unused components type is the same as the coordinate
+        // components type.
+        // |zero_constant_type_id| %5 = OpTypeVector %4 2
+        zero_constant_type_id =
+            coordinate_type->AsInteger() || coordinate_type->AsFloat()
+                ? FindOrCreateVectorType(coordinate_instruction->type_id(),
+                                         unused_component_count)
+                : FindOrCreateVectorType(
+                      GetIRContext()->get_type_mgr()->GetId(
+                          coordinate_type->AsVector()->element_type()),
+                      unused_component_count);
+        break;
+      default:
+        assert(false && "Should be unreachable.");
+        zero_constant_type_id = 0;
+        break;
+    }
+
+    // Gets |coordinate_type| again because the module may have changed due to
+    // the use of FindOrCreateVectorType above.
+    coordinate_type = GetIRContext()->get_type_mgr()->GetType(
+        coordinate_instruction->type_id());
+
+    // If the new vector type with unused components does not exist, then create
+    // it. |coordinate_with_unused_components_type_id| %6 = OpTypeVector %4 4
+    uint32_t coordinate_with_unused_components_type_id =
+        coordinate_type->AsInteger() || coordinate_type->AsFloat()
+            ? FindOrCreateVectorType(coordinate_instruction->type_id(),
+                                     1 + unused_component_count)
+            : FindOrCreateVectorType(
+                  GetIRContext()->get_type_mgr()->GetId(
+                      coordinate_type->AsVector()->element_type()),
+                  coordinate_type->AsVector()->element_count() +
+                      unused_component_count);
+
+    // Inserts an OpCompositeConstruct instruction which
+    // represents the coordinate with unused components.
+    // |coordinate_with_unused_components_id|
+    // %22 = OpCompositeConstruct %6 %17 %21
+    uint32_t coordinate_with_unused_components_id =
+        GetFuzzerContext()->GetFreshId();
+    ApplyTransformation(TransformationCompositeConstruct(
+        coordinate_with_unused_components_type_id,
+        {coordinate_instruction->result_id(),
+         // FindOrCreateZeroConstant
+         // %20 = OpConstant %4 0
+         // %21 = OpConstantComposite %5 %20 %20
+         FindOrCreateZeroConstant(zero_constant_type_id, false)},
+        MakeInstructionDescriptor(GetIRContext(), instruction),
+        coordinate_with_unused_components_id));
+
+    // Tries to add unused components to the image sample coordinate.
+    // %19 = OpImageSampleImplicitLod %6 %18 %22
+    ApplyTransformation(TransformationAddImageSampleUnusedComponents(
+        coordinate_with_unused_components_id,
+        MakeInstructionDescriptor(GetIRContext(), instruction)));
+  });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h
new file mode 100644
index 0000000..26374c3
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// This fuzzer pass searches for image sample instructions in the module and
+// randomly applies the transformation to add unused components to the image
+// sample coordinate.
+class FuzzerPassAddImageSampleUnusedComponents : public FuzzerPass {
+ public:
+  FuzzerPassAddImageSampleUnusedComponents(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddImageSampleUnusedComponents();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_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
index 661159e..ef8b5d0 100644
--- 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
@@ -71,7 +71,7 @@
       }
       ApplyTransformation(TransformationAddLocalVariable(
           GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(),
-          FindOrCreateZeroConstant(basic_type), true));
+          FindOrCreateZeroConstant(basic_type, false), true));
     }
   }
 }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_parameters.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_parameters.cpp
new file mode 100644
index 0000000..c5c9c33
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_parameters.cpp
@@ -0,0 +1,95 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_parameters.h"
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_add_parameter.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddParameters::FuzzerPassAddParameters(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassAddParameters::~FuzzerPassAddParameters() = default;
+
+void FuzzerPassAddParameters::Apply() {
+  // Compute type candidates for the new parameter.
+  std::vector<uint32_t> type_candidates;
+  for (const auto& type_inst : GetIRContext()->module()->GetTypes()) {
+    const auto* type =
+        GetIRContext()->get_type_mgr()->GetType(type_inst->result_id());
+    assert(type && "Type instruction is not registered in the type manager");
+    if (TransformationAddParameter::IsParameterTypeSupported(*type)) {
+      type_candidates.push_back(type_inst->result_id());
+    }
+  }
+
+  if (type_candidates.empty()) {
+    // The module contains no suitable types to use in new parameters.
+    return;
+  }
+
+  // Iterate over all functions in the module.
+  for (const auto& function : *GetIRContext()->module()) {
+    // Skip all entry-point functions - we don't want to change those.
+    if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(),
+                                         function.result_id())) {
+      continue;
+    }
+
+    if (GetNumberOfParameters(function) >=
+        GetFuzzerContext()->GetMaximumNumberOfFunctionParameters()) {
+      continue;
+    }
+
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfAddingParameters())) {
+      continue;
+    }
+
+    auto num_new_parameters =
+        GetFuzzerContext()->GetRandomNumberOfNewParameters(
+            GetNumberOfParameters(function));
+    for (uint32_t i = 0; i < num_new_parameters; ++i) {
+      ApplyTransformation(TransformationAddParameter(
+          function.result_id(), GetFuzzerContext()->GetFreshId(),
+          // We mark the constant as irrelevant so that we can replace it with a
+          // more interesting value later.
+          FindOrCreateZeroConstant(
+              type_candidates[GetFuzzerContext()->RandomIndex(type_candidates)],
+              true),
+          GetFuzzerContext()->GetFreshId()));
+    }
+  }
+}
+
+uint32_t FuzzerPassAddParameters::GetNumberOfParameters(
+    const opt::Function& function) const {
+  const auto* type = GetIRContext()->get_type_mgr()->GetType(
+      function.DefInst().GetSingleWordInOperand(1));
+  assert(type && type->AsFunction());
+
+  return static_cast<uint32_t>(type->AsFunction()->param_types().size());
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_parameters.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_parameters.h
new file mode 100644
index 0000000..f1261ae
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_parameters.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_PARAMETERS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Randomly decides for each non-entry-point function in the module whether to
+// add new parameters to it. If so, randomly determines the number of parameters
+// to add, their type and creates constants used to initialize them.
+class FuzzerPassAddParameters : public FuzzerPass {
+ public:
+  FuzzerPassAddParameters(opt::IRContext* ir_context,
+                          TransformationContext* transformation_context,
+                          FuzzerContext* fuzzer_context,
+                          protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddParameters() override;
+
+  void Apply() override;
+
+ private:
+  // Returns number of parameters of |function|.
+  uint32_t GetNumberOfParameters(const opt::Function& function) const;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp
new file mode 100644
index 0000000..a2497df
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp
@@ -0,0 +1,55 @@
+// 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_relaxed_decorations.h"
+
+#include "source/fuzz/transformation_add_relaxed_decoration.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddRelaxedDecorations::FuzzerPassAddRelaxedDecorations(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassAddRelaxedDecorations::~FuzzerPassAddRelaxedDecorations() = default;
+
+void FuzzerPassAddRelaxedDecorations::Apply() {
+  // Consider every instruction in every block in every function.
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      for (auto& inst : block) {
+        // Randomly choose whether to apply the RelaxedPrecision decoration
+        // to this instruction.
+        if (GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAddingRelaxedDecoration())) {
+          TransformationAddRelaxedDecoration transformation(inst.result_id());
+          // Restrict attention to numeric instructions (returning 32-bit
+          // floats or ints according to SPIR-V documentation) in dead blocks.
+          if (transformation.IsApplicable(GetIRContext(),
+                                          *GetTransformationContext())) {
+            transformation.Apply(GetIRContext(), GetTransformationContext());
+            *GetTransformations()->add_transformation() =
+                transformation.ToMessage();
+          }
+        }
+      }
+    }
+  }
+}
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h
new file mode 100644
index 0000000..dad5dfc
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h
@@ -0,0 +1,39 @@
+// 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 SPIRV_TOOLS_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H
+#define SPIRV_TOOLS_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A pass that applies the Relaxed decoration to numeric instructions.
+class FuzzerPassAddRelaxedDecorations : public FuzzerPass {
+ public:
+  FuzzerPassAddRelaxedDecorations(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddRelaxedDecorations() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_synonyms.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_synonyms.cpp
new file mode 100644
index 0000000..f109174
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_synonyms.cpp
@@ -0,0 +1,127 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_synonyms.h"
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_add_synonym.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddSynonyms::FuzzerPassAddSynonyms(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassAddSynonyms::~FuzzerPassAddSynonyms() = default;
+
+void FuzzerPassAddSynonyms::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor) {
+        // Skip |inst_it| if we can't insert anything above it. OpIAdd is just
+        // a representative of some instruction that might be produced by the
+        // transformation.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, inst_it)) {
+          return;
+        }
+
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAddingSynonyms())) {
+          return;
+        }
+
+        auto synonym_type = GetFuzzerContext()->GetRandomSynonymType();
+
+        // Select all instructions that can be used to create a synonym to.
+        auto available_instructions = FindAvailableInstructions(
+            function, block, inst_it,
+            [synonym_type, this](opt::IRContext* ir_context,
+                                 opt::Instruction* inst) {
+              // Check that we can create a synonym to |inst| as described by
+              // the |synonym_type| and insert it before |inst_it|.
+              return TransformationAddSynonym::IsInstructionValid(
+                  ir_context, *GetTransformationContext(), inst, synonym_type);
+            });
+
+        if (available_instructions.empty()) {
+          return;
+        }
+
+        const auto* existing_synonym =
+            available_instructions[GetFuzzerContext()->RandomIndex(
+                available_instructions)];
+
+        // Make sure the module contains all instructions required to apply the
+        // transformation.
+        switch (synonym_type) {
+          case protobufs::TransformationAddSynonym::ADD_ZERO:
+          case protobufs::TransformationAddSynonym::SUB_ZERO:
+          case protobufs::TransformationAddSynonym::LOGICAL_OR:
+            // Create a zero constant to be used as an operand of the synonymous
+            // instruction.
+            FindOrCreateZeroConstant(existing_synonym->type_id(), false);
+            break;
+          case protobufs::TransformationAddSynonym::MUL_ONE:
+          case protobufs::TransformationAddSynonym::LOGICAL_AND: {
+            const auto* existing_synonym_type =
+                GetIRContext()->get_type_mgr()->GetType(
+                    existing_synonym->type_id());
+            assert(existing_synonym_type && "Instruction has invalid type");
+
+            if (const auto* vector = existing_synonym_type->AsVector()) {
+              auto element_type_id =
+                  GetIRContext()->get_type_mgr()->GetId(vector->element_type());
+              assert(element_type_id && "Vector's element type is invalid");
+
+              auto one_word = vector->element_type()->AsFloat()
+                                  ? fuzzerutil::FloatToWord(1)
+                                  : 1u;
+              FindOrCreateCompositeConstant(
+                  std::vector<uint32_t>(
+                      vector->element_count(),
+                      FindOrCreateConstant({one_word}, element_type_id, false)),
+                  existing_synonym->type_id(), false);
+            } else {
+              FindOrCreateConstant(
+                  {existing_synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1)
+                                                    : 1u},
+                  existing_synonym->type_id(), false);
+            }
+          } break;
+          default:
+            // This assertion will fail if some SynonymType is missing from the
+            // switch statement.
+            assert(
+                !TransformationAddSynonym::IsAdditionalConstantRequired(
+                    synonym_type) &&
+                "|synonym_type| requires an additional constant to be present "
+                "in the module");
+            break;
+        }
+
+        ApplyTransformation(TransformationAddSynonym(
+            existing_synonym->result_id(), synonym_type,
+            GetFuzzerContext()->GetFreshId(), instruction_descriptor));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_synonyms.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_synonyms.h
new file mode 100644
index 0000000..dcfb938
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_synonyms.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_SYNONYMS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Sprinkles instructions through the module that produce ids, synonymous to
+// some other instructions.
+class FuzzerPassAddSynonyms : public FuzzerPass {
+ public:
+  FuzzerPassAddSynonyms(opt::IRContext* ir_context,
+                        TransformationContext* transformation_context,
+                        FuzzerContext* fuzzer_context,
+                        protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddSynonyms() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
new file mode 100644
index 0000000..453448b
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
@@ -0,0 +1,145 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_vector_shuffle_instructions.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_vector_shuffle.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddVectorShuffleInstructions::FuzzerPassAddVectorShuffleInstructions(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassAddVectorShuffleInstructions::
+    ~FuzzerPassAddVectorShuffleInstructions() = default;
+
+void FuzzerPassAddVectorShuffleInstructions::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator instruction_iterator,
+             const protobufs::InstructionDescriptor& instruction_descriptor)
+          -> void {
+        assert(instruction_iterator->opcode() ==
+                   instruction_descriptor.target_instruction_opcode() &&
+               "The opcode of the instruction we might insert before must be "
+               "the same as the opcode in the descriptor for the instruction");
+
+        // Randomly decide whether to try adding an OpVectorShuffle instruction.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAddingVectorShuffle())) {
+          return;
+        }
+
+        // It must be valid to insert an OpVectorShuffle instruction
+        // before |instruction_iterator|.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+                SpvOpVectorShuffle, instruction_iterator)) {
+          return;
+        }
+
+        // Looks for vectors that we might consider to use as OpVectorShuffle
+        // operands.
+        std::vector<opt::Instruction*> vector_instructions =
+            FindAvailableInstructions(
+                function, block, instruction_iterator,
+                [this, instruction_descriptor](
+                    opt::IRContext* ir_context,
+                    opt::Instruction* instruction) -> bool {
+                  if (!instruction->result_id() || !instruction->type_id()) {
+                    return false;
+                  }
+
+                  if (!ir_context->get_type_mgr()
+                           ->GetType(instruction->type_id())
+                           ->AsVector()) {
+                    return false;
+                  }
+
+                  if (!GetTransformationContext()
+                           ->GetFactManager()
+                           ->IdIsIrrelevant(instruction->result_id()) &&
+                      !fuzzerutil::CanMakeSynonymOf(ir_context,
+                                                    *GetTransformationContext(),
+                                                    instruction)) {
+                    // If the id is irrelevant, we can use it since it will not
+                    // participate in DataSynonym fact. Otherwise, we should be
+                    // able to produce a synonym out of the id.
+                    return false;
+                  }
+
+                  return fuzzerutil::IdIsAvailableBeforeInstruction(
+                      ir_context,
+                      FindInstruction(instruction_descriptor, ir_context),
+                      instruction->result_id());
+                });
+
+        // If there are no vector instructions, then return.
+        if (vector_instructions.empty()) {
+          return;
+        }
+
+        auto vector_1_instruction =
+            vector_instructions[GetFuzzerContext()->RandomIndex(
+                vector_instructions)];
+        auto vector_1_type = GetIRContext()
+                                 ->get_type_mgr()
+                                 ->GetType(vector_1_instruction->type_id())
+                                 ->AsVector();
+
+        auto vector_2_instruction =
+            GetFuzzerContext()->RemoveAtRandomIndex(&vector_instructions);
+        auto vector_2_type = GetIRContext()
+                                 ->get_type_mgr()
+                                 ->GetType(vector_2_instruction->type_id())
+                                 ->AsVector();
+
+        // |vector_1| and |vector_2| must have the same element type as each
+        // other. The loop is guaranteed to terminate because each iteration
+        // removes on possible choice for |vector_2|, and there is at least one
+        // choice that will cause the loop to exit - namely |vector_1|.
+        while (vector_1_type->element_type() != vector_2_type->element_type()) {
+          vector_2_instruction =
+              GetFuzzerContext()->RemoveAtRandomIndex(&vector_instructions);
+          vector_2_type = GetIRContext()
+                              ->get_type_mgr()
+                              ->GetType(vector_2_instruction->type_id())
+                              ->AsVector();
+        }
+
+        // Gets components and creates the appropriate result vector type.
+        std::vector<uint32_t> components =
+            GetFuzzerContext()->GetRandomComponentsForVectorShuffle(
+                vector_1_type->element_count() +
+                vector_2_type->element_count());
+        FindOrCreateVectorType(GetIRContext()->get_type_mgr()->GetId(
+                                   vector_1_type->element_type()),
+                               static_cast<uint32_t>(components.size()));
+
+        // Applies the vector shuffle transformation.
+        ApplyTransformation(TransformationVectorShuffle(
+            instruction_descriptor, GetFuzzerContext()->GetFreshId(),
+            vector_1_instruction->result_id(),
+            vector_2_instruction->result_id(), components));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h
new file mode 100644
index 0000000..99b9f24
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_VECTOR_SHUFFLE_INSTRUCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Adds OpVectorShuffle instructions to the module.
+class FuzzerPassAddVectorShuffleInstructions : public FuzzerPass {
+ public:
+  FuzzerPassAddVectorShuffleInstructions(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddVectorShuffleInstructions();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
index 32f5ea5..68f0ca7 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
@@ -75,7 +75,7 @@
                   *inst_it, mask_index);
           auto existing_mask =
               existing_mask_in_operand_index < inst_it->NumInOperands()
-                  ? inst_it->GetSingleWordOperand(
+                  ? inst_it->GetSingleWordInOperand(
                         existing_mask_in_operand_index)
                   : static_cast<uint32_t>(SpvMemoryAccessMaskNone);
 
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 0ec93e1..2808ad5 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
@@ -73,10 +73,9 @@
         continue;
       }
       // |use_index| is the absolute index of the operand.  We require
-      // the index of the operand restricted to input operands only, so
-      // we subtract the number of non-input operands from |use_index|.
+      // the index of the operand restricted to input operands only.
       uint32_t use_in_operand_index =
-          use_index - use_inst->NumOperands() + use_inst->NumInOperands();
+          fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
       if (!TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
               GetIRContext(), use_inst, use_in_operand_index)) {
         continue;
@@ -143,6 +142,9 @@
                                                : parent_block->terminator();
           }
 
+          assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
+                     synonym_to_try->object()) &&
+                 "Irrelevant ids can't participate in DataSynonym facts");
           ApplyTransformation(TransformationCompositeExtract(
               MakeInstructionDescriptor(GetIRContext(),
                                         instruction_to_insert_before),
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 e78f8ec..6443e89 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
@@ -14,12 +14,10 @@
 
 #include "source/fuzz/fuzzer_pass_construct_composites.h"
 
-#include <cmath>
 #include <memory>
 
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/transformation_composite_construct.h"
-#include "source/util/make_unique.h"
 
 namespace spvtools {
 namespace fuzz {
@@ -67,8 +65,23 @@
         // program point) and suitable for making a synonym of, associate it
         // with the id of its result type.
         TypeIdToInstructions type_id_to_available_instructions;
-        for (auto instruction : FindAvailableInstructions(
-                 function, block, inst_it, fuzzerutil::CanMakeSynonymOf)) {
+        auto available_instructions = FindAvailableInstructions(
+            function, block, inst_it,
+            [this](opt::IRContext* ir_context, opt::Instruction* inst) {
+              if (!inst->result_id() || !inst->type_id()) {
+                return false;
+              }
+
+              // If the id is irrelevant, we can use it since it will not
+              // participate in DataSynonym fact. Otherwise, we should be able
+              // to produce a synonym out of the id.
+              return GetTransformationContext()
+                         ->GetFactManager()
+                         ->IdIsIrrelevant(inst->result_id()) ||
+                     fuzzerutil::CanMakeSynonymOf(
+                         ir_context, *GetTransformationContext(), inst);
+            });
+        for (auto instruction : available_instructions) {
           RecordAvailableInstruction(instruction,
                                      &type_id_to_available_instructions);
         }
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 f055b59..81326ac 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
@@ -15,7 +15,8 @@
 #include "source/fuzz/fuzzer_pass_copy_objects.h"
 
 #include "source/fuzz/fuzzer_util.h"
-#include "source/fuzz/transformation_copy_object.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation_add_synonym.h"
 
 namespace spvtools {
 namespace fuzz {
@@ -54,8 +55,12 @@
         }
 
         std::vector<opt::Instruction*> relevant_instructions =
-            FindAvailableInstructions(function, block, inst_it,
-                                      fuzzerutil::CanMakeSynonymOf);
+            FindAvailableInstructions(
+                function, block, inst_it,
+                [this](opt::IRContext* ir_context, opt::Instruction* inst) {
+                  return fuzzerutil::CanMakeSynonymOf(
+                      ir_context, *GetTransformationContext(), inst);
+                });
 
         // At this point, |relevant_instructions| contains all the instructions
         // we might think of copying.
@@ -65,11 +70,12 @@
 
         // Choose a copyable instruction at random, and create and apply an
         // object copying transformation based on it.
-        ApplyTransformation(TransformationCopyObject(
+        ApplyTransformation(TransformationAddSynonym(
             relevant_instructions[GetFuzzerContext()->RandomIndex(
                                       relevant_instructions)]
                 ->result_id(),
-            instruction_descriptor, GetFuzzerContext()->GetFreshId()));
+            protobufs::TransformationAddSynonym::COPY_OBJECT,
+            GetFuzzerContext()->GetFreshId(), instruction_descriptor));
       });
 }
 
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
index b907376..aa0e243 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_donate_modules.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_donate_modules.cpp
@@ -27,6 +27,7 @@
 #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_spec_constant_op.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"
@@ -326,8 +327,9 @@
       new_result_id = GetFuzzerContext()->GetFreshId();
       ApplyTransformation(TransformationAddTypeArray(
           new_result_id, original_id_to_donated_id->at(component_type_id),
-          FindOrCreate32BitIntegerConstant(
-              GetFuzzerContext()->GetRandomSizeForNewArray(), false)));
+          FindOrCreateIntegerConstant(
+              {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false,
+              false)));
     } break;
     case SpvOpTypeStruct: {
       // Similar to SpvOpTypeArray.
@@ -413,14 +415,41 @@
             argument_type_ids));
       }
     } break;
+    case SpvOpSpecConstantOp: {
+      new_result_id = GetFuzzerContext()->GetFreshId();
+      auto type_id = original_id_to_donated_id->at(type_or_value.type_id());
+      auto opcode = static_cast<SpvOp>(type_or_value.GetSingleWordInOperand(0));
+
+      // Make sure we take into account |original_id_to_donated_id| when
+      // computing operands for OpSpecConstantOp.
+      opt::Instruction::OperandList operands;
+      for (uint32_t i = 1; i < type_or_value.NumInOperands(); ++i) {
+        const auto& operand = type_or_value.GetInOperand(i);
+        auto data =
+            operand.type == SPV_OPERAND_TYPE_ID
+                ? opt::Operand::OperandData{original_id_to_donated_id->at(
+                      operand.words[0])}
+                : operand.words;
+
+        operands.push_back({operand.type, std::move(data)});
+      }
+
+      ApplyTransformation(TransformationAddSpecConstantOp(
+          new_result_id, type_id, opcode, std::move(operands)));
+    } break;
+    case SpvOpSpecConstantTrue:
+    case SpvOpSpecConstantFalse:
     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));
+      auto value = type_or_value.opcode() == SpvOpConstantTrue ||
+                   type_or_value.opcode() == SpvOpSpecConstantTrue;
+      ApplyTransformation(
+          TransformationAddConstantBoolean(new_result_id, value, false));
     } break;
+    case SpvOpSpecConstant:
     case SpvOpConstant: {
       // It is OK to have duplicate constant definitions, so add this to the
       // module using a remapped result type.
@@ -431,8 +460,9 @@
       });
       ApplyTransformation(TransformationAddConstantScalar(
           new_result_id, original_id_to_donated_id->at(type_or_value.type_id()),
-          data_words));
+          data_words, false));
     } break;
+    case SpvOpSpecConstantComposite:
     case SpvOpConstantComposite: {
       assert(original_id_to_donated_id->count(type_or_value.type_id()) &&
              "Composite types for which it is possible to create a constant "
@@ -453,7 +483,7 @@
       });
       ApplyTransformation(TransformationAddConstantComposite(
           new_result_id, original_id_to_donated_id->at(type_or_value.type_id()),
-          constituent_ids));
+          constituent_ids, false));
     } break;
     case SpvOpConstantNull: {
       if (!original_id_to_donated_id->count(type_or_value.type_id())) {
@@ -515,7 +545,8 @@
                              ? 0
                              : FindOrCreateZeroConstant(
                                    fuzzerutil::GetPointeeTypeIdFromPointerType(
-                                       GetIRContext(), remapped_pointer_type));
+                                       GetIRContext(), remapped_pointer_type),
+                                   false);
       } else {
         // The variable already had an initializer; use its remapped id.
         initializer_id = original_id_to_donated_id->at(
@@ -890,11 +921,10 @@
 
   // We find or add a zero constant to the receiving module for the type in
   // question, and add an OpCopyObject instruction that copies this zero.
-  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
-  //  Using this particular constant is arbitrary, so if we have a
-  //  mechanism for noting that an id use is arbitrary and could be
-  //  fuzzed we should use it here.
-  auto zero_constant = FindOrCreateZeroConstant(remapped_type_id);
+  //
+  // We mark the constant as irrelevant so that we can replace it with a
+  // more interesting value later.
+  auto zero_constant = FindOrCreateZeroConstant(remapped_type_id, true);
   donated_instructions->push_back(MakeInstructionMessage(
       SpvOpCopyObject, remapped_type_id,
       original_id_to_donated_id->at(instruction.result_id()),
@@ -951,9 +981,11 @@
     // 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())))}});
+         {FindOrCreateZeroConstant(
+             fuzzerutil::GetPointeeTypeIdFromPointerType(
+                 GetIRContext(),
+                 original_id_to_donated_id->at(instruction.type_id())),
+             false)}});
   }
 
   if (instruction.result_id() &&
@@ -982,19 +1014,19 @@
   // 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
+  FindOrCreatePointerToIntegerType(
+      32, false, SpvStorageClassFunction);  // Needed for adding loop limiters
+  FindOrCreateIntegerConstant({0}, 32, false,
+                              false);  // Needed for initializing loop limiters
+  FindOrCreateIntegerConstant({1}, 32, false,
+                              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);
+  const uint32_t loop_limit = FindOrCreateIntegerConstant(
+      {GetFuzzerContext()->GetRandomLoopLimit()}, 32, false, false);
 
   // Consider every loop header in the function to donate, and create a
   // structure capturing the ids to be used for manipulating the loop
@@ -1068,11 +1100,11 @@
                      "A runtime array type in the donor should have been "
                      "replaced by a fixed-sized array in the recipient.");
               // The size of this fixed-size array is a suitable bound.
-              bound = TransformationAddFunction::GetBoundForCompositeIndex(
-                  GetIRContext(), *fixed_size_array_type);
+              bound = fuzzerutil::GetBoundForCompositeIndex(
+                  *fixed_size_array_type, GetIRContext());
             } else {
-              bound = TransformationAddFunction::GetBoundForCompositeIndex(
-                  donor_ir_context, *should_be_composite_type);
+              bound = fuzzerutil::GetBoundForCompositeIndex(
+                  *should_be_composite_type, donor_ir_context);
             }
             const uint32_t index_id = inst.GetSingleWordInOperand(index);
             auto index_inst =
@@ -1080,7 +1112,6 @@
             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())
@@ -1089,8 +1120,8 @@
               // 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());
+              FindOrCreateIntegerConstant({bound - 1}, 32,
+                                          index_int_type->IsSigned(), false);
             }
             should_be_composite_type =
                 TransformationAddFunction::FollowCompositeIndex(
@@ -1119,7 +1150,8 @@
     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()));
+        original_id_to_donated_id.at(function_return_type_inst->result_id()),
+        false);
   }
   // Add the function in a livesafe manner.
   ApplyTransformation(TransformationAddFunction(
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
new file mode 100644
index 0000000..727132e
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
@@ -0,0 +1,128 @@
+// Copyright (c) 2020 Stefano Milizia
+// 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_interchange_zero_like_constants.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/id_use_descriptor.h"
+#include "source/fuzz/transformation_record_synonymous_constants.h"
+#include "source/fuzz/transformation_replace_id_with_synonym.h"
+
+namespace spvtools {
+namespace fuzz {
+FuzzerPassInterchangeZeroLikeConstants::FuzzerPassInterchangeZeroLikeConstants(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassInterchangeZeroLikeConstants::
+    ~FuzzerPassInterchangeZeroLikeConstants() = default;
+
+uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant(
+    opt::Instruction* declaration) {
+  auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
+      declaration->result_id());
+
+  // This pass only toggles zero-like constants
+  if (!constant->IsZero()) {
+    return 0;
+  }
+
+  if (constant->AsScalarConstant()) {
+    return FindOrCreateNullConstant(declaration->type_id());
+  } else if (constant->AsNullConstant()) {
+    // Add declaration of equivalent scalar constant
+    auto kind = constant->type()->kind();
+    if (kind == opt::analysis::Type::kBool ||
+        kind == opt::analysis::Type::kInteger ||
+        kind == opt::analysis::Type::kFloat) {
+      return FindOrCreateZeroConstant(declaration->type_id(), false);
+    }
+  }
+
+  return 0;
+}
+
+void FuzzerPassInterchangeZeroLikeConstants::MaybeAddUseToReplace(
+    opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
+    std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
+        uses_to_replace) {
+  // Only consider this use if it is in a block
+  if (!GetIRContext()->get_instr_block(use_inst)) {
+    return;
+  }
+
+  // Get the index of the operand restricted to input operands.
+  uint32_t in_operand_index =
+      fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
+  auto id_use_descriptor =
+      MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, in_operand_index);
+  uses_to_replace->emplace_back(
+      std::make_pair(id_use_descriptor, replacement_id));
+}
+
+void FuzzerPassInterchangeZeroLikeConstants::Apply() {
+  // Make vector keeping track of all the uses we want to replace.
+  // This is a vector of pairs, where the first element is an id use descriptor
+  // identifying the use of a constant id and the second is the id that should
+  // be used to replace it.
+  std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>> uses_to_replace;
+
+  for (auto constant : GetIRContext()->GetConstants()) {
+    uint32_t constant_id = constant->result_id();
+    if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
+            constant_id)) {
+      continue;
+    }
+
+    uint32_t toggled_id = FindOrCreateToggledConstant(constant);
+    if (!toggled_id) {
+      // Not a zero-like constant
+      continue;
+    }
+
+    assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
+               toggled_id) &&
+           "FindOrCreateToggledConstant can't produce an irrelevant id");
+
+    // Record synonymous constants
+    ApplyTransformation(
+        TransformationRecordSynonymousConstants(constant_id, toggled_id));
+
+    // Find all the uses of the constant and, for each, probabilistically
+    // decide whether to replace it.
+    GetIRContext()->get_def_use_mgr()->ForEachUse(
+        constant_id,
+        [this, toggled_id, &uses_to_replace](opt::Instruction* use_inst,
+                                             uint32_t use_index) -> void {
+          if (GetFuzzerContext()->ChoosePercentage(
+                  GetFuzzerContext()
+                      ->GetChanceOfInterchangingZeroLikeConstants())) {
+            MaybeAddUseToReplace(use_inst, use_index, toggled_id,
+                                 &uses_to_replace);
+          }
+        });
+  }
+
+  // Replace the ids
+  for (auto use_to_replace : uses_to_replace) {
+    MaybeApplyTransformation(TransformationReplaceIdWithSynonym(
+        use_to_replace.first, use_to_replace.second));
+  }
+}
+}  // namespace fuzz
+}  // namespace spvtools
\ No newline at end of file
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
new file mode 100644
index 0000000..4fcc44e
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2020 Stefano Milizia
+// 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_INTERCHANGE_ZERO_LIKE_CONSTANTS_
+#define SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A pass that:
+// - Finds all the zero-like constant definitions in the module and adds the
+//   definitions of the corresponding synonym, recording the fact that they
+//   are synonymous. If the synonym is already in the module, it does not
+//   add a new one.
+// - For each use of a zero-like constant, decides whether to change it to the
+//   id of the toggled constant.
+class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass {
+ public:
+  FuzzerPassInterchangeZeroLikeConstants(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassInterchangeZeroLikeConstants() override;
+
+  void Apply() override;
+
+ private:
+  // Given the declaration of a zero-like constant, it finds or creates the
+  // corresponding toggled constant (a scalar constant of value 0 becomes a
+  // null constant of the same type and vice versa).
+  // Returns the id of the toggled instruction if the constant is zero-like,
+  // 0 otherwise.
+  uint32_t FindOrCreateToggledConstant(opt::Instruction* declaration);
+
+  // Given an id use (described by an instruction and an index) and an id with
+  // which the original one should be replaced, adds a pair (with the elements
+  // being the corresponding id use descriptor and the replacement id) to
+  // |uses_to_replace| if the use is in an instruction block, otherwise does
+  // nothing.
+  void MaybeAddUseToReplace(
+      opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
+      std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
+          uses_to_replace);
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+#endif  // SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp
new file mode 100644
index 0000000..de4ff1d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp
@@ -0,0 +1,52 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_invert_comparison_operators.h"
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_invert_comparison_operator.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassInvertComparisonOperators::FuzzerPassInvertComparisonOperators(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassInvertComparisonOperators::~FuzzerPassInvertComparisonOperators() =
+    default;
+
+void FuzzerPassInvertComparisonOperators::Apply() {
+  GetIRContext()->module()->ForEachInst([this](const opt::Instruction* inst) {
+    if (!TransformationInvertComparisonOperator::IsInversionSupported(
+            inst->opcode())) {
+      return;
+    }
+
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfInvertingComparisonOperators())) {
+      return;
+    }
+
+    ApplyTransformation(TransformationInvertComparisonOperator(
+        inst->result_id(), GetFuzzerContext()->GetFreshId()));
+  });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h
new file mode 100644
index 0000000..9c80bbb
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_INVERT_COMPARISON_OPERATORS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Iterates over different comparison operators in the module (>=, <, > etc.)
+// and randomly decides whether to invert each one or not.
+class FuzzerPassInvertComparisonOperators : public FuzzerPass {
+ public:
+  FuzzerPassInvertComparisonOperators(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassInvertComparisonOperators() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_
\ No newline at end of file
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.cpp
index 49778ae..e66fc44 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.cpp
@@ -53,14 +53,9 @@
   }
 
   while (!potential_transformations.empty()) {
-    uint32_t index = GetFuzzerContext()->RandomIndex(potential_transformations);
-    auto transformation = potential_transformations.at(index);
-    potential_transformations.erase(potential_transformations.begin() + index);
-    if (transformation.IsApplicable(GetIRContext(),
-                                    *GetTransformationContext())) {
-      transformation.Apply(GetIRContext(), GetTransformationContext());
-      *GetTransformations()->add_transformation() = transformation.ToMessage();
-    }
+    auto transformation =
+        GetFuzzerContext()->RemoveAtRandomIndex(&potential_transformations);
+    MaybeApplyTransformation(transformation);
   }
 }
 
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 543c0d7..2775bb8 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
@@ -311,9 +311,9 @@
   } while (constant_index_1 == constant_index_2);
 
   auto constant_id_1 = FindOrCreateConstant(
-      available_constant_words[constant_index_1], chosen_type_id);
+      available_constant_words[constant_index_1], chosen_type_id, false);
   auto constant_id_2 = FindOrCreateConstant(
-      available_constant_words[constant_index_2], chosen_type_id);
+      available_constant_words[constant_index_2], chosen_type_id, false);
 
   assert(constant_id_1 != 0 && constant_id_2 != 0 &&
          "We should not find an available constant with an id of 0.");
@@ -361,7 +361,7 @@
   // Make sure the module has OpConstant instructions for each index used to
   // access a uniform.
   for (auto index : uniform_descriptor.index()) {
-    FindOrCreate32BitIntegerConstant(index, true);
+    FindOrCreateIntegerConstant({index}, 32, true, false);
   }
 
   // Make sure the module has OpTypePointer that points to the element type of
@@ -477,28 +477,18 @@
           skipped_opcode_count.clear();
         }
 
-        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;
+        // The instruction must not be an OpVariable, the only id that an
+        // OpVariable uses is an initializer id, which has to remain
+        // constant.
+        if (inst.opcode() != SpvOpVariable) {
+          // 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);
+          }
         }
 
         if (!inst.HasResultId()) {
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.cpp
index 1665d05..1cd291e 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.cpp
@@ -17,7 +17,9 @@
 #include <vector>
 
 #include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/transformation_outline_function.h"
+#include "source/fuzz/transformation_split_block.h"
 
 namespace spvtools {
 namespace fuzz {
@@ -46,6 +48,31 @@
       blocks.push_back(&block);
     }
     auto entry_block = blocks[GetFuzzerContext()->RandomIndex(blocks)];
+
+    // If the entry block starts with OpPhi, try to split it.
+    if (entry_block->begin()->opcode() == SpvOpPhi) {
+      // Find the first non-OpPhi instruction.
+      opt::Instruction* non_phi_inst;
+      for (auto instruction : *entry_block) {
+        if (instruction.opcode() != SpvOpPhi) {
+          non_phi_inst = &instruction;
+          break;
+        }
+      }
+
+      // If the split was not applicable, the transformation will not work.
+      uint32_t new_block_id = GetFuzzerContext()->GetFreshId();
+      if (!MaybeApplyTransformation(TransformationSplitBlock(
+              MakeInstructionDescriptor(non_phi_inst->result_id(),
+                                        non_phi_inst->opcode(), 0),
+              new_block_id))) {
+        return;
+      }
+
+      // The new entry block is the newly-created block.
+      entry_block = &*function->FindBlock(new_block_id);
+    }
+
     auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function);
     auto postdominator_analysis =
         GetIRContext()->GetPostDominatorAnalysis(function);
@@ -89,11 +116,7 @@
         /*new_callee_result_id*/ GetFuzzerContext()->GetFreshId(),
         /*input_id_to_fresh_id*/ std::move(input_id_to_fresh_id),
         /*output_id_to_fresh_id*/ std::move(output_id_to_fresh_id));
-    if (transformation.IsApplicable(GetIRContext(),
-                                    *GetTransformationContext())) {
-      transformation.Apply(GetIRContext(), GetTransformationContext());
-      *GetTransformations()->add_transformation() = transformation.ToMessage();
-    }
+    MaybeApplyTransformation(transformation);
   }
 }
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_blocks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_blocks.cpp
index 27a2d67..24c16fb 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_blocks.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_blocks.cpp
@@ -67,12 +67,7 @@
       // down indefinitely.
       while (true) {
         TransformationMoveBlockDown transformation(*id);
-        if (transformation.IsApplicable(GetIRContext(),
-                                        *GetTransformationContext())) {
-          transformation.Apply(GetIRContext(), GetTransformationContext());
-          *GetTransformations()->add_transformation() =
-              transformation.ToMessage();
-        } else {
+        if (!MaybeApplyTransformation(transformation)) {
           break;
         }
       }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
index 57d9cab..e15aef6 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
@@ -61,20 +61,9 @@
     std::iota(permutation.begin(), permutation.end(), 0);
     GetFuzzerContext()->Shuffle(&permutation);
 
-    // Create a new OpFunctionType instruction with permuted arguments
-    // if needed
-    auto result_type_id = function_type->GetSingleWordInOperand(0);
-    std::vector<uint32_t> argument_ids;
-
-    for (auto index : permutation) {
-      // +1 to take function's return type into account
-      argument_ids.push_back(function_type->GetSingleWordInOperand(index + 1));
-    }
-
     // Apply our transformation
     ApplyTransformation(TransformationPermuteFunctionParameters(
-        function_id, FindOrCreateFunctionType(result_type_id, argument_ids),
-        permutation));
+        function_id, GetFuzzerContext()->GetFreshId(), permutation));
   }
 }
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.h
index 3f32864..bc1031c 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.h
@@ -34,7 +34,7 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassPermuteFunctionParameters();
+  ~FuzzerPassPermuteFunctionParameters() override;
 
   void Apply() override;
 };
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
new file mode 100644
index 0000000..c241d9d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
@@ -0,0 +1,64 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 <numeric>
+#include <vector>
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_pass_permute_phi_operands.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_permute_phi_operands.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassPermutePhiOperands::FuzzerPassPermutePhiOperands(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassPermutePhiOperands::~FuzzerPassPermutePhiOperands() = default;
+
+void FuzzerPassPermutePhiOperands::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& /*unused*/) {
+        const auto& inst = *inst_it;
+
+        if (inst.opcode() != SpvOpPhi) {
+          return;
+        }
+
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfPermutingPhiOperands())) {
+          return;
+        }
+
+        // Create a vector of indices for each pair of operands in the |inst|.
+        // OpPhi always has an even number of operands.
+        std::vector<uint32_t> permutation(inst.NumInOperands() / 2);
+        std::iota(permutation.begin(), permutation.end(), 0);
+        GetFuzzerContext()->Shuffle(&permutation);
+
+        ApplyTransformation(TransformationPermutePhiOperands(
+            inst.result_id(), std::move(permutation)));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_phi_operands.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_phi_operands.h
new file mode 100644
index 0000000..974c2c1
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_phi_operands.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Iterates over all instructions in the module and randomly decides for each
+// OpPhi instruction whether to permute its operands.
+class FuzzerPassPermutePhiOperands : public FuzzerPass {
+ public:
+  FuzzerPassPermutePhiOperands(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassPermutePhiOperands() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
new file mode 100644
index 0000000..8d9acaa
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
@@ -0,0 +1,154 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_push_ids_through_variables.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_push_id_through_variable.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassPushIdsThroughVariables::FuzzerPassPushIdsThroughVariables(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassPushIdsThroughVariables::~FuzzerPassPushIdsThroughVariables() =
+    default;
+
+void FuzzerPassPushIdsThroughVariables::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator instruction_iterator,
+             const protobufs::InstructionDescriptor& instruction_descriptor)
+          -> void {
+        assert(instruction_iterator->opcode() ==
+                   instruction_descriptor.target_instruction_opcode() &&
+               "The opcode of the instruction we might insert before must be "
+               "the same as the opcode in the descriptor for the instruction");
+
+        // Randomly decide whether to try pushing an id through a variable.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfPushingIdThroughVariable())) {
+          return;
+        }
+
+        // The block containing the instruction we are going to insert before
+        // must be reachable.
+        if (!fuzzerutil::BlockIsReachableInItsFunction(GetIRContext(), block)) {
+          return;
+        }
+
+        // It must be valid to insert OpStore and OpLoad instructions
+        // before the instruction to insert before.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+                SpvOpStore, instruction_iterator) ||
+            !fuzzerutil::CanInsertOpcodeBeforeInstruction(
+                SpvOpLoad, instruction_iterator)) {
+          return;
+        }
+
+        // Randomly decides whether a global or local variable will be added.
+        auto variable_storage_class = GetFuzzerContext()->ChooseEven()
+                                          ? SpvStorageClassPrivate
+                                          : SpvStorageClassFunction;
+
+        // Gets the available basic and pointer types.
+        auto basic_type_ids_and_pointers =
+            GetAvailableBasicTypesAndPointers(variable_storage_class);
+        auto& basic_types = basic_type_ids_and_pointers.first;
+        uint32_t basic_type_id =
+            basic_types[GetFuzzerContext()->RandomIndex(basic_types)];
+
+        // Looks for ids that we might wish to consider pushing through a
+        // variable.
+        std::vector<opt::Instruction*> value_instructions =
+            FindAvailableInstructions(
+                function, block, instruction_iterator,
+                [this, basic_type_id, instruction_descriptor](
+                    opt::IRContext* ir_context,
+                    opt::Instruction* instruction) -> bool {
+                  if (!instruction->result_id() || !instruction->type_id()) {
+                    return false;
+                  }
+
+                  if (instruction->type_id() != basic_type_id) {
+                    return false;
+                  }
+
+                  // If the id is irrelevant, we can use it since it will not
+                  // participate in DataSynonym fact. Otherwise, we should be
+                  // able to produce a synonym out of the id.
+                  if (!GetTransformationContext()
+                           ->GetFactManager()
+                           ->IdIsIrrelevant(instruction->result_id()) &&
+                      !fuzzerutil::CanMakeSynonymOf(ir_context,
+                                                    *GetTransformationContext(),
+                                                    instruction)) {
+                    return false;
+                  }
+
+                  return fuzzerutil::IdIsAvailableBeforeInstruction(
+                      ir_context,
+                      FindInstruction(instruction_descriptor, ir_context),
+                      instruction->result_id());
+                });
+
+        if (value_instructions.empty()) {
+          return;
+        }
+
+        // If the pointer type does not exist, then create it.
+        FindOrCreatePointerType(basic_type_id, variable_storage_class);
+
+        // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
+        //  type support here is limited by the FindOrCreateZeroConstant
+        //  function.
+        const auto* type_inst =
+            GetIRContext()->get_def_use_mgr()->GetDef(basic_type_id);
+        assert(type_inst);
+        switch (type_inst->opcode()) {
+          case SpvOpTypeBool:
+          case SpvOpTypeFloat:
+          case SpvOpTypeInt:
+          case SpvOpTypeArray:
+          case SpvOpTypeMatrix:
+          case SpvOpTypeVector:
+          case SpvOpTypeStruct:
+            break;
+          default:
+            return;
+        }
+
+        // Create a constant to initialize the variable from. This might update
+        // module's id bound so it must be done before any fresh ids are
+        // computed.
+        auto initializer_id = FindOrCreateZeroConstant(basic_type_id, false);
+
+        // Applies the push id through variable transformation.
+        ApplyTransformation(TransformationPushIdThroughVariable(
+            value_instructions[GetFuzzerContext()->RandomIndex(
+                                   value_instructions)]
+                ->result_id(),
+            GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId(),
+            variable_storage_class, initializer_id, instruction_descriptor));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h
new file mode 100644
index 0000000..3ad5404
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_PUSH_IDS_THROUGH_VARIABLES_H_
+#define SOURCE_FUZZ_FUZZER_PASS_PUSH_IDS_THROUGH_VARIABLES_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Adds instructions to the module that store existing ids into fresh variables
+// and immediately load from said variables into new ids, thus creating synonyms
+// between the existing and fresh ids.
+class FuzzerPassPushIdsThroughVariables : public FuzzerPass {
+ public:
+  FuzzerPassPushIdsThroughVariables(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassPushIdsThroughVariables() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_PUSH_IDS_THROUGH_VARIABLES_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
new file mode 100644
index 0000000..6847146
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
@@ -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.
+
+#include "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceCopyMemoriesWithLoadsStores::
+    FuzzerPassReplaceCopyMemoriesWithLoadsStores(
+        opt::IRContext* ir_context,
+        TransformationContext* transformation_context,
+        FuzzerContext* fuzzer_context,
+        protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassReplaceCopyMemoriesWithLoadsStores::
+    ~FuzzerPassReplaceCopyMemoriesWithLoadsStores() = default;
+
+void FuzzerPassReplaceCopyMemoriesWithLoadsStores::Apply() {
+  GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
+    // Randomly decide whether to replace the OpCopyMemory.
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()
+                ->GetChanceOfReplacingCopyMemoryWithLoadStore())) {
+      return;
+    }
+
+    // The instruction must be OpCopyMemory.
+    if (instruction->opcode() != SpvOpCopyMemory) {
+      return;
+    }
+
+    // Apply the transformation replacing OpCopyMemory with OpLoad and OpStore.
+    ApplyTransformation(TransformationReplaceCopyMemoryWithLoadStore(
+        GetFuzzerContext()->GetFreshId(),
+        MakeInstructionDescriptor(GetIRContext(), instruction)));
+  });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h
new file mode 100644
index 0000000..2a89006
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.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 SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H
+#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Replaces instructions OpCopyMemory with loading the source variable to
+// an intermediate value and storing this value into the target variable of
+// the original OpCopyMemory instruction.
+class FuzzerPassReplaceCopyMemoriesWithLoadsStores : public FuzzerPass {
+ public:
+  FuzzerPassReplaceCopyMemoriesWithLoadsStores(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassReplaceCopyMemoriesWithLoadsStores() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
new file mode 100644
index 0000000..e21ea5e
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
@@ -0,0 +1,83 @@
+// 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_replace_copy_objects_with_stores_loads.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_replace_copy_object_with_store_load.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceCopyObjectsWithStoresLoads::
+    FuzzerPassReplaceCopyObjectsWithStoresLoads(
+        opt::IRContext* ir_context,
+        TransformationContext* transformation_context,
+        FuzzerContext* fuzzer_context,
+        protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassReplaceCopyObjectsWithStoresLoads::
+    ~FuzzerPassReplaceCopyObjectsWithStoresLoads() = default;
+
+void FuzzerPassReplaceCopyObjectsWithStoresLoads::Apply() {
+  GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
+    // Randomly decide whether to replace OpCopyObject.
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()
+                ->GetChanceOfReplacingCopyObjectWithStoreLoad())) {
+      return;
+    }
+    // The instruction must be OpCopyObject.
+    if (instruction->opcode() != SpvOpCopyObject) {
+      return;
+    }
+    // The opcode of the type_id instruction cannot be a OpTypePointer,
+    // because we cannot define a pointer to pointer.
+    if (GetIRContext()
+            ->get_def_use_mgr()
+            ->GetDef(instruction->type_id())
+            ->opcode() == SpvOpTypePointer) {
+      return;
+    }
+    // It must be valid to insert OpStore and OpLoad instructions
+    // before the instruction OpCopyObject.
+    if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
+                                                      instruction) ||
+        !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, instruction)) {
+      return;
+    }
+
+    // Randomly decides whether a global or local variable will be added.
+    auto variable_storage_class = GetFuzzerContext()->ChooseEven()
+                                      ? SpvStorageClassPrivate
+                                      : SpvStorageClassFunction;
+
+    // Find or create a constant to initialize the variable from.
+    auto variable_initializer_id =
+        FindOrCreateZeroConstant(instruction->type_id(), false);
+
+    // Make sure that pointer type is defined.
+    FindOrCreatePointerType(instruction->type_id(), variable_storage_class);
+    // Apply the transformation replacing OpCopyObject with Store and Load.
+    ApplyTransformation(TransformationReplaceCopyObjectWithStoreLoad(
+        instruction->result_id(), GetFuzzerContext()->GetFreshId(),
+        variable_storage_class, variable_initializer_id));
+  });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h
new file mode 100644
index 0000000..15ec10b
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.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 SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H
+#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Replaces instructions OpCopyObject with storing into a new variable
+// and immediately loading this variable to |result_id| of the
+// original OpCopyObject instruction.
+class FuzzerPassReplaceCopyObjectsWithStoresLoads : public FuzzerPass {
+ public:
+  FuzzerPassReplaceCopyObjectsWithStoresLoads(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassReplaceCopyObjectsWithStoresLoads() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp
new file mode 100644
index 0000000..1e5d697
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp
@@ -0,0 +1,68 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_replace_linear_algebra_instructions.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceLinearAlgebraInstructions::
+    FuzzerPassReplaceLinearAlgebraInstructions(
+        opt::IRContext* ir_context,
+        TransformationContext* transformation_context,
+        FuzzerContext* fuzzer_context,
+        protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassReplaceLinearAlgebraInstructions::
+    ~FuzzerPassReplaceLinearAlgebraInstructions() = default;
+
+void FuzzerPassReplaceLinearAlgebraInstructions::Apply() {
+  // For each instruction, checks whether it is a supported linear algebra
+  // instruction. In this case, the transformation is randomly applied.
+  GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
+    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
+    // Right now we only support certain operations. When this issue is
+    // addressed the following conditional can use the function
+    // |spvOpcodeIsLinearAlgebra|.
+    if (instruction->opcode() != SpvOpVectorTimesScalar &&
+        instruction->opcode() != SpvOpMatrixTimesScalar &&
+        instruction->opcode() != SpvOpVectorTimesMatrix &&
+        instruction->opcode() != SpvOpMatrixTimesVector &&
+        instruction->opcode() != SpvOpMatrixTimesMatrix &&
+        instruction->opcode() != SpvOpDot) {
+      return;
+    }
+
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()
+                ->GetChanceOfReplacingLinearAlgebraInstructions())) {
+      return;
+    }
+
+    ApplyTransformation(TransformationReplaceLinearAlgebraInstruction(
+        GetFuzzerContext()->GetFreshIds(
+            TransformationReplaceLinearAlgebraInstruction::
+                GetRequiredFreshIdCount(GetIRContext(), instruction)),
+        MakeInstructionDescriptor(GetIRContext(), instruction)));
+  });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h
new file mode 100644
index 0000000..2c6126c
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_REPLACE_LINEAR_ALGEBRA_INSTRUCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_LINEAR_ALGEBRA_INSTRUCTIONS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// This fuzzer pass replaces linear algebra instructions with its mathematical
+// definition.
+class FuzzerPassReplaceLinearAlgebraInstructions : public FuzzerPass {
+ public:
+  FuzzerPassReplaceLinearAlgebraInstructions(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassReplaceLinearAlgebraInstructions();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_REPLACE_LINEAR_ALGEBRA_INSTRUCTIONS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
new file mode 100644
index 0000000..7690ac4
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
@@ -0,0 +1,105 @@
+// 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_replace_loads_stores_with_copy_memories.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h"
+#include "source/opt/instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceLoadsStoresWithCopyMemories::
+    FuzzerPassReplaceLoadsStoresWithCopyMemories(
+        opt::IRContext* ir_context,
+        TransformationContext* transformation_context,
+        FuzzerContext* fuzzer_context,
+        protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassReplaceLoadsStoresWithCopyMemories::
+    ~FuzzerPassReplaceLoadsStoresWithCopyMemories() = default;
+
+void FuzzerPassReplaceLoadsStoresWithCopyMemories::Apply() {
+  // We look for matching pairs of instructions OpLoad and
+  // OpStore within the same block. Potential instructions OpLoad to be matched
+  // are stored in a hash map. If we encounter instructions that write to memory
+  // or instructions of memory barriers that could operate on variables within
+  // unsafe storage classes we need to erase the hash map to avoid unsafe
+  // operations.
+
+  // A vector of matching OpLoad and OpStore instructions.
+  std::vector<std::pair<opt::Instruction*, opt::Instruction*>>
+      op_load_store_pairs;
+
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      // A hash map storing potential OpLoad instructions.
+      std::unordered_map<uint32_t, opt::Instruction*> current_op_loads;
+      for (auto& instruction : block) {
+        // Add a potential OpLoad instruction.
+        if (instruction.opcode() == SpvOpLoad) {
+          current_op_loads[instruction.result_id()] = &instruction;
+        } else if (instruction.opcode() == SpvOpStore) {
+          if (current_op_loads.find(instruction.GetSingleWordOperand(1)) !=
+              current_op_loads.end()) {
+            // We have found the matching OpLoad instruction to the current
+            // OpStore instruction.
+            op_load_store_pairs.push_back(std::make_pair(
+                current_op_loads[instruction.GetSingleWordOperand(1)],
+                &instruction));
+          }
+        }
+        if (TransformationReplaceLoadStoreWithCopyMemory::IsMemoryWritingOpCode(
+                instruction.opcode())) {
+          current_op_loads.clear();
+        } else if (TransformationReplaceLoadStoreWithCopyMemory::
+                       IsMemoryBarrierOpCode(instruction.opcode())) {
+          for (auto it = current_op_loads.begin();
+               it != current_op_loads.end();) {
+            // Get the storage class.
+            opt::Instruction* source_id =
+                GetIRContext()->get_def_use_mgr()->GetDef(
+                    it->second->GetSingleWordOperand(2));
+            SpvStorageClass storage_class =
+                fuzzerutil::GetStorageClassFromPointerType(
+                    GetIRContext(), source_id->type_id());
+            if (!TransformationReplaceLoadStoreWithCopyMemory::
+                    IsStorageClassSafeAcrossMemoryBarriers(storage_class)) {
+              it = current_op_loads.erase(it);
+            } else {
+              it++;
+            }
+          }
+        }
+      }
+    }
+  }
+  for (auto instr_pair : op_load_store_pairs) {
+    // Randomly decide to apply the transformation for the
+    // potential pairs.
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()
+                ->GetChanceOfReplacingLoadStoreWithCopyMemory())) {
+      ApplyTransformation(TransformationReplaceLoadStoreWithCopyMemory(
+          MakeInstructionDescriptor(GetIRContext(), instr_pair.first),
+          MakeInstructionDescriptor(GetIRContext(), instr_pair.second)));
+    }
+  }
+}  // namespace fuzz
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h
new file mode 100644
index 0000000..db09500
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.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 SPIRV_TOOLS_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H
+#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass that takes pairs of instruction descriptors to OpLoad and
+// OpStore that have the same intermediate value and in each pair replaces the
+// OpStore with an equivalent OpCopyMemory.
+class FuzzerPassReplaceLoadsStoresWithCopyMemories : public FuzzerPass {
+ public:
+  FuzzerPassReplaceLoadsStoresWithCopyMemories(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassReplaceLoadsStoresWithCopyMemories() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
new file mode 100644
index 0000000..8672a3b
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
@@ -0,0 +1,93 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_replace_parameter_with_global.h"
+
+#include <numeric>
+#include <vector>
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_replace_parameter_with_global.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceParameterWithGlobal::FuzzerPassReplaceParameterWithGlobal(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassReplaceParameterWithGlobal::~FuzzerPassReplaceParameterWithGlobal() =
+    default;
+
+void FuzzerPassReplaceParameterWithGlobal::Apply() {
+  for (const auto& function : *GetIRContext()->module()) {
+    if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(),
+                                         function.result_id())) {
+      continue;
+    }
+
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfReplacingParametersWithGlobals())) {
+      continue;
+    }
+
+    auto params =
+        fuzzerutil::GetParameters(GetIRContext(), function.result_id());
+
+    // Make sure at least one parameter can be replaced. Also checks that the
+    // function has at least one parameter.
+    if (std::none_of(params.begin(), params.end(),
+                     [this](const opt::Instruction* param) {
+                       const auto* param_type =
+                           GetIRContext()->get_type_mgr()->GetType(
+                               param->type_id());
+                       assert(param_type && "Parameter has invalid type");
+                       return TransformationReplaceParameterWithGlobal::
+                           IsParameterTypeSupported(*param_type);
+                     })) {
+      continue;
+    }
+
+    // Select id of a parameter to replace.
+    const opt::Instruction* replaced_param = nullptr;
+    const opt::analysis::Type* param_type = nullptr;
+    do {
+      replaced_param = GetFuzzerContext()->RemoveAtRandomIndex(&params);
+      param_type =
+          GetIRContext()->get_type_mgr()->GetType(replaced_param->type_id());
+      assert(param_type && "Parameter has invalid type");
+    } while (
+        !TransformationReplaceParameterWithGlobal::IsParameterTypeSupported(
+            *param_type));
+
+    assert(replaced_param && "Unable to find a parameter to replace");
+
+    // Make sure type id for the global variable exists in the module.
+    FindOrCreatePointerType(replaced_param->type_id(), SpvStorageClassPrivate);
+
+    // Make sure initializer for the global variable exists in the module.
+    FindOrCreateZeroConstant(replaced_param->type_id(), false);
+
+    ApplyTransformation(TransformationReplaceParameterWithGlobal(
+        GetFuzzerContext()->GetFreshId(), replaced_param->result_id(),
+        GetFuzzerContext()->GetFreshId()));
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h
new file mode 100644
index 0000000..25011bd
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_REPLACE_PARAMETER_WITH_GLOBAL_H_
+#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Iterates over all non-entry-point functions in the module and randomly
+// replaces a parameter with a global variable.
+class FuzzerPassReplaceParameterWithGlobal : public FuzzerPass {
+ public:
+  FuzzerPassReplaceParameterWithGlobal(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassReplaceParameterWithGlobal() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp
new file mode 100644
index 0000000..e49eacb
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp
@@ -0,0 +1,115 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_replace_params_with_struct.h"
+
+#include <numeric>
+#include <vector>
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_replace_params_with_struct.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceParamsWithStruct::FuzzerPassReplaceParamsWithStruct(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassReplaceParamsWithStruct::~FuzzerPassReplaceParamsWithStruct() =
+    default;
+
+void FuzzerPassReplaceParamsWithStruct::Apply() {
+  for (const auto& function : *GetIRContext()->module()) {
+    auto params =
+        fuzzerutil::GetParameters(GetIRContext(), function.result_id());
+
+    if (params.empty() || fuzzerutil::FunctionIsEntryPoint(
+                              GetIRContext(), function.result_id())) {
+      continue;
+    }
+
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfReplacingParametersWithStruct())) {
+      continue;
+    }
+
+    std::vector<uint32_t> parameter_index(params.size());
+    std::iota(parameter_index.begin(), parameter_index.end(), 0);
+
+    // Remove the indices of unsupported parameters.
+    auto new_end = std::remove_if(
+        parameter_index.begin(), parameter_index.end(),
+        [this, &params](uint32_t index) {
+          const auto* type =
+              GetIRContext()->get_type_mgr()->GetType(params[index]->type_id());
+          assert(type && "Parameter has invalid type");
+          return !TransformationReplaceParamsWithStruct::
+              IsParameterTypeSupported(*type);
+        });
+
+    // std::remove_if changes the vector so that removed elements are placed at
+    // the end (i.e. [new_end, parameter_index.end()) is a range of removed
+    // elements). However, the size of the vector is not changed so we need to
+    // change that explicitly by popping those elements from the vector.
+    parameter_index.erase(new_end, parameter_index.end());
+
+    if (parameter_index.empty()) {
+      continue;
+    }
+
+    // Select |num_replaced_params| parameters at random. We shuffle the vector
+    // of indices for randomization and shrink it to select first
+    // |num_replaced_params| parameters.
+    auto num_replaced_params = std::min<size_t>(
+        parameter_index.size(),
+        GetFuzzerContext()->GetRandomNumberOfParametersReplacedWithStruct(
+            static_cast<uint32_t>(params.size())));
+
+    GetFuzzerContext()->Shuffle(&parameter_index);
+    parameter_index.resize(num_replaced_params);
+
+    // Make sure OpTypeStruct exists in the module.
+    std::vector<uint32_t> component_type_ids;
+    for (auto index : parameter_index) {
+      component_type_ids.push_back(params[index]->type_id());
+    }
+
+    FindOrCreateStructType(component_type_ids);
+
+    // Map parameters' indices to parameters' ids.
+    std::vector<uint32_t> parameter_id;
+    for (auto index : parameter_index) {
+      parameter_id.push_back(params[index]->result_id());
+    }
+
+    std::unordered_map<uint32_t, uint32_t> caller_id_to_fresh_id;
+    for (const auto* inst :
+         fuzzerutil::GetCallers(GetIRContext(), function.result_id())) {
+      caller_id_to_fresh_id[inst->result_id()] =
+          GetFuzzerContext()->GetFreshId();
+    }
+
+    ApplyTransformation(TransformationReplaceParamsWithStruct(
+        parameter_id, GetFuzzerContext()->GetFreshId(),
+        GetFuzzerContext()->GetFreshId(), caller_id_to_fresh_id));
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h
new file mode 100644
index 0000000..ed1aa6b
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_REPLACE_PARAMS_WITH_STRUCT_H_
+#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Iterates over all functions in the module and randomly decides for each one
+// whether to replace a subset of its parameters with a struct value.
+class FuzzerPassReplaceParamsWithStruct : public FuzzerPass {
+ public:
+  FuzzerPassReplaceParamsWithStruct(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassReplaceParamsWithStruct() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_split_blocks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_split_blocks.cpp
index 15c6790..481cd96 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_split_blocks.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_split_blocks.cpp
@@ -96,11 +96,7 @@
     // If the position we have chosen turns out to be a valid place to split
     // the block, we apply the split. Otherwise the block just doesn't get
     // split.
-    if (transformation.IsApplicable(GetIRContext(),
-                                    *GetTransformationContext())) {
-      transformation.Apply(GetIRContext(), GetTransformationContext());
-      *GetTransformations()->add_transformation() = transformation.ToMessage();
-    }
+    MaybeApplyTransformation(transformation);
   }
 }
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
new file mode 100644
index 0000000..dc8b1eb
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
@@ -0,0 +1,59 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassSwapBranchConditionalOperands::
+    FuzzerPassSwapBranchConditionalOperands(
+        opt::IRContext* ir_context,
+        TransformationContext* transformation_context,
+        FuzzerContext* fuzzer_context,
+        protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassSwapBranchConditionalOperands::
+    ~FuzzerPassSwapBranchConditionalOperands() = default;
+
+void FuzzerPassSwapBranchConditionalOperands::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor) {
+        const auto& inst = *inst_it;
+
+        if (inst.opcode() != SpvOpBranchConditional) {
+          return;
+        }
+
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()
+                    ->GetChanceOfSwappingConditionalBranchOperands())) {
+          return;
+        }
+
+        ApplyTransformation(TransformationSwapConditionalBranchOperands(
+            instruction_descriptor, GetFuzzerContext()->GetFreshId()));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h
new file mode 100644
index 0000000..f84f3ba
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Randomly decides for each OpBranchConditional instruction in the module
+// whether to swap its operands or not.
+class FuzzerPassSwapBranchConditionalOperands : public FuzzerPass {
+ public:
+  FuzzerPassSwapBranchConditionalOperands(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassSwapBranchConditionalOperands() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
index f09943f..16cbf1a 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
@@ -14,12 +14,34 @@
 
 #include "source/fuzz/fuzzer_util.h"
 
+#include <algorithm>
+#include <unordered_set>
+
 #include "source/opt/build_module.h"
 
 namespace spvtools {
 namespace fuzz {
 
 namespace fuzzerutil {
+namespace {
+
+uint32_t MaybeGetOpConstant(opt::IRContext* ir_context,
+                            const TransformationContext& transformation_context,
+                            const std::vector<uint32_t>& words,
+                            uint32_t type_id, bool is_irrelevant) {
+  for (const auto& inst : ir_context->types_values()) {
+    if (inst.opcode() == SpvOpConstant && inst.type_id() == type_id &&
+        inst.GetInOperand(0).words == words &&
+        transformation_context.GetFactManager()->IdIsIrrelevant(
+            inst.result_id()) == is_irrelevant) {
+      return inst.result_id();
+    }
+  }
+
+  return 0;
+}
+
+}  // namespace
 
 bool IsFreshId(opt::IRContext* context, uint32_t id) {
   return !context->get_def_use_mgr()->GetDef(id);
@@ -109,22 +131,9 @@
   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(
     opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
-    bool condition_value,
+    uint32_t bool_id,
     const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids) {
   assert(PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) &&
          "Precondition on phi_ids is not satisfied");
@@ -132,10 +141,13 @@
          "Precondition on terminator of bb_from is not satisfied");
 
   // Get the id of the boolean constant to be used as the condition.
-  uint32_t bool_id = MaybeGetBoolConstantId(context, condition_value);
-  assert(
-      bool_id &&
-      "Precondition that condition value must be available is not satisfied");
+  auto condition_inst = context->get_def_use_mgr()->GetDef(bool_id);
+  assert(condition_inst &&
+         (condition_inst->opcode() == SpvOpConstantTrue ||
+          condition_inst->opcode() == SpvOpConstantFalse) &&
+         "|bool_id| is invalid");
+
+  auto condition_value = condition_inst->opcode() == SpvOpConstantTrue;
 
   const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to);
   auto successor = bb_from->terminator()->GetSingleWordInOperand(0);
@@ -166,6 +178,25 @@
   }
 }
 
+bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id,
+                     uint32_t loop_header_id) {
+  auto block = context->cfg()->block(block_id);
+  auto loop_header = context->cfg()->block(loop_header_id);
+
+  // |block| and |loop_header| must be defined, |loop_header| must be in fact
+  // loop header and |block| must branch to it.
+  if (!(block && loop_header && loop_header->IsLoopHeader() &&
+        block->IsSuccessor(loop_header))) {
+    return false;
+  }
+
+  // |block_id| must be reachable and be dominated by |loop_header|.
+  opt::DominatorAnalysis* dominator_analysis =
+      context->GetDominatorAnalysis(loop_header->GetParent());
+  return dominator_analysis->IsReachable(block_id) &&
+         dominator_analysis->Dominates(loop_header_id, block_id);
+}
+
 bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
                                     uint32_t maybe_loop_header_id) {
   // We deem a block to be part of a loop's continue construct if the loop's
@@ -217,7 +248,9 @@
   return opcode == SpvOpPhi || instruction_in_block->opcode() != SpvOpPhi;
 }
 
-bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) {
+bool CanMakeSynonymOf(opt::IRContext* ir_context,
+                      const TransformationContext& transformation_context,
+                      opt::Instruction* inst) {
   if (inst->opcode() == SpvOpSampledImage) {
     // The SPIR-V data rules say that only very specific instructions may
     // may consume the result id of an OpSampledImage, and this excludes the
@@ -228,6 +261,11 @@
     // We can only make a synonym of an instruction that generates an id.
     return false;
   }
+  if (transformation_context.GetFactManager()->IdIsIrrelevant(
+          inst->result_id())) {
+    // An irrelevant id can't be a synonym of anything.
+    return false;
+  }
   if (!inst->type_id()) {
     // We can only make a synonym of an instruction that has a type.
     return false;
@@ -335,6 +373,28 @@
   return array_length_constant->GetU32();
 }
 
+uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst,
+                                   opt::IRContext* ir_context) {
+  switch (composite_type_inst.opcode()) {
+    case SpvOpTypeArray:
+      return fuzzerutil::GetArraySize(composite_type_inst, ir_context);
+    case SpvOpTypeMatrix:
+    case SpvOpTypeVector:
+      return composite_type_inst.GetSingleWordInOperand(1);
+    case SpvOpTypeStruct: {
+      return fuzzerutil::GetNumberOfStructMembers(composite_type_inst);
+    }
+    case SpvOpTypeRuntimeArray:
+      assert(false &&
+             "GetBoundForCompositeIndex should not be invoked with an "
+             "OpTypeRuntimeArray, which does not have a static bound.");
+      return 0;
+    default:
+      assert(false && "Unknown composite type.");
+      return 0;
+  }
+}
+
 bool IsValid(opt::IRContext* context, spv_validator_options validator_options) {
   std::vector<uint32_t> binary;
   context->module()->ToBinary(&binary, false);
@@ -543,6 +603,12 @@
   return 0;
 }
 
+uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst,
+                                        uint32_t absolute_index) {
+  // Subtract the number of non-input operands from the index
+  return absolute_index - inst.NumOperands() + inst.NumInOperands();
+}
+
 bool IsNullConstantSupported(const opt::analysis::Type& type) {
   return type.AsBool() || type.AsInteger() || type.AsFloat() ||
          type.AsMatrix() || type.AsVector() || type.AsArray() ||
@@ -550,6 +616,663 @@
          type.AsDeviceEvent() || type.AsReserveId() || type.AsQueue();
 }
 
+bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
+    const opt::IRContext* ir_context) {
+  // TODO(afd): We capture the universal environments for which this requirement
+  //  holds.  The check should be refined on demand for other target
+  //  environments.
+  switch (ir_context->grammar().target_env()) {
+    case SPV_ENV_UNIVERSAL_1_0:
+    case SPV_ENV_UNIVERSAL_1_1:
+    case SPV_ENV_UNIVERSAL_1_2:
+    case SPV_ENV_UNIVERSAL_1_3:
+      return false;
+    default:
+      return true;
+  }
+}
+
+void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id) {
+  if (GlobalVariablesMustBeDeclaredInEntryPointInterfaces(context)) {
+    // Conservatively add this global to the interface of every entry point in
+    // the module.  This means that the global is available for other
+    // transformations to use.
+    //
+    // A downside of this is that the global will be in the interface even if it
+    // ends up never being used.
+    //
+    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3111) revisit
+    //  this if a more thorough approach to entry point interfaces is taken.
+    for (auto& entry_point : context->module()->entry_points()) {
+      entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {id}});
+    }
+  }
+}
+
+void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
+                       uint32_t type_id, SpvStorageClass storage_class,
+                       uint32_t initializer_id) {
+  // Check various preconditions.
+  assert(result_id != 0 && "Result id can't be 0");
+
+  assert((storage_class == SpvStorageClassPrivate ||
+          storage_class == SpvStorageClassWorkgroup) &&
+         "Variable's storage class must be either Private or Workgroup");
+
+  auto* type_inst = context->get_def_use_mgr()->GetDef(type_id);
+  (void)type_inst;  // Variable becomes unused in release mode.
+  assert(type_inst && type_inst->opcode() == SpvOpTypePointer &&
+         GetStorageClassFromPointerType(type_inst) == storage_class &&
+         "Variable's type is invalid");
+
+  if (storage_class == SpvStorageClassWorkgroup) {
+    assert(initializer_id == 0);
+  }
+
+  if (initializer_id != 0) {
+    const auto* constant_inst =
+        context->get_def_use_mgr()->GetDef(initializer_id);
+    (void)constant_inst;  // Variable becomes unused in release mode.
+    assert(constant_inst && spvOpcodeIsConstant(constant_inst->opcode()) &&
+           GetPointeeTypeIdFromPointerType(type_inst) ==
+               constant_inst->type_id() &&
+           "Initializer is invalid");
+  }
+
+  opt::Instruction::OperandList operands = {
+      {SPV_OPERAND_TYPE_STORAGE_CLASS, {static_cast<uint32_t>(storage_class)}}};
+
+  if (initializer_id) {
+    operands.push_back({SPV_OPERAND_TYPE_ID, {initializer_id}});
+  }
+
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context, SpvOpVariable, type_id, result_id, std::move(operands)));
+
+  AddVariableIdToEntryPointInterfaces(context, result_id);
+  UpdateModuleIdBound(context, result_id);
+}
+
+void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
+                      uint32_t type_id, uint32_t function_id,
+                      uint32_t initializer_id) {
+  // Check various preconditions.
+  assert(result_id != 0 && "Result id can't be 0");
+
+  auto* type_inst = context->get_def_use_mgr()->GetDef(type_id);
+  (void)type_inst;  // Variable becomes unused in release mode.
+  assert(type_inst && type_inst->opcode() == SpvOpTypePointer &&
+         GetStorageClassFromPointerType(type_inst) == SpvStorageClassFunction &&
+         "Variable's type is invalid");
+
+  const auto* constant_inst =
+      context->get_def_use_mgr()->GetDef(initializer_id);
+  (void)constant_inst;  // Variable becomes unused in release mode.
+  assert(constant_inst && spvOpcodeIsConstant(constant_inst->opcode()) &&
+         GetPointeeTypeIdFromPointerType(type_inst) ==
+             constant_inst->type_id() &&
+         "Initializer is invalid");
+
+  auto* function = FindFunction(context, function_id);
+  assert(function && "Function id is invalid");
+
+  function->begin()->begin()->InsertBefore(MakeUnique<opt::Instruction>(
+      context, SpvOpVariable, type_id, result_id,
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
+          {SPV_OPERAND_TYPE_ID, {initializer_id}}}));
+
+  UpdateModuleIdBound(context, result_id);
+}
+
+bool HasDuplicates(const std::vector<uint32_t>& arr) {
+  return std::unordered_set<uint32_t>(arr.begin(), arr.end()).size() !=
+         arr.size();
+}
+
+bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
+                          uint32_t hi) {
+  if (arr.empty()) {
+    return lo > hi;
+  }
+
+  if (HasDuplicates(arr)) {
+    return false;
+  }
+
+  auto min_max = std::minmax_element(arr.begin(), arr.end());
+  return arr.size() == hi - lo + 1 && *min_max.first == lo &&
+         *min_max.second == hi;
+}
+
+std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
+                                             uint32_t function_id) {
+  auto* function = FindFunction(ir_context, function_id);
+  assert(function && "|function_id| is invalid");
+
+  std::vector<opt::Instruction*> result;
+  function->ForEachParam(
+      [&result](opt::Instruction* inst) { result.push_back(inst); });
+
+  return result;
+}
+
+std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
+                                          uint32_t function_id) {
+  assert(FindFunction(ir_context, function_id) &&
+         "|function_id| is not a result id of a function");
+
+  std::vector<opt::Instruction*> result;
+  ir_context->get_def_use_mgr()->ForEachUser(
+      function_id, [&result, function_id](opt::Instruction* inst) {
+        if (inst->opcode() == SpvOpFunctionCall &&
+            inst->GetSingleWordInOperand(0) == function_id) {
+          result.push_back(inst);
+        }
+      });
+
+  return result;
+}
+
+opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
+                                          uint32_t param_id) {
+  auto* param_inst = ir_context->get_def_use_mgr()->GetDef(param_id);
+  assert(param_inst && "Parameter id is invalid");
+
+  for (auto& function : *ir_context->module()) {
+    if (InstructionIsFunctionParameter(param_inst, &function)) {
+      return &function;
+    }
+  }
+
+  return nullptr;
+}
+
+uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id,
+                            uint32_t new_function_type_result_id,
+                            uint32_t return_type_id,
+                            const std::vector<uint32_t>& parameter_type_ids) {
+  // Check some initial constraints.
+  assert(ir_context->get_type_mgr()->GetType(return_type_id) &&
+         "Return type is invalid");
+  for (auto id : parameter_type_ids) {
+    const auto* type = ir_context->get_type_mgr()->GetType(id);
+    (void)type;  // Make compilers happy in release mode.
+    // Parameters can't be OpTypeVoid.
+    assert(type && !type->AsVoid() && "Parameter has invalid type");
+  }
+
+  auto* function = FindFunction(ir_context, function_id);
+  assert(function && "|function_id| is invalid");
+
+  auto* old_function_type = GetFunctionType(ir_context, function);
+  assert(old_function_type && "Function has invalid type");
+
+  std::vector<uint32_t> operand_ids = {return_type_id};
+  operand_ids.insert(operand_ids.end(), parameter_type_ids.begin(),
+                     parameter_type_ids.end());
+
+  if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1 &&
+      FindFunctionType(ir_context, operand_ids) == 0) {
+    // We can change |old_function_type| only if it's used once in the module
+    // and we are certain we won't create a duplicate as a result of the change.
+
+    // Update |old_function_type| in-place.
+    opt::Instruction::OperandList operands;
+    for (auto id : operand_ids) {
+      operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
+    }
+
+    old_function_type->SetInOperands(std::move(operands));
+
+    // |operands| may depend on result ids defined below the |old_function_type|
+    // in the module.
+    old_function_type->RemoveFromList();
+    ir_context->AddType(std::unique_ptr<opt::Instruction>(old_function_type));
+    return old_function_type->result_id();
+  } else {
+    // We can't modify the |old_function_type| so we have to either use an
+    // existing one or create a new one.
+    auto type_id = FindOrCreateFunctionType(
+        ir_context, new_function_type_result_id, operand_ids);
+
+    if (type_id != old_function_type->result_id()) {
+      function->DefInst().SetInOperand(1, {type_id});
+
+      // DefUseManager hasn't been updated yet, so if the following condition is
+      // true, then |old_function_type| will have no users when this function
+      // returns. We might as well remove it.
+      if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
+        old_function_type->RemoveFromList();
+        delete old_function_type;
+      }
+    }
+
+    return type_id;
+  }
+}
+
+void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id,
+                     const std::vector<uint32_t>& type_ids) {
+  assert(result_id != 0 && "Result id can't be 0");
+  assert(!type_ids.empty() &&
+         "OpTypeFunction always has at least one operand - function's return "
+         "type");
+  assert(IsNonFunctionTypeId(ir_context, type_ids[0]) &&
+         "Return type must not be a function");
+
+  for (size_t i = 1; i < type_ids.size(); ++i) {
+    const auto* param_type = ir_context->get_type_mgr()->GetType(type_ids[i]);
+    (void)param_type;  // Make compiler happy in release mode.
+    assert(param_type && !param_type->AsVoid() && !param_type->AsFunction() &&
+           "Function parameter can't have a function or void type");
+  }
+
+  opt::Instruction::OperandList operands;
+  operands.reserve(type_ids.size());
+  for (auto id : type_ids) {
+    operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
+  }
+
+  ir_context->AddType(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeFunction, 0, result_id, std::move(operands)));
+
+  UpdateModuleIdBound(ir_context, result_id);
+}
+
+uint32_t FindOrCreateFunctionType(opt::IRContext* ir_context,
+                                  uint32_t result_id,
+                                  const std::vector<uint32_t>& type_ids) {
+  if (auto existing_id = FindFunctionType(ir_context, type_ids)) {
+    return existing_id;
+  }
+  AddFunctionType(ir_context, result_id, type_ids);
+  return result_id;
+}
+
+uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width,
+                             bool is_signed) {
+  opt::analysis::Integer type(width, is_signed);
+  return ir_context->get_type_mgr()->GetId(&type);
+}
+
+uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width) {
+  opt::analysis::Float type(width);
+  return ir_context->get_type_mgr()->GetId(&type);
+}
+
+uint32_t MaybeGetBoolType(opt::IRContext* ir_context) {
+  opt::analysis::Bool type;
+  return ir_context->get_type_mgr()->GetId(&type);
+}
+
+uint32_t MaybeGetVectorType(opt::IRContext* ir_context,
+                            uint32_t component_type_id,
+                            uint32_t element_count) {
+  const auto* component_type =
+      ir_context->get_type_mgr()->GetType(component_type_id);
+  assert(component_type &&
+         (component_type->AsInteger() || component_type->AsFloat() ||
+          component_type->AsBool()) &&
+         "|component_type_id| is invalid");
+  assert(element_count >= 2 && element_count <= 4 &&
+         "Precondition: component count must be in range [2, 4].");
+  opt::analysis::Vector type(component_type, element_count);
+  return ir_context->get_type_mgr()->GetId(&type);
+}
+
+uint32_t MaybeGetStructType(opt::IRContext* ir_context,
+                            const std::vector<uint32_t>& component_type_ids) {
+  std::vector<const opt::analysis::Type*> component_types;
+  component_types.reserve(component_type_ids.size());
+
+  for (auto type_id : component_type_ids) {
+    const auto* component_type = ir_context->get_type_mgr()->GetType(type_id);
+    assert(component_type && !component_type->AsFunction() &&
+           "Component type is invalid");
+    component_types.push_back(component_type);
+  }
+
+  opt::analysis::Struct type(component_types);
+  return ir_context->get_type_mgr()->GetId(&type);
+}
+
+uint32_t MaybeGetZeroConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    uint32_t scalar_or_composite_type_id, bool is_irrelevant) {
+  const auto* type =
+      ir_context->get_type_mgr()->GetType(scalar_or_composite_type_id);
+  assert(type && "|scalar_or_composite_type_id| is invalid");
+
+  switch (type->kind()) {
+    case opt::analysis::Type::kBool:
+      return MaybeGetBoolConstant(ir_context, transformation_context, false,
+                                  is_irrelevant);
+    case opt::analysis::Type::kFloat:
+    case opt::analysis::Type::kInteger: {
+      std::vector<uint32_t> words = {0};
+      if ((type->AsInteger() && type->AsInteger()->width() > 32) ||
+          (type->AsFloat() && type->AsFloat()->width() > 32)) {
+        words.push_back(0);
+      }
+
+      return MaybeGetScalarConstant(ir_context, transformation_context, words,
+                                    scalar_or_composite_type_id, is_irrelevant);
+    }
+    case opt::analysis::Type::kStruct: {
+      std::vector<uint32_t> component_ids;
+      for (const auto* component_type : type->AsStruct()->element_types()) {
+        auto component_type_id =
+            ir_context->get_type_mgr()->GetId(component_type);
+        assert(component_type_id && "Component type is invalid");
+
+        auto component_id =
+            MaybeGetZeroConstant(ir_context, transformation_context,
+                                 component_type_id, is_irrelevant);
+        if (component_id == 0 && is_irrelevant) {
+          // Irrelevant constants can use either relevant or irrelevant
+          // constituents.
+          component_id = MaybeGetZeroConstant(
+              ir_context, transformation_context, component_type_id, false);
+        }
+
+        if (component_id == 0) {
+          return 0;
+        }
+
+        component_ids.push_back(component_id);
+      }
+
+      return MaybeGetCompositeConstant(
+          ir_context, transformation_context, component_ids,
+          scalar_or_composite_type_id, is_irrelevant);
+    }
+    case opt::analysis::Type::kMatrix:
+    case opt::analysis::Type::kVector: {
+      const auto* component_type = type->AsVector()
+                                       ? type->AsVector()->element_type()
+                                       : type->AsMatrix()->element_type();
+      auto component_type_id =
+          ir_context->get_type_mgr()->GetId(component_type);
+      assert(component_type_id && "Component type is invalid");
+
+      auto component_id = MaybeGetZeroConstant(
+          ir_context, transformation_context, component_type_id, is_irrelevant);
+
+      if (component_id == 0 && is_irrelevant) {
+        // Irrelevant constants can use either relevant or irrelevant
+        // constituents.
+        component_id = MaybeGetZeroConstant(ir_context, transformation_context,
+                                            component_type_id, false);
+      }
+
+      if (component_id == 0) {
+        return 0;
+      }
+
+      auto component_count = type->AsVector()
+                                 ? type->AsVector()->element_count()
+                                 : type->AsMatrix()->element_count();
+      return MaybeGetCompositeConstant(
+          ir_context, transformation_context,
+          std::vector<uint32_t>(component_count, component_id),
+          scalar_or_composite_type_id, is_irrelevant);
+    }
+    case opt::analysis::Type::kArray: {
+      auto component_type_id =
+          ir_context->get_type_mgr()->GetId(type->AsArray()->element_type());
+      assert(component_type_id && "Component type is invalid");
+
+      auto component_id = MaybeGetZeroConstant(
+          ir_context, transformation_context, component_type_id, is_irrelevant);
+
+      if (component_id == 0 && is_irrelevant) {
+        component_id = MaybeGetZeroConstant(ir_context, transformation_context,
+                                            component_type_id, false);
+      }
+
+      if (component_id == 0) {
+        return 0;
+      }
+
+      auto type_id = ir_context->get_type_mgr()->GetId(type);
+      assert(type_id && "|type| is invalid");
+
+      const auto* type_inst = ir_context->get_def_use_mgr()->GetDef(type_id);
+      assert(type_inst && "Array's type id is invalid");
+
+      return MaybeGetCompositeConstant(
+          ir_context, transformation_context,
+          std::vector<uint32_t>(GetArraySize(*type_inst, ir_context),
+                                component_id),
+          scalar_or_composite_type_id, is_irrelevant);
+    }
+    default:
+      assert(false && "Type is not supported");
+      return 0;
+  }
+}
+
+uint32_t MaybeGetScalarConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    const std::vector<uint32_t>& words, uint32_t scalar_type_id,
+    bool is_irrelevant) {
+  const auto* type = ir_context->get_type_mgr()->GetType(scalar_type_id);
+  assert(type && "|scalar_type_id| is invalid");
+
+  if (const auto* int_type = type->AsInteger()) {
+    return MaybeGetIntegerConstant(ir_context, transformation_context, words,
+                                   int_type->width(), int_type->IsSigned(),
+                                   is_irrelevant);
+  } else if (const auto* float_type = type->AsFloat()) {
+    return MaybeGetFloatConstant(ir_context, transformation_context, words,
+                                 float_type->width(), is_irrelevant);
+  } else {
+    assert(type->AsBool() && words.size() == 1 &&
+           "|scalar_type_id| doesn't represent a scalar type");
+    return MaybeGetBoolConstant(ir_context, transformation_context, words[0],
+                                is_irrelevant);
+  }
+}
+
+uint32_t MaybeGetCompositeConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    const std::vector<uint32_t>& component_ids, uint32_t composite_type_id,
+    bool is_irrelevant) {
+  const auto* type = ir_context->get_type_mgr()->GetType(composite_type_id);
+  (void)type;  // Make compilers happy in release mode.
+  assert(type &&
+         (type->AsArray() || type->AsStruct() || type->AsVector() ||
+          type->AsMatrix()) &&
+         "|composite_type_id| is invalid");
+
+  for (const auto& inst : ir_context->types_values()) {
+    if (inst.opcode() == SpvOpConstantComposite &&
+        inst.type_id() == composite_type_id &&
+        transformation_context.GetFactManager()->IdIsIrrelevant(
+            inst.result_id()) == is_irrelevant &&
+        inst.NumInOperands() == component_ids.size()) {
+      bool is_match = true;
+
+      for (uint32_t i = 0; i < inst.NumInOperands(); ++i) {
+        if (inst.GetSingleWordInOperand(i) != component_ids[i]) {
+          is_match = false;
+          break;
+        }
+      }
+
+      if (is_match) {
+        return inst.result_id();
+      }
+    }
+  }
+
+  return 0;
+}
+
+uint32_t MaybeGetIntegerConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
+    bool is_irrelevant) {
+  if (auto type_id = MaybeGetIntegerType(ir_context, width, is_signed)) {
+    return MaybeGetOpConstant(ir_context, transformation_context, words,
+                              type_id, is_irrelevant);
+  }
+
+  return 0;
+}
+
+uint32_t MaybeGetIntegerConstantFromValueAndType(opt::IRContext* ir_context,
+                                                 uint32_t value,
+                                                 uint32_t int_type_id) {
+  auto int_type_inst = ir_context->get_def_use_mgr()->GetDef(int_type_id);
+
+  assert(int_type_inst && "The given type id must exist.");
+
+  auto int_type = ir_context->get_type_mgr()
+                      ->GetType(int_type_inst->result_id())
+                      ->AsInteger();
+
+  assert(int_type && int_type->width() == 32 &&
+         "The given type id must correspond to an 32-bit integer type.");
+
+  opt::analysis::IntConstant constant(int_type, {value});
+
+  // Check that the constant exists in the module.
+  if (!ir_context->get_constant_mgr()->FindConstant(&constant)) {
+    return 0;
+  }
+
+  return ir_context->get_constant_mgr()
+      ->GetDefiningInstruction(&constant)
+      ->result_id();
+}
+
+uint32_t MaybeGetFloatConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    const std::vector<uint32_t>& words, uint32_t width, bool is_irrelevant) {
+  if (auto type_id = MaybeGetFloatType(ir_context, width)) {
+    return MaybeGetOpConstant(ir_context, transformation_context, words,
+                              type_id, is_irrelevant);
+  }
+
+  return 0;
+}
+
+uint32_t MaybeGetBoolConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context, bool value,
+    bool is_irrelevant) {
+  if (auto type_id = MaybeGetBoolType(ir_context)) {
+    for (const auto& inst : ir_context->types_values()) {
+      if (inst.opcode() == (value ? SpvOpConstantTrue : SpvOpConstantFalse) &&
+          inst.type_id() == type_id &&
+          transformation_context.GetFactManager()->IdIsIrrelevant(
+              inst.result_id()) == is_irrelevant) {
+        return inst.result_id();
+      }
+    }
+  }
+
+  return 0;
+}
+
+void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id,
+                    uint32_t width, bool is_signed) {
+  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeInt, 0, result_id,
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_LITERAL_INTEGER, {width}},
+          {SPV_OPERAND_TYPE_LITERAL_INTEGER, {is_signed ? 1u : 0u}}}));
+
+  UpdateModuleIdBound(ir_context, result_id);
+}
+
+void AddFloatType(opt::IRContext* ir_context, uint32_t result_id,
+                  uint32_t width) {
+  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeFloat, 0, result_id,
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_LITERAL_INTEGER, {width}}}));
+
+  UpdateModuleIdBound(ir_context, result_id);
+}
+
+void AddVectorType(opt::IRContext* ir_context, uint32_t result_id,
+                   uint32_t component_type_id, uint32_t element_count) {
+  const auto* component_type =
+      ir_context->get_type_mgr()->GetType(component_type_id);
+  (void)component_type;  // Make compiler happy in release mode.
+  assert(component_type &&
+         (component_type->AsInteger() || component_type->AsFloat() ||
+          component_type->AsBool()) &&
+         "|component_type_id| is invalid");
+  assert(element_count >= 2 && element_count <= 4 &&
+         "Precondition: component count must be in range [2, 4].");
+  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeVector, 0, result_id,
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_ID, {component_type_id}},
+          {SPV_OPERAND_TYPE_LITERAL_INTEGER, {element_count}}}));
+
+  UpdateModuleIdBound(ir_context, result_id);
+}
+
+void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
+                   const std::vector<uint32_t>& component_type_ids) {
+  opt::Instruction::OperandList operands;
+  operands.reserve(component_type_ids.size());
+
+  for (auto type_id : component_type_ids) {
+    const auto* type = ir_context->get_type_mgr()->GetType(type_id);
+    (void)type;  // Make compiler happy in release mode.
+    assert(type && !type->AsFunction() && "Component's type id is invalid");
+    operands.push_back({SPV_OPERAND_TYPE_ID, {type_id}});
+  }
+
+  ir_context->AddType(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeStruct, 0, result_id, std::move(operands)));
+
+  UpdateModuleIdBound(ir_context, result_id);
+}
+
+bool TypesAreEqualUpToSign(opt::IRContext* ir_context, uint32_t type1_id,
+                           uint32_t type2_id) {
+  if (type1_id == type2_id) {
+    return true;
+  }
+
+  auto type1 = ir_context->get_type_mgr()->GetType(type1_id);
+  auto type2 = ir_context->get_type_mgr()->GetType(type2_id);
+
+  // Integer scalar types must have the same width
+  if (type1->AsInteger() && type2->AsInteger()) {
+    return type1->AsInteger()->width() == type2->AsInteger()->width();
+  }
+
+  // Integer vector types must have the same number of components and their
+  // component types must be integers with the same width.
+  if (type1->AsVector() && type2->AsVector()) {
+    auto component_type1 = type1->AsVector()->element_type()->AsInteger();
+    auto component_type2 = type2->AsVector()->element_type()->AsInteger();
+
+    // Only check the component count and width if they are integer.
+    if (component_type1 && component_type2) {
+      return type1->AsVector()->element_count() ==
+                 type2->AsVector()->element_count() &&
+             component_type1->width() == component_type2->width();
+    }
+  }
+
+  // In all other cases, the types cannot be considered equal.
+  return false;
+}
+
 }  // 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 bccd1d0..af8ff16 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
@@ -53,21 +53,25 @@
     opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
     const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
 
-// 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.
+// Requires that |bool_id| is a valid result id of either OpConstantTrue or
+// OpConstantFalse, that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids)
+// holds, and that bb_from ends with "OpBranch %some_block".  Turns OpBranch
+// 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. |condition_value| above is equal to |true| if |bool_id| is a result id
+// of an OpConstantTrue instruction.
 void AddUnreachableEdgeAndUpdateOpPhis(
     opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
-    bool condition_value,
+    uint32_t bool_id,
     const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
 
+// Returns true if and only if |loop_header_id| is a loop header and
+// |block_id| is a reachable block branching to and dominated by
+// |loop_header_id|.
+bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id,
+                     uint32_t loop_header_id);
+
 // Returns true if and only if |maybe_loop_header_id| is a loop header and
 // |block_id| is in the continue construct of the associated loop.
 bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
@@ -89,7 +93,11 @@
     SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block);
 
 // Determines whether it is OK to make a synonym of |inst|.
-bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst);
+// |transformation_context| is used to verify that the result id of |inst|
+// does not participate in IdIsIrrelevant fact.
+bool CanMakeSynonymOf(opt::IRContext* ir_context,
+                      const TransformationContext& transformation_context,
+                      opt::Instruction* inst);
 
 // Determines whether the given type is a composite; that is: an array, matrix,
 // struct or vector.
@@ -133,6 +141,13 @@
 uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
                       opt::IRContext* context);
 
+// 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. |composite_type_inst| must be the type of a composite.
+uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst,
+                                   opt::IRContext* ir_context);
+
 // Returns true if and only if |context| is valid, according to the validator
 // instantiated with |validator_options|.
 bool IsValid(opt::IRContext* context, spv_validator_options validator_options);
@@ -211,10 +226,243 @@
 uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
                              SpvStorageClass storage_class);
 
+// Given an instruction |inst| and an operand absolute index |absolute_index|,
+// returns the index of the operand restricted to the input operands.
+uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst,
+                                        uint32_t absolute_index);
+
 // Returns true if and only if |type| is one of the types for which it is legal
 // to have an OpConstantNull value.
 bool IsNullConstantSupported(const opt::analysis::Type& type);
 
+// Returns true if and only if the SPIR-V version being used requires that
+// global variables accessed in the static call graph of an entry point need
+// to be listed in that entry point's interface.
+bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
+    const opt::IRContext* context);
+
+// Adds |id| into the interface of every entry point of the shader.
+// Does nothing if SPIR-V doesn't require global variables, that are accessed
+// from an entry point function, to be listed in that function's interface.
+void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id);
+
+// Adds a global variable with storage class |storage_class| to the module, with
+// type |type_id| and either no initializer or |initializer_id| as an
+// initializer, depending on whether |initializer_id| is 0. The global variable
+// has result id |result_id|. Updates module's id bound to accommodate for
+// |result_id|.
+//
+// - |type_id| must be the id of a pointer type with the same storage class as
+//   |storage_class|.
+// - |storage_class| must be Private or Workgroup.
+// - |initializer_id| must be 0 if |storage_class| is Workgroup, and otherwise
+//   may either be 0 or the id of a constant whose type is the pointee type of
+//   |type_id|.
+void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
+                       uint32_t type_id, SpvStorageClass storage_class,
+                       uint32_t initializer_id);
+
+// Adds an instruction to the start of |function_id|, of the form:
+//   |result_id| = OpVariable |type_id| Function |initializer_id|.
+// Updates module's id bound to accommodate for |result_id|.
+//
+// - |type_id| must be the id of a pointer type with Function storage class.
+// - |initializer_id| must be the id of a constant with the same type as the
+//   pointer's pointee type.
+// - |function_id| must be the id of a function.
+void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
+                      uint32_t type_id, uint32_t function_id,
+                      uint32_t initializer_id);
+
+// Returns true if the vector |arr| has duplicates.
+bool HasDuplicates(const std::vector<uint32_t>& arr);
+
+// Checks that the given vector |arr| contains a permutation of a range
+// [lo, hi]. That being said, all elements in the range are present without
+// duplicates. If |arr| is empty, returns true iff |lo > hi|.
+bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
+                          uint32_t hi);
+
+// Returns OpFunctionParameter instructions corresponding to the function
+// with result id |function_id|.
+std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
+                                             uint32_t function_id);
+
+// Returns all OpFunctionCall instructions that call a function with result id
+// |function_id|.
+std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
+                                          uint32_t function_id);
+
+// Returns a function that contains OpFunctionParameter instruction with result
+// id |param_id|. Returns nullptr if the module has no such function.
+opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
+                                          uint32_t param_id);
+
+// Changes the type of function |function_id| so that its return type is
+// |return_type_id| and its parameters' types are |parameter_type_ids|. If a
+// suitable function type already exists in the module, it is used, otherwise
+// |new_function_type_result_id| is used as the result id of a suitable new
+// function type instruction. If the old type of the function doesn't have any
+// more users, it is removed from the module. Returns the result id of the
+// OpTypeFunction instruction that is used as a type of the function with
+// |function_id|.
+uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id,
+                            uint32_t new_function_type_result_id,
+                            uint32_t return_type_id,
+                            const std::vector<uint32_t>& parameter_type_ids);
+
+// Creates new OpTypeFunction instruction in the module. |type_ids| may not be
+// empty. It may not contain result ids of OpTypeFunction instructions.
+// |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|.
+// |result_id| may not equal to 0. Updates module's id bound to accommodate for
+// |result_id|.
+void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id,
+                     const std::vector<uint32_t>& type_ids);
+
+// Returns a result id of an OpTypeFunction instruction in the module. Creates a
+// new instruction if required and returns |result_id|. type_ids| may not be
+// empty. It may not contain result ids of OpTypeFunction instructions.
+// |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|.
+// |result_id| must not be equal to 0. Updates module's id bound to accommodate
+// for |result_id|.
+uint32_t FindOrCreateFunctionType(opt::IRContext* ir_context,
+                                  uint32_t result_id,
+                                  const std::vector<uint32_t>& type_ids);
+
+// Returns a result id of an OpTypeInt instruction if present. Returns 0
+// otherwise.
+uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width,
+                             bool is_signed);
+
+// Returns a result id of an OpTypeFloat instruction if present. Returns 0
+// otherwise.
+uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width);
+
+// Returns a result id of an OpTypeBool instruction if present. Returns 0
+// otherwise.
+uint32_t MaybeGetBoolType(opt::IRContext* ir_context);
+
+// Returns a result id of an OpTypeVector instruction if present. Returns 0
+// otherwise. |component_type_id| must be a valid result id of an OpTypeInt,
+// OpTypeFloat or OpTypeBool instruction in the module. |element_count| must be
+// in the range [2, 4].
+uint32_t MaybeGetVectorType(opt::IRContext* ir_context,
+                            uint32_t component_type_id, uint32_t element_count);
+
+// Returns a result id of an OpTypeStruct instruction if present. Returns 0
+// otherwise. |component_type_ids| may not contain a result id of an
+// OpTypeFunction.
+uint32_t MaybeGetStructType(opt::IRContext* ir_context,
+                            const std::vector<uint32_t>& component_type_ids);
+
+// Recursive definition is the following:
+// - if |scalar_or_composite_type_id| is a result id of a scalar type - returns
+//   a result id of the following constants (depending on the type): int -> 0,
+//   float -> 0.0, bool -> false.
+// - otherwise, returns a result id of an OpConstantComposite instruction.
+//   Every component of the composite constant is looked up by calling this
+//   function with the type id of that component.
+// Returns 0 if no such instruction is present in the module.
+// The returned id either participates in IdIsIrrelevant fact or not, depending
+// on the |is_irrelevant| parameter.
+uint32_t MaybeGetZeroConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    uint32_t scalar_or_composite_type_id, bool is_irrelevant);
+
+// Returns the result id of an OpConstant instruction. |scalar_type_id| must be
+// a result id of a scalar type (i.e. int, float or bool). Returns 0 if no such
+// instruction is present in the module. The returned id either participates in
+// IdIsIrrelevant fact or not, depending on the |is_irrelevant| parameter.
+uint32_t MaybeGetScalarConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    const std::vector<uint32_t>& words, uint32_t scalar_type_id,
+    bool is_irrelevant);
+
+// Returns the result id of an OpConstantComposite instruction.
+// |composite_type_id| must be a result id of a composite type (i.e. vector,
+// matrix, struct or array). Returns 0 if no such instruction is present in the
+// module. The returned id either participates in IdIsIrrelevant fact or not,
+// depending on the |is_irrelevant| parameter.
+uint32_t MaybeGetCompositeConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    const std::vector<uint32_t>& component_ids, uint32_t composite_type_id,
+    bool is_irrelevant);
+
+// Returns the result id of an OpConstant instruction of integral type.
+// Returns 0 if no such instruction or type is present in the module.
+// The returned id either participates in IdIsIrrelevant fact or not, depending
+// on the |is_irrelevant| parameter.
+uint32_t MaybeGetIntegerConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
+    bool is_irrelevant);
+
+// Returns the id of a 32-bit integer constant in the module with type
+// |int_type_id| and value |value|, or 0 if no such constant exists in the
+// module. |int_type_id| must exist in the module and it must correspond to a
+// 32-bit integer type.
+uint32_t MaybeGetIntegerConstantFromValueAndType(opt::IRContext* ir_context,
+                                                 uint32_t value,
+                                                 uint32_t int_type_id);
+
+// Returns the result id of an OpConstant instruction of floating-point type.
+// Returns 0 if no such instruction or type is present in the module.
+// The returned id either participates in IdIsIrrelevant fact or not, depending
+// on the |is_irrelevant| parameter.
+uint32_t MaybeGetFloatConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    const std::vector<uint32_t>& words, uint32_t width, bool is_irrelevant);
+
+// Returns the id of a boolean constant with value |value| if it exists in the
+// module, or 0 otherwise. The returned id either participates in IdIsIrrelevant
+// fact or not, depending on the |is_irrelevant| parameter.
+uint32_t MaybeGetBoolConstant(
+    opt::IRContext* context,
+    const TransformationContext& transformation_context, bool value,
+    bool is_irrelevant);
+
+// Creates a new OpTypeInt instruction in the module. Updates module's id bound
+// to accommodate for |result_id|.
+void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id,
+                    uint32_t width, bool is_signed);
+
+// Creates a new OpTypeFloat instruction in the module. Updates module's id
+// bound to accommodate for |result_id|.
+void AddFloatType(opt::IRContext* ir_context, uint32_t result_id,
+                  uint32_t width);
+
+// Creates a new OpTypeVector instruction in the module. |component_type_id|
+// must be a valid result id of an OpTypeInt, OpTypeFloat or OpTypeBool
+// instruction in the module. |element_count| must be in the range [2, 4].
+// Updates module's id bound to accommodate for |result_id|.
+void AddVectorType(opt::IRContext* ir_context, uint32_t result_id,
+                   uint32_t component_type_id, uint32_t element_count);
+
+// Creates a new OpTypeStruct instruction in the module. Updates module's id
+// bound to accommodate for |result_id|. |component_type_ids| may not contain
+// a result id of an OpTypeFunction.
+void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
+                   const std::vector<uint32_t>& component_type_ids);
+
+// Returns a bit pattern that represents a floating-point |value|.
+inline uint32_t FloatToWord(float value) {
+  uint32_t result;
+  memcpy(&result, &value, sizeof(uint32_t));
+  return result;
+}
+
+// Returns true if any of the following is true:
+// - |type1_id| and |type2_id| are the same id
+// - |type1_id| and |type2_id| refer to integer scalar or vector types, only
+//   differing by their signedness.
+bool TypesAreEqualUpToSign(opt::IRContext* ir_context, uint32_t type1_id,
+                           uint32_t type2_id);
+
 }  // namespace fuzzerutil
 
 }  // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/protobufs/spirvfuzz_protobufs.h b/third_party/SPIRV-Tools/source/fuzz/protobufs/spirvfuzz_protobufs.h
index b801626..26b8672 100644
--- a/third_party/SPIRV-Tools/source/fuzz/protobufs/spirvfuzz_protobufs.h
+++ b/third_party/SPIRV-Tools/source/fuzz/protobufs/spirvfuzz_protobufs.h
@@ -39,6 +39,7 @@
 // where warnings are ignored.
 
 #include "google/protobuf/util/json_util.h"
+#include "google/protobuf/util/message_differencer.h"
 #include "source/fuzz/protobufs/spvtoolsfuzz.pb.h"
 
 #if defined(__clang__)
diff --git a/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
index 775b2ad..3aa5675 100644
--- a/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -170,6 +170,7 @@
     FactFunctionIsLivesafe function_is_livesafe_fact = 4;
     FactPointeeValueIsIrrelevant pointee_value_is_irrelevant_fact = 5;
     FactIdEquation id_equation_fact = 6;
+    FactIdIsIrrelevant id_is_irrelevant = 7;
   }
 }
 
@@ -182,6 +183,7 @@
   // can be made to this block.
 
   uint32 block_id = 1;
+
 }
 
 message FactConstantUniform {
@@ -222,6 +224,7 @@
   // functions.
 
   uint32 function_id = 1;
+
 }
 
 message FactIdEquation {
@@ -249,6 +252,18 @@
 
 }
 
+message FactIdIsIrrelevant {
+
+  // Records a fact that |result_id| is irrelevant (i.e. it's usage doesn't
+  // change the semantics of the module). This implies that a use of this id
+  // can later be replaced with some other id of the same type, or the
+  // definition of |result_id| can be changed so that it yields a different value.
+
+  // An irrelevant id.
+  uint32 result_id = 1;
+
+}
+
 message FactPointeeValueIsIrrelevant {
 
   // Records the fact that value of the data pointed to by a pointer id does
@@ -258,6 +273,7 @@
 
   // A result id of pointer type
   uint32 pointer_id = 1;
+
 }
 
 message AccessChainClampingInfo {
@@ -341,40 +357,56 @@
     TransformationAddTypePointer add_type_pointer = 10;
     TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11;
     TransformationAddDeadContinue add_dead_continue = 12;
-    TransformationCopyObject copy_object = 13;
-    TransformationReplaceIdWithSynonym replace_id_with_synonym = 14;
-    TransformationSetSelectionControl set_selection_control = 15;
-    TransformationCompositeConstruct composite_construct = 16;
-    TransformationSetLoopControl set_loop_control = 17;
-    TransformationSetFunctionControl set_function_control = 18;
-    TransformationAddNoContractionDecoration add_no_contraction_decoration = 19;
-    TransformationSetMemoryOperandsMask set_memory_operands_mask = 20;
-    TransformationCompositeExtract composite_extract = 21;
-    TransformationVectorShuffle vector_shuffle = 22;
-    TransformationOutlineFunction outline_function = 23;
-    TransformationMergeBlocks merge_blocks = 24;
-    TransformationAddTypeVector add_type_vector = 25;
-    TransformationAddTypeArray add_type_array = 26;
-    TransformationAddTypeMatrix add_type_matrix = 27;
-    TransformationAddTypeStruct add_type_struct = 28;
-    TransformationAddTypeFunction add_type_function = 29;
-    TransformationAddConstantComposite add_constant_composite = 30;
-    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;
-    TransformationEquationInstruction equation_instruction = 40;
-    TransformationSwapCommutableOperands swap_commutable_operands = 41;
-    TransformationPermuteFunctionParameters permute_function_parameters = 42;
-    TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 43;
-    TransformationAddConstantNull add_constant_null = 44;
-    TransformationComputeDataSynonymFactClosure compute_data_synonym_fact_closure = 45;
-    TransformationAdjustBranchWeights adjust_branch_weights = 46;
+    TransformationReplaceIdWithSynonym replace_id_with_synonym = 13;
+    TransformationSetSelectionControl set_selection_control = 14;
+    TransformationCompositeConstruct composite_construct = 15;
+    TransformationSetLoopControl set_loop_control = 16;
+    TransformationSetFunctionControl set_function_control = 17;
+    TransformationAddNoContractionDecoration add_no_contraction_decoration = 18;
+    TransformationSetMemoryOperandsMask set_memory_operands_mask = 19;
+    TransformationCompositeExtract composite_extract = 20;
+    TransformationVectorShuffle vector_shuffle = 21;
+    TransformationOutlineFunction outline_function = 22;
+    TransformationMergeBlocks merge_blocks = 23;
+    TransformationAddTypeVector add_type_vector = 24;
+    TransformationAddTypeArray add_type_array = 25;
+    TransformationAddTypeMatrix add_type_matrix = 26;
+    TransformationAddTypeStruct add_type_struct = 27;
+    TransformationAddTypeFunction add_type_function = 28;
+    TransformationAddConstantComposite add_constant_composite = 29;
+    TransformationAddGlobalVariable add_global_variable = 30;
+    TransformationAddGlobalUndef add_global_undef = 31;
+    TransformationAddFunction add_function = 32;
+    TransformationAddDeadBlock add_dead_block = 33;
+    TransformationAddLocalVariable add_local_variable = 34;
+    TransformationLoad load = 35;
+    TransformationStore store = 36;
+    TransformationFunctionCall function_call = 37;
+    TransformationAccessChain access_chain = 38;
+    TransformationEquationInstruction equation_instruction = 39;
+    TransformationSwapCommutableOperands swap_commutable_operands = 40;
+    TransformationPermuteFunctionParameters permute_function_parameters = 41;
+    TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 42;
+    TransformationAddConstantNull add_constant_null = 43;
+    TransformationComputeDataSynonymFactClosure compute_data_synonym_fact_closure = 44;
+    TransformationAdjustBranchWeights adjust_branch_weights = 45;
+    TransformationPushIdThroughVariable push_id_through_variable = 46;
+    TransformationAddSpecConstantOp add_spec_constant_op = 47;
+    TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 48;
+    TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 49;
+    TransformationPermutePhiOperands permute_phi_operands = 50;
+    TransformationAddParameter add_parameter = 51;
+    TransformationAddCopyMemory add_copy_memory = 52;
+    TransformationInvertComparisonOperator invert_comparison_operator = 53;
+    TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 54;
+    TransformationReplaceParameterWithGlobal replace_parameter_with_global = 55;
+    TransformationRecordSynonymousConstants record_synonymous_constants = 56;
+    TransformationAddSynonym add_synonym = 57;
+    TransformationAddRelaxedDecoration add_relaxed_decoration = 58;
+    TransformationReplaceParamsWithStruct replace_params_with_struct = 59;
+    TransformationReplaceCopyObjectWithStoreLoad replace_copy_object_with_store_load = 60;
+    TransformationReplaceCopyMemoryWithLoadStore replace_copy_memory_with_load_store = 61;
+    TransformationReplaceLoadStoreWithCopyMemory replace_load_store_with_copy_memory = 62;
     // Add additional option using the next available number.
   }
 }
@@ -385,6 +417,13 @@
 
   // Adds an access chain instruction based on a given pointer and indices.
 
+  // When accessing a struct, the corresponding indices must be 32-bit integer constants.
+  // For any other composite, the indices can be any 32-bit integer, and the transformation
+  // adds two instructions for each such index to clamp it to the bound, as follows:
+  //
+  // %t1 = OpULessThanEqual %bool %index %bound_minus_one
+  // %t2 = OpSelect %int_type %t1 %index %bound_minus_one
+
   // Result id for the access chain
   uint32 fresh_id = 1;
 
@@ -398,21 +437,31 @@
   // OpAccessChain instruction should be inserted
   InstructionDescriptor instruction_to_insert_before = 4;
 
+  // Additional fresh ids, required to clamp index variables. A pair is needed
+  // for each access to a non-struct composite.
+  repeated UInt32Pair fresh_ids_for_clamping = 5;
+
 }
 
 message TransformationAddConstantBoolean {
 
   // Supports adding the constants true and false to a module, which may be
   // necessary in order to enable other transformations if they are not present.
+  // Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true.
 
   uint32 fresh_id = 1;
   bool is_true = 2;
 
+  // If the constant should be marked as irrelevant.
+  bool is_irrelevant = 3;
+
 }
 
 message TransformationAddConstantComposite {
 
   // Adds a constant of the given composite type to the module.
+  // Also, creates an IdIsIrrelevant fact about |fresh_id| if
+  // |is_irrelevant| is true.
 
   // Fresh id for the composite
   uint32 fresh_id = 1;
@@ -423,6 +472,9 @@
   // Constituent ids for the composite
   repeated uint32 constituent_id = 3;
 
+  // If the constant should be marked as irrelevant.
+  bool is_irrelevant = 4;
+
 }
 
 message TransformationAddConstantNull {
@@ -440,6 +492,8 @@
 message TransformationAddConstantScalar {
 
   // Adds a constant of the given scalar type.
+  // Also, creates an IdIsIrrelevant fact about
+  // |fresh_id| if |is_irrelevant| is true.
 
   // Id for the constant
   uint32 fresh_id = 1;
@@ -450,6 +504,33 @@
   // Value of the constant
   repeated uint32 word = 3;
 
+  // If the constant should be marked as irrelevant.
+  bool is_irrelevant = 4;
+
+}
+
+message TransformationAddCopyMemory {
+
+  // Adds an OpCopyMemory instruction into the module.
+  // Creates either a global or a local variable (based on
+  // |storage_class| field) to copy the target into.
+
+  // OpCopyMemory will be inserted before this instruction.
+  InstructionDescriptor instruction_descriptor = 1;
+
+  // Fresh id to copy memory into.
+  uint32 fresh_id = 2;
+
+  // Source to copy memory from.
+  uint32 source_id = 3;
+
+  // Storage class for the target variable. Can be either Function or Private.
+  uint32 storage_class = 4;
+
+  // Result id for the variable's initializer operand. Its type must be equal to
+  // variable's pointee type.
+  uint32 initializer_id = 5;
+
 }
 
 message TransformationAddDeadBlock {
@@ -584,6 +665,18 @@
 
 }
 
+message TransformationAddImageSampleUnusedComponents {
+
+  // A transformation that adds unused components to an image sample coordinate.
+
+  // An vector id with the original coordinate and the unused components.
+  uint32 coordinate_with_unused_components_id = 1;
+
+  // A descriptor for an image sample instruction.
+  InstructionDescriptor instruction_descriptor = 2;
+
+}
+
 message TransformationAddLocalVariable {
 
   // Adds a local variable of the given type (which must be a pointer with
@@ -618,6 +711,103 @@
 
 }
 
+message TransformationAddParameter {
+
+  // Adds a new parameter into the function.
+
+  // Result id of the function to add parameters to.
+  uint32 function_id = 1;
+
+  // Fresh id for a new parameter.
+  uint32 parameter_fresh_id = 2;
+
+  // Result id of the instruction, used to initializer new parameter
+  // in function calls. Type id of that instruction is the type id of the parameter.
+  // It may not be OpTypeVoid.
+  uint32 initializer_id = 3;
+
+  // A fresh id for a new function type. This might not be used
+  // if a required function type already exists or if we can change
+  // the old function type.
+  uint32 function_type_fresh_id = 4;
+
+}
+
+message TransformationAddRelaxedDecoration {
+
+  // Applies OpDecorate RelaxedPrecision to the given result id
+
+  // Result id to be decorated
+  uint32 result_id = 1;
+
+}
+
+message TransformationAddSpecConstantOp {
+
+  // Adds OpSpecConstantOp into the module.
+
+  // Result id for the new instruction.
+  uint32 fresh_id = 1;
+
+  // Type id for the new instruction.
+  uint32 type_id = 2;
+
+  // Opcode operand of the OpSpecConstantOp instruction.
+  uint32 opcode = 3;
+
+  // Operands of the |opcode| instruction.
+  repeated InstructionOperand operand = 4;
+
+}
+
+message TransformationAddSynonym {
+
+  // Adds a |synonymous_instruction| before |insert_before| instruction with
+  // and creates a fact that |result_id| and the result id of |synonymous_instruction|
+  // are synonymous.
+
+  // Result id of the first synonym.
+  uint32 result_id = 1;
+
+  // Type of the synonym to apply. Some types might produce instructions
+  // with commutative operands. Such types do not specify the order of the
+  // operands since we have a special transformation to swap commutable operands.
+  //
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3499):
+  //  Consider adding more types here.
+  enum SynonymType {
+    // New synonym is derived by adding zero to the |result_id|.
+    ADD_ZERO = 0;
+
+    // New synonym is derived by subtracting zero from the |result_id|.
+    SUB_ZERO = 1;
+
+    // New synonym is derived by multiplying |result_id| by one.
+    MUL_ONE = 2;
+
+    // New synonym is derived by applying OpCopyObject instruction to |result_id|.
+    COPY_OBJECT = 3;
+
+    // New synonym is derived by applying OpLogicalOr to |result_id| with the second
+    // operand being 'false'.
+    LOGICAL_OR = 4;
+
+    // New synonym is derived by applying OpLogicalAnd to |result_id| with the second
+    // operand being 'true'.
+    LOGICAL_AND = 5;
+  }
+
+  // Type of the synonym to create. See SynonymType for more details.
+  SynonymType synonym_type = 2;
+
+  // Fresh result id for a created synonym.
+  uint32 synonym_fresh_id = 3;
+
+  // An instruction to insert a new synonym before.
+  InstructionDescriptor insert_before = 4;
+
+}
+
 message TransformationAddTypeArray {
 
   // Adds an array type of the given element type and size to the module
@@ -809,23 +999,6 @@
 
 }
 
-message TransformationCopyObject {
-
-  // A transformation that introduces an OpCopyObject instruction to make a
-  // copy of an object.
-
-  // Id of the object to be copied
-  uint32 object = 1;
-
-  // A descriptor for an instruction in a block before which the new
-  // OpCopyObject instruction should be inserted
-  InstructionDescriptor instruction_to_insert_before = 2;
-
-  // A fresh id for the copied object
-  uint32 fresh_id = 3;
-
-}
-
 message TransformationEquationInstruction {
 
   // A transformation that adds an instruction to the module that defines an
@@ -871,6 +1044,22 @@
 
 }
 
+message TransformationInvertComparisonOperator {
+
+  // For some instruction with result id |operator_id| that
+  // represents a binary comparison operator (e.g. <, >, <=), this transformation
+  // will replace that instruction's result id with |fresh_id|,
+  // invert the opcode (< will become >=) and insert OpLogicalNot
+  // instruction with result id |operator_id| below.
+
+  // Result id of the instruction to invert.
+  uint32 operator_id = 1;
+
+  // Fresh id that will be used by the operator after the inversion.
+  uint32 fresh_id = 2;
+
+}
+
 message TransformationLoad {
 
   // Transformation that adds an OpLoad instruction from a pointer into an id.
@@ -966,12 +1155,10 @@
   // Function, whose parameters will be permuted
   uint32 function_id = 1;
 
-  // |new_type_id| is a result id of a valid OpTypeFunction instruction.
-  // New type is valid if:
-  //   - it has the same number of operands as the old one
-  //   - function's result type is the same as the old one
-  //   - function's arguments are permuted according to |permutation| vector
-  uint32 new_type_id = 2;
+  // Fresh id for a new type of the function. This might not be used
+  // if a required function type already exists or if we can change
+  // the old function type.
+  uint32 function_type_fresh_id = 2;
 
   // An array of size |n|, where |n| is a number of arguments to a function
   // with |function_id|. For each i: 0 <= permutation[i] < n.
@@ -983,6 +1170,83 @@
 
 }
 
+message TransformationPermutePhiOperands {
+
+  // Permutes operands of some OpPhi instruction.
+
+  // Result id of the instruction to apply the transformation to.
+  uint32 result_id = 1;
+
+  // A sequence of numbers in the range [0, n/2 - 1] where |n| is the number
+  // of operands of the OpPhi instruction with |result_id|.
+  repeated uint32 permutation = 2;
+
+}
+
+message TransformationPushIdThroughVariable {
+
+  // A transformation that makes |value_synonym_id| and |value_id| to be
+  // synonymous by storing |value_id| into |variable_id| and
+  // loading |variable_id| to |value_synonym_id|.
+
+  // The value to be stored.
+  uint32 value_id = 1;
+
+  // A fresh id for the result of the load instruction.
+  uint32 value_synonym_id = 2;
+
+  // A fresh id for the variable to be stored to.
+  uint32 variable_id = 3;
+
+  // Constant to initialize the variable from.
+  uint32 initializer_id = 4;
+
+  // The variable storage class (global or local).
+  uint32 variable_storage_class = 5;
+
+  // A descriptor for an instruction which the new OpStore
+  // and OpLoad instructions might be inserted before.
+  InstructionDescriptor instruction_descriptor = 6;
+
+}
+
+message TransformationRecordSynonymousConstants {
+
+  // A transformation that, given the IDs to two synonymous constants,
+  // records the fact that they are synonymous. The module is not changed.
+  // Two constants are synonymous if:
+  // - they have the same type (ignoring the presence of integer sign)
+  // - they have the same opcode (one of OpConstant, OpConstantTrue,
+  //   OpConstantFalse, OpConstantNull)
+  // - they have the same value
+  // If the types are the same, OpConstantNull is equivalent to
+  // OpConstantFalse or OpConstant with value zero.
+
+  // The id of a constant
+  uint32 constant1_id = 1;
+
+  // The id of the synonym
+  uint32 constant2_id = 2;
+
+}
+
+message TransformationReplaceParameterWithGlobal {
+
+  // Removes parameter with result id |parameter_id| from its function
+  // and creates a global variable to pass its value to the function instead.
+
+  // Fresh id for a new function type. This might not be used if a required
+  // function type already exists or if we can change the old function type.
+  uint32 function_type_fresh_id = 2;
+
+  // Result id of the OpFunctionParameter instruction to remove.
+  uint32 parameter_id = 3;
+
+  // Fresh id of a global variable used to pass parameter's value to the function.
+  uint32 global_variable_fresh_id = 4;
+
+}
+
 message TransformationReplaceBooleanConstantWithConstantBinary {
 
   // A transformation to capture replacing a use of a boolean constant with
@@ -1024,6 +1288,39 @@
 
 }
 
+message TransformationReplaceCopyMemoryWithLoadStore {
+
+   // A transformation that replaces instructions OpCopyMemory with loading
+   // the source variable to an intermediate value and storing this value into the
+   // target variable of the original OpCopyMemory instruction.
+
+   // The intermediate value.
+   uint32 fresh_id = 1;
+
+   // The instruction descriptor to OpCopyMemory. It is necessary, because
+   // OpCopyMemory doesn't have a result id.
+   InstructionDescriptor copy_memory_instruction_descriptor = 2;
+}
+
+message TransformationReplaceCopyObjectWithStoreLoad {
+
+  // A transformation that replaces instruction OpCopyObject with
+  // storing into a new variable and immediately loading from this
+  // variable to |result_id| of the original OpCopyObject instruction.
+
+  // The result id of initial OpCopyObject instruction
+  uint32 copy_object_result_id = 1;
+
+  // A fresh id for the variable to be stored to.
+  uint32 fresh_variable_id = 2;
+
+  // The variable storage class (Function or Private).
+  uint32 variable_storage_class = 3;
+
+  // Constant to initialize the variable with.
+  uint32 variable_initializer_id = 4;
+}
+
 message TransformationReplaceIdWithSynonym {
 
   // Replaces a use of an id with an id that is known to be synonymous, e.g.
@@ -1037,6 +1334,70 @@
 
 }
 
+message TransformationReplaceLinearAlgebraInstruction {
+
+  // Replaces a linear algebra instruction with its
+  // mathematical definition.
+
+  // The fresh ids needed to apply the transformation.
+  repeated uint32 fresh_ids = 1;
+
+  // A descriptor for a linear algebra instruction.
+  // This transformation is only applicable if the described instruction has one of the following opcodes.
+  // Supported:
+  //   OpVectorTimesScalar
+  //   OpMatrixTimesScalar
+  //   OpVectorTimesMatrix
+  //   OpMatrixTimesVector
+  //   OpMatrixTimesMatrix
+  //   OpDot
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
+  // Right now we only support certain operations. When this issue is addressed
+  // the supporting comments can be removed.
+  // To be supported in the future:
+  //   OpTranspose
+  //   OpOuterProduct
+  InstructionDescriptor instruction_descriptor = 2;
+
+}
+
+message TransformationReplaceLoadStoreWithCopyMemory {
+  // A transformation that takes a pair of instruction descriptors
+  // to OpLoad and OpStore that have the same intermediate value
+  // and replaces the OpStore with an equivalent OpCopyMemory.
+
+  // The instruction descriptor to OpLoad
+  InstructionDescriptor load_instruction_descriptor = 1;
+
+  // The instruction descriptor to OpStore
+  InstructionDescriptor store_instruction_descriptor = 2;
+}
+
+message TransformationReplaceParamsWithStruct {
+
+  // Replaces parameters of the function with a struct containing
+  // values of those parameters.
+
+  // Result ids of parameters to replace.
+  repeated uint32 parameter_id = 1;
+
+  // Fresh id for a new function type. This might be unused if the required type
+  // already exists in the module or if we can change the old type.
+  uint32 fresh_function_type_id = 2;
+
+  // Fresh id for a new struct function parameter to be used as a replacement.
+  uint32 fresh_parameter_id = 3;
+
+  // Fresh ids for struct objects containing values of replaced parameters.
+  // This map contains a fresh id for at least every result id of a relevant
+  // OpFunctionCall instruction.
+  //
+  // While maps are not fully deterministic, the way this map is used does not
+  // exhibit nondeterminism. Change to repeated Uint32Pair if this changes.
+  map<uint32, uint32> caller_id_to_fresh_composite_id = 4;
+
+}
+
 message TransformationSetFunctionControl {
 
   // A transformation that sets the function control operand of an OpFunction
@@ -1154,6 +1515,21 @@
 
 }
 
+message TransformationSwapConditionalBranchOperands {
+
+  // Swaps label ids in OpBranchConditional instruction.
+  // Additionally, inverts the guard and swaps branch weights
+  // if present.
+
+  // Descriptor of the instruction to swap operands of.
+  InstructionDescriptor instruction_descriptor = 1;
+
+  // Fresh result id for the OpLogicalNot instruction, used
+  // to invert the guard.
+  uint32 fresh_id = 2;
+
+}
+
 message TransformationToggleAccessChainInstruction {
 
   // A transformation that toggles an access chain instruction.
diff --git a/third_party/SPIRV-Tools/source/fuzz/replayer.cpp b/third_party/SPIRV-Tools/source/fuzz/replayer.cpp
index 6312cba..bc680d8 100644
--- a/third_party/SPIRV-Tools/source/fuzz/replayer.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/replayer.cpp
@@ -65,12 +65,20 @@
     const std::vector<uint32_t>& binary_in,
     const protobufs::FactSequence& initial_facts,
     const protobufs::TransformationSequence& transformation_sequence_in,
-    std::vector<uint32_t>* binary_out,
+    uint32_t num_transformations_to_apply, std::vector<uint32_t>* binary_out,
     protobufs::TransformationSequence* transformation_sequence_out) const {
   // Check compatibility between the library version being linked with and the
   // header files being used.
   GOOGLE_PROTOBUF_VERIFY_VERSION;
 
+  if (num_transformations_to_apply >
+      static_cast<uint32_t>(transformation_sequence_in.transformation_size())) {
+    impl_->consumer(SPV_MSG_ERROR, nullptr, {},
+                    "The number of transformations to be replayed must not "
+                    "exceed the size of the transformation sequence.");
+    return Replayer::ReplayerResultStatus::kTooManyTransformationsRequested;
+  }
+
   spvtools::SpirvTools tools(impl_->target_env);
   if (!tools.IsValid()) {
     impl_->consumer(SPV_MSG_ERROR, nullptr, {},
@@ -104,7 +112,13 @@
                                                impl_->validator_options);
 
   // Consider the transformation proto messages in turn.
+  uint32_t counter = 0;
   for (auto& message : transformation_sequence_in.transformation()) {
+    if (counter >= num_transformations_to_apply) {
+      break;
+    }
+    counter++;
+
     auto transformation = Transformation::FromMessage(message);
 
     // Check whether the transformation can be applied.
diff --git a/third_party/SPIRV-Tools/source/fuzz/replayer.h b/third_party/SPIRV-Tools/source/fuzz/replayer.h
index e77d840..d6395aa 100644
--- a/third_party/SPIRV-Tools/source/fuzz/replayer.h
+++ b/third_party/SPIRV-Tools/source/fuzz/replayer.h
@@ -34,6 +34,7 @@
     kFailedToCreateSpirvToolsInterface,
     kInitialBinaryInvalid,
     kReplayValidationFailure,
+    kTooManyTransformationsRequested,
   };
 
   // Constructs a replayer from the given target environment.
@@ -52,16 +53,17 @@
   // invoked once for each message communicated from the library.
   void SetMessageConsumer(MessageConsumer consumer);
 
-  // Transforms |binary_in| to |binary_out| by attempting to apply the
-  // transformations from |transformation_sequence_in|.  Initial facts about the
-  // input binary and the context in which it will execute are provided via
-  // |initial_facts|.  The transformations that were successfully applied are
-  // returned via |transformation_sequence_out|.
+  // Transforms |binary_in| to |binary_out| by attempting to apply the first
+  // |num_transformations_to_apply| transformations from
+  // |transformation_sequence_in|.  Initial facts about the input binary and the
+  // context in which it will execute are provided via |initial_facts|.  The
+  // transformations that were successfully applied are returned via
+  // |transformation_sequence_out|.
   ReplayerResultStatus Run(
       const std::vector<uint32_t>& binary_in,
       const protobufs::FactSequence& initial_facts,
       const protobufs::TransformationSequence& transformation_sequence_in,
-      std::vector<uint32_t>* binary_out,
+      uint32_t num_transformations_to_apply, std::vector<uint32_t>* binary_out,
       protobufs::TransformationSequence* transformation_sequence_out) const;
 
  private:
diff --git a/third_party/SPIRV-Tools/source/fuzz/shrinker.cpp b/third_party/SPIRV-Tools/source/fuzz/shrinker.cpp
index 002e8a7..829df63 100644
--- a/third_party/SPIRV-Tools/source/fuzz/shrinker.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/shrinker.cpp
@@ -121,10 +121,13 @@
   // succeeds, (b) get the binary that results from running these
   // transformations, and (c) get the subsequence of the initial transformations
   // that actually apply (in principle this could be a strict subsequence).
-  if (Replayer(impl_->target_env, impl_->validate_during_replay,
-               impl_->validator_options)
-          .Run(binary_in, initial_facts, transformation_sequence_in,
-               &current_best_binary, &current_best_transformations) !=
+  Replayer replayer(impl_->target_env, impl_->validate_during_replay,
+                    impl_->validator_options);
+  replayer.SetMessageConsumer(impl_->consumer);
+  if (replayer.Run(binary_in, initial_facts, transformation_sequence_in,
+                   static_cast<uint32_t>(
+                       transformation_sequence_in.transformation_size()),
+                   &current_best_binary, &current_best_transformations) !=
       Replayer::ReplayerResultStatus::kComplete) {
     return ShrinkerResultStatus::kReplayFailed;
   }
@@ -184,7 +187,8 @@
       // Remove a chunk of transformations according to the current index and
       // chunk size.
       auto transformations_with_chunk_removed =
-          RemoveChunk(current_best_transformations, chunk_index, chunk_size);
+          RemoveChunk(current_best_transformations,
+                      static_cast<uint32_t>(chunk_index), chunk_size);
 
       // Replay the smaller sequence of transformations to get a next binary and
       // transformation sequence. Note that the transformations arising from
@@ -193,10 +197,11 @@
       // transformations inapplicable.
       std::vector<uint32_t> next_binary;
       protobufs::TransformationSequence next_transformation_sequence;
-      if (Replayer(impl_->target_env, impl_->validate_during_replay,
-                   impl_->validator_options)
-              .Run(binary_in, initial_facts, transformations_with_chunk_removed,
-                   &next_binary, &next_transformation_sequence) !=
+      if (replayer.Run(
+              binary_in, initial_facts, transformations_with_chunk_removed,
+              static_cast<uint32_t>(
+                  transformations_with_chunk_removed.transformation_size()),
+              &next_binary, &next_transformation_sequence) !=
           Replayer::ReplayerResultStatus::kComplete) {
         // Replay should not fail; if it does, we need to abort shrinking.
         return ShrinkerResultStatus::kReplayFailed;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
index 8b84169..a31384f 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
@@ -22,14 +22,20 @@
 #include "source/fuzz/transformation_add_constant_composite.h"
 #include "source/fuzz/transformation_add_constant_null.h"
 #include "source/fuzz/transformation_add_constant_scalar.h"
+#include "source/fuzz/transformation_add_copy_memory.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_image_sample_unused_components.h"
 #include "source/fuzz/transformation_add_local_variable.h"
 #include "source/fuzz/transformation_add_no_contraction_decoration.h"
+#include "source/fuzz/transformation_add_parameter.h"
+#include "source/fuzz/transformation_add_relaxed_decoration.h"
+#include "source/fuzz/transformation_add_spec_constant_op.h"
+#include "source/fuzz/transformation_add_synonym.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"
@@ -43,17 +49,26 @@
 #include "source/fuzz/transformation_composite_construct.h"
 #include "source/fuzz/transformation_composite_extract.h"
 #include "source/fuzz/transformation_compute_data_synonym_fact_closure.h"
-#include "source/fuzz/transformation_copy_object.h"
 #include "source/fuzz/transformation_equation_instruction.h"
 #include "source/fuzz/transformation_function_call.h"
+#include "source/fuzz/transformation_invert_comparison_operator.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"
 #include "source/fuzz/transformation_permute_function_parameters.h"
+#include "source/fuzz/transformation_permute_phi_operands.h"
+#include "source/fuzz/transformation_push_id_through_variable.h"
+#include "source/fuzz/transformation_record_synonymous_constants.h"
 #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
+#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
+#include "source/fuzz/transformation_replace_copy_object_with_store_load.h"
 #include "source/fuzz/transformation_replace_id_with_synonym.h"
+#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
+#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h"
+#include "source/fuzz/transformation_replace_parameter_with_global.h"
+#include "source/fuzz/transformation_replace_params_with_struct.h"
 #include "source/fuzz/transformation_set_function_control.h"
 #include "source/fuzz/transformation_set_loop_control.h"
 #include "source/fuzz/transformation_set_memory_operands_mask.h"
@@ -61,6 +76,7 @@
 #include "source/fuzz/transformation_split_block.h"
 #include "source/fuzz/transformation_store.h"
 #include "source/fuzz/transformation_swap_commutable_operands.h"
+#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
 #include "source/fuzz/transformation_toggle_access_chain_instruction.h"
 #include "source/fuzz/transformation_vector_shuffle.h"
 #include "source/util/make_unique.h"
@@ -87,6 +103,8 @@
     case protobufs::Transformation::TransformationCase::kAddConstantScalar:
       return MakeUnique<TransformationAddConstantScalar>(
           message.add_constant_scalar());
+    case protobufs::Transformation::TransformationCase::kAddCopyMemory:
+      return MakeUnique<TransformationAddCopyMemory>(message.add_copy_memory());
     case protobufs::Transformation::TransformationCase::kAddDeadBlock:
       return MakeUnique<TransformationAddDeadBlock>(message.add_dead_block());
     case protobufs::Transformation::TransformationCase::kAddDeadBreak:
@@ -102,6 +120,10 @@
     case protobufs::Transformation::TransformationCase::kAddGlobalVariable:
       return MakeUnique<TransformationAddGlobalVariable>(
           message.add_global_variable());
+    case protobufs::Transformation::TransformationCase::
+        kAddImageSampleUnusedComponents:
+      return MakeUnique<TransformationAddImageSampleUnusedComponents>(
+          message.add_image_sample_unused_components());
     case protobufs::Transformation::TransformationCase::kAddLocalVariable:
       return MakeUnique<TransformationAddLocalVariable>(
           message.add_local_variable());
@@ -109,6 +131,16 @@
         kAddNoContractionDecoration:
       return MakeUnique<TransformationAddNoContractionDecoration>(
           message.add_no_contraction_decoration());
+    case protobufs::Transformation::TransformationCase::kAddParameter:
+      return MakeUnique<TransformationAddParameter>(message.add_parameter());
+    case protobufs::Transformation::TransformationCase::kAddRelaxedDecoration:
+      return MakeUnique<TransformationAddRelaxedDecoration>(
+          message.add_relaxed_decoration());
+    case protobufs::Transformation::TransformationCase::kAddSpecConstantOp:
+      return MakeUnique<TransformationAddSpecConstantOp>(
+          message.add_spec_constant_op());
+    case protobufs::Transformation::TransformationCase::kAddSynonym:
+      return MakeUnique<TransformationAddSynonym>(message.add_synonym());
     case protobufs::Transformation::TransformationCase::kAddTypeArray:
       return MakeUnique<TransformationAddTypeArray>(message.add_type_array());
     case protobufs::Transformation::TransformationCase::kAddTypeBoolean:
@@ -143,13 +175,15 @@
         kComputeDataSynonymFactClosure:
       return MakeUnique<TransformationComputeDataSynonymFactClosure>(
           message.compute_data_synonym_fact_closure());
-    case protobufs::Transformation::TransformationCase::kCopyObject:
-      return MakeUnique<TransformationCopyObject>(message.copy_object());
     case protobufs::Transformation::TransformationCase::kEquationInstruction:
       return MakeUnique<TransformationEquationInstruction>(
           message.equation_instruction());
     case protobufs::Transformation::TransformationCase::kFunctionCall:
       return MakeUnique<TransformationFunctionCall>(message.function_call());
+    case protobufs::Transformation::TransformationCase::
+        kInvertComparisonOperator:
+      return MakeUnique<TransformationInvertComparisonOperator>(
+          message.invert_comparison_operator());
     case protobufs::Transformation::TransformationCase::kLoad:
       return MakeUnique<TransformationLoad>(message.load());
     case protobufs::Transformation::TransformationCase::kMergeBlocks:
@@ -163,6 +197,20 @@
         kPermuteFunctionParameters:
       return MakeUnique<TransformationPermuteFunctionParameters>(
           message.permute_function_parameters());
+    case protobufs::Transformation::TransformationCase::kPermutePhiOperands:
+      return MakeUnique<TransformationPermutePhiOperands>(
+          message.permute_phi_operands());
+    case protobufs::Transformation::TransformationCase::kPushIdThroughVariable:
+      return MakeUnique<TransformationPushIdThroughVariable>(
+          message.push_id_through_variable());
+    case protobufs::Transformation::TransformationCase::
+        kRecordSynonymousConstants:
+      return MakeUnique<TransformationRecordSynonymousConstants>(
+          message.record_synonymous_constants());
+    case protobufs::Transformation::TransformationCase::
+        kReplaceParameterWithGlobal:
+      return MakeUnique<TransformationReplaceParameterWithGlobal>(
+          message.replace_parameter_with_global());
     case protobufs::Transformation::TransformationCase::
         kReplaceBooleanConstantWithConstantBinary:
       return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(
@@ -171,9 +219,29 @@
         kReplaceConstantWithUniform:
       return MakeUnique<TransformationReplaceConstantWithUniform>(
           message.replace_constant_with_uniform());
+    case protobufs::Transformation::TransformationCase::
+        kReplaceCopyMemoryWithLoadStore:
+      return MakeUnique<TransformationReplaceCopyMemoryWithLoadStore>(
+          message.replace_copy_memory_with_load_store());
+    case protobufs::Transformation::TransformationCase::
+        kReplaceCopyObjectWithStoreLoad:
+      return MakeUnique<TransformationReplaceCopyObjectWithStoreLoad>(
+          message.replace_copy_object_with_store_load());
     case protobufs::Transformation::TransformationCase::kReplaceIdWithSynonym:
       return MakeUnique<TransformationReplaceIdWithSynonym>(
           message.replace_id_with_synonym());
+    case protobufs::Transformation::TransformationCase::
+        kReplaceLinearAlgebraInstruction:
+      return MakeUnique<TransformationReplaceLinearAlgebraInstruction>(
+          message.replace_linear_algebra_instruction());
+    case protobufs::Transformation::TransformationCase::
+        kReplaceLoadStoreWithCopyMemory:
+      return MakeUnique<TransformationReplaceLoadStoreWithCopyMemory>(
+          message.replace_load_store_with_copy_memory());
+    case protobufs::Transformation::TransformationCase::
+        kReplaceParamsWithStruct:
+      return MakeUnique<TransformationReplaceParamsWithStruct>(
+          message.replace_params_with_struct());
     case protobufs::Transformation::TransformationCase::kSetFunctionControl:
       return MakeUnique<TransformationSetFunctionControl>(
           message.set_function_control());
@@ -194,6 +262,10 @@
       return MakeUnique<TransformationSwapCommutableOperands>(
           message.swap_commutable_operands());
     case protobufs::Transformation::TransformationCase::
+        kSwapConditionalBranchOperands:
+      return MakeUnique<TransformationSwapConditionalBranchOperands>(
+          message.swap_conditional_branch_operands());
+    case protobufs::Transformation::TransformationCase::
         kToggleAccessChainInstruction:
       return MakeUnique<TransformationToggleAccessChainInstruction>(
           message.toggle_access_chain_instruction());
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.cpp
index ff17c36..3366869 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.cpp
@@ -29,7 +29,8 @@
 TransformationAccessChain::TransformationAccessChain(
     uint32_t fresh_id, uint32_t pointer_id,
     const std::vector<uint32_t>& index_id,
-    const protobufs::InstructionDescriptor& instruction_to_insert_before) {
+    const protobufs::InstructionDescriptor& instruction_to_insert_before,
+    const std::vector<std::pair<uint32_t, uint32_t>>& fresh_ids_for_clamping) {
   message_.set_fresh_id(fresh_id);
   message_.set_pointer_id(pointer_id);
   for (auto id : index_id) {
@@ -37,12 +38,22 @@
   }
   *message_.mutable_instruction_to_insert_before() =
       instruction_to_insert_before;
+  for (auto clamping_ids_pair : fresh_ids_for_clamping) {
+    protobufs::UInt32Pair pair;
+    pair.set_first(clamping_ids_pair.first);
+    pair.set_second(clamping_ids_pair.second);
+    *message_.add_fresh_ids_for_clamping() = pair;
+  }
 }
 
 bool TransformationAccessChain::IsApplicable(
     opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
-  // The result id must be fresh
-  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
+  // Keep track of the fresh ids used to make sure that they are distinct.
+  std::set<uint32_t> fresh_ids_used;
+
+  // The result id must be fresh.
+  if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+          message_.fresh_id(), ir_context, &fresh_ids_used)) {
     return false;
   }
   // The pointer id must exist and have a type.
@@ -50,7 +61,7 @@
   if (!pointer || !pointer->type_id()) {
     return false;
   }
-  // The type must indeed be a pointer
+  // The type must indeed be a pointer.
   auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
   if (pointer_type->opcode() != SpvOpTypePointer) {
     return false;
@@ -74,9 +85,9 @@
   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.
+      assert(
+          false &&
+          "Access chains should not be created from null/undefined pointers");
       return false;
     default:
       break;
@@ -96,23 +107,86 @@
   // Start from the base type of the pointer.
   uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
 
+  int id_pairs_used = 0;
+
   // 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(ir_context, index_id);
-    if (!maybe_index_value.first) {
-      // There was no integer: this index is no good.
-      return false;
+    // The index value will correspond to the value of the index if the object
+    // is a struct, otherwise the value 0 will be used.
+    uint32_t index_value;
+
+    // Check whether the object is a struct.
+    if (ir_context->get_def_use_mgr()->GetDef(subobject_type_id)->opcode() ==
+        SpvOpTypeStruct) {
+      // It is a struct: we need to retrieve the integer value.
+
+      bool successful;
+      std::tie(successful, index_value) =
+          GetIndexValue(ir_context, index_id, subobject_type_id);
+
+      if (!successful) {
+        return false;
+      }
+    } else {
+      // It is not a struct: the index will need clamping.
+
+      if (message_.fresh_ids_for_clamping().size() <= id_pairs_used) {
+        // We don't have enough ids
+        return false;
+      }
+
+      // Get two new ids to use and update the amount used.
+      protobufs::UInt32Pair fresh_ids =
+          message_.fresh_ids_for_clamping()[id_pairs_used++];
+
+      // Valid ids need to have been given
+      if (fresh_ids.first() == 0 || fresh_ids.second() == 0) {
+        return false;
+      }
+
+      // Check that the ids are actually fresh and not already used by this
+      // transformation.
+      if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+              fresh_ids.first(), ir_context, &fresh_ids_used) ||
+          !CheckIdIsFreshAndNotUsedByThisTransformation(
+              fresh_ids.second(), ir_context, &fresh_ids_used)) {
+        return false;
+      }
+
+      if (!ValidIndexToComposite(ir_context, index_id, subobject_type_id)) {
+        return false;
+      }
+
+      // Perform the clamping using the fresh ids at our disposal.
+      auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
+
+      uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
+          *ir_context->get_def_use_mgr()->GetDef(subobject_type_id),
+          ir_context);
+
+      // The module must have an integer constant of value bound-1 of the same
+      // type as the index.
+      if (!fuzzerutil::MaybeGetIntegerConstantFromValueAndType(
+              ir_context, bound - 1, index_instruction->type_id())) {
+        return false;
+      }
+
+      // The module must have the definition of bool type to make a comparison.
+      if (!fuzzerutil::MaybeGetBoolType(ir_context)) {
+        return false;
+      }
+
+      // The index is not necessarily a constant, so we may not know its value.
+      // We can use index 0 because the components of a non-struct composite
+      // all have the same type, and index 0 is always in bounds.
+      index_value = 0;
     }
+
     // 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(
-        ir_context, subobject_type_id, maybe_index_value.second);
+        ir_context, subobject_type_id, index_value);
     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.
@@ -152,25 +226,105 @@
       ir_context->get_def_use_mgr()->GetDef(message_.pointer_id())->type_id());
   uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
 
+  uint32_t id_pairs_used = 0;
+
   // 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(ir_context, index_id).second;
+    uint32_t index_value;
+
+    // Actual id to be used in the instruction: the original id
+    // or the clamped one.
+    uint32_t new_index_id;
+
+    // Check whether the object is a struct.
+    if (ir_context->get_def_use_mgr()->GetDef(subobject_type_id)->opcode() ==
+        SpvOpTypeStruct) {
+      // It is a struct: we need to retrieve the integer value.
+
+      index_value =
+          GetIndexValue(ir_context, index_id, subobject_type_id).second;
+
+      new_index_id = index_id;
+
+    } else {
+      // It is not a struct: the index will need clamping.
+
+      // Get two new ids to use and update the amount used.
+      protobufs::UInt32Pair fresh_ids =
+          message_.fresh_ids_for_clamping()[id_pairs_used++];
+
+      // Perform the clamping using the fresh ids at our disposal.
+      // The module will not be changed if |add_clamping_instructions| is not
+      // set.
+      auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
+
+      uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
+          *ir_context->get_def_use_mgr()->GetDef(subobject_type_id),
+          ir_context);
+
+      auto bound_minus_one_id =
+          fuzzerutil::MaybeGetIntegerConstantFromValueAndType(
+              ir_context, bound - 1, index_instruction->type_id());
+
+      assert(bound_minus_one_id &&
+             "A constant of value bound - 1 and the same type as the index "
+             "must exist as a precondition.");
+
+      uint32_t bool_type_id = fuzzerutil::MaybeGetBoolType(ir_context);
+
+      assert(bool_type_id &&
+             "An OpTypeBool instruction must exist as a precondition.");
+
+      auto int_type_inst =
+          ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id());
+
+      // Clamp the integer and add the corresponding instructions in the module
+      // if |add_clamping_instructions| is set.
+      auto instruction_to_insert_before =
+          FindInstruction(message_.instruction_to_insert_before(), ir_context);
+
+      // Compare the index with the bound via an instruction of the form:
+      //   %fresh_ids.first = OpULessThanEqual %bool %int_id %bound_minus_one.
+      fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.first());
+      instruction_to_insert_before->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpULessThanEqual, bool_type_id, fresh_ids.first(),
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}},
+               {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
+
+      // Select the index if in-bounds, otherwise one less than the bound:
+      //   %fresh_ids.second = OpSelect %int_type %fresh_ids.first %int_id
+      //                           %bound_minus_one
+      fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.second());
+      instruction_to_insert_before->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpSelect, int_type_inst->result_id(),
+          fresh_ids.second(),
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {fresh_ids.first()}},
+               {SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}},
+               {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
+
+      new_index_id = fresh_ids.second();
+
+      index_value = 0;
+    }
+
+    // Add the correct index id to the operands.
+    operands.push_back({SPV_OPERAND_TYPE_ID, {new_index_id}});
+
     // Walk to the next type in the composite object using this index.
     subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
         ir_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.
+  // 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(
       ir_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.
+  // Add the access chain instruction to the module, and update the module's
+  // id bound.
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   FindInstruction(message_.instruction_to_insert_before(), ir_context)
       ->InsertBefore(MakeUnique<opt::Instruction>(
@@ -180,8 +334,8 @@
   // Conservatively invalidate all analyses.
   ir_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 the base pointer's pointee value was irrelevant, the same is true of
+  // the pointee value of the result of this access chain.
   if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
           message_.pointer_id())) {
     transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
@@ -196,21 +350,61 @@
 }
 
 std::pair<bool, uint32_t> TransformationAccessChain::GetIndexValue(
-    opt::IRContext* ir_context, uint32_t index_id) const {
-  auto index_instruction = ir_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.
+    opt::IRContext* ir_context, uint32_t index_id,
+    uint32_t object_type_id) const {
+  if (!ValidIndexToComposite(ir_context, index_id, object_type_id)) {
     return {false, 0};
   }
+  auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
+
+  uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
+      *ir_context->get_def_use_mgr()->GetDef(object_type_id), ir_context);
+
+  // The index must be a constant
+  if (!spvOpcodeIsConstant(index_instruction->opcode())) {
+    return {false, 0};
+  }
+
+  // The index must be in bounds.
+  uint32_t value = index_instruction->GetSingleWordInOperand(0);
+
+  if (value >= bound) {
+    return {false, 0};
+  }
+
+  return {true, value};
+}
+
+bool TransformationAccessChain::ValidIndexToComposite(
+    opt::IRContext* ir_context, uint32_t index_id, uint32_t object_type_id) {
+  auto object_type_def = ir_context->get_def_use_mgr()->GetDef(object_type_id);
+  // The object being indexed must be a composite.
+  if (!spvOpcodeIsComposite(object_type_def->opcode())) {
+    return false;
+  }
+
+  // Get the defining instruction of the index.
+  auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
+  if (!index_instruction) {
+    return false;
+  }
+
+  // The index type must be 32-bit integer.
   auto index_type =
       ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id());
   if (index_type->opcode() != SpvOpTypeInt ||
       index_type->GetSingleWordInOperand(0) != 32) {
-    return {false, 0};
+    return false;
   }
-  return {true, index_instruction->GetSingleWordInOperand(0)};
+
+  // If the object being traversed is a struct, the id must correspond to an
+  // in-bound constant.
+  if (object_type_def->opcode() == SpvOpTypeStruct) {
+    if (!spvOpcodeIsConstant(index_instruction->opcode())) {
+      return false;
+    }
+  }
+  return true;
 }
 
 }  // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.h b/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.h
index 9306a59..db5b8e6 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.h
@@ -33,20 +33,28 @@
   TransformationAccessChain(
       uint32_t fresh_id, uint32_t pointer_id,
       const std::vector<uint32_t>& index_id,
-      const protobufs::InstructionDescriptor& instruction_to_insert_before);
+      const protobufs::InstructionDescriptor& instruction_to_insert_before,
+      const std::vector<std::pair<uint32_t, uint32_t>>& fresh_ids_for_clamping =
+          {});
 
-  // - |message_.fresh_id| must be fresh
+  // - |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
+  //   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
+  // - The pointer must not be OpConstantNull or OpUndef.
+  // - |message_.index_id| must be a sequence of ids of 32-bit integers
   //   such that it is possible to walk the pointee type of
-  //   |message_.pointer_id| using these indices, remaining in-bounds.
+  //   |message_.pointer_id| using these indices.
+  // - All indices used to access a struct must be OpConstant.
+  // - The indices used to index non-struct composites will be clamped to be
+  //   in bound. Enough fresh ids must be given in
+  //   |message_.fresh_id_for_clamping| to perform clamping (2 for
+  //   each index accessing a non-struct). This requires the bool type and
+  //   a constant of value (bound - 1) to be declared in the module.
   // - 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|
+  //   class associated with |message_.pointer_id|.
   bool IsApplicable(
       opt::IRContext* ir_context,
       const TransformationContext& transformation_context) const override;
@@ -58,6 +66,9 @@
   // the indices in |message_.index_id|, and with the same storage class as
   // |message_.pointer_id|.
   //
+  // For each of the indices traversing non-struct composites, two clamping
+  // instructions are added using ids in |message_.fresh_id_for_clamping|.
+  //
   // If the fact manager in |transformation_context| 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
@@ -68,11 +79,21 @@
   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.
+  // Returns {false, 0} in each of the following cases:
+  // - |index_id| does not correspond to a 32-bit integer constant
+  // - the object being indexed is not a composite type
+  // - the constant at |index_id| is out of bounds.
+  // Otherwise, returns {true, value}, where value is the value of the constant
+  // at |index_id|.
   std::pair<bool, uint32_t> GetIndexValue(opt::IRContext* ir_context,
-                                          uint32_t index_id) const;
+                                          uint32_t index_id,
+                                          uint32_t object_type_id) const;
+
+  // Returns true if |index_id| corresponds, in the given context, to a 32-bit
+  // integer which can be used to index an object of the type specified by
+  // |object_type_id|. Returns false otherwise.
+  static bool ValidIndexToComposite(opt::IRContext* ir_context,
+                                    uint32_t index_id, uint32_t object_type_id);
 
   protobufs::TransformationAccessChain message_;
 };
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_boolean.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_boolean.cpp
index 1930f7e..904ad61 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_boolean.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_boolean.cpp
@@ -25,34 +25,36 @@
     : message_(message) {}
 
 TransformationAddConstantBoolean::TransformationAddConstantBoolean(
-    uint32_t fresh_id, bool is_true) {
+    uint32_t fresh_id, bool is_true, bool is_irrelevant) {
   message_.set_fresh_id(fresh_id);
   message_.set_is_true(is_true);
+  message_.set_is_irrelevant(is_irrelevant);
 }
 
 bool TransformationAddConstantBoolean::IsApplicable(
     opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
-  opt::analysis::Bool bool_type;
-  if (!ir_context->get_type_mgr()->GetId(&bool_type)) {
-    // No OpTypeBool is present.
-    return false;
-  }
-  return fuzzerutil::IsFreshId(ir_context, message_.fresh_id());
+  return fuzzerutil::MaybeGetBoolType(ir_context) != 0 &&
+         fuzzerutil::IsFreshId(ir_context, message_.fresh_id());
 }
 
 void TransformationAddConstantBoolean::Apply(
-    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
-  opt::analysis::Bool bool_type;
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   // Add the boolean constant to the module, ensuring the module's id bound is
   // high enough.
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   ir_context->module()->AddGlobalValue(
       message_.is_true() ? SpvOpConstantTrue : SpvOpConstantFalse,
-      message_.fresh_id(), ir_context->get_type_mgr()->GetId(&bool_type));
+      message_.fresh_id(), fuzzerutil::MaybeGetBoolType(ir_context));
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
   ir_context->InvalidateAnalysesExceptFor(
       opt::IRContext::Analysis::kAnalysisNone);
+
+  if (message_.is_irrelevant()) {
+    transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
+        message_.fresh_id());
+  }
 }
 
 protobufs::Transformation TransformationAddConstantBoolean::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_boolean.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_boolean.h
index 5d876cf..d2f9e9a 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_boolean.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_boolean.h
@@ -28,7 +28,8 @@
   explicit TransformationAddConstantBoolean(
       const protobufs::TransformationAddConstantBoolean& message);
 
-  TransformationAddConstantBoolean(uint32_t fresh_id, bool is_true);
+  TransformationAddConstantBoolean(uint32_t fresh_id, bool is_true,
+                                   bool is_irrelevant);
 
   // - |message_.fresh_id| must not be used by the module.
   // - The module must already contain OpTypeBool.
@@ -38,6 +39,8 @@
 
   // - Adds OpConstantTrue (OpConstantFalse) to the module with id
   //   |message_.fresh_id| if |message_.is_true| holds (does not hold).
+  // - Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant|
+  //   is true.
   void Apply(opt::IRContext* ir_context,
              TransformationContext* transformation_context) const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.cpp
index ae34b26..99d88b4 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.cpp
@@ -28,9 +28,10 @@
 
 TransformationAddConstantComposite::TransformationAddConstantComposite(
     uint32_t fresh_id, uint32_t type_id,
-    const std::vector<uint32_t>& constituent_ids) {
+    const std::vector<uint32_t>& constituent_ids, bool is_irrelevant) {
   message_.set_fresh_id(fresh_id);
   message_.set_type_id(type_id);
+  message_.set_is_irrelevant(is_irrelevant);
   for (auto constituent_id : constituent_ids) {
     message_.add_constituent_id(constituent_id);
   }
@@ -104,7 +105,8 @@
 }
 
 void TransformationAddConstantComposite::Apply(
-    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   opt::Instruction::OperandList in_operands;
   for (auto constituent_id : message_.constituent_id()) {
     in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}});
@@ -117,6 +119,11 @@
   // validity of existing analyses.
   ir_context->InvalidateAnalysesExceptFor(
       opt::IRContext::Analysis::kAnalysisNone);
+
+  if (message_.is_irrelevant()) {
+    transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
+        message_.fresh_id());
+  }
 }
 
 protobufs::Transformation TransformationAddConstantComposite::ToMessage()
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.h
index 4fec561..2dddbfb 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.h
@@ -32,7 +32,7 @@
 
   TransformationAddConstantComposite(
       uint32_t fresh_id, uint32_t type_id,
-      const std::vector<uint32_t>& constituent_ids);
+      const std::vector<uint32_t>& constituent_ids, bool is_irrelevant);
 
   // - |message_.fresh_id| must be a fresh id
   // - |message_.type_id| must be the id of a composite type
@@ -42,9 +42,11 @@
       opt::IRContext* ir_context,
       const TransformationContext& transformation_context) const override;
 
-  // Adds an OpConstantComposite instruction defining a constant of type
-  // |message_.type_id|, using |message_.constituent_id| as constituents, with
-  // result id |message_.fresh_id|.
+  // - Adds an OpConstantComposite instruction defining a constant of type
+  //   |message_.type_id|, using |message_.constituent_id| as constituents, with
+  //   result id |message_.fresh_id|.
+  // - Creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is
+  //   true.
   void Apply(opt::IRContext* ir_context,
              TransformationContext* transformation_context) const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_scalar.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_scalar.cpp
index e13d08f..98bfbf0 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_scalar.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_scalar.cpp
@@ -24,9 +24,11 @@
     : message_(message) {}
 
 TransformationAddConstantScalar::TransformationAddConstantScalar(
-    uint32_t fresh_id, uint32_t type_id, std::vector<uint32_t> words) {
+    uint32_t fresh_id, uint32_t type_id, const std::vector<uint32_t>& words,
+    bool is_irrelevant) {
   message_.set_fresh_id(fresh_id);
   message_.set_type_id(type_id);
+  message_.set_is_irrelevant(is_irrelevant);
   for (auto word : words) {
     message_.add_word(word);
   }
@@ -60,7 +62,8 @@
 }
 
 void TransformationAddConstantScalar::Apply(
-    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   opt::Instruction::OperandList operand_list;
   for (auto word : message_.word()) {
     operand_list.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {word}});
@@ -75,6 +78,11 @@
   // validity of existing analyses.
   ir_context->InvalidateAnalysesExceptFor(
       opt::IRContext::Analysis::kAnalysisNone);
+
+  if (message_.is_irrelevant()) {
+    transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
+        message_.fresh_id());
+  }
 }
 
 protobufs::Transformation TransformationAddConstantScalar::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_scalar.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_scalar.h
index e0ed39f..06a77fc 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_scalar.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_scalar.h
@@ -31,7 +31,8 @@
       const protobufs::TransformationAddConstantScalar& message);
 
   TransformationAddConstantScalar(uint32_t fresh_id, uint32_t type_id,
-                                  std::vector<uint32_t> words);
+                                  const std::vector<uint32_t>& words,
+                                  bool is_irrelevant);
 
   // - |message_.fresh_id| must not be used by the module
   // - |message_.type_id| must be the id of a floating-point or integer type
@@ -42,6 +43,7 @@
       const TransformationContext& transformation_context) const override;
 
   // Adds a new OpConstant instruction with the given type and words.
+  // Creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true.
   void Apply(opt::IRContext* ir_context,
              TransformationContext* transformation_context) const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_copy_memory.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_copy_memory.cpp
new file mode 100644
index 0000000..e9c401d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_copy_memory.cpp
@@ -0,0 +1,193 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_copy_memory.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/opt/instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddCopyMemory::TransformationAddCopyMemory(
+    const protobufs::TransformationAddCopyMemory& message)
+    : message_(message) {}
+
+TransformationAddCopyMemory::TransformationAddCopyMemory(
+    const protobufs::InstructionDescriptor& instruction_descriptor,
+    uint32_t fresh_id, uint32_t source_id, SpvStorageClass storage_class,
+    uint32_t initializer_id) {
+  *message_.mutable_instruction_descriptor() = instruction_descriptor;
+  message_.set_fresh_id(fresh_id);
+  message_.set_source_id(source_id);
+  message_.set_storage_class(storage_class);
+  message_.set_initializer_id(initializer_id);
+}
+
+bool TransformationAddCopyMemory::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  // Check that target id is fresh.
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
+    return false;
+  }
+
+  // Check that instruction descriptor is valid. This also checks that
+  // |message_.instruction_descriptor| is not a global instruction.
+  auto* inst = FindInstruction(message_.instruction_descriptor(), ir_context);
+  if (!inst) {
+    return false;
+  }
+
+  // Check that we can insert OpCopyMemory before |instruction_descriptor|.
+  auto iter = fuzzerutil::GetIteratorForInstruction(
+      ir_context->get_instr_block(inst), inst);
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyMemory, iter)) {
+    return false;
+  }
+
+  // Check that source instruction exists and is valid.
+  auto* source_inst =
+      ir_context->get_def_use_mgr()->GetDef(message_.source_id());
+  if (!source_inst || !IsInstructionSupported(ir_context, source_inst)) {
+    return false;
+  }
+
+  // |storage_class| is either Function or Private.
+  if (message_.storage_class() != SpvStorageClassFunction &&
+      message_.storage_class() != SpvStorageClassPrivate) {
+    return false;
+  }
+
+  auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
+      ir_context, source_inst->type_id());
+
+  // OpTypePointer with |message_.storage_class| exists.
+  if (!fuzzerutil::MaybeGetPointerType(
+          ir_context, pointee_type_id,
+          static_cast<SpvStorageClass>(message_.storage_class()))) {
+    return false;
+  }
+
+  // Check that |initializer_id| exists and has valid type.
+  const auto* initializer_inst =
+      ir_context->get_def_use_mgr()->GetDef(message_.initializer_id());
+  if (!initializer_inst || initializer_inst->type_id() != pointee_type_id) {
+    return false;
+  }
+
+  // Check that domination rules are satisfied.
+  return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, inst,
+                                                    message_.source_id());
+}
+
+void TransformationAddCopyMemory::Apply(
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  // Insert OpCopyMemory before |instruction_descriptor|.
+  auto* insert_before_inst =
+      FindInstruction(message_.instruction_descriptor(), ir_context);
+  assert(insert_before_inst);
+
+  auto insert_before_iter = fuzzerutil::GetIteratorForInstruction(
+      ir_context->get_instr_block(insert_before_inst), insert_before_inst);
+
+  insert_before_iter.InsertBefore(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpCopyMemory, 0, 0,
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}},
+          {SPV_OPERAND_TYPE_ID, {message_.source_id()}}}));
+
+  // Add global or local variable to copy memory into.
+  auto storage_class = static_cast<SpvStorageClass>(message_.storage_class());
+  auto type_id = fuzzerutil::MaybeGetPointerType(
+      ir_context,
+      fuzzerutil::GetPointeeTypeIdFromPointerType(
+          ir_context, fuzzerutil::GetTypeId(ir_context, message_.source_id())),
+      storage_class);
+
+  if (storage_class == SpvStorageClassPrivate) {
+    fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_id(), type_id,
+                                  storage_class, message_.initializer_id());
+  } else {
+    assert(storage_class == SpvStorageClassFunction &&
+           "Storage class can be either Private or Function");
+    fuzzerutil::AddLocalVariable(ir_context, message_.fresh_id(), type_id,
+                                 ir_context->get_instr_block(insert_before_inst)
+                                     ->GetParent()
+                                     ->result_id(),
+                                 message_.initializer_id());
+  }
+
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+  // Even though the copy memory instruction will - at least temporarily - lead
+  // to the destination and source pointers referring to identical values, this
+  // fact is not guaranteed to hold throughout execution of the SPIR-V code
+  // since the source pointer could be over-written. We thus assume nothing
+  // about the destination pointer, and record this fact so that the destination
+  // pointer can be used freely by other fuzzer passes.
+  transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      message_.fresh_id());
+
+  // Make sure our changes are analyzed
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddCopyMemory::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_copy_memory() = message_;
+  return result;
+}
+
+bool TransformationAddCopyMemory::IsInstructionSupported(
+    opt::IRContext* ir_context, opt::Instruction* inst) {
+  if (!inst->result_id() || !inst->type_id() ||
+      inst->opcode() == SpvOpConstantNull || inst->opcode() == SpvOpUndef) {
+    return false;
+  }
+
+  const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
+  assert(type && "Instruction must have a valid type");
+
+  return type->AsPointer() &&
+         CanUsePointeeWithCopyMemory(*type->AsPointer()->pointee_type());
+}
+
+bool TransformationAddCopyMemory::CanUsePointeeWithCopyMemory(
+    const opt::analysis::Type& type) {
+  switch (type.kind()) {
+    case opt::analysis::Type::kBool:
+    case opt::analysis::Type::kInteger:
+    case opt::analysis::Type::kFloat:
+    case opt::analysis::Type::kArray:
+      return true;
+    case opt::analysis::Type::kVector:
+      return CanUsePointeeWithCopyMemory(*type.AsVector()->element_type());
+    case opt::analysis::Type::kMatrix:
+      return CanUsePointeeWithCopyMemory(*type.AsMatrix()->element_type());
+    case opt::analysis::Type::kStruct:
+      return std::all_of(type.AsStruct()->element_types().begin(),
+                         type.AsStruct()->element_types().end(),
+                         [](const opt::analysis::Type* element) {
+                           return CanUsePointeeWithCopyMemory(*element);
+                         });
+    default:
+      return false;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_copy_memory.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_copy_memory.h
new file mode 100644
index 0000000..138d992
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_copy_memory.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_COPY_MEMORY_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_COPY_MEMORY_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddCopyMemory : public Transformation {
+ public:
+  explicit TransformationAddCopyMemory(
+      const protobufs::TransformationAddCopyMemory& message);
+
+  TransformationAddCopyMemory(
+      const protobufs::InstructionDescriptor& instruction_descriptor,
+      uint32_t fresh_id, uint32_t source_id, SpvStorageClass storage_class,
+      uint32_t initializer_id);
+
+  // - |instruction_descriptor| must point to a valid instruction in the module.
+  // - it should be possible to insert OpCopyMemory before
+  //   |instruction_descriptor| (i.e. the module remains valid after the
+  //   insertion).
+  // - |source_id| must be a result id for some valid instruction in the module.
+  // - |fresh_id| must be a fresh id to copy memory into.
+  // - type of |source_id| must be OpTypePointer where pointee can be used with
+  //   OpCopyMemory.
+  // - |storage_class| must be either Private or Function.
+  // - type ids of instructions with result ids |source_id| and |initialize_id|
+  //   must be the same.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // A global or local variable with id |target_id| and |storage_class| class is
+  // created. An 'OpCopyMemory %fresh_id %source_id' instruction is inserted
+  // before the |instruction_descriptor|.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Returns true if we can copy memory from |instruction| using OpCopyMemory.
+  static bool IsInstructionSupported(opt::IRContext* ir_context,
+                                     opt::Instruction* inst);
+
+ private:
+  // Returns whether the type, pointed to by some OpTypePointer, can be used
+  // with OpCopyMemory instruction.
+  static bool CanUsePointeeWithCopyMemory(const opt::analysis::Type& type);
+
+  protobufs::TransformationAddCopyMemory message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_COPY_MEMORY_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
index b246c3f..5b06e97 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_block.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_block.cpp
@@ -32,7 +32,8 @@
 }
 
 bool TransformationAddDeadBlock::IsApplicable(
-    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   // The new block's id must be fresh.
   if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
@@ -40,8 +41,8 @@
 
   // First, we check that a constant with the same value as
   // |message_.condition_value| is present.
-  if (!fuzzerutil::MaybeGetBoolConstantId(ir_context,
-                                          message_.condition_value())) {
+  if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+                                        message_.condition_value(), false)) {
     // The required constant is not present, so the transformation cannot be
     // applied.
     return false;
@@ -92,8 +93,8 @@
       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(ir_context,
-                                                    message_.condition_value());
+  auto bool_id = fuzzerutil::MaybeGetBoolConstant(
+      ir_context, *transformation_context, message_.condition_value(), false);
 
   // Make a new block that unconditionally branches to the original successor
   // block.
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 db9de7d..284174a 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
@@ -85,12 +85,10 @@
     // loop as part of the loop, but it is not legal to jump from a loop's
     // continue construct to the loop's merge (except from the back-edge block),
     // so we need to check for this case.
-    //
-    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2577): We do not
-    //  currently allow a dead break from a back edge block, but we could and
-    //  ultimately should.
     return !fuzzerutil::BlockIsInLoopContinueConstruct(
-        ir_context, message_.from_block(), containing_construct);
+               ir_context, message_.from_block(), containing_construct) ||
+           fuzzerutil::BlockIsBackEdge(ir_context, message_.from_block(),
+                                       containing_construct);
   }
 
   // Case (3) holds if and only if |to_block| is the merge block for this
@@ -102,7 +100,9 @@
       message_.to_block() ==
           ir_context->cfg()->block(containing_loop_header)->MergeBlockId()) {
     return !fuzzerutil::BlockIsInLoopContinueConstruct(
-        ir_context, message_.from_block(), containing_loop_header);
+               ir_context, message_.from_block(), containing_loop_header) ||
+           fuzzerutil::BlockIsBackEdge(ir_context, message_.from_block(),
+                                       containing_loop_header);
   }
   return false;
 }
@@ -112,8 +112,9 @@
     const TransformationContext& transformation_context) const {
   // First, we check that a constant with the same value as
   // |message_.break_condition_value| is present.
-  if (!fuzzerutil::MaybeGetBoolConstantId(ir_context,
-                                          message_.break_condition_value())) {
+  if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+                                        message_.break_condition_value(),
+                                        false)) {
     // The required constant is not present, so the transformation cannot be
     // applied.
     return false;
@@ -179,14 +180,15 @@
   // the validator is complete with respect to checking structured control flow
   // rules.
   auto cloned_context = fuzzerutil::CloneIRContext(ir_context);
-  ApplyImpl(cloned_context.get());
+  ApplyImpl(cloned_context.get(), transformation_context);
   return fuzzerutil::IsValid(cloned_context.get(),
                              transformation_context.GetValidatorOptions());
 }
 
 void TransformationAddDeadBreak::Apply(
-    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
-  ApplyImpl(ir_context);
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  ApplyImpl(ir_context, *transformation_context);
   // Invalidate all analyses
   ir_context->InvalidateAnalysesExceptFor(
       opt::IRContext::Analysis::kAnalysisNone);
@@ -199,11 +201,14 @@
 }
 
 void TransformationAddDeadBreak::ApplyImpl(
-    spvtools::opt::IRContext* ir_context) const {
+    spvtools::opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
       ir_context, ir_context->cfg()->block(message_.from_block()),
       ir_context->cfg()->block(message_.to_block()),
-      message_.break_condition_value(), message_.phi_id());
+      fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+                                       message_.break_condition_value(), false),
+      message_.phi_id());
 }
 
 }  // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.h
index 0ea9210..f010b11 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.h
@@ -75,7 +75,8 @@
   // module.  This is only invoked by 'IsApplicable' after certain basic
   // applicability checks have been made, ensuring that the invocation of this
   // method is legal.
-  void ApplyImpl(opt::IRContext* ir_context) const;
+  void ApplyImpl(opt::IRContext* ir_context,
+                 const TransformationContext& transformation_context) const;
 
   protobufs::TransformationAddDeadBreak message_;
 };
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 1fc6d67..b5b7ae3 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
@@ -38,8 +38,9 @@
     const TransformationContext& transformation_context) const {
   // First, we check that a constant with the same value as
   // |message_.continue_condition_value| is present.
-  if (!fuzzerutil::MaybeGetBoolConstantId(
-          ir_context, message_.continue_condition_value())) {
+  if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+                                        message_.continue_condition_value(),
+                                        false)) {
     // The required constant is not present, so the transformation cannot be
     // applied.
     return false;
@@ -119,14 +120,15 @@
   // the validator is complete with respect to checking structured control flow
   // rules.
   auto cloned_context = fuzzerutil::CloneIRContext(ir_context);
-  ApplyImpl(cloned_context.get());
+  ApplyImpl(cloned_context.get(), transformation_context);
   return fuzzerutil::IsValid(cloned_context.get(),
                              transformation_context.GetValidatorOptions());
 }
 
 void TransformationAddDeadContinue::Apply(
-    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
-  ApplyImpl(ir_context);
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  ApplyImpl(ir_context, *transformation_context);
   // Invalidate all analyses
   ir_context->InvalidateAnalysesExceptFor(
       opt::IRContext::Analysis::kAnalysisNone);
@@ -139,7 +141,8 @@
 }
 
 void TransformationAddDeadContinue::ApplyImpl(
-    spvtools::opt::IRContext* ir_context) const {
+    spvtools::opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   auto bb_from = ir_context->cfg()->block(message_.from_block());
   auto continue_block =
       bb_from->IsLoopHeader()
@@ -149,7 +152,10 @@
   assert(continue_block && "message_.from_block must be in a loop.");
   fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
       ir_context, bb_from, ir_context->cfg()->block(continue_block),
-      message_.continue_condition_value(), message_.phi_id());
+      fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+                                       message_.continue_condition_value(),
+                                       false),
+      message_.phi_id());
 }
 
 }  // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.h
index 1053c16..b977bb2 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.h
@@ -72,7 +72,8 @@
   // module.  This is only invoked by 'IsApplicable' after certain basic
   // applicability checks have been made, ensuring that the invocation of this
   // method is legal.
-  void ApplyImpl(opt::IRContext* ir_context) const;
+  void ApplyImpl(opt::IRContext* ir_context,
+                 const TransformationContext& transformation_context) const;
 
   protobufs::TransformationAddDeadContinue message_;
 };
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 90276ed..380d59c 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.cpp
@@ -794,8 +794,8 @@
 
     // 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(ir_context, *should_be_composite_type);
+    uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
+        *should_be_composite_type, ir_context);
 
     // Get the instruction associated with the index and figure out its integer
     // type.
@@ -873,28 +873,6 @@
   return true;
 }
 
-uint32_t TransformationAddFunction::GetBoundForCompositeIndex(
-    opt::IRContext* ir_context, const opt::Instruction& composite_type_inst) {
-  switch (composite_type_inst.opcode()) {
-    case SpvOpTypeArray:
-      return fuzzerutil::GetArraySize(composite_type_inst, ir_context);
-    case SpvOpTypeMatrix:
-    case SpvOpTypeVector:
-      return composite_type_inst.GetSingleWordInOperand(1);
-    case SpvOpTypeStruct: {
-      return fuzzerutil::GetNumberOfStructMembers(composite_type_inst);
-    }
-    case SpvOpTypeRuntimeArray:
-      assert(false &&
-             "GetBoundForCompositeIndex should not be invoked with an "
-             "OpTypeRuntimeArray, which does not have a static bound.");
-      return 0;
-    default:
-      assert(false && "Unknown composite type.");
-      return 0;
-  }
-}
-
 opt::Instruction* TransformationAddFunction::FollowCompositeIndex(
     opt::IRContext* ir_context, const opt::Instruction& composite_type_inst,
     uint32_t index_id) {
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 5af197b..4a84c70 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.h
@@ -58,13 +58,6 @@
 
   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* ir_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.
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 6464bfb..303c4d9 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
@@ -93,32 +93,10 @@
 void TransformationAddGlobalVariable::Apply(
     opt::IRContext* ir_context,
     TransformationContext* transformation_context) const {
-  opt::Instruction::OperandList input_operands;
-  input_operands.push_back(
-      {SPV_OPERAND_TYPE_STORAGE_CLASS, {message_.storage_class()}});
-  if (message_.initializer_id()) {
-    input_operands.push_back(
-        {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
-  }
-  ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpVariable, message_.type_id(), message_.fresh_id(),
-      input_operands));
-  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-
-  if (GlobalVariablesMustBeDeclaredInEntryPointInterfaces(ir_context)) {
-    // Conservatively add this global to the interface of every entry point in
-    // the module.  This means that the global is available for other
-    // transformations to use.
-    //
-    // A downside of this is that the global will be in the interface even if it
-    // ends up never being used.
-    //
-    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3111) revisit
-    //  this if a more thorough approach to entry point interfaces is taken.
-    for (auto& entry_point : ir_context->module()->entry_points()) {
-      entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {message_.fresh_id()}});
-    }
-  }
+  fuzzerutil::AddGlobalVariable(
+      ir_context, message_.fresh_id(), message_.type_id(),
+      static_cast<SpvStorageClass>(message_.storage_class()),
+      message_.initializer_id());
 
   if (message_.value_is_irrelevant()) {
     transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
@@ -137,22 +115,5 @@
   return result;
 }
 
-bool TransformationAddGlobalVariable::
-    GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
-        opt::IRContext* ir_context) {
-  // TODO(afd): We capture the universal environments for which this requirement
-  //  holds.  The check should be refined on demand for other target
-  //  environments.
-  switch (ir_context->grammar().target_env()) {
-    case SPV_ENV_UNIVERSAL_1_0:
-    case SPV_ENV_UNIVERSAL_1_1:
-    case SPV_ENV_UNIVERSAL_1_2:
-    case SPV_ENV_UNIVERSAL_1_3:
-      return false;
-    default:
-      return true;
-  }
-}
-
 }  // namespace fuzz
 }  // namespace spvtools
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 289af9e..89bd044 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
@@ -58,12 +58,6 @@
   protobufs::Transformation ToMessage() const override;
 
  private:
-  // Returns true if and only if the SPIR-V version being used requires that
-  // global variables accessed in the static call graph of an entry point need
-  // to be listed in that entry point's interface.
-  static bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
-      opt::IRContext* ir_context);
-
   protobufs::TransformationAddGlobalVariable message_;
 };
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_image_sample_unused_components.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_image_sample_unused_components.cpp
new file mode 100644
index 0000000..1be1d43
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_image_sample_unused_components.cpp
@@ -0,0 +1,117 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_image_sample_unused_components.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddImageSampleUnusedComponents::
+    TransformationAddImageSampleUnusedComponents(
+        const spvtools::fuzz::protobufs::
+            TransformationAddImageSampleUnusedComponents& message)
+    : message_(message) {}
+
+TransformationAddImageSampleUnusedComponents::
+    TransformationAddImageSampleUnusedComponents(
+        uint32_t coordinate_with_unused_components_id,
+        const protobufs::InstructionDescriptor& instruction_descriptor) {
+  message_.set_coordinate_with_unused_components_id(
+      coordinate_with_unused_components_id);
+  *message_.mutable_instruction_descriptor() = instruction_descriptor;
+}
+
+bool TransformationAddImageSampleUnusedComponents::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  auto image_sample_instruction =
+      FindInstruction(message_.instruction_descriptor(), ir_context);
+
+  // The image sample instruction must be defined.
+  if (image_sample_instruction == nullptr) {
+    return false;
+  }
+
+  // The instruction must be an image sample instruction.
+  if (!spvOpcodeIsImageSample(image_sample_instruction->opcode())) {
+    return false;
+  }
+
+  uint32_t coordinate_id = image_sample_instruction->GetSingleWordInOperand(1);
+  auto coordinate_instruction =
+      ir_context->get_def_use_mgr()->GetDef(coordinate_id);
+  auto coordinate_type =
+      ir_context->get_type_mgr()->GetType(coordinate_instruction->type_id());
+
+  // It must be possible to add unused components.
+  if (coordinate_type->AsVector() &&
+      coordinate_type->AsVector()->element_count() == 4) {
+    return false;
+  }
+
+  auto coordinate_with_unused_components_instruction =
+      ir_context->get_def_use_mgr()->GetDef(
+          message_.coordinate_with_unused_components_id());
+
+  // The coordinate with unused components instruction must be defined.
+  if (coordinate_with_unused_components_instruction == nullptr) {
+    return false;
+  }
+
+  // It must be an OpCompositeConstruct instruction such that it can be checked
+  // that the original components are present.
+  if (coordinate_with_unused_components_instruction->opcode() !=
+      SpvOpCompositeConstruct) {
+    return false;
+  }
+
+  // The first constituent must be the original coordinate.
+  if (coordinate_with_unused_components_instruction->GetSingleWordInOperand(
+          0) != coordinate_id) {
+    return false;
+  }
+
+  auto coordinate_with_unused_components_type =
+      ir_context->get_type_mgr()->GetType(
+          coordinate_with_unused_components_instruction->type_id());
+
+  // |coordinate_with_unused_components_type| must be a vector.
+  if (!coordinate_with_unused_components_type->AsVector()) {
+    return false;
+  }
+
+  return true;
+}
+
+void TransformationAddImageSampleUnusedComponents::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  // Sets the coordinate operand.
+  auto image_sample_instruction =
+      FindInstruction(message_.instruction_descriptor(), ir_context);
+  image_sample_instruction->SetInOperand(
+      1, {message_.coordinate_with_unused_components_id()});
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation
+TransformationAddImageSampleUnusedComponents::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_image_sample_unused_components() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_image_sample_unused_components.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_image_sample_unused_components.h
new file mode 100644
index 0000000..7493481
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_image_sample_unused_components.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddImageSampleUnusedComponents : public Transformation {
+ public:
+  explicit TransformationAddImageSampleUnusedComponents(
+      const protobufs::TransformationAddImageSampleUnusedComponents& message);
+
+  TransformationAddImageSampleUnusedComponents(
+      uint32_t coordinate_with_unused_components_id,
+      const protobufs::InstructionDescriptor& instruction_descriptor);
+
+  // - |coordinate_with_unused_components_id| must identify a vector such that
+  //   the first components match the components of the image sample coordinate.
+  // - |message_.instruction_descriptor| must identify an image sample
+  //   instruction
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Add unused components to an image sample coordinate by replacing the
+  // coordinate with |coordinate_with_unused_components_id|.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddImageSampleUnusedComponents message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
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
index 5136249..a6b31b4 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.cpp
@@ -70,18 +70,10 @@
 void TransformationAddLocalVariable::Apply(
     opt::IRContext* ir_context,
     TransformationContext* transformation_context) const {
-  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  fuzzerutil::FindFunction(ir_context, message_.function_id())
-      ->begin()
-      ->begin()
-      ->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpVariable, message_.type_id(), message_.fresh_id(),
-          opt::Instruction::OperandList(
-              {{SPV_OPERAND_TYPE_STORAGE_CLASS,
-                {
+  fuzzerutil::AddLocalVariable(ir_context, message_.fresh_id(),
+                               message_.type_id(), message_.function_id(),
+                               message_.initializer_id());
 
-                    SpvStorageClassFunction}},
-               {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}}})));
   if (message_.value_is_irrelevant()) {
     transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
         message_.fresh_id());
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_parameter.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_parameter.cpp
new file mode 100644
index 0000000..cc32362
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_parameter.cpp
@@ -0,0 +1,169 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_parameter.h"
+
+#include <source/spirv_constant.h>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddParameter::TransformationAddParameter(
+    const protobufs::TransformationAddParameter& message)
+    : message_(message) {}
+
+TransformationAddParameter::TransformationAddParameter(
+    uint32_t function_id, uint32_t parameter_fresh_id, uint32_t initializer_id,
+    uint32_t function_type_fresh_id) {
+  message_.set_function_id(function_id);
+  message_.set_parameter_fresh_id(parameter_fresh_id);
+  message_.set_initializer_id(initializer_id);
+  message_.set_function_type_fresh_id(function_type_fresh_id);
+}
+
+bool TransformationAddParameter::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  // Check that function exists
+  const auto* function =
+      fuzzerutil::FindFunction(ir_context, message_.function_id());
+  if (!function ||
+      fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
+    return false;
+  }
+
+  // Check that |initializer_id| is valid.
+  const auto* initializer_inst =
+      ir_context->get_def_use_mgr()->GetDef(message_.initializer_id());
+
+  if (!initializer_inst) {
+    return false;
+  }
+
+  // Check that initializer's type is valid.
+  const auto* initializer_type =
+      ir_context->get_type_mgr()->GetType(initializer_inst->type_id());
+
+  if (!initializer_type || !IsParameterTypeSupported(*initializer_type)) {
+    return false;
+  }
+
+  return fuzzerutil::IsFreshId(ir_context, message_.parameter_fresh_id()) &&
+         fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()) &&
+         message_.parameter_fresh_id() != message_.function_type_fresh_id();
+}
+
+void TransformationAddParameter::Apply(
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  // Find the function that will be transformed
+  auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id());
+  assert(function && "Can't find the function");
+
+  auto parameter_type_id =
+      fuzzerutil::GetTypeId(ir_context, message_.initializer_id());
+  assert(parameter_type_id != 0 && "Initializer has invalid type");
+
+  // Add new parameters to the function.
+  function->AddParameter(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpFunctionParameter, parameter_type_id,
+      message_.parameter_fresh_id(), opt::Instruction::OperandList()));
+
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.parameter_fresh_id());
+
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
+  //  Add an PointeeValueIsIrrelevant fact if the parameter is a pointer.
+
+  // Mark new parameter as irrelevant so that we can replace its use with some
+  // other id.
+  transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
+      message_.parameter_fresh_id());
+
+  // Fix all OpFunctionCall instructions.
+  ir_context->get_def_use_mgr()->ForEachUser(
+      &function->DefInst(), [this](opt::Instruction* call) {
+        if (call->opcode() != SpvOpFunctionCall ||
+            call->GetSingleWordInOperand(0) != message_.function_id()) {
+          return;
+        }
+
+        call->AddOperand({SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
+      });
+
+  auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
+  assert(old_function_type && "Function must have a valid type");
+
+  if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
+    // Adjust existing function type if it is used only by this function.
+    old_function_type->AddOperand({SPV_OPERAND_TYPE_ID, {parameter_type_id}});
+
+    // We must make sure that all dependencies of |old_function_type| are
+    // evaluated before |old_function_type| (i.e. the domination rules are not
+    // broken). Thus, we move |old_function_type| to the end of the list of all
+    // types in the module.
+    old_function_type->RemoveFromList();
+    ir_context->AddType(std::unique_ptr<opt::Instruction>(old_function_type));
+  } else {
+    // Otherwise, either create a new type or use an existing one.
+    std::vector<uint32_t> type_ids;
+    type_ids.reserve(old_function_type->NumInOperands() + 1);
+
+    for (uint32_t i = 0, n = old_function_type->NumInOperands(); i < n; ++i) {
+      type_ids.push_back(old_function_type->GetSingleWordInOperand(i));
+    }
+
+    type_ids.push_back(parameter_type_id);
+
+    function->DefInst().SetInOperand(
+        1, {fuzzerutil::FindOrCreateFunctionType(
+               ir_context, message_.function_type_fresh_id(), type_ids)});
+  }
+
+  // Make sure our changes are analyzed.
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddParameter::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_parameter() = message_;
+  return result;
+}
+
+bool TransformationAddParameter::IsParameterTypeSupported(
+    const opt::analysis::Type& type) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
+  //  Think about other type instructions we can add here.
+  switch (type.kind()) {
+    case opt::analysis::Type::kBool:
+    case opt::analysis::Type::kInteger:
+    case opt::analysis::Type::kFloat:
+    case opt::analysis::Type::kArray:
+    case opt::analysis::Type::kMatrix:
+    case opt::analysis::Type::kVector:
+      return true;
+    case opt::analysis::Type::kStruct:
+      return std::all_of(type.AsStruct()->element_types().begin(),
+                         type.AsStruct()->element_types().end(),
+                         [](const opt::analysis::Type* element_type) {
+                           return IsParameterTypeSupported(*element_type);
+                         });
+    default:
+      return false;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_parameter.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_parameter.h
new file mode 100644
index 0000000..e6b9019
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_parameter.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_PARAMETER_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddParameter : public Transformation {
+ public:
+  explicit TransformationAddParameter(
+      const protobufs::TransformationAddParameter& message);
+
+  TransformationAddParameter(uint32_t function_id, uint32_t parameter_fresh_id,
+                             uint32_t initializer_id,
+                             uint32_t function_type_fresh_id);
+
+  // - |function_id| must be a valid result id of some non-entry-point function
+  //   in the module.
+  // - |initializer_id| must be a valid result id of some instruction in the
+  //   module. Instruction's type must be supported by this transformation
+  //   as specified by IsParameterTypeSupported function.
+  // - |parameter_fresh_id| and |function_type_fresh_id| are fresh ids and are
+  //   not equal.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // - Creates a new OpFunctionParameter instruction with result id
+  //   |parameter_fresh_id| for the function with |function_id|.
+  // - Adjusts function's type to include a new parameter.
+  // - Adds |initializer_id| as a new operand to every OpFunctionCall
+  //   instruction that calls the function.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Returns true if the type of the parameter is supported by this
+  // transformation.
+  static bool IsParameterTypeSupported(const opt::analysis::Type& type);
+
+ private:
+  protobufs::TransformationAddParameter message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_relaxed_decoration.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_relaxed_decoration.cpp
new file mode 100644
index 0000000..effa71d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_relaxed_decoration.cpp
@@ -0,0 +1,146 @@
+// 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_relaxed_decoration.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration(
+    const spvtools::fuzz::protobufs::TransformationAddRelaxedDecoration&
+        message)
+    : message_(message) {}
+
+TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration(
+    uint32_t result_id) {
+  message_.set_result_id(result_id);
+}
+
+bool TransformationAddRelaxedDecoration::IsApplicable(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
+  // |message_.result_id| must be the id of an instruction.
+  auto instr = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+  if (!instr) {
+    return false;
+  }
+  opt::BasicBlock* cur_block = ir_context->get_instr_block(instr);
+  // The instruction must have a block.
+  if (cur_block == nullptr) {
+    return false;
+  }
+  // |cur_block| must be a dead block.
+  if (!(transformation_context.GetFactManager()->BlockIsDead(
+          cur_block->id()))) {
+    return false;
+  }
+  // The instruction must be numeric.
+  return IsNumeric(instr->opcode());
+}
+
+void TransformationAddRelaxedDecoration::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  // Add a RelaxedPrecision decoration targeting |message_.result_id|.
+  ir_context->get_decoration_mgr()->AddDecoration(
+      message_.result_id(), SpvDecorationRelaxedPrecision);
+}
+
+protobufs::Transformation TransformationAddRelaxedDecoration::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_add_relaxed_decoration() = message_;
+  return result;
+}
+
+bool TransformationAddRelaxedDecoration::IsNumeric(uint32_t opcode) {
+  switch (opcode) {
+    case SpvOpConvertFToU:
+    case SpvOpConvertFToS:
+    case SpvOpConvertSToF:
+    case SpvOpConvertUToF:
+    case SpvOpUConvert:
+    case SpvOpSConvert:
+    case SpvOpFConvert:
+    case SpvOpConvertPtrToU:
+    case SpvOpSatConvertSToU:
+    case SpvOpSatConvertUToS:
+    case SpvOpVectorExtractDynamic:
+    case SpvOpVectorInsertDynamic:
+    case SpvOpVectorShuffle:
+    case SpvOpTranspose:
+    case SpvOpSNegate:
+    case SpvOpFNegate:
+    case SpvOpIAdd:
+    case SpvOpFAdd:
+    case SpvOpISub:
+    case SpvOpFSub:
+    case SpvOpIMul:
+    case SpvOpFMul:
+    case SpvOpUDiv:
+    case SpvOpSDiv:
+    case SpvOpFDiv:
+    case SpvOpUMod:
+    case SpvOpSRem:
+    case SpvOpSMod:
+    case SpvOpFRem:
+    case SpvOpFMod:
+    case SpvOpVectorTimesScalar:
+    case SpvOpMatrixTimesScalar:
+    case SpvOpVectorTimesMatrix:
+    case SpvOpMatrixTimesVector:
+    case SpvOpMatrixTimesMatrix:
+    case SpvOpOuterProduct:
+    case SpvOpDot:
+    case SpvOpIAddCarry:
+    case SpvOpISubBorrow:
+    case SpvOpUMulExtended:
+    case SpvOpSMulExtended:
+    case SpvOpShiftRightLogical:
+    case SpvOpShiftRightArithmetic:
+    case SpvOpShiftLeftLogical:
+    case SpvOpBitwiseOr:
+    case SpvOpBitwiseXor:
+    case SpvOpBitwiseAnd:
+    case SpvOpNot:
+    case SpvOpBitFieldInsert:
+    case SpvOpBitFieldSExtract:
+    case SpvOpBitFieldUExtract:
+    case SpvOpBitReverse:
+    case SpvOpBitCount:
+    case SpvOpAtomicLoad:
+    case SpvOpAtomicStore:
+    case SpvOpAtomicExchange:
+    case SpvOpAtomicCompareExchange:
+    case SpvOpAtomicCompareExchangeWeak:
+    case SpvOpAtomicIIncrement:
+    case SpvOpAtomicIDecrement:
+    case SpvOpAtomicIAdd:
+    case SpvOpAtomicISub:
+    case SpvOpAtomicSMin:
+    case SpvOpAtomicUMin:
+    case SpvOpAtomicSMax:
+    case SpvOpAtomicUMax:
+    case SpvOpAtomicAnd:
+    case SpvOpAtomicOr:
+    case SpvOpAtomicXor:
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
\ No newline at end of file
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_relaxed_decoration.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_relaxed_decoration.h
new file mode 100644
index 0000000..30c1abf
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_relaxed_decoration.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 SPIRV_TOOLS_TRANSFORMATION_ADD_RELAXED_DECORATION_H
+#define SPIRV_TOOLS_TRANSFORMATION_ADD_RELAXED_DECORATION_H
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddRelaxedDecoration : public Transformation {
+ public:
+  explicit TransformationAddRelaxedDecoration(
+      const protobufs::TransformationAddRelaxedDecoration& message);
+
+  explicit TransformationAddRelaxedDecoration(uint32_t fresh_id);
+
+  // - |message_.result_id| must be the result id of an instruction, which is
+  //   located in a dead block and Relaxed decoration can be applied.
+  // - It does not matter whether this instruction is already annotated with the
+  //   Relaxed decoration.
+  bool IsApplicable(
+
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Adds a decoration of the form:
+  //   'OpDecoration |message_.result_id| RelaxedPrecision'
+  // to the module.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Returns true if and only if |opcode| is the opcode of an instruction
+  // that operates on 32-bit integers and 32-bit floats
+  // as defined by the SPIR-V specification.
+  static bool IsNumeric(uint32_t opcode);
+
+ private:
+  protobufs::TransformationAddRelaxedDecoration message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_TRANSFORMATION_ADD_RELAXED_DECORATION_H
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_spec_constant_op.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_spec_constant_op.cpp
new file mode 100644
index 0000000..d6a7083
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_spec_constant_op.cpp
@@ -0,0 +1,84 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 <utility>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_add_spec_constant_op.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddSpecConstantOp::TransformationAddSpecConstantOp(
+    spvtools::fuzz::protobufs::TransformationAddSpecConstantOp message)
+    : message_(std::move(message)) {}
+
+TransformationAddSpecConstantOp::TransformationAddSpecConstantOp(
+    uint32_t fresh_id, uint32_t type_id, SpvOp opcode,
+    const opt::Instruction::OperandList& operands) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_type_id(type_id);
+  message_.set_opcode(opcode);
+  for (const auto& operand : operands) {
+    auto* op = message_.add_operand();
+    op->set_operand_type(operand.type);
+    for (auto word : operand.words) {
+      op->add_operand_data(word);
+    }
+  }
+}
+
+bool TransformationAddSpecConstantOp::IsApplicable(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
+  auto clone = fuzzerutil::CloneIRContext(ir_context);
+  ApplyImpl(clone.get());
+  return fuzzerutil::IsValid(clone.get(),
+                             transformation_context.GetValidatorOptions());
+}
+
+void TransformationAddSpecConstantOp::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  ApplyImpl(ir_context);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+}
+
+void TransformationAddSpecConstantOp::ApplyImpl(
+    opt::IRContext* ir_context) const {
+  opt::Instruction::OperandList operands = {
+      {SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER, {message_.opcode()}}};
+
+  for (const auto& operand : message_.operand()) {
+    std::vector<uint32_t> words(operand.operand_data().begin(),
+                                operand.operand_data().end());
+    operands.push_back({static_cast<spv_operand_type_t>(operand.operand_type()),
+                        std::move(words)});
+  }
+
+  ir_context->AddGlobalValue(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpSpecConstantOp, message_.type_id(), message_.fresh_id(),
+      std::move(operands)));
+
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+}
+
+protobufs::Transformation TransformationAddSpecConstantOp::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_spec_constant_op() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_spec_constant_op.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_spec_constant_op.h
new file mode 100644
index 0000000..c0f7154
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_spec_constant_op.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_SPEC_CONSTANT_OP_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_SPEC_CONSTANT_OP_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddSpecConstantOp : public Transformation {
+ public:
+  explicit TransformationAddSpecConstantOp(
+      protobufs::TransformationAddSpecConstantOp message);
+
+  TransformationAddSpecConstantOp(
+      uint32_t fresh_id, uint32_t type_id, SpvOp opcode,
+      const opt::Instruction::OperandList& operands);
+
+  // - |fresh_id| is a fresh result id in the module.
+  // - |type_id| is a valid result id of some OpType* instruction in the
+  // module. It is also a valid type id with respect to |opcode|.
+  // - |opcode| is one of the opcodes supported by OpSpecConstantOp.
+  // - |operands| are valid with respect to |opcode|
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // |%fresh_id = OpSpecConstantOp %type_id opcode operands...| is added to the
+  // module.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  void ApplyImpl(opt::IRContext* ir_context) const;
+
+  protobufs::TransformationAddSpecConstantOp message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_SPEC_CONSTANT_OP_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_synonym.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_synonym.cpp
new file mode 100644
index 0000000..6a93e61
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_synonym.cpp
@@ -0,0 +1,313 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_synonym.h"
+
+#include <utility>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddSynonym::TransformationAddSynonym(
+    protobufs::TransformationAddSynonym message)
+    : message_(std::move(message)) {}
+
+TransformationAddSynonym::TransformationAddSynonym(
+    uint32_t result_id,
+    protobufs::TransformationAddSynonym::SynonymType synonym_type,
+    uint32_t synonym_fresh_id,
+    const protobufs::InstructionDescriptor& insert_before) {
+  message_.set_result_id(result_id);
+  message_.set_synonym_type(synonym_type);
+  message_.set_synonym_fresh_id(synonym_fresh_id);
+  *message_.mutable_insert_before() = insert_before;
+}
+
+bool TransformationAddSynonym::IsApplicable(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
+  assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(
+             message_.synonym_type()) &&
+         "Synonym type is invalid");
+
+  // |synonym_fresh_id| must be fresh.
+  if (!fuzzerutil::IsFreshId(ir_context, message_.synonym_fresh_id())) {
+    return false;
+  }
+
+  // Check that |message_.result_id| is valid.
+  auto* synonym = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+  if (!synonym) {
+    return false;
+  }
+
+  // Check that we can apply |synonym_type| to |result_id|.
+  if (!IsInstructionValid(ir_context, transformation_context, synonym,
+                          message_.synonym_type())) {
+    return false;
+  }
+
+  // Check that |insert_before| is valid.
+  auto* insert_before_inst =
+      FindInstruction(message_.insert_before(), ir_context);
+  if (!insert_before_inst) {
+    return false;
+  }
+
+  // Check that we can insert |message._synonymous_instruction| before
+  // |message_.insert_before| instruction. We use OpIAdd to represent some
+  // instruction that can produce a synonym.
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd,
+                                                    insert_before_inst)) {
+    return false;
+  }
+
+  // A constant instruction must be present in the module if required.
+  if (IsAdditionalConstantRequired(message_.synonym_type()) &&
+      MaybeGetConstantId(ir_context, transformation_context) == 0) {
+    return false;
+  }
+
+  // Domination rules must be satisfied.
+  return fuzzerutil::IdIsAvailableBeforeInstruction(
+      ir_context, insert_before_inst, message_.result_id());
+}
+
+void TransformationAddSynonym::Apply(
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  // Add a synonymous instruction.
+  FindInstruction(message_.insert_before(), ir_context)
+      ->InsertBefore(
+          MakeSynonymousInstruction(ir_context, *transformation_context));
+
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id());
+
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+
+  // Propagate PointeeValueIsIrrelevant fact.
+  const auto* new_synonym_type = ir_context->get_type_mgr()->GetType(
+      fuzzerutil::GetTypeId(ir_context, message_.synonym_fresh_id()));
+  assert(new_synonym_type && "New synonym should have a valid type");
+
+  if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
+          message_.result_id()) &&
+      new_synonym_type->AsPointer()) {
+    transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+        message_.synonym_fresh_id());
+  }
+
+  // Mark two ids as synonymous.
+  transformation_context->GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(message_.result_id(), {}),
+      MakeDataDescriptor(message_.synonym_fresh_id(), {}), ir_context);
+}
+
+protobufs::Transformation TransformationAddSynonym::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_synonym() = message_;
+  return result;
+}
+
+bool TransformationAddSynonym::IsInstructionValid(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context, opt::Instruction* inst,
+    protobufs::TransformationAddSynonym::SynonymType synonym_type) {
+  // Instruction must have a result id, type id. We skip OpUndef and
+  // OpConstantNull.
+  if (!inst || !inst->result_id() || !inst->type_id() ||
+      inst->opcode() == SpvOpUndef || inst->opcode() == SpvOpConstantNull) {
+    return false;
+  }
+
+  if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, inst)) {
+    return false;
+  }
+
+  switch (synonym_type) {
+    case protobufs::TransformationAddSynonym::ADD_ZERO:
+    case protobufs::TransformationAddSynonym::SUB_ZERO:
+    case protobufs::TransformationAddSynonym::MUL_ONE: {
+      // The instruction must be either scalar or vector of integers or floats.
+      const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
+      assert(type && "Instruction's result id is invalid");
+
+      if (const auto* vector = type->AsVector()) {
+        return vector->element_type()->AsInteger() ||
+               vector->element_type()->AsFloat();
+      }
+
+      return type->AsInteger() || type->AsFloat();
+    }
+    case protobufs::TransformationAddSynonym::COPY_OBJECT:
+      // All checks for OpCopyObject are handled by
+      // fuzzerutil::CanMakeSynonymOf.
+      return true;
+    case protobufs::TransformationAddSynonym::LOGICAL_AND:
+    case protobufs::TransformationAddSynonym::LOGICAL_OR: {
+      // The instruction must be either a scalar or a vector of booleans.
+      const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
+      assert(type && "Instruction's result id is invalid");
+      return (type->AsVector() && type->AsVector()->element_type()->AsBool()) ||
+             type->AsBool();
+    }
+    default:
+      assert(false && "Synonym type is not supported");
+      return false;
+  }
+}
+
+std::unique_ptr<opt::Instruction>
+TransformationAddSynonym::MakeSynonymousInstruction(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
+  auto synonym_type_id =
+      fuzzerutil::GetTypeId(ir_context, message_.result_id());
+  assert(synonym_type_id && "Synonym has invalid type id");
+
+  switch (message_.synonym_type()) {
+    case protobufs::TransformationAddSynonym::SUB_ZERO:
+    case protobufs::TransformationAddSynonym::MUL_ONE:
+    case protobufs::TransformationAddSynonym::ADD_ZERO: {
+      const auto* synonym_type =
+          ir_context->get_type_mgr()->GetType(synonym_type_id);
+      assert(synonym_type && "Synonym has invalid type");
+
+      // Compute instruction's opcode based on the type of the operand.
+      // We have already checked that the operand is either a scalar or a vector
+      // of either integers or floats.
+      auto is_integral =
+          (synonym_type->AsVector() &&
+           synonym_type->AsVector()->element_type()->AsInteger()) ||
+          synonym_type->AsInteger();
+      auto opcode = SpvOpNop;
+      switch (message_.synonym_type()) {
+        case protobufs::TransformationAddSynonym::SUB_ZERO:
+          opcode = is_integral ? SpvOpISub : SpvOpFSub;
+          break;
+        case protobufs::TransformationAddSynonym::MUL_ONE:
+          opcode = is_integral ? SpvOpIMul : SpvOpFMul;
+          break;
+        case protobufs::TransformationAddSynonym::ADD_ZERO:
+          opcode = is_integral ? SpvOpIAdd : SpvOpFAdd;
+          break;
+        default:
+          assert(false && "Unreachable");
+          break;
+      }
+
+      return MakeUnique<opt::Instruction>(
+          ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
+          opt::Instruction::OperandList{
+              {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
+              {SPV_OPERAND_TYPE_ID,
+               {MaybeGetConstantId(ir_context, transformation_context)}}});
+    }
+    case protobufs::TransformationAddSynonym::COPY_OBJECT:
+      return MakeUnique<opt::Instruction>(
+          ir_context, SpvOpCopyObject, synonym_type_id,
+          message_.synonym_fresh_id(),
+          opt::Instruction::OperandList{
+              {SPV_OPERAND_TYPE_ID, {message_.result_id()}}});
+    case protobufs::TransformationAddSynonym::LOGICAL_OR:
+    case protobufs::TransformationAddSynonym::LOGICAL_AND: {
+      auto opcode = message_.synonym_type() ==
+                            protobufs::TransformationAddSynonym::LOGICAL_OR
+                        ? SpvOpLogicalOr
+                        : SpvOpLogicalAnd;
+      return MakeUnique<opt::Instruction>(
+          ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
+          opt::Instruction::OperandList{
+              {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
+              {SPV_OPERAND_TYPE_ID,
+               {MaybeGetConstantId(ir_context, transformation_context)}}});
+    }
+    default:
+      assert(false && "Unhandled synonym type");
+      return nullptr;
+  }
+}
+
+uint32_t TransformationAddSynonym::MaybeGetConstantId(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
+  assert(IsAdditionalConstantRequired(message_.synonym_type()) &&
+         "Synonym type doesn't require an additional constant");
+
+  auto synonym_type_id =
+      fuzzerutil::GetTypeId(ir_context, message_.result_id());
+  assert(synonym_type_id && "Synonym has invalid type id");
+
+  switch (message_.synonym_type()) {
+    case protobufs::TransformationAddSynonym::ADD_ZERO:
+    case protobufs::TransformationAddSynonym::SUB_ZERO:
+    case protobufs::TransformationAddSynonym::LOGICAL_OR:
+      return fuzzerutil::MaybeGetZeroConstant(
+          ir_context, transformation_context, synonym_type_id, false);
+    case protobufs::TransformationAddSynonym::MUL_ONE:
+    case protobufs::TransformationAddSynonym::LOGICAL_AND: {
+      auto synonym_type = ir_context->get_type_mgr()->GetType(synonym_type_id);
+      assert(synonym_type && "Synonym has invalid type");
+
+      if (const auto* vector = synonym_type->AsVector()) {
+        auto element_type_id =
+            ir_context->get_type_mgr()->GetId(vector->element_type());
+        assert(element_type_id && "Vector's element type is invalid");
+
+        auto one_word =
+            vector->element_type()->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u;
+        if (auto scalar_one_id = fuzzerutil::MaybeGetScalarConstant(
+                ir_context, transformation_context, {one_word}, element_type_id,
+                false)) {
+          return fuzzerutil::MaybeGetCompositeConstant(
+              ir_context, transformation_context,
+              std::vector<uint32_t>(vector->element_count(), scalar_one_id),
+              synonym_type_id, false);
+        }
+
+        return 0;
+      } else {
+        return fuzzerutil::MaybeGetScalarConstant(
+            ir_context, transformation_context,
+            {synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u},
+            synonym_type_id, false);
+      }
+    }
+    default:
+      // The assertion at the beginning of the function will fail in the debug
+      // mode.
+      return 0;
+  }
+}
+
+bool TransformationAddSynonym::IsAdditionalConstantRequired(
+    protobufs::TransformationAddSynonym::SynonymType synonym_type) {
+  switch (synonym_type) {
+    case protobufs::TransformationAddSynonym::ADD_ZERO:
+    case protobufs::TransformationAddSynonym::SUB_ZERO:
+    case protobufs::TransformationAddSynonym::LOGICAL_OR:
+    case protobufs::TransformationAddSynonym::MUL_ONE:
+    case protobufs::TransformationAddSynonym::LOGICAL_AND:
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_synonym.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_synonym.h
new file mode 100644
index 0000000..7705415
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_synonym.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_SYNONYM_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddSynonym : public Transformation {
+ public:
+  explicit TransformationAddSynonym(
+      protobufs::TransformationAddSynonym message);
+
+  TransformationAddSynonym(
+      uint32_t result_id,
+      protobufs::TransformationAddSynonym::SynonymType synonym_type,
+      uint32_t synonym_fresh_id,
+      const protobufs::InstructionDescriptor& insert_before);
+
+  // - |result_id| must be a valid result id of some instruction in the module.
+  // - |result_id| may not be an irrelevant id.
+  // - |synonym_type| is a type of the synonymous instruction that will be
+  //   created.
+  // - |synonym_fresh_id| is a fresh id.
+  // - |insert_before| must be a valid instruction descriptor and we must be
+  //   able to insert a new synonymous instruction before |insert_before|.
+  // - |result_id| must be available before |insert_before|.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Creates a new synonymous instruction according to the |synonym_type| with
+  // result id |synonym_fresh_id|.
+  // Inserts that instruction before |insert_before| and creates a fact
+  // that the |synonym_fresh_id| and the |result_id| are synonymous.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Returns true if we can create a synonym of |inst| according to the
+  // |synonym_type|.
+  static bool IsInstructionValid(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context,
+      opt::Instruction* inst,
+      protobufs::TransformationAddSynonym::SynonymType synonym_type);
+
+  // Returns true if |synonym_type| requires an additional constant instruction
+  // to be present in the module.
+  static bool IsAdditionalConstantRequired(
+      protobufs::TransformationAddSynonym::SynonymType synonym_type);
+
+ private:
+  // Returns a new instruction which is synonymous to |message_.result_id|.
+  std::unique_ptr<opt::Instruction> MakeSynonymousInstruction(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const;
+
+  // Returns a result id of a constant instruction that is required to be
+  // present in some synonym types (e.g. returns a result id of a zero constant
+  // for ADD_ZERO synonym type). Returns 0 if no such instruction is present in
+  // the module. This method should only be called when
+  // IsAdditionalConstantRequired returns true.
+  uint32_t MaybeGetConstantId(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const;
+
+  protobufs::TransformationAddSynonym message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_float.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_float.cpp
index 80716e1..c0c434b 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_float.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_float.cpp
@@ -38,17 +38,12 @@
 
   // Applicable if there is no float type with this width already declared in
   // the module.
-  opt::analysis::Float float_type(message_.width());
-  return ir_context->get_type_mgr()->GetId(&float_type) == 0;
+  return fuzzerutil::MaybeGetFloatType(ir_context, message_.width()) == 0;
 }
 
 void TransformationAddTypeFloat::Apply(
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
-  opt::Instruction::OperandList width = {
-      {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}}};
-  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeFloat, 0, message_.fresh_id(), width));
-  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+  fuzzerutil::AddFloatType(ir_context, message_.fresh_id(), message_.width());
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
   ir_context->InvalidateAnalysesExceptFor(
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.cpp
index 991a28b..c878025 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.cpp
@@ -55,48 +55,19 @@
   // exactly the same return and argument type ids.  (Note that the type manager
   // does not allow us to check this, as it does not distinguish between
   // function types with different but isomorphic pointer argument types.)
-  for (auto& inst : ir_context->module()->types_values()) {
-    if (inst.opcode() != SpvOpTypeFunction) {
-      // Consider only OpTypeFunction instructions.
-      continue;
-    }
-    if (inst.GetSingleWordInOperand(0) != message_.return_type_id()) {
-      // Different return types - cannot be the same.
-      continue;
-    }
-    if (inst.NumInOperands() !=
-        1 + static_cast<uint32_t>(message_.argument_type_id().size())) {
-      // Different numbers of arguments - cannot be the same.
-      continue;
-    }
-    bool found_argument_mismatch = false;
-    for (uint32_t index = 1; index < inst.NumInOperands(); index++) {
-      if (message_.argument_type_id(index - 1) !=
-          inst.GetSingleWordInOperand(index)) {
-        // Argument mismatch - cannot be the same.
-        found_argument_mismatch = true;
-        break;
-      }
-    }
-    if (found_argument_mismatch) {
-      continue;
-    }
-    // Everything matches - the type is already declared.
-    return false;
-  }
-  return true;
+  std::vector<uint32_t> type_ids = {message_.return_type_id()};
+  type_ids.insert(type_ids.end(), message_.argument_type_id().begin(),
+                  message_.argument_type_id().end());
+  return fuzzerutil::FindFunctionType(ir_context, type_ids) == 0;
 }
 
 void TransformationAddTypeFunction::Apply(
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
-  opt::Instruction::OperandList in_operands;
-  in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.return_type_id()}});
-  for (auto argument_type_id : message_.argument_type_id()) {
-    in_operands.push_back({SPV_OPERAND_TYPE_ID, {argument_type_id}});
-  }
-  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeFunction, 0, message_.fresh_id(), in_operands));
-  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+  std::vector<uint32_t> type_ids = {message_.return_type_id()};
+  type_ids.insert(type_ids.end(), message_.argument_type_id().begin(),
+                  message_.argument_type_id().end());
+
+  fuzzerutil::AddFunctionType(ir_context, message_.fresh_id(), type_ids);
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
   ir_context->InvalidateAnalysesExceptFor(
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_int.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_int.cpp
index a932a5f..20759fc 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_int.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_int.cpp
@@ -40,18 +40,14 @@
 
   // Applicable if there is no int type with this width and signedness already
   // declared in the module.
-  opt::analysis::Integer int_type(message_.width(), message_.is_signed());
-  return ir_context->get_type_mgr()->GetId(&int_type) == 0;
+  return fuzzerutil::MaybeGetIntegerType(ir_context, message_.width(),
+                                         message_.is_signed()) == 0;
 }
 
 void TransformationAddTypeInt::Apply(opt::IRContext* ir_context,
                                      TransformationContext* /*unused*/) const {
-  opt::Instruction::OperandList in_operands = {
-      {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}},
-      {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.is_signed() ? 1u : 0u}}};
-  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeInt, 0, message_.fresh_id(), in_operands));
-  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+  fuzzerutil::AddIntegerType(ir_context, message_.fresh_id(), message_.width(),
+                             message_.is_signed());
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
   ir_context->InvalidateAnalysesExceptFor(
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.cpp
index 6ce5ea1..a7345a1 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.cpp
@@ -50,13 +50,10 @@
 
 void TransformationAddTypeStruct::Apply(
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
-  opt::Instruction::OperandList in_operands;
-  for (auto member_type : message_.member_type_id()) {
-    in_operands.push_back({SPV_OPERAND_TYPE_ID, {member_type}});
-  }
-  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeStruct, 0, message_.fresh_id(), in_operands));
-  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+  fuzzerutil::AddStructType(
+      ir_context, message_.fresh_id(),
+      std::vector<uint32_t>(message_.member_type_id().begin(),
+                            message_.member_type_id().end()));
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
   ir_context->InvalidateAnalysesExceptFor(
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.cpp
index f7b2fb5..10a6224 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.cpp
@@ -46,13 +46,9 @@
 
 void TransformationAddTypeVector::Apply(
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
-  opt::Instruction::OperandList in_operands;
-  in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.component_type_id()}});
-  in_operands.push_back(
-      {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.component_count()}});
-  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeVector, 0, message_.fresh_id(), in_operands));
-  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+  fuzzerutil::AddVectorType(ir_context, message_.fresh_id(),
+                            message_.component_type_id(),
+                            message_.component_count());
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
   ir_context->InvalidateAnalysesExceptFor(
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 cd4f22f..15af53e 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_construct.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_construct.cpp
@@ -40,7 +40,8 @@
 }
 
 bool TransformationCompositeConstruct::IsApplicable(
-    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     // We require the id for the composite constructor to be unused.
     return false;
@@ -87,7 +88,20 @@
 
   // Now check whether every component being used to initialize the composite is
   // available at the desired program point.
-  for (auto& component : message_.component()) {
+  for (auto component : message_.component()) {
+    auto* inst = ir_context->get_def_use_mgr()->GetDef(component);
+    if (!inst) {
+      return false;
+    }
+
+    // We should be able to create a synonym of |component| if it's not
+    // irrelevant.
+    if (!transformation_context.GetFactManager()->IdIsIrrelevant(component) &&
+        !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+                                      inst)) {
+      return false;
+    }
+
     if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
                                                     component)) {
       return false;
@@ -144,17 +158,23 @@
       for (uint32_t subvector_index = 0;
            subvector_index < component_type->AsVector()->element_count();
            subvector_index++) {
-        transformation_context->GetFactManager()->AddFactDataSynonym(
-            MakeDataDescriptor(component, {subvector_index}),
-            MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
+        if (!transformation_context->GetFactManager()->IdIsIrrelevant(
+                component)) {
+          transformation_context->GetFactManager()->AddFactDataSynonym(
+              MakeDataDescriptor(component, {subvector_index}),
+              MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
+        }
         index++;
       }
     } else {
       // The other cases are simple: the component is made directly synonymous
       // with the element of the composite being constructed.
-      transformation_context->GetFactManager()->AddFactDataSynonym(
-          MakeDataDescriptor(component, {}),
-          MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
+      if (!transformation_context->GetFactManager()->IdIsIrrelevant(
+              component)) {
+        transformation_context->GetFactManager()->AddFactDataSynonym(
+            MakeDataDescriptor(component, {}),
+            MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
+      }
       index++;
     }
   }
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.cpp
index 3dc3953..9f4d554 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.cpp
@@ -40,7 +40,8 @@
 }
 
 bool TransformationCompositeExtract::IsApplicable(
-    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
@@ -54,6 +55,14 @@
   if (!composite_instruction) {
     return false;
   }
+  if (!transformation_context.GetFactManager()->IdIsIrrelevant(
+          message_.composite_id()) &&
+      !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+                                    composite_instruction)) {
+    // |composite_id| will participate in DataSynonym facts. Thus, it can't be
+    // an irrelevant id.
+    return false;
+  }
   if (auto block = ir_context->get_instr_block(composite_instruction)) {
     if (composite_instruction == instruction_to_insert_before ||
         !ir_context->GetDominatorAnalysis(block->GetParent())
@@ -105,17 +114,20 @@
 
   // Add the fact that the id storing the extracted element is synonymous with
   // the index into the structure.
-  std::vector<uint32_t> indices;
-  for (auto an_index : message_.index()) {
-    indices.push_back(an_index);
+  if (!transformation_context->GetFactManager()->IdIsIrrelevant(
+          message_.composite_id())) {
+    std::vector<uint32_t> indices;
+    for (auto an_index : message_.index()) {
+      indices.push_back(an_index);
+    }
+    protobufs::DataDescriptor data_descriptor_for_extracted_element =
+        MakeDataDescriptor(message_.composite_id(), std::move(indices));
+    protobufs::DataDescriptor data_descriptor_for_result_id =
+        MakeDataDescriptor(message_.fresh_id(), {});
+    transformation_context->GetFactManager()->AddFactDataSynonym(
+        data_descriptor_for_extracted_element, data_descriptor_for_result_id,
+        ir_context);
   }
-  protobufs::DataDescriptor data_descriptor_for_extracted_element =
-      MakeDataDescriptor(message_.composite_id(), std::move(indices));
-  protobufs::DataDescriptor data_descriptor_for_result_id =
-      MakeDataDescriptor(message_.fresh_id(), {});
-  transformation_context->GetFactManager()->AddFactDataSynonym(
-      data_descriptor_for_extracted_element, data_descriptor_for_result_id,
-      ir_context);
 }
 
 protobufs::Transformation TransformationCompositeExtract::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.h b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.h
index 8f52d22..34df823 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.h
@@ -48,7 +48,8 @@
   // Adds an OpCompositeConstruct instruction before the instruction identified
   // by |message_.instruction_to_insert_before|, that extracts from
   // |message_.composite_id| via indices |message_.index| into
-  // |message_.fresh_id|.  Generates a data synonym fact relating
+  // |message_.fresh_id|. If |composite_id| is not an irrelevant id,
+  // generates a data synonym fact relating
   // |message_.fresh_id| to the extracted element.
   void Apply(opt::IRContext* ir_context,
              TransformationContext* transformation_context) const override;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_context.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_context.cpp
index 9c8a90f..6c2dfdf 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_context.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_context.cpp
@@ -18,10 +18,8 @@
 namespace fuzz {
 
 TransformationContext::TransformationContext(
-    FactManager* transformation_context,
-    spv_validator_options validator_options)
-    : fact_manager_(transformation_context),
-      validator_options_(validator_options) {}
+    FactManager* fact_manager, spv_validator_options validator_options)
+    : fact_manager_(fact_manager), validator_options_(validator_options) {}
 
 TransformationContext::~TransformationContext() = default;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.cpp
deleted file mode 100644
index 7b5b5c9..0000000
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (c) 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "source/fuzz/transformation_copy_object.h"
-
-#include "source/fuzz/data_descriptor.h"
-#include "source/fuzz/fuzzer_util.h"
-#include "source/fuzz/instruction_descriptor.h"
-#include "source/opt/instruction.h"
-#include "source/util/make_unique.h"
-
-namespace spvtools {
-namespace fuzz {
-
-TransformationCopyObject::TransformationCopyObject(
-    const protobufs::TransformationCopyObject& message)
-    : message_(message) {}
-
-TransformationCopyObject::TransformationCopyObject(
-    uint32_t object,
-    const protobufs::InstructionDescriptor& instruction_to_insert_before,
-    uint32_t fresh_id) {
-  message_.set_object(object);
-  *message_.mutable_instruction_to_insert_before() =
-      instruction_to_insert_before;
-  message_.set_fresh_id(fresh_id);
-}
-
-bool TransformationCopyObject::IsApplicable(
-    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
-  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
-    // We require the id for the object copy to be unused.
-    return false;
-  }
-  // The id of the object to be copied must exist
-  auto object_inst = ir_context->get_def_use_mgr()->GetDef(message_.object());
-  if (!object_inst) {
-    return false;
-  }
-  if (!fuzzerutil::CanMakeSynonymOf(ir_context, object_inst)) {
-    return false;
-  }
-
-  auto insert_before =
-      FindInstruction(message_.instruction_to_insert_before(), ir_context);
-  if (!insert_before) {
-    // The instruction before which the copy should be inserted was not found.
-    return false;
-  }
-
-  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject,
-                                                    insert_before)) {
-    return false;
-  }
-
-  // |message_object| must be available directly before the point where we want
-  // to add the copy.
-  return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
-                                                    message_.object());
-}
-
-void TransformationCopyObject::Apply(
-    opt::IRContext* ir_context,
-    TransformationContext* transformation_context) const {
-  auto object_inst = ir_context->get_def_use_mgr()->GetDef(message_.object());
-  assert(object_inst && "The object to be copied must exist.");
-  auto insert_before_inst =
-      FindInstruction(message_.instruction_to_insert_before(), ir_context);
-  auto destination_block = ir_context->get_instr_block(insert_before_inst);
-  assert(destination_block && "The base instruction must be in a block.");
-  auto insert_before = fuzzerutil::GetIteratorForInstruction(
-      destination_block, insert_before_inst);
-  assert(insert_before != destination_block->end() &&
-         "There must be an instruction before which the copy can be inserted.");
-
-  opt::Instruction::OperandList operands = {
-      {SPV_OPERAND_TYPE_ID, {message_.object()}}};
-  insert_before->InsertBefore(MakeUnique<opt::Instruction>(
-      ir_context, SpvOp::SpvOpCopyObject, object_inst->type_id(),
-      message_.fresh_id(), operands));
-
-  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
-
-  transformation_context->GetFactManager()->AddFactDataSynonym(
-      MakeDataDescriptor(message_.object(), {}),
-      MakeDataDescriptor(message_.fresh_id(), {}), ir_context);
-
-  if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
-          message_.object())) {
-    transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
-        message_.fresh_id());
-  }
-}
-
-protobufs::Transformation TransformationCopyObject::ToMessage() const {
-  protobufs::Transformation result;
-  *result.mutable_copy_object() = message_;
-  return result;
-}
-
-}  // namespace fuzz
-}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.h b/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.h
deleted file mode 100644
index 80d57ae..0000000
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_H_
-#define SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_H_
-
-#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
-#include "source/fuzz/transformation.h"
-#include "source/fuzz/transformation_context.h"
-#include "source/opt/ir_context.h"
-
-namespace spvtools {
-namespace fuzz {
-
-class TransformationCopyObject : public Transformation {
- public:
-  explicit TransformationCopyObject(
-      const protobufs::TransformationCopyObject& message);
-
-  TransformationCopyObject(
-      uint32_t object,
-      const protobufs::InstructionDescriptor& instruction_to_insert_before,
-      uint32_t fresh_id);
-
-  // - |message_.fresh_id| must not be used by the module.
-  // - |message_.object| must be a result id that is a legitimate operand for
-  //   OpCopyObject.  In particular, it must be the id of an instruction that
-  //   has a result type
-  // - |message_.object| must not be the target of any decoration.
-  //   TODO(afd): consider copying decorations along with objects.
-  // - |message_.base_instruction_id| must be the result id of an instruction
-  //   'base' in some block 'blk'.
-  // - 'blk' must contain an instruction 'inst' located |message_.offset|
-  //   instructions after 'base' (if |message_.offset| = 0 then 'inst' =
-  //   'base').
-  // - It must be legal to insert an OpCopyObject instruction directly
-  //   before 'inst'.
-  // - |message_.object| must be available directly before 'inst'.
-  // - |message_.object| must not be a null pointer or undefined pointer (so as
-  //   to make it legal to load from copied pointers).
-  bool IsApplicable(
-      opt::IRContext* ir_context,
-      const TransformationContext& transformation_context) const override;
-
-  // - A new instruction,
-  //     %|message_.fresh_id| = OpCopyObject %ty %|message_.object|
-  //   is added directly before the instruction at |message_.insert_after_id| +
-  //   |message_|.offset, where %ty is the type of |message_.object|.
-  // - The fact that |message_.fresh_id| and |message_.object| are synonyms
-  //   is added to the fact manager in |transformation_context|.
-  // - If |message_.object| is a pointer whose pointee value is known to be
-  //   irrelevant, the analogous fact is added to the fact manager in
-  //   |transformation_context| about |message_.fresh_id|.
-  void Apply(opt::IRContext* ir_context,
-             TransformationContext* transformation_context) const override;
-
-  protobufs::Transformation ToMessage() const override;
-
- private:
-  protobufs::TransformationCopyObject message_;
-};
-
-}  // namespace fuzz
-}  // namespace spvtools
-
-#endif  // SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.cpp
index 5c31417..e27cd29 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.cpp
@@ -37,11 +37,13 @@
 }
 
 bool TransformationEquationInstruction::IsApplicable(
-    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   // The result id must be fresh.
   if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
+
   // The instruction to insert before must exist.
   auto insert_before =
       FindInstruction(message_.instruction_to_insert_before(), ir_context);
@@ -58,13 +60,16 @@
     if (inst->opcode() == SpvOpUndef) {
       return false;
     }
+    if (transformation_context.GetFactManager()->IdIsIrrelevant(id)) {
+      return false;
+    }
     if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
                                                     id)) {
       return false;
     }
   }
 
-  return MaybeGetResultType(ir_context) != 0;
+  return MaybeGetResultTypeId(ir_context) != 0;
 }
 
 void TransformationEquationInstruction::Apply(
@@ -82,7 +87,8 @@
   FindInstruction(message_.instruction_to_insert_before(), ir_context)
       ->InsertBefore(MakeUnique<opt::Instruction>(
           ir_context, static_cast<SpvOp>(message_.opcode()),
-          MaybeGetResultType(ir_context), message_.fresh_id(), in_operands));
+          MaybeGetResultTypeId(ir_context), message_.fresh_id(),
+          std::move(in_operands)));
 
   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 
@@ -97,12 +103,108 @@
   return result;
 }
 
-uint32_t TransformationEquationInstruction::MaybeGetResultType(
+uint32_t TransformationEquationInstruction::MaybeGetResultTypeId(
     opt::IRContext* ir_context) const {
-  switch (static_cast<SpvOp>(message_.opcode())) {
+  auto opcode = static_cast<SpvOp>(message_.opcode());
+  switch (opcode) {
+    case SpvOpConvertUToF:
+    case SpvOpConvertSToF: {
+      if (message_.in_operand_id_size() != 1) {
+        return 0;
+      }
+
+      const auto* type = ir_context->get_type_mgr()->GetType(
+          fuzzerutil::GetTypeId(ir_context, message_.in_operand_id(0)));
+      if (!type) {
+        return 0;
+      }
+
+      if (const auto* vector = type->AsVector()) {
+        if (!vector->element_type()->AsInteger()) {
+          return 0;
+        }
+
+        if (auto element_type_id = fuzzerutil::MaybeGetFloatType(
+                ir_context, vector->element_type()->AsInteger()->width())) {
+          return fuzzerutil::MaybeGetVectorType(ir_context, element_type_id,
+                                                vector->element_count());
+        }
+
+        return 0;
+      } else {
+        if (!type->AsInteger()) {
+          return 0;
+        }
+
+        return fuzzerutil::MaybeGetFloatType(ir_context,
+                                             type->AsInteger()->width());
+      }
+    }
+    case SpvOpBitcast: {
+      if (message_.in_operand_id_size() != 1) {
+        return 0;
+      }
+
+      const auto* operand_inst =
+          ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0));
+      if (!operand_inst) {
+        return 0;
+      }
+
+      const auto* operand_type =
+          ir_context->get_type_mgr()->GetType(operand_inst->type_id());
+      if (!operand_type) {
+        return 0;
+      }
+
+      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3539):
+      //  The only constraint on the types of OpBitcast's parameters is that
+      //  they must have the same number of bits. Consider improving the code
+      //  below to support this in full.
+      if (const auto* vector = operand_type->AsVector()) {
+        uint32_t component_type_id;
+        if (const auto* int_type = vector->element_type()->AsInteger()) {
+          component_type_id =
+              fuzzerutil::MaybeGetFloatType(ir_context, int_type->width());
+        } else if (const auto* float_type = vector->element_type()->AsFloat()) {
+          component_type_id = fuzzerutil::MaybeGetIntegerType(
+              ir_context, float_type->width(), true);
+          if (component_type_id == 0 ||
+              fuzzerutil::MaybeGetVectorType(ir_context, component_type_id,
+                                             vector->element_count()) == 0) {
+            component_type_id = fuzzerutil::MaybeGetIntegerType(
+                ir_context, float_type->width(), false);
+          }
+        } else {
+          assert(false && "Only vectors of numerical components are supported");
+          return 0;
+        }
+
+        if (component_type_id == 0) {
+          return 0;
+        }
+
+        return fuzzerutil::MaybeGetVectorType(ir_context, component_type_id,
+                                              vector->element_count());
+      } else if (const auto* int_type = operand_type->AsInteger()) {
+        return fuzzerutil::MaybeGetFloatType(ir_context, int_type->width());
+      } else if (const auto* float_type = operand_type->AsFloat()) {
+        if (auto existing_id = fuzzerutil::MaybeGetIntegerType(
+                ir_context, float_type->width(), true)) {
+          return existing_id;
+        }
+
+        return fuzzerutil::MaybeGetIntegerType(ir_context, float_type->width(),
+                                               false);
+      } else {
+        assert(false &&
+               "Operand is not a scalar or a vector of numerical type");
+        return 0;
+      }
+    }
     case SpvOpIAdd:
     case SpvOpISub: {
-      if (message_.in_operand_id().size() != 2) {
+      if (message_.in_operand_id_size() != 2) {
         return 0;
       }
       uint32_t first_operand_width = 0;
@@ -175,7 +277,6 @@
       }
       return operand_inst->type_id();
     }
-
     default:
       assert(false && "Inappropriate opcode for equation instruction.");
       return 0;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.h b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.h
index 7eec9c6..9ed01a8 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.h
@@ -63,11 +63,9 @@
   protobufs::Transformation ToMessage() const override;
 
  private:
-  // A helper that, in one fell swoop, checks that |message_.opcode| and the ids
-  // in |message_.in_operand_id| are compatible, and that the module contains
-  // an appropriate result type id.  If all is well, the result type id is
-  // returned.  Otherwise, 0 is returned.
-  uint32_t MaybeGetResultType(opt::IRContext* ir_context) const;
+  // Returns type id for the equation instruction. Returns 0 if result type does
+  // not exist.
+  uint32_t MaybeGetResultTypeId(opt::IRContext* ir_context) const;
 
   protobufs::TransformationEquationInstruction message_;
 };
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_invert_comparison_operator.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_invert_comparison_operator.cpp
new file mode 100644
index 0000000..a4e6d8b
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_invert_comparison_operator.cpp
@@ -0,0 +1,178 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_invert_comparison_operator.h"
+
+#include <utility>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationInvertComparisonOperator::TransformationInvertComparisonOperator(
+    protobufs::TransformationInvertComparisonOperator message)
+    : message_(std::move(message)) {}
+
+TransformationInvertComparisonOperator::TransformationInvertComparisonOperator(
+    uint32_t operator_id, uint32_t fresh_id) {
+  message_.set_operator_id(operator_id);
+  message_.set_fresh_id(fresh_id);
+}
+
+bool TransformationInvertComparisonOperator::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  // |message_.operator_id| must be valid and inversion must be supported for
+  // it.
+  auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.operator_id());
+  if (!inst || !IsInversionSupported(inst->opcode())) {
+    return false;
+  }
+
+  // Check that we can insert negation instruction.
+  auto* block = ir_context->get_instr_block(inst);
+  assert(block && "Instruction must have a basic block");
+
+  auto iter = fuzzerutil::GetIteratorForInstruction(block, inst);
+  ++iter;
+  assert(iter != block->end() && "Instruction can't be the last in the block");
+  assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) &&
+         "Can't insert negation after comparison operator");
+
+  // |message_.fresh_id| must be fresh.
+  return fuzzerutil::IsFreshId(ir_context, message_.fresh_id());
+}
+
+void TransformationInvertComparisonOperator::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.operator_id());
+  assert(inst && "Result id of an operator is invalid");
+
+  // Insert negation after |inst|.
+  auto iter = fuzzerutil::GetIteratorForInstruction(
+      ir_context->get_instr_block(inst), inst);
+  ++iter;
+
+  iter.InsertBefore(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpLogicalNot, inst->type_id(), inst->result_id(),
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}}));
+
+  // Change the result id of the original operator to |fresh_id|.
+  inst->SetResultId(message_.fresh_id());
+
+  // Invert the operator.
+  inst->SetOpcode(InvertOpcode(inst->opcode()));
+
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+}
+
+bool TransformationInvertComparisonOperator::IsInversionSupported(
+    SpvOp opcode) {
+  switch (opcode) {
+    case SpvOpSGreaterThan:
+    case SpvOpSGreaterThanEqual:
+    case SpvOpSLessThan:
+    case SpvOpSLessThanEqual:
+    case SpvOpUGreaterThan:
+    case SpvOpUGreaterThanEqual:
+    case SpvOpULessThan:
+    case SpvOpULessThanEqual:
+    case SpvOpIEqual:
+    case SpvOpINotEqual:
+    case SpvOpFOrdEqual:
+    case SpvOpFUnordEqual:
+    case SpvOpFOrdNotEqual:
+    case SpvOpFUnordNotEqual:
+    case SpvOpFOrdLessThan:
+    case SpvOpFUnordLessThan:
+    case SpvOpFOrdLessThanEqual:
+    case SpvOpFUnordLessThanEqual:
+    case SpvOpFOrdGreaterThan:
+    case SpvOpFUnordGreaterThan:
+    case SpvOpFOrdGreaterThanEqual:
+    case SpvOpFUnordGreaterThanEqual:
+      return true;
+    default:
+      return false;
+  }
+}
+
+SpvOp TransformationInvertComparisonOperator::InvertOpcode(SpvOp opcode) {
+  assert(IsInversionSupported(opcode) && "Inversion must be supported");
+
+  switch (opcode) {
+    case SpvOpSGreaterThan:
+      return SpvOpSLessThanEqual;
+    case SpvOpSGreaterThanEqual:
+      return SpvOpSLessThan;
+    case SpvOpSLessThan:
+      return SpvOpSGreaterThanEqual;
+    case SpvOpSLessThanEqual:
+      return SpvOpSGreaterThan;
+    case SpvOpUGreaterThan:
+      return SpvOpULessThanEqual;
+    case SpvOpUGreaterThanEqual:
+      return SpvOpULessThan;
+    case SpvOpULessThan:
+      return SpvOpUGreaterThanEqual;
+    case SpvOpULessThanEqual:
+      return SpvOpUGreaterThan;
+    case SpvOpIEqual:
+      return SpvOpINotEqual;
+    case SpvOpINotEqual:
+      return SpvOpIEqual;
+    case SpvOpFOrdEqual:
+      return SpvOpFUnordNotEqual;
+    case SpvOpFUnordEqual:
+      return SpvOpFOrdNotEqual;
+    case SpvOpFOrdNotEqual:
+      return SpvOpFUnordEqual;
+    case SpvOpFUnordNotEqual:
+      return SpvOpFOrdEqual;
+    case SpvOpFOrdLessThan:
+      return SpvOpFUnordGreaterThanEqual;
+    case SpvOpFUnordLessThan:
+      return SpvOpFOrdGreaterThanEqual;
+    case SpvOpFOrdLessThanEqual:
+      return SpvOpFUnordGreaterThan;
+    case SpvOpFUnordLessThanEqual:
+      return SpvOpFOrdGreaterThan;
+    case SpvOpFOrdGreaterThan:
+      return SpvOpFUnordLessThanEqual;
+    case SpvOpFUnordGreaterThan:
+      return SpvOpFOrdLessThanEqual;
+    case SpvOpFOrdGreaterThanEqual:
+      return SpvOpFUnordLessThan;
+    case SpvOpFUnordGreaterThanEqual:
+      return SpvOpFOrdLessThan;
+    default:
+      // The program will fail in the debug mode because of the assertion
+      // at the beginning of the function.
+      return SpvOpNop;
+  }
+}
+
+protobufs::Transformation TransformationInvertComparisonOperator::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_invert_comparison_operator() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_invert_comparison_operator.h b/third_party/SPIRV-Tools/source/fuzz/transformation_invert_comparison_operator.h
new file mode 100644
index 0000000..9047a14
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_invert_comparison_operator.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_INVERT_COMPARISON_OPERATOR_H_
+#define SOURCE_FUZZ_TRANSFORMATION_INVERT_COMPARISON_OPERATOR_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationInvertComparisonOperator : public Transformation {
+ public:
+  explicit TransformationInvertComparisonOperator(
+      protobufs::TransformationInvertComparisonOperator message);
+
+  TransformationInvertComparisonOperator(uint32_t operator_id,
+                                         uint32_t fresh_id);
+
+  // - |operator_id| should be a result id of some instruction for which
+  //   IsInversionSupported returns true.
+  // - |fresh_id| must be a fresh id.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Inverts the opcode of the instruction with result id |operator_id| (e.g >=
+  // becomes <) and inserts OpLogicalNot instruction after |operator_id|. Also,
+  // changes the result id of OpLogicalNot to |operator_id| and the result id of
+  // the inverted operator to |fresh_id|.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Returns true if |opcode| is supported by this transformation.
+  static bool IsInversionSupported(SpvOp opcode);
+
+ private:
+  // Returns an inverted |opcode| (e.g. < becomes >=, == becomes != etc.)
+  static SpvOp InvertOpcode(SpvOp opcode);
+
+  protobufs::TransformationInvertComparisonOperator message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_INVERT_COMPARISON_OPERATOR_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.cpp
index 0f1220e..c4de743 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.cpp
@@ -12,11 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <unordered_set>
+#include "source/fuzz/transformation_permute_function_parameters.h"
+
 #include <vector>
 
 #include "source/fuzz/fuzzer_util.h"
-#include "source/fuzz/transformation_permute_function_parameters.h"
 
 namespace spvtools {
 namespace fuzz {
@@ -29,10 +29,10 @@
 
 TransformationPermuteFunctionParameters::
     TransformationPermuteFunctionParameters(
-        uint32_t function_id, uint32_t new_type_id,
+        uint32_t function_id, uint32_t function_type_fresh_id,
         const std::vector<uint32_t>& permutation) {
   message_.set_function_id(function_id);
-  message_.set_new_type_id(new_type_id);
+  message_.set_function_type_fresh_id(function_type_fresh_id);
 
   for (auto index : permutation) {
     message_.add_permutation(index);
@@ -53,7 +53,8 @@
   const auto* function_type = fuzzerutil::GetFunctionType(ir_context, function);
   assert(function_type && "Function type is null");
 
-  const auto& permutation = message_.permutation();
+  std::vector<uint32_t> permutation(message_.permutation().begin(),
+                                    message_.permutation().end());
 
   // Don't take return type into account
   auto arg_size = function_type->NumInOperands() - 1;
@@ -63,65 +64,29 @@
     return false;
   }
 
-  // Check that all indices are valid
-  // and unique integers from the [0, n-1] set
-  std::unordered_set<uint32_t> unique_indices;
-  for (auto index : permutation) {
-    // We don't compare |index| with 0 since it's an unsigned integer
-    if (index >= arg_size) {
-      return false;
-    }
+  // Check that permutation doesn't have duplicated values.
+  assert(!fuzzerutil::HasDuplicates(permutation) &&
+         "Permutation has duplicates");
 
-    unique_indices.insert(index);
-  }
-
-  // Check that permutation doesn't have duplicated values
-  assert(unique_indices.size() == arg_size && "Permutation has duplicates");
-
-  // Check that new function's type is valid:
-  //   - Has the same number of operands
-  //   - Has the same result type as the old one
-  //   - Order of arguments is permuted
-  auto new_type_id = message_.new_type_id();
-  const auto* new_type = ir_context->get_def_use_mgr()->GetDef(new_type_id);
-
-  if (!new_type || new_type->opcode() != SpvOpTypeFunction ||
-      new_type->NumInOperands() != function_type->NumInOperands()) {
+  // Check that elements in permutation are in range [0, arg_size - 1].
+  //
+  // We must check whether the permutation is empty first because in that case
+  // |arg_size - 1| will produce |std::numeric_limits<uint32_t>::max()| since
+  // it's an unsigned integer.
+  if (!permutation.empty() &&
+      !fuzzerutil::IsPermutationOfRange(permutation, 0, arg_size - 1)) {
     return false;
   }
 
-  // Check that both instructions have the same result type
-  if (new_type->GetSingleWordInOperand(0) !=
-      function_type->GetSingleWordInOperand(0)) {
-    return false;
-  }
-
-  // Check that new function type has its arguments permuted
-  for (int i = 0, n = static_cast<int>(permutation.size()); i < n; ++i) {
-    // +1 to take return type into account
-    if (new_type->GetSingleWordInOperand(i + 1) !=
-        function_type->GetSingleWordInOperand(permutation[i] + 1)) {
-      return false;
-    }
-  }
-
-  return true;
+  return fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id());
 }
 
 void TransformationPermuteFunctionParameters::Apply(
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
-  // Retrieve all data from the message
-  uint32_t function_id = message_.function_id();
-  uint32_t new_type_id = message_.new_type_id();
-  const auto& permutation = message_.permutation();
-
   // Find the function that will be transformed
-  auto* function = fuzzerutil::FindFunction(ir_context, function_id);
+  auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id());
   assert(function && "Can't find the function");
 
-  // Change function's type
-  function->DefInst().SetInOperand(1, {new_type_id});
-
   // Adjust OpFunctionParameter instructions
 
   // Collect ids and types from OpFunctionParameter instructions
@@ -134,7 +99,7 @@
 
   // Permute parameters' ids and types
   std::vector<uint32_t> permuted_param_id, permuted_param_type;
-  for (auto index : permutation) {
+  for (auto index : message_.permutation()) {
     permuted_param_id.push_back(param_id[index]);
     permuted_param_type.push_back(param_type[index]);
   }
@@ -149,25 +114,40 @@
       });
 
   // Fix all OpFunctionCall instructions
-  ir_context->get_def_use_mgr()->ForEachUser(
-      &function->DefInst(),
-      [function_id, &permutation](opt::Instruction* call) {
-        if (call->opcode() != SpvOpFunctionCall ||
-            call->GetSingleWordInOperand(0) != function_id) {
-          return;
-        }
+  for (auto* call : fuzzerutil::GetCallers(ir_context, function->result_id())) {
+    opt::Instruction::OperandList call_operands = {
+        call->GetInOperand(0)  // Function id
+    };
 
-        opt::Instruction::OperandList call_operands = {
-            call->GetInOperand(0)  // Function id
-        };
+    for (auto index : message_.permutation()) {
+      // Take function id into account
+      call_operands.push_back(call->GetInOperand(index + 1));
+    }
 
-        for (auto index : permutation) {
-          // Take function id into account
-          call_operands.push_back(call->GetInOperand(index + 1));
-        }
+    call->SetInOperands(std::move(call_operands));
+  }
 
-        call->SetInOperands(std::move(call_operands));
-      });
+  // Update function type.
+  {
+    // We use a separate scope here since |old_function_type_inst| might become
+    // a dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
+
+    auto* old_function_type_inst =
+        fuzzerutil::GetFunctionType(ir_context, function);
+    assert(old_function_type_inst && "Function must have a valid type");
+
+    std::vector<uint32_t> parameter_type_ids;
+    for (auto index : message_.permutation()) {
+      // +1 since the first operand to OpTypeFunction is a return type.
+      parameter_type_ids.push_back(
+          old_function_type_inst->GetSingleWordInOperand(index + 1));
+    }
+
+    // Change function's type.
+    fuzzerutil::UpdateFunctionType(
+        ir_context, function->result_id(), message_.function_type_fresh_id(),
+        old_function_type_inst->GetSingleWordInOperand(0), parameter_type_ids);
+  }
 
   // Make sure our changes are analyzed
   ir_context->InvalidateAnalysesExceptFor(
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.h b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.h
index 994e4c2..8308051 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.h
@@ -29,11 +29,11 @@
       const protobufs::TransformationPermuteFunctionParameters& message);
 
   TransformationPermuteFunctionParameters(
-      uint32_t function_id, uint32_t new_type_id,
+      uint32_t function_id, uint32_t function_type_fresh_id,
       const std::vector<uint32_t>& permutation);
 
   // - |function_id| is a valid non-entry-point OpFunction instruction
-  // - |new_type_id| is a result id of a valid OpTypeFunction instruction.
+  // - |function_type_fresh_id| is a fresh id.
   //   New type is valid if:
   //     - it has the same number of operands as the old one
   //     - function's result type is the same as the old one
@@ -46,7 +46,7 @@
 
   // - OpFunction instruction with |result_id == function_id| is changed.
   //   Its arguments are permuted according to the |permutation| vector
-  // - Changed function gets a new type specified by |type_id|
+  // - Adjusts function's type to accommodate for permuted parameters.
   // - Calls to the function are adjusted accordingly
   void Apply(opt::IRContext* ir_context,
              TransformationContext* transformation_context) const override;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_permute_phi_operands.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_phi_operands.cpp
new file mode 100644
index 0000000..95e7a1f
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_phi_operands.cpp
@@ -0,0 +1,94 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_permute_phi_operands.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationPermutePhiOperands::TransformationPermutePhiOperands(
+    const spvtools::fuzz::protobufs::TransformationPermutePhiOperands& message)
+    : message_(message) {}
+
+TransformationPermutePhiOperands::TransformationPermutePhiOperands(
+    uint32_t result_id, const std::vector<uint32_t>& permutation) {
+  message_.set_result_id(result_id);
+
+  for (auto index : permutation) {
+    message_.add_permutation(index);
+  }
+}
+
+bool TransformationPermutePhiOperands::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  // Check that |message_.result_id| is valid.
+  const auto* inst =
+      ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+  if (!inst || inst->opcode() != SpvOpPhi) {
+    return false;
+  }
+
+  // Check that |message_.permutation| has expected size.
+  auto expected_permutation_size = inst->NumInOperands() / 2;
+  if (static_cast<uint32_t>(message_.permutation().size()) !=
+      expected_permutation_size) {
+    return false;
+  }
+
+  // Check that |message_.permutation| has elements in range
+  // [0, expected_permutation_size - 1].
+  std::vector<uint32_t> permutation(message_.permutation().begin(),
+                                    message_.permutation().end());
+  assert(!fuzzerutil::HasDuplicates(permutation) &&
+         "Permutation has duplicates");
+
+  // We must check whether the permutation is empty first because in that case
+  // |expected_permutation_size - 1| will produce
+  // |std::numeric_limits<uint32_t>::max()| since it's an unsigned integer.
+  return permutation.empty() ||
+         fuzzerutil::IsPermutationOfRange(permutation, 0,
+                                          expected_permutation_size - 1);
+}
+
+void TransformationPermutePhiOperands::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+  assert(inst);
+
+  opt::Instruction::OperandList permuted_operands;
+  permuted_operands.reserve(inst->NumInOperands());
+
+  for (auto index : message_.permutation()) {
+    permuted_operands.push_back(std::move(inst->GetInOperand(2 * index)));
+    permuted_operands.push_back(std::move(inst->GetInOperand(2 * index + 1)));
+  }
+
+  inst->SetInOperands(std::move(permuted_operands));
+
+  // Make sure our changes are analyzed
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationPermutePhiOperands::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_permute_phi_operands() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_permute_phi_operands.h b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_phi_operands.h
new file mode 100644
index 0000000..df242e3
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_phi_operands.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_PERMUTE_PHI_OPERANDS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationPermutePhiOperands : public Transformation {
+ public:
+  explicit TransformationPermutePhiOperands(
+      const protobufs::TransformationPermutePhiOperands& message);
+
+  TransformationPermutePhiOperands(uint32_t result_id,
+                                   const std::vector<uint32_t>& permutation);
+
+  // - |result_id| must be a valid id of some OpPhi instruction in the module.
+  // - |permutation| must contain elements in the range [0, n/2 - 1] where |n|
+  //   is a number of operands to the instruction with |result_id|. All elements
+  //   must be unique (i.e. |permutation.size() == n / 2|).
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Permutes operands of the OpPhi instruction with |result_id| according to
+  // the elements in |permutation|.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationPermutePhiOperands message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_push_id_through_variable.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_push_id_through_variable.cpp
new file mode 100644
index 0000000..647bfa0
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_push_id_through_variable.cpp
@@ -0,0 +1,174 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_push_id_through_variable.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationPushIdThroughVariable::TransformationPushIdThroughVariable(
+    const spvtools::fuzz::protobufs::TransformationPushIdThroughVariable&
+        message)
+    : message_(message) {}
+
+TransformationPushIdThroughVariable::TransformationPushIdThroughVariable(
+    uint32_t value_id, uint32_t value_synonym_id, uint32_t variable_id,
+    uint32_t variable_storage_class, uint32_t initializer_id,
+    const protobufs::InstructionDescriptor& instruction_descriptor) {
+  message_.set_value_id(value_id);
+  message_.set_value_synonym_id(value_synonym_id);
+  message_.set_variable_id(variable_id);
+  message_.set_variable_storage_class(variable_storage_class);
+  message_.set_initializer_id(initializer_id);
+  *message_.mutable_instruction_descriptor() = instruction_descriptor;
+}
+
+bool TransformationPushIdThroughVariable::IsApplicable(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
+  // |message_.value_synonym_id| and |message_.variable_id| must be fresh.
+  if (!fuzzerutil::IsFreshId(ir_context, message_.value_synonym_id()) ||
+      !fuzzerutil::IsFreshId(ir_context, message_.variable_id())) {
+    return false;
+  }
+
+  // The instruction to insert before must be defined.
+  auto instruction_to_insert_before =
+      FindInstruction(message_.instruction_descriptor(), ir_context);
+  if (!instruction_to_insert_before) {
+    return false;
+  }
+
+  // It must be valid to insert the OpStore and OpLoad instruction before it.
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+          SpvOpStore, instruction_to_insert_before) ||
+      !fuzzerutil::CanInsertOpcodeBeforeInstruction(
+          SpvOpLoad, instruction_to_insert_before)) {
+    return false;
+  }
+
+  // The instruction to insert before must belong to a reachable block.
+  auto basic_block = ir_context->get_instr_block(instruction_to_insert_before);
+  if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, basic_block)) {
+    return false;
+  }
+
+  // The value instruction must be defined and have a type.
+  auto value_instruction =
+      ir_context->get_def_use_mgr()->GetDef(message_.value_id());
+  if (!value_instruction || !value_instruction->type_id()) {
+    return false;
+  }
+
+  // We should be able to create a synonym of |value_id| if it's not irrelevant.
+  if (!transformation_context.GetFactManager()->IdIsIrrelevant(
+          message_.value_id()) &&
+      !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+                                    value_instruction)) {
+    return false;
+  }
+
+  // A pointer type instruction pointing to the value type must be defined.
+  auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
+      ir_context, value_instruction->type_id(),
+      static_cast<SpvStorageClass>(message_.variable_storage_class()));
+  if (!pointer_type_id) {
+    return false;
+  }
+
+  // |message_.variable_storage_class| must be private or function.
+  assert((message_.variable_storage_class() == SpvStorageClassPrivate ||
+          message_.variable_storage_class() == SpvStorageClassFunction) &&
+         "The variable storage class must be private or function.");
+
+  // Check that initializer is valid.
+  const auto* constant_inst =
+      ir_context->get_def_use_mgr()->GetDef(message_.initializer_id());
+  if (!constant_inst || !spvOpcodeIsConstant(constant_inst->opcode()) ||
+      value_instruction->type_id() != constant_inst->type_id()) {
+    return false;
+  }
+
+  // |message_.value_id| must be available at the insertion point.
+  return fuzzerutil::IdIsAvailableBeforeInstruction(
+      ir_context, instruction_to_insert_before, message_.value_id());
+}
+
+void TransformationPushIdThroughVariable::Apply(
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  auto value_instruction =
+      ir_context->get_def_use_mgr()->GetDef(message_.value_id());
+
+  // A pointer type instruction pointing to the value type must be defined.
+  auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
+      ir_context, value_instruction->type_id(),
+      static_cast<SpvStorageClass>(message_.variable_storage_class()));
+  assert(pointer_type_id && "The required pointer type must be available.");
+
+  // Adds whether a global or local variable.
+  if (message_.variable_storage_class() == SpvStorageClassPrivate) {
+    fuzzerutil::AddGlobalVariable(ir_context, message_.variable_id(),
+                                  pointer_type_id, SpvStorageClassPrivate,
+                                  message_.initializer_id());
+  } else {
+    auto function_id = ir_context
+                           ->get_instr_block(FindInstruction(
+                               message_.instruction_descriptor(), ir_context))
+                           ->GetParent()
+                           ->result_id();
+    fuzzerutil::AddLocalVariable(ir_context, message_.variable_id(),
+                                 pointer_type_id, function_id,
+                                 message_.initializer_id());
+  }
+
+  // First, insert the OpLoad instruction before |instruction_descriptor| and
+  // then insert the OpStore instruction before the OpLoad instruction.
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.value_synonym_id());
+  FindInstruction(message_.instruction_descriptor(), ir_context)
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpLoad, value_instruction->type_id(),
+          message_.value_synonym_id(),
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}})))
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpStore, 0, 0,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}},
+               {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})));
+
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+
+  if (!transformation_context->GetFactManager()->IdIsIrrelevant(
+          message_.value_id())) {
+    // Adds the fact that |message_.value_synonym_id|
+    // and |message_.value_id| are synonymous.
+    transformation_context->GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(message_.value_synonym_id(), {}),
+        MakeDataDescriptor(message_.value_id(), {}), ir_context);
+  }
+}
+
+protobufs::Transformation TransformationPushIdThroughVariable::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_push_id_through_variable() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_push_id_through_variable.h b/third_party/SPIRV-Tools/source/fuzz/transformation_push_id_through_variable.h
new file mode 100644
index 0000000..f49db31
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_push_id_through_variable.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_PUSH_ID_THROUGH_VARIABLE_H_
+#define SOURCE_FUZZ_TRANSFORMATION_PUSH_ID_THROUGH_VARIABLE_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationPushIdThroughVariable : public Transformation {
+ public:
+  explicit TransformationPushIdThroughVariable(
+      const protobufs::TransformationPushIdThroughVariable& message);
+
+  TransformationPushIdThroughVariable(
+      uint32_t value_id, uint32_t value_synonym_fresh_id,
+      uint32_t variable_fresh_id, uint32_t variable_storage_class,
+      uint32_t initializer_id,
+      const protobufs::InstructionDescriptor& instruction_descriptor);
+
+  // - |message_.value_id| must be an instruction result id that has the same
+  //   type as the pointee type of |message_.pointer_id|
+  // - |message_.value_synonym_id| must be fresh
+  // - |message_.variable_id| must be fresh
+  // - |message_.variable_storage_class| must be either StorageClassPrivate or
+  //   StorageClassFunction
+  // - |message_.initializer_id| must be a result id of some constant in the
+  //   module. Its type must be equal to the pointee type of the variable that
+  //   will be created.
+  // - |message_.instruction_descriptor| must identify an instruction
+  //   which it is valid to insert the OpStore and OpLoad instructions before it
+  //   and must be belongs to a reachable block.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Stores |value_id| to |variable_id|, loads |variable_id| to
+  // |value_synonym_id|. Adds the fact that |value_synonym_id| and |value_id|
+  // are synonymous if |value_id| is not irrelevant.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationPushIdThroughVariable message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_PUSH_ID_THROUGH_VARIABLE_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_record_synonymous_constants.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_record_synonymous_constants.cpp
new file mode 100644
index 0000000..a93e1d4
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_record_synonymous_constants.cpp
@@ -0,0 +1,126 @@
+// Copyright (c) 2020 Stefano Milizia
+// 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 "transformation_record_synonymous_constants.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationRecordSynonymousConstants::
+    TransformationRecordSynonymousConstants(
+        const protobufs::TransformationRecordSynonymousConstants& message)
+    : message_(message) {}
+
+TransformationRecordSynonymousConstants::
+    TransformationRecordSynonymousConstants(uint32_t constant1_id,
+                                            uint32_t constant2_id) {
+  message_.set_constant1_id(constant1_id);
+  message_.set_constant2_id(constant2_id);
+}
+
+bool TransformationRecordSynonymousConstants::IsApplicable(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
+  // The ids must be different
+  if (message_.constant1_id() == message_.constant2_id()) {
+    return false;
+  }
+
+  if (transformation_context.GetFactManager()->IdIsIrrelevant(
+          message_.constant1_id()) ||
+      transformation_context.GetFactManager()->IdIsIrrelevant(
+          message_.constant2_id())) {
+    return false;
+  }
+
+  return AreEquivalentConstants(ir_context, message_.constant1_id(),
+                                message_.constant2_id());
+}
+
+void TransformationRecordSynonymousConstants::Apply(
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  // Add the fact to the fact manager
+  transformation_context->GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(message_.constant1_id(), {}),
+      MakeDataDescriptor(message_.constant2_id(), {}), ir_context);
+}
+
+protobufs::Transformation TransformationRecordSynonymousConstants::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_record_synonymous_constants() = message_;
+  return result;
+}
+
+bool TransformationRecordSynonymousConstants::AreEquivalentConstants(
+    opt::IRContext* ir_context, uint32_t constant_id1, uint32_t constant_id2) {
+  const auto* def_1 = ir_context->get_def_use_mgr()->GetDef(constant_id1);
+  const auto* def_2 = ir_context->get_def_use_mgr()->GetDef(constant_id2);
+
+  // Check that the definitions exist
+  if (!def_1 || !def_2) {
+    // We don't use an assertion since otherwise the shrinker fails.
+    return false;
+  }
+
+  auto constant1 = ir_context->get_constant_mgr()->GetConstantFromInst(def_1);
+  auto constant2 = ir_context->get_constant_mgr()->GetConstantFromInst(def_2);
+
+  assert(constant1 && constant2 && "The ids must refer to constants.");
+
+  // The types must be compatible.
+  if (!fuzzerutil::TypesAreEqualUpToSign(ir_context, def_1->type_id(),
+                                         def_2->type_id())) {
+    return false;
+  }
+
+  // If either constant is null, the other is equivalent iff it is zero-like
+  if (constant1->AsNullConstant()) {
+    return constant2->IsZero();
+  }
+
+  if (constant2->AsNullConstant()) {
+    return constant1->IsZero();
+  }
+
+  // If the constants are scalar, they are equal iff their words are the same
+  if (auto scalar1 = constant1->AsScalarConstant()) {
+    return scalar1->words() == constant2->AsScalarConstant()->words();
+  }
+
+  // The only remaining possibility is that the constants are composite
+  assert(constant1->AsCompositeConstant() &&
+         "Equivalence of constants can only be checked with scalar, composite "
+         "or null constants.");
+
+  // Since the types match, we already know that the number of components is
+  // the same. We check that the input operands of the definitions are all
+  // constants and that they are pairwise equivalent.
+  for (uint32_t i = 0; i < def_1->NumInOperands(); i++) {
+    if (!AreEquivalentConstants(ir_context, def_1->GetSingleWordInOperand(i),
+                                def_2->GetSingleWordInOperand(i))) {
+      return false;
+    }
+  }
+
+  // If we get here, all the components are equivalent
+  return true;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_record_synonymous_constants.h b/third_party/SPIRV-Tools/source/fuzz/transformation_record_synonymous_constants.h
new file mode 100644
index 0000000..8cff0cd
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_record_synonymous_constants.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2020 Stefano Milizia
+// 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_RECORD_SYNONYMOUS_CONSTANTS_H
+#define SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS_H
+
+#include "source/fuzz/transformation.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationRecordSynonymousConstants : public Transformation {
+ public:
+  explicit TransformationRecordSynonymousConstants(
+      const protobufs::TransformationRecordSynonymousConstants& message);
+
+  TransformationRecordSynonymousConstants(uint32_t constant1_id,
+                                          uint32_t constant2_id);
+
+  // - |message_.constant_id| and |message_.synonym_id| are distinct ids
+  //   of constants
+  // - |message_.constant_id| and |message_.synonym_id| refer to constants
+  //   that are equivalent.
+  // Constants are equivalent if at least one of the following holds:
+  // - they are equal (i.e. they have the same type ids and equal values)
+  // - both of them represent zero-like values of compatible types
+  // - they are composite constants with compatible types and their
+  //   components are pairwise equivalent
+  // Two types are compatible if at least one of the following holds:
+  // - they have the same id
+  // - they are integer scalar types with the same width
+  // - they are integer vectors and their components have the same width
+  //   (this is always the case if the components are equivalent)
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Adds the fact that |message_.constant_id| and |message_.synonym_id|
+  // are synonyms to the fact manager. The module is not changed.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationRecordSynonymousConstants message_;
+
+  // Returns true if the two given constants are equivalent
+  // (the description of IsApplicable specifies the conditions they must satisfy
+  // to be considered equivalent)
+  static bool AreEquivalentConstants(opt::IRContext* ir_context,
+                                     uint32_t constant_id1,
+                                     uint32_t constant_id2);
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS
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 d6f17fc..6e22e7c 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,22 +243,15 @@
     return false;
   }
 
-  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;
+  // 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.
+  if (instruction->opcode() == SpvOpVariable) {
+    return false;
   }
+
+  return true;
 }
 
 void TransformationReplaceBooleanConstantWithConstantBinary::Apply(
@@ -281,11 +274,22 @@
   opt::Instruction* result = binary_instruction.get();
   auto instruction_containing_constant_use =
       FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
+  auto instruction_before_which_to_insert = instruction_containing_constant_use;
+
+  // If |instruction_before_which_to_insert| is an OpPhi instruction,
+  // then |binary_instruction| will be inserted into the parent block associated
+  // with the OpPhi variable operand.
+  if (instruction_containing_constant_use->opcode() == SpvOpPhi) {
+    instruction_before_which_to_insert =
+        ir_context->cfg()
+            ->block(instruction_containing_constant_use->GetSingleWordInOperand(
+                message_.id_use_descriptor().in_operand_index() + 1))
+            ->terminator();
+  }
 
   // We want to insert the new instruction before the instruction that contains
   // the use of the boolean, but we need to go backwards one more instruction if
   // the using instruction is preceded by a merge instruction.
-  auto instruction_before_which_to_insert = instruction_containing_constant_use;
   {
     opt::Instruction* previous_node =
         instruction_before_which_to_insert->PreviousNode();
@@ -294,6 +298,7 @@
       instruction_before_which_to_insert = previous_node;
     }
   }
+
   instruction_before_which_to_insert->InsertBefore(
       std::move(binary_instruction));
   instruction_containing_constant_use->SetInOperand(
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp
new file mode 100644
index 0000000..bf6996a
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp
@@ -0,0 +1,127 @@
+// 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_replace_copy_memory_with_load_store.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationReplaceCopyMemoryWithLoadStore::
+    TransformationReplaceCopyMemoryWithLoadStore(
+        const spvtools::fuzz::protobufs::
+            TransformationReplaceCopyMemoryWithLoadStore& message)
+    : message_(message) {}
+
+TransformationReplaceCopyMemoryWithLoadStore::
+    TransformationReplaceCopyMemoryWithLoadStore(
+        uint32_t fresh_id, const protobufs::InstructionDescriptor&
+                               copy_memory_instruction_descriptor) {
+  message_.set_fresh_id(fresh_id);
+  *message_.mutable_copy_memory_instruction_descriptor() =
+      copy_memory_instruction_descriptor;
+}
+
+bool TransformationReplaceCopyMemoryWithLoadStore::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  // |message_.fresh_id| must be fresh.
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
+    return false;
+  }
+  // The instruction to be replaced must be defined and have opcode
+  // OpCopyMemory.
+  auto copy_memory_instruction = FindInstruction(
+      message_.copy_memory_instruction_descriptor(), ir_context);
+  if (!copy_memory_instruction ||
+      copy_memory_instruction->opcode() != SpvOpCopyMemory) {
+    return false;
+  }
+  return true;
+}
+
+void TransformationReplaceCopyMemoryWithLoadStore::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  auto copy_memory_instruction = FindInstruction(
+      message_.copy_memory_instruction_descriptor(), ir_context);
+  // |copy_memory_instruction| must be defined.
+  assert(copy_memory_instruction &&
+         copy_memory_instruction->opcode() == SpvOpCopyMemory &&
+         "The required OpCopyMemory instruction must be defined.");
+
+  // Coherence check: Both operands must be pointers.
+
+  // Get types of ids used as a source and target of |copy_memory_instruction|.
+  auto target = ir_context->get_def_use_mgr()->GetDef(
+      copy_memory_instruction->GetSingleWordInOperand(0));
+  auto source = ir_context->get_def_use_mgr()->GetDef(
+      copy_memory_instruction->GetSingleWordInOperand(1));
+  auto target_type_opcode =
+      ir_context->get_def_use_mgr()->GetDef(target->type_id())->opcode();
+  auto source_type_opcode =
+      ir_context->get_def_use_mgr()->GetDef(source->type_id())->opcode();
+
+  // Keep release-mode compilers happy. (No unused variables.)
+  (void)target;
+  (void)source;
+  (void)target_type_opcode;
+  (void)source_type_opcode;
+
+  assert(target_type_opcode == SpvOpTypePointer &&
+         source_type_opcode == SpvOpTypePointer &&
+         "Operands must be of type OpTypePointer");
+
+  // Coherence check: |source| and |target| must point to the same type.
+  uint32_t target_pointee_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
+      ir_context, target->type_id());
+  uint32_t source_pointee_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
+      ir_context, source->type_id());
+
+  // Keep release-mode compilers happy. (No unused variables.)
+  (void)target_pointee_type;
+  (void)source_pointee_type;
+
+  assert(target_pointee_type == source_pointee_type &&
+         "Operands must have the same type to which they point to.");
+
+  // First, insert the OpStore instruction before the OpCopyMemory instruction
+  // and then insert the OpLoad instruction before the OpStore instruction.
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+  FindInstruction(message_.copy_memory_instruction_descriptor(), ir_context)
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpStore, 0, 0,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {target->result_id()}},
+               {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}})))
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpLoad, target_pointee_type, message_.fresh_id(),
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {source->result_id()}}})));
+
+  // Remove the OpCopyMemory instruction.
+  ir_context->KillInst(copy_memory_instruction);
+
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation
+TransformationReplaceCopyMemoryWithLoadStore::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_replace_copy_memory_with_load_store() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h
new file mode 100644
index 0000000..70120f8
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h
@@ -0,0 +1,57 @@
+// 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 SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H
+#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationReplaceCopyMemoryWithLoadStore : public Transformation {
+ public:
+  explicit TransformationReplaceCopyMemoryWithLoadStore(
+      const protobufs::TransformationReplaceCopyMemoryWithLoadStore& message);
+
+  TransformationReplaceCopyMemoryWithLoadStore(
+      uint32_t fresh_id, const protobufs::InstructionDescriptor&
+                             copy_memory_instruction_descriptor);
+
+  // - |message_.fresh_id| must be fresh.
+  // - |message_.copy_memory_instruction_descriptor| must refer to an
+  //   OpCopyMemory instruction.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Replaces instruction OpCopyMemory with loading the source variable to an
+  // intermediate value and storing this value into the target variable of the
+  // original OpCopyMemory instruction.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationReplaceCopyMemoryWithLoadStore message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
new file mode 100644
index 0000000..05e8cda
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
@@ -0,0 +1,155 @@
+// 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_replace_copy_object_with_store_load.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationReplaceCopyObjectWithStoreLoad::
+    TransformationReplaceCopyObjectWithStoreLoad(
+        const spvtools::fuzz::protobufs::
+            TransformationReplaceCopyObjectWithStoreLoad& message)
+    : message_(message) {}
+
+TransformationReplaceCopyObjectWithStoreLoad::
+    TransformationReplaceCopyObjectWithStoreLoad(
+        uint32_t copy_object_result_id, uint32_t fresh_variable_id,
+        uint32_t variable_storage_class, uint32_t variable_initializer_id) {
+  message_.set_copy_object_result_id(copy_object_result_id);
+  message_.set_fresh_variable_id(fresh_variable_id);
+  message_.set_variable_storage_class(variable_storage_class);
+  message_.set_variable_initializer_id(variable_initializer_id);
+}
+
+bool TransformationReplaceCopyObjectWithStoreLoad::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  // |message_.fresh_variable_id| must be fresh.
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_variable_id())) {
+    return false;
+  }
+  auto copy_object_instruction =
+      ir_context->get_def_use_mgr()->GetDef(message_.copy_object_result_id());
+
+  // This must be a defined OpCopyObject instruction.
+  if (!copy_object_instruction ||
+      copy_object_instruction->opcode() != SpvOpCopyObject) {
+    return false;
+  }
+
+  // The opcode of the type_id instruction cannot be a OpTypePointer,
+  // because we cannot define a pointer to pointer.
+  if (ir_context->get_def_use_mgr()
+          ->GetDef(copy_object_instruction->type_id())
+          ->opcode() == SpvOpTypePointer) {
+    return false;
+  }
+
+  // It must be valid to insert the OpStore and OpLoad instruction before it.
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
+                                                    copy_object_instruction) ||
+      !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad,
+                                                    copy_object_instruction)) {
+    return false;
+  }
+
+  // A pointer type instruction pointing to the value type must be defined.
+  auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
+      ir_context, copy_object_instruction->type_id(),
+      static_cast<SpvStorageClass>(message_.variable_storage_class()));
+  if (!pointer_type_id) {
+    return false;
+  }
+
+  // Check that initializer is valid.
+  const auto* constant_inst =
+      ir_context->get_def_use_mgr()->GetDef(message_.variable_initializer_id());
+  if (!constant_inst || !spvOpcodeIsConstant(constant_inst->opcode()) ||
+      copy_object_instruction->type_id() != constant_inst->type_id()) {
+    return false;
+  }
+  // |message_.variable_storage_class| must be Private or Function.
+  return message_.variable_storage_class() == SpvStorageClassPrivate ||
+         message_.variable_storage_class() == SpvStorageClassFunction;
+}
+
+void TransformationReplaceCopyObjectWithStoreLoad::Apply(
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  auto copy_object_instruction =
+      ir_context->get_def_use_mgr()->GetDef(message_.copy_object_result_id());
+  // |copy_object_instruction| must be defined.
+  assert(copy_object_instruction &&
+         copy_object_instruction->opcode() == SpvOpCopyObject &&
+         "The required OpCopyObject instruction must be defined.");
+  // Get id used as a source by the OpCopyObject instruction.
+  uint32_t src_operand = copy_object_instruction->GetSingleWordOperand(2);
+  // A pointer type instruction pointing to the value type must be defined.
+  auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
+      ir_context, copy_object_instruction->type_id(),
+      static_cast<SpvStorageClass>(message_.variable_storage_class()));
+  assert(pointer_type_id && "The required pointer type must be available.");
+
+  // Adds a global or local variable (according to the storage class).
+  if (message_.variable_storage_class() == SpvStorageClassPrivate) {
+    fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_variable_id(),
+                                  pointer_type_id, SpvStorageClassPrivate,
+                                  message_.variable_initializer_id());
+  } else {
+    auto function_id = ir_context->get_instr_block(copy_object_instruction)
+                           ->GetParent()
+                           ->result_id();
+    fuzzerutil::AddLocalVariable(ir_context, message_.fresh_variable_id(),
+                                 pointer_type_id, function_id,
+                                 message_.variable_initializer_id());
+  }
+
+  // First, insert the OpLoad instruction before the OpCopyObject instruction
+  // and then insert the OpStore instruction before the OpLoad instruction.
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_variable_id());
+  copy_object_instruction
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpLoad, copy_object_instruction->type_id(),
+          message_.copy_object_result_id(),
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}})))
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpStore, 0, 0,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}},
+               {SPV_OPERAND_TYPE_ID, {src_operand}}})));
+  // Remove the CopyObject instruction.
+  ir_context->KillInst(copy_object_instruction);
+
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+
+  // Adds the fact that |message_.copy_object_result_id|
+  // and src_operand (id used by OpCopyObject) are synonymous.
+  transformation_context->GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(message_.copy_object_result_id(), {}),
+      MakeDataDescriptor(src_operand, {}), ir_context);
+}
+
+protobufs::Transformation
+TransformationReplaceCopyObjectWithStoreLoad::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_replace_copy_object_with_store_load() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_object_with_store_load.h b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_object_with_store_load.h
new file mode 100644
index 0000000..db9c74e
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_object_with_store_load.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 SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H
+#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationReplaceCopyObjectWithStoreLoad : public Transformation {
+ public:
+  explicit TransformationReplaceCopyObjectWithStoreLoad(
+      const protobufs::TransformationReplaceCopyObjectWithStoreLoad& message);
+
+  TransformationReplaceCopyObjectWithStoreLoad(
+      uint32_t copy_object_result_id, uint32_t fresh_variable_id,
+      uint32_t variable_storage_class, uint32_t variable_initializer_id);
+
+  // - |message_.copy_object_result_id| must be a result id of an OpCopyObject
+  //   instruction.
+  // - |message_.fresh_variable_id| must be a fresh id given to variable used by
+  //   OpStore.
+  // - |message_.variable_storage_class| must be either StorageClassPrivate or
+  //   StorageClassFunction.
+  // - |message_.initializer_id| must be a result id of some constant in the
+  //   module. Its type must be equal to the pointee type of the variable that
+  //   will be created.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Replaces instruction OpCopyObject with storing into a new variable and
+  // immediately loading from this variable to |result_id| of the original
+  // OpCopyObject instruction.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationReplaceCopyObjectWithStoreLoad message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H
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 e427f3c..55607e1 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
@@ -57,6 +57,24 @@
     return false;
   }
 
+  uint32_t type_id_of_interest =
+      ir_context->get_def_use_mgr()->GetDef(id_of_interest)->type_id();
+  uint32_t type_id_synonym = ir_context->get_def_use_mgr()
+                                 ->GetDef(message_.synonymous_id())
+                                 ->type_id();
+
+  // If the id of interest and the synonym are scalar or vector integer
+  // constants with different signedness, their use can only be swapped if the
+  // instruction is agnostic to the signedness of the operand.
+  if (type_id_of_interest != type_id_synonym &&
+      fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_of_interest,
+                                        type_id_synonym) &&
+      !IsAgnosticToSignednessOfOperand(
+          use_instruction->opcode(),
+          message_.id_use_descriptor().in_operand_index())) {
+    return false;
+  }
+
   // Is the use suitable for being replaced in principle?
   if (!UseCanBeReplacedWithSynonym(
           ir_context, use_instruction,
@@ -182,5 +200,46 @@
   return true;
 }
 
+// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3582): Add all
+//  opcodes that are agnostic to signedness of operands to function.
+//  This is not exhaustive yet.
+bool TransformationReplaceIdWithSynonym::IsAgnosticToSignednessOfOperand(
+    SpvOp opcode, uint32_t use_in_operand_index) {
+  switch (opcode) {
+    case SpvOpSNegate:
+    case SpvOpNot:
+    case SpvOpIAdd:
+    case SpvOpISub:
+    case SpvOpIMul:
+    case SpvOpSDiv:
+    case SpvOpSRem:
+    case SpvOpSMod:
+    case SpvOpShiftRightLogical:
+    case SpvOpShiftRightArithmetic:
+    case SpvOpShiftLeftLogical:
+    case SpvOpBitwiseOr:
+    case SpvOpBitwiseXor:
+    case SpvOpBitwiseAnd:
+    case SpvOpIEqual:
+    case SpvOpINotEqual:
+    case SpvOpULessThan:
+    case SpvOpSLessThan:
+    case SpvOpUGreaterThan:
+    case SpvOpSGreaterThan:
+    case SpvOpULessThanEqual:
+    case SpvOpSLessThanEqual:
+    case SpvOpUGreaterThanEqual:
+    case SpvOpSGreaterThanEqual:
+      return true;
+    case SpvOpAccessChain:
+      // The signedness of indices does not matter.
+      return use_in_operand_index > 0;
+    default:
+      // Conservatively assume that the id cannot be swapped in other
+      // instructions.
+      return false;
+  }
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
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 a5a9dfd..78878b2 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
@@ -64,6 +64,13 @@
                                           opt::Instruction* use_instruction,
                                           uint32_t use_in_operand_index);
 
+  // Returns true if the instruction with opcode |opcode| does not change its
+  // behaviour depending on the signedness of the operand at
+  // |use_in_operand_index|.
+  // Assumes that the operand must be the id of an integer scalar or vector.
+  static bool IsAgnosticToSignednessOfOperand(SpvOp opcode,
+                                              uint32_t use_in_operand_index);
+
  private:
   protobufs::TransformationReplaceIdWithSynonym message_;
 };
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp
new file mode 100644
index 0000000..76f083b
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp
@@ -0,0 +1,836 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationReplaceLinearAlgebraInstruction::
+    TransformationReplaceLinearAlgebraInstruction(
+        const spvtools::fuzz::protobufs::
+            TransformationReplaceLinearAlgebraInstruction& message)
+    : message_(message) {}
+
+TransformationReplaceLinearAlgebraInstruction::
+    TransformationReplaceLinearAlgebraInstruction(
+        const std::vector<uint32_t>& fresh_ids,
+        const protobufs::InstructionDescriptor& instruction_descriptor) {
+  for (auto fresh_id : fresh_ids) {
+    message_.add_fresh_ids(fresh_id);
+  }
+  *message_.mutable_instruction_descriptor() = instruction_descriptor;
+}
+
+bool TransformationReplaceLinearAlgebraInstruction::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  auto instruction =
+      FindInstruction(message_.instruction_descriptor(), ir_context);
+
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
+  // Right now we only support certain operations. When this issue is addressed
+  // the following conditional can use the function |spvOpcodeIsLinearAlgebra|.
+  // It must be a supported linear algebra instruction.
+  if (instruction->opcode() != SpvOpVectorTimesScalar &&
+      instruction->opcode() != SpvOpMatrixTimesScalar &&
+      instruction->opcode() != SpvOpVectorTimesMatrix &&
+      instruction->opcode() != SpvOpMatrixTimesVector &&
+      instruction->opcode() != SpvOpMatrixTimesMatrix &&
+      instruction->opcode() != SpvOpDot) {
+    return false;
+  }
+
+  // |message_.fresh_ids.size| must be the exact number of fresh ids needed to
+  // apply the transformation.
+  if (static_cast<uint32_t>(message_.fresh_ids().size()) !=
+      GetRequiredFreshIdCount(ir_context, instruction)) {
+    return false;
+  }
+
+  // All ids in |message_.fresh_ids| must be fresh.
+  for (uint32_t fresh_id : message_.fresh_ids()) {
+    if (!fuzzerutil::IsFreshId(ir_context, fresh_id)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void TransformationReplaceLinearAlgebraInstruction::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  auto linear_algebra_instruction =
+      FindInstruction(message_.instruction_descriptor(), ir_context);
+
+  switch (linear_algebra_instruction->opcode()) {
+    case SpvOpVectorTimesScalar:
+      ReplaceOpVectorTimesScalar(ir_context, linear_algebra_instruction);
+      break;
+    case SpvOpMatrixTimesScalar:
+      ReplaceOpMatrixTimesScalar(ir_context, linear_algebra_instruction);
+      break;
+    case SpvOpVectorTimesMatrix:
+      ReplaceOpVectorTimesMatrix(ir_context, linear_algebra_instruction);
+      break;
+    case SpvOpMatrixTimesVector:
+      ReplaceOpMatrixTimesVector(ir_context, linear_algebra_instruction);
+      break;
+    case SpvOpMatrixTimesMatrix:
+      ReplaceOpMatrixTimesMatrix(ir_context, linear_algebra_instruction);
+      break;
+    case SpvOpDot:
+      ReplaceOpDot(ir_context, linear_algebra_instruction);
+      break;
+    default:
+      assert(false && "Should be unreachable.");
+      break;
+  }
+
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation
+TransformationReplaceLinearAlgebraInstruction::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_replace_linear_algebra_instruction() = message_;
+  return result;
+}
+
+uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
+    opt::IRContext* ir_context, opt::Instruction* instruction) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
+  // Right now we only support certain operations.
+  switch (instruction->opcode()) {
+    case SpvOpVectorTimesScalar:
+      // For each vector component, 1 OpCompositeExtract and 1 OpFMul will be
+      // inserted.
+      return 2 *
+             ir_context->get_type_mgr()
+                 ->GetType(ir_context->get_def_use_mgr()
+                               ->GetDef(instruction->GetSingleWordInOperand(0))
+                               ->type_id())
+                 ->AsVector()
+                 ->element_count();
+    case SpvOpMatrixTimesScalar: {
+      // For each matrix column, |1 + column.size| OpCompositeExtract,
+      // |column.size| OpFMul and 1 OpCompositeConstruct instructions will be
+      // inserted.
+      auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
+          instruction->GetSingleWordInOperand(0));
+      auto matrix_type =
+          ir_context->get_type_mgr()->GetType(matrix_instruction->type_id());
+      return 2 * matrix_type->AsMatrix()->element_count() *
+             (1 + matrix_type->AsMatrix()
+                      ->element_type()
+                      ->AsVector()
+                      ->element_count());
+    }
+    case SpvOpVectorTimesMatrix: {
+      // For each vector component, 1 OpCompositeExtract instruction will be
+      // inserted. For each matrix column, |1 + vector_component_count|
+      // OpCompositeExtract, |vector_component_count| OpFMul and
+      // |vector_component_count - 1| OpFAdd instructions will be inserted.
+      auto vector_instruction = ir_context->get_def_use_mgr()->GetDef(
+          instruction->GetSingleWordInOperand(0));
+      auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
+          instruction->GetSingleWordInOperand(1));
+      uint32_t vector_component_count =
+          ir_context->get_type_mgr()
+              ->GetType(vector_instruction->type_id())
+              ->AsVector()
+              ->element_count();
+      uint32_t matrix_column_count =
+          ir_context->get_type_mgr()
+              ->GetType(matrix_instruction->type_id())
+              ->AsMatrix()
+              ->element_count();
+      return vector_component_count * (3 * matrix_column_count + 1);
+    }
+    case SpvOpMatrixTimesVector: {
+      // For each matrix column, |1 + matrix_row_count| OpCompositeExtract
+      // will be inserted. For each matrix row, |matrix_column_count| OpFMul and
+      // |matrix_column_count - 1| OpFAdd instructions will be inserted. For
+      // each vector component, 1 OpCompositeExtract instruction will be
+      // inserted.
+      auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
+          instruction->GetSingleWordInOperand(0));
+      uint32_t matrix_column_count =
+          ir_context->get_type_mgr()
+              ->GetType(matrix_instruction->type_id())
+              ->AsMatrix()
+              ->element_count();
+      uint32_t matrix_row_count = ir_context->get_type_mgr()
+                                      ->GetType(matrix_instruction->type_id())
+                                      ->AsMatrix()
+                                      ->element_type()
+                                      ->AsVector()
+                                      ->element_count();
+      return 3 * matrix_column_count * matrix_row_count +
+             2 * matrix_column_count - matrix_row_count;
+    }
+    case SpvOpMatrixTimesMatrix: {
+      // For each matrix 2 column, 1 OpCompositeExtract, 1 OpCompositeConstruct,
+      // |3 * matrix_1_row_count * matrix_1_column_count| OpCompositeExtract,
+      // |matrix_1_row_count * matrix_1_column_count| OpFMul,
+      // |matrix_1_row_count * (matrix_1_column_count - 1)| OpFAdd instructions
+      // will be inserted.
+      auto matrix_1_instruction = ir_context->get_def_use_mgr()->GetDef(
+          instruction->GetSingleWordInOperand(0));
+      uint32_t matrix_1_column_count =
+          ir_context->get_type_mgr()
+              ->GetType(matrix_1_instruction->type_id())
+              ->AsMatrix()
+              ->element_count();
+      uint32_t matrix_1_row_count =
+          ir_context->get_type_mgr()
+              ->GetType(matrix_1_instruction->type_id())
+              ->AsMatrix()
+              ->element_type()
+              ->AsVector()
+              ->element_count();
+
+      auto matrix_2_instruction = ir_context->get_def_use_mgr()->GetDef(
+          instruction->GetSingleWordInOperand(1));
+      uint32_t matrix_2_column_count =
+          ir_context->get_type_mgr()
+              ->GetType(matrix_2_instruction->type_id())
+              ->AsMatrix()
+              ->element_count();
+      return matrix_2_column_count *
+             (2 + matrix_1_row_count * (5 * matrix_1_column_count - 1));
+    }
+    case SpvOpDot:
+      // For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul
+      // will be inserted. The first two OpFMul instructions will result the
+      // first OpFAdd instruction to be inserted. For each remaining OpFMul, 1
+      // OpFAdd will be inserted. The last OpFAdd instruction is got by changing
+      // the OpDot instruction.
+      return 4 * ir_context->get_type_mgr()
+                     ->GetType(
+                         ir_context->get_def_use_mgr()
+                             ->GetDef(instruction->GetSingleWordInOperand(0))
+                             ->type_id())
+                     ->AsVector()
+                     ->element_count() -
+             2;
+    default:
+      assert(false && "Unsupported linear algebra instruction.");
+      return 0;
+  }
+}
+
+void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesScalar(
+    opt::IRContext* ir_context,
+    opt::Instruction* linear_algebra_instruction) const {
+  // Gets OpVectorTimesScalar in operands.
+  auto vector = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(0));
+  auto scalar = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(1));
+
+  uint32_t vector_component_count = ir_context->get_type_mgr()
+                                        ->GetType(vector->type_id())
+                                        ->AsVector()
+                                        ->element_count();
+  std::vector<uint32_t> float_multiplication_ids(vector_component_count);
+  uint32_t fresh_id_index = 0;
+
+  for (uint32_t i = 0; i < vector_component_count; i++) {
+    // Extracts |vector| component.
+    uint32_t vector_extract_id = message_.fresh_ids(fresh_id_index++);
+    fuzzerutil::UpdateModuleIdBound(ir_context, vector_extract_id);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeExtract, scalar->type_id(), vector_extract_id,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {vector->result_id()}},
+             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+    // Multiplies the |vector| component with the |scalar|.
+    uint32_t float_multiplication_id = message_.fresh_ids(fresh_id_index++);
+    float_multiplication_ids[i] = float_multiplication_id;
+    fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_id);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpFMul, scalar->type_id(), float_multiplication_id,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {vector_extract_id}},
+             {SPV_OPERAND_TYPE_ID, {scalar->result_id()}}})));
+  }
+
+  // The OpVectorTimesScalar instruction is changed to an OpCompositeConstruct
+  // instruction.
+  linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+  linear_algebra_instruction->SetInOperand(0, {float_multiplication_ids[0]});
+  linear_algebra_instruction->SetInOperand(1, {float_multiplication_ids[1]});
+  for (uint32_t i = 2; i < float_multiplication_ids.size(); i++) {
+    linear_algebra_instruction->AddOperand(
+        {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[i]}});
+  }
+}
+
+void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesScalar(
+    opt::IRContext* ir_context,
+    opt::Instruction* linear_algebra_instruction) const {
+  // Gets OpMatrixTimesScalar in operands.
+  auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(0));
+  auto scalar_instruction = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(1));
+
+  // Gets matrix information.
+  uint32_t matrix_column_count = ir_context->get_type_mgr()
+                                     ->GetType(matrix_instruction->type_id())
+                                     ->AsMatrix()
+                                     ->element_count();
+  auto matrix_column_type = ir_context->get_type_mgr()
+                                ->GetType(matrix_instruction->type_id())
+                                ->AsMatrix()
+                                ->element_type();
+  uint32_t matrix_column_size = matrix_column_type->AsVector()->element_count();
+
+  std::vector<uint32_t> composite_construct_ids(matrix_column_count);
+  uint32_t fresh_id_index = 0;
+
+  for (uint32_t i = 0; i < matrix_column_count; i++) {
+    // Extracts |matrix| column.
+    uint32_t matrix_extract_id = message_.fresh_ids(fresh_id_index++);
+    fuzzerutil::UpdateModuleIdBound(ir_context, matrix_extract_id);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeExtract,
+        ir_context->get_type_mgr()->GetId(matrix_column_type),
+        matrix_extract_id,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
+             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+    std::vector<uint32_t> float_multiplication_ids(matrix_column_size);
+
+    for (uint32_t j = 0; j < matrix_column_size; j++) {
+      // Extracts |column| component.
+      uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++);
+      fuzzerutil::UpdateModuleIdBound(ir_context, column_extract_id);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpCompositeExtract, scalar_instruction->type_id(),
+          column_extract_id,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {matrix_extract_id}},
+               {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
+
+      // Multiplies the |column| component with the |scalar|.
+      float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++);
+      fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_ids[j]);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpFMul, scalar_instruction->type_id(),
+          float_multiplication_ids[j],
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {column_extract_id}},
+               {SPV_OPERAND_TYPE_ID, {scalar_instruction->result_id()}}})));
+    }
+
+    // Constructs a new column multiplied by |scalar|.
+    opt::Instruction::OperandList composite_construct_in_operands;
+    for (uint32_t& float_multiplication_id : float_multiplication_ids) {
+      composite_construct_in_operands.push_back(
+          {SPV_OPERAND_TYPE_ID, {float_multiplication_id}});
+    }
+    composite_construct_ids[i] = message_.fresh_ids(fresh_id_index++);
+    fuzzerutil::UpdateModuleIdBound(ir_context, composite_construct_ids[i]);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeConstruct,
+        ir_context->get_type_mgr()->GetId(matrix_column_type),
+        composite_construct_ids[i], composite_construct_in_operands));
+  }
+
+  // The OpMatrixTimesScalar instruction is changed to an OpCompositeConstruct
+  // instruction.
+  linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+  linear_algebra_instruction->SetInOperand(0, {composite_construct_ids[0]});
+  linear_algebra_instruction->SetInOperand(1, {composite_construct_ids[1]});
+  for (uint32_t i = 2; i < composite_construct_ids.size(); i++) {
+    linear_algebra_instruction->AddOperand(
+        {SPV_OPERAND_TYPE_ID, {composite_construct_ids[i]}});
+  }
+}
+
+void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesMatrix(
+    opt::IRContext* ir_context,
+    opt::Instruction* linear_algebra_instruction) const {
+  // Gets vector information.
+  auto vector_instruction = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(0));
+  uint32_t vector_component_count = ir_context->get_type_mgr()
+                                        ->GetType(vector_instruction->type_id())
+                                        ->AsVector()
+                                        ->element_count();
+  auto vector_component_type = ir_context->get_type_mgr()
+                                   ->GetType(vector_instruction->type_id())
+                                   ->AsVector()
+                                   ->element_type();
+
+  // Extracts vector components.
+  uint32_t fresh_id_index = 0;
+  std::vector<uint32_t> vector_component_ids(vector_component_count);
+  for (uint32_t i = 0; i < vector_component_count; i++) {
+    vector_component_ids[i] = message_.fresh_ids(fresh_id_index++);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeExtract,
+        ir_context->get_type_mgr()->GetId(vector_component_type),
+        vector_component_ids[i],
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {vector_instruction->result_id()}},
+             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+  }
+
+  // Gets matrix information.
+  auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(1));
+  uint32_t matrix_column_count = ir_context->get_type_mgr()
+                                     ->GetType(matrix_instruction->type_id())
+                                     ->AsMatrix()
+                                     ->element_count();
+  auto matrix_column_type = ir_context->get_type_mgr()
+                                ->GetType(matrix_instruction->type_id())
+                                ->AsMatrix()
+                                ->element_type();
+
+  std::vector<uint32_t> result_component_ids(matrix_column_count);
+  for (uint32_t i = 0; i < matrix_column_count; i++) {
+    // Extracts matrix column.
+    uint32_t matrix_extract_id = message_.fresh_ids(fresh_id_index++);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeExtract,
+        ir_context->get_type_mgr()->GetId(matrix_column_type),
+        matrix_extract_id,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
+             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+    std::vector<uint32_t> float_multiplication_ids(vector_component_count);
+    for (uint32_t j = 0; j < vector_component_count; j++) {
+      // Extracts column component.
+      uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpCompositeExtract,
+          ir_context->get_type_mgr()->GetId(vector_component_type),
+          column_extract_id,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {matrix_extract_id}},
+               {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
+
+      // Multiplies corresponding vector and column components.
+      float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpFMul,
+          ir_context->get_type_mgr()->GetId(vector_component_type),
+          float_multiplication_ids[j],
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {vector_component_ids[j]}},
+               {SPV_OPERAND_TYPE_ID, {column_extract_id}}})));
+    }
+
+    // Adds the multiplication results.
+    std::vector<uint32_t> float_add_ids;
+    uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
+    float_add_ids.push_back(float_add_id);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpFAdd,
+        ir_context->get_type_mgr()->GetId(vector_component_type), float_add_id,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
+             {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}})));
+    for (uint32_t j = 2; j < float_multiplication_ids.size(); j++) {
+      float_add_id = message_.fresh_ids(fresh_id_index++);
+      float_add_ids.push_back(float_add_id);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpFAdd,
+          ir_context->get_type_mgr()->GetId(vector_component_type),
+          float_add_id,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[j]}},
+               {SPV_OPERAND_TYPE_ID, {float_add_ids[j - 2]}}})));
+    }
+
+    result_component_ids[i] = float_add_ids.back();
+  }
+
+  // The OpVectorTimesMatrix instruction is changed to an OpCompositeConstruct
+  // instruction.
+  linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+  linear_algebra_instruction->SetInOperand(0, {result_component_ids[0]});
+  linear_algebra_instruction->SetInOperand(1, {result_component_ids[1]});
+  for (uint32_t i = 2; i < result_component_ids.size(); i++) {
+    linear_algebra_instruction->AddOperand(
+        {SPV_OPERAND_TYPE_ID, {result_component_ids[i]}});
+  }
+
+  fuzzerutil::UpdateModuleIdBound(
+      ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
+}
+
+void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesVector(
+    opt::IRContext* ir_context,
+    opt::Instruction* linear_algebra_instruction) const {
+  // Gets matrix information.
+  auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(0));
+  uint32_t matrix_column_count = ir_context->get_type_mgr()
+                                     ->GetType(matrix_instruction->type_id())
+                                     ->AsMatrix()
+                                     ->element_count();
+  auto matrix_column_type = ir_context->get_type_mgr()
+                                ->GetType(matrix_instruction->type_id())
+                                ->AsMatrix()
+                                ->element_type();
+  uint32_t matrix_row_count = matrix_column_type->AsVector()->element_count();
+
+  // Extracts matrix columns.
+  uint32_t fresh_id_index = 0;
+  std::vector<uint32_t> matrix_column_ids(matrix_column_count);
+  for (uint32_t i = 0; i < matrix_column_count; i++) {
+    matrix_column_ids[i] = message_.fresh_ids(fresh_id_index++);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeExtract,
+        ir_context->get_type_mgr()->GetId(matrix_column_type),
+        matrix_column_ids[i],
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
+             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+  }
+
+  // Gets vector information.
+  auto vector_instruction = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(1));
+  auto vector_component_type = ir_context->get_type_mgr()
+                                   ->GetType(vector_instruction->type_id())
+                                   ->AsVector()
+                                   ->element_type();
+
+  // Extracts vector components.
+  std::vector<uint32_t> vector_component_ids(matrix_column_count);
+  for (uint32_t i = 0; i < matrix_column_count; i++) {
+    vector_component_ids[i] = message_.fresh_ids(fresh_id_index++);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeExtract,
+        ir_context->get_type_mgr()->GetId(vector_component_type),
+        vector_component_ids[i],
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {vector_instruction->result_id()}},
+             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+  }
+
+  std::vector<uint32_t> result_component_ids(matrix_row_count);
+  for (uint32_t i = 0; i < matrix_row_count; i++) {
+    std::vector<uint32_t> float_multiplication_ids(matrix_column_count);
+    for (uint32_t j = 0; j < matrix_column_count; j++) {
+      // Extracts column component.
+      uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpCompositeExtract,
+          ir_context->get_type_mgr()->GetId(vector_component_type),
+          column_extract_id,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {matrix_column_ids[j]}},
+               {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+      // Multiplies corresponding vector and column components.
+      float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpFMul,
+          ir_context->get_type_mgr()->GetId(vector_component_type),
+          float_multiplication_ids[j],
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {column_extract_id}},
+               {SPV_OPERAND_TYPE_ID, {vector_component_ids[j]}}})));
+    }
+
+    // Adds the multiplication results.
+    std::vector<uint32_t> float_add_ids;
+    uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
+    float_add_ids.push_back(float_add_id);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpFAdd,
+        ir_context->get_type_mgr()->GetId(vector_component_type), float_add_id,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
+             {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}})));
+    for (uint32_t j = 2; j < float_multiplication_ids.size(); j++) {
+      float_add_id = message_.fresh_ids(fresh_id_index++);
+      float_add_ids.push_back(float_add_id);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpFAdd,
+          ir_context->get_type_mgr()->GetId(vector_component_type),
+          float_add_id,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[j]}},
+               {SPV_OPERAND_TYPE_ID, {float_add_ids[j - 2]}}})));
+    }
+
+    result_component_ids[i] = float_add_ids.back();
+  }
+
+  // The OpMatrixTimesVector instruction is changed to an OpCompositeConstruct
+  // instruction.
+  linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+  linear_algebra_instruction->SetInOperand(0, {result_component_ids[0]});
+  linear_algebra_instruction->SetInOperand(1, {result_component_ids[1]});
+  for (uint32_t i = 2; i < result_component_ids.size(); i++) {
+    linear_algebra_instruction->AddOperand(
+        {SPV_OPERAND_TYPE_ID, {result_component_ids[i]}});
+  }
+
+  fuzzerutil::UpdateModuleIdBound(
+      ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
+}
+
+void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesMatrix(
+    opt::IRContext* ir_context,
+    opt::Instruction* linear_algebra_instruction) const {
+  // Gets matrix 1 information.
+  auto matrix_1_instruction = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(0));
+  uint32_t matrix_1_column_count =
+      ir_context->get_type_mgr()
+          ->GetType(matrix_1_instruction->type_id())
+          ->AsMatrix()
+          ->element_count();
+  auto matrix_1_column_type = ir_context->get_type_mgr()
+                                  ->GetType(matrix_1_instruction->type_id())
+                                  ->AsMatrix()
+                                  ->element_type();
+  auto matrix_1_column_component_type =
+      matrix_1_column_type->AsVector()->element_type();
+  uint32_t matrix_1_row_count =
+      matrix_1_column_type->AsVector()->element_count();
+
+  // Gets matrix 2 information.
+  auto matrix_2_instruction = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(1));
+  uint32_t matrix_2_column_count =
+      ir_context->get_type_mgr()
+          ->GetType(matrix_2_instruction->type_id())
+          ->AsMatrix()
+          ->element_count();
+  auto matrix_2_column_type = ir_context->get_type_mgr()
+                                  ->GetType(matrix_2_instruction->type_id())
+                                  ->AsMatrix()
+                                  ->element_type();
+
+  uint32_t fresh_id_index = 0;
+  std::vector<uint32_t> result_column_ids(matrix_2_column_count);
+  for (uint32_t i = 0; i < matrix_2_column_count; i++) {
+    // Extracts matrix 2 column.
+    uint32_t matrix_2_column_id = message_.fresh_ids(fresh_id_index++);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeExtract,
+        ir_context->get_type_mgr()->GetId(matrix_2_column_type),
+        matrix_2_column_id,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {matrix_2_instruction->result_id()}},
+             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+    std::vector<uint32_t> column_component_ids(matrix_1_row_count);
+    for (uint32_t j = 0; j < matrix_1_row_count; j++) {
+      std::vector<uint32_t> float_multiplication_ids(matrix_1_column_count);
+      for (uint32_t k = 0; k < matrix_1_column_count; k++) {
+        // Extracts matrix 1 column.
+        uint32_t matrix_1_column_id = message_.fresh_ids(fresh_id_index++);
+        linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+            ir_context, SpvOpCompositeExtract,
+            ir_context->get_type_mgr()->GetId(matrix_1_column_type),
+            matrix_1_column_id,
+            opt::Instruction::OperandList(
+                {{SPV_OPERAND_TYPE_ID, {matrix_1_instruction->result_id()}},
+                 {SPV_OPERAND_TYPE_LITERAL_INTEGER, {k}}})));
+
+        // Extracts matrix 1 column component.
+        uint32_t matrix_1_column_component_id =
+            message_.fresh_ids(fresh_id_index++);
+        linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+            ir_context, SpvOpCompositeExtract,
+            ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
+            matrix_1_column_component_id,
+            opt::Instruction::OperandList(
+                {{SPV_OPERAND_TYPE_ID, {matrix_1_column_id}},
+                 {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
+
+        // Extracts matrix 2 column component.
+        uint32_t matrix_2_column_component_id =
+            message_.fresh_ids(fresh_id_index++);
+        linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+            ir_context, SpvOpCompositeExtract,
+            ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
+            matrix_2_column_component_id,
+            opt::Instruction::OperandList(
+                {{SPV_OPERAND_TYPE_ID, {matrix_2_column_id}},
+                 {SPV_OPERAND_TYPE_LITERAL_INTEGER, {k}}})));
+
+        // Multiplies corresponding matrix 1 and matrix 2 column components.
+        float_multiplication_ids[k] = message_.fresh_ids(fresh_id_index++);
+        linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+            ir_context, SpvOpFMul,
+            ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
+            float_multiplication_ids[k],
+            opt::Instruction::OperandList(
+                {{SPV_OPERAND_TYPE_ID, {matrix_1_column_component_id}},
+                 {SPV_OPERAND_TYPE_ID, {matrix_2_column_component_id}}})));
+      }
+
+      // Adds the multiplication results.
+      std::vector<uint32_t> float_add_ids;
+      uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
+      float_add_ids.push_back(float_add_id);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpFAdd,
+          ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
+          float_add_id,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
+               {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}})));
+      for (uint32_t k = 2; k < float_multiplication_ids.size(); k++) {
+        float_add_id = message_.fresh_ids(fresh_id_index++);
+        float_add_ids.push_back(float_add_id);
+        linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+            ir_context, SpvOpFAdd,
+            ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
+            float_add_id,
+            opt::Instruction::OperandList(
+                {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[k]}},
+                 {SPV_OPERAND_TYPE_ID, {float_add_ids[k - 2]}}})));
+      }
+
+      column_component_ids[j] = float_add_ids.back();
+    }
+
+    // Inserts the resulting matrix column.
+    opt::Instruction::OperandList in_operands;
+    for (auto& column_component_id : column_component_ids) {
+      in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}});
+    }
+    result_column_ids[i] = message_.fresh_ids(fresh_id_index++);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeConstruct,
+        ir_context->get_type_mgr()->GetId(matrix_1_column_type),
+        result_column_ids[i], opt::Instruction::OperandList(in_operands)));
+  }
+
+  // The OpMatrixTimesMatrix instruction is changed to an OpCompositeConstruct
+  // instruction.
+  linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+  linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]});
+  linear_algebra_instruction->SetInOperand(1, {result_column_ids[1]});
+  for (uint32_t i = 2; i < result_column_ids.size(); i++) {
+    linear_algebra_instruction->AddOperand(
+        {SPV_OPERAND_TYPE_ID, {result_column_ids[i]}});
+  }
+
+  fuzzerutil::UpdateModuleIdBound(
+      ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
+}
+
+void TransformationReplaceLinearAlgebraInstruction::ReplaceOpDot(
+    opt::IRContext* ir_context,
+    opt::Instruction* linear_algebra_instruction) const {
+  // Gets OpDot in operands.
+  auto vector_1 = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(0));
+  auto vector_2 = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(1));
+
+  uint32_t vectors_component_count = ir_context->get_type_mgr()
+                                         ->GetType(vector_1->type_id())
+                                         ->AsVector()
+                                         ->element_count();
+  std::vector<uint32_t> float_multiplication_ids(vectors_component_count);
+  uint32_t fresh_id_index = 0;
+
+  for (uint32_t i = 0; i < vectors_component_count; i++) {
+    // Extracts |vector_1| component.
+    uint32_t vector_1_extract_id = message_.fresh_ids(fresh_id_index++);
+    fuzzerutil::UpdateModuleIdBound(ir_context, vector_1_extract_id);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeExtract,
+        linear_algebra_instruction->type_id(), vector_1_extract_id,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {vector_1->result_id()}},
+             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+    // Extracts |vector_2| component.
+    uint32_t vector_2_extract_id = message_.fresh_ids(fresh_id_index++);
+    fuzzerutil::UpdateModuleIdBound(ir_context, vector_2_extract_id);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeExtract,
+        linear_algebra_instruction->type_id(), vector_2_extract_id,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {vector_2->result_id()}},
+             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+    // Multiplies the pair of components.
+    float_multiplication_ids[i] = message_.fresh_ids(fresh_id_index++);
+    fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_ids[i]);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpFMul, linear_algebra_instruction->type_id(),
+        float_multiplication_ids[i],
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {vector_1_extract_id}},
+             {SPV_OPERAND_TYPE_ID, {vector_2_extract_id}}})));
+  }
+
+  // If the vector has 2 components, then there will be 2 float multiplication
+  // instructions.
+  if (vectors_component_count == 2) {
+    linear_algebra_instruction->SetOpcode(SpvOpFAdd);
+    linear_algebra_instruction->SetInOperand(0, {float_multiplication_ids[0]});
+    linear_algebra_instruction->SetInOperand(1, {float_multiplication_ids[1]});
+  } else {
+    // The first OpFAdd instruction has as operands the first two OpFMul
+    // instructions.
+    std::vector<uint32_t> float_add_ids;
+    uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
+    float_add_ids.push_back(float_add_id);
+    fuzzerutil::UpdateModuleIdBound(ir_context, float_add_id);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpFAdd, linear_algebra_instruction->type_id(),
+        float_add_id,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
+             {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}})));
+
+    // The remaining OpFAdd instructions has as operands an OpFMul and an OpFAdd
+    // instruction.
+    for (uint32_t i = 2; i < float_multiplication_ids.size() - 1; i++) {
+      float_add_id = message_.fresh_ids(fresh_id_index++);
+      fuzzerutil::UpdateModuleIdBound(ir_context, float_add_id);
+      float_add_ids.push_back(float_add_id);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpFAdd, linear_algebra_instruction->type_id(),
+          float_add_id,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[i]}},
+               {SPV_OPERAND_TYPE_ID, {float_add_ids[i - 2]}}})));
+    }
+
+    // The last OpFAdd instruction is got by changing some of the OpDot
+    // instruction attributes.
+    linear_algebra_instruction->SetOpcode(SpvOpFAdd);
+    linear_algebra_instruction->SetInOperand(
+        0, {float_multiplication_ids[float_multiplication_ids.size() - 1]});
+    linear_algebra_instruction->SetInOperand(
+        1, {float_add_ids[float_add_ids.size() - 1]});
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_linear_algebra_instruction.h b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_linear_algebra_instruction.h
new file mode 100644
index 0000000..530c1f2
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_linear_algebra_instruction.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_LINEAR_ALGEBRA_INSTRUCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_LINEAR_ALGEBRA_INSTRUCTION_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationReplaceLinearAlgebraInstruction : public Transformation {
+ public:
+  explicit TransformationReplaceLinearAlgebraInstruction(
+      const protobufs::TransformationReplaceLinearAlgebraInstruction& message);
+
+  TransformationReplaceLinearAlgebraInstruction(
+      const std::vector<uint32_t>& fresh_ids,
+      const protobufs::InstructionDescriptor& instruction_descriptor);
+
+  // - |message_.fresh_ids| must be fresh ids needed to apply the
+  // transformation.
+  // - |message_.instruction_descriptor| must be a linear algebra instruction
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Replaces a linear algebra instruction.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Returns the number of ids needed to apply the transformation.
+  static uint32_t GetRequiredFreshIdCount(opt::IRContext* ir_context,
+                                          opt::Instruction* instruction);
+
+ private:
+  protobufs::TransformationReplaceLinearAlgebraInstruction message_;
+
+  // Replaces an OpVectorTimesScalar instruction.
+  void ReplaceOpVectorTimesScalar(opt::IRContext* ir_context,
+                                  opt::Instruction* instruction) const;
+
+  // Replaces an OpMatrixTimesScalar instruction.
+  void ReplaceOpMatrixTimesScalar(opt::IRContext* ir_context,
+                                  opt::Instruction* instruction) const;
+
+  // Replaces an OpVectorTimesMatrix instruction.
+  void ReplaceOpVectorTimesMatrix(opt::IRContext* ir_context,
+                                  opt::Instruction* instruction) const;
+
+  // Replaces an OpMatrixTimesVector instruction.
+  void ReplaceOpMatrixTimesVector(opt::IRContext* ir_context,
+                                  opt::Instruction* instruction) const;
+
+  // Replaces an OpMatrixTimesMatrix instruction.
+  void ReplaceOpMatrixTimesMatrix(opt::IRContext* ir_context,
+                                  opt::Instruction* instruction) const;
+
+  // Replaces an OpDot instruction.
+  void ReplaceOpDot(opt::IRContext* ir_context,
+                    opt::Instruction* instruction) const;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_REPLACE_LINEAR_ALGEBRA_INSTRUCTION_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp
new file mode 100644
index 0000000..52964bf
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp
@@ -0,0 +1,192 @@
+// 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 "transformation_replace_load_store_with_copy_memory.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/opcode.h"
+
+namespace spvtools {
+namespace fuzz {
+
+namespace {
+const uint32_t kOpStoreOperandIndexTargetVariable = 0;
+const uint32_t kOpStoreOperandIndexIntermediateIdToWrite = 1;
+const uint32_t kOpLoadOperandIndexSourceVariable = 2;
+}  // namespace
+
+TransformationReplaceLoadStoreWithCopyMemory::
+    TransformationReplaceLoadStoreWithCopyMemory(
+        const spvtools::fuzz::protobufs::
+            TransformationReplaceLoadStoreWithCopyMemory& message)
+    : message_(message) {}
+
+TransformationReplaceLoadStoreWithCopyMemory::
+    TransformationReplaceLoadStoreWithCopyMemory(
+        const protobufs::InstructionDescriptor& load_instruction_descriptor,
+        const protobufs::InstructionDescriptor& store_instruction_descriptor) {
+  *message_.mutable_load_instruction_descriptor() = load_instruction_descriptor;
+  *message_.mutable_store_instruction_descriptor() =
+      store_instruction_descriptor;
+}
+bool TransformationReplaceLoadStoreWithCopyMemory::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  // This transformation is only applicable to the pair of OpLoad and OpStore
+  // instructions.
+  if (message_.load_instruction_descriptor().target_instruction_opcode() !=
+      SpvOpLoad) {
+    return false;
+  }
+  if (message_.store_instruction_descriptor().target_instruction_opcode() !=
+      SpvOpStore) {
+    return false;
+  }
+
+  // The OpLoad instruction must be defined.
+  auto load_instruction =
+      FindInstruction(message_.load_instruction_descriptor(), ir_context);
+  if (!load_instruction) {
+    return false;
+  }
+
+  // The OpStore instruction must be defined.
+  auto store_instruction =
+      FindInstruction(message_.store_instruction_descriptor(), ir_context);
+  if (!store_instruction) {
+    return false;
+  }
+
+  // Intermediate values of the OpLoad and the OpStore must match.
+  if (load_instruction->result_id() !=
+      store_instruction->GetSingleWordOperand(
+          kOpStoreOperandIndexIntermediateIdToWrite)) {
+    return false;
+  }
+
+  // Get storage class of the variable pointed by the source operand in OpLoad.
+  opt::Instruction* source_id = ir_context->get_def_use_mgr()->GetDef(
+      load_instruction->GetSingleWordOperand(2));
+  SpvStorageClass storage_class = fuzzerutil::GetStorageClassFromPointerType(
+      ir_context, source_id->type_id());
+
+  // Iterate over all instructions between |load_instruction| and
+  // |store_instruction|.
+  for (auto it = load_instruction; it != store_instruction;
+       it = it->NextNode()) {
+    //|load_instruction| and |store_instruction| are not in the same block.
+    if (it == nullptr) {
+      return false;
+    }
+
+    // We need to make sure that the value pointed to by the source of the
+    // OpLoad hasn't changed by the time we see the matching OpStore
+    // instruction.
+    if (IsMemoryWritingOpCode(it->opcode())) {
+      return false;
+    } else if (IsMemoryBarrierOpCode(it->opcode()) &&
+               !IsStorageClassSafeAcrossMemoryBarriers(storage_class)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void TransformationReplaceLoadStoreWithCopyMemory::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  // OpLoad and OpStore instructions must be defined.
+  auto load_instruction =
+      FindInstruction(message_.load_instruction_descriptor(), ir_context);
+  assert(load_instruction && load_instruction->opcode() == SpvOpLoad &&
+         "The required OpLoad instruction must be defined.");
+  auto store_instruction =
+      FindInstruction(message_.store_instruction_descriptor(), ir_context);
+  assert(store_instruction && store_instruction->opcode() == SpvOpStore &&
+         "The required OpStore instruction must be defined.");
+
+  // Intermediate values of the OpLoad and the OpStore must match.
+  assert(load_instruction->result_id() ==
+             store_instruction->GetSingleWordOperand(
+                 kOpStoreOperandIndexIntermediateIdToWrite) &&
+         "OpLoad and OpStore must refer to the same value.");
+
+  // Get the ids of the source operand of the OpLoad and the target operand of
+  // the OpStore.
+  uint32_t source_variable_id =
+      load_instruction->GetSingleWordOperand(kOpLoadOperandIndexSourceVariable);
+  uint32_t target_variable_id = store_instruction->GetSingleWordOperand(
+      kOpStoreOperandIndexTargetVariable);
+
+  // Insert the OpCopyMemory instruction before the OpStore instruction.
+  store_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpCopyMemory, 0, 0,
+      opt::Instruction::OperandList(
+          {{SPV_OPERAND_TYPE_ID, {target_variable_id}},
+           {SPV_OPERAND_TYPE_ID, {source_variable_id}}})));
+
+  // Remove the OpStore instruction.
+  ir_context->KillInst(store_instruction);
+
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryWritingOpCode(
+    SpvOp op_code) {
+  if (spvOpcodeIsAtomicOp(op_code)) {
+    return op_code != SpvOpAtomicLoad;
+  }
+  switch (op_code) {
+    case SpvOpStore:
+    case SpvOpCopyMemory:
+    case SpvOpCopyMemorySized:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryBarrierOpCode(
+    SpvOp op_code) {
+  switch (op_code) {
+    case SpvOpMemoryBarrier:
+    case SpvOpMemoryNamedBarrier:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool TransformationReplaceLoadStoreWithCopyMemory::
+    IsStorageClassSafeAcrossMemoryBarriers(SpvStorageClass storage_class) {
+  switch (storage_class) {
+    case SpvStorageClassUniformConstant:
+    case SpvStorageClassInput:
+    case SpvStorageClassUniform:
+    case SpvStorageClassPrivate:
+    case SpvStorageClassFunction:
+      return true;
+    default:
+      return false;
+  }
+}
+
+protobufs::Transformation
+TransformationReplaceLoadStoreWithCopyMemory::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_replace_load_store_with_copy_memory() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h
new file mode 100644
index 0000000..0fb9a09
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h
@@ -0,0 +1,76 @@
+// 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 SPIRV_TOOLS_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H
+#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationReplaceLoadStoreWithCopyMemory : public Transformation {
+ public:
+  explicit TransformationReplaceLoadStoreWithCopyMemory(
+      const protobufs::TransformationReplaceLoadStoreWithCopyMemory& message);
+
+  TransformationReplaceLoadStoreWithCopyMemory(
+      const protobufs::InstructionDescriptor& load_instruction_descriptor,
+      const protobufs::InstructionDescriptor& store_instruction_descriptor);
+
+  // - |message_.load_instruction_descriptor| must identify an OpLoad
+  //   instruction.
+  // - |message_.store_instruction_descriptor| must identify an OpStore
+  //   instruction.
+  // - The OpStore must write the intermediate value loaded by the OpLoad.
+  // - The OpLoad and the OpStore must not have certain instruction in between
+  //   (checked by IsMemoryWritingOpCode(), IsMemoryBarrierOpCode(),
+  //   IsStorageClassSafeAcrossMemoryBarriers()).
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Takes a pair of instruction descriptors to OpLoad and OpStore that have the
+  // same intermediate value and replaces the OpStore with an equivalent
+  // OpCopyMemory.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  // Checks if the instruction that has an |op_code| might write to
+  // the source operand of the OpLoad instruction.
+  static bool IsMemoryWritingOpCode(SpvOp op_code);
+
+  // Checks if the instruction that has an |op_code| is a memory barrier that
+  // could interfere with the source operand of the OpLoad instruction
+  static bool IsMemoryBarrierOpCode(SpvOp op_code);
+
+  // Checks if the |storage_class| of the source operand of the OpLoad
+  // instruction implies that this variable cannot change (due to other threads)
+  // across memory barriers.
+  static bool IsStorageClassSafeAcrossMemoryBarriers(
+      SpvStorageClass storage_class);
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationReplaceLoadStoreWithCopyMemory message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_parameter_with_global.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_parameter_with_global.cpp
new file mode 100644
index 0000000..f680b63
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_parameter_with_global.cpp
@@ -0,0 +1,224 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_parameter_with_global.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationReplaceParameterWithGlobal::
+    TransformationReplaceParameterWithGlobal(
+        const protobufs::TransformationReplaceParameterWithGlobal& message)
+    : message_(message) {}
+
+TransformationReplaceParameterWithGlobal::
+    TransformationReplaceParameterWithGlobal(
+        uint32_t function_type_fresh_id, uint32_t parameter_id,
+        uint32_t global_variable_fresh_id) {
+  message_.set_function_type_fresh_id(function_type_fresh_id);
+  message_.set_parameter_id(parameter_id);
+  message_.set_global_variable_fresh_id(global_variable_fresh_id);
+}
+
+bool TransformationReplaceParameterWithGlobal::IsApplicable(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
+  // Check that |parameter_id| is valid.
+  const auto* param_inst =
+      ir_context->get_def_use_mgr()->GetDef(message_.parameter_id());
+  if (!param_inst || param_inst->opcode() != SpvOpFunctionParameter) {
+    return false;
+  }
+
+  // Check that function exists and is not an entry point.
+  const auto* function = fuzzerutil::GetFunctionFromParameterId(
+      ir_context, message_.parameter_id());
+  if (!function ||
+      fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
+    return false;
+  }
+
+  // We already know that the function has at least one parameter -
+  // |parameter_id|.
+
+  // Check that replaced parameter has valid type.
+  const auto* param_type =
+      ir_context->get_type_mgr()->GetType(param_inst->type_id());
+  assert(param_type && "Parameter has invalid type");
+  if (!IsParameterTypeSupported(*param_type)) {
+    return false;
+  }
+
+  // Check that initializer for the global variable exists in the module.
+  if (fuzzerutil::MaybeGetZeroConstant(ir_context, transformation_context,
+                                       param_inst->type_id(), false) == 0) {
+    return false;
+  }
+
+  // Check that pointer type for the global variable exists in the module.
+  if (!fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(),
+                                       SpvStorageClassPrivate)) {
+    return false;
+  }
+
+  return fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()) &&
+         fuzzerutil::IsFreshId(ir_context,
+                               message_.global_variable_fresh_id()) &&
+         message_.function_type_fresh_id() !=
+             message_.global_variable_fresh_id();
+}
+
+void TransformationReplaceParameterWithGlobal::Apply(
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  const auto* param_inst =
+      ir_context->get_def_use_mgr()->GetDef(message_.parameter_id());
+  assert(param_inst && "Parameter must exist");
+
+  // Create global variable to store parameter's value.
+  fuzzerutil::AddGlobalVariable(
+      ir_context, message_.global_variable_fresh_id(),
+      fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(),
+                                      SpvStorageClassPrivate),
+      SpvStorageClassPrivate,
+      fuzzerutil::MaybeGetZeroConstant(ir_context, *transformation_context,
+                                       param_inst->type_id(), false));
+
+  // Mark the global variable's pointee as irrelevant if replaced parameter is
+  // irrelevant.
+  if (transformation_context->GetFactManager()->IdIsIrrelevant(
+          message_.parameter_id())) {
+    transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+        message_.global_variable_fresh_id());
+  }
+
+  auto* function = fuzzerutil::GetFunctionFromParameterId(
+      ir_context, message_.parameter_id());
+  assert(function && "Function must exist");
+
+  // Insert an OpLoad instruction right after OpVariable instructions.
+  auto it = function->begin()->begin();
+  while (it != function->begin()->end() &&
+         !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, it)) {
+    ++it;
+  }
+
+  assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, it) &&
+         "Can't insert OpLoad or OpCopyMemory into the first basic block of "
+         "the function");
+
+  it.InsertBefore(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpLoad, param_inst->type_id(), param_inst->result_id(),
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}}}));
+
+  // Calculate the index of the replaced parameter (we need to know this to
+  // remove operands from the OpFunctionCall).
+  auto params = fuzzerutil::GetParameters(ir_context, function->result_id());
+  auto parameter_index = static_cast<uint32_t>(params.size());
+  for (uint32_t i = 0, n = static_cast<uint32_t>(params.size()); i < n; ++i) {
+    if (params[i]->result_id() == message_.parameter_id()) {
+      parameter_index = i;
+      break;
+    }
+  }
+
+  assert(parameter_index != params.size() &&
+         "Parameter must exist in the function");
+
+  // Update all relevant OpFunctionCall instructions.
+  for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
+    assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, inst) &&
+           "Can't insert OpStore right before the function call");
+
+    // Insert an OpStore before the OpFunctionCall. +1 since the first
+    // operand of OpFunctionCall is an id of the function.
+    inst->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpStore, 0, 0,
+        opt::Instruction::OperandList{
+            {SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}},
+            {SPV_OPERAND_TYPE_ID,
+             {inst->GetSingleWordInOperand(parameter_index + 1)}}}));
+
+    // +1 since the first operand of OpFunctionCall is an id of the
+    // function.
+    inst->RemoveInOperand(parameter_index + 1);
+  }
+
+  // Remove the parameter from the function.
+  function->RemoveParameter(message_.parameter_id());
+
+  // Update function's type.
+  {
+    // We use a separate scope here since |old_function_type| might become a
+    // dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
+
+    auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
+    assert(old_function_type && "Function has invalid type");
+
+    // +1 and -1 since the first operand is the return type id.
+    std::vector<uint32_t> parameter_type_ids;
+    for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
+      if (i - 1 != parameter_index) {
+        parameter_type_ids.push_back(
+            old_function_type->GetSingleWordInOperand(i));
+      }
+    }
+
+    fuzzerutil::UpdateFunctionType(
+        ir_context, function->result_id(), message_.function_type_fresh_id(),
+        old_function_type->GetSingleWordInOperand(0), parameter_type_ids);
+  }
+
+  // Make sure our changes are analyzed
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationReplaceParameterWithGlobal::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_replace_parameter_with_global() = message_;
+  return result;
+}
+
+bool TransformationReplaceParameterWithGlobal::IsParameterTypeSupported(
+    const opt::analysis::Type& type) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
+  //  Think about other type instructions we can add here.
+  switch (type.kind()) {
+    case opt::analysis::Type::kBool:
+    case opt::analysis::Type::kInteger:
+    case opt::analysis::Type::kFloat:
+    case opt::analysis::Type::kArray:
+    case opt::analysis::Type::kMatrix:
+    case opt::analysis::Type::kVector:
+      return true;
+    case opt::analysis::Type::kStruct:
+      return std::all_of(type.AsStruct()->element_types().begin(),
+                         type.AsStruct()->element_types().end(),
+                         [](const opt::analysis::Type* element_type) {
+                           return IsParameterTypeSupported(*element_type);
+                         });
+    default:
+      return false;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_parameter_with_global.h b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_parameter_with_global.h
new file mode 100644
index 0000000..49e7585
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_parameter_with_global.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_
+#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationReplaceParameterWithGlobal : public Transformation {
+ public:
+  explicit TransformationReplaceParameterWithGlobal(
+      const protobufs::TransformationReplaceParameterWithGlobal& message);
+
+  TransformationReplaceParameterWithGlobal(uint32_t function_type_fresh_id,
+                                           uint32_t parameter_id,
+                                           uint32_t global_variable_fresh_id);
+
+  // - |function_type_fresh_id| is a fresh id.
+  // - |parameter_id| is the result id of the parameter to replace.
+  // - |global_variable_fresh_id| is a fresh id.
+  // - |function_type_fresh_id| is not equal to |global_variable_fresh_id|.
+  // - the function that contains |parameter_id| may not be an entry-point
+  //   function.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // - Removes parameter with result id |parameter_id| from its function
+  // - Adds a global variable to store the value for the parameter
+  // - Add an OpStore instruction before each function call to
+  //   store parameter's value into the variable
+  // - Adds OpLoad at the beginning of the function to load the
+  //   value from the variable into the old parameter's id
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Returns true if the type of the parameter is supported by this
+  // transformation.
+  static bool IsParameterTypeSupported(const opt::analysis::Type& type);
+
+ private:
+  protobufs::TransformationReplaceParameterWithGlobal message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_params_with_struct.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_params_with_struct.cpp
new file mode 100644
index 0000000..a2aaa3a
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_params_with_struct.cpp
@@ -0,0 +1,306 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_params_with_struct.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
+    const protobufs::TransformationReplaceParamsWithStruct& message)
+    : message_(message) {}
+
+TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
+    const std::vector<uint32_t>& parameter_id, uint32_t fresh_function_type_id,
+    uint32_t fresh_parameter_id,
+    const std::unordered_map<uint32_t, uint32_t>&
+        caller_id_to_fresh_composite_id) {
+  message_.set_fresh_function_type_id(fresh_function_type_id);
+  message_.set_fresh_parameter_id(fresh_parameter_id);
+
+  for (auto id : parameter_id) {
+    message_.add_parameter_id(id);
+  }
+
+  message_.mutable_caller_id_to_fresh_composite_id()->insert(
+      caller_id_to_fresh_composite_id.begin(),
+      caller_id_to_fresh_composite_id.end());
+}
+
+bool TransformationReplaceParamsWithStruct::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  std::vector<uint32_t> parameter_id(message_.parameter_id().begin(),
+                                     message_.parameter_id().end());
+
+  // Check that |parameter_id| is neither empty nor it has duplicates.
+  if (parameter_id.empty() || fuzzerutil::HasDuplicates(parameter_id)) {
+    return false;
+  }
+
+  // All ids must correspond to valid parameters of the same function.
+  // The function can't be an entry-point function.
+
+  // fuzzerutil::GetFunctionFromParameterId requires a valid id.
+  if (!ir_context->get_def_use_mgr()->GetDef(parameter_id[0])) {
+    return false;
+  }
+
+  const auto* function =
+      fuzzerutil::GetFunctionFromParameterId(ir_context, parameter_id[0]);
+  if (!function ||
+      fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
+    return false;
+  }
+
+  // Compute all ids of the function's parameters.
+  std::unordered_set<uint32_t> all_parameter_ids;
+  for (const auto* param :
+       fuzzerutil::GetParameters(ir_context, function->result_id())) {
+    all_parameter_ids.insert(param->result_id());
+  }
+
+  // Check that all elements in |parameter_id| are valid.
+  for (auto id : parameter_id) {
+    // fuzzerutil::GetFunctionFromParameterId requires a valid id.
+    if (!ir_context->get_def_use_mgr()->GetDef(id)) {
+      return false;
+    }
+
+    // Check that |id| is a result id of one of the |function|'s parameters.
+    if (!all_parameter_ids.count(id)) {
+      return false;
+    }
+
+    // Check that the parameter with result id |id| has supported type.
+    const auto* type = ir_context->get_type_mgr()->GetType(
+        fuzzerutil::GetTypeId(ir_context, id));
+    assert(type && "Parameter has invalid type");
+    if (!IsParameterTypeSupported(*type)) {
+      return false;
+    }
+  }
+
+  // We already know that the function has at least |parameter_id.size()|
+  // parameters.
+
+  // Check that a relevant OpTypeStruct exists in the module.
+  if (!MaybeGetRequiredStructType(ir_context)) {
+    return false;
+  }
+
+  // Check that |callee_id_to_fresh_composite_id| is valid.
+  for (const auto* inst :
+       fuzzerutil::GetCallers(ir_context, function->result_id())) {
+    // Check that the callee is present in the map. It's ok if the map contains
+    // more ids that there are callees (those ids will not be used).
+    if (!message_.caller_id_to_fresh_composite_id().contains(
+            inst->result_id())) {
+      return false;
+    }
+  }
+
+  // Check that all fresh ids are unique and fresh.
+  std::vector<uint32_t> fresh_ids = {message_.fresh_function_type_id(),
+                                     message_.fresh_parameter_id()};
+
+  for (const auto& entry : message_.caller_id_to_fresh_composite_id()) {
+    fresh_ids.push_back(entry.second);
+  }
+
+  return !fuzzerutil::HasDuplicates(fresh_ids) &&
+         std::all_of(fresh_ids.begin(), fresh_ids.end(),
+                     [ir_context](uint32_t id) {
+                       return fuzzerutil::IsFreshId(ir_context, id);
+                     });
+}
+
+void TransformationReplaceParamsWithStruct::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  auto* function = fuzzerutil::GetFunctionFromParameterId(
+      ir_context, message_.parameter_id(0));
+  assert(function &&
+         "All parameters' ids should've been checked in the IsApplicable");
+
+  // Get a type id of the OpTypeStruct used as a type id of the new parameter.
+  auto struct_type_id = MaybeGetRequiredStructType(ir_context);
+  assert(struct_type_id &&
+         "IsApplicable should've guaranteed that this value isn't equal to 0");
+
+  // Add new parameter to the function.
+  function->AddParameter(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpFunctionParameter, struct_type_id,
+      message_.fresh_parameter_id(), opt::Instruction::OperandList()));
+
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_parameter_id());
+
+  // Compute indices of replaced parameters. This will be used to adjust
+  // OpFunctionCall instructions and create OpCompositeConstruct instructions at
+  // every call site.
+  std::vector<uint32_t> indices_of_replaced_params;
+  {
+    // We want to destroy |params| after the loop because it will contain
+    // dangling pointers when we remove parameters from the function.
+    auto params = fuzzerutil::GetParameters(ir_context, function->result_id());
+    for (auto id : message_.parameter_id()) {
+      auto it = std::find_if(params.begin(), params.end(),
+                             [id](const opt::Instruction* param) {
+                               return param->result_id() == id;
+                             });
+      assert(it != params.end() && "Parameter's id is invalid");
+      indices_of_replaced_params.push_back(
+          static_cast<uint32_t>(it - params.begin()));
+    }
+  }
+
+  // Update all function calls.
+  for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
+    // Create a list of operands for the OpCompositeConstruct instruction.
+    opt::Instruction::OperandList composite_components;
+    for (auto index : indices_of_replaced_params) {
+      // +1 since the first in operand to OpFunctionCall is the result id of
+      // the function.
+      composite_components.emplace_back(
+          std::move(inst->GetInOperand(index + 1)));
+    }
+
+    // Remove arguments from the function call. We do it in a separate loop
+    // and in reverse order to make sure we have removed correct operands.
+    for (auto it = indices_of_replaced_params.rbegin();
+         it != indices_of_replaced_params.rend(); ++it) {
+      // +1 since the first in operand to OpFunctionCall is the result id of
+      // the function.
+      inst->RemoveInOperand(*it + 1);
+    }
+
+    // Insert OpCompositeConstruct before the function call.
+    auto fresh_composite_id =
+        message_.caller_id_to_fresh_composite_id().at(inst->result_id());
+    inst->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeConstruct, struct_type_id, fresh_composite_id,
+        std::move(composite_components)));
+
+    // Add a new operand to the OpFunctionCall instruction.
+    inst->AddOperand({SPV_OPERAND_TYPE_ID, {fresh_composite_id}});
+    fuzzerutil::UpdateModuleIdBound(ir_context, fresh_composite_id);
+  }
+
+  // Insert OpCompositeExtract instructions into the entry point block of the
+  // function and remove replaced parameters.
+  for (int i = 0; i < message_.parameter_id_size(); ++i) {
+    const auto* param_inst =
+        ir_context->get_def_use_mgr()->GetDef(message_.parameter_id(i));
+    assert(param_inst && "Parameter id is invalid");
+
+    // Skip all OpVariable instructions.
+    auto iter = function->begin()->begin();
+    while (iter != function->begin()->end() &&
+           !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
+                                                         iter)) {
+      ++iter;
+    }
+
+    assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
+                                                        iter) &&
+           "Can't extract parameter's value from the structure");
+
+    // Insert OpCompositeExtract instructions to unpack parameters' values from
+    // the struct type.
+    iter.InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeExtract, param_inst->type_id(),
+        param_inst->result_id(),
+        opt::Instruction::OperandList{
+            {SPV_OPERAND_TYPE_ID, {message_.fresh_parameter_id()}},
+            {SPV_OPERAND_TYPE_LITERAL_INTEGER, {static_cast<uint32_t>(i)}}}));
+
+    function->RemoveParameter(param_inst->result_id());
+  }
+
+  // Update function's type.
+  {
+    // We use a separate scope here since |old_function_type| might become a
+    // dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
+
+    auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
+    assert(old_function_type && "Function has invalid type");
+
+    // +1 since the first in operand to OpTypeFunction is the result type id
+    // of the function.
+    std::vector<uint32_t> parameter_type_ids;
+    for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
+      if (std::find(indices_of_replaced_params.begin(),
+                    indices_of_replaced_params.end(),
+                    i - 1) == indices_of_replaced_params.end()) {
+        parameter_type_ids.push_back(
+            old_function_type->GetSingleWordInOperand(i));
+      }
+    }
+
+    parameter_type_ids.push_back(struct_type_id);
+
+    fuzzerutil::UpdateFunctionType(
+        ir_context, function->result_id(), message_.fresh_function_type_id(),
+        old_function_type->GetSingleWordInOperand(0), parameter_type_ids);
+  }
+
+  // Make sure our changes are analyzed
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationReplaceParamsWithStruct::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_replace_params_with_struct() = message_;
+  return result;
+}
+
+bool TransformationReplaceParamsWithStruct::IsParameterTypeSupported(
+    const opt::analysis::Type& param_type) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
+  //  Consider adding support for more types of parameters.
+  switch (param_type.kind()) {
+    case opt::analysis::Type::kBool:
+    case opt::analysis::Type::kInteger:
+    case opt::analysis::Type::kFloat:
+    case opt::analysis::Type::kArray:
+    case opt::analysis::Type::kVector:
+    case opt::analysis::Type::kMatrix:
+      return true;
+    case opt::analysis::Type::kStruct:
+      return std::all_of(param_type.AsStruct()->element_types().begin(),
+                         param_type.AsStruct()->element_types().end(),
+                         [](const opt::analysis::Type* type) {
+                           return IsParameterTypeSupported(*type);
+                         });
+    default:
+      return false;
+  }
+}
+
+uint32_t TransformationReplaceParamsWithStruct::MaybeGetRequiredStructType(
+    opt::IRContext* ir_context) const {
+  std::vector<uint32_t> component_type_ids;
+  for (auto id : message_.parameter_id()) {
+    component_type_ids.push_back(fuzzerutil::GetTypeId(ir_context, id));
+  }
+
+  return fuzzerutil::MaybeGetStructType(ir_context, component_type_ids);
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_params_with_struct.h b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_params_with_struct.h
new file mode 100644
index 0000000..0ff7340
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_params_with_struct.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
+#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
+
+#include <unordered_map>
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationReplaceParamsWithStruct : public Transformation {
+ public:
+  explicit TransformationReplaceParamsWithStruct(
+      const protobufs::TransformationReplaceParamsWithStruct& message);
+
+  TransformationReplaceParamsWithStruct(
+      const std::vector<uint32_t>& parameter_id,
+      uint32_t fresh_function_type_id, uint32_t fresh_parameter_id,
+      const std::unordered_map<uint32_t, uint32_t>&
+          caller_id_to_fresh_composite_id);
+
+  // - Each element of |parameter_id| is a valid result id of some
+  //   OpFunctionParameter instruction. All parameter ids must correspond to
+  //   parameters of the same function. That function may not be an entry-point
+  //   function.
+  // - Types of all parameters must be supported by this transformation (see
+  //   IsParameterTypeSupported method).
+  // - |parameter_id| may not be empty or contain duplicates.
+  // - There must exist an OpTypeStruct instruction containing types of all
+  //   replaced parameters. Type of the i'th component of the struct is equal
+  //   to the type of the instruction with result id |parameter_id[i]|.
+  // - |caller_id_to_fresh_composite_id| should contain a key for at least every
+  //   result id of an OpFunctionCall instruction that calls the function.
+  // - |fresh_function_type_id|, |fresh_parameter_id|,
+  //   |caller_id_to_fresh_composite_id| are all fresh and unique ids.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // - Creates a new function parameter with result id |fresh_parameter_id|.
+  //   Parameter's type is OpTypeStruct with each components type equal to the
+  //   type of the replaced parameter.
+  // - OpCompositeConstruct with result id from |fresh_composite_id| is inserted
+  //   before each OpFunctionCall instruction.
+  // - OpCompositeExtract with result id equal to the result id of the replaced
+  //   parameter is created in the function.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Returns true if parameter's type is supported by this transformation.
+  static bool IsParameterTypeSupported(const opt::analysis::Type& param_type);
+
+ private:
+  // Returns a result id of the OpTypeStruct instruction required by this
+  // transformation (see docs on the IsApplicable method to learn more).
+  uint32_t MaybeGetRequiredStructType(opt::IRContext* ir_context) const;
+
+  protobufs::TransformationReplaceParamsWithStruct message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_set_memory_operands_mask.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_set_memory_operands_mask.cpp
index 131a499..f52f170 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_set_memory_operands_mask.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_set_memory_operands_mask.cpp
@@ -101,6 +101,14 @@
   // Either add a new operand, if no mask operand was already present, or
   // replace an existing mask operand.
   if (original_mask_in_operand_index >= instruction->NumInOperands()) {
+    // Add first memory operand if it's missing.
+    if (message_.memory_operands_mask_index() == 1 &&
+        GetInOperandIndexForMask(*instruction, 0) >=
+            instruction->NumInOperands()) {
+      instruction->AddOperand(
+          {SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessMaskNone}});
+    }
+
     instruction->AddOperand(
         {SPV_OPERAND_TYPE_MEMORY_ACCESS, {message_.memory_operands_mask()}});
 
@@ -154,11 +162,26 @@
       break;
   }
   // If we are looking for the input operand index of the first mask, return it.
+  // This will also return a correct value if the operand is missing.
   if (mask_index == 0) {
     return first_mask_in_operand_index;
   }
   assert(mask_index == 1 && "Memory operands mask index must be 0 or 1.");
 
+  // Memory mask operands are optional. Thus, if the second operand exists,
+  // its index will be >= |first_mask_in_operand_index + 1|. We can reason as
+  // follows to separate the cases where the index of the second operand is
+  // equal to |first_mask_in_operand_index + 1|:
+  // - If the first memory operand doesn't exist, its value is equal to None.
+  //   This means that it doesn't have additional operands following it and the
+  //   condition in the if statement below will be satisfied.
+  // - If the first memory operand exists and has no additional memory operands
+  //   following it, the condition in the if statement below will be satisfied
+  //   and we will return the correct value from the function.
+  if (first_mask_in_operand_index + 1 >= instruction.NumInOperands()) {
+    return first_mask_in_operand_index + 1;
+  }
+
   // We are looking for the input operand index of the second mask.  This is a
   // little complicated because, depending on the contents of the first mask,
   // there may be some input operands separating the two masks.
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_swap_conditional_branch_operands.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_conditional_branch_operands.cpp
new file mode 100644
index 0000000..25f48c4
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_conditional_branch_operands.cpp
@@ -0,0 +1,104 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSwapConditionalBranchOperands::
+    TransformationSwapConditionalBranchOperands(
+        const spvtools::fuzz::protobufs::
+            TransformationSwapConditionalBranchOperands& message)
+    : message_(message) {}
+
+TransformationSwapConditionalBranchOperands::
+    TransformationSwapConditionalBranchOperands(
+        const protobufs::InstructionDescriptor& instruction_descriptor,
+        uint32_t fresh_id) {
+  *message_.mutable_instruction_descriptor() = instruction_descriptor;
+  message_.set_fresh_id(fresh_id);
+}
+
+bool TransformationSwapConditionalBranchOperands::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  const auto* inst =
+      FindInstruction(message_.instruction_descriptor(), ir_context);
+  return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()) && inst &&
+         inst->opcode() == SpvOpBranchConditional;
+}
+
+void TransformationSwapConditionalBranchOperands::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  auto* branch_inst =
+      FindInstruction(message_.instruction_descriptor(), ir_context);
+  assert(branch_inst);
+
+  auto* block = ir_context->get_instr_block(branch_inst);
+  assert(block);
+
+  // Compute the last instruction in the |block| that allows us to insert
+  // OpLogicalNot above it.
+  auto iter = fuzzerutil::GetIteratorForInstruction(block, branch_inst);
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter)) {
+    // There might be a merge instruction before OpBranchConditional.
+    --iter;
+  }
+
+  assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) &&
+         "We should now be able to insert SpvOpLogicalNot before |iter|");
+
+  // Get the instruction whose result is used as a condition for
+  // OpBranchConditional.
+  const auto* condition_inst = ir_context->get_def_use_mgr()->GetDef(
+      branch_inst->GetSingleWordInOperand(0));
+  assert(condition_inst);
+
+  // We are swapping the labels in OpBranchConditional. This means that we must
+  // invert the guard as well. We are using OpLogicalNot for that purpose here.
+  iter.InsertBefore(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpLogicalNot, condition_inst->type_id(),
+      message_.fresh_id(),
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}}));
+
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+  // Update OpBranchConditional condition operand.
+  branch_inst->GetInOperand(0).words[0] = message_.fresh_id();
+
+  // Swap label operands.
+  std::swap(branch_inst->GetInOperand(1), branch_inst->GetInOperand(2));
+
+  // Additionally, swap branch weights if present.
+  if (branch_inst->NumInOperands() > 3) {
+    std::swap(branch_inst->GetInOperand(3), branch_inst->GetInOperand(4));
+  }
+
+  // Make sure the changes are analyzed.
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation
+TransformationSwapConditionalBranchOperands::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_swap_conditional_branch_operands() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_swap_conditional_branch_operands.h b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_conditional_branch_operands.h
new file mode 100644
index 0000000..dd68ff8
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_conditional_branch_operands.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationSwapConditionalBranchOperands : public Transformation {
+ public:
+  explicit TransformationSwapConditionalBranchOperands(
+      const protobufs::TransformationSwapConditionalBranchOperands& message);
+
+  TransformationSwapConditionalBranchOperands(
+      const protobufs::InstructionDescriptor& instruction_descriptor,
+      uint32_t fresh_id);
+
+  // - |message_.instruction_descriptor| must be a valid descriptor of some
+  //   OpBranchConditional instruction in the module.
+  // - |message_.fresh_id| must be a fresh id.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Inserts |%fresh_id = OpLogicalNot %bool_type_id %cond_id| before
+  // |OpBranchConditional %cond_id %branch_a %branch_b [%weight_a %weight_b]|.
+  // Replaces %cond_id with %fresh_id and swaps %branch_* and %weight_*
+  // operands.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationSwapConditionalBranchOperands message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.cpp
index ee64292..b3bd593 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.cpp
@@ -39,7 +39,8 @@
 }
 
 bool TransformationVectorShuffle::IsApplicable(
-    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   // The fresh id must not already be in use.
   if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
@@ -56,12 +57,26 @@
   if (!vector1_instruction || !vector1_instruction->type_id()) {
     return false;
   }
+  // We should be able to create a synonym of |vector1| if it's not irrelevant.
+  if (!transformation_context.GetFactManager()->IdIsIrrelevant(
+          message_.vector1()) &&
+      !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+                                    vector1_instruction)) {
+    return false;
+  }
   // The second vector must be an instruction with a type id
   auto vector2_instruction =
       ir_context->get_def_use_mgr()->GetDef(message_.vector2());
   if (!vector2_instruction || !vector2_instruction->type_id()) {
     return false;
   }
+  // We should be able to create a synonym of |vector2| if it's not irrelevant.
+  if (!transformation_context.GetFactManager()->IdIsIrrelevant(
+          message_.vector2()) &&
+      !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+                                    vector2_instruction)) {
+    return false;
+  }
   auto vector1_type =
       ir_context->get_type_mgr()->GetType(vector1_instruction->type_id());
   // The first vector instruction's type must actually be a vector type.
@@ -161,9 +176,21 @@
     // |component| refers.
     if (component <
         GetVectorType(ir_context, message_.vector1())->element_count()) {
+      // Irrelevant id cannot participate in DataSynonym facts.
+      if (transformation_context->GetFactManager()->IdIsIrrelevant(
+              message_.vector1())) {
+        continue;
+      }
+
       descriptor_for_source_component =
           MakeDataDescriptor(message_.vector1(), {component});
     } else {
+      // Irrelevant id cannot participate in DataSynonym facts.
+      if (transformation_context->GetFactManager()->IdIsIrrelevant(
+              message_.vector2())) {
+        continue;
+      }
+
       auto index_into_vector_2 =
           component -
           GetVectorType(ir_context, message_.vector1())->element_count();
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.h b/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.h
index f73fc31..f911976 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.h
@@ -19,7 +19,6 @@
 #include "source/fuzz/transformation.h"
 #include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
-
 #include "source/opt/types.h"
 
 namespace spvtools {
@@ -59,7 +58,9 @@
   // from which it came (with undefined components being ignored).  If the
   // result vector is a contiguous sub-range of one of the input vectors, a
   // fact is added to record that |message_.fresh_id| is synonymous with this
-  // sub-range.
+  // sub-range. DataSynonym facts are added only for non-irrelevant vectors
+  // (e.g. if |vector1| is irrelevant but |vector2| is not, synonyms will be
+  // created for |vector1| but not |vector2|).
   void Apply(opt::IRContext* ir_context,
              TransformationContext* transformation_context) const override;
 
diff --git a/third_party/SPIRV-Tools/source/name_mapper.cpp b/third_party/SPIRV-Tools/source/name_mapper.cpp
index 43fdfb3..5525bbc 100644
--- a/third_party/SPIRV-Tools/source/name_mapper.cpp
+++ b/third_party/SPIRV-Tools/source/name_mapper.cpp
@@ -153,6 +153,7 @@
     CASE(SubgroupLocalInvocationId)
     GLCASE(VertexIndex)
     GLCASE(InstanceIndex)
+    GLCASE(BaseInstance)
     CASE(SubgroupEqMaskKHR)
     CASE(SubgroupGeMaskKHR)
     CASE(SubgroupGtMaskKHR)
diff --git a/third_party/SPIRV-Tools/source/opcode.cpp b/third_party/SPIRV-Tools/source/opcode.cpp
index 80fe3b3..f93cfd3 100644
--- a/third_party/SPIRV-Tools/source/opcode.cpp
+++ b/third_party/SPIRV-Tools/source/opcode.cpp
@@ -413,6 +413,7 @@
     case SpvOpAtomicIIncrement:
     case SpvOpAtomicIDecrement:
     case SpvOpAtomicIAdd:
+    case SpvOpAtomicFAddEXT:
     case SpvOpAtomicISub:
     case SpvOpAtomicSMin:
     case SpvOpAtomicUMin:
@@ -445,7 +446,7 @@
 
 bool spvOpcodeIsReturnOrAbort(SpvOp opcode) {
   return spvOpcodeIsReturn(opcode) || opcode == SpvOpKill ||
-         opcode == SpvOpUnreachable;
+         opcode == SpvOpUnreachable || opcode == SpvOpTerminateInvocation;
 }
 
 bool spvOpcodeIsBlockTerminator(SpvOp opcode) {
@@ -650,6 +651,42 @@
   }
 }
 
+bool spvOpcodeIsLinearAlgebra(SpvOp opcode) {
+  switch (opcode) {
+    case SpvOpTranspose:
+    case SpvOpVectorTimesScalar:
+    case SpvOpMatrixTimesScalar:
+    case SpvOpVectorTimesMatrix:
+    case SpvOpMatrixTimesVector:
+    case SpvOpMatrixTimesMatrix:
+    case SpvOpOuterProduct:
+    case SpvOpDot:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool spvOpcodeIsImageSample(const SpvOp opcode) {
+  switch (opcode) {
+    case SpvOpImageSampleImplicitLod:
+    case SpvOpImageSampleExplicitLod:
+    case SpvOpImageSampleDrefImplicitLod:
+    case SpvOpImageSampleDrefExplicitLod:
+    case SpvOpImageSampleProjImplicitLod:
+    case SpvOpImageSampleProjExplicitLod:
+    case SpvOpImageSampleProjDrefImplicitLod:
+    case SpvOpImageSampleProjDrefExplicitLod:
+    case SpvOpImageSparseSampleImplicitLod:
+    case SpvOpImageSparseSampleExplicitLod:
+    case SpvOpImageSparseSampleDrefImplicitLod:
+    case SpvOpImageSparseSampleDrefExplicitLod:
+      return true;
+    default:
+      return false;
+  }
+}
+
 std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) {
   switch (opcode) {
     case SpvOpMemoryBarrier:
@@ -664,6 +701,7 @@
     case SpvOpAtomicIIncrement:
     case SpvOpAtomicIDecrement:
     case SpvOpAtomicIAdd:
+    case SpvOpAtomicFAddEXT:
     case SpvOpAtomicISub:
     case SpvOpAtomicSMin:
     case SpvOpAtomicUMin:
diff --git a/third_party/SPIRV-Tools/source/opcode.h b/third_party/SPIRV-Tools/source/opcode.h
index b4f0271..9aeba76 100644
--- a/third_party/SPIRV-Tools/source/opcode.h
+++ b/third_party/SPIRV-Tools/source/opcode.h
@@ -134,6 +134,12 @@
 // where the order of the operands is irrelevant.
 bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode);
 
+// Returns true for opcodes that represents linear algebra instructions.
+bool spvOpcodeIsLinearAlgebra(SpvOp opcode);
+
+// Returns true for opcodes that represents an image sample instruction.
+bool spvOpcodeIsImageSample(SpvOp opcode);
+
 // Returns a vector containing the indices of the memory semantics <id>
 // operands for |opcode|.
 std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode);
diff --git a/third_party/SPIRV-Tools/source/operand.cpp b/third_party/SPIRV-Tools/source/operand.cpp
index 755ad6a..0910595 100644
--- a/third_party/SPIRV-Tools/source/operand.cpp
+++ b/third_party/SPIRV-Tools/source/operand.cpp
@@ -455,7 +455,7 @@
     return false;
   }
   switch (type) {
-    // Blacklist non-input IDs.
+    // Deny non-input IDs.
     case SPV_OPERAND_TYPE_TYPE_ID:
     case SPV_OPERAND_TYPE_RESULT_ID:
       return false;
diff --git a/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp b/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp
index db2b67b..b755787 100644
--- a/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -38,6 +38,8 @@
 const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
 const uint32_t kCopyMemoryTargetAddrInIdx = 0;
 const uint32_t kCopyMemorySourceAddrInIdx = 1;
+const uint32_t kDebugDeclareOperandVariableIndex = 5;
+const uint32_t kGlobalVariableVariableIndex = 12;
 
 // Sorting functor to present annotation instructions in an easy-to-process
 // order. The functor orders by opcode first and falls back on unique id
@@ -105,13 +107,17 @@
          IsVarOfStorage(varId, SpvStorageClassWorkgroup);
 }
 
-void AggressiveDCEPass::AddStores(uint32_t ptrId) {
-  get_def_use_mgr()->ForEachUser(ptrId, [this, ptrId](Instruction* user) {
+void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) {
+  get_def_use_mgr()->ForEachUser(ptrId, [this, ptrId, func](Instruction* user) {
+    // If the user is not a part of |func|, skip it.
+    BasicBlock* blk = context()->get_instr_block(user);
+    if (blk && blk->GetParent() != func) return;
+
     switch (user->opcode()) {
       case SpvOpAccessChain:
       case SpvOpInBoundsAccessChain:
       case SpvOpCopyObject:
-        this->AddStores(user->result_id());
+        this->AddStores(func, user->result_id());
         break;
       case SpvOpLoad:
         break;
@@ -131,11 +137,11 @@
 }
 
 bool AggressiveDCEPass::AllExtensionsSupported() const {
-  // If any extension not in whitelist, return false
+  // If any extension not in allowlist, return false
   for (auto& ei : get_module()->extensions()) {
     const char* extName =
         reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
-    if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
+    if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
       return false;
   }
   return true;
@@ -169,13 +175,13 @@
   return IsDead(tInst);
 }
 
-void AggressiveDCEPass::ProcessLoad(uint32_t varId) {
+void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) {
   // Only process locals
   if (!IsLocalVar(varId)) return;
   // Return if already processed
   if (live_local_vars_.find(varId) != live_local_vars_.end()) return;
   // Mark all stores to varId as live
-  AddStores(varId);
+  AddStores(func, varId);
   // Cache varId as processed
   live_local_vars_.insert(varId);
 }
@@ -332,6 +338,7 @@
   call_in_func_ = false;
   func_is_entry_point_ = false;
   private_stores_.clear();
+  live_local_vars_.clear();
   // Stacks to keep track of when we are inside an if- or loop-construct.
   // When immediately inside an if- or loop-construct, we do not initially
   // mark branches live. All other branches must be marked live.
@@ -454,7 +461,7 @@
       uint32_t varId;
       (void)GetPtr(liveInst, &varId);
       if (varId != 0) {
-        ProcessLoad(varId);
+        ProcessLoad(func, varId);
       }
       // Process memory copies like loads
     } else if (liveInst->opcode() == SpvOpCopyMemory ||
@@ -463,31 +470,44 @@
       (void)GetPtr(liveInst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx),
                    &varId);
       if (varId != 0) {
-        ProcessLoad(varId);
+        ProcessLoad(func, varId);
       }
+      // If DebugDeclare, process as load of variable
+    } else if (liveInst->GetOpenCL100DebugOpcode() ==
+               OpenCLDebugInfo100DebugDeclare) {
+      uint32_t varId =
+          liveInst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+      ProcessLoad(func, varId);
+      // If DebugValue with Deref, process as load of variable
+    } else if (liveInst->GetOpenCL100DebugOpcode() ==
+               OpenCLDebugInfo100DebugValue) {
+      uint32_t varId = context()
+                           ->get_debug_info_mgr()
+                           ->GetVariableIdOfDebugValueUsedForDeclare(liveInst);
+      if (varId != 0) ProcessLoad(func, varId);
       // If merge, add other branches that are part of its control structure
     } else if (liveInst->opcode() == SpvOpLoopMerge ||
                liveInst->opcode() == SpvOpSelectionMerge) {
       AddBreaksAndContinuesToWorklist(liveInst);
       // If function call, treat as if it loads from all pointer arguments
     } else if (liveInst->opcode() == SpvOpFunctionCall) {
-      liveInst->ForEachInId([this](const uint32_t* iid) {
+      liveInst->ForEachInId([this, func](const uint32_t* iid) {
         // Skip non-ptr args
         if (!IsPtr(*iid)) return;
         uint32_t varId;
         (void)GetPtr(*iid, &varId);
-        ProcessLoad(varId);
+        ProcessLoad(func, varId);
       });
       // If function parameter, treat as if it's result id is loaded from
     } else if (liveInst->opcode() == SpvOpFunctionParameter) {
-      ProcessLoad(liveInst->result_id());
+      ProcessLoad(func, liveInst->result_id());
       // We treat an OpImageTexelPointer as a load of the pointer, and
       // that value is manipulated to get the result.
     } else if (liveInst->opcode() == SpvOpImageTexelPointer) {
       uint32_t varId;
       (void)GetPtr(liveInst, &varId);
       if (varId != 0) {
-        ProcessLoad(varId);
+        ProcessLoad(func, varId);
       }
     }
 
@@ -616,6 +636,18 @@
       }
     }
   }
+
+  // For each DebugInfo GlobalVariable keep all operands except the Variable.
+  // Later, if the variable is dead, we will set the operand to DebugInfoNone.
+  for (auto& dbg : get_module()->ext_inst_debuginfo()) {
+    if (dbg.GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugGlobalVariable)
+      continue;
+    dbg.ForEachInId([this](const uint32_t* iid) {
+      Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
+      if (inInst->opcode() == SpvOpVariable) return;
+      AddToWorklist(inInst);
+    });
+  }
 }
 
 Pass::Status AggressiveDCEPass::ProcessImpl() {
@@ -835,6 +867,26 @@
     }
   }
 
+  for (auto& dbg : get_module()->ext_inst_debuginfo()) {
+    if (!IsDead(&dbg)) continue;
+    // Save GlobalVariable if its variable is live, otherwise null out variable
+    // index
+    if (dbg.GetOpenCL100DebugOpcode() ==
+        OpenCLDebugInfo100DebugGlobalVariable) {
+      auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex);
+      Instruction* var_inst = get_def_use_mgr()->GetDef(var_id);
+      if (!IsDead(var_inst)) continue;
+      context()->ForgetUses(&dbg);
+      dbg.SetOperand(
+          kGlobalVariableVariableIndex,
+          {context()->get_debug_info_mgr()->GetDebugInfoNone()->result_id()});
+      context()->AnalyzeUses(&dbg);
+      continue;
+    }
+    to_kill_.push_back(&dbg);
+    modified = true;
+  }
+
   // Since ADCE is disabled for non-shaders, we don't check for export linkage
   // attributes here.
   for (auto& val : get_module()->types_values()) {
@@ -882,14 +934,14 @@
 AggressiveDCEPass::AggressiveDCEPass() = default;
 
 Pass::Status AggressiveDCEPass::Process() {
-  // Initialize extensions whitelist
+  // Initialize extensions allowlist
   InitExtensions();
   return ProcessImpl();
 }
 
 void AggressiveDCEPass::InitExtensions() {
-  extensions_whitelist_.clear();
-  extensions_whitelist_.insert({
+  extensions_allowlist_.clear();
+  extensions_allowlist_.insert({
       "SPV_AMD_shader_explicit_vertex_parameter",
       "SPV_AMD_shader_trinary_minmax",
       "SPV_AMD_gcn_shader",
@@ -934,6 +986,7 @@
       "SPV_KHR_ray_tracing",
       "SPV_EXT_fragment_invocation_density",
       "SPV_EXT_physical_storage_buffer",
+      "SPV_KHR_terminate_invocation",
   });
 }
 
diff --git a/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.h b/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.h
index c043a96..2ce5b57 100644
--- a/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.h
@@ -85,9 +85,9 @@
 
   // Add all store instruction which use |ptrId|, directly or indirectly,
   // to the live instruction worklist.
-  void AddStores(uint32_t ptrId);
+  void AddStores(Function* func, uint32_t ptrId);
 
-  // Initialize extensions whitelist
+  // Initialize extensions allowlist
   void InitExtensions();
 
   // Return true if all extensions in this module are supported by this pass.
@@ -99,7 +99,7 @@
   bool IsTargetDead(Instruction* inst);
 
   // If |varId| is local, mark all stores of varId as live.
-  void ProcessLoad(uint32_t varId);
+  void ProcessLoad(Function* func, uint32_t varId);
 
   // If |bp| is structured header block, returns true and sets |mergeInst| to
   // the merge instruction, |branchInst| to the branch and |mergeBlockId| to the
@@ -191,7 +191,7 @@
   std::vector<Instruction*> to_kill_;
 
   // Extensions supported by this pass.
-  std::unordered_set<std::string> extensions_whitelist_;
+  std::unordered_set<std::string> extensions_allowlist_;
 };
 
 }  // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/code_sink.cpp b/third_party/SPIRV-Tools/source/opt/code_sink.cpp
index 4c88cd4..e49029f 100644
--- a/third_party/SPIRV-Tools/source/opt/code_sink.cpp
+++ b/third_party/SPIRV-Tools/source/opt/code_sink.cpp
@@ -214,6 +214,7 @@
       case SpvOpAtomicIIncrement:
       case SpvOpAtomicIDecrement:
       case SpvOpAtomicIAdd:
+      case SpvOpAtomicFAddEXT:
       case SpvOpAtomicISub:
       case SpvOpAtomicSMin:
       case SpvOpAtomicUMin:
diff --git a/third_party/SPIRV-Tools/source/opt/copy_prop_arrays.cpp b/third_party/SPIRV-Tools/source/opt/copy_prop_arrays.cpp
index b3b90da..67a97b6 100644
--- a/third_party/SPIRV-Tools/source/opt/copy_prop_arrays.cpp
+++ b/third_party/SPIRV-Tools/source/opt/copy_prop_arrays.cpp
@@ -29,6 +29,12 @@
 const uint32_t kTypePointerStorageClassInIdx = 0;
 const uint32_t kTypePointerPointeeInIdx = 1;
 
+bool IsOpenCL100DebugDeclareOrValue(Instruction* di) {
+  auto dbg_opcode = di->GetOpenCL100DebugOpcode();
+  return dbg_opcode == OpenCLDebugInfo100DebugDeclare ||
+         dbg_opcode == OpenCLDebugInfo100DebugValue;
+}
+
 }  // namespace
 
 Pass::Status CopyPropagateArrays::Process() {
@@ -188,6 +194,8 @@
           return ptr_inst->opcode() == SpvOpVariable &&
                  store_inst->GetSingleWordInOperand(kStorePointerInOperand) ==
                      ptr_inst->result_id();
+        } else if (IsOpenCL100DebugDeclareOrValue(use)) {
+          return true;
         }
         // Some other instruction.  Be conservative.
         return false;
@@ -492,6 +500,8 @@
                                                        const_mgr,
                                                        type](Instruction* use,
                                                              uint32_t) {
+    if (IsOpenCL100DebugDeclareOrValue(use)) return true;
+
     switch (use->opcode()) {
       case SpvOpLoad: {
         analysis::Pointer* pointer_type = type->AsPointer();
@@ -565,6 +575,7 @@
     }
   });
 }
+
 void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst,
                                      Instruction* new_ptr_inst) {
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
@@ -580,6 +591,52 @@
   for (auto pair : uses) {
     Instruction* use = pair.first;
     uint32_t index = pair.second;
+
+    if (use->IsOpenCL100DebugInstr()) {
+      switch (use->GetOpenCL100DebugOpcode()) {
+        case OpenCLDebugInfo100DebugDeclare: {
+          if (new_ptr_inst->opcode() == SpvOpVariable ||
+              new_ptr_inst->opcode() == SpvOpFunctionParameter) {
+            context()->ForgetUses(use);
+            use->SetOperand(index, {new_ptr_inst->result_id()});
+            context()->AnalyzeUses(use);
+          } else {
+            // Based on the spec, we cannot use a pointer other than OpVariable
+            // or OpFunctionParameter for DebugDeclare. We have to use
+            // DebugValue with Deref.
+
+            context()->ForgetUses(use);
+
+            // Change DebugDeclare to DebugValue.
+            use->SetOperand(
+                index - 2,
+                {static_cast<uint32_t>(OpenCLDebugInfo100DebugValue)});
+            use->SetOperand(index, {new_ptr_inst->result_id()});
+
+            // Add Deref operation.
+            Instruction* dbg_expr =
+                def_use_mgr->GetDef(use->GetSingleWordOperand(index + 1));
+            auto* deref_expr_instr =
+                context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr);
+            use->SetOperand(index + 1, {deref_expr_instr->result_id()});
+
+            context()->AnalyzeUses(deref_expr_instr);
+            context()->AnalyzeUses(use);
+          }
+          break;
+        }
+        case OpenCLDebugInfo100DebugValue:
+          context()->ForgetUses(use);
+          use->SetOperand(index, {new_ptr_inst->result_id()});
+          context()->AnalyzeUses(use);
+          break;
+        default:
+          assert(false && "Don't know how to rewrite instruction");
+          break;
+      }
+      continue;
+    }
+
     switch (use->opcode()) {
       case SpvOpLoad: {
         // Replace the actual use.
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 16d9fd5..0054f57 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
@@ -41,6 +41,7 @@
   bool condIsConst;
   Instruction* cInst = get_def_use_mgr()->GetDef(condId);
   switch (cInst->opcode()) {
+    case SpvOpConstantNull:
     case SpvOpConstantFalse: {
       *condVal = false;
       condIsConst = true;
diff --git a/third_party/SPIRV-Tools/source/opt/debug_info_manager.cpp b/third_party/SPIRV-Tools/source/opt/debug_info_manager.cpp
index 9d98584..6c33764 100644
--- a/third_party/SPIRV-Tools/source/opt/debug_info_manager.cpp
+++ b/third_party/SPIRV-Tools/source/opt/debug_info_manager.cpp
@@ -24,7 +24,21 @@
 static const uint32_t kLineOperandIndexDebugFunction = 7;
 static const uint32_t kLineOperandIndexDebugLexicalBlock = 5;
 static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
+static const uint32_t kDebugFunctionOperandParentIndex = 9;
+static const uint32_t kDebugTypeCompositeOperandParentIndex = 9;
+static const uint32_t kDebugLexicalBlockOperandParentIndex = 7;
 static const uint32_t kDebugInlinedAtOperandInlinedIndex = 6;
+static const uint32_t kDebugExpressOperandOperationIndex = 4;
+static const uint32_t kDebugDeclareOperandLocalVariableIndex = 4;
+static const uint32_t kDebugDeclareOperandVariableIndex = 5;
+static const uint32_t kDebugValueOperandLocalVariableIndex = 4;
+static const uint32_t kDebugValueOperandExpressionIndex = 6;
+static const uint32_t kDebugOperationOperandOperationIndex = 4;
+static const uint32_t kOpVariableOperandStorageClassIndex = 2;
+static const uint32_t kDebugLocalVariableOperandParentIndex = 9;
+static const uint32_t kExtInstInstructionInIdx = 1;
+static const uint32_t kDebugGlobalVariableOperandFlagsIndex = 12;
+static const uint32_t kDebugLocalVariableOperandFlagsIndex = 10;
 
 namespace spvtools {
 namespace opt {
@@ -36,7 +50,8 @@
   assert(dbg_inlined_at->GetOpenCL100DebugOpcode() ==
          OpenCLDebugInfo100DebugInlinedAt);
   if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) {
-    dbg_inlined_at->AddOperand({SPV_OPERAND_TYPE_RESULT_ID, {inlined_operand}});
+    dbg_inlined_at->AddOperand(
+        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inlined_operand}});
   } else {
     dbg_inlined_at->SetOperand(kDebugInlinedAtOperandInlinedIndex,
                                {inlined_operand});
@@ -53,6 +68,12 @@
       kDebugInlinedAtOperandInlinedIndex);
 }
 
+bool IsEmptyDebugExpression(Instruction* instr) {
+  return instr->GetOpenCL100DebugOpcode() ==
+             OpenCLDebugInfo100DebugExpression &&
+         instr->NumOperands() == kDebugExpressOperandOperationIndex;
+}
+
 }  // namespace
 
 DebugInfoManager::DebugInfoManager(IRContext* c) : context_(c) {
@@ -83,6 +104,20 @@
   fn_id_to_dbg_fn_[fn_id] = inst;
 }
 
+void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id,
+                                          Instruction* dbg_declare) {
+  assert(dbg_declare->GetOpenCL100DebugOpcode() ==
+             OpenCLDebugInfo100DebugDeclare ||
+         dbg_declare->GetOpenCL100DebugOpcode() ==
+             OpenCLDebugInfo100DebugValue);
+  auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_id);
+  if (dbg_decl_itr == var_id_to_dbg_decl_.end()) {
+    var_id_to_dbg_decl_[var_id] = {dbg_declare};
+  } else {
+    dbg_decl_itr->second.insert(dbg_declare);
+  }
+}
+
 uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
                                                 const DebugScope& scope) {
   if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() ==
@@ -139,10 +174,12 @@
   // |scope| already has DebugInlinedAt. We put the existing DebugInlinedAt
   // into the Inlined operand of this new DebugInlinedAt.
   if (scope.GetInlinedAt() != kNoInlinedAt) {
-    inlined_at->AddOperand({spv_operand_type_t::SPV_OPERAND_TYPE_RESULT_ID,
-                            {scope.GetInlinedAt()}});
+    inlined_at->AddOperand(
+        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetInlinedAt()}});
   }
   RegisterDbgInst(inlined_at.get());
+  if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+    context()->get_def_use_mgr()->AnalyzeInstDefUse(inlined_at.get());
   context()->module()->AddExtInstDebugInfo(std::move(inlined_at));
   return result_id;
 }
@@ -210,6 +247,51 @@
   return chain_head_id;
 }
 
+Instruction* DebugInfoManager::GetDebugOperationWithDeref() {
+  if (deref_operation_ != nullptr) return deref_operation_;
+
+  uint32_t result_id = context()->TakeNextId();
+  std::unique_ptr<Instruction> deref_operation(new Instruction(
+      context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+      result_id,
+      {
+          {SPV_OPERAND_TYPE_ID,
+           {context()
+                ->get_feature_mgr()
+                ->GetExtInstImportId_OpenCL100DebugInfo()}},
+          {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+           {static_cast<uint32_t>(OpenCLDebugInfo100DebugOperation)}},
+          {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION,
+           {static_cast<uint32_t>(OpenCLDebugInfo100Deref)}},
+      }));
+
+  // Add to the front of |ext_inst_debuginfo_|.
+  deref_operation_ =
+      context()->module()->ext_inst_debuginfo_begin()->InsertBefore(
+          std::move(deref_operation));
+
+  RegisterDbgInst(deref_operation_);
+  if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+    context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_operation_);
+  return deref_operation_;
+}
+
+Instruction* DebugInfoManager::DerefDebugExpression(Instruction* dbg_expr) {
+  assert(dbg_expr->GetOpenCL100DebugOpcode() ==
+         OpenCLDebugInfo100DebugExpression);
+  std::unique_ptr<Instruction> deref_expr(dbg_expr->Clone(context()));
+  deref_expr->SetResultId(context()->TakeNextId());
+  deref_expr->InsertOperand(
+      kDebugExpressOperandOperationIndex,
+      {SPV_OPERAND_TYPE_ID, {GetDebugOperationWithDeref()->result_id()}});
+  auto* deref_expr_instr =
+      context()->ext_inst_debuginfo_end()->InsertBefore(std::move(deref_expr));
+  AnalyzeDebugInst(deref_expr_instr);
+  if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+    context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_expr_instr);
+  return deref_expr_instr;
+}
+
 Instruction* DebugInfoManager::GetDebugInfoNone() {
   if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_;
 
@@ -218,11 +300,11 @@
       context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
       result_id,
       {
-          {SPV_OPERAND_TYPE_RESULT_ID,
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
            {context()
                 ->get_feature_mgr()
                 ->GetExtInstImportId_OpenCL100DebugInfo()}},
-          {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+          {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
            {static_cast<uint32_t>(OpenCLDebugInfo100DebugInfoNone)}},
       }));
 
@@ -232,9 +314,38 @@
           std::move(dbg_info_none_inst));
 
   RegisterDbgInst(debug_info_none_inst_);
+  if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+    context()->get_def_use_mgr()->AnalyzeInstDefUse(debug_info_none_inst_);
   return debug_info_none_inst_;
 }
 
+Instruction* DebugInfoManager::GetEmptyDebugExpression() {
+  if (empty_debug_expr_inst_ != nullptr) return empty_debug_expr_inst_;
+
+  uint32_t result_id = context()->TakeNextId();
+  std::unique_ptr<Instruction> empty_debug_expr(new Instruction(
+      context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+      result_id,
+      {
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+           {context()
+                ->get_feature_mgr()
+                ->GetExtInstImportId_OpenCL100DebugInfo()}},
+          {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+           {static_cast<uint32_t>(OpenCLDebugInfo100DebugExpression)}},
+      }));
+
+  // Add to the front of |ext_inst_debuginfo_|.
+  empty_debug_expr_inst_ =
+      context()->module()->ext_inst_debuginfo_begin()->InsertBefore(
+          std::move(empty_debug_expr));
+
+  RegisterDbgInst(empty_debug_expr_inst_);
+  if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+    context()->get_def_use_mgr()->AnalyzeInstDefUse(empty_debug_expr_inst_);
+  return empty_debug_expr_inst_;
+}
+
 Instruction* DebugInfoManager::GetDebugInlinedAt(uint32_t dbg_inlined_at_id) {
   auto* inlined_at = GetDbgInst(dbg_inlined_at_id);
   if (inlined_at == nullptr) return nullptr;
@@ -252,15 +363,189 @@
   std::unique_ptr<Instruction> new_inlined_at(inlined_at->Clone(context()));
   new_inlined_at->SetResultId(context()->TakeNextId());
   RegisterDbgInst(new_inlined_at.get());
+  if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+    context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inlined_at.get());
   if (insert_before != nullptr)
     return insert_before->InsertBefore(std::move(new_inlined_at));
   return context()->module()->ext_inst_debuginfo_end()->InsertBefore(
       std::move(new_inlined_at));
 }
 
+bool DebugInfoManager::IsDebugDeclared(uint32_t variable_id) {
+  auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
+  return dbg_decl_itr != var_id_to_dbg_decl_.end();
+}
+
+void DebugInfoManager::KillDebugDeclares(uint32_t variable_id) {
+  auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
+  if (dbg_decl_itr != var_id_to_dbg_decl_.end()) {
+    // We intentionally copy the list of DebugDeclare instructions because
+    // context()->KillInst(dbg_decl) will update |var_id_to_dbg_decl_|. If we
+    // directly use |dbg_decl_itr->second|, it accesses a dangling pointer.
+    auto copy_dbg_decls = dbg_decl_itr->second;
+
+    for (auto* dbg_decl : copy_dbg_decls) {
+      context()->KillInst(dbg_decl);
+    }
+    var_id_to_dbg_decl_.erase(dbg_decl_itr);
+  }
+}
+
+uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) {
+  auto dbg_scope_itr = id_to_dbg_inst_.find(child_scope);
+  assert(dbg_scope_itr != id_to_dbg_inst_.end());
+  OpenCLDebugInfo100Instructions debug_opcode =
+      dbg_scope_itr->second->GetOpenCL100DebugOpcode();
+  uint32_t parent_scope = kNoDebugScope;
+  switch (debug_opcode) {
+    case OpenCLDebugInfo100DebugFunction:
+      parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
+          kDebugFunctionOperandParentIndex);
+      break;
+    case OpenCLDebugInfo100DebugLexicalBlock:
+      parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
+          kDebugLexicalBlockOperandParentIndex);
+      break;
+    case OpenCLDebugInfo100DebugTypeComposite:
+      parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
+          kDebugTypeCompositeOperandParentIndex);
+      break;
+    case OpenCLDebugInfo100DebugCompilationUnit:
+      // DebugCompilationUnit does not have a parent scope.
+      break;
+    default:
+      assert(false &&
+             "Unreachable. A debug scope instruction must be "
+             "DebugFunction, DebugTypeComposite, DebugLexicalBlock, "
+             "or DebugCompilationUnit.");
+      break;
+  }
+  return parent_scope;
+}
+
+bool DebugInfoManager::IsAncestorOfScope(uint32_t scope, uint32_t ancestor) {
+  uint32_t ancestor_scope_itr = scope;
+  while (ancestor_scope_itr != kNoDebugScope) {
+    if (ancestor == ancestor_scope_itr) return true;
+    ancestor_scope_itr = GetParentScope(ancestor_scope_itr);
+  }
+  return false;
+}
+
+bool DebugInfoManager::IsDeclareVisibleToInstr(Instruction* dbg_declare,
+                                               uint32_t instr_scope_id) {
+  if (instr_scope_id == kNoDebugScope) return false;
+
+  uint32_t dbg_local_var_id =
+      dbg_declare->GetSingleWordOperand(kDebugDeclareOperandLocalVariableIndex);
+  auto dbg_local_var_itr = id_to_dbg_inst_.find(dbg_local_var_id);
+  assert(dbg_local_var_itr != id_to_dbg_inst_.end());
+  uint32_t decl_scope_id = dbg_local_var_itr->second->GetSingleWordOperand(
+      kDebugLocalVariableOperandParentIndex);
+
+  // If the scope of DebugDeclare is an ancestor scope of the instruction's
+  // scope, the local variable is visible to the instruction.
+  return IsAncestorOfScope(instr_scope_id, decl_scope_id);
+}
+
+void DebugInfoManager::AddDebugValue(Instruction* scope_and_line,
+                                     uint32_t variable_id, uint32_t value_id,
+                                     Instruction* insert_pos) {
+  auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
+  if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return;
+
+  uint32_t instr_scope_id = scope_and_line->GetDebugScope().GetLexicalScope();
+  for (auto* dbg_decl_or_val : dbg_decl_itr->second) {
+    if (!IsDeclareVisibleToInstr(dbg_decl_or_val, instr_scope_id)) continue;
+
+    uint32_t result_id = context()->TakeNextId();
+    std::unique_ptr<Instruction> new_dbg_value(new Instruction(
+        context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+        result_id,
+        {
+            {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+             {context()
+                  ->get_feature_mgr()
+                  ->GetExtInstImportId_OpenCL100DebugInfo()}},
+            {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+             {static_cast<uint32_t>(OpenCLDebugInfo100DebugValue)}},
+            {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+             {dbg_decl_or_val->GetSingleWordOperand(
+                 kDebugValueOperandLocalVariableIndex)}},
+            {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {value_id}},
+            {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+             {GetEmptyDebugExpression()->result_id()}},
+        }));
+
+    if (dbg_decl_or_val->NumOperands() >
+        kDebugValueOperandExpressionIndex + 1) {
+      assert(dbg_decl_or_val->GetOpenCL100DebugOpcode() ==
+             OpenCLDebugInfo100DebugValue);
+      for (uint32_t i = kDebugValueOperandExpressionIndex + 1;
+           i < dbg_decl_or_val->NumOperands(); ++i) {
+        new_dbg_value->AddOperand({spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+                                   {dbg_decl_or_val->GetSingleWordOperand(i)}});
+      }
+    }
+
+    // Avoid inserting the new DebugValue between OpPhi or OpVariable
+    // instructions.
+    Instruction* insert_before = insert_pos->NextNode();
+    while (insert_before->opcode() == SpvOpPhi ||
+           insert_before->opcode() == SpvOpVariable) {
+      insert_before = insert_before->NextNode();
+    }
+
+    Instruction* added_dbg_value =
+        insert_before->InsertBefore(std::move(new_dbg_value));
+    added_dbg_value->UpdateDebugInfo(scope_and_line);
+    AnalyzeDebugInst(added_dbg_value);
+    if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+      context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_value);
+    if (context()->AreAnalysesValid(
+            IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
+      auto insert_blk = context()->get_instr_block(insert_before);
+      context()->set_instr_block(added_dbg_value, insert_blk);
+    }
+  }
+}
+
+uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare(
+    Instruction* inst) {
+  if (inst->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugValue) return 0;
+
+  auto* expr =
+      GetDbgInst(inst->GetSingleWordOperand(kDebugValueOperandExpressionIndex));
+  if (expr == nullptr) return 0;
+  if (expr->NumOperands() != kDebugExpressOperandOperationIndex + 1) return 0;
+
+  auto* operation = GetDbgInst(
+      expr->GetSingleWordOperand(kDebugExpressOperandOperationIndex));
+  if (operation == nullptr) return 0;
+  if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) !=
+      OpenCLDebugInfo100Deref) {
+    return 0;
+  }
+
+  uint32_t var_id =
+      inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+  if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) {
+    assert(false &&
+           "Checking a DebugValue can be used for declare needs DefUseManager");
+    return 0;
+  }
+
+  auto* var = context()->get_def_use_mgr()->GetDef(var_id);
+  if (var->opcode() == SpvOpVariable &&
+      SpvStorageClass(var->GetSingleWordOperand(
+          kOpVariableOperandStorageClassIndex)) == SpvStorageClassFunction) {
+    return var_id;
+  }
+  return 0;
+}
+
 void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
-  if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax)
-    return;
+  if (!dbg_inst->IsOpenCL100DebugInstr()) return;
 
   RegisterDbgInst(dbg_inst);
 
@@ -271,27 +556,175 @@
     RegisterDbgFunction(dbg_inst);
   }
 
+  if (deref_operation_ == nullptr &&
+      dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation &&
+      dbg_inst->GetSingleWordOperand(kDebugOperationOperandOperationIndex) ==
+          OpenCLDebugInfo100Deref) {
+    deref_operation_ = dbg_inst;
+  }
+
   if (debug_info_none_inst_ == nullptr &&
       dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
     debug_info_none_inst_ = dbg_inst;
   }
+
+  if (empty_debug_expr_inst_ == nullptr && IsEmptyDebugExpression(dbg_inst)) {
+    empty_debug_expr_inst_ = dbg_inst;
+  }
+
+  if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) {
+    uint32_t var_id =
+        dbg_inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+    RegisterDbgDeclare(var_id, dbg_inst);
+  }
+
+  if (uint32_t var_id = GetVariableIdOfDebugValueUsedForDeclare(dbg_inst)) {
+    RegisterDbgDeclare(var_id, dbg_inst);
+  }
+}
+
+void DebugInfoManager::ConvertDebugGlobalToLocalVariable(
+    Instruction* dbg_global_var, Instruction* local_var) {
+  if (dbg_global_var->GetOpenCL100DebugOpcode() !=
+      OpenCLDebugInfo100DebugGlobalVariable) {
+    return;
+  }
+  assert(local_var->opcode() == SpvOpVariable ||
+         local_var->opcode() == SpvOpFunctionParameter);
+
+  // Convert |dbg_global_var| to DebugLocalVariable
+  dbg_global_var->SetInOperand(kExtInstInstructionInIdx,
+                               {OpenCLDebugInfo100DebugLocalVariable});
+  auto flags = dbg_global_var->GetSingleWordOperand(
+      kDebugGlobalVariableOperandFlagsIndex);
+  for (uint32_t i = dbg_global_var->NumInOperands() - 1;
+       i >= kDebugLocalVariableOperandFlagsIndex; --i) {
+    dbg_global_var->RemoveOperand(i);
+  }
+  dbg_global_var->SetOperand(kDebugLocalVariableOperandFlagsIndex, {flags});
+  context()->ForgetUses(dbg_global_var);
+  context()->AnalyzeUses(dbg_global_var);
+
+  // Create a DebugDeclare
+  std::unique_ptr<Instruction> new_dbg_decl(new Instruction(
+      context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+      context()->TakeNextId(),
+      {
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+           {context()
+                ->get_feature_mgr()
+                ->GetExtInstImportId_OpenCL100DebugInfo()}},
+          {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+           {static_cast<uint32_t>(OpenCLDebugInfo100DebugDeclare)}},
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+           {dbg_global_var->result_id()}},
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {local_var->result_id()}},
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+           {GetEmptyDebugExpression()->result_id()}},
+      }));
+  auto* added_dbg_decl =
+      local_var->NextNode()->InsertBefore(std::move(new_dbg_decl));
+  if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+    context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_decl);
+  if (context()->AreAnalysesValid(
+          IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
+    auto insert_blk = context()->get_instr_block(local_var);
+    context()->set_instr_block(added_dbg_decl, insert_blk);
+  }
 }
 
 void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
+  deref_operation_ = nullptr;
   debug_info_none_inst_ = nullptr;
+  empty_debug_expr_inst_ = nullptr;
   module.ForEachInst([this](Instruction* cpi) { AnalyzeDebugInst(cpi); });
 
+  // Move |empty_debug_expr_inst_| to the beginning of the debug instruction
+  // list.
+  if (empty_debug_expr_inst_ != nullptr &&
+      empty_debug_expr_inst_->PreviousNode() != nullptr &&
+      empty_debug_expr_inst_->PreviousNode()->IsOpenCL100DebugInstr()) {
+    empty_debug_expr_inst_->InsertBefore(
+        &*context()->module()->ext_inst_debuginfo_begin());
+  }
+
   // Move |debug_info_none_inst_| to the beginning of the debug instruction
   // list.
   if (debug_info_none_inst_ != nullptr &&
       debug_info_none_inst_->PreviousNode() != nullptr &&
-      debug_info_none_inst_->PreviousNode()->GetOpenCL100DebugOpcode() !=
-          OpenCLDebugInfo100InstructionsMax) {
+      debug_info_none_inst_->PreviousNode()->IsOpenCL100DebugInstr()) {
     debug_info_none_inst_->InsertBefore(
         &*context()->module()->ext_inst_debuginfo_begin());
   }
 }
 
+void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
+  if (instr == nullptr || !instr->IsOpenCL100DebugInstr()) {
+    return;
+  }
+
+  id_to_dbg_inst_.erase(instr->result_id());
+
+  if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) {
+    auto fn_id =
+        instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex);
+    fn_id_to_dbg_fn_.erase(fn_id);
+  }
+
+  if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare ||
+      instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) {
+    auto var_or_value_id =
+        instr->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+    auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_or_value_id);
+    if (dbg_decl_itr != var_id_to_dbg_decl_.end()) {
+      dbg_decl_itr->second.erase(instr);
+    }
+  }
+
+  if (deref_operation_ == instr) {
+    deref_operation_ = nullptr;
+    for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
+         dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
+         ++dbg_instr_itr) {
+      if (instr != &*dbg_instr_itr &&
+          dbg_instr_itr->GetOpenCL100DebugOpcode() ==
+              OpenCLDebugInfo100DebugOperation &&
+          dbg_instr_itr->GetSingleWordOperand(
+              kDebugOperationOperandOperationIndex) ==
+              OpenCLDebugInfo100Deref) {
+        deref_operation_ = &*dbg_instr_itr;
+        break;
+      }
+    }
+  }
+
+  if (debug_info_none_inst_ == instr) {
+    debug_info_none_inst_ = nullptr;
+    for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
+         dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
+         ++dbg_instr_itr) {
+      if (instr != &*dbg_instr_itr &&
+          dbg_instr_itr->GetOpenCL100DebugOpcode() ==
+              OpenCLDebugInfo100DebugInfoNone) {
+        debug_info_none_inst_ = &*dbg_instr_itr;
+        break;
+      }
+    }
+  }
+
+  if (empty_debug_expr_inst_ == instr) {
+    empty_debug_expr_inst_ = nullptr;
+    for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
+         dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
+         ++dbg_instr_itr) {
+      if (instr != &*dbg_instr_itr && IsEmptyDebugExpression(&*dbg_instr_itr)) {
+        empty_debug_expr_inst_ = &*dbg_instr_itr;
+        break;
+      }
+    }
+  }
+}
+
 }  // namespace analysis
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/opt/debug_info_manager.h b/third_party/SPIRV-Tools/source/opt/debug_info_manager.h
index 0c7186e..6592109 100644
--- a/third_party/SPIRV-Tools/source/opt/debug_info_manager.h
+++ b/third_party/SPIRV-Tools/source/opt/debug_info_manager.h
@@ -16,6 +16,7 @@
 #define SOURCE_OPT_DEBUG_INFO_MANAGER_H_
 
 #include <unordered_map>
+#include <unordered_set>
 
 #include "source/opt/instruction.h"
 #include "source/opt/module.h"
@@ -94,6 +95,10 @@
   uint32_t CreateDebugInlinedAt(const Instruction* line,
                                 const DebugScope& scope);
 
+  // Clones DebugExpress instruction |dbg_expr| and add Deref Operation
+  // in the front of the Operation list of |dbg_expr|.
+  Instruction* DerefDebugExpression(Instruction* dbg_expr);
+
   // Returns a DebugInfoNone instruction.
   Instruction* GetDebugInfoNone();
 
@@ -128,6 +133,31 @@
   uint32_t BuildDebugInlinedAtChain(uint32_t callee_inlined_at,
                                     DebugInlinedAtContext* inlined_at_ctx);
 
+  // Return true if |variable_id| has DebugDeclare or DebugVal.
+  bool IsDebugDeclared(uint32_t variable_id);
+
+  // Kill all DebugDeclares for |variable_id|
+  void KillDebugDeclares(uint32_t variable_id);
+
+  // Generates a DebugValue instruction with value |value_id| for every local
+  // variable that is in the scope of |scope_and_line| and whose memory is
+  // |variable_id| and inserts it after the instruction |insert_pos|.
+  void AddDebugValue(Instruction* scope_and_line, uint32_t variable_id,
+                     uint32_t value_id, Instruction* insert_pos);
+
+  // Erases |instr| from data structures of this class.
+  void ClearDebugInfo(Instruction* instr);
+
+  // Returns the id of Value operand if |inst| is DebugValue who has Deref
+  // operation and its Value operand is a result id of OpVariable with
+  // Function storage class. Otherwise, returns 0.
+  uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst);
+
+  // Converts DebugGlobalVariable |dbg_global_var| to a DebugLocalVariable and
+  // creates a DebugDeclare mapping the new DebugLocalVariable to |local_var|.
+  void ConvertDebugGlobalToLocalVariable(Instruction* dbg_global_var,
+                                         Instruction* local_var);
+
  private:
   IRContext* context() { return context_; }
 
@@ -139,6 +169,9 @@
   // does not exists.
   Instruction* GetDbgInst(uint32_t id);
 
+  // Returns a DebugOperation instruction with OpCode Deref.
+  Instruction* GetDebugOperationWithDeref();
+
   // Registers the debug instruction |inst| into |id_to_dbg_inst_| using id of
   // |inst| as a key.
   void RegisterDbgInst(Instruction* inst);
@@ -147,6 +180,26 @@
   // in |inst| must not already be registered.
   void RegisterDbgFunction(Instruction* inst);
 
+  // Register the DebugDeclare or DebugValue with Deref operation
+  // |dbg_declare| into |var_id_to_dbg_decl_| using OpVariable id
+  // |var_id| as a key.
+  void RegisterDbgDeclare(uint32_t var_id, Instruction* dbg_declare);
+
+  // Returns a DebugExpression instruction without Operation operands.
+  Instruction* GetEmptyDebugExpression();
+
+  // Returns true if a scope |ancestor| is |scope| or an ancestor scope
+  // of |scope|.
+  bool IsAncestorOfScope(uint32_t scope, uint32_t ancestor);
+
+  // Returns true if the declaration of a local variable |dbg_declare|
+  // is visible in the scope of an instruction |instr_scope_id|.
+  bool IsDeclareVisibleToInstr(Instruction* dbg_declare,
+                               uint32_t instr_scope_id);
+
+  // Returns the parent scope of the scope |child_scope|.
+  uint32_t GetParentScope(uint32_t child_scope);
+
   IRContext* context_;
 
   // Mapping from ids of OpenCL.DebugInfo.100 extension instructions
@@ -157,9 +210,22 @@
   // operand is the function.
   std::unordered_map<uint32_t, Instruction*> fn_id_to_dbg_fn_;
 
+  // Mapping from variable or value ids to DebugDeclare or DebugValue
+  // instructions whose operand is the variable or value.
+  std::unordered_map<uint32_t, std::unordered_set<Instruction*>>
+      var_id_to_dbg_decl_;
+
+  // DebugOperation whose OpCode is OpenCLDebugInfo100Deref.
+  Instruction* deref_operation_;
+
   // DebugInfoNone instruction. We need only a single DebugInfoNone.
   // To reuse the existing one, we keep it using this member variable.
   Instruction* debug_info_none_inst_;
+
+  // DebugExpression instruction without Operation operands. We need only
+  // a single DebugExpression without Operation operands. To reuse the
+  // existing one, we keep it using this member variable.
+  Instruction* empty_debug_expr_inst_;
 };
 
 }  // namespace analysis
diff --git a/third_party/SPIRV-Tools/source/opt/desc_sroa.cpp b/third_party/SPIRV-Tools/source/opt/desc_sroa.cpp
index 1f25b33..b68549a 100644
--- a/third_party/SPIRV-Tools/source/opt/desc_sroa.cpp
+++ b/third_party/SPIRV-Tools/source/opt/desc_sroa.cpp
@@ -56,7 +56,23 @@
   uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1);
   Instruction* var_type_inst =
       context()->get_def_use_mgr()->GetDef(var_type_id);
-  if (var_type_inst->opcode() != SpvOpTypeArray) {
+  if (var_type_inst->opcode() != SpvOpTypeArray &&
+      var_type_inst->opcode() != SpvOpTypeStruct) {
+    return false;
+  }
+
+  // All structures with descriptor assignments must be replaced by variables,
+  // one for each of their members - with the exceptions of buffers.
+  // Buffers are represented as structures, but we shouldn't replace a buffer
+  // with its elements. All buffers have offset decorations for members of their
+  // structure types.
+  bool has_offset_decoration = false;
+  context()->get_decoration_mgr()->ForEachDecoration(
+      var_type_inst->result_id(), SpvDecorationOffset,
+      [&has_offset_decoration](const Instruction&) {
+        has_offset_decoration = true;
+      });
+  if (has_offset_decoration) {
     return false;
   }
 
@@ -84,9 +100,11 @@
 }
 
 bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) {
-  std::vector<Instruction*> work_list;
+  std::vector<Instruction*> access_chain_work_list;
+  std::vector<Instruction*> load_work_list;
   bool failed = !get_def_use_mgr()->WhileEachUser(
-      var->result_id(), [this, &work_list](Instruction* use) {
+      var->result_id(),
+      [this, &access_chain_work_list, &load_work_list](Instruction* use) {
         if (use->opcode() == SpvOpName) {
           return true;
         }
@@ -98,7 +116,10 @@
         switch (use->opcode()) {
           case SpvOpAccessChain:
           case SpvOpInBoundsAccessChain:
-            work_list.push_back(use);
+            access_chain_work_list.push_back(use);
+            return true;
+          case SpvOpLoad:
+            load_work_list.push_back(use);
             return true;
           default:
             context()->EmitErrorMessage(
@@ -112,11 +133,16 @@
     return false;
   }
 
-  for (Instruction* use : work_list) {
+  for (Instruction* use : access_chain_work_list) {
     if (!ReplaceAccessChain(var, use)) {
       return false;
     }
   }
+  for (Instruction* use : load_work_list) {
+    if (!ReplaceLoadedValue(var, use)) {
+      return false;
+    }
+  }
   return true;
 }
 
@@ -177,21 +203,36 @@
     uint32_t ptr_type_id = var->type_id();
     Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
     assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
-           "Variable should be a pointer to an array.");
-    uint32_t arr_type_id = ptr_type_inst->GetSingleWordInOperand(1);
-    Instruction* arr_type_inst = get_def_use_mgr()->GetDef(arr_type_id);
-    assert(arr_type_inst->opcode() == SpvOpTypeArray &&
-           "Variable should be a pointer to an array.");
+           "Variable should be a pointer to an array or structure.");
+    uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
+    Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id);
+    const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray;
+    const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct;
+    assert((is_array || is_struct) &&
+           "Variable should be a pointer to an array or structure.");
 
-    uint32_t array_len_id = arr_type_inst->GetSingleWordInOperand(1);
-    const analysis::Constant* array_len_const =
-        context()->get_constant_mgr()->FindDeclaredConstant(array_len_id);
-    assert(array_len_const != nullptr && "Array length must be a constant.");
-    uint32_t array_len = array_len_const->GetU32();
+    // For arrays, each array element should be replaced with a new replacement
+    // variable
+    if (is_array) {
+      uint32_t array_len_id = pointee_type_inst->GetSingleWordInOperand(1);
+      const analysis::Constant* array_len_const =
+          context()->get_constant_mgr()->FindDeclaredConstant(array_len_id);
+      assert(array_len_const != nullptr && "Array length must be a constant.");
+      uint32_t array_len = array_len_const->GetU32();
 
-    replacement_vars = replacement_variables_
-                           .insert({var, std::vector<uint32_t>(array_len, 0)})
-                           .first;
+      replacement_vars = replacement_variables_
+                             .insert({var, std::vector<uint32_t>(array_len, 0)})
+                             .first;
+    }
+    // For structures, each member should be replaced with a new replacement
+    // variable
+    if (is_struct) {
+      const uint32_t num_members = pointee_type_inst->NumInOperands();
+      replacement_vars =
+          replacement_variables_
+              .insert({var, std::vector<uint32_t>(num_members, 0)})
+              .first;
+    }
   }
 
   if (replacement_vars->second[idx] == 0) {
@@ -212,12 +253,17 @@
   uint32_t ptr_type_id = var->type_id();
   Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
   assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
-         "Variable should be a pointer to an array.");
-  uint32_t arr_type_id = ptr_type_inst->GetSingleWordInOperand(1);
-  Instruction* arr_type_inst = get_def_use_mgr()->GetDef(arr_type_id);
-  assert(arr_type_inst->opcode() == SpvOpTypeArray &&
-         "Variable should be a pointer to an array.");
-  uint32_t element_type_id = arr_type_inst->GetSingleWordInOperand(0);
+         "Variable should be a pointer to an array or structure.");
+  uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
+  Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id);
+  const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray;
+  const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct;
+  assert((is_array || is_struct) &&
+         "Variable should be a pointer to an array or structure.");
+
+  uint32_t element_type_id =
+      is_array ? pointee_type_inst->GetSingleWordInOperand(0)
+               : pointee_type_inst->GetSingleWordInOperand(idx);
 
   uint32_t ptr_element_type_id = context()->get_type_mgr()->FindPointerToType(
       element_type_id, storage_class);
@@ -242,19 +288,42 @@
 
     uint32_t decoration = new_decoration->GetSingleWordInOperand(1u);
     if (decoration == SpvDecorationBinding) {
-      uint32_t new_binding = new_decoration->GetSingleWordInOperand(2) + idx;
+      uint32_t new_binding = new_decoration->GetSingleWordInOperand(2);
+      if (is_array) {
+        new_binding += idx * GetNumBindingsUsedByType(ptr_element_type_id);
+      }
+      if (is_struct) {
+        // The binding offset that should be added is the sum of binding numbers
+        // used by previous members of the current struct.
+        for (uint32_t i = 0; i < idx; ++i) {
+          new_binding += GetNumBindingsUsedByType(
+              pointee_type_inst->GetSingleWordInOperand(i));
+        }
+      }
       new_decoration->SetInOperand(2, {new_binding});
     }
     context()->AddAnnotationInst(std::move(new_decoration));
   }
 
   // Create a new OpName for the replacement variable.
+  std::vector<std::unique_ptr<Instruction>> names_to_add;
   for (auto p : context()->GetNames(var->result_id())) {
     Instruction* name_inst = p.second;
     std::string name_str = utils::MakeString(name_inst->GetOperand(1).words);
-    name_str += "[";
-    name_str += utils::ToString(idx);
-    name_str += "]";
+    if (is_array) {
+      name_str += "[" + utils::ToString(idx) + "]";
+    }
+    if (is_struct) {
+      Instruction* member_name_inst =
+          context()->GetMemberName(pointee_type_inst->result_id(), idx);
+      name_str += ".";
+      if (member_name_inst)
+        name_str += utils::MakeString(member_name_inst->GetOperand(2).words);
+      else
+        // In case the member does not have a name assigned to it, use the
+        // member index.
+        name_str += utils::ToString(idx);
+    }
 
     std::unique_ptr<Instruction> new_name(new Instruction(
         context(), SpvOpName, 0, 0,
@@ -262,12 +331,118 @@
             {SPV_OPERAND_TYPE_ID, {id}},
             {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}}));
     Instruction* new_name_inst = new_name.get();
-    context()->AddDebug2Inst(std::move(new_name));
     get_def_use_mgr()->AnalyzeInstDefUse(new_name_inst);
+    names_to_add.push_back(std::move(new_name));
   }
 
+  // We shouldn't add the new names when we are iterating over name ranges
+  // above. We can add all the new names now.
+  for (auto& new_name : names_to_add)
+    context()->AddDebug2Inst(std::move(new_name));
+
   return id;
 }
 
+uint32_t DescriptorScalarReplacement::GetNumBindingsUsedByType(
+    uint32_t type_id) {
+  Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
+
+  // If it's a pointer, look at the underlying type.
+  if (type_inst->opcode() == SpvOpTypePointer) {
+    type_id = type_inst->GetSingleWordInOperand(1);
+    type_inst = get_def_use_mgr()->GetDef(type_id);
+  }
+
+  // Arrays consume N*M binding numbers where N is the array length, and M is
+  // the number of bindings used by each array element.
+  if (type_inst->opcode() == SpvOpTypeArray) {
+    uint32_t element_type_id = type_inst->GetSingleWordInOperand(0);
+    uint32_t length_id = type_inst->GetSingleWordInOperand(1);
+    const analysis::Constant* length_const =
+        context()->get_constant_mgr()->FindDeclaredConstant(length_id);
+    // OpTypeArray's length must always be a constant
+    assert(length_const != nullptr);
+    uint32_t num_elems = length_const->GetU32();
+    return num_elems * GetNumBindingsUsedByType(element_type_id);
+  }
+
+  // The number of bindings consumed by a structure is the sum of the bindings
+  // used by its members.
+  if (type_inst->opcode() == SpvOpTypeStruct) {
+    uint32_t sum = 0;
+    for (uint32_t i = 0; i < type_inst->NumInOperands(); i++)
+      sum += GetNumBindingsUsedByType(type_inst->GetSingleWordInOperand(i));
+    return sum;
+  }
+
+  // All other types are considered to take up 1 binding number.
+  return 1;
+}
+
+bool DescriptorScalarReplacement::ReplaceLoadedValue(Instruction* var,
+                                                     Instruction* value) {
+  // |var| is the global variable that has to be eliminated (OpVariable).
+  // |value| is the OpLoad instruction that has loaded |var|.
+  // The function expects all users of |value| to be OpCompositeExtract
+  // instructions. Otherwise the function returns false with an error message.
+  assert(value->opcode() == SpvOpLoad);
+  assert(value->GetSingleWordInOperand(0) == var->result_id());
+  std::vector<Instruction*> work_list;
+  bool failed = !get_def_use_mgr()->WhileEachUser(
+      value->result_id(), [this, &work_list](Instruction* use) {
+        if (use->opcode() != SpvOpCompositeExtract) {
+          context()->EmitErrorMessage(
+              "Variable cannot be replaced: invalid instruction", use);
+          return false;
+        }
+        work_list.push_back(use);
+        return true;
+      });
+
+  if (failed) {
+    return false;
+  }
+
+  for (Instruction* use : work_list) {
+    if (!ReplaceCompositeExtract(var, use)) {
+      return false;
+    }
+  }
+
+  // All usages of the loaded value have been killed. We can kill the OpLoad.
+  context()->KillInst(value);
+  return true;
+}
+
+bool DescriptorScalarReplacement::ReplaceCompositeExtract(
+    Instruction* var, Instruction* extract) {
+  assert(extract->opcode() == SpvOpCompositeExtract);
+  // We're currently only supporting extractions of one index at a time. If we
+  // need to, we can handle cases with multiple indexes in the future.
+  if (extract->NumInOperands() != 2) {
+    context()->EmitErrorMessage(
+        "Variable cannot be replaced: invalid instruction", extract);
+    return false;
+  }
+
+  uint32_t replacement_var =
+      GetReplacementVariable(var, extract->GetSingleWordInOperand(1));
+
+  // The result type of the OpLoad is the same as the result type of the
+  // OpCompositeExtract.
+  uint32_t load_id = TakeNextId();
+  std::unique_ptr<Instruction> load(
+      new Instruction(context(), SpvOpLoad, extract->type_id(), load_id,
+                      std::initializer_list<Operand>{
+                          {SPV_OPERAND_TYPE_ID, {replacement_var}}}));
+  Instruction* load_instr = load.get();
+  get_def_use_mgr()->AnalyzeInstDefUse(load_instr);
+  context()->set_instr_block(load_instr, context()->get_instr_block(extract));
+  extract->InsertBefore(std::move(load));
+  context()->ReplaceAllUsesWith(extract->result_id(), load_id);
+  context()->KillInst(extract);
+  return true;
+}
+
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/opt/desc_sroa.h b/third_party/SPIRV-Tools/source/opt/desc_sroa.h
index a95c6b5..c3aa0ea 100644
--- a/third_party/SPIRV-Tools/source/opt/desc_sroa.h
+++ b/third_party/SPIRV-Tools/source/opt/desc_sroa.h
@@ -61,6 +61,20 @@
   // |true| if successful.
   bool ReplaceAccessChain(Instruction* var, Instruction* use);
 
+  // Replaces the given compososite variable |var| loaded by OpLoad |value| with
+  // replacement variables, one for each component that's accessed in the
+  // shader. Assumes that |value| is only used by OpCompositeExtract
+  // instructions, one index at a time. Returns true on success, and false
+  // otherwise.
+  bool ReplaceLoadedValue(Instruction* var, Instruction* value);
+
+  // Replaces the given OpCompositeExtract |extract| and all of its references
+  // with an OpLoad of a replacement variable. |var| is the variable with
+  // composite type whose value is being used by |extract|. Assumes that
+  // |extract| is extracting one index only. Returns true on success, and false
+  // otherwise.
+  bool ReplaceCompositeExtract(Instruction* var, Instruction* extract);
+
   // Returns the id of the variable that will be used to replace the |idx|th
   // element of |var|.  The variable is created if it has not already been
   // created.
@@ -70,6 +84,15 @@
   // element of |var|.
   uint32_t CreateReplacementVariable(Instruction* var, uint32_t idx);
 
+  // Returns the number of bindings used by the given |type_id|.
+  // All types are considered to use 1 binding slot, except:
+  // 1- A pointer type consumes as many binding numbers as its pointee.
+  // 2- An array of size N consumes N*M binding numbers, where M is the number
+  // of bindings used by each array element.
+  // 3- The number of bindings consumed by a structure is the sum of the
+  // bindings used by its members.
+  uint32_t GetNumBindingsUsedByType(uint32_t type_id);
+
   // A map from an OpVariable instruction to the set of variables that will be
   // used to replace it. The entry |replacement_variables_[var][i]| is the id of
   // a variable that will be used in the place of the the ith element of the
diff --git a/third_party/SPIRV-Tools/source/opt/dominator_analysis.cpp b/third_party/SPIRV-Tools/source/opt/dominator_analysis.cpp
index aef43e6..b692d26 100644
--- a/third_party/SPIRV-Tools/source/opt/dominator_analysis.cpp
+++ b/third_party/SPIRV-Tools/source/opt/dominator_analysis.cpp
@@ -55,12 +55,25 @@
     return tree_.Dominates(bb_a, bb_b);
   }
 
-  Instruction* current_inst = a;
-  while ((current_inst = current_inst->NextNode())) {
-    if (current_inst == b) {
+  const Instruction* current = a;
+  const Instruction* other = b;
+
+  if (tree_.IsPostDominator()) {
+    std::swap(current, other);
+  }
+
+  // We handle OpLabel instructions explicitly since they are not stored in the
+  // instruction list.
+  if (current->opcode() == SpvOpLabel) {
+    return true;
+  }
+
+  while ((current = current->NextNode())) {
+    if (current == other) {
       return true;
     }
   }
+
   return false;
 }
 
diff --git a/third_party/SPIRV-Tools/source/opt/dominator_tree.cpp b/third_party/SPIRV-Tools/source/opt/dominator_tree.cpp
index da5073a..7e61506 100644
--- a/third_party/SPIRV-Tools/source/opt/dominator_tree.cpp
+++ b/third_party/SPIRV-Tools/source/opt/dominator_tree.cpp
@@ -176,7 +176,8 @@
     // The tree construction requires 1 entry point, so we add a dummy node
     // that is connected to all function exiting basic blocks.
     // An exiting basic block is a block with an OpKill, OpUnreachable,
-    // OpReturn or OpReturnValue as terminator instruction.
+    // OpReturn, OpReturnValue, or OpTerminateInvocation  as terminator
+    // instruction.
     for (BasicBlock& bb : f) {
       if (bb.hasSuccessor()) {
         BasicBlockListTy& pred_list = predecessors_[&bb];
diff --git a/third_party/SPIRV-Tools/source/opt/function.h b/third_party/SPIRV-Tools/source/opt/function.h
index d569bf9..f5035f0 100644
--- a/third_party/SPIRV-Tools/source/opt/function.h
+++ b/third_party/SPIRV-Tools/source/opt/function.h
@@ -72,6 +72,10 @@
   // Delete all basic blocks that contain no instructions.
   inline void RemoveEmptyBlocks();
 
+  // Removes a parameter from the function with result id equal to |id|.
+  // Does nothing if the function doesn't have such a parameter.
+  inline void RemoveParameter(uint32_t id);
+
   // Saves the given function end instruction.
   inline void SetFunctionEnd(std::unique_ptr<Instruction> end_inst);
 
@@ -219,6 +223,14 @@
   blocks_.erase(first_empty, std::end(blocks_));
 }
 
+inline void Function::RemoveParameter(uint32_t id) {
+  params_.erase(std::remove_if(params_.begin(), params_.end(),
+                               [id](const std::unique_ptr<Instruction>& param) {
+                                 return param->result_id() == id;
+                               }),
+                params_.end());
+}
+
 inline void Function::SetFunctionEnd(std::unique_ptr<Instruction> end_inst) {
   end_inst_ = std::move(end_inst);
 }
diff --git a/third_party/SPIRV-Tools/source/opt/inline_pass.cpp b/third_party/SPIRV-Tools/source/opt/inline_pass.cpp
index cb5a126..ef94d0d 100644
--- a/third_party/SPIRV-Tools/source/opt/inline_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/inline_pass.cpp
@@ -384,7 +384,8 @@
   for (auto callee_block_itr = calleeFn->begin();
        callee_block_itr != calleeFn->end(); ++callee_block_itr) {
     if (callee_block_itr->tail()->opcode() == SpvOpUnreachable ||
-        callee_block_itr->tail()->opcode() == SpvOpKill) {
+        callee_block_itr->tail()->opcode() == SpvOpKill ||
+        callee_block_itr->tail()->opcode() == SpvOpTerminateInvocation) {
       returnLabelId = context()->TakeNextId();
       break;
     }
@@ -738,16 +739,18 @@
   bool func_is_called_from_continue =
       funcs_called_from_continue_.count(func->result_id()) != 0;
 
-  if (func_is_called_from_continue && ContainsKill(func)) {
+  if (func_is_called_from_continue && ContainsKillOrTerminateInvocation(func)) {
     return false;
   }
 
   return true;
 }
 
-bool InlinePass::ContainsKill(Function* func) const {
-  return !func->WhileEachInst(
-      [](Instruction* inst) { return inst->opcode() != SpvOpKill; });
+bool InlinePass::ContainsKillOrTerminateInvocation(Function* func) const {
+  return !func->WhileEachInst([](Instruction* inst) {
+    const auto opcode = inst->opcode();
+    return (opcode != SpvOpKill) && (opcode != SpvOpTerminateInvocation);
+  });
 }
 
 void InlinePass::InitializeInline() {
diff --git a/third_party/SPIRV-Tools/source/opt/inline_pass.h b/third_party/SPIRV-Tools/source/opt/inline_pass.h
index 202bc97..abe773a 100644
--- a/third_party/SPIRV-Tools/source/opt/inline_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/inline_pass.h
@@ -139,8 +139,9 @@
   // Return true if |func| is a function that can be inlined.
   bool IsInlinableFunction(Function* func);
 
-  // Returns true if |func| contains an OpKill instruction.
-  bool ContainsKill(Function* func) const;
+  // Returns true if |func| contains an OpKill or OpTerminateInvocation
+  // instruction.
+  bool ContainsKillOrTerminateInvocation(Function* func) const;
 
   // Update phis in succeeding blocks to point to new last block
   void UpdateSucceedingPhis(
diff --git a/third_party/SPIRV-Tools/source/opt/instruction.h b/third_party/SPIRV-Tools/source/opt/instruction.h
index 7d8fed8..e99a5ae 100644
--- a/third_party/SPIRV-Tools/source/opt/instruction.h
+++ b/third_party/SPIRV-Tools/source/opt/instruction.h
@@ -306,6 +306,10 @@
   void RemoveOperand(uint32_t index) {
     operands_.erase(operands_.begin() + index);
   }
+  // Insert an operand before the |index|-th operand
+  void InsertOperand(uint32_t index, Operand&& operand) {
+    operands_.insert(operands_.begin() + index, operand);
+  }
 
   // The following methods are similar to the above, but are for in operands.
   uint32_t NumInOperands() const {
@@ -535,6 +539,11 @@
   // OpenCLDebugInfo100InstructionsMax.
   OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const;
 
+  // Returns true if it is an OpenCL.DebugInfo.100 instruction.
+  bool IsOpenCL100DebugInstr() const {
+    return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax;
+  }
+
   // Dump this instruction on stderr.  Useful when running interactive
   // debuggers.
   void Dump() const;
diff --git a/third_party/SPIRV-Tools/source/opt/instruction_list.h b/third_party/SPIRV-Tools/source/opt/instruction_list.h
index ea1cc7c..417cbd7 100644
--- a/third_party/SPIRV-Tools/source/opt/instruction_list.h
+++ b/third_party/SPIRV-Tools/source/opt/instruction_list.h
@@ -61,6 +61,16 @@
         : utils::IntrusiveList<Instruction>::iterator(i) {}
     iterator(Instruction* i) : utils::IntrusiveList<Instruction>::iterator(i) {}
 
+    iterator& operator++() {
+      utils::IntrusiveList<Instruction>::iterator::operator++();
+      return *this;
+    }
+
+    iterator& operator--() {
+      utils::IntrusiveList<Instruction>::iterator::operator--();
+      return *this;
+    }
+
     // DEPRECATED: Please use MoveBefore with an InstructionList instead.
     //
     // Moves the nodes in |list| to the list that |this| points to.  The
diff --git a/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp b/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
index 4210ad5..e6a55a8 100644
--- a/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
@@ -174,7 +174,9 @@
           context()->GetBuiltinInputVarId(SpvBuiltInInstanceIndex),
           kInstVertOutInstanceIndex, base_offset_id, builder);
     } break;
-    case SpvExecutionModelGLCompute: {
+    case SpvExecutionModelGLCompute:
+    case SpvExecutionModelTaskNV:
+    case SpvExecutionModelMeshNV: {
       // Load and store GlobalInvocationId.
       uint32_t load_id = GenVarLoad(
           context()->GetBuiltinInputVarId(SpvBuiltInGlobalInvocationId),
@@ -914,6 +916,7 @@
       stage != SpvExecutionModelGLCompute &&
       stage != SpvExecutionModelTessellationControl &&
       stage != SpvExecutionModelTessellationEvaluation &&
+      stage != SpvExecutionModelTaskNV && stage != SpvExecutionModelMeshNV &&
       stage != SpvExecutionModelRayGenerationNV &&
       stage != SpvExecutionModelIntersectionNV &&
       stage != SpvExecutionModelAnyHitNV &&
diff --git a/third_party/SPIRV-Tools/source/opt/ir_context.cpp b/third_party/SPIRV-Tools/source/opt/ir_context.cpp
index df04066..0791097 100644
--- a/third_party/SPIRV-Tools/source/opt/ir_context.cpp
+++ b/third_party/SPIRV-Tools/source/opt/ir_context.cpp
@@ -97,8 +97,9 @@
 }
 
 void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
-  // The ConstantManager contains Type pointers. If the TypeManager goes
-  // away, the ConstantManager has to go away.
+  // The ConstantManager and DebugInfoManager contain Type pointers. If the
+  // TypeManager goes away, the ConstantManager and DebugInfoManager have to
+  // go away.
   if (analyses_to_invalidate & kAnalysisTypes) {
     analyses_to_invalidate |= kAnalysisConstants;
     analyses_to_invalidate |= kAnalysisDebugInfo;
@@ -179,6 +180,9 @@
       decoration_mgr_->RemoveDecoration(inst);
     }
   }
+  if (AreAnalysesValid(kAnalysisDebugInfo)) {
+    get_debug_info_mgr()->ClearDebugInfo(inst);
+  }
   if (type_mgr_ && IsTypeInst(inst->opcode())) {
     type_mgr_->RemoveId(inst->result_id());
   }
@@ -218,6 +222,13 @@
   return false;
 }
 
+void IRContext::KillDebugDeclareInsts(Function* fn) {
+  fn->ForEachInst([this](Instruction* inst) {
+    if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare)
+      KillInst(inst);
+  });
+}
+
 bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) {
   return ReplaceAllUsesWithPredicate(
       before, after, [](Instruction*, uint32_t) { return true; });
@@ -344,6 +355,9 @@
       get_decoration_mgr()->RemoveDecoration(inst);
     }
   }
+  if (AreAnalysesValid(kAnalysisDebugInfo)) {
+    get_debug_info_mgr()->ClearDebugInfo(inst);
+  }
   RemoveFromIdToName(inst);
 }
 
@@ -356,6 +370,9 @@
       get_decoration_mgr()->AddDecoration(inst);
     }
   }
+  if (AreAnalysesValid(kAnalysisDebugInfo)) {
+    get_debug_info_mgr()->AnalyzeDebugInst(inst);
+  }
   if (id_to_name_ &&
       (inst->opcode() == SpvOpName || inst->opcode() == SpvOpMemberName)) {
     id_to_name_->insert({inst->GetSingleWordInOperand(0), inst});
@@ -394,6 +411,7 @@
       if (operand.words[0] == id) {
         operand.words[0] =
             get_debug_info_mgr()->GetDebugInfoNone()->result_id();
+        get_def_use_mgr()->AnalyzeInstUse(&*it);
       }
     }
   }
@@ -408,13 +426,10 @@
       if (operand.words[0] == id) {
         operand.words[0] =
             get_debug_info_mgr()->GetDebugInfoNone()->result_id();
+        get_def_use_mgr()->AnalyzeInstUse(&*it);
       }
     }
   }
-  // Notice that we do not need anythings to do for local variables.
-  // DebugLocalVariable does not have an OpVariable operand. Instead,
-  // DebugDeclare/DebugValue has an OpVariable operand for a local
-  // variable. The function inlining pass handles it properly.
 }
 
 void IRContext::AddCombinatorsForCapability(uint32_t capability) {
diff --git a/third_party/SPIRV-Tools/source/opt/ir_context.h b/third_party/SPIRV-Tools/source/opt/ir_context.h
index a1b63ff..37be836 100644
--- a/third_party/SPIRV-Tools/source/opt/ir_context.h
+++ b/third_party/SPIRV-Tools/source/opt/ir_context.h
@@ -357,6 +357,13 @@
   inline IteratorRange<std::multimap<uint32_t, Instruction*>::iterator>
   GetNames(uint32_t id);
 
+  // Returns an OpMemberName instruction that targets |struct_type_id| at
+  // index |index|. Returns nullptr if no such instruction exists.
+  // While the SPIR-V spec does not prohibit having multiple OpMemberName
+  // instructions for the same structure member, it is hard to imagine a member
+  // having more than one name. This method returns the first one it finds.
+  inline Instruction* GetMemberName(uint32_t struct_type_id, uint32_t index);
+
   // Sets the message consumer to the given |consumer|. |consumer| which will be
   // invoked every time there is a message to be communicated to the outside.
   void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); }
@@ -396,6 +403,9 @@
   // instruction exists.
   Instruction* KillInst(Instruction* inst);
 
+  // Deletes DebugDeclare instructions in the given function |fn|.
+  void KillDebugDeclareInsts(Function* fn);
+
   // Returns true if all of the given analyses are valid.
   bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; }
 
@@ -1061,7 +1071,9 @@
 void IRContext::AddDebug2Inst(std::unique_ptr<Instruction>&& d) {
   if (AreAnalysesValid(kAnalysisNameMap)) {
     if (d->opcode() == SpvOpName || d->opcode() == SpvOpMemberName) {
-      id_to_name_->insert({d->result_id(), d.get()});
+      // OpName and OpMemberName do not have result-ids. The target of the
+      // instruction is at InOperand index 0.
+      id_to_name_->insert({d->GetSingleWordInOperand(0), d.get()});
     }
   }
   module()->AddDebug2Inst(std::move(d));
@@ -1135,6 +1147,21 @@
   return make_range(std::move(result.first), std::move(result.second));
 }
 
+Instruction* IRContext::GetMemberName(uint32_t struct_type_id, uint32_t index) {
+  if (!AreAnalysesValid(kAnalysisNameMap)) {
+    BuildIdToNameMap();
+  }
+  auto result = id_to_name_->equal_range(struct_type_id);
+  for (auto i = result.first; i != result.second; ++i) {
+    auto* name_instr = i->second;
+    if (name_instr->opcode() == SpvOpMemberName &&
+        name_instr->GetSingleWordInOperand(1) == index) {
+      return name_instr;
+    }
+  }
+  return nullptr;
+}
+
 }  // namespace opt
 }  // namespace spvtools
 
diff --git a/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp b/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp
index 0afe798..9b8c112 100644
--- a/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp
@@ -281,7 +281,7 @@
   // Initialize collections
   supported_ref_ptrs_.clear();
 
-  // Initialize extension whitelist
+  // Initialize extension allowlist
   InitExtensions();
 }
 
@@ -292,11 +292,11 @@
   if (context()->get_feature_mgr()->HasCapability(
           SpvCapabilityVariablePointers))
     return false;
-  // If any extension not in whitelist, return false
+  // If any extension not in allowlist, return false
   for (auto& ei : get_module()->extensions()) {
     const char* extName =
         reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
-    if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
+    if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
       return false;
   }
   return true;
@@ -336,8 +336,8 @@
 }
 
 void LocalAccessChainConvertPass::InitExtensions() {
-  extensions_whitelist_.clear();
-  extensions_whitelist_.insert({
+  extensions_allowlist_.clear();
+  extensions_allowlist_.insert({
       "SPV_AMD_shader_explicit_vertex_parameter",
       "SPV_AMD_shader_trinary_minmax",
       "SPV_AMD_gcn_shader",
@@ -382,6 +382,7 @@
       "SPV_KHR_ray_tracing",
       "SPV_KHR_ray_query",
       "SPV_EXT_fragment_invocation_density",
+      "SPV_KHR_terminate_invocation",
   });
 }
 
diff --git a/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.h b/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.h
index e3592bf..552062e 100644
--- a/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.h
@@ -110,7 +110,7 @@
   // Returns a status to indicate success or failure, and change or no change.
   Status ConvertLocalAccessChains(Function* func);
 
-  // Initialize extensions whitelist
+  // Initialize extensions allowlist
   void InitExtensions();
 
   // Return true if all extensions in this module are allowed by this pass.
@@ -124,7 +124,7 @@
   std::unordered_set<uint32_t> supported_ref_ptrs_;
 
   // Extensions supported by this pass.
-  std::unordered_set<std::string> extensions_whitelist_;
+  std::unordered_set<std::string> extensions_allowlist_;
 };
 
 }  // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.cpp b/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.cpp
index b5435bb..bd5d751 100644
--- a/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.cpp
@@ -31,6 +31,11 @@
 bool LocalSingleBlockLoadStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) {
   if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true;
   if (get_def_use_mgr()->WhileEachUser(ptrId, [this](Instruction* user) {
+        auto dbg_op = user->GetOpenCL100DebugOpcode();
+        if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
+            dbg_op == OpenCLDebugInfo100DebugValue) {
+          return true;
+        }
         SpvOp op = user->opcode();
         if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) {
           if (!HasOnlySupportedRefs(user->result_id())) {
@@ -73,10 +78,12 @@
           // variable.
           if (ptrInst->opcode() == SpvOpVariable) {
             // If a previous store to same variable, mark the store
-            // for deletion if not still used.
+            // for deletion if not still used. Don't delete store
+            // if debugging; let ssa-rewrite and DCE handle it
             auto prev_store = var2store_.find(varId);
             if (prev_store != var2store_.end() &&
-                instructions_to_save.count(prev_store->second) == 0) {
+                instructions_to_save.count(prev_store->second) == 0 &&
+                !context()->get_debug_info_mgr()->IsDebugDeclared(varId)) {
               instructions_to_kill.push_back(prev_store->second);
               modified = true;
             }
@@ -168,16 +175,16 @@
   // Clear collections
   supported_ref_ptrs_.clear();
 
-  // Initialize extensions whitelist
+  // Initialize extensions allowlist
   InitExtensions();
 }
 
 bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const {
-  // If any extension not in whitelist, return false
+  // If any extension not in allowlist, return false
   for (auto& ei : get_module()->extensions()) {
     const char* extName =
         reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
-    if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
+    if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
       return false;
   }
   return true;
@@ -214,8 +221,8 @@
 }
 
 void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
-  extensions_whitelist_.clear();
-  extensions_whitelist_.insert({
+  extensions_allowlist_.clear();
+  extensions_allowlist_.insert({
       "SPV_AMD_shader_explicit_vertex_parameter",
       "SPV_AMD_shader_trinary_minmax",
       "SPV_AMD_gcn_shader",
@@ -260,6 +267,7 @@
       "SPV_KHR_ray_query",
       "SPV_EXT_fragment_invocation_density",
       "SPV_EXT_physical_storage_buffer",
+      "SPV_KHR_terminate_invocation",
   });
 }
 
diff --git a/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.h b/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.h
index 0fe7732..ea72816 100644
--- a/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.h
@@ -63,7 +63,7 @@
   // where possible. Assumes logical addressing.
   bool LocalSingleBlockLoadStoreElim(Function* func);
 
-  // Initialize extensions whitelist
+  // Initialize extensions allowlist
   void InitExtensions();
 
   // Return true if all extensions in this module are supported by this pass.
@@ -94,7 +94,7 @@
   std::unordered_set<uint32_t> pinned_vars_;
 
   // Extensions supported by this pass.
-  std::unordered_set<std::string> extensions_whitelist_;
+  std::unordered_set<std::string> extensions_allowlist_;
 
   // Variables that are only referenced by supported operations for this
   // pass ie. loads and stores.
diff --git a/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.cpp b/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.cpp
index 4c71ce1..2384107 100644
--- a/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.cpp
@@ -46,11 +46,11 @@
 }
 
 bool LocalSingleStoreElimPass::AllExtensionsSupported() const {
-  // If any extension not in whitelist, return false
+  // If any extension not in allowlist, return false
   for (auto& ei : get_module()->extensions()) {
     const char* extName =
         reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
-    if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
+    if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
       return false;
   }
   return true;
@@ -74,12 +74,12 @@
 LocalSingleStoreElimPass::LocalSingleStoreElimPass() = default;
 
 Pass::Status LocalSingleStoreElimPass::Process() {
-  InitExtensionWhiteList();
+  InitExtensionAllowList();
   return ProcessImpl();
 }
 
-void LocalSingleStoreElimPass::InitExtensionWhiteList() {
-  extensions_whitelist_.insert({
+void LocalSingleStoreElimPass::InitExtensionAllowList() {
+  extensions_allowlist_.insert({
       "SPV_AMD_shader_explicit_vertex_parameter",
       "SPV_AMD_shader_trinary_minmax",
       "SPV_AMD_gcn_shader",
@@ -121,6 +121,7 @@
       "SPV_KHR_ray_query",
       "SPV_EXT_fragment_invocation_density",
       "SPV_EXT_physical_storage_buffer",
+      "SPV_KHR_terminate_invocation",
   });
 }
 bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
@@ -133,7 +134,27 @@
     return false;
   }
 
-  return RewriteLoads(store_inst, users);
+  bool all_rewritten;
+  bool modified = RewriteLoads(store_inst, users, &all_rewritten);
+
+  // If all uses are rewritten and the variable has a DebugDeclare and the
+  // variable is not an aggregate, add a DebugValue after the store and remove
+  // the DebugDeclare.
+  uint32_t var_id = var_inst->result_id();
+  if (all_rewritten &&
+      context()->get_debug_info_mgr()->IsDebugDeclared(var_id)) {
+    const analysis::Type* var_type =
+        context()->get_type_mgr()->GetType(var_inst->type_id());
+    const analysis::Type* store_type = var_type->AsPointer()->pointee_type();
+    if (!(store_type->AsStruct() || store_type->AsArray())) {
+      context()->get_debug_info_mgr()->AddDebugValue(
+          store_inst, var_id, store_inst->GetSingleWordInOperand(1),
+          store_inst);
+      context()->get_debug_info_mgr()->KillDebugDeclares(var_id);
+    }
+  }
+
+  return modified;
 }
 
 Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses(
@@ -172,6 +193,14 @@
       case SpvOpName:
       case SpvOpCopyObject:
         break;
+      case SpvOpExtInst: {
+        auto dbg_op = user->GetOpenCL100DebugOpcode();
+        if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
+            dbg_op == OpenCLDebugInfo100DebugValue) {
+          break;
+        }
+        return nullptr;
+      }
       default:
         if (!user->IsDecoration()) {
           // Don't know if this instruction modifies the variable.
@@ -218,7 +247,8 @@
 }
 
 bool LocalSingleStoreElimPass::RewriteLoads(
-    Instruction* store_inst, const std::vector<Instruction*>& uses) {
+    Instruction* store_inst, const std::vector<Instruction*>& uses,
+    bool* all_rewritten) {
   BasicBlock* store_block = context()->get_instr_block(store_inst);
   DominatorAnalysis* dominator_analysis =
       context()->GetDominatorAnalysis(store_block->GetParent());
@@ -229,16 +259,22 @@
   else
     stored_id = store_inst->GetSingleWordInOperand(kVariableInitIdInIdx);
 
-  std::vector<Instruction*> uses_in_store_block;
+  *all_rewritten = true;
   bool modified = false;
   for (Instruction* use : uses) {
-    if (use->opcode() == SpvOpLoad) {
-      if (dominator_analysis->Dominates(store_inst, use)) {
-        modified = true;
-        context()->KillNamesAndDecorates(use->result_id());
-        context()->ReplaceAllUsesWith(use->result_id(), stored_id);
-        context()->KillInst(use);
-      }
+    if (use->opcode() == SpvOpStore) continue;
+    auto dbg_op = use->GetOpenCL100DebugOpcode();
+    if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
+        dbg_op == OpenCLDebugInfo100DebugValue)
+      continue;
+    if (use->opcode() == SpvOpLoad &&
+        dominator_analysis->Dominates(store_inst, use)) {
+      modified = true;
+      context()->KillNamesAndDecorates(use->result_id());
+      context()->ReplaceAllUsesWith(use->result_id(), stored_id);
+      context()->KillInst(use);
+    } else {
+      *all_rewritten = false;
     }
   }
 
diff --git a/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.h b/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.h
index 4cf8bbb..d66a441 100644
--- a/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.h
@@ -57,8 +57,8 @@
   // any resulting dead code.
   bool LocalSingleStoreElim(Function* func);
 
-  // Initialize extensions whitelist
-  void InitExtensionWhiteList();
+  // Initialize extensions allowlist
+  void InitExtensionAllowList();
 
   // Return true if all extensions in this module are allowed by this pass.
   bool AllExtensionsSupported() const;
@@ -89,12 +89,13 @@
   bool FeedsAStore(Instruction* inst) const;
 
   // Replaces all of the loads in |uses| by the value stored in |store_inst|.
-  // The load instructions are then killed.
+  // The load instructions are then killed. |all_rewritten| is true iff all
+  // uses have been rewritten.
   bool RewriteLoads(Instruction* store_inst,
-                    const std::vector<Instruction*>& uses);
+                    const std::vector<Instruction*>& uses, bool* all_rewritten);
 
   // Extensions supported by this pass.
-  std::unordered_set<std::string> extensions_whitelist_;
+  std::unordered_set<std::string> extensions_allowlist_;
 };
 
 }  // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/loop_descriptor.cpp b/third_party/SPIRV-Tools/source/opt/loop_descriptor.cpp
index 11f7e9c..ed0dd28 100644
--- a/third_party/SPIRV-Tools/source/opt/loop_descriptor.cpp
+++ b/third_party/SPIRV-Tools/source/opt/loop_descriptor.cpp
@@ -485,10 +485,27 @@
 
   if (include_pre_header && GetPreHeaderBlock())
     ordered_loop_blocks->push_back(loop_preheader_);
-  cfg.ForEachBlockInReversePostOrder(
-      loop_header_, [ordered_loop_blocks, this](BasicBlock* bb) {
-        if (IsInsideLoop(bb)) ordered_loop_blocks->push_back(bb);
-      });
+
+  bool is_shader =
+      context_->get_feature_mgr()->HasCapability(SpvCapabilityShader);
+  if (!is_shader) {
+    cfg.ForEachBlockInReversePostOrder(
+        loop_header_, [ordered_loop_blocks, this](BasicBlock* bb) {
+          if (IsInsideLoop(bb)) ordered_loop_blocks->push_back(bb);
+        });
+  } else {
+    // If this is a shader, it is possible that there are unreachable merge and
+    // continue blocks that must be copied to retain the structured order.
+    // The structured order will include these.
+    std::list<BasicBlock*> order;
+    cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_, &order);
+    for (BasicBlock* bb : order) {
+      if (bb == GetMergeBlock()) {
+        break;
+      }
+      ordered_loop_blocks->push_back(bb);
+    }
+  }
   if (include_merge && GetMergeBlock())
     ordered_loop_blocks->push_back(loop_merge_);
 }
diff --git a/third_party/SPIRV-Tools/source/opt/loop_unroller.cpp b/third_party/SPIRV-Tools/source/opt/loop_unroller.cpp
index 10fac04..40cf6bc 100644
--- a/third_party/SPIRV-Tools/source/opt/loop_unroller.cpp
+++ b/third_party/SPIRV-Tools/source/opt/loop_unroller.cpp
@@ -997,7 +997,8 @@
     const BasicBlock* block = context_->cfg()->block(label_id);
     if (block->ctail()->opcode() == SpvOp::SpvOpKill ||
         block->ctail()->opcode() == SpvOp::SpvOpReturn ||
-        block->ctail()->opcode() == SpvOp::SpvOpReturnValue) {
+        block->ctail()->opcode() == SpvOp::SpvOpReturnValue ||
+        block->ctail()->opcode() == SpvOp::SpvOpTerminateInvocation) {
       return false;
     }
   }
diff --git a/third_party/SPIRV-Tools/source/opt/mem_pass.cpp b/third_party/SPIRV-Tools/source/opt/mem_pass.cpp
index d23d679..5738798 100644
--- a/third_party/SPIRV-Tools/source/opt/mem_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/mem_pass.cpp
@@ -20,6 +20,7 @@
 #include <set>
 #include <vector>
 
+#include "OpenCLDebugInfo100.h"
 #include "source/cfa.h"
 #include "source/opt/basic_block.h"
 #include "source/opt/dominator_analysis.h"
@@ -225,6 +226,11 @@
 
 bool MemPass::HasOnlySupportedRefs(uint32_t varId) {
   return get_def_use_mgr()->WhileEachUser(varId, [this](Instruction* user) {
+    auto dbg_op = user->GetOpenCL100DebugOpcode();
+    if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
+        dbg_op == OpenCLDebugInfo100DebugValue) {
+      return true;
+    }
     SpvOp op = user->opcode();
     if (op != SpvOpStore && op != SpvOpLoad && op != SpvOpName &&
         !IsNonTypeDecorate(op)) {
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 8cb4299..2421c2c 100644
--- a/third_party/SPIRV-Tools/source/opt/merge_return_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/merge_return_pass.cpp
@@ -299,9 +299,6 @@
 
     // There is at least one values that needs to be replaced.
     // First create the OpPhi instruction.
-    InstructionBuilder builder(
-        context(), &*merge_block->begin(),
-        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
     uint32_t undef_id = Type2Undef(inst.type_id());
     std::vector<uint32_t> phi_operands;
     const std::set<uint32_t>& new_edges = new_edges_[merge_block];
@@ -318,7 +315,50 @@
       phi_operands.push_back(pred_id);
     }
 
-    Instruction* new_phi = builder.AddPhi(inst.type_id(), phi_operands);
+    Instruction* new_phi = nullptr;
+    // If the instruction is a pointer and variable pointers are not an option,
+    // then we have to regenerate the instruction instead of creating an OpPhi
+    // instruction.  If not, the Spir-V will be invalid.
+    Instruction* inst_type = get_def_use_mgr()->GetDef(inst.type_id());
+    bool regenerateInstruction = false;
+    if (inst_type->opcode() == SpvOpTypePointer) {
+      if (!context()->get_feature_mgr()->HasCapability(
+              SpvCapabilityVariablePointers)) {
+        regenerateInstruction = true;
+      }
+
+      uint32_t storage_class = inst_type->GetSingleWordInOperand(0);
+      if (storage_class != SpvStorageClassWorkgroup &&
+          storage_class != SpvStorageClassStorageBuffer) {
+        regenerateInstruction = true;
+      }
+    }
+
+    if (regenerateInstruction) {
+      std::unique_ptr<Instruction> regen_inst(inst.Clone(context()));
+      uint32_t new_id = TakeNextId();
+      regen_inst->SetResultId(new_id);
+      Instruction* insert_pos = &*merge_block->begin();
+      while (insert_pos->opcode() == SpvOpPhi) {
+        insert_pos = insert_pos->NextNode();
+      }
+      new_phi = insert_pos->InsertBefore(std::move(regen_inst));
+      get_def_use_mgr()->AnalyzeInstDefUse(new_phi);
+      context()->set_instr_block(new_phi, merge_block);
+
+      new_phi->ForEachInId([dom_tree, merge_block, this](uint32_t* use_id) {
+        Instruction* use = get_def_use_mgr()->GetDef(*use_id);
+        BasicBlock* use_bb = context()->get_instr_block(use);
+        if (use_bb != nullptr && !dom_tree->Dominates(use_bb, merge_block)) {
+          CreatePhiNodesForInst(merge_block, *use);
+        }
+      });
+    } else {
+      InstructionBuilder builder(
+          context(), &*merge_block->begin(),
+          IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+      new_phi = builder.AddPhi(inst.type_id(), phi_operands);
+    }
     uint32_t result_of_phi = new_phi->result_id();
 
     // Update all of the users to use the result of the new OpPhi.
diff --git a/third_party/SPIRV-Tools/source/opt/private_to_local_pass.cpp b/third_party/SPIRV-Tools/source/opt/private_to_local_pass.cpp
index 6df690d..dd6cbbd 100644
--- a/third_party/SPIRV-Tools/source/opt/private_to_local_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/private_to_local_pass.cpp
@@ -138,7 +138,7 @@
   function->begin()->begin()->InsertBefore(move(var));
 
   // Update uses where the type may have changed.
-  return UpdateUses(variable->result_id());
+  return UpdateUses(variable);
 }
 
 uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) {
@@ -157,6 +157,10 @@
 bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const {
   // The cases in this switch have to match the cases in |UpdateUse|.
   // If we don't know how to update it, it is not valid.
+  if (inst->GetOpenCL100DebugOpcode() ==
+      OpenCLDebugInfo100DebugGlobalVariable) {
+    return true;
+  }
   switch (inst->opcode()) {
     case SpvOpLoad:
     case SpvOpStore:
@@ -175,10 +179,16 @@
   }
 }
 
-bool PrivateToLocalPass::UpdateUse(Instruction* inst) {
+bool PrivateToLocalPass::UpdateUse(Instruction* inst, Instruction* user) {
   // The cases in this switch have to match the cases in |IsValidUse|.  If we
   // don't think it is valid, the optimization will not view the variable as a
   // candidate, and therefore the use will not be updated.
+  if (inst->GetOpenCL100DebugOpcode() ==
+      OpenCLDebugInfo100DebugGlobalVariable) {
+    context()->get_debug_info_mgr()->ConvertDebugGlobalToLocalVariable(inst,
+                                                                       user);
+    return true;
+  }
   switch (inst->opcode()) {
     case SpvOpLoad:
     case SpvOpStore:
@@ -196,7 +206,7 @@
       context()->AnalyzeUses(inst);
 
       // Update uses where the type may have changed.
-      if (!UpdateUses(inst->result_id())) {
+      if (!UpdateUses(inst)) {
         return false;
       }
     } break;
@@ -211,13 +221,14 @@
   return true;
 }
 
-bool PrivateToLocalPass::UpdateUses(uint32_t id) {
+bool PrivateToLocalPass::UpdateUses(Instruction* inst) {
+  uint32_t id = inst->result_id();
   std::vector<Instruction*> uses;
   context()->get_def_use_mgr()->ForEachUser(
       id, [&uses](Instruction* use) { uses.push_back(use); });
 
   for (Instruction* use : uses) {
-    if (!UpdateUse(use)) {
+    if (!UpdateUse(use, inst)) {
       return false;
     }
   }
diff --git a/third_party/SPIRV-Tools/source/opt/private_to_local_pass.h b/third_party/SPIRV-Tools/source/opt/private_to_local_pass.h
index 3f9135c0..c6127d6 100644
--- a/third_party/SPIRV-Tools/source/opt/private_to_local_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/private_to_local_pass.h
@@ -63,8 +63,8 @@
 
   // Updates |inst|, and any instruction dependent on |inst|, to reflect the
   // change of the base pointer now pointing to the function storage class.
-  bool UpdateUse(Instruction* inst);
-  bool UpdateUses(uint32_t id);
+  bool UpdateUse(Instruction* inst, Instruction* user);
+  bool UpdateUses(Instruction* inst);
 };
 
 }  // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/reduce_load_size.cpp b/third_party/SPIRV-Tools/source/opt/reduce_load_size.cpp
index 7b5a015..c58adf4 100644
--- a/third_party/SPIRV-Tools/source/opt/reduce_load_size.cpp
+++ b/third_party/SPIRV-Tools/source/opt/reduce_load_size.cpp
@@ -112,10 +112,10 @@
   Instruction* new_access_chain = ir_builder.AddAccessChain(
       pointer_to_result_type_id,
       composite_inst->GetSingleWordInOperand(kLoadPointerInIdx), ids);
-  Instruction* new_laod =
+  Instruction* new_load =
       ir_builder.AddLoad(inst->type_id(), new_access_chain->result_id());
 
-  context()->ReplaceAllUsesWith(inst->result_id(), new_laod->result_id());
+  context()->ReplaceAllUsesWith(inst->result_id(), new_load->result_id());
   context()->KillInst(inst);
   return true;
 }
@@ -139,6 +139,7 @@
 
   all_elements_used =
       !def_use_mgr->WhileEachUser(op_inst, [&elements_used](Instruction* use) {
+        if (use->IsOpenCL100DebugInstr()) return true;
         if (use->opcode() != SpvOpCompositeExtract ||
             use->NumInOperands() == 1) {
           return false;
diff --git a/third_party/SPIRV-Tools/source/opt/reflect.h b/third_party/SPIRV-Tools/source/opt/reflect.h
index 51d23a7..2e253ad 100644
--- a/third_party/SPIRV-Tools/source/opt/reflect.h
+++ b/third_party/SPIRV-Tools/source/opt/reflect.h
@@ -60,7 +60,8 @@
   return opcode >= SpvOpSpecConstantTrue && opcode <= SpvOpSpecConstantOp;
 }
 inline bool IsTerminatorInst(SpvOp opcode) {
-  return opcode >= SpvOpBranch && opcode <= SpvOpUnreachable;
+  return (opcode >= SpvOpBranch && opcode <= SpvOpUnreachable) ||
+         (opcode == SpvOpTerminateInvocation);
 }
 
 }  // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/replace_invalid_opc.cpp b/third_party/SPIRV-Tools/source/opt/replace_invalid_opc.cpp
index 4e0f24f..38b7539 100644
--- a/third_party/SPIRV-Tools/source/opt/replace_invalid_opc.cpp
+++ b/third_party/SPIRV-Tools/source/opt/replace_invalid_opc.cpp
@@ -141,6 +141,7 @@
       // TODO: Teach |ReplaceInstruction| to handle block terminators.  Then
       // uncomment the OpKill case.
       // case SpvOpKill:
+      // case SpvOpTerminateInstruction:
       return true;
     default:
       return false;
diff --git a/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.cpp b/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.cpp
index 69c3a1f..1477db4 100644
--- a/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.cpp
@@ -307,6 +307,8 @@
   }
   if (pass_->IsTargetVar(var_id)) {
     WriteVariable(var_id, bb, val_id);
+    pass_->context()->get_debug_info_mgr()->AddDebugValue(inst, var_id, val_id,
+                                                          inst);
 
 #if SSA_REWRITE_DEBUGGING_LEVEL > 1
     std::cerr << "\tFound store '%" << var_id << " = %" << val_id << "': "
@@ -437,6 +439,8 @@
 
   // Add Phi instructions from completed Phi candidates.
   std::vector<Instruction*> generated_phis;
+  // Add DebugValue instructions for Phi instructions.
+  std::vector<Instruction*> dbg_values_for_phis;
   for (const PhiCandidate* phi_candidate : phis_to_generate_) {
 #if SSA_REWRITE_DEBUGGING_LEVEL > 2
     std::cerr << "Phi candidate: " << phi_candidate->PrettyPrint(pass_->cfg())
@@ -447,9 +451,10 @@
            "Tried to instantiate a Phi instruction from an incomplete Phi "
            "candidate");
 
+    auto* local_var = pass_->get_def_use_mgr()->GetDef(phi_candidate->var_id());
+
     // Build the vector of operands for the new OpPhi instruction.
-    uint32_t type_id = pass_->GetPointeeTypeId(
-        pass_->get_def_use_mgr()->GetDef(phi_candidate->var_id()));
+    uint32_t type_id = pass_->GetPointeeTypeId(local_var);
     std::vector<Operand> phi_operands;
     uint32_t arg_ix = 0;
     std::unordered_map<uint32_t, uint32_t> already_seen;
@@ -479,11 +484,17 @@
     pass_->get_def_use_mgr()->AnalyzeInstDef(&*phi_inst);
     pass_->context()->set_instr_block(&*phi_inst, phi_candidate->bb());
     auto insert_it = phi_candidate->bb()->begin();
-    insert_it.InsertBefore(std::move(phi_inst));
+    insert_it = insert_it.InsertBefore(std::move(phi_inst));
     pass_->context()->get_decoration_mgr()->CloneDecorations(
         phi_candidate->var_id(), phi_candidate->result_id(),
         {SpvDecorationRelaxedPrecision});
 
+    // Add DebugValue for the new OpPhi instruction.
+    insert_it->SetDebugScope(local_var->GetDebugScope());
+    pass_->context()->get_debug_info_mgr()->AddDebugValue(
+        &*insert_it, phi_candidate->var_id(), phi_candidate->result_id(),
+        &*insert_it);
+
     modified = true;
   }
 
@@ -604,6 +615,8 @@
             << fp->PrettyPrint(0) << "\n";
 #endif
 
+  if (modified) pass_->context()->KillDebugDeclareInsts(fp);
+
   return modified ? Pass::Status::SuccessWithChange
                   : Pass::Status::SuccessWithoutChange;
 }
diff --git a/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.h b/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.h
index bbe89c8..bbbfebb 100644
--- a/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.h
@@ -39,8 +39,7 @@
 // (https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6)
 class SSARewriter {
  public:
-  SSARewriter(MemPass* pass)
-      : pass_(pass), first_phi_id_(pass_->get_module()->IdBound()) {}
+  SSARewriter(MemPass* pass) : pass_(pass) {}
 
   // Rewrites SSA-target variables in function |fp| into SSA.  This is the
   // entry point for the SSA rewrite algorithm.  SSA-target variables are
@@ -287,10 +286,6 @@
 
   // Memory pass requesting the SSA rewriter.
   MemPass* pass_;
-
-  // ID of the first Phi created by the SSA rewriter.  During rewriting, any
-  // ID bigger than this corresponds to a Phi candidate.
-  uint32_t first_phi_id_;
 };
 
 class SSARewritePass : public MemPass {
diff --git a/third_party/SPIRV-Tools/source/opt/vector_dce.cpp b/third_party/SPIRV-Tools/source/opt/vector_dce.cpp
index 92532e3..14a55c1 100644
--- a/third_party/SPIRV-Tools/source/opt/vector_dce.cpp
+++ b/third_party/SPIRV-Tools/source/opt/vector_dce.cpp
@@ -52,6 +52,9 @@
   // components are live because of arbitrary nesting of structs.
   function->ForEachInst(
       [&work_list, this, live_components](Instruction* current_inst) {
+        if (current_inst->IsOpenCL100DebugInstr()) {
+          return;
+        }
         if (!HasVectorOrScalarResult(current_inst) ||
             !context()->IsCombinatorInstruction(current_inst)) {
           MarkUsesAsLive(current_inst, all_components_live_, live_components,
@@ -297,51 +300,60 @@
 bool VectorDCE::RewriteInstructions(
     Function* function, const VectorDCE::LiveComponentMap& live_components) {
   bool modified = false;
-  function->ForEachInst(
-      [&modified, this, live_components](Instruction* current_inst) {
-        if (!context()->IsCombinatorInstruction(current_inst)) {
-          return;
-        }
 
-        auto live_component = live_components.find(current_inst->result_id());
-        if (live_component == live_components.end()) {
-          // If this instruction is not in live_components then it does not
-          // produce a vector, or it is never referenced and ADCE will remove
-          // it.  No point in trying to differentiate.
-          return;
-        }
+  // Kill DebugValue in the middle of the instruction iteration will result
+  // in accessing a dangling pointer. We keep dead DebugValue instructions
+  // in |dead_dbg_value| to kill them once after the iteration.
+  std::vector<Instruction*> dead_dbg_value;
 
-        // If no element in the current instruction is used replace it with an
-        // OpUndef.
-        if (live_component->second.Empty()) {
-          modified = true;
-          uint32_t undef_id = this->Type2Undef(current_inst->type_id());
-          context()->KillNamesAndDecorates(current_inst);
-          context()->ReplaceAllUsesWith(current_inst->result_id(), undef_id);
-          context()->KillInst(current_inst);
-          return;
-        }
+  function->ForEachInst([&modified, this, live_components,
+                         &dead_dbg_value](Instruction* current_inst) {
+    if (!context()->IsCombinatorInstruction(current_inst)) {
+      return;
+    }
 
-        switch (current_inst->opcode()) {
-          case SpvOpCompositeInsert:
-            modified |=
-                RewriteInsertInstruction(current_inst, live_component->second);
-            break;
-          case SpvOpCompositeConstruct:
-            // TODO: The members that are not live can be replaced by an undef
-            // or constant. This will remove uses of those values, and possibly
-            // create opportunities for ADCE.
-            break;
-          default:
-            // Do nothing.
-            break;
-        }
-      });
+    auto live_component = live_components.find(current_inst->result_id());
+    if (live_component == live_components.end()) {
+      // If this instruction is not in live_components then it does not
+      // produce a vector, or it is never referenced and ADCE will remove
+      // it.  No point in trying to differentiate.
+      return;
+    }
+
+    // If no element in the current instruction is used replace it with an
+    // OpUndef.
+    if (live_component->second.Empty()) {
+      modified = true;
+      MarkDebugValueUsesAsDead(current_inst, &dead_dbg_value);
+      uint32_t undef_id = this->Type2Undef(current_inst->type_id());
+      context()->KillNamesAndDecorates(current_inst);
+      context()->ReplaceAllUsesWith(current_inst->result_id(), undef_id);
+      context()->KillInst(current_inst);
+      return;
+    }
+
+    switch (current_inst->opcode()) {
+      case SpvOpCompositeInsert:
+        modified |= RewriteInsertInstruction(
+            current_inst, live_component->second, &dead_dbg_value);
+        break;
+      case SpvOpCompositeConstruct:
+        // TODO: The members that are not live can be replaced by an undef
+        // or constant. This will remove uses of those values, and possibly
+        // create opportunities for ADCE.
+        break;
+      default:
+        // Do nothing.
+        break;
+    }
+  });
+  for (auto* i : dead_dbg_value) context()->KillInst(i);
   return modified;
 }
 
 bool VectorDCE::RewriteInsertInstruction(
-    Instruction* current_inst, const utils::BitVector& live_components) {
+    Instruction* current_inst, const utils::BitVector& live_components,
+    std::vector<Instruction*>* dead_dbg_value) {
   // If the value being inserted is not live, then we can skip the insert.
 
   if (current_inst->NumInOperands() == 2) {
@@ -355,6 +367,7 @@
 
   uint32_t insert_index = current_inst->GetSingleWordInOperand(2);
   if (!live_components.Get(insert_index)) {
+    MarkDebugValueUsesAsDead(current_inst, dead_dbg_value);
     context()->KillNamesAndDecorates(current_inst->result_id());
     uint32_t composite_id =
         current_inst->GetSingleWordInOperand(kInsertCompositeIdInIdx);
@@ -377,6 +390,15 @@
   return false;
 }
 
+void VectorDCE::MarkDebugValueUsesAsDead(
+    Instruction* composite, std::vector<Instruction*>* dead_dbg_value) {
+  context()->get_def_use_mgr()->ForEachUser(
+      composite, [&dead_dbg_value](Instruction* use) {
+        if (use->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue)
+          dead_dbg_value->push_back(use);
+      });
+}
+
 void VectorDCE::AddItemToWorkListIfNeeded(
     WorkListItem work_item, VectorDCE::LiveComponentMap* live_components,
     std::vector<WorkListItem>* work_list) {
diff --git a/third_party/SPIRV-Tools/source/opt/vector_dce.h b/third_party/SPIRV-Tools/source/opt/vector_dce.h
index 4f039c5..0df9aee 100644
--- a/third_party/SPIRV-Tools/source/opt/vector_dce.h
+++ b/third_party/SPIRV-Tools/source/opt/vector_dce.h
@@ -73,6 +73,11 @@
   bool RewriteInstructions(Function* function,
                            const LiveComponentMap& live_components);
 
+  // Makrs all DebugValue instructions that use |composite| for their values as
+  // dead instructions by putting them into |dead_dbg_value|.
+  void MarkDebugValueUsesAsDead(Instruction* composite,
+                                std::vector<Instruction*>* dead_dbg_value);
+
   // Rewrites the OpCompositeInsert instruction |current_inst| to avoid
   // unnecessary computes given that the only components of the result that are
   // live are |live_components|.
@@ -83,7 +88,8 @@
   // If the composite input to |current_inst| is not live, then it is replaced
   // by and OpUndef in |current_inst|.
   bool RewriteInsertInstruction(Instruction* current_inst,
-                                const utils::BitVector& live_components);
+                                const utils::BitVector& live_components,
+                                std::vector<Instruction*>* dead_dbg_value);
 
   // Returns true if the result of |inst| is a vector or a scalar.
   bool HasVectorOrScalarResult(const Instruction* inst) const;
diff --git a/third_party/SPIRV-Tools/source/opt/wrap_opkill.cpp b/third_party/SPIRV-Tools/source/opt/wrap_opkill.cpp
index 3c8bae6..4d70840 100644
--- a/third_party/SPIRV-Tools/source/opt/wrap_opkill.cpp
+++ b/third_party/SPIRV-Tools/source/opt/wrap_opkill.cpp
@@ -27,7 +27,8 @@
   for (uint32_t func_id : func_to_process) {
     Function* func = context()->GetFunction(func_id);
     bool successful = func->WhileEachInst([this, &modified](Instruction* inst) {
-      if (inst->opcode() == SpvOpKill) {
+      const auto opcode = inst->opcode();
+      if ((opcode == SpvOpKill) || (opcode == SpvOpTerminateInvocation)) {
         modified = true;
         if (!ReplaceWithFunctionCall(inst)) {
           return false;
@@ -46,16 +47,22 @@
            "The function should only be generated if something was modified.");
     context()->AddFunction(std::move(opkill_function_));
   }
+  if (opterminateinvocation_function_ != nullptr) {
+    assert(modified &&
+           "The function should only be generated if something was modified.");
+    context()->AddFunction(std::move(opterminateinvocation_function_));
+  }
   return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
 }
 
 bool WrapOpKill::ReplaceWithFunctionCall(Instruction* inst) {
-  assert(inst->opcode() == SpvOpKill &&
-         "|inst| must be an OpKill instruction.");
+  assert((inst->opcode() == SpvOpKill ||
+          inst->opcode() == SpvOpTerminateInvocation) &&
+         "|inst| must be an OpKill or OpTerminateInvocation instruction.");
   InstructionBuilder ir_builder(
       context(), inst,
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-  uint32_t func_id = GetOpKillFuncId();
+  uint32_t func_id = GetKillingFuncId(inst->opcode());
   if (func_id == 0) {
     return false;
   }
@@ -108,13 +115,20 @@
   return type_mgr->GetTypeInstruction(&func_type);
 }
 
-uint32_t WrapOpKill::GetOpKillFuncId() {
-  if (opkill_function_ != nullptr) {
-    return opkill_function_->result_id();
+uint32_t WrapOpKill::GetKillingFuncId(SpvOp opcode) {
+  //  Parameterize by opcode
+  assert(opcode == SpvOpKill || opcode == SpvOpTerminateInvocation);
+
+  std::unique_ptr<Function>* const killing_func =
+      (opcode == SpvOpKill) ? &opkill_function_
+                            : &opterminateinvocation_function_;
+
+  if (*killing_func != nullptr) {
+    return (*killing_func)->result_id();
   }
 
-  uint32_t opkill_func_id = TakeNextId();
-  if (opkill_func_id == 0) {
+  uint32_t killing_func_id = TakeNextId();
+  if (killing_func_id == 0) {
     return 0;
   }
 
@@ -125,15 +139,15 @@
 
   // Generate the function start instruction
   std::unique_ptr<Instruction> func_start(new Instruction(
-      context(), SpvOpFunction, void_type_id, opkill_func_id, {}));
+      context(), SpvOpFunction, void_type_id, killing_func_id, {}));
   func_start->AddOperand({SPV_OPERAND_TYPE_FUNCTION_CONTROL, {0}});
   func_start->AddOperand({SPV_OPERAND_TYPE_ID, {GetVoidFunctionTypeId()}});
-  opkill_function_.reset(new Function(std::move(func_start)));
+  (*killing_func).reset(new Function(std::move(func_start)));
 
   // Generate the function end instruction
   std::unique_ptr<Instruction> func_end(
       new Instruction(context(), SpvOpFunctionEnd, 0, 0, {}));
-  opkill_function_->SetFunctionEnd(std::move(func_end));
+  (*killing_func)->SetFunctionEnd(std::move(func_end));
 
   // Create the one basic block for the function.
   uint32_t lab_id = TakeNextId();
@@ -146,21 +160,22 @@
 
   // Add the OpKill to the basic block
   std::unique_ptr<Instruction> kill_inst(
-      new Instruction(context(), SpvOpKill, 0, 0, {}));
+      new Instruction(context(), opcode, 0, 0, {}));
   bb->AddInstruction(std::move(kill_inst));
 
   // Add the bb to the function
-  bb->SetParent(opkill_function_.get());
-  opkill_function_->AddBasicBlock(std::move(bb));
+  bb->SetParent((*killing_func).get());
+  (*killing_func)->AddBasicBlock(std::move(bb));
 
   // Add the function to the module.
   if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) {
-    opkill_function_->ForEachInst(
-        [this](Instruction* inst) { context()->AnalyzeDefUse(inst); });
+    (*killing_func)->ForEachInst([this](Instruction* inst) {
+      context()->AnalyzeDefUse(inst);
+    });
   }
 
   if (context()->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) {
-    for (BasicBlock& basic_block : *opkill_function_) {
+    for (BasicBlock& basic_block : *(*killing_func)) {
       context()->set_instr_block(basic_block.GetLabelInst(), &basic_block);
       for (Instruction& inst : basic_block) {
         context()->set_instr_block(&inst, &basic_block);
@@ -168,7 +183,7 @@
     }
   }
 
-  return opkill_function_->result_id();
+  return (*killing_func)->result_id();
 }
 
 uint32_t WrapOpKill::GetOwningFunctionsReturnType(Instruction* inst) {
diff --git a/third_party/SPIRV-Tools/source/opt/wrap_opkill.h b/third_party/SPIRV-Tools/source/opt/wrap_opkill.h
index 09f2dfa..7e43ca6 100644
--- a/third_party/SPIRV-Tools/source/opt/wrap_opkill.h
+++ b/third_party/SPIRV-Tools/source/opt/wrap_opkill.h
@@ -38,10 +38,10 @@
   }
 
  private:
-  // Replaces the OpKill instruction |inst| with a function call to a function
-  // that contains a single instruction, which is OpKill.  An OpUnreachable
-  // instruction will be placed after the function call.  Return true if
-  // successful.
+  // Replaces the OpKill or OpTerminateInvocation instruction |inst| with a
+  // function call to a function that contains a single instruction, a clone of
+  // |inst|.  An OpUnreachable instruction will be placed after the function
+  // call. Return true if successful.
   bool ReplaceWithFunctionCall(Instruction* inst);
 
   // Returns the id of the void type.
@@ -51,9 +51,9 @@
   uint32_t GetVoidFunctionTypeId();
 
   // Return the id of a function that has return type void, has no parameters,
-  // and contains a single instruction, which is an OpKill.  Returns 0 if the
-  // function could not be generated.
-  uint32_t GetOpKillFuncId();
+  // and contains a single instruction, which is |opcode|, either OpKill or
+  // OpTerminateInvocation.  Returns 0 if the function could not be generated.
+  uint32_t GetKillingFuncId(SpvOp opcode);
 
   // Returns the id of the return type for the function that contains |inst|.
   // Returns 0 if |inst| is not in a function.
@@ -67,6 +67,11 @@
   // function has a void return type and takes no parameters. If the function is
   // |nullptr|, then the function has not been generated.
   std::unique_ptr<Function> opkill_function_;
+  // The function that is a single instruction, which is an
+  // OpTerminateInvocation. The function has a void return type and takes no
+  // parameters. If the function is |nullptr|, then the function has not been
+  // generated.
+  std::unique_ptr<Function> opterminateinvocation_function_;
 };
 
 }  // namespace opt
diff --git a/third_party/SPIRV-Tools/source/spirv_fuzzer_options.cpp b/third_party/SPIRV-Tools/source/spirv_fuzzer_options.cpp
index b407f14..64fefbc 100644
--- a/third_party/SPIRV-Tools/source/spirv_fuzzer_options.cpp
+++ b/third_party/SPIRV-Tools/source/spirv_fuzzer_options.cpp
@@ -22,6 +22,7 @@
 spv_fuzzer_options_t::spv_fuzzer_options_t()
     : has_random_seed(false),
       random_seed(0),
+      replay_range(0),
       replay_validation_enabled(false),
       shrinker_step_limit(kDefaultStepLimit),
       fuzzer_pass_validation_enabled(false) {}
@@ -45,6 +46,11 @@
   options->random_seed = seed;
 }
 
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetReplayRange(
+    spv_fuzzer_options options, int32_t replay_range) {
+  options->replay_range = replay_range;
+}
+
 SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(
     spv_fuzzer_options options, uint32_t shrinker_step_limit) {
   options->shrinker_step_limit = shrinker_step_limit;
diff --git a/third_party/SPIRV-Tools/source/spirv_fuzzer_options.h b/third_party/SPIRV-Tools/source/spirv_fuzzer_options.h
index 143f77f..0db16a3 100644
--- a/third_party/SPIRV-Tools/source/spirv_fuzzer_options.h
+++ b/third_party/SPIRV-Tools/source/spirv_fuzzer_options.h
@@ -29,6 +29,9 @@
   bool has_random_seed;
   uint32_t random_seed;
 
+  // See spvFuzzerOptionsSetReplayRange.
+  int32_t replay_range;
+
   // See spvFuzzerOptionsEnableReplayValidation.
   bool replay_validation_enabled;
 
diff --git a/third_party/SPIRV-Tools/source/val/basic_block.cpp b/third_party/SPIRV-Tools/source/val/basic_block.cpp
index a53103c..b2a8793 100644
--- a/third_party/SPIRV-Tools/source/val/basic_block.cpp
+++ b/third_party/SPIRV-Tools/source/val/basic_block.cpp
@@ -58,15 +58,9 @@
   for (auto& block : next_blocks) {
     block->predecessors_.push_back(this);
     successors_.push_back(block);
-    if (block->reachable_ == false) block->set_reachable(reachable_);
   }
 }
 
-void BasicBlock::RegisterBranchInstruction(SpvOp branch_instruction) {
-  if (branch_instruction == SpvOpUnreachable) reachable_ = false;
-  return;
-}
-
 bool BasicBlock::dominates(const BasicBlock& other) const {
   return (this == &other) ||
          !(other.dom_end() ==
diff --git a/third_party/SPIRV-Tools/source/val/basic_block.h b/third_party/SPIRV-Tools/source/val/basic_block.h
index 876105c..5eea4f9 100644
--- a/third_party/SPIRV-Tools/source/val/basic_block.h
+++ b/third_party/SPIRV-Tools/source/val/basic_block.h
@@ -106,9 +106,6 @@
   /// Returns the immedate post dominator of this basic block
   const BasicBlock* immediate_post_dominator() const;
 
-  /// Ends the block without a successor
-  void RegisterBranchInstruction(SpvOp branch_instruction);
-
   /// Returns the label instruction for the block, or nullptr if not set.
   const Instruction* label() const { return label_; }
 
diff --git a/third_party/SPIRV-Tools/source/val/function.cpp b/third_party/SPIRV-Tools/source/val/function.cpp
index 0281770..249c866 100644
--- a/third_party/SPIRV-Tools/source/val/function.cpp
+++ b/third_party/SPIRV-Tools/source/val/function.cpp
@@ -130,7 +130,6 @@
     undefined_blocks_.erase(block_id);
     current_block_ = &inserted_block->second;
     ordered_blocks_.push_back(current_block_);
-    if (IsFirstBlock(block_id)) current_block_->set_reachable(true);
   } else if (success) {  // Block doesn't exsist but this is not a definition
     undefined_blocks_.insert(block_id);
   }
@@ -138,8 +137,7 @@
   return SPV_SUCCESS;
 }
 
-void Function::RegisterBlockEnd(std::vector<uint32_t> next_list,
-                                SpvOp branch_instruction) {
+void Function::RegisterBlockEnd(std::vector<uint32_t> next_list) {
   assert(
       current_block_ &&
       "RegisterBlockEnd can only be called when parsing a binary in a block");
@@ -174,7 +172,6 @@
     }
   }
 
-  current_block_->RegisterBranchInstruction(branch_instruction);
   current_block_->RegisterSuccessors(next_blocks);
   current_block_ = nullptr;
   return;
diff --git a/third_party/SPIRV-Tools/source/val/function.h b/third_party/SPIRV-Tools/source/val/function.h
index 0d6873d..400bb63 100644
--- a/third_party/SPIRV-Tools/source/val/function.h
+++ b/third_party/SPIRV-Tools/source/val/function.h
@@ -97,9 +97,7 @@
   /// Registers the end of the block
   ///
   /// @param[in] successors_list A list of ids to the block's successors
-  /// @param[in] branch_instruction the branch instruction that ended the block
-  void RegisterBlockEnd(std::vector<uint32_t> successors_list,
-                        SpvOp branch_instruction);
+  void RegisterBlockEnd(std::vector<uint32_t> successors_list);
 
   /// Registers the end of the function.  This is idempotent.
   void RegisterFunctionEnd();
diff --git a/third_party/SPIRV-Tools/source/val/validate.cpp b/third_party/SPIRV-Tools/source/val/validate.cpp
index 168968d..f964b9b 100644
--- a/third_party/SPIRV-Tools/source/val/validate.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate.cpp
@@ -368,6 +368,10 @@
   // Catch undefined forward references before performing further checks.
   if (auto error = ValidateForwardDecls(*vstate)) return error;
 
+  // Calculate reachability after all the blocks are parsed, but early that it
+  // can be relied on in subsequent pases.
+  ReachabilityPass(*vstate);
+
   // ID usage needs be handled in its own iteration of the instructions,
   // between the two others. It depends on the first loop to have been
   // finished, so that all instructions have been registered. And the following
diff --git a/third_party/SPIRV-Tools/source/val/validate.h b/third_party/SPIRV-Tools/source/val/validate.h
index 31a775b..3fc183d 100644
--- a/third_party/SPIRV-Tools/source/val/validate.h
+++ b/third_party/SPIRV-Tools/source/val/validate.h
@@ -197,6 +197,9 @@
 /// Validates correctness of miscellaneous instructions.
 spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst);
 
+/// Calculates the reachability of basic blocks.
+void ReachabilityPass(ValidationState_t& _);
+
 /// Validates execution limitations.
 ///
 /// Verifies execution models are allowed for all functionality they contain.
diff --git a/third_party/SPIRV-Tools/source/val/validate_atomics.cpp b/third_party/SPIRV-Tools/source/val/validate_atomics.cpp
index b8867dd..df7973f 100644
--- a/third_party/SPIRV-Tools/source/val/validate_atomics.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_atomics.cpp
@@ -54,11 +54,16 @@
 spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
   const SpvOp opcode = inst->opcode();
   const uint32_t result_type = inst->type_id();
-
+  bool is_atomic_float_opcode = false;
+  if (opcode == SpvOpAtomicLoad || opcode == SpvOpAtomicStore ||
+      opcode == SpvOpAtomicFAddEXT || opcode == SpvOpAtomicExchange) {
+    is_atomic_float_opcode = true;
+  }
   switch (opcode) {
     case SpvOpAtomicLoad:
     case SpvOpAtomicStore:
     case SpvOpAtomicExchange:
+    case SpvOpAtomicFAddEXT:
     case SpvOpAtomicCompareExchange:
     case SpvOpAtomicCompareExchangeWeak:
     case SpvOpAtomicIIncrement:
@@ -92,11 +97,59 @@
       } else if (opcode == SpvOpAtomicFlagClear || opcode == SpvOpAtomicStore) {
         assert(result_type == 0);
       } else {
-        if (!_.IsIntScalarType(result_type)) {
+        if (_.IsFloatScalarType(result_type)) {
+          if (is_atomic_float_opcode) {
+            if (opcode == SpvOpAtomicFAddEXT) {
+              if ((_.GetBitWidth(result_type) == 32) &&
+                  (!_.HasCapability(SpvCapabilityAtomicFloat32AddEXT))) {
+                return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                       << spvOpcodeString(opcode)
+                       << ": float add atomics require the AtomicFloat32AddEXT "
+                          "capability";
+              }
+              if ((_.GetBitWidth(result_type) == 64) &&
+                  (!_.HasCapability(SpvCapabilityAtomicFloat64AddEXT))) {
+                return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                       << spvOpcodeString(opcode)
+                       << ": float add atomics require the AtomicFloat64AddEXT "
+                          "capability";
+              }
+            }
+          } else {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << spvOpcodeString(opcode)
+                   << ": expected Result Type to be int scalar type";
+          }
+        } else if (_.IsIntScalarType(result_type) &&
+                   opcode == SpvOpAtomicFAddEXT) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
-                 << ": expected Result Type to be int scalar type";
+                 << ": expected Result Type to be float scalar type";
+        } else if (!_.IsFloatScalarType(result_type) &&
+                   !_.IsIntScalarType(result_type)) {
+          switch (opcode) {
+            case SpvOpAtomicFAddEXT:
+              return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                     << spvOpcodeString(opcode)
+                     << ": expected Result Type to be float scalar type";
+            case SpvOpAtomicIIncrement:
+            case SpvOpAtomicIDecrement:
+            case SpvOpAtomicIAdd:
+            case SpvOpAtomicISub:
+            case SpvOpAtomicSMin:
+            case SpvOpAtomicSMax:
+            case SpvOpAtomicUMin:
+            case SpvOpAtomicUMax:
+              return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                     << spvOpcodeString(opcode)
+                     << ": expected Result Type to be integer scalar type";
+            default:
+              return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                     << spvOpcodeString(opcode)
+                     << ": expected Result Type to be int or float scalar type";
+          }
         }
+
         if (spvIsVulkanEnv(_.context()->target_env) &&
             _.GetBitWidth(result_type) != 32) {
           switch (opcode) {
@@ -108,11 +161,17 @@
             case SpvOpAtomicOr:
             case SpvOpAtomicXor:
             case SpvOpAtomicIAdd:
+            case SpvOpAtomicISub:
+            case SpvOpAtomicFAddEXT:
             case SpvOpAtomicLoad:
             case SpvOpAtomicStore:
             case SpvOpAtomicExchange:
+            case SpvOpAtomicIIncrement:
+            case SpvOpAtomicIDecrement:
+            case SpvOpAtomicCompareExchangeWeak:
             case SpvOpAtomicCompareExchange: {
               if (_.GetBitWidth(result_type) == 64 &&
+                  _.IsIntScalarType(result_type) &&
                   !_.HasCapability(SpvCapabilityInt64Atomics))
                 return _.diag(SPV_ERROR_INVALID_DATA, inst)
                        << spvOpcodeString(opcode)
diff --git a/third_party/SPIRV-Tools/source/val/validate_cfg.cpp b/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
index 1e33e51..8eb3a96 100644
--- a/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
@@ -1062,7 +1062,7 @@
       uint32_t target = inst->GetOperandAs<uint32_t>(0);
       CFG_ASSERT(FirstBlockAssert, target);
 
-      _.current_function().RegisterBlockEnd({target}, opcode);
+      _.current_function().RegisterBlockEnd({target});
     } break;
     case SpvOpBranchConditional: {
       uint32_t tlabel = inst->GetOperandAs<uint32_t>(1);
@@ -1070,7 +1070,7 @@
       CFG_ASSERT(FirstBlockAssert, tlabel);
       CFG_ASSERT(FirstBlockAssert, flabel);
 
-      _.current_function().RegisterBlockEnd({tlabel, flabel}, opcode);
+      _.current_function().RegisterBlockEnd({tlabel, flabel});
     } break;
 
     case SpvOpSwitch: {
@@ -1080,7 +1080,7 @@
         CFG_ASSERT(FirstBlockAssert, target);
         cases.push_back(target);
       }
-      _.current_function().RegisterBlockEnd({cases}, opcode);
+      _.current_function().RegisterBlockEnd({cases});
     } break;
     case SpvOpReturn: {
       const uint32_t return_type = _.current_function().GetResultTypeId();
@@ -1090,18 +1090,24 @@
         return _.diag(SPV_ERROR_INVALID_CFG, inst)
                << "OpReturn can only be called from a function with void "
                << "return type.";
-      _.current_function().RegisterBlockEnd(std::vector<uint32_t>(), opcode);
+      _.current_function().RegisterBlockEnd(std::vector<uint32_t>());
       break;
     }
     case SpvOpKill:
     case SpvOpReturnValue:
     case SpvOpUnreachable:
-      _.current_function().RegisterBlockEnd(std::vector<uint32_t>(), opcode);
+    case SpvOpTerminateInvocation:
+      _.current_function().RegisterBlockEnd(std::vector<uint32_t>());
       if (opcode == SpvOpKill) {
         _.current_function().RegisterExecutionModelLimitation(
             SpvExecutionModelFragment,
             "OpKill requires Fragment execution model");
       }
+      if (opcode == SpvOpTerminateInvocation) {
+        _.current_function().RegisterExecutionModelLimitation(
+            SpvExecutionModelFragment,
+            "OpTerminateInvocation requires Fragment execution model");
+      }
       break;
     default:
       break;
@@ -1109,6 +1115,27 @@
   return SPV_SUCCESS;
 }
 
+void ReachabilityPass(ValidationState_t& _) {
+  for (auto& f : _.functions()) {
+    std::vector<BasicBlock*> stack;
+    auto entry = f.first_block();
+    // Skip function declarations.
+    if (entry) stack.push_back(entry);
+
+    while (!stack.empty()) {
+      auto block = stack.back();
+      stack.pop_back();
+
+      if (block->reachable()) continue;
+
+      block->set_reachable(true);
+      for (auto succ : *block->successors()) {
+        stack.push_back(succ);
+      }
+    }
+  }
+}
+
 spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) {
   switch (inst->opcode()) {
     case SpvOpPhi:
diff --git a/third_party/SPIRV-Tools/source/val/validate_decorations.cpp b/third_party/SPIRV-Tools/source/val/validate_decorations.cpp
index ce09e18..d381276 100644
--- a/third_party/SPIRV-Tools/source/val/validate_decorations.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_decorations.cpp
@@ -1540,6 +1540,21 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t CheckLocationDecoration(ValidationState_t& vstate,
+                                     const Instruction& inst,
+                                     const Decoration& decoration) {
+  if (inst.opcode() == SpvOpVariable) return SPV_SUCCESS;
+
+  if (decoration.struct_member_index() != Decoration::kInvalidMember &&
+      inst.opcode() == SpvOpTypeStruct) {
+    return SPV_SUCCESS;
+  }
+
+  return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+         << "Location decoration can only be applied to a variable or member "
+            "of a structure type";
+}
+
 #define PASS_OR_BAIL_AT_LINE(X, LINE)           \
   {                                             \
     spv_result_t e##LINE = (X);                 \
@@ -1590,6 +1605,9 @@
         case SpvDecorationBufferBlock:
           PASS_OR_BAIL(CheckBlockDecoration(vstate, *inst, decoration));
           break;
+        case SpvDecorationLocation:
+          PASS_OR_BAIL(CheckLocationDecoration(vstate, *inst, decoration));
+          break;
         default:
           break;
       }
diff --git a/third_party/SPIRV-Tools/source/val/validate_instruction.cpp b/third_party/SPIRV-Tools/source/val/validate_instruction.cpp
index 6478b3c..9d395fb 100644
--- a/third_party/SPIRV-Tools/source/val/validate_instruction.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_instruction.cpp
@@ -296,7 +296,12 @@
            << SPV_SPIRV_VERSION_MINOR_PART(last_version) << " or earlier";
   }
 
-  if (inst_desc->numCapabilities > 0u) {
+  // OpTerminateInvocation is special because it is enabled by Shader
+  // capability, but also requries a extension and/or version check.
+  const bool capability_check_is_sufficient =
+      inst->opcode() != SpvOpTerminateInvocation;
+
+  if (capability_check_is_sufficient && (inst_desc->numCapabilities > 0u)) {
     // We already checked that the direct capability dependency has been
     // satisfied. We don't need to check any further.
     return SPV_SUCCESS;
diff --git a/third_party/SPIRV-Tools/source/val/validate_interfaces.cpp b/third_party/SPIRV-Tools/source/val/validate_interfaces.cpp
index c85b673..833734f 100644
--- a/third_party/SPIRV-Tools/source/val/validate_interfaces.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_interfaces.cpp
@@ -102,6 +102,365 @@
   return SPV_SUCCESS;
 }
 
+// This function assumes a base location has been determined already. As such
+// any further location decorations are invalid.
+// TODO: if this code turns out to be slow, there is an opportunity to cache
+// the result for a given type id.
+spv_result_t NumConsumedLocations(ValidationState_t& _, const Instruction* type,
+                                  uint32_t* num_locations) {
+  *num_locations = 0;
+  switch (type->opcode()) {
+    case SpvOpTypeInt:
+    case SpvOpTypeFloat:
+      // Scalars always consume a single location.
+      *num_locations = 1;
+      break;
+    case SpvOpTypeVector:
+      // 3- and 4-component 64-bit vectors consume two locations.
+      if ((_.ContainsSizedIntOrFloatType(type->id(), SpvOpTypeInt, 64) ||
+           _.ContainsSizedIntOrFloatType(type->id(), SpvOpTypeFloat, 64)) &&
+          (type->GetOperandAs<uint32_t>(2) > 2)) {
+        *num_locations = 2;
+      } else {
+        *num_locations = 1;
+      }
+      break;
+    case SpvOpTypeMatrix:
+      // Matrices consume locations equal to the underlying vector type for
+      // each column.
+      NumConsumedLocations(_, _.FindDef(type->GetOperandAs<uint32_t>(1)),
+                           num_locations);
+      *num_locations *= type->GetOperandAs<uint32_t>(2);
+      break;
+    case SpvOpTypeArray: {
+      // Arrays consume locations equal to the underlying type times the number
+      // of elements in the vector.
+      NumConsumedLocations(_, _.FindDef(type->GetOperandAs<uint32_t>(1)),
+                           num_locations);
+      bool is_int = false;
+      bool is_const = false;
+      uint32_t value = 0;
+      // Attempt to evaluate the number of array elements.
+      std::tie(is_int, is_const, value) =
+          _.EvalInt32IfConst(type->GetOperandAs<uint32_t>(2));
+      if (is_int && is_const) *num_locations *= value;
+      break;
+    }
+    case SpvOpTypeStruct: {
+      // Members cannot have location decorations at this point.
+      if (_.HasDecoration(type->id(), SpvDecorationLocation)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, type)
+               << "Members cannot be assigned a location";
+      }
+
+      // Structs consume locations equal to the sum of the locations consumed
+      // by the members.
+      for (uint32_t i = 1; i < type->operands().size(); ++i) {
+        uint32_t member_locations = 0;
+        if (auto error = NumConsumedLocations(
+                _, _.FindDef(type->GetOperandAs<uint32_t>(i)),
+                &member_locations)) {
+          return error;
+        }
+        *num_locations += member_locations;
+      }
+      break;
+    }
+    default:
+      break;
+  }
+
+  return SPV_SUCCESS;
+}
+
+// Returns the number of components consumed by types that support a component
+// decoration.
+uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) {
+  uint32_t num_components = 0;
+  switch (type->opcode()) {
+    case SpvOpTypeInt:
+    case SpvOpTypeFloat:
+      // 64-bit types consume two components.
+      if (type->GetOperandAs<uint32_t>(1) == 64) {
+        num_components = 2;
+      } else {
+        num_components = 1;
+      }
+      break;
+    case SpvOpTypeVector:
+      // Vectors consume components equal to the underlying type's consumption
+      // times the number of elements in the vector. Note that 3- and 4-element
+      // vectors cannot have a component decoration (i.e. assumed to be zero).
+      num_components =
+          NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1)));
+      num_components *= type->GetOperandAs<uint32_t>(2);
+      break;
+    default:
+      // This is an error that is validated elsewhere.
+      break;
+  }
+
+  return num_components;
+}
+
+// Populates |locations| (and/or |output_index1_locations|) with the use
+// location and component coordinates for |variable|. Indices are calculated as
+// 4 * location + component.
+spv_result_t GetLocationsForVariable(
+    ValidationState_t& _, const Instruction* entry_point,
+    const Instruction* variable, std::unordered_set<uint32_t>* locations,
+    std::unordered_set<uint32_t>* output_index1_locations) {
+  const bool is_fragment = entry_point->GetOperandAs<SpvExecutionModel>(0) ==
+                           SpvExecutionModelFragment;
+  const bool is_output =
+      variable->GetOperandAs<SpvStorageClass>(2) == SpvStorageClassOutput;
+  auto ptr_type_id = variable->GetOperandAs<uint32_t>(0);
+  auto ptr_type = _.FindDef(ptr_type_id);
+  auto type_id = ptr_type->GetOperandAs<uint32_t>(2);
+  auto type = _.FindDef(type_id);
+
+  // Check for Location, Component and Index decorations on the variable. The
+  // validator allows duplicate decorations if the location/component/index are
+  // equal. Also track Patch and PerTaskNV decorations.
+  bool has_location = false;
+  uint32_t location = 0;
+  bool has_component = false;
+  uint32_t component = 0;
+  bool has_index = false;
+  uint32_t index = 0;
+  bool has_patch = false;
+  bool has_per_task_nv = false;
+  bool has_per_vertex_nv = false;
+  for (auto& dec : _.id_decorations(variable->id())) {
+    if (dec.dec_type() == SpvDecorationLocation) {
+      if (has_location && dec.params()[0] != location) {
+        return _.diag(SPV_ERROR_INVALID_DATA, variable)
+               << "Variable has conflicting location decorations";
+      }
+      has_location = true;
+      location = dec.params()[0];
+    } else if (dec.dec_type() == SpvDecorationComponent) {
+      if (has_component && dec.params()[0] != component) {
+        return _.diag(SPV_ERROR_INVALID_DATA, variable)
+               << "Variable has conflicting component decorations";
+      }
+      has_component = true;
+      component = dec.params()[0];
+    } else if (dec.dec_type() == SpvDecorationIndex) {
+      if (!is_output || !is_fragment) {
+        return _.diag(SPV_ERROR_INVALID_DATA, variable)
+               << "Index can only be applied to Fragment output variables";
+      }
+      if (has_index && dec.params()[0] != index) {
+        return _.diag(SPV_ERROR_INVALID_DATA, variable)
+               << "Variable has conflicting index decorations";
+      }
+      has_index = true;
+      index = dec.params()[0];
+    } else if (dec.dec_type() == SpvDecorationBuiltIn) {
+      // Don't check built-ins.
+      return SPV_SUCCESS;
+    } else if (dec.dec_type() == SpvDecorationPatch) {
+      has_patch = true;
+    } else if (dec.dec_type() == SpvDecorationPerTaskNV) {
+      has_per_task_nv = true;
+    } else if (dec.dec_type() == SpvDecorationPerVertexNV) {
+      has_per_vertex_nv = true;
+    }
+  }
+
+  // Vulkan 14.1.3: Tessellation control and mesh per-vertex outputs and
+  // tessellation control, evaluation and geometry per-vertex inputs have a
+  // layer of arraying that is not included in interface matching.
+  bool is_arrayed = false;
+  switch (entry_point->GetOperandAs<SpvExecutionModel>(0)) {
+    case SpvExecutionModelTessellationControl:
+      if (!has_patch) {
+        is_arrayed = true;
+      }
+      break;
+    case SpvExecutionModelTessellationEvaluation:
+      if (!is_output && !has_patch) {
+        is_arrayed = true;
+      }
+      break;
+    case SpvExecutionModelGeometry:
+      if (!is_output) {
+        is_arrayed = true;
+      }
+      break;
+    case SpvExecutionModelFragment:
+      if (!is_output && has_per_vertex_nv) {
+        is_arrayed = true;
+      }
+      break;
+    case SpvExecutionModelMeshNV:
+      if (is_output && !has_per_task_nv) {
+        is_arrayed = true;
+      }
+      break;
+    default:
+      break;
+  }
+
+  // Unpack arrayness.
+  if (is_arrayed && (type->opcode() == SpvOpTypeArray ||
+                     type->opcode() == SpvOpTypeRuntimeArray)) {
+    type_id = type->GetOperandAs<uint32_t>(1);
+    type = _.FindDef(type_id);
+  }
+
+  if (type->opcode() == SpvOpTypeStruct) {
+    // Don't check built-ins.
+    if (_.HasDecoration(type_id, SpvDecorationBuiltIn)) return SPV_SUCCESS;
+  }
+
+  // Only block-decorated structs don't need a location on the variable.
+  const bool is_block = _.HasDecoration(type_id, SpvDecorationBlock);
+  if (!has_location && !is_block) {
+    return _.diag(SPV_ERROR_INVALID_DATA, variable)
+           << "Variable must be decorated with a location";
+  }
+
+  const std::string storage_class = is_output ? "output" : "input";
+  if (has_location) {
+    auto sub_type = type;
+    bool is_int = false;
+    bool is_const = false;
+    uint32_t array_size = 1;
+    // If the variable is still arrayed, mark the locations/components per
+    // index.
+    if (type->opcode() == SpvOpTypeArray) {
+      // Determine the array size if possible and get the element type.
+      std::tie(is_int, is_const, array_size) =
+          _.EvalInt32IfConst(type->GetOperandAs<uint32_t>(2));
+      if (!is_int || !is_const) array_size = 1;
+      auto sub_type_id = type->GetOperandAs<uint32_t>(1);
+      sub_type = _.FindDef(sub_type_id);
+    }
+
+    for (uint32_t array_idx = 0; array_idx < array_size; ++array_idx) {
+      uint32_t num_locations = 0;
+      if (auto error = NumConsumedLocations(_, sub_type, &num_locations))
+        return error;
+
+      uint32_t num_components = NumConsumedComponents(_, sub_type);
+      uint32_t array_location = location + (num_locations * array_idx);
+      uint32_t start = array_location * 4;
+      uint32_t end = (array_location + num_locations) * 4;
+      if (num_components != 0) {
+        start += component;
+        end = array_location * 4 + component + num_components;
+      }
+
+      auto locs = locations;
+      if (has_index && index == 1) locs = output_index1_locations;
+
+      for (uint32_t i = start; i < end; ++i) {
+        if (!locs->insert(i).second) {
+          return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
+                 << "Entry-point has conflicting " << storage_class
+                 << " location assignment at location " << i / 4
+                 << ", component " << i % 4;
+        }
+      }
+    }
+  } else {
+    // For Block-decorated structs with no location assigned to the variable,
+    // each member of the block must be assigned a location. Also record any
+    // member component assignments. The validator allows duplicate decorations
+    // if they agree on the location/component.
+    std::unordered_map<uint32_t, uint32_t> member_locations;
+    std::unordered_map<uint32_t, uint32_t> member_components;
+    for (auto& dec : _.id_decorations(type_id)) {
+      if (dec.dec_type() == SpvDecorationLocation) {
+        auto where = member_locations.find(dec.struct_member_index());
+        if (where == member_locations.end()) {
+          member_locations[dec.struct_member_index()] = dec.params()[0];
+        } else if (where->second != dec.params()[0]) {
+          return _.diag(SPV_ERROR_INVALID_DATA, type)
+                 << "Member index " << dec.struct_member_index()
+                 << " has conflicting location assignments";
+        }
+      } else if (dec.dec_type() == SpvDecorationComponent) {
+        auto where = member_components.find(dec.struct_member_index());
+        if (where == member_components.end()) {
+          member_components[dec.struct_member_index()] = dec.params()[0];
+        } else if (where->second != dec.params()[0]) {
+          return _.diag(SPV_ERROR_INVALID_DATA, type)
+                 << "Member index " << dec.struct_member_index()
+                 << " has conflicting component assignments";
+        }
+      }
+    }
+
+    for (uint32_t i = 1; i < type->operands().size(); ++i) {
+      auto where = member_locations.find(i - 1);
+      if (where == member_locations.end()) {
+        return _.diag(SPV_ERROR_INVALID_DATA, type)
+               << "Member index " << i - 1
+               << " is missing a location assignment";
+      }
+
+      location = where->second;
+      auto member = _.FindDef(type->GetOperandAs<uint32_t>(i));
+      uint32_t num_locations = 0;
+      if (auto error = NumConsumedLocations(_, member, &num_locations))
+        return error;
+
+      // If the component is not specified, it is assumed to be zero.
+      uint32_t num_components = NumConsumedComponents(_, member);
+      component = 0;
+      if (member_components.count(i - 1)) {
+        component = member_components[i - 1];
+      }
+
+      uint32_t start = location * 4;
+      uint32_t end = (location + num_locations) * 4;
+      if (num_components != 0) {
+        start += component;
+        end = location * 4 + component + num_components;
+      }
+      for (uint32_t l = start; l < end; ++l) {
+        if (!locations->insert(l).second) {
+          return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
+                 << "Entry-point has conflicting " << storage_class
+                 << " location assignment at location " << l / 4
+                 << ", component " << l % 4;
+        }
+      }
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateLocations(ValidationState_t& _,
+                               const Instruction* entry_point) {
+  // Locations are stored as a combined location and component values.
+  std::unordered_set<uint32_t> input_locations;
+  std::unordered_set<uint32_t> output_locations_index0;
+  std::unordered_set<uint32_t> output_locations_index1;
+  for (uint32_t i = 3; i < entry_point->operands().size(); ++i) {
+    auto interface_id = entry_point->GetOperandAs<uint32_t>(i);
+    auto interface_var = _.FindDef(interface_id);
+    auto storage_class = interface_var->GetOperandAs<SpvStorageClass>(2);
+    if (storage_class != SpvStorageClassInput &&
+        storage_class != SpvStorageClassOutput) {
+      continue;
+    }
+
+    auto locations = (storage_class == SpvStorageClassInput)
+                         ? &input_locations
+                         : &output_locations_index0;
+    if (auto error = GetLocationsForVariable(
+            _, entry_point, interface_var, locations, &output_locations_index1))
+      return error;
+  }
+
+  return SPV_SUCCESS;
+}
+
 }  // namespace
 
 spv_result_t ValidateInterfaces(ValidationState_t& _) {
@@ -114,6 +473,17 @@
     }
   }
 
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    for (auto& inst : _.ordered_instructions()) {
+      if (inst.opcode() == SpvOpEntryPoint) {
+        if (auto error = ValidateLocations(_, &inst)) {
+          return error;
+        }
+      }
+      if (inst.opcode() == SpvOpTypeVoid) break;
+    }
+  }
+
   return SPV_SUCCESS;
 }
 
diff --git a/third_party/SPIRV-Tools/source/val/validate_mode_setting.cpp b/third_party/SPIRV-Tools/source/val/validate_mode_setting.cpp
index e020f5a..a7f8d33 100644
--- a/third_party/SPIRV-Tools/source/val/validate_mode_setting.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_mode_setting.cpp
@@ -501,10 +501,6 @@
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Addressing model must be Logical for WebGPU environment.";
     }
-    if (_.memory_model() != SpvMemoryModelVulkanKHR) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Memory model must be VulkanKHR for WebGPU environment.";
-    }
   }
 
   if (spvIsOpenCLEnv(_.context()->target_env)) {
diff --git a/third_party/SPIRV-Tools/test/enum_string_mapping_test.cpp b/third_party/SPIRV-Tools/test/enum_string_mapping_test.cpp
index a0379c1..184ae4f 100644
--- a/third_party/SPIRV-Tools/test/enum_string_mapping_test.cpp
+++ b/third_party/SPIRV-Tools/test/enum_string_mapping_test.cpp
@@ -177,6 +177,8 @@
          {SpvCapabilityStoragePushConstant16, "StoragePushConstant16"},
          {SpvCapabilityStorageInputOutput16, "StorageInputOutput16"},
          {SpvCapabilityDeviceGroup, "DeviceGroup"},
+         {SpvCapabilityAtomicFloat32AddEXT, "AtomicFloat32AddEXT"},
+         {SpvCapabilityAtomicFloat64AddEXT, "AtomicFloat64AddEXT"},
          {SpvCapabilityMultiView, "MultiView"},
          {SpvCapabilitySampleMaskOverrideCoverageNV,
           "SampleMaskOverrideCoverageNV"},
diff --git a/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt b/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
index dca142a..7047749 100644
--- a/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
@@ -24,19 +24,25 @@
           fuzzer_pass_construct_composites_test.cpp
           fuzzer_pass_donate_modules_test.cpp
           instruction_descriptor_test.cpp
+          replayer_test.cpp
           transformation_access_chain_test.cpp
           transformation_add_constant_boolean_test.cpp
           transformation_add_constant_composite_test.cpp
           transformation_add_constant_null_test.cpp
           transformation_add_constant_scalar_test.cpp
+          transformation_add_copy_memory_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_image_sample_unused_components_test.cpp
           transformation_add_local_variable_test.cpp
           transformation_add_no_contraction_decoration_test.cpp
+          transformation_add_parameter_test.cpp
+          transformation_add_relaxed_decoration_test.cpp
+          transformation_add_synonym_test.cpp
           transformation_add_type_array_test.cpp
           transformation_add_type_boolean_test.cpp
           transformation_add_type_float_test.cpp
@@ -50,17 +56,25 @@
           transformation_composite_construct_test.cpp
           transformation_composite_extract_test.cpp
           transformation_compute_data_synonym_fact_closure_test.cpp
-          transformation_copy_object_test.cpp
           transformation_equation_instruction_test.cpp
           transformation_function_call_test.cpp
+          transformation_invert_comparison_operator_test.cpp
           transformation_load_test.cpp
           transformation_merge_blocks_test.cpp
           transformation_move_block_down_test.cpp
           transformation_outline_function_test.cpp
           transformation_permute_function_parameters_test.cpp
+          transformation_permute_phi_operands_test.cpp
+          transformation_push_id_through_variable_test.cpp
+          transformation_replace_parameter_with_global_test.cpp
           transformation_replace_boolean_constant_with_constant_binary_test.cpp
+          transformation_replace_copy_object_with_store_load_test.cpp
           transformation_replace_constant_with_uniform_test.cpp
+          transformation_replace_copy_memory_with_load_store_test.cpp
           transformation_replace_id_with_synonym_test.cpp
+          transformation_replace_linear_algebra_instruction_test.cpp
+          transformation_replace_load_store_with_copy_memory_test.cpp
+          transformation_replace_params_with_struct_test.cpp
           transformation_set_function_control_test.cpp
           transformation_set_loop_control_test.cpp
           transformation_set_memory_operands_mask_test.cpp
@@ -68,7 +82,9 @@
           transformation_split_block_test.cpp
           transformation_store_test.cpp
           transformation_swap_commutable_operands_test.cpp
+          transformation_swap_conditional_branch_operands_test.cpp
           transformation_toggle_access_chain_instruction_test.cpp
+          transformation_record_synonymous_constants_test.cpp
           transformation_vector_shuffle_test.cpp
           uniform_buffer_element_descriptor_test.cpp)
 
diff --git a/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp b/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp
index 8b1e0c4..bce10b9 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp
@@ -782,6 +782,94 @@
                                         MakeDataDescriptor(11, {2, 3})));
 }
 
+TEST(FactManagerTest, CorollaryConversionFacts) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %8 = OpTypeVector %6 2
+          %9 = OpTypeVector %7 2
+         %10 = OpTypeFloat 32
+         %11 = OpTypeVector %10 2
+         %15 = OpConstant %6 24 ; synonym of %16
+         %16 = OpConstant %6 24
+         %17 = OpConstant %7 24 ; synonym of %18
+         %18 = OpConstant %7 24
+         %19 = OpConstantComposite %8 %15 %15 ; synonym of %20
+         %20 = OpConstantComposite %8 %16 %16
+         %21 = OpConstantComposite %9 %17 %17 ; synonym of %22
+         %22 = OpConstantComposite %9 %18 %18
+         %23 = OpConstantComposite %8 %15 %15 ; not a synonym of %19
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %24 = OpConvertSToF %10 %15 ; synonym of %25
+         %25 = OpConvertSToF %10 %16
+         %26 = OpConvertUToF %10 %17 ; not a synonym of %27 (different opcode)
+         %27 = OpConvertSToF %10 %18
+         %28 = OpConvertUToF %11 %19 ; synonym of %29
+         %29 = OpConvertUToF %11 %20
+         %30 = OpConvertSToF %11 %21 ; not a synonym of %31 (different opcode)
+         %31 = OpConvertUToF %11 %22
+         %32 = OpConvertUToF %11 %23 ; not a synonym of %28 (operand is not synonymous)
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Add equation facts
+  fact_manager.AddFactIdEquation(24, SpvOpConvertSToF, {15}, context.get());
+  fact_manager.AddFactIdEquation(25, SpvOpConvertSToF, {16}, context.get());
+  fact_manager.AddFactIdEquation(26, SpvOpConvertUToF, {17}, context.get());
+  fact_manager.AddFactIdEquation(27, SpvOpConvertSToF, {18}, context.get());
+  fact_manager.AddFactIdEquation(28, SpvOpConvertUToF, {19}, context.get());
+  fact_manager.AddFactIdEquation(29, SpvOpConvertUToF, {20}, context.get());
+  fact_manager.AddFactIdEquation(30, SpvOpConvertSToF, {21}, context.get());
+  fact_manager.AddFactIdEquation(31, SpvOpConvertUToF, {22}, context.get());
+  fact_manager.AddFactIdEquation(32, SpvOpConvertUToF, {23}, context.get());
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(15, {}),
+                                  MakeDataDescriptor(16, {}), context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {}),
+                                        MakeDataDescriptor(25, {})));
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(17, {}),
+                                  MakeDataDescriptor(18, {}), context.get());
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(26, {}),
+                                         MakeDataDescriptor(27, {})));
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(19, {}),
+                                  MakeDataDescriptor(20, {}), context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(28, {}),
+                                        MakeDataDescriptor(29, {})));
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {}),
+                                  MakeDataDescriptor(22, {}), context.get());
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {}),
+                                         MakeDataDescriptor(31, {})));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(32, {}),
+                                         MakeDataDescriptor(28, {})));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(23, {}),
+                                  MakeDataDescriptor(19, {}), context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(32, {}),
+                                        MakeDataDescriptor(28, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(32, {}),
+                                        MakeDataDescriptor(29, {})));
+}
+
 TEST(FactManagerTest, LogicalNotEquationFacts) {
   std::string shader = R"(
                OpCapability Shader
@@ -982,6 +1070,91 @@
                                         MakeDataDescriptor(16, {})));
 }
 
+TEST(FactManagerTest, ConversionEquations) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 1
+          %5 = OpTypeInt 32 0
+          %6 = OpTypeFloat 32
+         %14 = OpTypeVector %4 2
+         %15 = OpTypeVector %5 2
+         %24 = OpTypeVector %6 2
+         %16 = OpConstant %4 32 ; synonym of %17
+         %17 = OpConstant %4 32
+         %18 = OpConstant %5 32 ; synonym of %19
+         %19 = OpConstant %5 32
+         %20 = OpConstantComposite %14 %16 %16 ; synonym of %21
+         %21 = OpConstantComposite %14 %17 %17
+         %22 = OpConstantComposite %15 %18 %18 ; synonym of %23
+         %23 = OpConstantComposite %15 %19 %19
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %25 = OpConvertUToF %6 %16 ; synonym of %26
+         %26 = OpConvertUToF %6 %17
+         %27 = OpConvertSToF %24 %20 ; not a synonym of %28 (wrong opcode)
+         %28 = OpConvertUToF %24 %21
+         %29 = OpConvertSToF %6 %18 ; not a synonym of %30 (wrong opcode)
+         %30 = OpConvertUToF %6 %19
+         %31 = OpConvertSToF %24 %22 ; synonym of %32
+         %32 = OpConvertSToF %24 %23
+         %33 = OpConvertUToF %6 %17 ; synonym of %26
+         %34 = OpConvertSToF %24 %23 ; synonym of %32
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(16, {}),
+                                  MakeDataDescriptor(17, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(18, {}),
+                                  MakeDataDescriptor(19, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(20, {}),
+                                  MakeDataDescriptor(21, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(22, {}),
+                                  MakeDataDescriptor(23, {}), context.get());
+
+  fact_manager.AddFactIdEquation(25, SpvOpConvertUToF, {16}, context.get());
+  fact_manager.AddFactIdEquation(26, SpvOpConvertUToF, {17}, context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(25, {}),
+                                        MakeDataDescriptor(26, {})));
+
+  fact_manager.AddFactIdEquation(27, SpvOpConvertSToF, {20}, context.get());
+  fact_manager.AddFactIdEquation(28, SpvOpConvertUToF, {21}, context.get());
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {}),
+                                         MakeDataDescriptor(28, {})));
+
+  fact_manager.AddFactIdEquation(29, SpvOpConvertSToF, {18}, context.get());
+  fact_manager.AddFactIdEquation(30, SpvOpConvertUToF, {19}, context.get());
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(29, {}),
+                                         MakeDataDescriptor(30, {})));
+
+  fact_manager.AddFactIdEquation(31, SpvOpConvertSToF, {22}, context.get());
+  fact_manager.AddFactIdEquation(32, SpvOpConvertSToF, {23}, context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(31, {}),
+                                        MakeDataDescriptor(32, {})));
+
+  fact_manager.AddFactIdEquation(33, SpvOpConvertUToF, {17}, context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {}),
+                                        MakeDataDescriptor(26, {})));
+
+  fact_manager.AddFactIdEquation(34, SpvOpConvertSToF, {23}, context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(32, {}),
+                                        MakeDataDescriptor(34, {})));
+}
+
 TEST(FactManagerTest, EquationAndEquivalenceFacts) {
   std::string shader = R"(
                OpCapability Shader
@@ -1110,6 +1283,41 @@
   ASSERT_FALSE(context->get_constant_mgr()->FindConstant(&constant_one));
 }
 
+TEST(FactManagerTest, IdIsIrrelevant) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %12 = OpConstant %6 0
+         %13 = OpConstant %6 1
+          %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;
+
+  ASSERT_FALSE(fact_manager.IdIsIrrelevant(12));
+  ASSERT_FALSE(fact_manager.IdIsIrrelevant(13));
+
+  fact_manager.AddFactIdIsIrrelevant(12);
+
+  ASSERT_TRUE(fact_manager.IdIsIrrelevant(12));
+  ASSERT_FALSE(fact_manager.IdIsIrrelevant(13));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
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
index 0833c1d..0be3d5a 100644
--- 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
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <algorithm>
+
 #include "source/fuzz/fuzzer_pass_donate_modules.h"
 #include "source/fuzz/pseudo_random_generator.h"
 #include "test/fuzz/fuzz_test_util.h"
@@ -1664,7 +1666,8 @@
   TransformationContext transformation_context(&fact_manager,
                                                validator_options);
 
-  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
+  PseudoRandomGenerator rng(0);
+  FuzzerContext fuzzer_context(&rng, 100);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -1678,6 +1681,227 @@
   ASSERT_TRUE(IsValid(env, recipient_context.get()));
 }
 
+TEST(FuzzerPassDonateModulesTest, OpSpecConstantInstructions) {
+  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
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeStruct %6 %6 %7
+          %9 = OpSpecConstantTrue %6
+         %10 = OpSpecConstantFalse %6
+         %11 = OpSpecConstant %7 2
+         %12 = OpSpecConstantComposite %8 %9 %10 %11
+         %13 = OpSpecConstantOp %6 LogicalEqual %9 %10
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  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
+  )";
+
+  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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), false);
+
+  // Check that the module is valid first.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  std::string expected_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
+        %100 = OpTypeBool
+        %101 = OpTypeInt 32 1
+        %102 = OpTypeStruct %100 %100 %101
+        %103 = OpConstantTrue %100
+        %104 = OpConstantFalse %100
+        %105 = OpConstant %101 2
+        %106 = OpConstantComposite %102 %103 %104 %105
+        %107 = OpSpecConstantOp %100 LogicalEqual %103 %104
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %108 = OpFunction %2 None %3
+        %109 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  // Now check that the transformation has produced the expected result.
+  ASSERT_TRUE(IsEqual(env, expected_shader, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonationSupportsOpTypeRuntimeArray) {
+  std::string donor_shader = R"(
+               OpCapability Shader
+               OpExtension "SPV_KHR_storage_buffer_storage_class"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %29 "kernel_1"
+               OpEntryPoint GLCompute %37 "kernel_2"
+               OpSource OpenCL_C 120
+               OpDecorate %2 ArrayStride 4
+               OpMemberDecorate %3 0 Offset 0
+               OpDecorate %3 Block
+               OpMemberDecorate %5 0 Offset 0
+               OpMemberDecorate %6 0 Offset 0
+               OpDecorate %6 Block
+               OpDecorate %21 BuiltIn WorkgroupSize
+               OpDecorate %23 DescriptorSet 0
+               OpDecorate %23 Binding 0
+               OpDecorate %25 SpecId 3
+               OpDecorate %18 SpecId 0
+               OpDecorate %19 SpecId 1
+               OpDecorate %20 SpecId 2
+          %1 = OpTypeInt 32 0
+          %2 = OpTypeRuntimeArray %1
+          %3 = OpTypeStruct %2
+          %4 = OpTypePointer StorageBuffer %3
+          %5 = OpTypeStruct %1
+          %6 = OpTypeStruct %5
+          %7 = OpTypePointer PushConstant %6
+          %8 = OpTypeFloat 32
+          %9 = OpTypeVoid
+         %10 = OpTypeFunction %9
+         %11 = OpTypePointer Workgroup %1
+         %12 = OpTypePointer PushConstant %5
+         %13 = OpTypePointer StorageBuffer %1
+         %14 = OpTypeFunction %1 %1
+         %15 = OpTypeVector %1 3
+         %16 = OpTypePointer Private %15
+         %17 = OpConstant %1 0
+         %18 = OpSpecConstant %1 1
+         %19 = OpSpecConstant %1 1
+         %20 = OpSpecConstant %1 1
+         %21 = OpSpecConstantComposite %15 %18 %19 %20
+         %25 = OpSpecConstant %1 1
+         %26 = OpTypeArray %1 %25
+         %27 = OpTypePointer Workgroup %26
+         %22 = OpVariable %16 Private %21
+         %23 = OpVariable %4 StorageBuffer
+         %24 = OpVariable %7 PushConstant
+         %28 = OpVariable %27 Workgroup
+         %29 = OpFunction %9 None %10
+         %30 = OpLabel
+         %31 = OpAccessChain %11 %28 %17
+         %32 = OpAccessChain %12 %24 %17
+         %33 = OpLoad %5 %32
+         %34 = OpCompositeExtract %1 %33 0
+         %35 = OpFunctionCall %1 %45 %34
+         %36 = OpAccessChain %13 %23 %17 %34
+               OpStore %36 %35
+               OpReturn
+               OpFunctionEnd
+         %37 = OpFunction %9 None %10
+         %38 = OpLabel
+         %39 = OpAccessChain %11 %28 %17
+         %40 = OpAccessChain %12 %24 %17
+         %41 = OpLoad %5 %40
+         %42 = OpCompositeExtract %1 %41 0
+         %43 = OpFunctionCall %1 %45 %42
+         %44 = OpAccessChain %13 %23 %17 %42
+               OpStore %44 %43
+               OpReturn
+               OpFunctionEnd
+         %45 = OpFunction %1 Pure %14
+         %46 = OpFunctionParameter %1
+         %47 = OpLabel
+         %48 = OpAccessChain %11 %28 %46
+         %49 = OpLoad %1 %48
+               OpReturnValue %49
+               OpFunctionEnd
+  )";
+
+  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
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_0;
+  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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  PseudoRandomGenerator rng(0);
+  FuzzerContext fuzzer_context(&rng, 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), false);
+
+  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 1e7c643..8a23574 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
@@ -1659,6 +1659,8 @@
     replayer.SetMessageConsumer(kConsoleMessageConsumer);
     auto replayer_result_status = replayer.Run(
         binary_in, initial_facts, fuzzer_transformation_sequence_out,
+        static_cast<uint32_t>(
+            fuzzer_transformation_sequence_out.transformation_size()),
         &replayer_binary_out, &replayer_transformation_sequence_out);
     ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
               replayer_result_status);
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 24b4460..709e9ce 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fuzzer_shrinker_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fuzzer_shrinker_test.cpp
@@ -1111,6 +1111,18 @@
     *temp.mutable_constant_uniform_fact() = resolution_y_eq_100;
     *facts.mutable_fact()->Add() = temp;
   }
+  // Also add an invalid fact, which should be ignored.
+  {
+    protobufs::FactConstantUniform bad_fact;
+    // The descriptor set, binding and indices used here deliberately make no
+    // sense.
+    *bad_fact.mutable_uniform_buffer_element_descriptor() =
+        MakeUniformBufferElementDescriptor(22, 33, {44, 55});
+    *bad_fact.mutable_constant_word()->Add() = 100;
+    protobufs::Fact temp;
+    *temp.mutable_constant_uniform_fact() = bad_fact;
+    *facts.mutable_fact()->Add() = temp;
+  }
 
   // Do 2 fuzzer runs, starting from an initial seed of 194 (seed value chosen
   // arbitrarily).
diff --git a/third_party/SPIRV-Tools/test/fuzz/replayer_test.cpp b/third_party/SPIRV-Tools/test/fuzz/replayer_test.cpp
new file mode 100644
index 0000000..5d6169e
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/replayer_test.cpp
@@ -0,0 +1,301 @@
+// 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/replayer.h"
+
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_split_block.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(ReplayerTest, PartialReplay) {
+  const std::string kTestShader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "g"
+               OpName %11 "x"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Private %6
+          %8 = OpVariable %7 Private
+          %9 = OpConstant %6 10
+         %10 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+               OpStore %8 %9
+         %12 = OpLoad %6 %8
+               OpStore %11 %12
+         %13 = OpLoad %6 %8
+               OpStore %11 %13
+         %14 = OpLoad %6 %8
+               OpStore %11 %14
+         %15 = OpLoad %6 %8
+               OpStore %11 %15
+         %16 = OpLoad %6 %8
+               OpStore %11 %16
+         %17 = OpLoad %6 %8
+               OpStore %11 %17
+         %18 = OpLoad %6 %8
+               OpStore %11 %18
+         %19 = OpLoad %6 %8
+               OpStore %11 %19
+         %20 = OpLoad %6 %8
+               OpStore %11 %20
+         %21 = OpLoad %6 %8
+               OpStore %11 %21
+         %22 = OpLoad %6 %8
+               OpStore %11 %22
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  spvtools::ValidatorOptions validator_options;
+
+  std::vector<uint32_t> binary_in;
+  SpirvTools t(env);
+  t.SetMessageConsumer(kSilentConsumer);
+  ASSERT_TRUE(t.Assemble(kTestShader, &binary_in, kFuzzAssembleOption));
+  ASSERT_TRUE(t.Validate(binary_in));
+
+  protobufs::TransformationSequence transformations;
+  for (uint32_t id = 12; id <= 22; id++) {
+    *transformations.add_transformation() =
+        TransformationSplitBlock(MakeInstructionDescriptor(id, SpvOpLoad, 0),
+                                 id + 100)
+            .ToMessage();
+  }
+
+  {
+    // Full replay
+    protobufs::TransformationSequence transformations_out;
+    protobufs::FactSequence empty_facts;
+    std::vector<uint32_t> binary_out;
+    Replayer replayer(env, true, validator_options);
+    replayer.SetMessageConsumer(kSilentConsumer);
+    auto replayer_result_status =
+        replayer.Run(binary_in, empty_facts, transformations, 11, &binary_out,
+                     &transformations_out);
+    // Replay should succeed.
+    ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
+              replayer_result_status);
+    // All transformations should be applied.
+    ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
+        transformations, transformations_out));
+
+    const std::string kFullySplitShader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "g"
+               OpName %11 "x"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Private %6
+          %8 = OpVariable %7 Private
+          %9 = OpConstant %6 10
+         %10 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+               OpStore %8 %9
+               OpBranch %112
+        %112 = OpLabel
+         %12 = OpLoad %6 %8
+               OpStore %11 %12
+               OpBranch %113
+        %113 = OpLabel
+         %13 = OpLoad %6 %8
+               OpStore %11 %13
+               OpBranch %114
+        %114 = OpLabel
+         %14 = OpLoad %6 %8
+               OpStore %11 %14
+               OpBranch %115
+        %115 = OpLabel
+         %15 = OpLoad %6 %8
+               OpStore %11 %15
+               OpBranch %116
+        %116 = OpLabel
+         %16 = OpLoad %6 %8
+               OpStore %11 %16
+               OpBranch %117
+        %117 = OpLabel
+         %17 = OpLoad %6 %8
+               OpStore %11 %17
+               OpBranch %118
+        %118 = OpLabel
+         %18 = OpLoad %6 %8
+               OpStore %11 %18
+               OpBranch %119
+        %119 = OpLabel
+         %19 = OpLoad %6 %8
+               OpStore %11 %19
+               OpBranch %120
+        %120 = OpLabel
+         %20 = OpLoad %6 %8
+               OpStore %11 %20
+               OpBranch %121
+        %121 = OpLabel
+         %21 = OpLoad %6 %8
+               OpStore %11 %21
+               OpBranch %122
+        %122 = OpLabel
+         %22 = OpLoad %6 %8
+               OpStore %11 %22
+               OpReturn
+               OpFunctionEnd
+    )";
+    ASSERT_TRUE(IsEqual(env, kFullySplitShader, binary_out));
+  }
+
+  {
+    // Half replay
+    protobufs::TransformationSequence transformations_out;
+    protobufs::FactSequence empty_facts;
+    std::vector<uint32_t> binary_out;
+    Replayer replayer(env, true, validator_options);
+    replayer.SetMessageConsumer(kSilentConsumer);
+    auto replayer_result_status =
+        replayer.Run(binary_in, empty_facts, transformations, 5, &binary_out,
+                     &transformations_out);
+    // Replay should succeed.
+    ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
+              replayer_result_status);
+    // The first 5 transformations should be applied
+    ASSERT_EQ(5, transformations_out.transformation_size());
+    for (uint32_t i = 0; i < 5; i++) {
+      ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
+          transformations.transformation(i),
+          transformations_out.transformation(i)));
+    }
+
+    const std::string kHalfSplitShader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "g"
+               OpName %11 "x"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Private %6
+          %8 = OpVariable %7 Private
+          %9 = OpConstant %6 10
+         %10 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+               OpStore %8 %9
+               OpBranch %112
+        %112 = OpLabel
+         %12 = OpLoad %6 %8
+               OpStore %11 %12
+               OpBranch %113
+        %113 = OpLabel
+         %13 = OpLoad %6 %8
+               OpStore %11 %13
+               OpBranch %114
+        %114 = OpLabel
+         %14 = OpLoad %6 %8
+               OpStore %11 %14
+               OpBranch %115
+        %115 = OpLabel
+         %15 = OpLoad %6 %8
+               OpStore %11 %15
+               OpBranch %116
+        %116 = OpLabel
+         %16 = OpLoad %6 %8
+               OpStore %11 %16
+         %17 = OpLoad %6 %8
+               OpStore %11 %17
+         %18 = OpLoad %6 %8
+               OpStore %11 %18
+         %19 = OpLoad %6 %8
+               OpStore %11 %19
+         %20 = OpLoad %6 %8
+               OpStore %11 %20
+         %21 = OpLoad %6 %8
+               OpStore %11 %21
+         %22 = OpLoad %6 %8
+               OpStore %11 %22
+               OpReturn
+               OpFunctionEnd
+    )";
+    ASSERT_TRUE(IsEqual(env, kHalfSplitShader, binary_out));
+  }
+
+  {
+    // Empty replay
+    protobufs::TransformationSequence transformations_out;
+    protobufs::FactSequence empty_facts;
+    std::vector<uint32_t> binary_out;
+    Replayer replayer(env, true, validator_options);
+    replayer.SetMessageConsumer(kSilentConsumer);
+    auto replayer_result_status =
+        replayer.Run(binary_in, empty_facts, transformations, 0, &binary_out,
+                     &transformations_out);
+    // Replay should succeed.
+    ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
+              replayer_result_status);
+    // No transformations should be applied
+    ASSERT_EQ(0, transformations_out.transformation_size());
+    ASSERT_TRUE(IsEqual(env, kTestShader, binary_out));
+  }
+
+  {
+    // Invalid replay: too many transformations
+    protobufs::TransformationSequence transformations_out;
+    protobufs::FactSequence empty_facts;
+    std::vector<uint32_t> binary_out;
+    // The number of transformations requested to be applied exceeds the number
+    // of transformations
+    Replayer replayer(env, true, validator_options);
+    replayer.SetMessageConsumer(kSilentConsumer);
+    auto replayer_result_status =
+        replayer.Run(binary_in, empty_facts, transformations, 12, &binary_out,
+                     &transformations_out);
+
+    // Replay should not succeed.
+    ASSERT_EQ(Replayer::ReplayerResultStatus::kTooManyTransformationsRequested,
+              replayer_result_status);
+    // No transformations should be applied
+    ASSERT_EQ(0, transformations_out.transformation_size());
+    // The output binary should be empty
+    ASSERT_TRUE(binary_out.empty());
+  }
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
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
index 443c31c..adb14e3 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_access_chain_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_access_chain_test.cpp
@@ -150,7 +150,7 @@
                    100, 43, {1000}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
                    .IsApplicable(context.get(), transformation_context));
 
-  // Bad: index id is not a constant
+  // Bad: index id is not a constant and the pointer refers to a struct
   ASSERT_FALSE(TransformationAccessChain(
                    100, 43, {24}, MakeInstructionDescriptor(25, SpvOpIAdd, 0))
                    .IsApplicable(context.get(), transformation_context));
@@ -161,9 +161,9 @@
                                 MakeInstructionDescriptor(24, SpvOpLoad, 0))
           .IsApplicable(context.get(), transformation_context));
 
-  // Bad: index id is out of bounds
+  // Bad: index id is out of bounds when accessing a struct
   ASSERT_FALSE(
-      TransformationAccessChain(100, 43, {80, 83},
+      TransformationAccessChain(100, 43, {83, 80},
                                 MakeInstructionDescriptor(24, SpvOpLoad, 0))
           .IsApplicable(context.get(), transformation_context));
 
@@ -172,6 +172,12 @@
                    100, 34, {}, MakeInstructionDescriptor(36, SpvOpVariable, 0))
                    .IsApplicable(context.get(), transformation_context));
 
+  // Bad: OpTypeBool must be present in the module to clamp an index
+  ASSERT_FALSE(
+      TransformationAccessChain(100, 36, {80, 81},
+                                MakeInstructionDescriptor(37, SpvOpStore, 0))
+          .IsApplicable(context.get(), transformation_context));
+
   // Bad: pointer not available
   ASSERT_FALSE(
       TransformationAccessChain(
@@ -183,15 +189,23 @@
                    100, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 100))
                    .IsApplicable(context.get(), transformation_context));
 
+#ifndef NDEBUG
   // Bad: pointer is null
-  ASSERT_FALSE(TransformationAccessChain(
-                   100, 45, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_DEATH(
+      TransformationAccessChain(100, 45, {80},
+                                MakeInstructionDescriptor(24, SpvOpLoad, 0))
+          .IsApplicable(context.get(), transformation_context),
+      "Access chains should not be created from null/undefined pointers");
+#endif
 
+#ifndef NDEBUG
   // Bad: pointer is undef
-  ASSERT_FALSE(TransformationAccessChain(
-                   100, 46, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_DEATH(
+      TransformationAccessChain(100, 46, {80},
+                                MakeInstructionDescriptor(24, SpvOpLoad, 0))
+          .IsApplicable(context.get(), transformation_context),
+      "Access chains should not be created from null/undefined pointers");
+#endif
 
   // Bad: pointer to result type does not exist
   ASSERT_FALSE(TransformationAccessChain(
@@ -222,18 +236,7 @@
 
   {
     TransformationAccessChain transformation(
-        102, 36, {80, 81}, MakeInstructionDescriptor(37, SpvOpStore, 0));
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    transformation.Apply(context.get(), &transformation_context);
-    ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_FALSE(
-        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
-  }
-
-  {
-    TransformationAccessChain transformation(
-        103, 44, {}, MakeInstructionDescriptor(44, SpvOpStore, 0));
+        102, 44, {}, MakeInstructionDescriptor(44, SpvOpStore, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     transformation.Apply(context.get(), &transformation_context);
@@ -244,7 +247,7 @@
 
   {
     TransformationAccessChain transformation(
-        104, 13, {80}, MakeInstructionDescriptor(21, SpvOpAccessChain, 0));
+        103, 13, {80}, MakeInstructionDescriptor(21, SpvOpAccessChain, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     transformation.Apply(context.get(), &transformation_context);
@@ -255,7 +258,7 @@
 
   {
     TransformationAccessChain transformation(
-        105, 34, {}, MakeInstructionDescriptor(44, SpvOpStore, 1));
+        104, 34, {}, MakeInstructionDescriptor(44, SpvOpStore, 1));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     transformation.Apply(context.get(), &transformation_context);
@@ -266,7 +269,7 @@
 
   {
     TransformationAccessChain transformation(
-        106, 38, {}, MakeInstructionDescriptor(40, SpvOpFunctionCall, 0));
+        105, 38, {}, MakeInstructionDescriptor(40, SpvOpFunctionCall, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     transformation.Apply(context.get(), &transformation_context);
@@ -277,7 +280,7 @@
 
   {
     TransformationAccessChain transformation(
-        107, 14, {}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
+        106, 14, {}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     transformation.Apply(context.get(), &transformation_context);
@@ -286,28 +289,6 @@
         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(107));
   }
 
-  {
-    TransformationAccessChain transformation(
-        108, 54, {85, 81, 81}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    transformation.Apply(context.get(), &transformation_context);
-    ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_TRUE(
-        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(108));
-  }
-
-  {
-    TransformationAccessChain transformation(
-        109, 48, {80, 80}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    transformation.Apply(context.get(), &transformation_context);
-    ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_FALSE(
-        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(109));
-  }
-
   std::string after_transformation = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
@@ -359,16 +340,15 @@
          %36 = OpVariable %9 Function
          %38 = OpVariable %11 Function
          %44 = OpCopyObject %9 %36
-        %103 = OpAccessChain %9 %44
+        %102 = OpAccessChain %9 %44
                OpStore %28 %33
-        %105 = OpAccessChain %11 %34
+        %104 = 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
+        %105 = OpAccessChain %11 %38
          %40 = OpFunctionCall %10 %15 %36 %38
          %41 = OpLoad %10 %34
          %42 = OpIAdd %10 %41 %40
@@ -380,15 +360,13 @@
          %13 = OpFunctionParameter %9
          %14 = OpFunctionParameter %11
          %16 = OpLabel
-        %104 = OpAccessChain %70 %13 %80
+        %103 = 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
+        %106 = OpAccessChain %11 %14
          %24 = OpLoad %10 %14
          %25 = OpIAdd %10 %23 %24
                OpReturnValue %25
@@ -473,6 +451,237 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationAccessChainTest, ClampingVariables) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 310
+          %4 = OpTypeVoid
+          %5 = OpTypeBool
+          %6 = OpTypeFunction %4
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %7 4
+          %9 = OpTypePointer Function %8
+         %10 = OpConstant %7 0
+         %11 = OpConstant %7 1
+         %12 = OpConstant %7 3
+         %13 = OpConstant %7 2
+         %14 = OpConstantComposite %8 %10 %11 %12 %13
+         %15 = OpTypePointer Function %7
+         %16 = OpTypeInt 32 0
+         %17 = OpConstant %16 1
+         %18 = OpConstant %16 3
+         %19 = OpTypeStruct %8
+         %20 = OpTypePointer Function %19
+         %21 = OpConstant %7 9
+         %22 = OpConstant %16 10
+         %23 = OpTypeArray %19 %22
+         %24 = OpTypePointer Function %23
+         %25 = OpTypeFloat 32
+         %26 = OpTypeVector %25 4
+         %27 = OpTypePointer Output %26
+          %3 = OpVariable %27 Output
+          %2 = OpFunction %4 None %6
+         %28 = OpLabel
+         %29 = OpVariable %9 Function
+         %30 = OpVariable %15 Function
+         %31 = OpVariable %15 Function
+         %32 = OpVariable %20 Function
+         %33 = OpVariable %15 Function
+         %34 = OpVariable %24 Function
+               OpStore %29 %14
+               OpStore %30 %10
+         %36 = OpLoad %7 %30
+         %38 = OpLoad %8 %29
+         %39 = OpCompositeConstruct %19 %38
+         %40 = OpLoad %7 %30
+         %42 = OpLoad %8 %29
+         %43 = OpCompositeConstruct %19 %42
+         %45 = OpLoad %7 %30
+         %46 = OpLoad %7 %33
+               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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Bad: no ids given for clamping
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0))
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Bad: an id given for clamping is not fresh
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0),
+                   {{46, 201}})
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Bad: an id given for clamping is not fresh
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0),
+                   {{200, 46}})
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Bad: an id given for clamping is the same as the id for the access chain
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0),
+                   {{100, 201}})
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Bad: the fresh ids given are not distinct
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0),
+                   {{200, 200}})
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Bad: not enough ids given for clamping (2 pairs needed)
+  ASSERT_FALSE(
+      TransformationAccessChain(104, 34, {45, 10, 46},
+                                MakeInstructionDescriptor(46, SpvOpReturn, 0),
+                                {{208, 209}, {209, 211}})
+          .IsApplicable(context.get(), transformation_context));
+
+  // Bad: the fresh ids given are not distinct
+  ASSERT_FALSE(
+      TransformationAccessChain(104, 34, {45, 10, 46},
+                                MakeInstructionDescriptor(46, SpvOpReturn, 0),
+                                {{208, 209}, {209, 211}})
+          .IsApplicable(context.get(), transformation_context));
+
+  {
+    TransformationAccessChain transformation(
+        100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0),
+        {{200, 201}});
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  {
+    TransformationAccessChain transformation(
+        101, 29, {36}, MakeInstructionDescriptor(38, SpvOpLoad, 0),
+        {{202, 203}});
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  {
+    TransformationAccessChain transformation(
+        102, 32, {10, 40}, MakeInstructionDescriptor(42, SpvOpLoad, 0),
+        {{204, 205}});
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  {
+    TransformationAccessChain transformation(
+        103, 34, {11}, MakeInstructionDescriptor(45, SpvOpLoad, 0),
+        {{206, 207}});
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  {
+    TransformationAccessChain transformation(
+        104, 34, {45, 10, 46}, MakeInstructionDescriptor(46, SpvOpReturn, 0),
+        {{208, 209}, {210, 211}});
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 310
+          %4 = OpTypeVoid
+          %5 = OpTypeBool
+          %6 = OpTypeFunction %4
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %7 4
+          %9 = OpTypePointer Function %8
+         %10 = OpConstant %7 0
+         %11 = OpConstant %7 1
+         %12 = OpConstant %7 3
+         %13 = OpConstant %7 2
+         %14 = OpConstantComposite %8 %10 %11 %12 %13
+         %15 = OpTypePointer Function %7
+         %16 = OpTypeInt 32 0
+         %17 = OpConstant %16 1
+         %18 = OpConstant %16 3
+         %19 = OpTypeStruct %8
+         %20 = OpTypePointer Function %19
+         %21 = OpConstant %7 9
+         %22 = OpConstant %16 10
+         %23 = OpTypeArray %19 %22
+         %24 = OpTypePointer Function %23
+         %25 = OpTypeFloat 32
+         %26 = OpTypeVector %25 4
+         %27 = OpTypePointer Output %26
+          %3 = OpVariable %27 Output
+          %2 = OpFunction %4 None %6
+         %28 = OpLabel
+         %29 = OpVariable %9 Function
+         %30 = OpVariable %15 Function
+         %31 = OpVariable %15 Function
+         %32 = OpVariable %20 Function
+         %33 = OpVariable %15 Function
+         %34 = OpVariable %24 Function
+               OpStore %29 %14
+               OpStore %30 %10
+        %200 = OpULessThanEqual %5 %17 %18
+        %201 = OpSelect %16 %200 %17 %18
+        %100 = OpAccessChain %15 %29 %201
+         %36 = OpLoad %7 %30
+        %202 = OpULessThanEqual %5 %36 %12
+        %203 = OpSelect %7 %202 %36 %12
+        %101 = OpAccessChain %15 %29 %203
+         %38 = OpLoad %8 %29
+         %39 = OpCompositeConstruct %19 %38
+         %40 = OpLoad %7 %30
+        %204 = OpULessThanEqual %5 %40 %12
+        %205 = OpSelect %7 %204 %40 %12
+        %102 = OpAccessChain %15 %32 %10 %205
+         %42 = OpLoad %8 %29
+         %43 = OpCompositeConstruct %19 %42
+        %206 = OpULessThanEqual %5 %11 %21
+        %207 = OpSelect %7 %206 %11 %21
+        %103 = OpAccessChain %20 %34 %207
+         %45 = OpLoad %7 %30
+         %46 = OpLoad %7 %33
+        %208 = OpULessThanEqual %5 %45 %21
+        %209 = OpSelect %7 %208 %45 %21
+        %210 = OpULessThanEqual %5 %46 %12
+        %211 = OpSelect %7 %210 %46 %12
+        %104 = OpAccessChain %15 %34 %209 %10 %211
+               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_constant_boolean_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_boolean_test.cpp
index c603333..1a40329 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_boolean_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_boolean_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/transformation_add_constant_boolean.h"
+
 #include "test/fuzz/fuzz_test_util.h"
 
 namespace spvtools {
@@ -48,17 +49,23 @@
                                                validator_options);
 
   // True and false can both be added as neither is present.
-  ASSERT_TRUE(TransformationAddConstantBoolean(7, true).IsApplicable(
-      context.get(), transformation_context));
-  ASSERT_TRUE(TransformationAddConstantBoolean(7, false).IsApplicable(
-      context.get(), transformation_context));
+  ASSERT_TRUE(TransformationAddConstantBoolean(7, true, false)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationAddConstantBoolean(7, false, false)
+                  .IsApplicable(context.get(), transformation_context));
+
+  // Irrelevant true and false can both be added as neither is present.
+  ASSERT_TRUE(TransformationAddConstantBoolean(7, true, true)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationAddConstantBoolean(7, false, true)
+                  .IsApplicable(context.get(), transformation_context));
 
   // Id 5 is already taken.
-  ASSERT_FALSE(TransformationAddConstantBoolean(5, true).IsApplicable(
-      context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddConstantBoolean(5, true, false)
+                   .IsApplicable(context.get(), transformation_context));
 
-  auto add_true = TransformationAddConstantBoolean(7, true);
-  auto add_false = TransformationAddConstantBoolean(8, false);
+  auto add_true = TransformationAddConstantBoolean(7, true, false);
+  auto add_false = TransformationAddConstantBoolean(8, false, false);
 
   ASSERT_TRUE(add_true.IsApplicable(context.get(), transformation_context));
   add_true.Apply(context.get(), &transformation_context);
@@ -67,7 +74,7 @@
   // Having added true, we cannot add it again with the same id.
   ASSERT_FALSE(add_true.IsApplicable(context.get(), transformation_context));
   // But we can add it with a different id.
-  auto add_true_again = TransformationAddConstantBoolean(100, true);
+  auto add_true_again = TransformationAddConstantBoolean(100, true, false);
   ASSERT_TRUE(
       add_true_again.IsApplicable(context.get(), transformation_context));
   add_true_again.Apply(context.get(), &transformation_context);
@@ -80,12 +87,31 @@
   // Having added false, we cannot add it again with the same id.
   ASSERT_FALSE(add_false.IsApplicable(context.get(), transformation_context));
   // But we can add it with a different id.
-  auto add_false_again = TransformationAddConstantBoolean(101, false);
+  auto add_false_again = TransformationAddConstantBoolean(101, false, false);
   ASSERT_TRUE(
       add_false_again.IsApplicable(context.get(), transformation_context));
   add_false_again.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
+  // We can create an irrelevant OpConstantTrue.
+  TransformationAddConstantBoolean irrelevant_true(102, true, true);
+  ASSERT_TRUE(
+      irrelevant_true.IsApplicable(context.get(), transformation_context));
+  irrelevant_true.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // We can create an irrelevant OpConstantFalse.
+  TransformationAddConstantBoolean irrelevant_false(103, false, true);
+  ASSERT_TRUE(
+      irrelevant_false.IsApplicable(context.get(), transformation_context));
+  irrelevant_false.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  ASSERT_FALSE(fact_manager.IdIsIrrelevant(100));
+  ASSERT_FALSE(fact_manager.IdIsIrrelevant(101));
+  ASSERT_TRUE(fact_manager.IdIsIrrelevant(102));
+  ASSERT_TRUE(fact_manager.IdIsIrrelevant(103));
+
   std::string after_transformation = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
@@ -101,6 +127,8 @@
         %100 = OpConstantTrue %6
           %8 = OpConstantFalse %6
         %101 = OpConstantFalse %6
+        %102 = OpConstantTrue %6
+        %103 = OpConstantFalse %6
           %4 = OpFunction %2 None %3
           %5 = OpLabel
                OpReturn
@@ -138,10 +166,16 @@
                                                validator_options);
 
   // Neither true nor false can be added as OpTypeBool is not present.
-  ASSERT_FALSE(TransformationAddConstantBoolean(6, true).IsApplicable(
-      context.get(), transformation_context));
-  ASSERT_FALSE(TransformationAddConstantBoolean(6, false).IsApplicable(
-      context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddConstantBoolean(6, true, false)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddConstantBoolean(6, false, false)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // This does not depend on whether the constant is relevant or not.
+  ASSERT_FALSE(TransformationAddConstantBoolean(6, true, true)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddConstantBoolean(6, false, true)
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_composite_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_composite_test.cpp
index 021bf58..75e23ad 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_composite_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_composite_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/transformation_add_constant_composite.h"
+
 #include "test/fuzz/fuzz_test_util.h"
 
 namespace spvtools {
@@ -69,39 +70,62 @@
                                                validator_options);
 
   // Too few ids
-  ASSERT_FALSE(TransformationAddConstantComposite(103, 8, {100, 101})
+  ASSERT_FALSE(TransformationAddConstantComposite(103, 8, {100, 101}, false)
                    .IsApplicable(context.get(), transformation_context));
   // Too many ids
-  ASSERT_FALSE(TransformationAddConstantComposite(101, 7, {14, 15, 14})
+  ASSERT_FALSE(TransformationAddConstantComposite(101, 7, {14, 15, 14}, false)
                    .IsApplicable(context.get(), transformation_context));
   // Id already in use
-  ASSERT_FALSE(TransformationAddConstantComposite(40, 7, {11, 12})
+  ASSERT_FALSE(TransformationAddConstantComposite(40, 7, {11, 12}, false)
                    .IsApplicable(context.get(), transformation_context));
   // %39 is not a type
-  ASSERT_FALSE(TransformationAddConstantComposite(100, 39, {11, 12})
+  ASSERT_FALSE(TransformationAddConstantComposite(100, 39, {11, 12}, false)
                    .IsApplicable(context.get(), transformation_context));
 
   TransformationAddConstantComposite transformations[] = {
       // %100 = OpConstantComposite %7 %11 %12
-      TransformationAddConstantComposite(100, 7, {11, 12}),
+      TransformationAddConstantComposite(100, 7, {11, 12}, false),
 
       // %101 = OpConstantComposite %7 %14 %15
-      TransformationAddConstantComposite(101, 7, {14, 15}),
+      TransformationAddConstantComposite(101, 7, {14, 15}, false),
 
       // %102 = OpConstantComposite %7 %17 %18
-      TransformationAddConstantComposite(102, 7, {17, 18}),
+      TransformationAddConstantComposite(102, 7, {17, 18}, false),
 
       // %103 = OpConstantComposite %8 %100 %101 %102
-      TransformationAddConstantComposite(103, 8, {100, 101, 102}),
+      TransformationAddConstantComposite(103, 8, {100, 101, 102}, false),
 
       // %104 = OpConstantComposite %24 %29 %30 %31
-      TransformationAddConstantComposite(104, 24, {29, 30, 31}),
+      TransformationAddConstantComposite(104, 24, {29, 30, 31}, false),
 
       // %105 = OpConstantComposite %26 %104 %33
-      TransformationAddConstantComposite(105, 26, {104, 33}),
+      TransformationAddConstantComposite(105, 26, {104, 33}, false),
 
       // %106 = OpConstantComposite %35 %38 %39 %40
-      TransformationAddConstantComposite(106, 35, {38, 39, 40})};
+      TransformationAddConstantComposite(106, 35, {38, 39, 40}, false),
+
+      // Same constants but with an irrelevant fact applied.
+
+      // %107 = OpConstantComposite %7 %11 %12
+      TransformationAddConstantComposite(107, 7, {11, 12}, true),
+
+      // %108 = OpConstantComposite %7 %14 %15
+      TransformationAddConstantComposite(108, 7, {14, 15}, true),
+
+      // %109 = OpConstantComposite %7 %17 %18
+      TransformationAddConstantComposite(109, 7, {17, 18}, true),
+
+      // %110 = OpConstantComposite %8 %100 %101 %102
+      TransformationAddConstantComposite(110, 8, {100, 101, 102}, true),
+
+      // %111 = OpConstantComposite %24 %29 %30 %31
+      TransformationAddConstantComposite(111, 24, {29, 30, 31}, true),
+
+      // %112 = OpConstantComposite %26 %104 %33
+      TransformationAddConstantComposite(112, 26, {104, 33}, true),
+
+      // %113 = OpConstantComposite %35 %38 %39 %40
+      TransformationAddConstantComposite(113, 35, {38, 39, 40}, true)};
 
   for (auto& transformation : transformations) {
     ASSERT_TRUE(
@@ -110,6 +134,14 @@
   }
   ASSERT_TRUE(IsValid(env, context.get()));
 
+  for (uint32_t id = 100; id <= 106; ++id) {
+    ASSERT_FALSE(fact_manager.IdIsIrrelevant(id));
+  }
+
+  for (uint32_t id = 107; id <= 113; ++id) {
+    ASSERT_TRUE(fact_manager.IdIsIrrelevant(id));
+  }
+
   std::string after_transformation = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
@@ -149,6 +181,13 @@
         %104 = OpConstantComposite %24 %29 %30 %31
         %105 = OpConstantComposite %26 %104 %33
         %106 = OpConstantComposite %35 %38 %39 %40
+        %107 = OpConstantComposite %7 %11 %12
+        %108 = OpConstantComposite %7 %14 %15
+        %109 = OpConstantComposite %7 %17 %18
+        %110 = OpConstantComposite %8 %100 %101 %102
+        %111 = OpConstantComposite %24 %29 %30 %31
+        %112 = OpConstantComposite %26 %104 %33
+        %113 = OpConstantComposite %35 %38 %39 %40
           %4 = OpFunction %2 None %3
           %5 = OpLabel
                OpReturn
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_scalar_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_scalar_test.cpp
index 5124b7d..7d9608d 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_scalar_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_scalar_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/transformation_add_constant_scalar.h"
+
 #include "test/fuzz/fuzz_test_util.h"
 
 namespace spvtools {
@@ -70,24 +71,42 @@
   uint32_t uint_for_float[2];
   memcpy(uint_for_float, float_values, sizeof(float_values));
 
-  auto add_signed_int_1 = TransformationAddConstantScalar(100, 6, {1});
-  auto add_signed_int_10 = TransformationAddConstantScalar(101, 6, {10});
-  auto add_unsigned_int_2 = TransformationAddConstantScalar(102, 10, {2});
-  auto add_unsigned_int_20 = TransformationAddConstantScalar(103, 10, {20});
+  auto add_signed_int_1 = TransformationAddConstantScalar(100, 6, {1}, false);
+  auto add_signed_int_10 = TransformationAddConstantScalar(101, 6, {10}, false);
+  auto add_unsigned_int_2 =
+      TransformationAddConstantScalar(102, 10, {2}, false);
+  auto add_unsigned_int_20 =
+      TransformationAddConstantScalar(103, 10, {20}, false);
   auto add_float_3 =
-      TransformationAddConstantScalar(104, 14, {uint_for_float[0]});
+      TransformationAddConstantScalar(104, 14, {uint_for_float[0]}, false);
   auto add_float_30 =
-      TransformationAddConstantScalar(105, 14, {uint_for_float[1]});
+      TransformationAddConstantScalar(105, 14, {uint_for_float[1]}, false);
+  auto add_signed_int_1_irrelevant =
+      TransformationAddConstantScalar(106, 6, {1}, true);
+  auto add_signed_int_10_irrelevant =
+      TransformationAddConstantScalar(107, 6, {10}, true);
+  auto add_unsigned_int_2_irrelevant =
+      TransformationAddConstantScalar(108, 10, {2}, true);
+  auto add_unsigned_int_20_irrelevant =
+      TransformationAddConstantScalar(109, 10, {20}, true);
+  auto add_float_3_irrelevant =
+      TransformationAddConstantScalar(110, 14, {uint_for_float[0]}, true);
+  auto add_float_30_irrelevant =
+      TransformationAddConstantScalar(111, 14, {uint_for_float[1]}, true);
   auto bad_add_float_30_id_already_used =
-      TransformationAddConstantScalar(104, 14, {uint_for_float[1]});
-  auto bad_id_already_used = TransformationAddConstantScalar(1, 6, {1});
-  auto bad_no_data = TransformationAddConstantScalar(100, 6, {});
-  auto bad_too_much_data = TransformationAddConstantScalar(100, 6, {1, 2});
+      TransformationAddConstantScalar(104, 14, {uint_for_float[1]}, false);
+  auto bad_id_already_used = TransformationAddConstantScalar(1, 6, {1}, false);
+  auto bad_no_data = TransformationAddConstantScalar(100, 6, {}, false);
+  auto bad_too_much_data =
+      TransformationAddConstantScalar(100, 6, {1, 2}, false);
   auto bad_type_id_does_not_exist =
-      TransformationAddConstantScalar(108, 2020, {uint_for_float[0]});
-  auto bad_type_id_is_not_a_type = TransformationAddConstantScalar(109, 9, {0});
-  auto bad_type_id_is_void = TransformationAddConstantScalar(110, 2, {0});
-  auto bad_type_id_is_pointer = TransformationAddConstantScalar(111, 11, {0});
+      TransformationAddConstantScalar(108, 2020, {uint_for_float[0]}, false);
+  auto bad_type_id_is_not_a_type =
+      TransformationAddConstantScalar(109, 9, {0}, false);
+  auto bad_type_id_is_void =
+      TransformationAddConstantScalar(110, 2, {0}, false);
+  auto bad_type_id_is_pointer =
+      TransformationAddConstantScalar(111, 11, {0}, false);
 
   // Id is already in use.
   ASSERT_FALSE(
@@ -144,9 +163,48 @@
   add_float_30.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
+  // Add irrelevant ids.
+  ASSERT_TRUE(add_signed_int_1_irrelevant.IsApplicable(context.get(),
+                                                       transformation_context));
+  add_signed_int_1_irrelevant.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  ASSERT_TRUE(add_signed_int_10_irrelevant.IsApplicable(
+      context.get(), transformation_context));
+  add_signed_int_10_irrelevant.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  ASSERT_TRUE(add_unsigned_int_2_irrelevant.IsApplicable(
+      context.get(), transformation_context));
+  add_unsigned_int_2_irrelevant.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  ASSERT_TRUE(add_unsigned_int_20_irrelevant.IsApplicable(
+      context.get(), transformation_context));
+  add_unsigned_int_20_irrelevant.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  ASSERT_TRUE(add_float_3_irrelevant.IsApplicable(context.get(),
+                                                  transformation_context));
+  add_float_3_irrelevant.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  ASSERT_TRUE(add_float_30_irrelevant.IsApplicable(context.get(),
+                                                   transformation_context));
+  add_float_30_irrelevant.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
   ASSERT_FALSE(bad_add_float_30_id_already_used.IsApplicable(
       context.get(), transformation_context));
 
+  for (uint32_t id = 100; id <= 105; ++id) {
+    ASSERT_FALSE(fact_manager.IdIsIrrelevant(id));
+  }
+
+  for (uint32_t id = 106; id <= 111; ++id) {
+    ASSERT_TRUE(fact_manager.IdIsIrrelevant(id));
+  }
+
   std::string after_transformation = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
@@ -177,6 +235,12 @@
         %103 = OpConstant %10 20
         %104 = OpConstant %14 3
         %105 = OpConstant %14 30
+        %106 = OpConstant %6 1
+        %107 = OpConstant %6 10
+        %108 = OpConstant %10 2
+        %109 = OpConstant %10 20
+        %110 = OpConstant %14 3
+        %111 = OpConstant %14 30
           %4 = OpFunction %2 None %3
           %5 = OpLabel
           %8 = OpVariable %7 Function
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_copy_memory_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_copy_memory_test.cpp
new file mode 100644
index 0000000..66a15f4
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_copy_memory_test.cpp
@@ -0,0 +1,384 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_copy_memory.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddCopyMemoryTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %19 RelaxedPrecision
+               OpMemberDecorate %66 0 RelaxedPrecision
+               OpDecorate %69 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypePointer Function %6
+         %78 = OpTypePointer Private %6
+          %8 = OpTypeFunction %6 %7
+         %17 = OpTypeInt 32 1
+         %18 = OpTypePointer Function %17
+         %79 = OpTypePointer Private %17
+         %20 = OpConstant %17 0
+         %21 = OpTypeFloat 32
+         %22 = OpTypePointer Function %21
+         %80 = OpTypePointer Private %21
+         %24 = OpConstant %21 0
+         %25 = OpConstantFalse %6
+         %32 = OpConstantTrue %6
+         %33 = OpTypeVector %21 4
+         %34 = OpTypePointer Function %33
+         %81 = OpTypePointer Private %33
+         %36 = OpConstantComposite %33 %24 %24 %24 %24
+         %37 = OpTypeMatrix %33 4
+         %84 = OpConstantComposite %37 %36 %36 %36 %36
+         %38 = OpTypePointer Function %37
+         %82 = OpTypePointer Private %37
+         %44 = OpConstant %21 1
+         %66 = OpTypeStruct %17 %21 %6 %33 %37
+         %85 = OpConstantComposite %66 %20 %24 %25 %36 %84
+         %67 = OpTypePointer Function %66
+         %83 = OpTypePointer Private %66
+         %86 = OpVariable %79 Private %20
+         %87 = OpUndef %79
+         %88 = OpConstantNull %79
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %19 = OpVariable %18 Function
+         %23 = OpVariable %22 Function
+         %26 = OpVariable %7 Function
+         %30 = OpVariable %7 Function
+         %35 = OpVariable %34 Function
+         %39 = OpVariable %38 Function
+         %68 = OpVariable %67 Function
+               OpStore %19 %20
+               OpStore %23 %24
+               OpStore %26 %25
+         %27 = OpFunctionCall %6 %10 %26
+               OpSelectionMerge %29 None
+               OpBranchConditional %27 %28 %31
+         %28 = OpLabel
+               OpBranch %29
+         %31 = OpLabel
+               OpBranch %29
+         %76 = OpLabel
+         %77 = OpLogicalEqual %6 %25 %32
+               OpBranch %29
+         %29 = OpLabel
+         %75 = OpPhi %6 %25 %31 %32 %28 %77 %76
+               OpStore %30 %75
+         %40 = OpLoad %33 %35
+         %41 = OpLoad %33 %35
+         %42 = OpLoad %33 %35
+         %43 = OpLoad %33 %35
+         %45 = OpCompositeExtract %21 %40 0
+         %46 = OpCompositeExtract %21 %40 1
+         %47 = OpCompositeExtract %21 %40 2
+         %48 = OpCompositeExtract %21 %40 3
+         %49 = OpCompositeExtract %21 %41 0
+         %50 = OpCompositeExtract %21 %41 1
+         %51 = OpCompositeExtract %21 %41 2
+         %52 = OpCompositeExtract %21 %41 3
+         %53 = OpCompositeExtract %21 %42 0
+         %54 = OpCompositeExtract %21 %42 1
+         %55 = OpCompositeExtract %21 %42 2
+         %56 = OpCompositeExtract %21 %42 3
+         %57 = OpCompositeExtract %21 %43 0
+         %58 = OpCompositeExtract %21 %43 1
+         %59 = OpCompositeExtract %21 %43 2
+         %60 = OpCompositeExtract %21 %43 3
+         %61 = OpCompositeConstruct %33 %45 %46 %47 %48
+         %62 = OpCompositeConstruct %33 %49 %50 %51 %52
+         %63 = OpCompositeConstruct %33 %53 %54 %55 %56
+         %64 = OpCompositeConstruct %33 %57 %58 %59 %60
+         %65 = OpCompositeConstruct %37 %61 %62 %63 %64
+               OpStore %39 %65
+         %69 = OpLoad %17 %19
+         %70 = OpLoad %21 %23
+         %71 = OpLoad %6 %30
+         %72 = OpLoad %33 %35
+         %73 = OpLoad %37 %39
+         %74 = OpCompositeConstruct %66 %69 %70 %71 %72 %73
+               OpStore %68 %74
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %6 None %8
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+         %12 = OpVariable %7 Function
+         %13 = OpLoad %6 %9
+               OpStore %12 %13
+         %14 = OpLoad %6 %12
+               OpReturnValue %14
+               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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Target id is not fresh (59).
+  ASSERT_FALSE(TransformationAddCopyMemory(
+                   MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 59, 19,
+                   SpvStorageClassPrivate, 20)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Instruction descriptor is invalid (id 89 is undefined).
+  ASSERT_FALSE(TransformationAddCopyMemory(
+                   MakeInstructionDescriptor(89, SpvOpVariable, 0), 89, 19,
+                   SpvStorageClassPrivate, 20)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Cannot insert OpCopyMemory before OpPhi.
+  ASSERT_FALSE(
+      TransformationAddCopyMemory(MakeInstructionDescriptor(75, SpvOpPhi, 0),
+                                  89, 19, SpvStorageClassPrivate, 20)
+          .IsApplicable(context.get(), transformation_context));
+
+  // Source instruction is invalid.
+  ASSERT_FALSE(TransformationAddCopyMemory(
+                   MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 89, 76,
+                   SpvStorageClassPrivate, 0)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Source instruction's type doesn't exist.
+  ASSERT_FALSE(TransformationAddCopyMemory(
+                   MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 89, 5,
+                   SpvStorageClassPrivate, 0)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Source instruction's type is invalid.
+  ASSERT_FALSE(
+      TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0),
+                                  89, 40, SpvStorageClassPrivate, 0)
+          .IsApplicable(context.get(), transformation_context));
+
+  // Source instruction is OpUndef.
+  ASSERT_FALSE(
+      TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0),
+                                  89, 87, SpvStorageClassPrivate, 0)
+          .IsApplicable(context.get(), transformation_context));
+
+  // Source instruction is OpConstantNull.
+  ASSERT_FALSE(
+      TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0),
+                                  89, 88, SpvStorageClassPrivate, 0)
+          .IsApplicable(context.get(), transformation_context));
+
+  // Storage class is invalid.
+  ASSERT_FALSE(TransformationAddCopyMemory(
+                   MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 89, 19,
+                   SpvStorageClassWorkgroup, 20)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Initializer is 0.
+  ASSERT_FALSE(TransformationAddCopyMemory(
+                   MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 89, 19,
+                   SpvStorageClassPrivate, 0)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Initializer has wrong type.
+  ASSERT_FALSE(TransformationAddCopyMemory(
+                   MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 89, 19,
+                   SpvStorageClassPrivate, 25)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Source and target instructions are in different functions.
+  ASSERT_FALSE(
+      TransformationAddCopyMemory(MakeInstructionDescriptor(13, SpvOpLoad, 0),
+                                  89, 19, SpvStorageClassPrivate, 20)
+          .IsApplicable(context.get(), transformation_context));
+
+  // Source instruction doesn't dominate the target instruction.
+  ASSERT_FALSE(TransformationAddCopyMemory(
+                   MakeInstructionDescriptor(77, SpvOpLogicalEqual, 0), 89, 19,
+                   SpvStorageClassPrivate, 20)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Source and target instructions are the same.
+  ASSERT_FALSE(TransformationAddCopyMemory(
+                   MakeInstructionDescriptor(19, SpvOpVariable, 0), 89, 19,
+                   SpvStorageClassPrivate, 20)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Correct transformations.
+  uint32_t fresh_id = 89;
+  auto descriptor = MakeInstructionDescriptor(27, SpvOpFunctionCall, 0);
+  std::vector<uint32_t> source_ids = {19, 23, 26, 30, 35, 39, 68, 86};
+  std::vector<uint32_t> initializers = {20, 24, 25, 25, 36, 84, 85, 20};
+  std::vector<SpvStorageClass> storage_classes = {SpvStorageClassPrivate,
+                                                  SpvStorageClassFunction};
+  for (size_t i = 0, n = source_ids.size(); i < n; ++i) {
+    TransformationAddCopyMemory transformation(
+        descriptor, fresh_id, source_ids[i],
+        storage_classes[i % storage_classes.size()], initializers[i]);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+    ASSERT_TRUE(IsValid(env, context.get()));
+    ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(fresh_id));
+    fresh_id++;
+  }
+
+  std::string expected = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %19 RelaxedPrecision
+               OpMemberDecorate %66 0 RelaxedPrecision
+               OpDecorate %69 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypePointer Function %6
+         %78 = OpTypePointer Private %6
+          %8 = OpTypeFunction %6 %7
+         %17 = OpTypeInt 32 1
+         %18 = OpTypePointer Function %17
+         %79 = OpTypePointer Private %17
+         %20 = OpConstant %17 0
+         %21 = OpTypeFloat 32
+         %22 = OpTypePointer Function %21
+         %80 = OpTypePointer Private %21
+         %24 = OpConstant %21 0
+         %25 = OpConstantFalse %6
+         %32 = OpConstantTrue %6
+         %33 = OpTypeVector %21 4
+         %34 = OpTypePointer Function %33
+         %81 = OpTypePointer Private %33
+         %36 = OpConstantComposite %33 %24 %24 %24 %24
+         %37 = OpTypeMatrix %33 4
+         %84 = OpConstantComposite %37 %36 %36 %36 %36
+         %38 = OpTypePointer Function %37
+         %82 = OpTypePointer Private %37
+         %44 = OpConstant %21 1
+         %66 = OpTypeStruct %17 %21 %6 %33 %37
+         %85 = OpConstantComposite %66 %20 %24 %25 %36 %84
+         %67 = OpTypePointer Function %66
+         %83 = OpTypePointer Private %66
+         %86 = OpVariable %79 Private %20
+         %87 = OpUndef %79
+         %88 = OpConstantNull %79
+         %89 = OpVariable %79 Private %20
+         %91 = OpVariable %78 Private %25
+         %93 = OpVariable %81 Private %36
+         %95 = OpVariable %83 Private %85
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %96 = OpVariable %18 Function %20
+         %94 = OpVariable %38 Function %84
+         %92 = OpVariable %7 Function %25
+         %90 = OpVariable %22 Function %24
+         %19 = OpVariable %18 Function
+         %23 = OpVariable %22 Function
+         %26 = OpVariable %7 Function
+         %30 = OpVariable %7 Function
+         %35 = OpVariable %34 Function
+         %39 = OpVariable %38 Function
+         %68 = OpVariable %67 Function
+               OpStore %19 %20
+               OpStore %23 %24
+               OpStore %26 %25
+               OpCopyMemory %89 %19
+               OpCopyMemory %90 %23
+               OpCopyMemory %91 %26
+               OpCopyMemory %92 %30
+               OpCopyMemory %93 %35
+               OpCopyMemory %94 %39
+               OpCopyMemory %95 %68
+               OpCopyMemory %96 %86
+         %27 = OpFunctionCall %6 %10 %26
+               OpSelectionMerge %29 None
+               OpBranchConditional %27 %28 %31
+         %28 = OpLabel
+               OpBranch %29
+         %31 = OpLabel
+               OpBranch %29
+         %76 = OpLabel
+         %77 = OpLogicalEqual %6 %25 %32
+               OpBranch %29
+         %29 = OpLabel
+         %75 = OpPhi %6 %25 %31 %32 %28 %77 %76
+               OpStore %30 %75
+         %40 = OpLoad %33 %35
+         %41 = OpLoad %33 %35
+         %42 = OpLoad %33 %35
+         %43 = OpLoad %33 %35
+         %45 = OpCompositeExtract %21 %40 0
+         %46 = OpCompositeExtract %21 %40 1
+         %47 = OpCompositeExtract %21 %40 2
+         %48 = OpCompositeExtract %21 %40 3
+         %49 = OpCompositeExtract %21 %41 0
+         %50 = OpCompositeExtract %21 %41 1
+         %51 = OpCompositeExtract %21 %41 2
+         %52 = OpCompositeExtract %21 %41 3
+         %53 = OpCompositeExtract %21 %42 0
+         %54 = OpCompositeExtract %21 %42 1
+         %55 = OpCompositeExtract %21 %42 2
+         %56 = OpCompositeExtract %21 %42 3
+         %57 = OpCompositeExtract %21 %43 0
+         %58 = OpCompositeExtract %21 %43 1
+         %59 = OpCompositeExtract %21 %43 2
+         %60 = OpCompositeExtract %21 %43 3
+         %61 = OpCompositeConstruct %33 %45 %46 %47 %48
+         %62 = OpCompositeConstruct %33 %49 %50 %51 %52
+         %63 = OpCompositeConstruct %33 %53 %54 %55 %56
+         %64 = OpCompositeConstruct %33 %57 %58 %59 %60
+         %65 = OpCompositeConstruct %37 %61 %62 %63 %64
+               OpStore %39 %65
+         %69 = OpLoad %17 %19
+         %70 = OpLoad %21 %23
+         %71 = OpLoad %6 %30
+         %72 = OpLoad %33 %35
+         %73 = OpLoad %37 %39
+         %74 = OpCompositeConstruct %66 %69 %70 %71 %72 %73
+               OpStore %68 %74
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %6 None %8
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+         %12 = OpVariable %7 Function
+         %13 = OpLoad %6 %9
+               OpStore %12 %13
+         %14 = OpLoad %6 %12
+               OpReturnValue %14
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, expected, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
\ No newline at end of file
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 8400b0c..19fac35 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
@@ -1196,15 +1196,15 @@
       TransformationAddDeadBreak(header_for_j, merge_do_while, true, {})
           .IsApplicable(context.get(), transformation_context));
 
-  // Not OK to break loop from its continue construct
+  // Not OK to break loop from its continue construct, except from the back-edge
+  // block.
   ASSERT_FALSE(
       TransformationAddDeadBreak(continue_do_while, merge_do_while, true, {})
           .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationAddDeadBreak(continue_for_j, merge_for_j, false, {})
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(TransformationAddDeadBreak(continue_for_i, merge_for_i, true, {})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationAddDeadBreak(continue_for_j, merge_for_j, false, {})
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationAddDeadBreak(continue_for_i, merge_for_i, true, {})
+                  .IsApplicable(context.get(), transformation_context));
 
   // Not OK to break out of multiple non-loop constructs if not breaking to a
   // loop merge
@@ -1468,11 +1468,117 @@
   TransformationContext transformation_context(&fact_manager,
                                                validator_options);
 
-  // Not OK to break loop from its continue construct
+  // Not OK to break loop from its continue construct, except from the back-edge
+  // block.
   ASSERT_FALSE(TransformationAddDeadBreak(13, 12, true, {})
                    .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(TransformationAddDeadBreak(23, 12, true, {})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationAddDeadBreak(23, 12, true, {})
+                  .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationAddDeadBreakTest, BreakFromBackEdgeBlock) {
+  std::string reference_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %10 "main"
+
+; Types
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 0
+          %5 = OpTypeBool
+          %6 = OpTypePointer Function %4
+
+; Constants
+          %7 = OpConstant %4 0
+          %8 = OpConstant %4 1
+          %9 = OpConstantTrue %5
+
+; main function
+         %10 = OpFunction %2 None %3
+         %11 = OpLabel
+         %12 = OpVariable %6 Function
+               OpStore %12 %7
+               OpBranch %13
+         %13 = OpLabel
+               OpLoopMerge %21 %18 None ; structured loop
+               OpBranch %14
+         %14 = OpLabel
+         %15 = OpLoad %4 %12
+         %16 = OpULessThan %5 %15 %8 ; i < 1 ?
+               OpBranchConditional %16 %17 %21 ; body or break
+         %17 = OpLabel ; body
+               OpBranch %18
+         %18 = OpLabel ; continue target does not strictly dominates the back-edge block
+         %19 = OpLoad %4 %12
+         %20 = OpIAdd %4 %19 %8 ; ++i
+               OpStore %12 %20
+               OpBranch %13
+         %21 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto transformation = TransformationAddDeadBreak(18, 21, true, {});
+  transformation.Apply(context.get(), &transformation_context);
+
+  std::string variant_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %10 "main"
+
+; Types
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 0
+          %5 = OpTypeBool
+          %6 = OpTypePointer Function %4
+
+; Constants
+          %7 = OpConstant %4 0
+          %8 = OpConstant %4 1
+          %9 = OpConstantTrue %5
+
+; main function
+         %10 = OpFunction %2 None %3
+         %11 = OpLabel
+         %12 = OpVariable %6 Function
+               OpStore %12 %7
+               OpBranch %13
+         %13 = OpLabel
+               OpLoopMerge %21 %18 None ; structured loop
+               OpBranch %14
+         %14 = OpLabel
+         %15 = OpLoad %4 %12
+         %16 = OpULessThan %5 %15 %8 ; i < 1 ?
+               OpBranchConditional %16 %17 %21 ; body or break
+         %17 = OpLabel ; body
+               OpBranch %18
+         %18 = OpLabel ; continue target does not strictly dominates the back-edge block
+         %19 = OpLoad %4 %12
+         %20 = OpIAdd %4 %19 %8 ; ++i
+               OpStore %12 %20
+               OpBranchConditional %9 %13 %21
+         %21 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
 }
 
 TEST(TransformationAddDeadBreakTest, SelectionInContinueConstruct) {
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_image_sample_unused_components_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_image_sample_unused_components_test.cpp
new file mode 100644
index 0000000..fc78f9f
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_image_sample_unused_components_test.cpp
@@ -0,0 +1,256 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_image_sample_unused_components.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddImageSampleUnusedComponentsTest, IsApplicable) {
+  std::string shader = R"(
+               OpCapability Shader
+               OpCapability LiteralSampler
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %18 "main" %17
+               OpExecutionMode %18 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %18 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeFloat 32
+          %5 = OpTypeVector %4 2
+          %6 = OpTypeVector %4 3
+          %7 = OpTypeVector %4 4
+          %8 = OpTypeImage %4 2D 0 0 0 1 Rgba32f
+          %9 = OpTypePointer Image %8
+         %10 = OpTypeSampledImage %8
+         %11 = OpTypeSampler
+         %12 = OpConstant %4 1
+         %13 = OpConstant %4 2
+         %14 = OpConstant %4 3
+         %15 = OpConstant %4 4
+         %16 = OpConstantSampler %11 None 0 Linear
+         %17 = OpVariable %9 Image
+         %18 = OpFunction %2 None %3
+         %19 = OpLabel
+         %20 = OpLoad %8 %17
+         %21 = OpSampledImage %10 %20 %16
+         %22 = OpCompositeConstruct %5 %12 %13
+         %23 = OpCompositeConstruct %6 %22 %14
+         %24 = OpCompositeConstruct %7 %23 %15
+         %25 = OpImageSampleImplicitLod %7 %21 %22
+         %26 = OpImageSampleExplicitLod %7 %21 %23 Lod %12
+         %27 = OpImageSampleExplicitLod %7 %21 %24 Lod %12
+               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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Tests applicable image instruction.
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+  auto transformation =
+      TransformationAddImageSampleUnusedComponents(23, instruction_descriptor);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(26, SpvOpImageSampleExplicitLod, 0);
+  transformation =
+      TransformationAddImageSampleUnusedComponents(24, instruction_descriptor);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests undefined image instructions.
+  instruction_descriptor =
+      MakeInstructionDescriptor(27, SpvOpImageSampleImplicitLod, 0);
+  transformation =
+      TransformationAddImageSampleUnusedComponents(23, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(28, SpvOpImageSampleExplicitLod, 0);
+  transformation =
+      TransformationAddImageSampleUnusedComponents(23, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests non-image instructions.
+  instruction_descriptor = MakeInstructionDescriptor(19, SpvOpLabel, 0);
+  transformation =
+      TransformationAddImageSampleUnusedComponents(24, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  instruction_descriptor = MakeInstructionDescriptor(20, SpvOpLoad, 0);
+  transformation =
+      TransformationAddImageSampleUnusedComponents(24, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests coordinate operand being a vec4.
+  instruction_descriptor =
+      MakeInstructionDescriptor(27, SpvOpImageSampleExplicitLod, 0);
+  transformation =
+      TransformationAddImageSampleUnusedComponents(22, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests undefined coordinate with unused operands.
+  instruction_descriptor =
+      MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+  transformation =
+      TransformationAddImageSampleUnusedComponents(27, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests coordinate with unused operands being a non-OpCompositeConstruct
+  // instruction.
+  instruction_descriptor =
+      MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+  transformation =
+      TransformationAddImageSampleUnusedComponents(21, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests the first OpCompositeConstruct constituent not being the original
+  // coordinate.
+  instruction_descriptor =
+      MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+  transformation =
+      TransformationAddImageSampleUnusedComponents(22, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationAddImageSampleUnusedComponentsTest, Apply) {
+  std::string reference_shader = R"(
+               OpCapability Shader
+               OpCapability LiteralSampler
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %18 "main" %17
+               OpExecutionMode %18 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %18 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeFloat 32
+          %5 = OpTypeVector %4 2
+          %6 = OpTypeVector %4 3
+          %7 = OpTypeVector %4 4
+          %8 = OpTypeImage %4 2D 0 0 0 1 Rgba32f
+          %9 = OpTypePointer Image %8
+         %10 = OpTypeSampledImage %8
+         %11 = OpTypeSampler
+         %12 = OpConstant %4 1
+         %13 = OpConstant %4 2
+         %14 = OpConstant %4 3
+         %15 = OpConstant %4 4
+         %16 = OpConstantSampler %11 None 0 Linear
+         %17 = OpVariable %9 Image
+         %18 = OpFunction %2 None %3
+         %19 = OpLabel
+         %20 = OpLoad %8 %17
+         %21 = OpSampledImage %10 %20 %16
+         %22 = OpCompositeConstruct %5 %12 %13
+         %23 = OpCompositeConstruct %6 %22 %14
+         %24 = OpCompositeConstruct %7 %23 %15
+         %25 = OpImageSampleImplicitLod %7 %21 %22
+         %26 = OpImageSampleExplicitLod %7 %21 %23 Lod %12
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+  auto transformation =
+      TransformationAddImageSampleUnusedComponents(23, instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(26, SpvOpImageSampleExplicitLod, 0);
+  transformation =
+      TransformationAddImageSampleUnusedComponents(24, instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  std::string variant_shader = R"(
+               OpCapability Shader
+               OpCapability LiteralSampler
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %18 "main" %17
+               OpExecutionMode %18 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %18 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeFloat 32
+          %5 = OpTypeVector %4 2
+          %6 = OpTypeVector %4 3
+          %7 = OpTypeVector %4 4
+          %8 = OpTypeImage %4 2D 0 0 0 1 Rgba32f
+          %9 = OpTypePointer Image %8
+         %10 = OpTypeSampledImage %8
+         %11 = OpTypeSampler
+         %12 = OpConstant %4 1
+         %13 = OpConstant %4 2
+         %14 = OpConstant %4 3
+         %15 = OpConstant %4 4
+         %16 = OpConstantSampler %11 None 0 Linear
+         %17 = OpVariable %9 Image
+         %18 = OpFunction %2 None %3
+         %19 = OpLabel
+         %20 = OpLoad %8 %17
+         %21 = OpSampledImage %10 %20 %16
+         %22 = OpCompositeConstruct %5 %12 %13
+         %23 = OpCompositeConstruct %6 %22 %14
+         %24 = OpCompositeConstruct %7 %23 %15
+         %25 = OpImageSampleImplicitLod %7 %21 %23
+         %26 = OpImageSampleExplicitLod %7 %21 %24 Lod %12
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_parameter_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_parameter_test.cpp
new file mode 100644
index 0000000..6593d00
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_parameter_test.cpp
@@ -0,0 +1,128 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_parameter.h"
+
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddParameterTest, 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
+          %7 = OpTypeBool
+         %11 = OpTypeInt 32 1
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFunction %7 %7
+          %8 = OpConstant %11 23
+         %12 = OpConstantTrue %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %13 = OpFunctionCall %7 %9 %12
+               OpReturn
+               OpFunctionEnd
+          %9 = OpFunction %7 None %6
+         %14 = OpFunctionParameter %7
+         %10 = OpLabel
+               OpReturnValue %12
+               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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Can't modify entry point function.
+  ASSERT_FALSE(TransformationAddParameter(4, 15, 12, 16)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // There is no function with result id 29.
+  ASSERT_FALSE(TransformationAddParameter(29, 15, 8, 16)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Parameter id is not fresh.
+  ASSERT_FALSE(TransformationAddParameter(9, 14, 8, 16)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Function type id is not fresh.
+  ASSERT_FALSE(TransformationAddParameter(9, 15, 8, 14)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Function type id and parameter type id are equal.
+  ASSERT_FALSE(TransformationAddParameter(9, 15, 8, 15)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Parameter's initializer doesn't exist.
+  ASSERT_FALSE(TransformationAddParameter(9, 15, 15, 16)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Correct transformation.
+  TransformationAddParameter correct(9, 15, 8, 16);
+  ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context));
+  correct.Apply(context.get(), &transformation_context);
+
+  // The module remains valid.
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  ASSERT_TRUE(fact_manager.IdIsIrrelevant(15));
+
+  std::string expected_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
+          %7 = OpTypeBool
+         %11 = OpTypeInt 32 1
+          %3 = OpTypeFunction %2
+          %8 = OpConstant %11 23
+         %12 = OpConstantTrue %7
+          %6 = OpTypeFunction %7 %7 %11
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %13 = OpFunctionCall %7 %9 %12 %8
+               OpReturn
+               OpFunctionEnd
+          %9 = OpFunction %7 None %6
+         %14 = OpFunctionParameter %7
+         %15 = OpFunctionParameter %11
+         %10 = OpLabel
+               OpReturnValue %12
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_relaxed_decoration_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_relaxed_decoration_test.cpp
new file mode 100644
index 0000000..6e163ad
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_relaxed_decoration_test.cpp
@@ -0,0 +1,141 @@
+// 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_relaxed_decoration.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+TEST(TransformationAddRelaxedDecorationTest, BasicScenarios) {
+  // This is a simple transformation and this test handles the main cases.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "b"
+               OpName %14 "c"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 4
+         %11 = OpConstant %6 6
+         %12 = OpTypeBool
+         %13 = OpTypePointer Function %12
+         %15 = OpConstantTrue %12
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %14 %15
+               OpSelectionMerge %19 None
+               OpBranchConditional %15 %19 %100
+        %100 = OpLabel
+         %25 = OpISub %6 %9 %11
+         %28 = OpLogicalNot %12 %15
+               OpBranch %19
+         %19 = OpLabel
+         %27 = OpISub %6 %9 %11
+               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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  fact_manager.AddFactBlockIsDead(100);
+
+  // Invalid: 200 is not an id.
+  ASSERT_FALSE(TransformationAddRelaxedDecoration(200).IsApplicable(
+      context.get(), transformation_context));
+  // Invalid: 27 is not in a dead block.
+  ASSERT_FALSE(TransformationAddRelaxedDecoration(27).IsApplicable(
+      context.get(), transformation_context));
+  // Invalid: 28 is in a dead block, but returns bool (not numeric).
+  ASSERT_FALSE(TransformationAddRelaxedDecoration(28).IsApplicable(
+      context.get(), transformation_context));
+  // It is valid to add RelaxedPrecision to 25 (and it's fine to
+  // have a duplicate).
+  for (uint32_t result_id : {25u, 25u}) {
+    TransformationAddRelaxedDecoration transformation(result_id);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "b"
+               OpName %14 "c"
+               OpDecorate %25 RelaxedPrecision
+               OpDecorate %25 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 4
+         %11 = OpConstant %6 6
+         %12 = OpTypeBool
+         %13 = OpTypePointer Function %12
+         %15 = OpConstantTrue %12
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %14 %15
+               OpSelectionMerge %19 None
+               OpBranchConditional %15 %19 %100
+        %100 = OpLabel
+         %25 = OpISub %6 %9 %11
+         %28 = OpLogicalNot %12 %15
+               OpBranch %19
+         %19 = OpLabel
+         %27 = OpISub %6 %9 %11
+               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_synonym_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_synonym_test.cpp
new file mode 100644
index 0000000..1adc948
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_synonym_test.cpp
@@ -0,0 +1,1397 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_synonym.h"
+
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddSynonymTest, NotApplicable) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %8 RelaxedPrecision
+               OpDecorate %22 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 3
+         %10 = OpTypeFloat 32
+         %11 = OpTypePointer Function %10
+         %13 = OpConstant %10 4.5
+         %14 = OpTypeVector %10 2
+         %15 = OpTypePointer Function %14
+         %17 = OpConstant %10 3
+         %18 = OpConstant %10 4
+         %19 = OpConstantComposite %14 %17 %18
+         %20 = OpTypeVector %6 2
+         %21 = OpTypePointer Function %20
+         %23 = OpConstant %6 4
+         %24 = OpConstantComposite %20 %9 %23
+         %26 = OpConstantNull %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %12 = OpVariable %11 Function
+         %16 = OpVariable %15 Function
+         %22 = OpVariable %21 Function
+               OpStore %8 %9
+               OpStore %12 %13
+               OpStore %16 %19
+               OpStore %22 %24
+         %25 = OpUndef %6
+         %27 = OpLoad %6 %8
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  fact_manager.AddFactIdIsIrrelevant(24);
+
+  auto insert_before = MakeInstructionDescriptor(22, SpvOpReturn, 0);
+
+#ifndef NDEBUG
+  ASSERT_DEATH(
+      TransformationAddSynonym(
+          9, static_cast<protobufs::TransformationAddSynonym::SynonymType>(-1),
+          40, insert_before)
+          .IsApplicable(context.get(), transformation_context),
+      "Synonym type is invalid");
+#endif
+
+  // These tests should succeed regardless of the synonym type.
+  for (int i = 0;
+       i < protobufs::TransformationAddSynonym::SynonymType_descriptor()
+               ->value_count();
+       ++i) {
+    const auto* synonym_value =
+        protobufs::TransformationAddSynonym::SynonymType_descriptor()->value(i);
+    ASSERT_TRUE(protobufs::TransformationAddSynonym::SynonymType_IsValid(
+        synonym_value->number()));
+    auto synonym_type =
+        static_cast<protobufs::TransformationAddSynonym::SynonymType>(
+            synonym_value->number());
+
+    // |synonym_fresh_id| is not fresh.
+    ASSERT_FALSE(TransformationAddSynonym(9, synonym_type, 9, insert_before)
+                     .IsApplicable(context.get(), transformation_context));
+
+    // |result_id| is invalid.
+    ASSERT_FALSE(TransformationAddSynonym(40, synonym_type, 40, insert_before)
+                     .IsApplicable(context.get(), transformation_context));
+
+    // Instruction with |result_id| has no type id.
+    ASSERT_FALSE(TransformationAddSynonym(5, synonym_type, 40, insert_before)
+                     .IsApplicable(context.get(), transformation_context));
+
+    // Instruction with |result_id| is an OpUndef.
+    ASSERT_FALSE(TransformationAddSynonym(25, synonym_type, 40, insert_before)
+                     .IsApplicable(context.get(), transformation_context));
+
+    // Instruction with |result_id| is an OpConstantNull.
+    ASSERT_FALSE(TransformationAddSynonym(26, synonym_type, 40, insert_before)
+                     .IsApplicable(context.get(), transformation_context));
+
+    // |result_id| is irrelevant.
+    ASSERT_FALSE(TransformationAddSynonym(24, synonym_type, 40, insert_before)
+                     .IsApplicable(context.get(), transformation_context));
+
+    // |insert_before| is invalid.
+    ASSERT_FALSE(
+        TransformationAddSynonym(9, synonym_type, 40,
+                                 MakeInstructionDescriptor(25, SpvOpStore, 0))
+            .IsApplicable(context.get(), transformation_context));
+
+    // Can't insert before |insert_before|.
+    ASSERT_FALSE(
+        TransformationAddSynonym(9, synonym_type, 40,
+                                 MakeInstructionDescriptor(5, SpvOpLabel, 0))
+            .IsApplicable(context.get(), transformation_context));
+    ASSERT_FALSE(TransformationAddSynonym(
+                     9, synonym_type, 40,
+                     MakeInstructionDescriptor(22, SpvOpVariable, 0))
+                     .IsApplicable(context.get(), transformation_context));
+    ASSERT_FALSE(TransformationAddSynonym(
+                     9, synonym_type, 40,
+                     MakeInstructionDescriptor(25, SpvOpFunctionEnd, 0))
+                     .IsApplicable(context.get(), transformation_context));
+
+    // Domination rules are not satisfied.
+    ASSERT_FALSE(
+        TransformationAddSynonym(27, synonym_type, 40,
+                                 MakeInstructionDescriptor(27, SpvOpLoad, 0))
+            .IsApplicable(context.get(), transformation_context));
+    ASSERT_FALSE(
+        TransformationAddSynonym(27, synonym_type, 40,
+                                 MakeInstructionDescriptor(22, SpvOpStore, 1))
+            .IsApplicable(context.get(), transformation_context));
+  }
+}
+
+TEST(TransformationAddSynonymTest, AddZeroSubZeroMulOne) {
+  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 = OpConstant %6 0
+          %8 = OpConstant %6 1
+          %9 = OpConstant %6 34
+         %10 = OpTypeInt 32 0
+         %13 = OpConstant %10 34
+         %14 = OpTypeFloat 32
+         %15 = OpConstant %14 0
+         %16 = OpConstant %14 1
+         %17 = OpConstant %14 34
+         %18 = OpTypeVector %14 2
+         %19 = OpConstantComposite %18 %15 %15
+         %20 = OpConstantComposite %18 %16 %16
+         %21 = OpConstant %14 3
+         %22 = OpConstant %14 4
+         %23 = OpConstantComposite %18 %21 %22
+         %24 = OpTypeVector %6 2
+         %25 = OpConstantComposite %24 %7 %7
+         %26 = OpConstantComposite %24 %8 %8
+         %27 = OpConstant %6 3
+         %28 = OpConstant %6 4
+         %29 = OpConstantComposite %24 %27 %28
+         %30 = OpTypeVector %10 2
+         %33 = OpConstant %10 3
+         %34 = OpConstant %10 4
+         %35 = OpConstantComposite %30 %33 %34
+         %36 = OpTypeBool
+         %37 = OpTypeVector %36 2
+         %38 = OpConstantTrue %36
+         %39 = OpConstantComposite %37 %38 %38
+          %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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
+
+  uint32_t fresh_id = 50;
+  for (auto synonym_type : {protobufs::TransformationAddSynonym::ADD_ZERO,
+                            protobufs::TransformationAddSynonym::SUB_ZERO,
+                            protobufs::TransformationAddSynonym::MUL_ONE}) {
+    ASSERT_TRUE(
+        TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type));
+
+    // Can't create a synonym of a scalar or a vector of a wrong (in this case -
+    // boolean) type.
+    ASSERT_FALSE(
+        TransformationAddSynonym(38, synonym_type, fresh_id, insert_before)
+            .IsApplicable(context.get(), transformation_context));
+    ASSERT_FALSE(
+        TransformationAddSynonym(39, synonym_type, fresh_id, insert_before)
+            .IsApplicable(context.get(), transformation_context));
+
+    // Required constant is not present in the module.
+    ASSERT_FALSE(
+        TransformationAddSynonym(13, synonym_type, fresh_id, insert_before)
+            .IsApplicable(context.get(), transformation_context));
+    ASSERT_FALSE(
+        TransformationAddSynonym(35, synonym_type, fresh_id, insert_before)
+            .IsApplicable(context.get(), transformation_context));
+
+    for (auto result_id : {9, 17, 23, 29}) {
+      TransformationAddSynonym transformation(result_id, synonym_type, fresh_id,
+                                              insert_before);
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      transformation.Apply(context.get(), &transformation_context);
+      ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(result_id, {}),
+                                            MakeDataDescriptor(fresh_id, {})));
+      ++fresh_id;
+    }
+  }
+
+  std::string expected_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 = OpConstant %6 0
+          %8 = OpConstant %6 1
+          %9 = OpConstant %6 34
+         %10 = OpTypeInt 32 0
+         %13 = OpConstant %10 34
+         %14 = OpTypeFloat 32
+         %15 = OpConstant %14 0
+         %16 = OpConstant %14 1
+         %17 = OpConstant %14 34
+         %18 = OpTypeVector %14 2
+         %19 = OpConstantComposite %18 %15 %15
+         %20 = OpConstantComposite %18 %16 %16
+         %21 = OpConstant %14 3
+         %22 = OpConstant %14 4
+         %23 = OpConstantComposite %18 %21 %22
+         %24 = OpTypeVector %6 2
+         %25 = OpConstantComposite %24 %7 %7
+         %26 = OpConstantComposite %24 %8 %8
+         %27 = OpConstant %6 3
+         %28 = OpConstant %6 4
+         %29 = OpConstantComposite %24 %27 %28
+         %30 = OpTypeVector %10 2
+         %33 = OpConstant %10 3
+         %34 = OpConstant %10 4
+         %35 = OpConstantComposite %30 %33 %34
+         %36 = OpTypeBool
+         %37 = OpTypeVector %36 2
+         %38 = OpConstantTrue %36
+         %39 = OpConstantComposite %37 %38 %38
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %50 = OpIAdd %6 %9 %7
+         %51 = OpFAdd %14 %17 %15
+         %52 = OpFAdd %18 %23 %19
+         %53 = OpIAdd %24 %29 %25
+         %54 = OpISub %6 %9 %7
+         %55 = OpFSub %14 %17 %15
+         %56 = OpFSub %18 %23 %19
+         %57 = OpISub %24 %29 %25
+         %58 = OpIMul %6 %9 %8
+         %59 = OpFMul %14 %17 %16
+         %60 = OpFMul %18 %23 %20
+         %61 = OpIMul %24 %29 %26
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationAddSynonymTest, LogicalAndLogicalOr) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantFalse %6
+          %9 = OpConstantTrue %6
+         %10 = OpTypeVector %6 2
+         %11 = OpConstantComposite %10 %7 %9
+         %12 = OpConstantComposite %10 %7 %7
+         %13 = OpConstantComposite %10 %9 %9
+         %14 = OpTypeFloat 32
+         %17 = OpConstant %14 35
+         %18 = OpTypeVector %14 2
+         %21 = OpConstant %14 3
+         %22 = OpConstant %14 4
+         %23 = OpConstantComposite %18 %21 %22
+          %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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
+
+  uint32_t fresh_id = 50;
+  for (auto synonym_type : {protobufs::TransformationAddSynonym::LOGICAL_AND,
+                            protobufs::TransformationAddSynonym::LOGICAL_OR}) {
+    ASSERT_TRUE(
+        TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type));
+
+    // Can't create a synonym of a scalar or a vector of a wrong (in this case -
+    // float) type.
+    ASSERT_FALSE(
+        TransformationAddSynonym(17, synonym_type, fresh_id, insert_before)
+            .IsApplicable(context.get(), transformation_context));
+    ASSERT_FALSE(
+        TransformationAddSynonym(23, synonym_type, fresh_id, insert_before)
+            .IsApplicable(context.get(), transformation_context));
+
+    for (auto result_id : {9, 11}) {
+      TransformationAddSynonym transformation(result_id, synonym_type, fresh_id,
+                                              insert_before);
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      transformation.Apply(context.get(), &transformation_context);
+      ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(result_id, {}),
+                                            MakeDataDescriptor(fresh_id, {})));
+      ++fresh_id;
+    }
+  }
+
+  std::string expected_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantFalse %6
+          %9 = OpConstantTrue %6
+         %10 = OpTypeVector %6 2
+         %11 = OpConstantComposite %10 %7 %9
+         %12 = OpConstantComposite %10 %7 %7
+         %13 = OpConstantComposite %10 %9 %9
+         %14 = OpTypeFloat 32
+         %17 = OpConstant %14 35
+         %18 = OpTypeVector %14 2
+         %21 = OpConstant %14 3
+         %22 = OpConstant %14 4
+         %23 = OpConstantComposite %18 %21 %22
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %50 = OpLogicalAnd %6 %9 %9
+         %51 = OpLogicalAnd %10 %11 %13
+         %52 = OpLogicalOr %6 %9 %7
+         %53 = OpLogicalOr %10 %11 %12
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationAddSynonymTest, LogicalAndConstantIsNotPresent) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantFalse %6
+         %10 = OpTypeVector %6 2
+         %12 = OpConstantComposite %10 %7 %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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
+  const auto synonym_type = protobufs::TransformationAddSynonym::LOGICAL_AND;
+
+  // Required constant is not present in the module.
+  ASSERT_FALSE(TransformationAddSynonym(7, synonym_type, 50, insert_before)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddSynonym(12, synonym_type, 50, insert_before)
+                   .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationAddSynonymTest, LogicalOrConstantIsNotPresent) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+         %10 = OpTypeVector %6 2
+         %12 = OpConstantComposite %10 %7 %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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
+  const auto synonym_type = protobufs::TransformationAddSynonym::LOGICAL_OR;
+
+  // Required constant is not present in the module.
+  ASSERT_FALSE(TransformationAddSynonym(7, synonym_type, 50, insert_before)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddSynonym(12, synonym_type, 50, insert_before)
+                   .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationAddSynonymTest, CopyObject) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %8 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 4
+         %10 = OpTypeFloat 32
+         %11 = OpTypePointer Function %10
+         %13 = OpConstant %10 4
+         %14 = OpTypeVector %10 2
+         %15 = OpTypePointer Function %14
+         %17 = OpConstant %10 3.4000001
+         %18 = OpConstantComposite %14 %17 %17
+         %19 = OpTypeBool
+         %20 = OpTypeStruct %19
+         %21 = OpTypePointer Function %20
+         %23 = OpConstantTrue %19
+         %24 = OpConstantComposite %20 %23
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %12 = OpVariable %11 Function
+         %16 = OpVariable %15 Function
+         %22 = OpVariable %21 Function
+               OpStore %8 %9
+               OpStore %12 %13
+               OpStore %16 %18
+               OpStore %22 %24
+               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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
+  const auto synonym_type = protobufs::TransformationAddSynonym::COPY_OBJECT;
+
+  ASSERT_FALSE(
+      TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type));
+
+  uint32_t fresh_id = 50;
+  for (auto result_id : {9, 13, 17, 18, 23, 24, 22}) {
+    TransformationAddSynonym transformation(result_id, synonym_type, fresh_id,
+                                            insert_before);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+    ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(result_id, {}),
+                                          MakeDataDescriptor(fresh_id, {})));
+    ++fresh_id;
+  }
+
+  std::string expected_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %8 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 4
+         %10 = OpTypeFloat 32
+         %11 = OpTypePointer Function %10
+         %13 = OpConstant %10 4
+         %14 = OpTypeVector %10 2
+         %15 = OpTypePointer Function %14
+         %17 = OpConstant %10 3.4000001
+         %18 = OpConstantComposite %14 %17 %17
+         %19 = OpTypeBool
+         %20 = OpTypeStruct %19
+         %21 = OpTypePointer Function %20
+         %23 = OpConstantTrue %19
+         %24 = OpConstantComposite %20 %23
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %12 = OpVariable %11 Function
+         %16 = OpVariable %15 Function
+         %22 = OpVariable %21 Function
+               OpStore %8 %9
+               OpStore %12 %13
+               OpStore %16 %18
+               OpStore %22 %24
+         %50 = OpCopyObject %6 %9
+         %51 = OpCopyObject %10 %13
+         %52 = OpCopyObject %10 %17
+         %53 = OpCopyObject %14 %18
+         %54 = OpCopyObject %19 %23
+         %55 = OpCopyObject %20 %24
+         %56 = OpCopyObject %21 %22
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationAddSynonymTest, CopyBooleanConstants) {
+  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
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+          %8 = OpConstantFalse %6
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  ASSERT_EQ(0, transformation_context.GetFactManager()
+                   ->GetIdsForWhichSynonymsAreKnown()
+                   .size());
+
+  {
+    TransformationAddSynonym copy_true(
+        7, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
+        MakeInstructionDescriptor(5, SpvOpReturn, 0));
+    ASSERT_TRUE(copy_true.IsApplicable(context.get(), transformation_context));
+    copy_true.Apply(context.get(), &transformation_context);
+
+    std::vector<uint32_t> ids_for_which_synonyms_are_known =
+        transformation_context.GetFactManager()
+            ->GetIdsForWhichSynonymsAreKnown();
+    ASSERT_EQ(2, ids_for_which_synonyms_are_known.size());
+    ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
+                          ids_for_which_synonyms_are_known.end(),
+                          7) != ids_for_which_synonyms_are_known.end());
+    ASSERT_EQ(
+        2, transformation_context.GetFactManager()->GetSynonymsForId(7).size());
+    protobufs::DataDescriptor descriptor_100 = MakeDataDescriptor(100, {});
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(7, {}), descriptor_100));
+  }
+
+  {
+    TransformationAddSynonym copy_false(
+        8, protobufs::TransformationAddSynonym::COPY_OBJECT, 101,
+        MakeInstructionDescriptor(100, SpvOpReturn, 0));
+    ASSERT_TRUE(copy_false.IsApplicable(context.get(), transformation_context));
+    copy_false.Apply(context.get(), &transformation_context);
+    std::vector<uint32_t> ids_for_which_synonyms_are_known =
+        transformation_context.GetFactManager()
+            ->GetIdsForWhichSynonymsAreKnown();
+    ASSERT_EQ(4, ids_for_which_synonyms_are_known.size());
+    ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
+                          ids_for_which_synonyms_are_known.end(),
+                          8) != ids_for_which_synonyms_are_known.end());
+    ASSERT_EQ(
+        2, transformation_context.GetFactManager()->GetSynonymsForId(8).size());
+    protobufs::DataDescriptor descriptor_101 = MakeDataDescriptor(101, {});
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(8, {}), descriptor_101));
+  }
+
+  {
+    TransformationAddSynonym copy_false_again(
+        101, protobufs::TransformationAddSynonym::COPY_OBJECT, 102,
+        MakeInstructionDescriptor(5, SpvOpReturn, 0));
+    ASSERT_TRUE(
+        copy_false_again.IsApplicable(context.get(), transformation_context));
+    copy_false_again.Apply(context.get(), &transformation_context);
+    std::vector<uint32_t> ids_for_which_synonyms_are_known =
+        transformation_context.GetFactManager()
+            ->GetIdsForWhichSynonymsAreKnown();
+    ASSERT_EQ(5, ids_for_which_synonyms_are_known.size());
+    ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
+                          ids_for_which_synonyms_are_known.end(),
+                          101) != ids_for_which_synonyms_are_known.end());
+    ASSERT_EQ(
+        3,
+        transformation_context.GetFactManager()->GetSynonymsForId(101).size());
+    protobufs::DataDescriptor descriptor_102 = MakeDataDescriptor(102, {});
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(101, {}), descriptor_102));
+  }
+
+  {
+    TransformationAddSynonym copy_true_again(
+        7, protobufs::TransformationAddSynonym::COPY_OBJECT, 103,
+        MakeInstructionDescriptor(102, SpvOpReturn, 0));
+    ASSERT_TRUE(
+        copy_true_again.IsApplicable(context.get(), transformation_context));
+    copy_true_again.Apply(context.get(), &transformation_context);
+    std::vector<uint32_t> ids_for_which_synonyms_are_known =
+        transformation_context.GetFactManager()
+            ->GetIdsForWhichSynonymsAreKnown();
+    ASSERT_EQ(6, ids_for_which_synonyms_are_known.size());
+    ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
+                          ids_for_which_synonyms_are_known.end(),
+                          7) != ids_for_which_synonyms_are_known.end());
+    ASSERT_EQ(
+        3, transformation_context.GetFactManager()->GetSynonymsForId(7).size());
+    protobufs::DataDescriptor descriptor_103 = MakeDataDescriptor(103, {});
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(7, {}), descriptor_103));
+  }
+
+  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
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+          %8 = OpConstantFalse %6
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %100 = OpCopyObject %6 %7
+        %101 = OpCopyObject %6 %8
+        %102 = OpCopyObject %6 %101
+        %103 = OpCopyObject %6 %7
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationAddSynonymTest, CheckIllegalCases) {
+  // The following SPIR-V comes from this GLSL, pushed through spirv-opt
+  // and then doctored a bit.
+  //
+  // #version 310 es
+  //
+  // precision highp float;
+  //
+  // struct S {
+  //   int a;
+  //   float b;
+  // };
+  //
+  // layout(set = 0, binding = 2) uniform block {
+  //   S s;
+  //   lowp float f;
+  //   int ii;
+  // } ubuf;
+  //
+  // layout(location = 0) out vec4 color;
+  //
+  // void main() {
+  //   float c = 0.0;
+  //   lowp float d = 0.0;
+  //   S localS = ubuf.s;
+  //   for (int i = 0; i < ubuf.s.a; i++) {
+  //     switch (ubuf.ii) {
+  //       case 0:
+  //         c += 0.1;
+  //         d += 0.2;
+  //       case 1:
+  //         c += 0.1;
+  //         if (c > d) {
+  //           d += 0.2;
+  //         } else {
+  //           d += c;
+  //         }
+  //         break;
+  //       default:
+  //         i += 1;
+  //         localS.b += d;
+  //     }
+  //   }
+  //   color = vec4(c, d, localS.b, 1.0);
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %80
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %12 "S"
+               OpMemberName %12 0 "a"
+               OpMemberName %12 1 "b"
+               OpName %15 "S"
+               OpMemberName %15 0 "a"
+               OpMemberName %15 1 "b"
+               OpName %16 "block"
+               OpMemberName %16 0 "s"
+               OpMemberName %16 1 "f"
+               OpMemberName %16 2 "ii"
+               OpName %18 "ubuf"
+               OpName %80 "color"
+               OpMemberDecorate %12 0 RelaxedPrecision
+               OpMemberDecorate %15 0 RelaxedPrecision
+               OpMemberDecorate %15 0 Offset 0
+               OpMemberDecorate %15 1 Offset 4
+               OpMemberDecorate %16 0 Offset 0
+               OpMemberDecorate %16 1 RelaxedPrecision
+               OpMemberDecorate %16 1 Offset 16
+               OpMemberDecorate %16 2 RelaxedPrecision
+               OpMemberDecorate %16 2 Offset 20
+               OpDecorate %16 Block
+               OpDecorate %18 DescriptorSet 0
+               OpDecorate %18 Binding 2
+               OpDecorate %38 RelaxedPrecision
+               OpDecorate %43 RelaxedPrecision
+               OpDecorate %53 RelaxedPrecision
+               OpDecorate %62 RelaxedPrecision
+               OpDecorate %69 RelaxedPrecision
+               OpDecorate %77 RelaxedPrecision
+               OpDecorate %80 Location 0
+               OpDecorate %101 RelaxedPrecision
+               OpDecorate %102 RelaxedPrecision
+               OpDecorate %96 RelaxedPrecision
+               OpDecorate %108 RelaxedPrecision
+               OpDecorate %107 RelaxedPrecision
+               OpDecorate %98 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %9 = OpConstant %6 0
+         %11 = OpTypeInt 32 1
+         %12 = OpTypeStruct %11 %6
+         %15 = OpTypeStruct %11 %6
+         %16 = OpTypeStruct %15 %6 %11
+         %17 = OpTypePointer Uniform %16
+         %18 = OpVariable %17 Uniform
+         %19 = OpConstant %11 0
+         %20 = OpTypePointer Uniform %15
+         %27 = OpConstant %11 1
+         %36 = OpTypePointer Uniform %11
+         %39 = OpTypeBool
+         %41 = OpConstant %11 2
+         %48 = OpConstant %6 0.100000001
+         %51 = OpConstant %6 0.200000003
+         %78 = OpTypeVector %6 4
+         %79 = OpTypePointer Output %78
+         %80 = OpVariable %79 Output
+         %85 = OpConstant %6 1
+         %95 = OpUndef %12
+        %112 = OpTypePointer Uniform %6
+        %113 = OpTypeInt 32 0
+        %114 = OpConstant %113 1
+        %179 = OpTypePointer Function %39
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %180 = OpVariable %179 Function
+        %181 = OpVariable %179 Function
+        %182 = OpVariable %179 Function
+         %21 = OpAccessChain %20 %18 %19
+        %115 = OpAccessChain %112 %21 %114
+        %116 = OpLoad %6 %115
+         %90 = OpCompositeInsert %12 %116 %95 1
+               OpBranch %30
+         %30 = OpLabel
+         %99 = OpPhi %12 %90 %5 %109 %47
+         %98 = OpPhi %6 %9 %5 %107 %47
+         %97 = OpPhi %6 %9 %5 %105 %47
+         %96 = OpPhi %11 %19 %5 %77 %47
+         %37 = OpAccessChain %36 %18 %19 %19
+         %38 = OpLoad %11 %37
+         %40 = OpSLessThan %39 %96 %38
+               OpLoopMerge %32 %47 None
+               OpBranchConditional %40 %31 %32
+         %31 = OpLabel
+         %42 = OpAccessChain %36 %18 %41
+         %43 = OpLoad %11 %42
+               OpSelectionMerge %45 None
+               OpSwitch %43 %46 0 %44 1 %45
+         %46 = OpLabel
+         %69 = OpIAdd %11 %96 %27
+         %72 = OpCompositeExtract %6 %99 1
+         %73 = OpFAdd %6 %72 %98
+         %93 = OpCompositeInsert %12 %73 %99 1
+               OpBranch %47
+         %44 = OpLabel
+         %50 = OpFAdd %6 %97 %48
+         %53 = OpFAdd %6 %98 %51
+               OpBranch %45
+         %45 = OpLabel
+        %101 = OpPhi %6 %98 %31 %53 %44
+        %100 = OpPhi %6 %97 %31 %50 %44
+         %55 = OpFAdd %6 %100 %48
+         %58 = OpFOrdGreaterThan %39 %55 %101
+               OpSelectionMerge %60 None
+               OpBranchConditional %58 %59 %63
+         %59 = OpLabel
+         %62 = OpFAdd %6 %101 %51
+               OpBranch %60
+         %63 = OpLabel
+         %66 = OpFAdd %6 %101 %55
+               OpBranch %60
+         %60 = OpLabel
+        %108 = OpPhi %6 %62 %59 %66 %63
+               OpBranch %47
+         %47 = OpLabel
+        %109 = OpPhi %12 %93 %46 %99 %60
+        %107 = OpPhi %6 %98 %46 %108 %60
+        %105 = OpPhi %6 %97 %46 %55 %60
+        %102 = OpPhi %11 %69 %46 %96 %60
+         %77 = OpIAdd %11 %102 %27
+               OpBranch %30
+         %32 = OpLabel
+         %84 = OpCompositeExtract %6 %99 1
+         %86 = OpCompositeConstruct %78 %97 %98 %84 %85
+               OpStore %80 %86
+               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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Inapplicable because %18 is decorated.
+  ASSERT_FALSE(TransformationAddSynonym(
+                   18, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                   MakeInstructionDescriptor(21, SpvOpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Inapplicable because %77 is decorated.
+  ASSERT_FALSE(TransformationAddSynonym(
+                   77, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                   MakeInstructionDescriptor(77, SpvOpBranch, 0))
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Inapplicable because %80 is decorated.
+  ASSERT_FALSE(TransformationAddSynonym(
+                   80, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                   MakeInstructionDescriptor(77, SpvOpIAdd, 0))
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Inapplicable because %84 is not available at the requested point
+  ASSERT_FALSE(TransformationAddSynonym(
+                   84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                   MakeInstructionDescriptor(32, SpvOpCompositeExtract, 0))
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Fine because %84 is available at the requested point
+  ASSERT_TRUE(TransformationAddSynonym(
+                  84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                  MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0))
+                  .IsApplicable(context.get(), transformation_context));
+
+  // Inapplicable because id %9 is already in use
+  ASSERT_FALSE(TransformationAddSynonym(
+                   84, protobufs::TransformationAddSynonym::COPY_OBJECT, 9,
+                   MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0))
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Inapplicable because the requested point does not exist
+  ASSERT_FALSE(TransformationAddSynonym(
+                   84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                   MakeInstructionDescriptor(86, SpvOpReturn, 2))
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Inapplicable because %9 is not in a function
+  ASSERT_FALSE(TransformationAddSynonym(
+                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                   MakeInstructionDescriptor(9, SpvOpTypeInt, 0))
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Inapplicable because the insert point is right before, or inside, a chunk
+  // of OpPhis
+  ASSERT_FALSE(TransformationAddSynonym(
+                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                   MakeInstructionDescriptor(30, SpvOpPhi, 0))
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddSynonym(
+                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                   MakeInstructionDescriptor(99, SpvOpPhi, 1))
+                   .IsApplicable(context.get(), transformation_context));
+
+  // OK, because the insert point is just after a chunk of OpPhis.
+  ASSERT_TRUE(TransformationAddSynonym(
+                  9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                  MakeInstructionDescriptor(96, SpvOpAccessChain, 0))
+                  .IsApplicable(context.get(), transformation_context));
+
+  // Inapplicable because the insert point is right after an OpSelectionMerge
+  ASSERT_FALSE(TransformationAddSynonym(
+                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                   MakeInstructionDescriptor(58, SpvOpBranchConditional, 0))
+                   .IsApplicable(context.get(), transformation_context));
+
+  // OK, because the insert point is right before the OpSelectionMerge
+  ASSERT_TRUE(TransformationAddSynonym(
+                  9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                  MakeInstructionDescriptor(58, SpvOpSelectionMerge, 0))
+                  .IsApplicable(context.get(), transformation_context));
+
+  // Inapplicable because the insert point is right after an OpSelectionMerge
+  ASSERT_FALSE(TransformationAddSynonym(
+                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                   MakeInstructionDescriptor(43, SpvOpSwitch, 0))
+                   .IsApplicable(context.get(), transformation_context));
+
+  // OK, because the insert point is right before the OpSelectionMerge
+  ASSERT_TRUE(TransformationAddSynonym(
+                  9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                  MakeInstructionDescriptor(43, SpvOpSelectionMerge, 0))
+                  .IsApplicable(context.get(), transformation_context));
+
+  // Inapplicable because the insert point is right after an OpLoopMerge
+  ASSERT_FALSE(TransformationAddSynonym(
+                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                   MakeInstructionDescriptor(40, SpvOpBranchConditional, 0))
+                   .IsApplicable(context.get(), transformation_context));
+
+  // OK, because the insert point is right before the OpLoopMerge
+  ASSERT_TRUE(TransformationAddSynonym(
+                  9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                  MakeInstructionDescriptor(40, SpvOpLoopMerge, 0))
+                  .IsApplicable(context.get(), transformation_context));
+
+  // Inapplicable because id %300 does not exist
+  ASSERT_FALSE(TransformationAddSynonym(
+                   300, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                   MakeInstructionDescriptor(40, SpvOpLoopMerge, 0))
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Inapplicable because the following instruction is OpVariable
+  ASSERT_FALSE(TransformationAddSynonym(
+                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                   MakeInstructionDescriptor(180, SpvOpVariable, 0))
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddSynonym(
+                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                   MakeInstructionDescriptor(181, SpvOpVariable, 0))
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddSynonym(
+                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                   MakeInstructionDescriptor(182, SpvOpVariable, 0))
+                   .IsApplicable(context.get(), transformation_context));
+
+  // OK, because this is just past the group of OpVariable instructions.
+  ASSERT_TRUE(TransformationAddSynonym(
+                  9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                  MakeInstructionDescriptor(182, SpvOpAccessChain, 0))
+                  .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationAddSynonymTest, MiscellaneousCopies) {
+  // The following SPIR-V comes from this GLSL:
+  //
+  // #version 310 es
+  //
+  // precision highp float;
+  //
+  // float g;
+  //
+  // vec4 h;
+  //
+  // void main() {
+  //   int a;
+  //   int b;
+  //   b = int(g);
+  //   h.x = float(a);
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "b"
+               OpName %11 "g"
+               OpName %16 "h"
+               OpName %17 "a"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpTypeFloat 32
+         %10 = OpTypePointer Private %9
+         %11 = OpVariable %10 Private
+         %14 = OpTypeVector %9 4
+         %15 = OpTypePointer Private %14
+         %16 = OpVariable %15 Private
+         %20 = OpTypeInt 32 0
+         %21 = OpConstant %20 0
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %17 = OpVariable %7 Function
+         %12 = OpLoad %9 %11
+         %13 = OpConvertFToS %6 %12
+               OpStore %8 %13
+         %18 = OpLoad %6 %17
+         %19 = OpConvertSToF %9 %18
+         %22 = OpAccessChain %10 %16 %21
+               OpStore %22 %19
+               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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  std::vector<TransformationAddSynonym> transformations = {
+      TransformationAddSynonym(
+          19, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
+          MakeInstructionDescriptor(22, SpvOpStore, 0)),
+      TransformationAddSynonym(
+          22, protobufs::TransformationAddSynonym::COPY_OBJECT, 101,
+          MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
+      TransformationAddSynonym(
+          12, protobufs::TransformationAddSynonym::COPY_OBJECT, 102,
+          MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
+      TransformationAddSynonym(
+          11, protobufs::TransformationAddSynonym::COPY_OBJECT, 103,
+          MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
+      TransformationAddSynonym(
+          16, protobufs::TransformationAddSynonym::COPY_OBJECT, 104,
+          MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
+      TransformationAddSynonym(
+          8, protobufs::TransformationAddSynonym::COPY_OBJECT, 105,
+          MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
+      TransformationAddSynonym(
+          17, protobufs::TransformationAddSynonym::COPY_OBJECT, 106,
+          MakeInstructionDescriptor(22, SpvOpCopyObject, 0))};
+
+  for (auto& transformation : transformations) {
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "b"
+               OpName %11 "g"
+               OpName %16 "h"
+               OpName %17 "a"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpTypeFloat 32
+         %10 = OpTypePointer Private %9
+         %11 = OpVariable %10 Private
+         %14 = OpTypeVector %9 4
+         %15 = OpTypePointer Private %14
+         %16 = OpVariable %15 Private
+         %20 = OpTypeInt 32 0
+         %21 = OpConstant %20 0
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %17 = OpVariable %7 Function
+         %12 = OpLoad %9 %11
+         %13 = OpConvertFToS %6 %12
+               OpStore %8 %13
+         %18 = OpLoad %6 %17
+         %19 = OpConvertSToF %9 %18
+         %22 = OpAccessChain %10 %16 %21
+        %106 = OpCopyObject %7 %17
+        %105 = OpCopyObject %7 %8
+        %104 = OpCopyObject %15 %16
+        %103 = OpCopyObject %10 %11
+        %102 = OpCopyObject %9 %12
+        %101 = OpCopyObject %10 %22
+        %100 = OpCopyObject %9 %19
+               OpStore %22 %19
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationAddSynonymTest, 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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Illegal to copy null.
+  ASSERT_FALSE(TransformationAddSynonym(
+                   8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
+                   MakeInstructionDescriptor(5, SpvOpReturn, 0))
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Illegal to copy an OpUndef of pointer type.
+  ASSERT_FALSE(TransformationAddSynonym(
+                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
+                   MakeInstructionDescriptor(5, SpvOpReturn, 0))
+                   .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationAddSynonymTest, 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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(8);
+
+  TransformationAddSynonym transformation1(
+      8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
+      MakeInstructionDescriptor(9, SpvOpReturn, 0));
+  TransformationAddSynonym transformation2(
+      9, protobufs::TransformationAddSynonym::COPY_OBJECT, 101,
+      MakeInstructionDescriptor(9, SpvOpReturn, 0));
+  TransformationAddSynonym transformation3(
+      100, protobufs::TransformationAddSynonym::COPY_OBJECT, 102,
+      MakeInstructionDescriptor(9, SpvOpReturn, 0));
+
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
+
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(8));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(9));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
+}
+
+TEST(TransformationAddSynonym, DoNotCopyOpSampledImage) {
+  // This checks that we do not try to copy the result id of an OpSampledImage
+  // instruction.
+  std::string shader = R"(
+               OpCapability Shader
+               OpCapability SampledBuffer
+               OpCapability ImageBuffer
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %40 %41
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource GLSL 450
+               OpDecorate %40 DescriptorSet 0
+               OpDecorate %40 Binding 69
+               OpDecorate %41 DescriptorSet 0
+               OpDecorate %41 Binding 1
+         %54 = OpTypeFloat 32
+         %76 = OpTypeVector %54 4
+         %55 = OpConstant %54 0
+         %56 = OpTypeVector %54 3
+         %94 = OpTypeVector %54 2
+        %112 = OpConstantComposite %94 %55 %55
+         %57 = OpConstantComposite %56 %55 %55 %55
+         %15 = OpTypeImage %54 2D 2 0 0 1 Unknown
+        %114 = OpTypePointer UniformConstant %15
+         %38 = OpTypeSampler
+        %125 = OpTypePointer UniformConstant %38
+        %132 = OpTypeVoid
+        %133 = OpTypeFunction %132
+         %45 = OpTypeSampledImage %15
+         %40 = OpVariable %114 UniformConstant
+         %41 = OpVariable %125 UniformConstant
+          %2 = OpFunction %132 None %133
+        %164 = OpLabel
+        %184 = OpLoad %15 %40
+        %213 = OpLoad %38 %41
+        %216 = OpSampledImage %45 %184 %213
+        %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55
+               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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  ASSERT_FALSE(
+      TransformationAddSynonym(
+          216, protobufs::TransformationAddSynonym::COPY_OBJECT, 500,
+          MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0))
+          .IsApplicable(context.get(), transformation_context));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_composite_construct_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_composite_construct_test.cpp
index b663866..df6f382 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_composite_construct_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_composite_construct_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/transformation_composite_construct.h"
+
 #include "source/fuzz/data_descriptor.h"
 #include "source/fuzz/instruction_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
@@ -1358,6 +1359,176 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationCompositeConstructTest, AddSynonymsForRelevantIds) {
+  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 = OpTypeFloat 32
+          %7 = OpTypeVector %6 3
+          %8 = OpTypePointer Function %7
+         %10 = OpConstant %6 1
+         %11 = OpConstantComposite %7 %10 %10 %10
+         %17 = OpTypeVector %6 4
+         %18 = OpTypePointer Function %17
+         %21 = OpConstant %6 2
+         %32 = OpTypeMatrix %17 3
+         %33 = OpTypePointer Private %32
+         %34 = OpVariable %33 Private
+         %35 = OpTypeMatrix %7 4
+         %36 = OpTypePointer Private %35
+         %37 = OpVariable %36 Private
+         %38 = OpTypeVector %6 2
+         %39 = OpTypeInt 32 0
+         %40 = OpConstant %39 3
+         %41 = OpTypeArray %38 %40
+         %42 = OpTypePointer Private %41
+         %43 = OpVariable %42 Private
+        %100 = OpUndef %7
+        %101 = OpUndef %17
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %9 = OpVariable %8 Function
+         %12 = OpVariable %8 Function
+         %14 = OpVariable %8 Function
+         %19 = OpVariable %18 Function
+         %26 = OpVariable %18 Function
+         %29 = OpVariable %18 Function
+               OpStore %9 %11
+         %13 = OpLoad %7 %9
+               OpStore %12 %13
+         %15 = OpLoad %7 %12
+         %16 = OpVectorShuffle %7 %15 %15 2 1 0
+               OpStore %14 %16
+         %20 = OpLoad %7 %14
+         %22 = OpCompositeExtract %6 %20 0
+         %23 = OpCompositeExtract %6 %20 1
+         %24 = OpCompositeExtract %6 %20 2
+         %25 = OpCompositeConstruct %17 %22 %23 %24 %21
+               OpStore %19 %25
+         %27 = OpLoad %17 %19
+         %28 = OpVectorShuffle %17 %27 %27 3 2 1 0
+               OpStore %26 %28
+         %30 = OpLoad %7 %9
+         %31 = OpVectorShuffle %17 %30 %30 0 0 1 1
+               OpStore %29 %31
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  TransformationCompositeConstruct transformation(
+      32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(25, {}),
+                                        MakeDataDescriptor(200, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(28, {}),
+                                        MakeDataDescriptor(200, {1})));
+}
+
+TEST(TransformationCompositeConstructTest, DontAddSynonymsForIrrelevantIds) {
+  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 = OpTypeFloat 32
+          %7 = OpTypeVector %6 3
+          %8 = OpTypePointer Function %7
+         %10 = OpConstant %6 1
+         %11 = OpConstantComposite %7 %10 %10 %10
+         %17 = OpTypeVector %6 4
+         %18 = OpTypePointer Function %17
+         %21 = OpConstant %6 2
+         %32 = OpTypeMatrix %17 3
+         %33 = OpTypePointer Private %32
+         %34 = OpVariable %33 Private
+         %35 = OpTypeMatrix %7 4
+         %36 = OpTypePointer Private %35
+         %37 = OpVariable %36 Private
+         %38 = OpTypeVector %6 2
+         %39 = OpTypeInt 32 0
+         %40 = OpConstant %39 3
+         %41 = OpTypeArray %38 %40
+         %42 = OpTypePointer Private %41
+         %43 = OpVariable %42 Private
+        %100 = OpUndef %7
+        %101 = OpUndef %17
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %9 = OpVariable %8 Function
+         %12 = OpVariable %8 Function
+         %14 = OpVariable %8 Function
+         %19 = OpVariable %18 Function
+         %26 = OpVariable %18 Function
+         %29 = OpVariable %18 Function
+               OpStore %9 %11
+         %13 = OpLoad %7 %9
+               OpStore %12 %13
+         %15 = OpLoad %7 %12
+         %16 = OpVectorShuffle %7 %15 %15 2 1 0
+               OpStore %14 %16
+         %20 = OpLoad %7 %14
+         %22 = OpCompositeExtract %6 %20 0
+         %23 = OpCompositeExtract %6 %20 1
+         %24 = OpCompositeExtract %6 %20 2
+         %25 = OpCompositeConstruct %17 %22 %23 %24 %21
+               OpStore %19 %25
+         %27 = OpLoad %17 %19
+         %28 = OpVectorShuffle %17 %27 %27 3 2 1 0
+               OpStore %26 %28
+         %30 = OpLoad %7 %9
+         %31 = OpVectorShuffle %17 %30 %30 0 0 1 1
+               OpStore %29 %31
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  fact_manager.AddFactIdIsIrrelevant(25);
+
+  TransformationCompositeConstruct transformation(
+      32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(25, {}),
+                                         MakeDataDescriptor(200, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(28, {}),
+                                        MakeDataDescriptor(200, {1})));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_composite_extract_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_composite_extract_test.cpp
index a7674a6..5b1a0f1 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_composite_extract_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_composite_extract_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/transformation_composite_extract.h"
+
 #include "source/fuzz/instruction_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 
@@ -399,6 +400,185 @@
                    .IsApplicable(context.get(), transformation_context));
 }
 
+TEST(TransformationCompositeExtractTest, AddSynonymsForRelevantIds) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "b"
+               OpName %17 "FunnyPoint"
+               OpMemberName %17 0 "x"
+               OpMemberName %17 1 "y"
+               OpMemberName %17 2 "z"
+               OpName %19 "p"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+         %12 = OpTypeBool
+         %16 = OpTypeFloat 32
+         %17 = OpTypeStruct %16 %16 %6
+         %81 = OpTypeStruct %17 %16
+         %18 = OpTypePointer Function %17
+         %20 = OpConstant %6 0
+         %23 = OpTypePointer Function %16
+         %26 = OpConstant %6 1
+         %30 = OpConstant %6 2
+         %80 = OpUndef %16
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %19 = OpVariable %18 Function
+          %9 = OpLoad %6 %8
+         %11 = OpLoad %6 %10
+        %100 = OpCompositeConstruct %17 %80 %80 %26
+        %104 = OpCompositeConstruct %81 %100 %80
+         %13 = OpIEqual %12 %9 %11
+               OpSelectionMerge %15 None
+               OpBranchConditional %13 %14 %25
+         %14 = OpLabel
+         %21 = OpLoad %6 %8
+         %22 = OpConvertSToF %16 %21
+        %101 = OpCompositeConstruct %17 %22 %80 %30
+         %24 = OpAccessChain %23 %19 %20
+               OpStore %24 %22
+               OpBranch %15
+         %25 = OpLabel
+         %27 = OpLoad %6 %10
+         %28 = OpConvertSToF %16 %27
+        %102 = OpCompositeConstruct %17 %80 %28 %27
+         %29 = OpAccessChain %23 %19 %26
+               OpStore %29 %28
+               OpBranch %15
+         %15 = OpLabel
+         %31 = OpAccessChain %23 %19 %20
+         %32 = OpLoad %16 %31
+         %33 = OpAccessChain %23 %19 %26
+         %34 = OpLoad %16 %33
+        %103 = OpCompositeConstruct %17 %34 %32 %9
+         %35 = OpFAdd %16 %32 %34
+         %36 = OpConvertFToS %6 %35
+         %37 = OpAccessChain %7 %19 %30
+               OpStore %37 %36
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  TransformationCompositeExtract transformation(
+      MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2});
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(201, {}),
+                                        MakeDataDescriptor(100, {2})));
+}
+
+TEST(TransformationCompositeExtractTest, DontAddSynonymsForIrrelevantIds) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "b"
+               OpName %17 "FunnyPoint"
+               OpMemberName %17 0 "x"
+               OpMemberName %17 1 "y"
+               OpMemberName %17 2 "z"
+               OpName %19 "p"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+         %12 = OpTypeBool
+         %16 = OpTypeFloat 32
+         %17 = OpTypeStruct %16 %16 %6
+         %81 = OpTypeStruct %17 %16
+         %18 = OpTypePointer Function %17
+         %20 = OpConstant %6 0
+         %23 = OpTypePointer Function %16
+         %26 = OpConstant %6 1
+         %30 = OpConstant %6 2
+         %80 = OpUndef %16
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %19 = OpVariable %18 Function
+          %9 = OpLoad %6 %8
+         %11 = OpLoad %6 %10
+        %100 = OpCompositeConstruct %17 %80 %80 %26
+        %104 = OpCompositeConstruct %81 %100 %80
+         %13 = OpIEqual %12 %9 %11
+               OpSelectionMerge %15 None
+               OpBranchConditional %13 %14 %25
+         %14 = OpLabel
+         %21 = OpLoad %6 %8
+         %22 = OpConvertSToF %16 %21
+        %101 = OpCompositeConstruct %17 %22 %80 %30
+         %24 = OpAccessChain %23 %19 %20
+               OpStore %24 %22
+               OpBranch %15
+         %25 = OpLabel
+         %27 = OpLoad %6 %10
+         %28 = OpConvertSToF %16 %27
+        %102 = OpCompositeConstruct %17 %80 %28 %27
+         %29 = OpAccessChain %23 %19 %26
+               OpStore %29 %28
+               OpBranch %15
+         %15 = OpLabel
+         %31 = OpAccessChain %23 %19 %20
+         %32 = OpLoad %16 %31
+         %33 = OpAccessChain %23 %19 %26
+         %34 = OpLoad %16 %33
+        %103 = OpCompositeConstruct %17 %34 %32 %9
+         %35 = OpFAdd %16 %32 %34
+         %36 = OpConvertFToS %6 %35
+         %37 = OpAccessChain %7 %19 %30
+               OpStore %37 %36
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  fact_manager.AddFactIdIsIrrelevant(100);
+  TransformationCompositeExtract transformation(
+      MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2});
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(201, {}),
+                                         MakeDataDescriptor(100, {2})));
+}
+
 }  // 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
deleted file mode 100644
index cf9d135..0000000
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_copy_object_test.cpp
+++ /dev/null
@@ -1,778 +0,0 @@
-// Copyright (c) 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <algorithm>
-#include <set>
-#include <unordered_set>
-
-#include "source/fuzz/data_descriptor.h"
-#include "source/fuzz/instruction_descriptor.h"
-#include "source/fuzz/transformation_copy_object.h"
-#include "test/fuzz/fuzz_test_util.h"
-
-namespace spvtools {
-namespace fuzz {
-namespace {
-
-TEST(TransformationCopyObjectTest, CopyBooleanConstants) {
-  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
-          %6 = OpTypeBool
-          %7 = OpConstantTrue %6
-          %8 = OpConstantFalse %6
-          %3 = OpTypeFunction %2
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
-  ASSERT_TRUE(IsValid(env, context.get()));
-
-  FactManager fact_manager;
-  spvtools::ValidatorOptions validator_options;
-  TransformationContext transformation_context(&fact_manager,
-                                               validator_options);
-
-  ASSERT_EQ(0, transformation_context.GetFactManager()
-                   ->GetIdsForWhichSynonymsAreKnown()
-                   .size());
-
-  {
-    TransformationCopyObject copy_true(
-        7, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100);
-    ASSERT_TRUE(copy_true.IsApplicable(context.get(), transformation_context));
-    copy_true.Apply(context.get(), &transformation_context);
-
-    std::vector<uint32_t> ids_for_which_synonyms_are_known =
-        transformation_context.GetFactManager()
-            ->GetIdsForWhichSynonymsAreKnown();
-    ASSERT_EQ(2, ids_for_which_synonyms_are_known.size());
-    ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
-                          ids_for_which_synonyms_are_known.end(),
-                          7) != ids_for_which_synonyms_are_known.end());
-    ASSERT_EQ(
-        2, transformation_context.GetFactManager()->GetSynonymsForId(7).size());
-    protobufs::DataDescriptor descriptor_100 = MakeDataDescriptor(100, {});
-    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
-        MakeDataDescriptor(7, {}), descriptor_100));
-  }
-
-  {
-    TransformationCopyObject copy_false(
-        8, MakeInstructionDescriptor(100, SpvOpReturn, 0), 101);
-    ASSERT_TRUE(copy_false.IsApplicable(context.get(), transformation_context));
-    copy_false.Apply(context.get(), &transformation_context);
-    std::vector<uint32_t> ids_for_which_synonyms_are_known =
-        transformation_context.GetFactManager()
-            ->GetIdsForWhichSynonymsAreKnown();
-    ASSERT_EQ(4, ids_for_which_synonyms_are_known.size());
-    ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
-                          ids_for_which_synonyms_are_known.end(),
-                          8) != ids_for_which_synonyms_are_known.end());
-    ASSERT_EQ(
-        2, transformation_context.GetFactManager()->GetSynonymsForId(8).size());
-    protobufs::DataDescriptor descriptor_101 = MakeDataDescriptor(101, {});
-    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
-        MakeDataDescriptor(8, {}), descriptor_101));
-  }
-
-  {
-    TransformationCopyObject copy_false_again(
-        101, MakeInstructionDescriptor(5, SpvOpReturn, 0), 102);
-    ASSERT_TRUE(
-        copy_false_again.IsApplicable(context.get(), transformation_context));
-    copy_false_again.Apply(context.get(), &transformation_context);
-    std::vector<uint32_t> ids_for_which_synonyms_are_known =
-        transformation_context.GetFactManager()
-            ->GetIdsForWhichSynonymsAreKnown();
-    ASSERT_EQ(5, ids_for_which_synonyms_are_known.size());
-    ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
-                          ids_for_which_synonyms_are_known.end(),
-                          101) != ids_for_which_synonyms_are_known.end());
-    ASSERT_EQ(
-        3,
-        transformation_context.GetFactManager()->GetSynonymsForId(101).size());
-    protobufs::DataDescriptor descriptor_102 = MakeDataDescriptor(102, {});
-    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
-        MakeDataDescriptor(101, {}), descriptor_102));
-  }
-
-  {
-    TransformationCopyObject copy_true_again(
-        7, MakeInstructionDescriptor(102, SpvOpReturn, 0), 103);
-    ASSERT_TRUE(
-        copy_true_again.IsApplicable(context.get(), transformation_context));
-    copy_true_again.Apply(context.get(), &transformation_context);
-    std::vector<uint32_t> ids_for_which_synonyms_are_known =
-        transformation_context.GetFactManager()
-            ->GetIdsForWhichSynonymsAreKnown();
-    ASSERT_EQ(6, ids_for_which_synonyms_are_known.size());
-    ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
-                          ids_for_which_synonyms_are_known.end(),
-                          7) != ids_for_which_synonyms_are_known.end());
-    ASSERT_EQ(
-        3, transformation_context.GetFactManager()->GetSynonymsForId(7).size());
-    protobufs::DataDescriptor descriptor_103 = MakeDataDescriptor(103, {});
-    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
-        MakeDataDescriptor(7, {}), descriptor_103));
-  }
-
-  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
-          %6 = OpTypeBool
-          %7 = OpConstantTrue %6
-          %8 = OpConstantFalse %6
-          %3 = OpTypeFunction %2
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-        %100 = OpCopyObject %6 %7
-        %101 = OpCopyObject %6 %8
-        %102 = OpCopyObject %6 %101
-        %103 = OpCopyObject %6 %7
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
-}
-
-TEST(TransformationCopyObjectTest, CheckIllegalCases) {
-  // The following SPIR-V comes from this GLSL, pushed through spirv-opt
-  // and then doctored a bit.
-  //
-  // #version 310 es
-  //
-  // precision highp float;
-  //
-  // struct S {
-  //   int a;
-  //   float b;
-  // };
-  //
-  // layout(set = 0, binding = 2) uniform block {
-  //   S s;
-  //   lowp float f;
-  //   int ii;
-  // } ubuf;
-  //
-  // layout(location = 0) out vec4 color;
-  //
-  // void main() {
-  //   float c = 0.0;
-  //   lowp float d = 0.0;
-  //   S localS = ubuf.s;
-  //   for (int i = 0; i < ubuf.s.a; i++) {
-  //     switch (ubuf.ii) {
-  //       case 0:
-  //         c += 0.1;
-  //         d += 0.2;
-  //       case 1:
-  //         c += 0.1;
-  //         if (c > d) {
-  //           d += 0.2;
-  //         } else {
-  //           d += c;
-  //         }
-  //         break;
-  //       default:
-  //         i += 1;
-  //         localS.b += d;
-  //     }
-  //   }
-  //   color = vec4(c, d, localS.b, 1.0);
-  // }
-
-  std::string shader = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main" %80
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-               OpName %4 "main"
-               OpName %12 "S"
-               OpMemberName %12 0 "a"
-               OpMemberName %12 1 "b"
-               OpName %15 "S"
-               OpMemberName %15 0 "a"
-               OpMemberName %15 1 "b"
-               OpName %16 "block"
-               OpMemberName %16 0 "s"
-               OpMemberName %16 1 "f"
-               OpMemberName %16 2 "ii"
-               OpName %18 "ubuf"
-               OpName %80 "color"
-               OpMemberDecorate %12 0 RelaxedPrecision
-               OpMemberDecorate %15 0 RelaxedPrecision
-               OpMemberDecorate %15 0 Offset 0
-               OpMemberDecorate %15 1 Offset 4
-               OpMemberDecorate %16 0 Offset 0
-               OpMemberDecorate %16 1 RelaxedPrecision
-               OpMemberDecorate %16 1 Offset 16
-               OpMemberDecorate %16 2 RelaxedPrecision
-               OpMemberDecorate %16 2 Offset 20
-               OpDecorate %16 Block
-               OpDecorate %18 DescriptorSet 0
-               OpDecorate %18 Binding 2
-               OpDecorate %38 RelaxedPrecision
-               OpDecorate %43 RelaxedPrecision
-               OpDecorate %53 RelaxedPrecision
-               OpDecorate %62 RelaxedPrecision
-               OpDecorate %69 RelaxedPrecision
-               OpDecorate %77 RelaxedPrecision
-               OpDecorate %80 Location 0
-               OpDecorate %101 RelaxedPrecision
-               OpDecorate %102 RelaxedPrecision
-               OpDecorate %96 RelaxedPrecision
-               OpDecorate %108 RelaxedPrecision
-               OpDecorate %107 RelaxedPrecision
-               OpDecorate %98 RelaxedPrecision
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeFloat 32
-          %9 = OpConstant %6 0
-         %11 = OpTypeInt 32 1
-         %12 = OpTypeStruct %11 %6
-         %15 = OpTypeStruct %11 %6
-         %16 = OpTypeStruct %15 %6 %11
-         %17 = OpTypePointer Uniform %16
-         %18 = OpVariable %17 Uniform
-         %19 = OpConstant %11 0
-         %20 = OpTypePointer Uniform %15
-         %27 = OpConstant %11 1
-         %36 = OpTypePointer Uniform %11
-         %39 = OpTypeBool
-         %41 = OpConstant %11 2
-         %48 = OpConstant %6 0.100000001
-         %51 = OpConstant %6 0.200000003
-         %78 = OpTypeVector %6 4
-         %79 = OpTypePointer Output %78
-         %80 = OpVariable %79 Output
-         %85 = OpConstant %6 1
-         %95 = OpUndef %12
-        %112 = OpTypePointer Uniform %6
-        %113 = OpTypeInt 32 0
-        %114 = OpConstant %113 1
-        %179 = OpTypePointer Function %39
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-        %180 = OpVariable %179 Function
-        %181 = OpVariable %179 Function
-        %182 = OpVariable %179 Function
-         %21 = OpAccessChain %20 %18 %19
-        %115 = OpAccessChain %112 %21 %114
-        %116 = OpLoad %6 %115
-         %90 = OpCompositeInsert %12 %116 %95 1
-               OpBranch %30
-         %30 = OpLabel
-         %99 = OpPhi %12 %90 %5 %109 %47
-         %98 = OpPhi %6 %9 %5 %107 %47
-         %97 = OpPhi %6 %9 %5 %105 %47
-         %96 = OpPhi %11 %19 %5 %77 %47
-         %37 = OpAccessChain %36 %18 %19 %19
-         %38 = OpLoad %11 %37
-         %40 = OpSLessThan %39 %96 %38
-               OpLoopMerge %32 %47 None
-               OpBranchConditional %40 %31 %32
-         %31 = OpLabel
-         %42 = OpAccessChain %36 %18 %41
-         %43 = OpLoad %11 %42
-               OpSelectionMerge %45 None
-               OpSwitch %43 %46 0 %44 1 %45
-         %46 = OpLabel
-         %69 = OpIAdd %11 %96 %27
-         %72 = OpCompositeExtract %6 %99 1
-         %73 = OpFAdd %6 %72 %98
-         %93 = OpCompositeInsert %12 %73 %99 1
-               OpBranch %47
-         %44 = OpLabel
-         %50 = OpFAdd %6 %97 %48
-         %53 = OpFAdd %6 %98 %51
-               OpBranch %45
-         %45 = OpLabel
-        %101 = OpPhi %6 %98 %31 %53 %44
-        %100 = OpPhi %6 %97 %31 %50 %44
-         %55 = OpFAdd %6 %100 %48
-         %58 = OpFOrdGreaterThan %39 %55 %101
-               OpSelectionMerge %60 None
-               OpBranchConditional %58 %59 %63
-         %59 = OpLabel
-         %62 = OpFAdd %6 %101 %51
-               OpBranch %60
-         %63 = OpLabel
-         %66 = OpFAdd %6 %101 %55
-               OpBranch %60
-         %60 = OpLabel
-        %108 = OpPhi %6 %62 %59 %66 %63
-               OpBranch %47
-         %47 = OpLabel
-        %109 = OpPhi %12 %93 %46 %99 %60
-        %107 = OpPhi %6 %98 %46 %108 %60
-        %105 = OpPhi %6 %97 %46 %55 %60
-        %102 = OpPhi %11 %69 %46 %96 %60
-         %77 = OpIAdd %11 %102 %27
-               OpBranch %30
-         %32 = OpLabel
-         %84 = OpCompositeExtract %6 %99 1
-         %86 = OpCompositeConstruct %78 %97 %98 %84 %85
-               OpStore %80 %86
-               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;
-  spvtools::ValidatorOptions validator_options;
-  TransformationContext transformation_context(&fact_manager,
-                                               validator_options);
-
-  // Inapplicable because %18 is decorated.
-  ASSERT_FALSE(TransformationCopyObject(
-                   18, MakeInstructionDescriptor(21, SpvOpAccessChain, 0), 200)
-                   .IsApplicable(context.get(), transformation_context));
-
-  // Inapplicable because %77 is decorated.
-  ASSERT_FALSE(TransformationCopyObject(
-                   77, MakeInstructionDescriptor(77, SpvOpBranch, 0), 200)
-                   .IsApplicable(context.get(), transformation_context));
-
-  // Inapplicable because %80 is decorated.
-  ASSERT_FALSE(TransformationCopyObject(
-                   80, MakeInstructionDescriptor(77, SpvOpIAdd, 0), 200)
-                   .IsApplicable(context.get(), transformation_context));
-
-  // Inapplicable because %84 is not available at the requested point
-  ASSERT_FALSE(
-      TransformationCopyObject(
-          84, MakeInstructionDescriptor(32, SpvOpCompositeExtract, 0), 200)
-          .IsApplicable(context.get(), transformation_context));
-
-  // Fine because %84 is available at the requested point
-  ASSERT_TRUE(
-      TransformationCopyObject(
-          84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 200)
-          .IsApplicable(context.get(), transformation_context));
-
-  // Inapplicable because id %9 is already in use
-  ASSERT_FALSE(
-      TransformationCopyObject(
-          84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 9)
-          .IsApplicable(context.get(), transformation_context));
-
-  // Inapplicable because the requested point does not exist
-  ASSERT_FALSE(TransformationCopyObject(
-                   84, MakeInstructionDescriptor(86, SpvOpReturn, 2), 200)
-                   .IsApplicable(context.get(), transformation_context));
-
-  // Inapplicable because %9 is not in a function
-  ASSERT_FALSE(TransformationCopyObject(
-                   9, MakeInstructionDescriptor(9, SpvOpTypeInt, 0), 200)
-                   .IsApplicable(context.get(), transformation_context));
-
-  // Inapplicable because the insert point is right before, or inside, a chunk
-  // of OpPhis
-  ASSERT_FALSE(TransformationCopyObject(
-                   9, MakeInstructionDescriptor(30, SpvOpPhi, 0), 200)
-                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(TransformationCopyObject(
-                   9, MakeInstructionDescriptor(99, SpvOpPhi, 1), 200)
-                   .IsApplicable(context.get(), transformation_context));
-
-  // OK, because the insert point is just after a chunk of OpPhis.
-  ASSERT_TRUE(TransformationCopyObject(
-                  9, MakeInstructionDescriptor(96, SpvOpAccessChain, 0), 200)
-                  .IsApplicable(context.get(), transformation_context));
-
-  // Inapplicable because the insert point is right after an OpSelectionMerge
-  ASSERT_FALSE(
-      TransformationCopyObject(
-          9, MakeInstructionDescriptor(58, SpvOpBranchConditional, 0), 200)
-          .IsApplicable(context.get(), transformation_context));
-
-  // OK, because the insert point is right before the OpSelectionMerge
-  ASSERT_TRUE(TransformationCopyObject(
-                  9, MakeInstructionDescriptor(58, SpvOpSelectionMerge, 0), 200)
-                  .IsApplicable(context.get(), transformation_context));
-
-  // Inapplicable because the insert point is right after an OpSelectionMerge
-  ASSERT_FALSE(TransformationCopyObject(
-                   9, MakeInstructionDescriptor(43, SpvOpSwitch, 0), 200)
-                   .IsApplicable(context.get(), transformation_context));
-
-  // OK, because the insert point is right before the OpSelectionMerge
-  ASSERT_TRUE(TransformationCopyObject(
-                  9, MakeInstructionDescriptor(43, SpvOpSelectionMerge, 0), 200)
-                  .IsApplicable(context.get(), transformation_context));
-
-  // Inapplicable because the insert point is right after an OpLoopMerge
-  ASSERT_FALSE(
-      TransformationCopyObject(
-          9, MakeInstructionDescriptor(40, SpvOpBranchConditional, 0), 200)
-          .IsApplicable(context.get(), transformation_context));
-
-  // OK, because the insert point is right before the OpLoopMerge
-  ASSERT_TRUE(TransformationCopyObject(
-                  9, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200)
-                  .IsApplicable(context.get(), transformation_context));
-
-  // Inapplicable because id %300 does not exist
-  ASSERT_FALSE(TransformationCopyObject(
-                   300, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200)
-                   .IsApplicable(context.get(), transformation_context));
-
-  // Inapplicable because the following instruction is OpVariable
-  ASSERT_FALSE(TransformationCopyObject(
-                   9, MakeInstructionDescriptor(180, SpvOpVariable, 0), 200)
-                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(TransformationCopyObject(
-                   9, MakeInstructionDescriptor(181, SpvOpVariable, 0), 200)
-                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(TransformationCopyObject(
-                   9, MakeInstructionDescriptor(182, SpvOpVariable, 0), 200)
-                   .IsApplicable(context.get(), transformation_context));
-
-  // OK, because this is just past the group of OpVariable instructions.
-  ASSERT_TRUE(TransformationCopyObject(
-                  9, MakeInstructionDescriptor(182, SpvOpAccessChain, 0), 200)
-                  .IsApplicable(context.get(), transformation_context));
-}
-
-TEST(TransformationCopyObjectTest, MiscellaneousCopies) {
-  // The following SPIR-V comes from this GLSL:
-  //
-  // #version 310 es
-  //
-  // precision highp float;
-  //
-  // float g;
-  //
-  // vec4 h;
-  //
-  // void main() {
-  //   int a;
-  //   int b;
-  //   b = int(g);
-  //   h.x = float(a);
-  // }
-
-  std::string shader = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-               OpName %4 "main"
-               OpName %8 "b"
-               OpName %11 "g"
-               OpName %16 "h"
-               OpName %17 "a"
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeInt 32 1
-          %7 = OpTypePointer Function %6
-          %9 = OpTypeFloat 32
-         %10 = OpTypePointer Private %9
-         %11 = OpVariable %10 Private
-         %14 = OpTypeVector %9 4
-         %15 = OpTypePointer Private %14
-         %16 = OpVariable %15 Private
-         %20 = OpTypeInt 32 0
-         %21 = OpConstant %20 0
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-          %8 = OpVariable %7 Function
-         %17 = OpVariable %7 Function
-         %12 = OpLoad %9 %11
-         %13 = OpConvertFToS %6 %12
-               OpStore %8 %13
-         %18 = OpLoad %6 %17
-         %19 = OpConvertSToF %9 %18
-         %22 = OpAccessChain %10 %16 %21
-               OpStore %22 %19
-               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;
-  spvtools::ValidatorOptions validator_options;
-  TransformationContext transformation_context(&fact_manager,
-                                               validator_options);
-
-  std::vector<TransformationCopyObject> transformations = {
-      TransformationCopyObject(19, MakeInstructionDescriptor(22, SpvOpStore, 0),
-                               100),
-      TransformationCopyObject(
-          22, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 101),
-      TransformationCopyObject(
-          12, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 102),
-      TransformationCopyObject(
-          11, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 103),
-      TransformationCopyObject(
-          16, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 104),
-      TransformationCopyObject(
-          8, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 105),
-      TransformationCopyObject(
-          17, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 106)};
-
-  for (auto& transformation : transformations) {
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    transformation.Apply(context.get(), &transformation_context);
-  }
-
-  ASSERT_TRUE(IsValid(env, context.get()));
-
-  std::string after_transformation = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-               OpName %4 "main"
-               OpName %8 "b"
-               OpName %11 "g"
-               OpName %16 "h"
-               OpName %17 "a"
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeInt 32 1
-          %7 = OpTypePointer Function %6
-          %9 = OpTypeFloat 32
-         %10 = OpTypePointer Private %9
-         %11 = OpVariable %10 Private
-         %14 = OpTypeVector %9 4
-         %15 = OpTypePointer Private %14
-         %16 = OpVariable %15 Private
-         %20 = OpTypeInt 32 0
-         %21 = OpConstant %20 0
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-          %8 = OpVariable %7 Function
-         %17 = OpVariable %7 Function
-         %12 = OpLoad %9 %11
-         %13 = OpConvertFToS %6 %12
-               OpStore %8 %13
-         %18 = OpLoad %6 %17
-         %19 = OpConvertSToF %9 %18
-         %22 = OpAccessChain %10 %16 %21
-        %106 = OpCopyObject %7 %17
-        %105 = OpCopyObject %7 %8
-        %104 = OpCopyObject %15 %16
-        %103 = OpCopyObject %10 %11
-        %102 = OpCopyObject %9 %12
-        %101 = OpCopyObject %10 %22
-        %100 = OpCopyObject %9 %19
-               OpStore %22 %19
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  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;
-  spvtools::ValidatorOptions validator_options;
-  TransformationContext transformation_context(&fact_manager,
-                                               validator_options);
-
-  // Illegal to copy null.
-  ASSERT_FALSE(TransformationCopyObject(
-                   8, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100)
-                   .IsApplicable(context.get(), transformation_context));
-
-  // Illegal to copy an OpUndef of pointer type.
-  ASSERT_FALSE(TransformationCopyObject(
-                   9, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100)
-                   .IsApplicable(context.get(), transformation_context));
-}
-
-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;
-  spvtools::ValidatorOptions validator_options;
-  TransformationContext transformation_context(&fact_manager,
-                                               validator_options);
-
-  transformation_context.GetFactManager()->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(), transformation_context));
-  transformation1.Apply(context.get(), &transformation_context);
-  ASSERT_TRUE(
-      transformation2.IsApplicable(context.get(), transformation_context));
-  transformation2.Apply(context.get(), &transformation_context);
-  ASSERT_TRUE(
-      transformation3.IsApplicable(context.get(), transformation_context));
-  transformation3.Apply(context.get(), &transformation_context);
-
-  ASSERT_TRUE(
-      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(8));
-  ASSERT_TRUE(
-      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
-  ASSERT_TRUE(
-      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
-  ASSERT_FALSE(
-      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(9));
-  ASSERT_FALSE(
-      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
-}
-
-TEST(TransformationCopyObject, DoNotCopyOpSampledImage) {
-  // This checks that we do not try to copy the result id of an OpSampledImage
-  // instruction.
-  std::string shader = R"(
-               OpCapability Shader
-               OpCapability SampledBuffer
-               OpCapability ImageBuffer
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %2 "main" %40 %41
-               OpExecutionMode %2 OriginUpperLeft
-               OpSource GLSL 450
-               OpDecorate %40 DescriptorSet 0
-               OpDecorate %40 Binding 69
-               OpDecorate %41 DescriptorSet 0
-               OpDecorate %41 Binding 1
-         %54 = OpTypeFloat 32
-         %76 = OpTypeVector %54 4
-         %55 = OpConstant %54 0
-         %56 = OpTypeVector %54 3
-         %94 = OpTypeVector %54 2
-        %112 = OpConstantComposite %94 %55 %55
-         %57 = OpConstantComposite %56 %55 %55 %55
-         %15 = OpTypeImage %54 2D 2 0 0 1 Unknown
-        %114 = OpTypePointer UniformConstant %15
-         %38 = OpTypeSampler
-        %125 = OpTypePointer UniformConstant %38
-        %132 = OpTypeVoid
-        %133 = OpTypeFunction %132
-         %45 = OpTypeSampledImage %15
-         %40 = OpVariable %114 UniformConstant
-         %41 = OpVariable %125 UniformConstant
-          %2 = OpFunction %132 None %133
-        %164 = OpLabel
-        %184 = OpLoad %15 %40
-        %213 = OpLoad %38 %41
-        %216 = OpSampledImage %45 %184 %213
-        %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55
-               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;
-  spvtools::ValidatorOptions validator_options;
-  TransformationContext transformation_context(&fact_manager,
-                                               validator_options);
-
-  ASSERT_FALSE(
-      TransformationCopyObject(
-          216, MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0),
-          500)
-          .IsApplicable(context.get(), transformation_context));
-}
-
-}  // namespace
-}  // namespace fuzz
-}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_equation_instruction_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_equation_instruction_test.cpp
index 1e8aa7e..132e446 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_equation_instruction_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_equation_instruction_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/transformation_equation_instruction.h"
+
 #include "source/fuzz/instruction_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 
@@ -486,6 +487,685 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationEquationInstructionTest, Bitcast) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %8 = OpTypeFloat 32
+          %9 = OpTypeVector %6 2
+         %10 = OpTypeVector %7 2
+         %11 = OpTypeVector %8 2
+         %21 = OpTypeBool
+         %22 = OpTypeVector %21 2
+         %15 = OpConstant %6 24
+         %16 = OpConstant %7 24
+         %17 = OpConstant %8 24
+         %18 = OpConstantComposite %9 %15 %15
+         %19 = OpConstantComposite %10 %16 %16
+         %20 = OpConstantComposite %11 %17 %17
+         %23 = OpConstantTrue %21
+         %24 = OpConstantComposite %22 %23 %23
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  // Too many operands.
+  ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpBitcast, {15, 16},
+                                                 insert_before)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Too few operands.
+  ASSERT_FALSE(
+      TransformationEquationInstruction(50, SpvOpBitcast, {}, insert_before)
+          .IsApplicable(context.get(), transformation_context));
+
+  // Operand's id is invalid.
+  ASSERT_FALSE(
+      TransformationEquationInstruction(50, SpvOpBitcast, {50}, insert_before)
+          .IsApplicable(context.get(), transformation_context));
+
+  // Operand's type is invalid
+  ASSERT_FALSE(
+      TransformationEquationInstruction(50, SpvOpBitcast, {13}, insert_before)
+          .IsApplicable(context.get(), transformation_context));
+
+  // Operand must be a scalar or a vector of numerical type.
+#ifndef NDEBUG
+  ASSERT_DEATH(
+      TransformationEquationInstruction(50, SpvOpBitcast, {23}, insert_before)
+          .IsApplicable(context.get(), transformation_context),
+      "Operand is not a scalar or a vector of numerical type");
+  ASSERT_DEATH(
+      TransformationEquationInstruction(50, SpvOpBitcast, {24}, insert_before)
+          .IsApplicable(context.get(), transformation_context),
+      "Only vectors of numerical components are supported");
+#else
+  ASSERT_FALSE(
+      TransformationEquationInstruction(50, SpvOpBitcast, {23}, insert_before)
+          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationEquationInstruction(50, SpvOpBitcast, {24}, insert_before)
+          .IsApplicable(context.get(), transformation_context));
+#endif
+
+  for (uint32_t operand_id = 15, fresh_id = 50; operand_id <= 20;
+       ++operand_id, ++fresh_id) {
+    TransformationEquationInstruction transformation(
+        fresh_id, SpvOpBitcast, {operand_id}, insert_before);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+
+  std::string expected_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %8 = OpTypeFloat 32
+          %9 = OpTypeVector %6 2
+         %10 = OpTypeVector %7 2
+         %11 = OpTypeVector %8 2
+         %21 = OpTypeBool
+         %22 = OpTypeVector %21 2
+         %15 = OpConstant %6 24
+         %16 = OpConstant %7 24
+         %17 = OpConstant %8 24
+         %18 = OpConstantComposite %9 %15 %15
+         %19 = OpConstantComposite %10 %16 %16
+         %20 = OpConstantComposite %11 %17 %17
+         %23 = OpConstantTrue %21
+         %24 = OpConstantComposite %22 %23 %23
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %50 = OpBitcast %8 %15
+         %51 = OpBitcast %8 %16
+         %52 = OpBitcast %6 %17
+         %53 = OpBitcast %11 %18
+         %54 = OpBitcast %11 %19
+         %55 = OpBitcast %9 %20
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest,
+     BitcastResultTypeFloatDoesNotExist) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %9 = OpTypeVector %6 2
+         %10 = OpTypeVector %7 2
+         %15 = OpConstant %6 24
+         %16 = OpConstant %7 24
+         %18 = OpConstantComposite %9 %15 %15
+         %19 = OpConstantComposite %10 %16 %16
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  // Scalar floating-point type does not exist.
+  ASSERT_FALSE(
+      TransformationEquationInstruction(50, SpvOpBitcast, {15}, insert_before)
+          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationEquationInstruction(50, SpvOpBitcast, {16}, insert_before)
+          .IsApplicable(context.get(), transformation_context));
+
+  // Vector of floating-point components does not exist.
+  ASSERT_FALSE(
+      TransformationEquationInstruction(50, SpvOpBitcast, {18}, insert_before)
+          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationEquationInstruction(50, SpvOpBitcast, {19}, insert_before)
+          .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist1) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeFloat 32
+         %11 = OpTypeVector %8 2
+         %17 = OpConstant %8 24
+         %20 = OpConstantComposite %11 %17 %17
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  // Scalar integral type does not exist.
+  ASSERT_FALSE(
+      TransformationEquationInstruction(50, SpvOpBitcast, {17}, insert_before)
+          .IsApplicable(context.get(), transformation_context));
+
+  // Vector of integral components does not exist.
+  ASSERT_FALSE(
+      TransformationEquationInstruction(50, SpvOpBitcast, {20}, insert_before)
+          .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist2) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 0
+          %8 = OpTypeFloat 32
+          %9 = OpTypeVector %4 2
+         %11 = OpTypeVector %8 2
+         %17 = OpConstant %8 24
+         %20 = OpConstantComposite %11 %17 %17
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  {
+    TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
+                                                     insert_before);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationEquationInstruction transformation(51, SpvOpBitcast, {20},
+                                                     insert_before);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+
+  std::string expected_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 0
+          %8 = OpTypeFloat 32
+          %9 = OpTypeVector %4 2
+         %11 = OpTypeVector %8 2
+         %17 = OpConstant %8 24
+         %20 = OpConstantComposite %11 %17 %17
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %50 = OpBitcast %4 %17
+         %51 = OpBitcast %9 %20
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist3) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 1
+          %8 = OpTypeFloat 32
+          %9 = OpTypeVector %4 2
+         %11 = OpTypeVector %8 2
+         %17 = OpConstant %8 24
+         %20 = OpConstantComposite %11 %17 %17
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  {
+    TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
+                                                     insert_before);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationEquationInstruction transformation(51, SpvOpBitcast, {20},
+                                                     insert_before);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+
+  std::string expected_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 1
+          %8 = OpTypeFloat 32
+          %9 = OpTypeVector %4 2
+         %11 = OpTypeVector %8 2
+         %17 = OpConstant %8 24
+         %20 = OpConstantComposite %11 %17 %17
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %50 = OpBitcast %4 %17
+         %51 = OpBitcast %9 %20
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist4) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 1
+          %8 = OpTypeFloat 32
+         %11 = OpTypeVector %8 2
+         %17 = OpConstant %8 24
+         %20 = OpConstantComposite %11 %17 %17
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  {
+    TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
+                                                     insert_before);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+
+  ASSERT_FALSE(
+      TransformationEquationInstruction(51, SpvOpBitcast, {20}, insert_before)
+          .IsApplicable(context.get(), transformation_context));
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string expected_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 1
+          %8 = OpTypeFloat 32
+         %11 = OpTypeVector %8 2
+         %17 = OpConstant %8 24
+         %20 = OpConstantComposite %11 %17 %17
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %50 = OpBitcast %4 %17
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist5) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 0
+          %8 = OpTypeFloat 32
+         %11 = OpTypeVector %8 2
+         %17 = OpConstant %8 24
+         %20 = OpConstantComposite %11 %17 %17
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  {
+    TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
+                                                     insert_before);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+
+  ASSERT_FALSE(
+      TransformationEquationInstruction(51, SpvOpBitcast, {20}, insert_before)
+          .IsApplicable(context.get(), transformation_context));
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string expected_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 0
+          %8 = OpTypeFloat 32
+         %11 = OpTypeVector %8 2
+         %17 = OpConstant %8 24
+         %20 = OpConstantComposite %11 %17 %17
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %50 = OpBitcast %4 %17
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist6) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 1
+          %5 = OpTypeInt 32 0
+          %8 = OpTypeFloat 32
+          %9 = OpTypeVector %5 2
+         %11 = OpTypeVector %8 2
+         %17 = OpConstant %8 24
+         %20 = OpConstantComposite %11 %17 %17
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  {
+    TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
+                                                     insert_before);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationEquationInstruction transformation(51, SpvOpBitcast, {20},
+                                                     insert_before);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string expected_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 1
+          %5 = OpTypeInt 32 0
+          %8 = OpTypeFloat 32
+          %9 = OpTypeVector %5 2
+         %11 = OpTypeVector %8 2
+         %17 = OpConstant %8 24
+         %20 = OpConstantComposite %11 %17 %17
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %50 = OpBitcast %4 %17
+         %51 = OpBitcast %9 %20
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist7) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 1
+          %5 = OpTypeInt 32 0
+          %8 = OpTypeFloat 32
+          %9 = OpTypeVector %4 2
+         %11 = OpTypeVector %8 2
+         %17 = OpConstant %8 24
+         %20 = OpConstantComposite %11 %17 %17
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  {
+    TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
+                                                     insert_before);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationEquationInstruction transformation(51, SpvOpBitcast, {20},
+                                                     insert_before);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string expected_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 1
+          %5 = OpTypeInt 32 0
+          %8 = OpTypeFloat 32
+          %9 = OpTypeVector %4 2
+         %11 = OpTypeVector %8 2
+         %17 = OpConstant %8 24
+         %20 = OpConstantComposite %11 %17 %17
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %50 = OpBitcast %4 %17
+         %51 = OpBitcast %9 %20
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
 TEST(TransformationEquationInstructionTest, Miscellaneous1) {
   std::string shader = R"(
                OpCapability Shader
@@ -626,6 +1306,286 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationEquationInstructionTest, ConversionInstructions) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %4 = OpTypeInt 32 0
+          %5 = OpTypeFloat 32
+          %7 = OpTypeVector %6 3
+          %8 = OpTypeVector %4 3
+          %9 = OpTypeVector %5 3
+         %10 = OpConstant %6 12
+         %20 = OpConstant %6 12
+         %11 = OpConstant %4 12
+         %21 = OpConstant %4 12
+         %14 = OpConstant %5 12
+         %15 = OpConstantComposite %7 %10 %10 %10
+         %18 = OpConstantComposite %7 %10 %10 %10
+         %16 = OpConstantComposite %8 %11 %11 %11
+         %19 = OpConstantComposite %8 %11 %11 %11
+         %17 = OpConstantComposite %9 %14 %14 %14
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  protobufs::InstructionDescriptor return_instruction =
+      MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  // Too few instruction operands.
+  ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Too many instruction operands.
+  ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {15, 16},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Operand has no type id.
+  ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {7},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // OpConvertSToF and OpConvertUToF require an operand to have scalar or vector
+  // of integral components type.
+  ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {17},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {14},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertUToF, {17},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertUToF, {14},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), transformation_context));
+
+  {
+    TransformationEquationInstruction transformation(50, SpvOpConvertSToF, {15},
+                                                     return_instruction);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationEquationInstruction transformation(51, SpvOpConvertSToF, {10},
+                                                     return_instruction);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationEquationInstruction transformation(52, SpvOpConvertUToF, {16},
+                                                     return_instruction);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationEquationInstruction transformation(53, SpvOpConvertUToF, {11},
+                                                     return_instruction);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationEquationInstruction transformation(58, SpvOpConvertSToF, {18},
+                                                     return_instruction);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationEquationInstruction transformation(59, SpvOpConvertUToF, {19},
+                                                     return_instruction);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationEquationInstruction transformation(60, SpvOpConvertSToF, {20},
+                                                     return_instruction);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationEquationInstruction transformation(61, SpvOpConvertUToF, {21},
+                                                     return_instruction);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformations = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %4 = OpTypeInt 32 0
+          %5 = OpTypeFloat 32
+          %7 = OpTypeVector %6 3
+          %8 = OpTypeVector %4 3
+          %9 = OpTypeVector %5 3
+         %10 = OpConstant %6 12
+         %20 = OpConstant %6 12
+         %11 = OpConstant %4 12
+         %21 = OpConstant %4 12
+         %14 = OpConstant %5 12
+         %15 = OpConstantComposite %7 %10 %10 %10
+         %18 = OpConstantComposite %7 %10 %10 %10
+         %16 = OpConstantComposite %8 %11 %11 %11
+         %19 = OpConstantComposite %8 %11 %11 %11
+         %17 = OpConstantComposite %9 %14 %14 %14
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %50 = OpConvertSToF %9 %15
+         %51 = OpConvertSToF %5 %10
+         %52 = OpConvertUToF %9 %16
+         %53 = OpConvertUToF %5 %11
+         %58 = OpConvertSToF %9 %18
+         %59 = OpConvertUToF %9 %19
+         %60 = OpConvertSToF %5 %20
+         %61 = OpConvertUToF %5 %21
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, FloatResultTypeDoesNotExist) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 0
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 3
+          %9 = OpTypeVector %7 3
+         %10 = OpConstant %6 24
+         %11 = OpConstant %7 25
+         %14 = OpConstantComposite %8 %10 %10 %10
+         %15 = OpConstantComposite %9 %11 %11 %11
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  protobufs::InstructionDescriptor return_instruction =
+      MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  // Scalar float type doesn't exist.
+  ASSERT_FALSE(TransformationEquationInstruction(16, SpvOpConvertUToF, {10},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(16, SpvOpConvertSToF, {11},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Vector float type doesn't exist.
+  ASSERT_FALSE(TransformationEquationInstruction(16, SpvOpConvertUToF, {14},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(16, SpvOpConvertSToF, {15},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationEquationInstructionTest, HandlesIrrelevantIds) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %30 = OpTypeVector %6 3
+         %15 = OpConstant %6 24
+         %16 = OpConstant %6 37
+         %31 = OpConstantComposite %30 %15 %16 %15
+         %33 = OpTypeBool
+         %32 = OpConstantTrue %33
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto return_instruction = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  // Applicable.
+  TransformationEquationInstruction transformation(14, SpvOpIAdd, {15, 16},
+                                                   return_instruction);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Handles irrelevant ids.
+  fact_manager.AddFactIdIsIrrelevant(16);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  fact_manager.AddFactIdIsIrrelevant(15);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_invert_comparison_operator_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_invert_comparison_operator_test.cpp
new file mode 100644
index 0000000..0468469
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_invert_comparison_operator_test.cpp
@@ -0,0 +1,135 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_invert_comparison_operator.h"
+
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationInvertComparisonOperatorTest, 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 = OpTypeInt 32 0
+          %8 = OpTypeBool
+          %9 = OpConstant %6 3
+         %10 = OpConstant %6 4
+         %11 = OpConstant %7 3
+         %12 = OpConstant %7 4
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %13 = OpSLessThan %8 %9 %10
+         %14 = OpSLessThanEqual %8 %9 %10
+         %15 = OpSGreaterThan %8 %9 %10
+         %16 = OpSGreaterThanEqual %8 %9 %10
+         %17 = OpULessThan %8 %11 %12
+         %18 = OpULessThanEqual %8 %11 %12
+         %19 = OpUGreaterThan %8 %11 %12
+         %20 = OpUGreaterThanEqual %8 %11 %12
+         %21 = OpIEqual %8 %9 %10
+         %22 = OpINotEqual %8 %9 %10
+               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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Operator id is not valid.
+  ASSERT_FALSE(TransformationInvertComparisonOperator(23, 23).IsApplicable(
+      context.get(), transformation_context));
+
+  // Operator instruction is not supported.
+  ASSERT_FALSE(TransformationInvertComparisonOperator(5, 23).IsApplicable(
+      context.get(), transformation_context));
+
+  // Fresh id is not fresh.
+  ASSERT_FALSE(TransformationInvertComparisonOperator(13, 22).IsApplicable(
+      context.get(), transformation_context));
+
+  for (uint32_t fresh_id = 23, operator_id = 13; operator_id <= 22;
+       ++fresh_id, ++operator_id) {
+    TransformationInvertComparisonOperator transformation(operator_id,
+                                                          fresh_id);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+
+  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
+          %7 = OpTypeInt 32 0
+          %8 = OpTypeBool
+          %9 = OpConstant %6 3
+         %10 = OpConstant %6 4
+         %11 = OpConstant %7 3
+         %12 = OpConstant %7 4
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %23 = OpSGreaterThanEqual %8 %9 %10
+         %13 = OpLogicalNot %8 %23
+         %24 = OpSGreaterThan %8 %9 %10
+         %14 = OpLogicalNot %8 %24
+         %25 = OpSLessThanEqual %8 %9 %10
+         %15 = OpLogicalNot %8 %25
+         %26 = OpSLessThan %8 %9 %10
+         %16 = OpLogicalNot %8 %26
+         %27 = OpUGreaterThanEqual %8 %11 %12
+         %17 = OpLogicalNot %8 %27
+         %28 = OpUGreaterThan %8 %11 %12
+         %18 = OpLogicalNot %8 %28
+         %29 = OpULessThanEqual %8 %11 %12
+         %19 = OpLogicalNot %8 %29
+         %30 = OpULessThan %8 %11 %12
+         %20 = OpLogicalNot %8 %30
+         %31 = OpINotEqual %8 %9 %10
+         %21 = OpLogicalNot %8 %31
+         %32 = OpIEqual %8 %9 %10
+         %22 = OpLogicalNot %8 %32
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, expected, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
\ No newline at end of file
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_permute_function_parameters_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_permute_function_parameters_test.cpp
index a4a7c00..4046f79 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_permute_function_parameters_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_permute_function_parameters_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/transformation_permute_function_parameters.h"
+
 #include "test/fuzz/fuzz_test_util.h"
 
 namespace spvtools {
@@ -73,7 +74,6 @@
          %18 = OpTypeFunction %8 %7 %15 %17
          %24 = OpTypeBool
          %25 = OpTypeFunction %24 %15 %7
-         %105 = OpTypeFunction %24 %7 %15    ; predefined type for %28
          %31 = OpConstant %6 255
          %33 = OpConstant %6 0
          %34 = OpConstant %6 1
@@ -90,6 +90,11 @@
          %84 = OpConstant %14 5
          %90 = OpConstant %6 3
          %98 = OpConstant %6 4
+        %206 = OpTypeFunction %2 %14 %16
+        %223 = OpTypeFunction %2 %6 %8
+        %224 = OpTypeFunction %2 %8 %6
+        %233 = OpTypeFunction %2 %42 %24
+        %234 = OpTypeFunction %2 %24 %42
           %4 = OpFunction %2 None %3
           %5 = OpLabel
          %66 = OpVariable %15 Function
@@ -148,6 +153,8 @@
          %70 = OpLabel
                OpReturn
                OpFunctionEnd
+
+         ; adjust type of the function in-place
          %12 = OpFunction %8 None %9
          %10 = OpFunctionParameter %7
          %11 = OpFunctionParameter %7
@@ -192,6 +199,53 @@
                OpReturnValue %61
                OpFunctionEnd
 
+        ; create a new function type
+        %200 = OpFunction %2 None %206
+        %207 = OpFunctionParameter %14
+        %208 = OpFunctionParameter %16
+        %202 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %203 = OpFunction %2 None %206
+        %209 = OpFunctionParameter %14
+        %210 = OpFunctionParameter %16
+        %205 = OpLabel
+               OpReturn
+               OpFunctionEnd
+
+        ; reuse an existing function type
+        %211 = OpFunction %2 None %223
+        %212 = OpFunctionParameter %6
+        %213 = OpFunctionParameter %8
+        %214 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %215 = OpFunction %2 None %224
+        %216 = OpFunctionParameter %8
+        %217 = OpFunctionParameter %6
+        %218 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %219 = OpFunction %2 None %224
+        %220 = OpFunctionParameter %8
+        %221 = OpFunctionParameter %6
+        %222 = OpLabel
+               OpReturn
+               OpFunctionEnd
+
+        ; don't adjust the type of the function if it creates a duplicate
+        %225 = OpFunction %2 None %233
+        %226 = OpFunctionParameter %42
+        %227 = OpFunctionParameter %24
+        %228 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %229 = OpFunction %2 None %234
+        %230 = OpFunctionParameter %24
+        %231 = OpFunctionParameter %42
+        %232 = OpLabel
+               OpReturn
+               OpFunctionEnd
   )";
 
   const auto env = SPV_ENV_UNIVERSAL_1_3;
@@ -205,49 +259,67 @@
                                                validator_options);
 
   // Can't permute main function
-  ASSERT_FALSE(TransformationPermuteFunctionParameters(4, 0, {}).IsApplicable(
-      context.get(), transformation_context));
+  ASSERT_FALSE(TransformationPermuteFunctionParameters(4, 105, {})
+                   .IsApplicable(context.get(), transformation_context));
 
   // Can't permute invalid instruction
-  ASSERT_FALSE(TransformationPermuteFunctionParameters(101, 0, {})
+  ASSERT_FALSE(TransformationPermuteFunctionParameters(101, 105, {})
                    .IsApplicable(context.get(), transformation_context));
 
   // Permutation has too many values
-  ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {2, 1, 0, 3})
+  ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 105, {2, 1, 0, 3})
                    .IsApplicable(context.get(), transformation_context));
 
   // Permutation has too few values
-  ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {0, 1})
+  ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 105, {0, 1})
                    .IsApplicable(context.get(), transformation_context));
 
-  // Permutation has invalid values
-  ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {3, 1, 0})
+  // Permutation has invalid values 1
+  ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 105, {3, 1, 0})
                    .IsApplicable(context.get(), transformation_context));
 
-  // Type id is not an OpTypeFunction instruction
+#ifndef NDEBUG
+  // Permutation has invalid values 2
+  ASSERT_DEATH(TransformationPermuteFunctionParameters(22, 105, {2, 2, 1})
+                   .IsApplicable(context.get(), transformation_context),
+               "Permutation has duplicates");
+#endif
+
+  // Result id for new function type is not fresh.
   ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 42, {2, 1, 0})
                    .IsApplicable(context.get(), transformation_context));
 
-  // Type id has incorrect number of operands
-  ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 9, {2, 1, 0})
-                   .IsApplicable(context.get(), transformation_context));
-
-  // OpTypeFunction has operands out of order
-  ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 18, {2, 1, 0})
-                   .IsApplicable(context.get(), transformation_context));
-
   // Successful transformations
   {
-    // Function has two operands of the same type:
-    // initial OpTypeFunction should be enough
-    TransformationPermuteFunctionParameters transformation(12, 9, {1, 0});
+    TransformationPermuteFunctionParameters transformation(12, 105, {1, 0});
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
   {
-    TransformationPermuteFunctionParameters transformation(28, 105, {1, 0});
+    TransformationPermuteFunctionParameters transformation(28, 106, {1, 0});
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+  {
+    TransformationPermuteFunctionParameters transformation(200, 107, {1, 0});
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+  {
+    TransformationPermuteFunctionParameters transformation(219, 108, {1, 0});
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+  {
+    TransformationPermuteFunctionParameters transformation(229, 109, {1, 0});
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     transformation.Apply(context.get(), &transformation_context);
@@ -306,8 +378,6 @@
          %17 = OpTypePointer Function %16
          %18 = OpTypeFunction %8 %7 %15 %17
          %24 = OpTypeBool
-         %25 = OpTypeFunction %24 %15 %7
-         %105 = OpTypeFunction %24 %7 %15    ; predefined type for %28
          %31 = OpConstant %6 255
          %33 = OpConstant %6 0
          %34 = OpConstant %6 1
@@ -324,6 +394,12 @@
          %84 = OpConstant %14 5
          %90 = OpConstant %6 3
          %98 = OpConstant %6 4
+        %206 = OpTypeFunction %2 %14 %16
+        %223 = OpTypeFunction %2 %6 %8
+        %224 = OpTypeFunction %2 %8 %6
+        %233 = OpTypeFunction %2 %42 %24
+         %25 = OpTypeFunction %24 %7 %15
+        %107 = OpTypeFunction %2 %16 %14
           %4 = OpFunction %2 None %3
           %5 = OpLabel
          %66 = OpVariable %15 Function
@@ -415,7 +491,7 @@
          %55 = OpFunctionCall %8 %12 %54 %53
                OpReturnValue %55
                OpFunctionEnd
-         %28 = OpFunction %24 None %105
+         %28 = OpFunction %24 None %25
          %27 = OpFunctionParameter %7
          %26 = OpFunctionParameter %15
          %29 = OpLabel
@@ -425,6 +501,48 @@
          %61 = OpFOrdLessThan %24 %59 %60
                OpReturnValue %61
                OpFunctionEnd
+        %200 = OpFunction %2 None %107
+        %208 = OpFunctionParameter %16
+        %207 = OpFunctionParameter %14
+        %202 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %203 = OpFunction %2 None %206
+        %209 = OpFunctionParameter %14
+        %210 = OpFunctionParameter %16
+        %205 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %211 = OpFunction %2 None %223
+        %212 = OpFunctionParameter %6
+        %213 = OpFunctionParameter %8
+        %214 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %215 = OpFunction %2 None %224
+        %216 = OpFunctionParameter %8
+        %217 = OpFunctionParameter %6
+        %218 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %219 = OpFunction %2 None %223
+        %221 = OpFunctionParameter %6
+        %220 = OpFunctionParameter %8
+        %222 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %225 = OpFunction %2 None %233
+        %226 = OpFunctionParameter %42
+        %227 = OpFunctionParameter %24
+        %228 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %229 = OpFunction %2 None %233
+        %231 = OpFunctionParameter %42
+        %230 = OpFunctionParameter %24
+        %232 = OpLabel
+               OpReturn
+               OpFunctionEnd
   )";
 
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_permute_phi_operands_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_permute_phi_operands_test.cpp
new file mode 100644
index 0000000..c0a428a
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_permute_phi_operands_test.cpp
@@ -0,0 +1,154 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_permute_phi_operands.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationPermutePhiOperandsTest, 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
+          %9 = OpConstant %6 0
+         %11 = OpConstant %6 1
+         %14 = OpTypeBool
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+               OpStore %8 %9
+               OpStore %10 %11
+         %12 = OpLoad %6 %8
+         %13 = OpLoad %6 %10
+         %15 = OpSLessThan %14 %12 %13
+               OpSelectionMerge %17 None
+               OpBranchConditional %15 %16 %21
+         %16 = OpLabel
+         %18 = OpLoad %6 %10
+         %19 = OpLoad %6 %8
+         %20 = OpIAdd %6 %19 %18
+               OpBranch %17
+         %21 = OpLabel
+         %22 = OpLoad %6 %10
+         %23 = OpLoad %6 %8
+         %24 = OpISub %6 %23 %22
+               OpBranch %17
+         %17 = OpLabel
+         %25 = OpPhi %6 %20 %16 %24 %21
+               OpStore %8 %25
+               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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Result id is invalid.
+  ASSERT_FALSE(TransformationPermutePhiOperands(26, {}).IsApplicable(
+      context.get(), transformation_context));
+
+  // Result id is not of an OpPhi instruction.
+  ASSERT_FALSE(TransformationPermutePhiOperands(24, {}).IsApplicable(
+      context.get(), transformation_context));
+
+  // Result id is not of an OpPhi instruction.
+  ASSERT_FALSE(TransformationPermutePhiOperands(24, {}).IsApplicable(
+      context.get(), transformation_context));
+
+  // Permutation has invalid size.
+  ASSERT_FALSE(TransformationPermutePhiOperands(25, {0, 1, 2})
+                   .IsApplicable(context.get(), transformation_context));
+
+#ifndef NDEBUG
+  // Permutation has duplicates.
+  ASSERT_DEATH(TransformationPermutePhiOperands(25, {0, 0})
+                   .IsApplicable(context.get(), transformation_context),
+               "Permutation has duplicates");
+#endif
+
+  // Permutation's values are not in range.
+  ASSERT_FALSE(TransformationPermutePhiOperands(25, {1, 2})
+                   .IsApplicable(context.get(), transformation_context));
+
+  TransformationPermutePhiOperands transformation(25, {1, 0});
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %11 = OpConstant %6 1
+         %14 = OpTypeBool
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+               OpStore %8 %9
+               OpStore %10 %11
+         %12 = OpLoad %6 %8
+         %13 = OpLoad %6 %10
+         %15 = OpSLessThan %14 %12 %13
+               OpSelectionMerge %17 None
+               OpBranchConditional %15 %16 %21
+         %16 = OpLabel
+         %18 = OpLoad %6 %10
+         %19 = OpLoad %6 %8
+         %20 = OpIAdd %6 %19 %18
+               OpBranch %17
+         %21 = OpLabel
+         %22 = OpLoad %6 %10
+         %23 = OpLoad %6 %8
+         %24 = OpISub %6 %23 %22
+               OpBranch %17
+         %17 = OpLabel
+         %25 = OpPhi %6 %24 %21 %20 %16
+               OpStore %8 %25
+               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_push_id_through_variable_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_push_id_through_variable_test.cpp
new file mode 100644
index 0000000..eac7820
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_push_id_through_variable_test.cpp
@@ -0,0 +1,707 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_push_id_through_variable.h"
+
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationPushIdThroughVariableTest, IsApplicable) {
+  std::string reference_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %92 %52 %53
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %92 BuiltIn FragCoord
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeFloat 32
+          %8 = OpTypeStruct %6 %7
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeFunction %6 %9
+         %14 = OpConstant %6 0
+         %15 = OpTypePointer Function %6
+         %51 = OpTypePointer Private %6
+         %21 = OpConstant %6 2
+         %23 = OpConstant %6 1
+         %24 = OpConstant %7 1
+         %25 = OpTypePointer Function %7
+         %50 = OpTypePointer Private %7
+         %34 = OpTypeBool
+         %35 = OpConstantFalse %34
+         %60 = OpConstantNull %50
+         %61 = OpUndef %51
+         %52 = OpVariable %50 Private
+         %53 = OpVariable %51 Private
+         %80 = OpConstantComposite %8 %21 %24
+         %90 = OpTypeVector %7 4
+         %91 = OpTypePointer Input %90
+         %92 = OpVariable %91 Input
+         %93 = OpConstantComposite %90 %24 %24 %24 %24
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %20 = OpVariable %9 Function
+         %27 = OpVariable %9 Function
+         %22 = OpAccessChain %15 %20 %14
+         %44 = OpCopyObject %9 %20
+         %26 = OpAccessChain %25 %20 %23
+         %29 = OpFunctionCall %6 %12 %27
+         %30 = OpAccessChain %15 %20 %14
+         %45 = OpCopyObject %15 %30
+         %81 = OpCopyObject %9 %27
+         %33 = OpAccessChain %15 %20 %14
+               OpSelectionMerge %37 None
+               OpBranchConditional %35 %36 %37
+         %36 = OpLabel
+         %38 = OpAccessChain %15 %20 %14
+         %40 = OpAccessChain %15 %20 %14
+         %43 = OpAccessChain %15 %20 %14
+         %82 = OpCopyObject %9 %27
+               OpBranch %37
+         %37 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9
+         %13 = OpLabel
+         %46 = OpCopyObject %9 %11
+         %16 = OpAccessChain %15 %11 %14
+         %95 = OpCopyObject %8 %80
+               OpReturnValue %21
+        %100 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Tests the reference shader validity.
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Tests |value_synonym_id| and |variable_id| are fresh ids.
+  uint32_t value_id = 21;
+  uint32_t value_synonym_id = 62;
+  uint32_t variable_id = 63;
+  uint32_t initializer_id = 23;
+  uint32_t variable_storage_class = SpvStorageClassPrivate;
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+  auto transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests |value_synonym_id| and |variable_id| are non-fresh ids.
+  value_id = 80;
+  value_synonym_id = 60;
+  variable_id = 61;
+  initializer_id = 80;
+  variable_storage_class = SpvStorageClassFunction;
+  instruction_descriptor = MakeInstructionDescriptor(38, SpvOpAccessChain, 0);
+  transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // The instruction to insert before is not defined.
+  value_id = 80;
+  value_synonym_id = 62;
+  variable_id = 63;
+  initializer_id = 80;
+  variable_storage_class = SpvStorageClassFunction;
+  instruction_descriptor = MakeInstructionDescriptor(64, SpvOpAccessChain, 0);
+  transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Attempting to insert the store and load instructions
+  // before an OpVariable instruction.
+  value_id = 24;
+  value_synonym_id = 62;
+  variable_id = 63;
+  initializer_id = 24;
+  variable_storage_class = SpvStorageClassFunction;
+  instruction_descriptor = MakeInstructionDescriptor(27, SpvOpVariable, 0);
+  transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // The block containing instruction descriptor must be reachable.
+  value_id = 80;
+  value_synonym_id = 62;
+  variable_id = 63;
+  initializer_id = 80;
+  variable_storage_class = SpvStorageClassFunction;
+  instruction_descriptor = MakeInstructionDescriptor(100, SpvOpUnreachable, 0);
+  transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests value instruction not available.
+  value_id = 64;
+  value_synonym_id = 62;
+  variable_id = 63;
+  initializer_id = 23;
+  variable_storage_class = SpvStorageClassFunction;
+  instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+  transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests pointer type not available.
+  value_id = 80;
+  value_synonym_id = 62;
+  variable_id = 63;
+  initializer_id = 80;
+  variable_storage_class = SpvStorageClassPrivate;
+  instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+  transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests not a private nor function storage class.
+  value_id = 93;
+  value_synonym_id = 62;
+  variable_id = 63;
+  initializer_id = 93;
+  variable_storage_class = SpvStorageClassInput;
+  instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+  transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+#ifndef NDEBUG
+  ASSERT_DEATH(
+      transformation.IsApplicable(context.get(), transformation_context),
+      "The variable storage class must be private or function");
+#endif
+
+  // Tests value instruction not available before instruction.
+  value_id = 95;
+  value_synonym_id = 62;
+  variable_id = 63;
+  initializer_id = 80;
+  variable_storage_class = SpvStorageClassFunction;
+  instruction_descriptor = MakeInstructionDescriptor(40, SpvOpAccessChain, 0);
+  transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Variable initializer is not constant.
+  value_id = 95;
+  value_synonym_id = 62;
+  variable_id = 63;
+  initializer_id = 95;
+  variable_storage_class = SpvStorageClassFunction;
+  instruction_descriptor = MakeInstructionDescriptor(40, SpvOpAccessChain, 0);
+  transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Variable initializer has wrong type.
+  value_id = 95;
+  value_synonym_id = 62;
+  variable_id = 63;
+  initializer_id = 93;
+  variable_storage_class = SpvStorageClassFunction;
+  instruction_descriptor = MakeInstructionDescriptor(40, SpvOpAccessChain, 0);
+  transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationPushIdThroughVariableTest, Apply) {
+  std::string reference_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
+               OpStore %53 %21
+         %22 = OpAccessChain %15 %20 %14
+         %44 = OpCopyObject %9 %20
+         %26 = OpAccessChain %25 %20 %23
+         %29 = OpFunctionCall %6 %12 %27
+         %30 = OpAccessChain %15 %20 %14
+         %45 = OpCopyObject %15 %30
+         %81 = OpCopyObject %9 %27
+         %33 = OpAccessChain %15 %20 %14
+               OpSelectionMerge %37 None
+               OpBranchConditional %35 %36 %37
+         %36 = OpLabel
+         %38 = OpAccessChain %15 %20 %14
+         %40 = OpAccessChain %15 %20 %14
+         %43 = OpAccessChain %15 %20 %14
+         %82 = OpCopyObject %9 %27
+               OpBranch %37
+         %37 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9
+         %13 = OpLabel
+         %46 = OpCopyObject %9 %11
+         %16 = OpAccessChain %15 %11 %14
+         %95 = OpCopyObject %8 %80
+               OpReturnValue %21
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  uint32_t value_id = 80;
+  uint32_t value_synonym_id = 100;
+  uint32_t variable_id = 101;
+  uint32_t initializer_id = 80;
+  uint32_t variable_storage_class = SpvStorageClassFunction;
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(38, SpvOpAccessChain, 0);
+  auto transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  value_id = 21;
+  value_synonym_id = 102;
+  variable_id = 103;
+  initializer_id = 21;
+  variable_storage_class = SpvStorageClassFunction;
+  instruction_descriptor = MakeInstructionDescriptor(38, SpvOpAccessChain, 0);
+  transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  value_id = 95;
+  value_synonym_id = 104;
+  variable_id = 105;
+  initializer_id = 80;
+  variable_storage_class = SpvStorageClassFunction;
+  instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+  transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  value_id = 80;
+  value_synonym_id = 106;
+  variable_id = 107;
+  initializer_id = 80;
+  variable_storage_class = SpvStorageClassFunction;
+  instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+  transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  value_id = 21;
+  value_synonym_id = 108;
+  variable_id = 109;
+  initializer_id = 21;
+  variable_storage_class = SpvStorageClassPrivate;
+  instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+  transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  value_id = 23;
+  value_synonym_id = 110;
+  variable_id = 111;
+  initializer_id = 21;
+  variable_storage_class = SpvStorageClassPrivate;
+  instruction_descriptor = MakeInstructionDescriptor(27, SpvOpStore, 0);
+  transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  std::string variant_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %92 %52 %53 %109 %111
+               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
+        %109 = OpVariable %51 Private %21
+        %111 = OpVariable %51 Private %21
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %103 = OpVariable %15 Function %21
+        %101 = OpVariable %9 Function %80
+         %20 = OpVariable %9 Function
+         %27 = OpVariable %9 Function
+               OpStore %111 %23
+        %110 = OpLoad %6 %111
+               OpStore %53 %21
+         %22 = OpAccessChain %15 %20 %14
+         %44 = OpCopyObject %9 %20
+         %26 = OpAccessChain %25 %20 %23
+         %29 = OpFunctionCall %6 %12 %27
+         %30 = OpAccessChain %15 %20 %14
+         %45 = OpCopyObject %15 %30
+         %81 = OpCopyObject %9 %27
+         %33 = OpAccessChain %15 %20 %14
+               OpSelectionMerge %37 None
+               OpBranchConditional %35 %36 %37
+         %36 = OpLabel
+               OpStore %101 %80
+        %100 = OpLoad %8 %101
+               OpStore %103 %21
+        %102 = OpLoad %6 %103
+         %38 = OpAccessChain %15 %20 %14
+         %40 = OpAccessChain %15 %20 %14
+         %43 = OpAccessChain %15 %20 %14
+         %82 = OpCopyObject %9 %27
+               OpBranch %37
+         %37 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9
+         %13 = OpLabel
+        %107 = OpVariable %9 Function %80
+        %105 = OpVariable %9 Function %80
+         %46 = OpCopyObject %9 %11
+         %16 = OpAccessChain %15 %11 %14
+         %95 = OpCopyObject %8 %80
+               OpStore %105 %95
+        %104 = OpLoad %8 %105
+               OpStore %107 %80
+        %106 = OpLoad %8 %107
+               OpStore %109 %21
+        %108 = OpLoad %6 %109
+               OpReturnValue %21
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(80, {}),
+                                        MakeDataDescriptor(100, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
+                                        MakeDataDescriptor(102, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(95, {}),
+                                        MakeDataDescriptor(104, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(80, {}),
+                                        MakeDataDescriptor(106, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
+                                        MakeDataDescriptor(108, {})));
+}
+
+TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds) {
+  std::string reference_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %92 %52 %53
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %92 BuiltIn FragCoord
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeFloat 32
+          %8 = OpTypeStruct %6 %7
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeFunction %6 %9
+         %14 = OpConstant %6 0
+         %15 = OpTypePointer Function %6
+         %51 = OpTypePointer Private %6
+         %21 = OpConstant %6 2
+         %23 = OpConstant %6 1
+         %24 = OpConstant %7 1
+         %25 = OpTypePointer Function %7
+         %50 = OpTypePointer Private %7
+         %34 = OpTypeBool
+         %35 = OpConstantFalse %34
+         %60 = OpConstantNull %50
+         %61 = OpUndef %51
+         %52 = OpVariable %50 Private
+         %53 = OpVariable %51 Private
+         %80 = OpConstantComposite %8 %21 %24
+         %90 = OpTypeVector %7 4
+         %91 = OpTypePointer Input %90
+         %92 = OpVariable %91 Input
+         %93 = OpConstantComposite %90 %24 %24 %24 %24
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %20 = OpVariable %9 Function
+         %27 = OpVariable %9 Function
+         %22 = OpAccessChain %15 %20 %14
+         %44 = OpCopyObject %9 %20
+         %26 = OpAccessChain %25 %20 %23
+         %29 = OpFunctionCall %6 %12 %27
+         %30 = OpAccessChain %15 %20 %14
+         %45 = OpCopyObject %15 %30
+         %81 = OpCopyObject %9 %27
+         %33 = OpAccessChain %15 %20 %14
+               OpSelectionMerge %37 None
+               OpBranchConditional %35 %36 %37
+         %36 = OpLabel
+         %38 = OpAccessChain %15 %20 %14
+         %40 = OpAccessChain %15 %20 %14
+         %43 = OpAccessChain %15 %20 %14
+         %82 = OpCopyObject %9 %27
+               OpBranch %37
+         %37 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9
+         %13 = OpLabel
+         %46 = OpCopyObject %9 %11
+         %16 = OpAccessChain %15 %11 %14
+         %95 = OpCopyObject %8 %80
+               OpReturnValue %21
+        %100 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Tests the reference shader validity.
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  uint32_t value_id = 21;
+  uint32_t value_synonym_id = 62;
+  uint32_t variable_id = 63;
+  uint32_t initializer_id = 23;
+  uint32_t variable_storage_class = SpvStorageClassPrivate;
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+  auto transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
+                                        MakeDataDescriptor(62, {})));
+}
+
+TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsForIrrelevantIds) {
+  std::string reference_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %92 %52 %53
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %92 BuiltIn FragCoord
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeFloat 32
+          %8 = OpTypeStruct %6 %7
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeFunction %6 %9
+         %14 = OpConstant %6 0
+         %15 = OpTypePointer Function %6
+         %51 = OpTypePointer Private %6
+         %21 = OpConstant %6 2
+         %23 = OpConstant %6 1
+         %24 = OpConstant %7 1
+         %25 = OpTypePointer Function %7
+         %50 = OpTypePointer Private %7
+         %34 = OpTypeBool
+         %35 = OpConstantFalse %34
+         %60 = OpConstantNull %50
+         %61 = OpUndef %51
+         %52 = OpVariable %50 Private
+         %53 = OpVariable %51 Private
+         %80 = OpConstantComposite %8 %21 %24
+         %90 = OpTypeVector %7 4
+         %91 = OpTypePointer Input %90
+         %92 = OpVariable %91 Input
+         %93 = OpConstantComposite %90 %24 %24 %24 %24
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %20 = OpVariable %9 Function
+         %27 = OpVariable %9 Function
+         %22 = OpAccessChain %15 %20 %14
+         %44 = OpCopyObject %9 %20
+         %26 = OpAccessChain %25 %20 %23
+         %29 = OpFunctionCall %6 %12 %27
+         %30 = OpAccessChain %15 %20 %14
+         %45 = OpCopyObject %15 %30
+         %81 = OpCopyObject %9 %27
+         %33 = OpAccessChain %15 %20 %14
+               OpSelectionMerge %37 None
+               OpBranchConditional %35 %36 %37
+         %36 = OpLabel
+         %38 = OpAccessChain %15 %20 %14
+         %40 = OpAccessChain %15 %20 %14
+         %43 = OpAccessChain %15 %20 %14
+         %82 = OpCopyObject %9 %27
+               OpBranch %37
+         %37 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9
+         %13 = OpLabel
+         %46 = OpCopyObject %9 %11
+         %16 = OpAccessChain %15 %11 %14
+         %95 = OpCopyObject %8 %80
+               OpReturnValue %21
+        %100 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Tests the reference shader validity.
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  fact_manager.AddFactIdIsIrrelevant(21);
+
+  uint32_t value_id = 21;
+  uint32_t value_synonym_id = 62;
+  uint32_t variable_id = 63;
+  uint32_t initializer_id = 23;
+  uint32_t variable_storage_class = SpvStorageClassPrivate;
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+  auto transformation = TransformationPushIdThroughVariable(
+      value_id, value_synonym_id, variable_id, variable_storage_class,
+      initializer_id, instruction_descriptor);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
+                                         MakeDataDescriptor(62, {})));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_record_synonymous_constants_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_record_synonymous_constants_test.cpp
new file mode 100644
index 0000000..2c309fd
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_record_synonymous_constants_test.cpp
@@ -0,0 +1,896 @@
+// Copyright (c) 2020 Stefano Milizia
+// 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_record_synonymous_constants.h"
+
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+// Apply the TransformationRecordSynonymousConstants defined by the given
+// constant1_id and constant2_id and check that the fact that the two
+// constants are synonym is recorded.
+void ApplyTransformationAndCheckFactManager(
+    uint32_t constant1_id, uint32_t constant2_id, opt::IRContext* ir_context,
+    TransformationContext* transformation_context) {
+  TransformationRecordSynonymousConstants(constant1_id, constant2_id)
+      .Apply(ir_context, transformation_context);
+
+  ASSERT_TRUE(transformation_context->GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(constant1_id, {}),
+      MakeDataDescriptor(constant2_id, {})));
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, IntConstants) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %17
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "b"
+               OpName %12 "c"
+               OpName %17 "color"
+               OpDecorate %8 RelaxedPrecision
+               OpDecorate %10 RelaxedPrecision
+               OpDecorate %12 RelaxedPrecision
+               OpDecorate %17 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 0
+         %19 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %18 = OpConstant %6 0
+         %11 = OpConstantNull %6
+         %13 = OpConstant %6 1
+         %20 = OpConstant %19 1
+         %21 = OpConstant %19 -1
+         %22 = OpConstant %6 1
+         %14 = OpTypeFloat 32
+         %15 = OpTypeVector %14 4
+         %16 = OpTypePointer Output %15
+         %17 = OpVariable %16 Output
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %12 = OpVariable %7 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %12 %13
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+#ifndef NDEBUG
+  // %3 is not a constant declaration
+  ASSERT_DEATH(TransformationRecordSynonymousConstants(3, 9).IsApplicable(
+                   context.get(), transformation_context),
+               "The ids must refer to constants.");
+#endif
+
+#ifndef NDEBUG
+  // %3 is not a constant declaration
+  ASSERT_DEATH(TransformationRecordSynonymousConstants(9, 3).IsApplicable(
+                   context.get(), transformation_context),
+               "The ids must refer to constants.");
+#endif
+
+  // The two constants must be different
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 9).IsApplicable(
+      context.get(), transformation_context));
+
+  // %9 and %13 are not equivalent
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 13).IsApplicable(
+      context.get(), transformation_context));
+
+  // Swapping the ids gives the same result
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(13, 9).IsApplicable(
+      context.get(), transformation_context));
+
+  // %11 and %13 are not equivalent
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(11, 13).IsApplicable(
+      context.get(), transformation_context));
+
+  // Swapping the ids gives the same result
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(13, 11).IsApplicable(
+      context.get(), transformation_context));
+
+  // %20 and %21 have different values
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(20, 21).IsApplicable(
+      context.get(), transformation_context));
+
+  // %13 and %22 are equal and thus equivalent (having the same value and type)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(13, 22).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(13, 22, context.get(),
+                                         &transformation_context);
+
+  // %13 and %20 are equal even if %13 is signed and %20 is unsigned
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(13, 20).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(13, 20, context.get(),
+                                         &transformation_context);
+
+  // %9 and %11 are equivalent (OpConstant with value 0 and OpConstantNull)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(9, 11).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(9, 11, context.get(),
+                                         &transformation_context);
+
+  // Swapping the ids gives the same result
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(11, 9).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(11, 9, context.get(),
+                                         &transformation_context);
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, BoolConstants) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %19
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "b"
+               OpName %19 "color"
+               OpDecorate %19 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypePointer Function %6
+          %9 = OpConstantFalse %6
+         %20 = OpConstantNull %6
+         %11 = OpConstantTrue %6
+         %21 = OpConstantFalse %6
+         %22 = OpConstantTrue %6
+         %16 = OpTypeFloat 32
+         %17 = OpTypeVector %16 4
+         %18 = OpTypePointer Output %17
+         %19 = OpVariable %18 Output
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpStore %8 %9
+         %10 = OpLoad %6 %8
+         %12 = OpLogicalEqual %6 %10 %11
+               OpSelectionMerge %14 None
+               OpBranchConditional %12 %13 %14
+         %13 = OpLabel
+               OpReturn
+         %14 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // %9 and %11 are not equivalent
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 11).IsApplicable(
+      context.get(), transformation_context));
+
+  // %20 and %11 are not equivalent
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(20, 11).IsApplicable(
+      context.get(), transformation_context));
+
+  // %9 and %21 are equivalent (both OpConstantFalse)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(9, 21).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(9, 21, context.get(),
+                                         &transformation_context);
+
+  // %11 and %22 are equivalent (both OpConstantTrue)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(11, 22).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(11, 22, context.get(),
+                                         &transformation_context);
+
+  // %9 and %20 are equivalent (OpConstantFalse and boolean OpConstantNull)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(9, 20).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(9, 20, context.get(),
+                                         &transformation_context);
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, FloatConstants) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %22
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "b"
+               OpName %12 "c"
+               OpName %22 "color"
+               OpDecorate %22 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %11 = OpConstantNull %6
+         %13 = OpConstant %6 2
+         %26 = OpConstant %6 2
+         %16 = OpTypeBool
+         %20 = OpTypeVector %6 4
+         %21 = OpTypePointer Output %20
+         %22 = OpVariable %21 Output
+         %23 = OpConstantComposite %20 %9 %11 %9 %11
+         %25 = OpConstantComposite %20 %11 %9 %9 %11
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %12 = OpVariable %7 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %12 %13
+         %14 = OpLoad %6 %8
+         %15 = OpLoad %6 %10
+         %17 = OpFOrdEqual %16 %14 %15
+               OpSelectionMerge %19 None
+               OpBranchConditional %17 %18 %24
+         %18 = OpLabel
+               OpStore %22 %23
+               OpBranch %19
+         %24 = OpLabel
+               OpStore %22 %25
+               OpBranch %19
+         %19 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // %9 and %13 are not equivalent
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 13).IsApplicable(
+      context.get(), transformation_context));
+
+  // %11 and %13 are not equivalent
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(11, 13).IsApplicable(
+      context.get(), transformation_context));
+
+  // %13 and %23 are not equivalent
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(13, 23).IsApplicable(
+      context.get(), transformation_context));
+
+  // %13 and %26 are identical float constants
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(13, 26).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(13, 26, context.get(),
+                                         &transformation_context);
+
+  // %9 and %11 are equivalent ()
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(9, 11).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(9, 11, context.get(),
+                                         &transformation_context);
+}
+
+TEST(TransformationRecordSynonymousConstantsTest,
+     VectorAndMatrixCompositeConstants) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %24
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %12 "d"
+               OpName %16 "e"
+               OpName %24 "color"
+               OpDecorate %12 RelaxedPrecision
+               OpDecorate %18 RelaxedPrecision
+               OpDecorate %24 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %28 = OpConstant %6 0
+         %30 = OpConstant %6 1
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Function %10
+         %13 = OpConstant %10 0
+         %14 = OpTypeBool
+         %15 = OpTypePointer Function %14
+         %17 = OpConstantFalse %14
+         %22 = OpTypeVector %6 4
+         %37 = OpTypeVector %6 3
+         %32 = OpTypeMatrix %22 2
+         %39 = OpTypeMatrix %22 3
+         %23 = OpTypePointer Output %22
+         %24 = OpVariable %23 Output
+         %25 = OpConstantComposite %22 %9 %9 %9 %9
+         %27 = OpConstantNull %22
+         %29 = OpConstantComposite %22 %9 %28 %28 %9
+         %31 = OpConstantComposite %22 %30 %9 %9 %9
+         %38 = OpConstantComposite %37 %9 %9 %9
+         %33 = OpConstantComposite %32 %25 %29
+         %34 = OpConstantComposite %32 %27 %25
+         %35 = OpConstantNull %32
+         %36 = OpConstantComposite %32 %31 %25
+         %40 = OpConstantComposite %39 %25 %25 %25
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %12 = OpVariable %11 Function
+         %16 = OpVariable %15 Function
+               OpStore %8 %9
+               OpStore %12 %13
+               OpStore %16 %17
+         %18 = OpLoad %10 %12
+         %19 = OpIEqual %14 %18 %13
+               OpSelectionMerge %21 None
+               OpBranchConditional %19 %20 %26
+         %20 = OpLabel
+               OpStore %24 %25
+               OpBranch %21
+         %26 = OpLabel
+               OpStore %24 %25
+               OpBranch %21
+         %21 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // %25 and %27 are equivalent (25 is zero-like, 27 is null)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(25, 27).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(25, 27, context.get(),
+                                         &transformation_context);
+
+  // %25 and %29 are equivalent (same type and value)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(25, 29).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(25, 29, context.get(),
+                                         &transformation_context);
+
+  // %27 and %29 are equivalent (27 is null, 29 is zero-like)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(27, 29).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(27, 29, context.get(),
+                                         &transformation_context);
+
+  // %25 and %31 are not equivalent (they have different values)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(25, 31).IsApplicable(
+      context.get(), transformation_context));
+
+  // %27 and %31 are not equivalent (27 is null, 31 is not zero-like)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(27, 31).IsApplicable(
+      context.get(), transformation_context));
+
+  // %25 and %38 are not equivalent (they have different sizes)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(25, 38).IsApplicable(
+      context.get(), transformation_context));
+
+  // %35 and %36 are not equivalent (35 is null, 36 has non-zero components)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(35, 36).IsApplicable(
+      context.get(), transformation_context));
+
+  // %33 and %36 are not equivalent (not all components are equivalent)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(33, 36).IsApplicable(
+      context.get(), transformation_context));
+
+  // %33 and %40 are not equivalent (they have different sizes)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(33, 40).IsApplicable(
+      context.get(), transformation_context));
+
+  // %33 and %34 are equivalent (same type, equivalent components)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(33, 34).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(33, 34, context.get(),
+                                         &transformation_context);
+
+  // %33 and %35 are equivalent (33 has zero-valued components, 35 is null)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(33, 35).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(33, 35, context.get(),
+                                         &transformation_context);
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, StructCompositeConstants) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %24
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %12 "d"
+               OpName %16 "e"
+               OpName %24 "color"
+               OpDecorate %12 RelaxedPrecision
+               OpDecorate %18 RelaxedPrecision
+               OpDecorate %24 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %28 = OpConstant %6 0
+         %30 = OpConstant %6 1
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Function %10
+         %13 = OpConstant %10 0
+         %33 = OpConstantNull %10
+         %14 = OpTypeBool
+         %15 = OpTypePointer Function %14
+         %17 = OpConstantFalse %14
+         %34 = OpConstantNull %14
+         %22 = OpTypeVector %6 4
+         %32 = OpTypeStruct %22 %10 %14 %6
+         %38 = OpTypeStruct %6 %6 %6 %6
+         %23 = OpTypePointer Output %22
+         %24 = OpVariable %23 Output
+         %25 = OpConstantComposite %22 %9 %9 %9 %9
+         %27 = OpConstantNull %22
+         %29 = OpConstantComposite %22 %9 %28 %28 %9
+         %31 = OpConstantComposite %22 %30 %9 %9 %9
+         %35 = OpConstantComposite %32 %25 %13 %17 %9
+         %36 = OpConstantComposite %32 %27 %33 %34 %28
+         %37 = OpConstantComposite %32 %31 %13 %17 %9
+         %39 = OpConstantComposite %38 %9 %9 %9 %9
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %12 = OpVariable %11 Function
+         %16 = OpVariable %15 Function
+               OpStore %8 %9
+               OpStore %12 %13
+               OpStore %16 %17
+         %18 = OpLoad %10 %12
+         %19 = OpIEqual %14 %18 %13
+               OpSelectionMerge %21 None
+               OpBranchConditional %19 %20 %26
+         %20 = OpLabel
+               OpStore %24 %25
+               OpBranch %21
+         %26 = OpLabel
+               OpStore %24 %25
+               OpBranch %21
+         %21 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // %29 and %35 are not equivalent (they have different types)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(29, 35).IsApplicable(
+      context.get(), transformation_context));
+
+  // %35 and %37 are not equivalent (their first components are not equivalent)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(35, 37).IsApplicable(
+      context.get(), transformation_context));
+
+  // %35 and %36 are equivalent (all their components are equivalent)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(35, 36).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(35, 36, context.get(),
+                                         &transformation_context);
+
+  // %25 and %39 are not equivalent (they have different types)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(25, 39).IsApplicable(
+      context.get(), transformation_context));
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, ArrayCompositeConstants) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %24
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %12 "d"
+               OpName %16 "e"
+               OpName %24 "color"
+               OpDecorate %12 RelaxedPrecision
+               OpDecorate %18 RelaxedPrecision
+               OpDecorate %24 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %38 = OpConstant %6 1
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Function %10
+         %13 = OpConstant %10 0
+         %27 = OpConstant %10 4
+         %39 = OpConstant %10 2
+         %14 = OpTypeBool
+         %15 = OpTypePointer Function %14
+         %17 = OpConstantFalse %14
+         %22 = OpTypeVector %6 4
+         %28 = OpTypeArray %6 %27
+         %29 = OpTypeArray %28 %27
+         %40 = OpTypeArray %6 %39
+         %23 = OpTypePointer Output %22
+         %24 = OpVariable %23 Output
+         %25 = OpConstantComposite %22 %9 %9 %9 %9
+         %31 = OpConstantComposite %28 %9 %9 %9 %9
+         %41 = OpConstantComposite %40 %9 %9
+         %32 = OpConstantComposite %28 %38 %9 %9 %9
+         %33 = OpConstantNull %28
+         %34 = OpConstantComposite %29 %31 %33 %31 %33
+         %35 = OpConstantComposite %29 %33 %31 %33 %31
+         %36 = OpConstantNull %29
+         %37 = OpConstantComposite %29 %32 %33 %31 %33
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %12 = OpVariable %11 Function
+         %16 = OpVariable %15 Function
+               OpStore %8 %9
+               OpStore %12 %13
+               OpStore %16 %17
+         %18 = OpLoad %10 %12
+         %19 = OpIEqual %14 %18 %13
+               OpSelectionMerge %21 None
+               OpBranchConditional %19 %20 %26
+         %20 = OpLabel
+               OpStore %24 %25
+               OpBranch %21
+         %26 = OpLabel
+               OpStore %24 %25
+               OpBranch %21
+         %21 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // %25 and %31 are not equivalent (they have different types)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(25, 31).IsApplicable(
+      context.get(), transformation_context));
+
+  // %25 and %41 are not equivalent (they have different sizes)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(25, 41).IsApplicable(
+      context.get(), transformation_context));
+
+  // %31 and %32 are not equivalent (their components are not pairwise
+  // equivalent)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(31, 32).IsApplicable(
+      context.get(), transformation_context));
+
+  // %31 and %33 are equivalent (%31 has zero-valued components, 32 is null)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(31, 33).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(31, 33, context.get(),
+                                         &transformation_context);
+
+  // %34 and %35 are equivalent (same type, equivalent components)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(34, 35).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(34, 35, context.get(),
+                                         &transformation_context);
+
+  // %35 and %36 are equivalent (%36 is null, %35 has zero-valued components)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(35, 36).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(35, 36, context.get(),
+                                         &transformation_context);
+
+  // %34 and %37 are not equivalent (they have non-equivalent components)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(34, 37).IsApplicable(
+      context.get(), transformation_context));
+
+  // %36 and %37 are not equivalent (36 is null, 37 does not have all-zero
+  // components)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(36, 37).IsApplicable(
+      context.get(), transformation_context));
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, IntVectors) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %3 Location 0
+          %4 = OpTypeVoid
+          %5 = OpTypeFunction %4
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %8 = OpTypeVector %6 4
+          %9 = OpTypeVector %7 4
+         %10 = OpTypePointer Function %8
+         %11 = OpTypePointer Function %8
+         %12 = OpConstant %6 0
+         %13 = OpConstant %7 0
+         %14 = OpConstant %6 1
+         %25 = OpConstant %7 1
+         %15 = OpConstantComposite %8 %12 %12 %12 %12
+         %16 = OpConstantComposite %9 %13 %13 %13 %13
+         %17 = OpConstantComposite %8 %14 %12 %12 %14
+         %18 = OpConstantComposite %9 %25 %13 %13 %25
+         %19 = OpConstantNull %8
+         %20 = OpConstantNull %9
+         %21 = OpTypeFloat 32
+         %22 = OpTypeVector %21 4
+         %23 = OpTypePointer Output %22
+          %3 = OpVariable %23 Output
+          %2 = OpFunction %4 None %5
+         %24 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // %15 and %17 are not equivalent (having non-equivalent components)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(15, 17).IsApplicable(
+      context.get(), transformation_context));
+
+  // %17 and %19 are not equivalent (%19 is null, %17 is non-zero)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(17, 19).IsApplicable(
+      context.get(), transformation_context));
+
+  // %17 and %20 are not equivalent (%19 is null, %20 is non-zero)
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(17, 20).IsApplicable(
+      context.get(), transformation_context));
+
+  // %15 and %16 are equivalent (having pairwise equivalent components)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(15, 16).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(15, 16, context.get(),
+                                         &transformation_context);
+
+  // %17 and %18 are equivalent (having pairwise equivalent components)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(17, 18).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(17, 18, context.get(),
+                                         &transformation_context);
+
+  // %19 and %20 are equivalent (both null vectors with compatible types)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(19, 20).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(19, 20, context.get(),
+                                         &transformation_context);
+
+  // %15 and %19 are equivalent (they have compatible types, %15 is zero-like
+  // and %19 is null)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(15, 19).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(15, 19, context.get(),
+                                         &transformation_context);
+
+  // %15 and %20 are equivalent (they have compatible types, %15 is zero-like
+  // and %20 is null)
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(15, 20).IsApplicable(
+      context.get(), transformation_context));
+
+  ApplyTransformationAndCheckFactManager(15, 20, context.get(),
+                                         &transformation_context);
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, FirstIrrelevantConstant) {
+  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 = OpTypeFloat 32
+          %7 = OpConstant %6 23
+          %8 = OpConstant %6 23
+          %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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(7, 8).IsApplicable(
+      context.get(), transformation_context));
+
+  fact_manager.AddFactIdIsIrrelevant(7);
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(7, 8).IsApplicable(
+      context.get(), transformation_context));
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, SecondIrrelevantConstant) {
+  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 = OpTypeFloat 32
+          %7 = OpConstant %6 23
+          %8 = OpConstant %6 23
+          %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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  ASSERT_TRUE(TransformationRecordSynonymousConstants(7, 8).IsApplicable(
+      context.get(), transformation_context));
+
+  fact_manager.AddFactIdIsIrrelevant(8);
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(7, 8).IsApplicable(
+      context.get(), transformation_context));
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, InvalidIds) {
+  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 = OpTypeFloat 32
+          %7 = OpConstant %6 23
+          %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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(7, 8).IsApplicable(
+      context.get(), transformation_context));
+
+  ASSERT_FALSE(TransformationRecordSynonymousConstants(8, 7).IsApplicable(
+      context.get(), transformation_context));
+}
+
+}  // 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 b320308..22815e6 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
@@ -616,41 +616,41 @@
   // Hand-written SPIR-V to check applicability of the transformation on an
   // OpPhi argument.
 
-  std::string shader = R"(
+  std::string reference_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"
+               OpEntryPoint Vertex %10 "main"
+
+; Types
           %2 = OpTypeVoid
           %3 = OpTypeFunction %2
-          %6 = OpTypeBool
-          %7 = OpTypePointer Function %6
-          %9 = OpConstantTrue %6
-         %16 = OpConstantFalse %6
-         %10 = OpTypeInt 32 1
-         %11 = OpTypePointer Function %10
-         %13 = OpConstant %10 0
-         %15 = OpConstant %10 1
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpSelectionMerge %20 None
-               OpBranchConditional %9 %21 %22
-         %21 = OpLabel
-               OpBranch %20
-         %22 = OpLabel
-               OpBranch %20
-         %20 = OpLabel
-         %23 = OpPhi %6 %9 %21 %16 %22
+          %4 = OpTypeInt 32 0
+          %5 = OpTypeBool
+
+; Constants
+          %6 = OpConstant %4 0
+          %7 = OpConstant %4 1
+          %8 = OpConstantTrue %5
+          %9 = OpConstantFalse %5
+
+; main function
+         %10 = OpFunction %2 None %3
+         %11 = OpLabel
+               OpSelectionMerge %13 None
+               OpBranchConditional %8 %12 %13
+         %12 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+         %14 = OpPhi %5 %8 %11 %9 %12
                OpReturn
                OpFunctionEnd
   )";
 
   const auto env = SPV_ENV_UNIVERSAL_1_3;
   const auto consumer = nullptr;
-  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
@@ -658,11 +658,48 @@
   TransformationContext transformation_context(&fact_manager,
                                                validator_options);
 
-  auto replacement = TransformationReplaceBooleanConstantWithConstantBinary(
-      MakeIdUseDescriptor(9, MakeInstructionDescriptor(23, SpvOpPhi, 0), 0), 13,
-      15, SpvOpSLessThan, 100);
+  auto instruction_descriptor = MakeInstructionDescriptor(14, SpvOpPhi, 0);
+  auto id_use_descriptor = MakeIdUseDescriptor(8, instruction_descriptor, 0);
+  auto transformation = TransformationReplaceBooleanConstantWithConstantBinary(
+      id_use_descriptor, 6, 7, SpvOpULessThan, 15);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
 
-  ASSERT_FALSE(replacement.IsApplicable(context.get(), transformation_context));
+  std::string variant_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %10 "main"
+
+; Types
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 0
+          %5 = OpTypeBool
+
+; Constants
+          %6 = OpConstant %4 0
+          %7 = OpConstant %4 1
+          %8 = OpConstantTrue %5
+          %9 = OpConstantFalse %5
+
+; main function
+         %10 = OpFunction %2 None %3
+         %11 = OpLabel
+         %15 = OpULessThan %5 %6 %7
+               OpSelectionMerge %13 None
+               OpBranchConditional %8 %12 %13
+         %12 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+         %14 = OpPhi %5 %15 %11 %9 %12
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
 }
 
 TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp
new file mode 100644
index 0000000..2bbe605
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp
@@ -0,0 +1,151 @@
+// 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_replace_copy_memory_with_load_store.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationReplaceCopyMemoryWithLoadStoreTest, BasicScenarios) {
+  // This is a simple transformation and this test handles the main cases.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "b"
+               OpName %14 "c"
+               OpName %16 "d"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+         %11 = OpConstant %6 3
+         %12 = OpTypeFloat 32
+         %13 = OpTypePointer Function %12
+         %15 = OpConstant %12 2
+         %17 = OpConstant %12 3
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+         %16 = OpVariable %13 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %14 %15
+               OpStore %16 %17
+               OpCopyMemory %8 %10
+               OpCopyMemory %16 %14
+               OpReturn
+               OpFunctionEnd
+    )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto instruction_descriptor_invalid_1 =
+      MakeInstructionDescriptor(5, SpvOpStore, 0);
+  auto instruction_descriptor_valid_1 =
+      MakeInstructionDescriptor(5, SpvOpCopyMemory, 0);
+  auto instruction_descriptor_valid_2 =
+      MakeInstructionDescriptor(5, SpvOpCopyMemory, 0);
+
+  // Invalid: |source_id| is not a fresh id.
+  auto transformation_invalid_1 = TransformationReplaceCopyMemoryWithLoadStore(
+      15, instruction_descriptor_valid_1);
+  ASSERT_FALSE(transformation_invalid_1.IsApplicable(context.get(),
+                                                     transformation_context));
+
+  // Invalid: |instruction_descriptor_invalid| refers to an instruction OpStore.
+  auto transformation_invalid_2 = TransformationReplaceCopyMemoryWithLoadStore(
+      20, instruction_descriptor_invalid_1);
+  ASSERT_FALSE(transformation_invalid_2.IsApplicable(context.get(),
+                                                     transformation_context));
+
+  auto transformation_valid_1 = TransformationReplaceCopyMemoryWithLoadStore(
+      20, instruction_descriptor_valid_1);
+  ASSERT_TRUE(transformation_valid_1.IsApplicable(context.get(),
+                                                  transformation_context));
+  transformation_valid_1.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto transformation_valid_2 = TransformationReplaceCopyMemoryWithLoadStore(
+      21, instruction_descriptor_valid_2);
+  ASSERT_TRUE(transformation_valid_2.IsApplicable(context.get(),
+                                                  transformation_context));
+  transformation_valid_2.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "b"
+               OpName %14 "c"
+               OpName %16 "d"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+         %11 = OpConstant %6 3
+         %12 = OpTypeFloat 32
+         %13 = OpTypePointer Function %12
+         %15 = OpConstant %12 2
+         %17 = OpConstant %12 3
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+         %16 = OpVariable %13 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %14 %15
+               OpStore %16 %17
+         %20 = OpLoad %6 %10
+               OpStore %8 %20
+         %21 = OpLoad %12 %14
+               OpStore %16 %21
+               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_copy_object_with_store_load_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_copy_object_with_store_load_test.cpp
new file mode 100644
index 0000000..d1d38a9
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_copy_object_with_store_load_test.cpp
@@ -0,0 +1,197 @@
+// 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_replace_copy_object_with_store_load.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationReplaceCopyObjectWithStoreLoad, BasicScenarios) {
+  std::string reference_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %23
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "b"
+               OpName %14 "c"
+               OpName %16 "d"
+               OpName %18 "e"
+               OpName %23 "f"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+         %11 = OpConstant %6 3
+         %12 = OpTypeFloat 32
+         %13 = OpTypePointer Function %12
+         %15 = OpConstant %12 2
+         %17 = OpConstant %12 3
+         %22 = OpTypePointer Private %12
+         %23 = OpVariable %22 Private
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+         %16 = OpVariable %13 Function
+         %18 = OpVariable %7 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %14 %15
+               OpStore %16 %17
+         %19 = OpLoad %6 %8
+         %20 = OpLoad %6 %10
+         %21 = OpIAdd %6 %19 %20
+               OpStore %18 %21
+         %24 = OpLoad %12 %14
+         %25 = OpLoad %12 %16
+         %26 = OpFMul %12 %24 %25
+               OpStore %23 %26
+         %27 = OpCopyObject %6 %21
+         %28 = OpCopyObject %12 %26
+         %40 = OpCopyObject %13 %14
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Invalid: fresh_variable_id=10 is not fresh.
+  auto transformation_invalid_1 = TransformationReplaceCopyObjectWithStoreLoad(
+      27, 10, SpvStorageClassFunction, 9);
+  ASSERT_FALSE(transformation_invalid_1.IsApplicable(context.get(),
+                                                     transformation_context));
+
+  // Invalid: copy_object_result_id=26 is not a CopyObject instruction.
+  auto transformation_invalid_2 = TransformationReplaceCopyObjectWithStoreLoad(
+      26, 30, SpvStorageClassFunction, 9);
+  ASSERT_FALSE(transformation_invalid_2.IsApplicable(context.get(),
+                                                     transformation_context));
+
+  // Invalid: copy_object_result_id=40 is of type pointer.
+  auto transformation_invalid_3 = TransformationReplaceCopyObjectWithStoreLoad(
+      40, 30, SpvStorageClassFunction, 9);
+  ASSERT_FALSE(transformation_invalid_3.IsApplicable(context.get(),
+                                                     transformation_context));
+
+  // Invalid: Pointer type instruction in this storage class pointing to the
+  // value type is not defined.
+  auto transformation_invalid_4 = TransformationReplaceCopyObjectWithStoreLoad(
+      40, 30, SpvStorageClassPrivate, 9);
+  ASSERT_FALSE(transformation_invalid_4.IsApplicable(context.get(),
+                                                     transformation_context));
+
+  // Invalid: initializer_id=15 is invalid.
+  auto transformation_invalid_5 = TransformationReplaceCopyObjectWithStoreLoad(
+      27, 30, SpvStorageClassPrivate, 15);
+  ASSERT_FALSE(transformation_invalid_5.IsApplicable(context.get(),
+                                                     transformation_context));
+
+  // Invalid: SpvStorageClassUniform is not applicable to the transformation.
+  auto transformation_invalid_6 = TransformationReplaceCopyObjectWithStoreLoad(
+      27, 30, SpvStorageClassUniform, 9);
+  ASSERT_FALSE(transformation_invalid_6.IsApplicable(context.get(),
+                                                     transformation_context));
+
+  auto transformation_valid_1 = TransformationReplaceCopyObjectWithStoreLoad(
+      27, 30, SpvStorageClassFunction, 9);
+  ASSERT_TRUE(transformation_valid_1.IsApplicable(context.get(),
+                                                  transformation_context));
+  transformation_valid_1.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto transformation_valid_2 = TransformationReplaceCopyObjectWithStoreLoad(
+      28, 32, SpvStorageClassPrivate, 15);
+  ASSERT_TRUE(transformation_valid_2.IsApplicable(context.get(),
+                                                  transformation_context));
+  transformation_valid_2.Apply(context.get(), &transformation_context);
+  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" %23 %32
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "b"
+               OpName %14 "c"
+               OpName %16 "d"
+               OpName %18 "e"
+               OpName %23 "f"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+         %11 = OpConstant %6 3
+         %12 = OpTypeFloat 32
+         %13 = OpTypePointer Function %12
+         %15 = OpConstant %12 2
+         %17 = OpConstant %12 3
+         %22 = OpTypePointer Private %12
+         %23 = OpVariable %22 Private
+         %32 = OpVariable %22 Private %15
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %30 = OpVariable %7 Function %9
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+         %16 = OpVariable %13 Function
+         %18 = OpVariable %7 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %14 %15
+               OpStore %16 %17
+         %19 = OpLoad %6 %8
+         %20 = OpLoad %6 %10
+         %21 = OpIAdd %6 %19 %20
+               OpStore %18 %21
+         %24 = OpLoad %12 %14
+         %25 = OpLoad %12 %16
+         %26 = OpFMul %12 %24 %25
+               OpStore %23 %26
+               OpStore %30 %21
+         %27 = OpLoad %6 %30
+               OpStore %32 %26
+         %28 = OpLoad %12 %32
+         %40 = OpCopyObject %13 %14
+               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_id_with_synonym_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp
index 37e9510..e4a3f00 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp
@@ -1450,6 +1450,271 @@
           .IsApplicable(context.get(), transformation_context));
 }
 
+TEST(TransformationReplaceIdWithSynonymTest, EquivalentIntegerConstants) {
+  // This checks that replacing an integer constant with an equivalent one with
+  // different signedness is allowed only when valid.
+  const std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %2 "main"
+               OpName %3 "a"
+               OpDecorate %3 RelaxedPrecision
+          %4 = OpTypeVoid
+          %5 = OpTypeBool
+          %6 = OpConstantTrue %5
+          %7 = OpTypeFunction %4
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %10 = OpConstant %8 1
+         %11 = OpTypeInt 32 0
+         %12 = OpTypePointer Function %11
+         %13 = OpConstant %11 1
+          %2 = OpFunction %4 None %7
+         %14 = OpLabel
+          %3 = OpVariable %9 Function
+         %15 = OpSNegate %8 %10
+         %16 = OpIAdd %8 %10 %10
+         %17 = OpSDiv %8 %10 %10
+         %18 = OpUDiv %11 %13 %13
+         %19 = OpBitwiseAnd %8 %10 %10
+         %20 = OpSelect %8 %6 %10 %17
+         %21 = OpIEqual %5 %10 %10
+               OpStore %3 %10
+               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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Add synonym fact relating %10 and %13 (equivalent integer constant with
+  // different signedness).
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(10, 13),
+                                                   context.get());
+
+  // Legal because OpSNegate always considers the integer as signed
+  auto replacement1 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(10, MakeInstructionDescriptor(15, SpvOpSNegate, 0),
+                          0),
+      13);
+  ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context));
+  replacement1.Apply(context.get(), &transformation_context);
+
+  // Legal because OpIAdd does not care about the signedness of the operands
+  auto replacement2 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(10, MakeInstructionDescriptor(16, SpvOpIAdd, 0), 0),
+      13);
+  ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context));
+  replacement2.Apply(context.get(), &transformation_context);
+
+  // Legal because OpSDiv does not care about the signedness of the operands
+  auto replacement3 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(10, MakeInstructionDescriptor(17, SpvOpSDiv, 0), 0),
+      13);
+  ASSERT_TRUE(replacement3.IsApplicable(context.get(), transformation_context));
+  replacement3.Apply(context.get(), &transformation_context);
+
+  // Not legal because OpUDiv requires unsigned integers
+  ASSERT_FALSE(TransformationReplaceIdWithSynonym(
+                   MakeIdUseDescriptor(
+                       13, MakeInstructionDescriptor(18, SpvOpUDiv, 0), 0),
+                   10)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Legal because OpSDiv does not care about the signedness of the operands
+  auto replacement4 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(10, MakeInstructionDescriptor(19, SpvOpBitwiseAnd, 0),
+                          0),
+      13);
+  ASSERT_TRUE(replacement4.IsApplicable(context.get(), transformation_context));
+  replacement4.Apply(context.get(), &transformation_context);
+
+  // Not legal because OpSelect requires both operands to have the same type as
+  // the result type
+  ASSERT_FALSE(TransformationReplaceIdWithSynonym(
+                   MakeIdUseDescriptor(
+                       10, MakeInstructionDescriptor(20, SpvOpUDiv, 0), 1),
+                   13)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Not legal because OpStore requires the object to match the type pointed
+  // to by the pointer.
+  ASSERT_FALSE(TransformationReplaceIdWithSynonym(
+                   MakeIdUseDescriptor(
+                       10, MakeInstructionDescriptor(21, SpvOpStore, 0), 1),
+                   13)
+                   .IsApplicable(context.get(), transformation_context));
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  const std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %2 "main"
+               OpName %3 "a"
+               OpDecorate %3 RelaxedPrecision
+          %4 = OpTypeVoid
+          %5 = OpTypeBool
+          %6 = OpConstantTrue %5
+          %7 = OpTypeFunction %4
+          %8 = OpTypeInt 32 1
+          %9 = OpTypePointer Function %8
+         %10 = OpConstant %8 1
+         %11 = OpTypeInt 32 0
+         %12 = OpTypePointer Function %11
+         %13 = OpConstant %11 1
+          %2 = OpFunction %4 None %7
+         %14 = OpLabel
+          %3 = OpVariable %9 Function
+         %15 = OpSNegate %8 %13
+         %16 = OpIAdd %8 %13 %10
+         %17 = OpSDiv %8 %13 %10
+         %18 = OpUDiv %11 %13 %13
+         %19 = OpBitwiseAnd %8 %13 %10
+         %20 = OpSelect %8 %6 %10 %17
+         %21 = OpIEqual %5 %10 %10
+               OpStore %3 %10
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationReplaceIdWithSynonymTest, EquivalentIntegerVectorConstants) {
+  // This checks that replacing an integer constant with an equivalent one with
+  // different signedness is allowed only when valid.
+  const std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %2 "main"
+               OpName %3 "a"
+               OpDecorate %3 RelaxedPrecision
+               OpDecorate %4 RelaxedPrecision
+          %5 = OpTypeVoid
+          %6 = OpTypeFunction %5
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeInt 32 0
+          %9 = OpTypeVector %7 4
+         %10 = OpTypeVector %8 4
+         %11 = OpTypePointer Function %9
+         %12 = OpConstant %7 1
+         %13 = OpConstant %8 1
+         %14 = OpConstantComposite %9 %12 %12 %12 %12
+         %15 = OpConstantComposite %10 %13 %13 %13 %13
+         %16 = OpTypePointer Function %7
+          %2 = OpFunction %5 None %6
+         %17 = OpLabel
+          %3 = OpVariable %11 Function
+         %18 = OpIAdd %9 %14 %14
+               OpStore %3 %14
+         %19 = OpAccessChain %16 %3 %13
+          %4 = OpLoad %7 %19
+               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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Add synonym fact relating %10 and %13 (equivalent integer vectors with
+  // different signedness).
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(14, 15),
+                                                   context.get());
+
+  // Legal because OpIAdd does not consider the signedness of the operands
+  auto replacement1 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(14, MakeInstructionDescriptor(18, SpvOpIAdd, 0), 0),
+      15);
+  ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context));
+  replacement1.Apply(context.get(), &transformation_context);
+
+  // Not legal because OpStore requires the object to match the type pointed
+  // to by the pointer.
+  ASSERT_FALSE(TransformationReplaceIdWithSynonym(
+                   MakeIdUseDescriptor(
+                       14, MakeInstructionDescriptor(18, SpvOpStore, 0), 1),
+                   15)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Add synonym fact relating %12 and %13 (equivalent integer constants with
+  // different signedness).
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(12, 13),
+                                                   context.get());
+
+  // Legal because the indices of OpAccessChain are always treated as signed
+  auto replacement2 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          13, MakeInstructionDescriptor(19, SpvOpAccessChain, 0), 1),
+      12);
+  ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context));
+  replacement2.Apply(context.get(), &transformation_context);
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  const std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %2 "main"
+               OpName %3 "a"
+               OpDecorate %3 RelaxedPrecision
+               OpDecorate %4 RelaxedPrecision
+          %5 = OpTypeVoid
+          %6 = OpTypeFunction %5
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeInt 32 0
+          %9 = OpTypeVector %7 4
+         %10 = OpTypeVector %8 4
+         %11 = OpTypePointer Function %9
+         %12 = OpConstant %7 1
+         %13 = OpConstant %8 1
+         %14 = OpConstantComposite %9 %12 %12 %12 %12
+         %15 = OpConstantComposite %10 %13 %13 %13 %13
+         %16 = OpTypePointer Function %7
+          %2 = OpFunction %5 None %6
+         %17 = OpLabel
+          %3 = OpVariable %11 Function
+         %18 = OpIAdd %9 %15 %14
+               OpStore %3 %14
+         %19 = OpAccessChain %16 %3 %12
+          %4 = OpLoad %7 %19
+               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_linear_algebra_instruction_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_linear_algebra_instruction_test.cpp
new file mode 100644
index 0000000..0b04e96
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_linear_algebra_instruction_test.cpp
@@ -0,0 +1,1876 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationReplaceLinearAlgebraInstructionTest, IsApplicable) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %22 "main"
+               OpExecutionMode %22 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %22 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeFloat 32
+          %5 = OpTypeVector %4 2
+          %6 = OpTypeVector %4 3
+          %7 = OpTypeVector %4 4
+          %8 = OpConstant %4 1
+          %9 = OpConstant %4 2
+         %10 = OpConstant %4 3
+         %11 = OpConstant %4 4
+         %12 = OpConstant %4 5
+         %13 = OpConstant %4 6
+         %14 = OpConstant %4 7
+         %15 = OpConstant %4 8
+         %16 = OpConstantComposite %5 %8 %9
+         %17 = OpConstantComposite %5 %10 %11
+         %18 = OpConstantComposite %6 %8 %9 %10
+         %19 = OpConstantComposite %6 %11 %12 %13
+         %20 = OpConstantComposite %7 %8 %9 %10 %11
+         %21 = OpConstantComposite %7 %12 %13 %14 %15
+         %22 = OpFunction %2 None %3
+         %23 = OpLabel
+         %24 = OpDot %4 %16 %17
+         %25 = OpDot %4 %18 %19
+         %26 = OpDot %4 %20 %21
+         %27 = OpVectorTimesScalar %5 %16 %8
+         %28 = OpVectorTimesScalar %6 %18 %9
+         %29 = OpVectorTimesScalar %7 %20 %10
+         %30 = OpCopyObject %4 %24
+         %31 = OpFAdd %4 %8 %9
+         %32 = OpFMul %4 %10 %11
+               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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Tests linear algebra instructions.
+  auto instruction_descriptor = MakeInstructionDescriptor(24, SpvOpDot, 0);
+  auto transformation = TransformationReplaceLinearAlgebraInstruction(
+      {33, 34, 35, 36, 37, 38}, instruction_descriptor);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(27, SpvOpVectorTimesScalar, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {33, 34, 35, 36}, instruction_descriptor);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests non-linear algebra instructions.
+  instruction_descriptor = MakeInstructionDescriptor(30, SpvOpCopyObject, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {33, 34, 35, 36, 37, 38}, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  instruction_descriptor = MakeInstructionDescriptor(31, SpvOpFAdd, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {33, 34, 35, 36, 37}, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  instruction_descriptor = MakeInstructionDescriptor(32, SpvOpFMul, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {33, 34, 35, 36}, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests number of fresh ids is different than necessary.
+  instruction_descriptor = MakeInstructionDescriptor(25, SpvOpDot, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {33, 34, 35, 36}, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(28, SpvOpVectorTimesScalar, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {33, 34, 35, 36, 37, 38, 39}, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests non-fresh ids.
+  instruction_descriptor = MakeInstructionDescriptor(26, SpvOpDot, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {33, 34, 5, 36, 37, 8, 39, 40, 1, 42, 3, 44, 45, 46},
+      instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(29, SpvOpVectorTimesScalar, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {33, 34, 35, 36, 7, 38, 9, 40}, instruction_descriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationReplaceLinearAlgebraInstructionTest,
+     ReplaceOpVectorTimesScalar) {
+  std::string reference_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %15 "main"
+               OpExecutionMode %15 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %15 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeFloat 32
+          %5 = OpTypeVector %4 2
+          %6 = OpTypeVector %4 3
+          %7 = OpTypeVector %4 4
+          %8 = OpConstant %4 1
+          %9 = OpConstant %4 2
+         %10 = OpConstant %4 3
+         %11 = OpConstant %4 4
+         %12 = OpConstantComposite %5 %8 %9
+         %13 = OpConstantComposite %6 %8 %9 %10
+         %14 = OpConstantComposite %7 %8 %9 %10 %11
+         %15 = OpFunction %2 None %3
+         %16 = OpLabel
+         %17 = OpVectorTimesScalar %5 %12 %8
+         %18 = OpVectorTimesScalar %6 %13 %9
+         %19 = OpVectorTimesScalar %7 %14 %10
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(17, SpvOpVectorTimesScalar, 0);
+  auto transformation = TransformationReplaceLinearAlgebraInstruction(
+      {20, 21, 22, 23}, instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(18, SpvOpVectorTimesScalar, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {24, 25, 26, 27, 28, 29}, instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(19, SpvOpVectorTimesScalar, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {30, 31, 32, 33, 34, 35, 36, 37}, instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  std::string variant_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %15 "main"
+               OpExecutionMode %15 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %15 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeFloat 32
+          %5 = OpTypeVector %4 2
+          %6 = OpTypeVector %4 3
+          %7 = OpTypeVector %4 4
+          %8 = OpConstant %4 1
+          %9 = OpConstant %4 2
+         %10 = OpConstant %4 3
+         %11 = OpConstant %4 4
+         %12 = OpConstantComposite %5 %8 %9
+         %13 = OpConstantComposite %6 %8 %9 %10
+         %14 = OpConstantComposite %7 %8 %9 %10 %11
+         %15 = OpFunction %2 None %3
+         %16 = OpLabel
+         %20 = OpCompositeExtract %4 %12 0
+         %21 = OpFMul %4 %20 %8
+         %22 = OpCompositeExtract %4 %12 1
+         %23 = OpFMul %4 %22 %8
+         %17 = OpCompositeConstruct %5 %21 %23
+         %24 = OpCompositeExtract %4 %13 0
+         %25 = OpFMul %4 %24 %9
+         %26 = OpCompositeExtract %4 %13 1
+         %27 = OpFMul %4 %26 %9
+         %28 = OpCompositeExtract %4 %13 2
+         %29 = OpFMul %4 %28 %9
+         %18 = OpCompositeConstruct %6 %25 %27 %29
+         %30 = OpCompositeExtract %4 %14 0
+         %31 = OpFMul %4 %30 %10
+         %32 = OpCompositeExtract %4 %14 1
+         %33 = OpFMul %4 %32 %10
+         %34 = OpCompositeExtract %4 %14 2
+         %35 = OpFMul %4 %34 %10
+         %36 = OpCompositeExtract %4 %14 3
+         %37 = OpFMul %4 %36 %10
+         %19 = OpCompositeConstruct %7 %31 %33 %35 %37
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+TEST(TransformationReplaceLinearAlgebraInstructionTest,
+     ReplaceOpMatrixTimesScalar) {
+  std::string reference_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %54 "main"
+               OpExecutionMode %54 OriginUpperLeft
+
+; Types
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeFloat 32
+          %5 = OpTypeVector %4 2
+          %6 = OpTypeVector %4 3
+          %7 = OpTypeVector %4 4
+          %8 = OpTypeMatrix %5 2
+          %9 = OpTypeMatrix %5 3
+         %10 = OpTypeMatrix %5 4
+         %11 = OpTypeMatrix %6 2
+         %12 = OpTypeMatrix %6 3
+         %13 = OpTypeMatrix %6 4
+         %14 = OpTypeMatrix %7 2
+         %15 = OpTypeMatrix %7 3
+         %16 = OpTypeMatrix %7 4
+
+; Constant scalars
+         %17 = OpConstant %4 1
+         %18 = OpConstant %4 2
+         %19 = OpConstant %4 3
+         %20 = OpConstant %4 4
+         %21 = OpConstant %4 5
+         %22 = OpConstant %4 6
+         %23 = OpConstant %4 7
+         %24 = OpConstant %4 8
+         %25 = OpConstant %4 9
+         %26 = OpConstant %4 10
+         %27 = OpConstant %4 11
+         %28 = OpConstant %4 12
+         %29 = OpConstant %4 13
+         %30 = OpConstant %4 14
+         %31 = OpConstant %4 15
+         %32 = OpConstant %4 16
+
+; Constant vectors
+         %33 = OpConstantComposite %5 %17 %18
+         %34 = OpConstantComposite %5 %19 %20
+         %35 = OpConstantComposite %5 %21 %22
+         %36 = OpConstantComposite %5 %23 %24
+         %37 = OpConstantComposite %6 %17 %18 %19
+         %38 = OpConstantComposite %6 %20 %21 %22
+         %39 = OpConstantComposite %6 %23 %24 %25
+         %40 = OpConstantComposite %6 %26 %27 %28
+         %41 = OpConstantComposite %7 %17 %18 %19 %20
+         %42 = OpConstantComposite %7 %21 %22 %23 %24
+         %43 = OpConstantComposite %7 %25 %26 %27 %28
+         %44 = OpConstantComposite %7 %29 %30 %31 %32
+
+; Constant matrices
+         %45 = OpConstantComposite %8 %33 %34
+         %46 = OpConstantComposite %9 %33 %34 %35
+         %47 = OpConstantComposite %10 %33 %34 %35 %36
+         %48 = OpConstantComposite %11 %37 %38
+         %49 = OpConstantComposite %12 %37 %38 %39
+         %50 = OpConstantComposite %13 %37 %38 %39 %40
+         %51 = OpConstantComposite %14 %41 %42
+         %52 = OpConstantComposite %15 %41 %42 %43
+         %53 = OpConstantComposite %16 %41 %42 %43 %44
+
+; main function
+         %54 = OpFunction %2 None %3
+         %55 = OpLabel
+
+; Multiplying 2-row matrices by scalar
+         %56 = OpMatrixTimesScalar %8 %45 %17
+         %57 = OpMatrixTimesScalar %9 %46 %18
+         %58 = OpMatrixTimesScalar %10 %47 %19
+
+; Multiplying 3-row matrices by scalar
+         %59 = OpMatrixTimesScalar %11 %48 %21
+         %60 = OpMatrixTimesScalar %12 %49 %22
+         %61 = OpMatrixTimesScalar %13 %50 %23
+
+; Multiplying 4-row matrices by scalar
+         %62 = OpMatrixTimesScalar %14 %51 %24
+         %63 = OpMatrixTimesScalar %15 %52 %25
+         %64 = OpMatrixTimesScalar %16 %53 %26
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(56, SpvOpMatrixTimesScalar, 0);
+  auto transformation = TransformationReplaceLinearAlgebraInstruction(
+      {65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76}, instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(57, SpvOpMatrixTimesScalar, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94},
+      instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(58, SpvOpMatrixTimesScalar, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {95,  96,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106,
+       107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118},
+      instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  std::string variant_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %54 "main"
+               OpExecutionMode %54 OriginUpperLeft
+
+; Types
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeFloat 32
+          %5 = OpTypeVector %4 2
+          %6 = OpTypeVector %4 3
+          %7 = OpTypeVector %4 4
+          %8 = OpTypeMatrix %5 2
+          %9 = OpTypeMatrix %5 3
+         %10 = OpTypeMatrix %5 4
+         %11 = OpTypeMatrix %6 2
+         %12 = OpTypeMatrix %6 3
+         %13 = OpTypeMatrix %6 4
+         %14 = OpTypeMatrix %7 2
+         %15 = OpTypeMatrix %7 3
+         %16 = OpTypeMatrix %7 4
+
+; Constant scalars
+         %17 = OpConstant %4 1
+         %18 = OpConstant %4 2
+         %19 = OpConstant %4 3
+         %20 = OpConstant %4 4
+         %21 = OpConstant %4 5
+         %22 = OpConstant %4 6
+         %23 = OpConstant %4 7
+         %24 = OpConstant %4 8
+         %25 = OpConstant %4 9
+         %26 = OpConstant %4 10
+         %27 = OpConstant %4 11
+         %28 = OpConstant %4 12
+         %29 = OpConstant %4 13
+         %30 = OpConstant %4 14
+         %31 = OpConstant %4 15
+         %32 = OpConstant %4 16
+
+; Constant vectors
+         %33 = OpConstantComposite %5 %17 %18
+         %34 = OpConstantComposite %5 %19 %20
+         %35 = OpConstantComposite %5 %21 %22
+         %36 = OpConstantComposite %5 %23 %24
+         %37 = OpConstantComposite %6 %17 %18 %19
+         %38 = OpConstantComposite %6 %20 %21 %22
+         %39 = OpConstantComposite %6 %23 %24 %25
+         %40 = OpConstantComposite %6 %26 %27 %28
+         %41 = OpConstantComposite %7 %17 %18 %19 %20
+         %42 = OpConstantComposite %7 %21 %22 %23 %24
+         %43 = OpConstantComposite %7 %25 %26 %27 %28
+         %44 = OpConstantComposite %7 %29 %30 %31 %32
+
+; Constant matrices
+         %45 = OpConstantComposite %8 %33 %34
+         %46 = OpConstantComposite %9 %33 %34 %35
+         %47 = OpConstantComposite %10 %33 %34 %35 %36
+         %48 = OpConstantComposite %11 %37 %38
+         %49 = OpConstantComposite %12 %37 %38 %39
+         %50 = OpConstantComposite %13 %37 %38 %39 %40
+         %51 = OpConstantComposite %14 %41 %42
+         %52 = OpConstantComposite %15 %41 %42 %43
+         %53 = OpConstantComposite %16 %41 %42 %43 %44
+
+; main function
+         %54 = OpFunction %2 None %3
+         %55 = OpLabel
+
+; Multiplying 2x2 matrix by scalar
+         %65 = OpCompositeExtract %5 %45 0
+         %66 = OpCompositeExtract %4 %65 0
+         %67 = OpFMul %4 %66 %17
+         %68 = OpCompositeExtract %4 %65 1
+         %69 = OpFMul %4 %68 %17
+         %70 = OpCompositeConstruct %5 %67 %69
+         %71 = OpCompositeExtract %5 %45 1
+         %72 = OpCompositeExtract %4 %71 0
+         %73 = OpFMul %4 %72 %17
+         %74 = OpCompositeExtract %4 %71 1
+         %75 = OpFMul %4 %74 %17
+         %76 = OpCompositeConstruct %5 %73 %75
+         %56 = OpCompositeConstruct %8 %70 %76
+
+; Multiplying 2x3 matrix by scalar
+         %77 = OpCompositeExtract %5 %46 0
+         %78 = OpCompositeExtract %4 %77 0
+         %79 = OpFMul %4 %78 %18
+         %80 = OpCompositeExtract %4 %77 1
+         %81 = OpFMul %4 %80 %18
+         %82 = OpCompositeConstruct %5 %79 %81
+         %83 = OpCompositeExtract %5 %46 1
+         %84 = OpCompositeExtract %4 %83 0
+         %85 = OpFMul %4 %84 %18
+         %86 = OpCompositeExtract %4 %83 1
+         %87 = OpFMul %4 %86 %18
+         %88 = OpCompositeConstruct %5 %85 %87
+         %89 = OpCompositeExtract %5 %46 2
+         %90 = OpCompositeExtract %4 %89 0
+         %91 = OpFMul %4 %90 %18
+         %92 = OpCompositeExtract %4 %89 1
+         %93 = OpFMul %4 %92 %18
+         %94 = OpCompositeConstruct %5 %91 %93
+         %57 = OpCompositeConstruct %9 %82 %88 %94
+
+; Multiplying 2x4 matrix by scalar
+         %95 = OpCompositeExtract %5 %47 0
+         %96 = OpCompositeExtract %4 %95 0
+         %97 = OpFMul %4 %96 %19
+         %98 = OpCompositeExtract %4 %95 1
+         %99 = OpFMul %4 %98 %19
+        %100 = OpCompositeConstruct %5 %97 %99
+        %101 = OpCompositeExtract %5 %47 1
+        %102 = OpCompositeExtract %4 %101 0
+        %103 = OpFMul %4 %102 %19
+        %104 = OpCompositeExtract %4 %101 1
+        %105 = OpFMul %4 %104 %19
+        %106 = OpCompositeConstruct %5 %103 %105
+        %107 = OpCompositeExtract %5 %47 2
+        %108 = OpCompositeExtract %4 %107 0
+        %109 = OpFMul %4 %108 %19
+        %110 = OpCompositeExtract %4 %107 1
+        %111 = OpFMul %4 %110 %19
+        %112 = OpCompositeConstruct %5 %109 %111
+        %113 = OpCompositeExtract %5 %47 3
+        %114 = OpCompositeExtract %4 %113 0
+        %115 = OpFMul %4 %114 %19
+        %116 = OpCompositeExtract %4 %113 1
+        %117 = OpFMul %4 %116 %19
+        %118 = OpCompositeConstruct %5 %115 %117
+         %58 = OpCompositeConstruct %10 %100 %106 %112 %118
+
+; Multiplying 3-row matrices by scalar
+         %59 = OpMatrixTimesScalar %11 %48 %21
+         %60 = OpMatrixTimesScalar %12 %49 %22
+         %61 = OpMatrixTimesScalar %13 %50 %23
+
+; Multiplying 4-row matrices by scalar
+         %62 = OpMatrixTimesScalar %14 %51 %24
+         %63 = OpMatrixTimesScalar %15 %52 %25
+         %64 = OpMatrixTimesScalar %16 %53 %26
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+TEST(TransformationReplaceLinearAlgebraInstructionTest,
+     ReplaceOpVectorTimesMatrix) {
+  std::string reference_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %54 "main"
+               OpExecutionMode %54 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %54 "main"
+
+; Types
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeFloat 32
+          %5 = OpTypeVector %4 2
+          %6 = OpTypeVector %4 3
+          %7 = OpTypeVector %4 4
+          %8 = OpTypeMatrix %5 2
+          %9 = OpTypeMatrix %5 3
+         %10 = OpTypeMatrix %5 4
+         %11 = OpTypeMatrix %6 2
+         %12 = OpTypeMatrix %6 3
+         %13 = OpTypeMatrix %6 4
+         %14 = OpTypeMatrix %7 2
+         %15 = OpTypeMatrix %7 3
+         %16 = OpTypeMatrix %7 4
+
+; Constant scalars
+         %17 = OpConstant %4 1
+         %18 = OpConstant %4 2
+         %19 = OpConstant %4 3
+         %20 = OpConstant %4 4
+         %21 = OpConstant %4 5
+         %22 = OpConstant %4 6
+         %23 = OpConstant %4 7
+         %24 = OpConstant %4 8
+         %25 = OpConstant %4 9
+         %26 = OpConstant %4 10
+         %27 = OpConstant %4 11
+         %28 = OpConstant %4 12
+         %29 = OpConstant %4 13
+         %30 = OpConstant %4 14
+         %31 = OpConstant %4 15
+         %32 = OpConstant %4 16
+
+; Constant vectors
+         %33 = OpConstantComposite %5 %17 %18
+         %34 = OpConstantComposite %5 %19 %20
+         %35 = OpConstantComposite %5 %21 %22
+         %36 = OpConstantComposite %5 %23 %24
+         %37 = OpConstantComposite %6 %17 %18 %19
+         %38 = OpConstantComposite %6 %20 %21 %22
+         %39 = OpConstantComposite %6 %23 %24 %25
+         %40 = OpConstantComposite %6 %26 %27 %28
+         %41 = OpConstantComposite %7 %17 %18 %19 %20
+         %42 = OpConstantComposite %7 %21 %22 %23 %24
+         %43 = OpConstantComposite %7 %25 %26 %27 %28
+         %44 = OpConstantComposite %7 %29 %30 %31 %32
+
+; Constant matrices
+         %45 = OpConstantComposite %8 %33 %34
+         %46 = OpConstantComposite %9 %33 %34 %35
+         %47 = OpConstantComposite %10 %33 %34 %35 %36
+         %48 = OpConstantComposite %11 %37 %38
+         %49 = OpConstantComposite %12 %37 %38 %39
+         %50 = OpConstantComposite %13 %37 %38 %39 %40
+         %51 = OpConstantComposite %14 %41 %42
+         %52 = OpConstantComposite %15 %41 %42 %43
+         %53 = OpConstantComposite %16 %41 %42 %43 %44
+
+; main function
+         %54 = OpFunction %2 None %3
+         %55 = OpLabel
+
+; Multiplying 2-dimensional vector by 2x2 matrix
+         %56 = OpVectorTimesMatrix %5 %33 %45
+
+; Multiplying 2-dimensional vector by 2x3 matrix
+         %57 = OpVectorTimesMatrix %6 %34 %46
+
+; Multiplying 2-dimensional vector by 2x4 matrix
+         %58 = OpVectorTimesMatrix %7 %35 %47
+
+; Multiplying 3-dimensional vector by 3x2 matrix
+         %59 = OpVectorTimesMatrix %5 %37 %48
+
+; Multiplying 3-dimensional vector by 3x3 matrix
+         %60 = OpVectorTimesMatrix %6 %38 %49
+
+; Multiplying 3-dimensional vector by 3x4 matrix
+         %61 = OpVectorTimesMatrix %7 %39 %50
+
+; Multiplying 4-dimensional vector by 4x2 matrix
+         %62 = OpVectorTimesMatrix %5 %41 %51
+
+; Multiplying 4-dimensional vector by 4x3 matrix
+         %63 = OpVectorTimesMatrix %6 %42 %52
+
+; Multiplying 4-dimensional vector by 4x4 matrix
+         %64 = OpVectorTimesMatrix %7 %43 %53
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(56, SpvOpVectorTimesMatrix, 0);
+  auto transformation = TransformationReplaceLinearAlgebraInstruction(
+      {65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78},
+      instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(57, SpvOpVectorTimesMatrix, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
+       89, 90, 91, 92, 93, 94, 95, 96, 97, 98},
+      instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(58, SpvOpVectorTimesMatrix, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {99,  100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+       112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124},
+      instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(59, SpvOpVectorTimesMatrix, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
+       136, 137, 138, 139, 140, 141, 142, 143, 144, 145},
+      instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  std::string variant_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %54 "main"
+               OpExecutionMode %54 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %54 "main"
+
+; Types
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeFloat 32
+          %5 = OpTypeVector %4 2
+          %6 = OpTypeVector %4 3
+          %7 = OpTypeVector %4 4
+          %8 = OpTypeMatrix %5 2
+          %9 = OpTypeMatrix %5 3
+         %10 = OpTypeMatrix %5 4
+         %11 = OpTypeMatrix %6 2
+         %12 = OpTypeMatrix %6 3
+         %13 = OpTypeMatrix %6 4
+         %14 = OpTypeMatrix %7 2
+         %15 = OpTypeMatrix %7 3
+         %16 = OpTypeMatrix %7 4
+
+; Constant scalars
+         %17 = OpConstant %4 1
+         %18 = OpConstant %4 2
+         %19 = OpConstant %4 3
+         %20 = OpConstant %4 4
+         %21 = OpConstant %4 5
+         %22 = OpConstant %4 6
+         %23 = OpConstant %4 7
+         %24 = OpConstant %4 8
+         %25 = OpConstant %4 9
+         %26 = OpConstant %4 10
+         %27 = OpConstant %4 11
+         %28 = OpConstant %4 12
+         %29 = OpConstant %4 13
+         %30 = OpConstant %4 14
+         %31 = OpConstant %4 15
+         %32 = OpConstant %4 16
+
+; Constant vectors
+         %33 = OpConstantComposite %5 %17 %18
+         %34 = OpConstantComposite %5 %19 %20
+         %35 = OpConstantComposite %5 %21 %22
+         %36 = OpConstantComposite %5 %23 %24
+         %37 = OpConstantComposite %6 %17 %18 %19
+         %38 = OpConstantComposite %6 %20 %21 %22
+         %39 = OpConstantComposite %6 %23 %24 %25
+         %40 = OpConstantComposite %6 %26 %27 %28
+         %41 = OpConstantComposite %7 %17 %18 %19 %20
+         %42 = OpConstantComposite %7 %21 %22 %23 %24
+         %43 = OpConstantComposite %7 %25 %26 %27 %28
+         %44 = OpConstantComposite %7 %29 %30 %31 %32
+
+; Constant matrices
+         %45 = OpConstantComposite %8 %33 %34
+         %46 = OpConstantComposite %9 %33 %34 %35
+         %47 = OpConstantComposite %10 %33 %34 %35 %36
+         %48 = OpConstantComposite %11 %37 %38
+         %49 = OpConstantComposite %12 %37 %38 %39
+         %50 = OpConstantComposite %13 %37 %38 %39 %40
+         %51 = OpConstantComposite %14 %41 %42
+         %52 = OpConstantComposite %15 %41 %42 %43
+         %53 = OpConstantComposite %16 %41 %42 %43 %44
+
+; main function
+         %54 = OpFunction %2 None %3
+         %55 = OpLabel
+
+; Multiplying 2-dimensional vector by 2x2 matrix
+         %65 = OpCompositeExtract %4 %33 0
+         %66 = OpCompositeExtract %4 %33 1
+         %67 = OpCompositeExtract %5 %45 0
+         %68 = OpCompositeExtract %4 %67 0
+         %69 = OpFMul %4 %65 %68
+         %70 = OpCompositeExtract %4 %67 1
+         %71 = OpFMul %4 %66 %70
+         %72 = OpFAdd %4 %69 %71
+         %73 = OpCompositeExtract %5 %45 1
+         %74 = OpCompositeExtract %4 %73 0
+         %75 = OpFMul %4 %65 %74
+         %76 = OpCompositeExtract %4 %73 1
+         %77 = OpFMul %4 %66 %76
+         %78 = OpFAdd %4 %75 %77
+         %56 = OpCompositeConstruct %5 %72 %78
+
+; Multiplying 2-dimensional vector by 2x3 matrix
+         %79 = OpCompositeExtract %4 %34 0
+         %80 = OpCompositeExtract %4 %34 1
+         %81 = OpCompositeExtract %5 %46 0
+         %82 = OpCompositeExtract %4 %81 0
+         %83 = OpFMul %4 %79 %82
+         %84 = OpCompositeExtract %4 %81 1
+         %85 = OpFMul %4 %80 %84
+         %86 = OpFAdd %4 %83 %85
+         %87 = OpCompositeExtract %5 %46 1
+         %88 = OpCompositeExtract %4 %87 0
+         %89 = OpFMul %4 %79 %88
+         %90 = OpCompositeExtract %4 %87 1
+         %91 = OpFMul %4 %80 %90
+         %92 = OpFAdd %4 %89 %91
+         %93 = OpCompositeExtract %5 %46 2
+         %94 = OpCompositeExtract %4 %93 0
+         %95 = OpFMul %4 %79 %94
+         %96 = OpCompositeExtract %4 %93 1
+         %97 = OpFMul %4 %80 %96
+         %98 = OpFAdd %4 %95 %97
+         %57 = OpCompositeConstruct %6 %86 %92 %98
+
+; Multiplying 2-dimensional vector by 2x4 matrix
+         %99 = OpCompositeExtract %4 %35 0
+        %100 = OpCompositeExtract %4 %35 1
+        %101 = OpCompositeExtract %5 %47 0
+        %102 = OpCompositeExtract %4 %101 0
+        %103 = OpFMul %4 %99 %102
+        %104 = OpCompositeExtract %4 %101 1
+        %105 = OpFMul %4 %100 %104
+        %106 = OpFAdd %4 %103 %105
+        %107 = OpCompositeExtract %5 %47 1
+        %108 = OpCompositeExtract %4 %107 0
+        %109 = OpFMul %4 %99 %108
+        %110 = OpCompositeExtract %4 %107 1
+        %111 = OpFMul %4 %100 %110
+        %112 = OpFAdd %4 %109 %111
+        %113 = OpCompositeExtract %5 %47 2
+        %114 = OpCompositeExtract %4 %113 0
+        %115 = OpFMul %4 %99 %114
+        %116 = OpCompositeExtract %4 %113 1
+        %117 = OpFMul %4 %100 %116
+        %118 = OpFAdd %4 %115 %117
+        %119 = OpCompositeExtract %5 %47 3
+        %120 = OpCompositeExtract %4 %119 0
+        %121 = OpFMul %4 %99 %120
+        %122 = OpCompositeExtract %4 %119 1
+        %123 = OpFMul %4 %100 %122
+        %124 = OpFAdd %4 %121 %123
+         %58 = OpCompositeConstruct %7 %106 %112 %118 %124
+
+; Multiplying 3-dimensional vector by 3x2 matrix
+        %125 = OpCompositeExtract %4 %37 0
+        %126 = OpCompositeExtract %4 %37 1
+        %127 = OpCompositeExtract %4 %37 2
+        %128 = OpCompositeExtract %6 %48 0
+        %129 = OpCompositeExtract %4 %128 0
+        %130 = OpFMul %4 %125 %129
+        %131 = OpCompositeExtract %4 %128 1
+        %132 = OpFMul %4 %126 %131
+        %133 = OpCompositeExtract %4 %128 2
+        %134 = OpFMul %4 %127 %133
+        %135 = OpFAdd %4 %130 %132
+        %136 = OpFAdd %4 %134 %135
+        %137 = OpCompositeExtract %6 %48 1
+        %138 = OpCompositeExtract %4 %137 0
+        %139 = OpFMul %4 %125 %138
+        %140 = OpCompositeExtract %4 %137 1
+        %141 = OpFMul %4 %126 %140
+        %142 = OpCompositeExtract %4 %137 2
+        %143 = OpFMul %4 %127 %142
+        %144 = OpFAdd %4 %139 %141
+        %145 = OpFAdd %4 %143 %144
+         %59 = OpCompositeConstruct %5 %136 %145
+
+; Multiplying 3-dimensional vector by 3x3 matrix
+         %60 = OpVectorTimesMatrix %6 %38 %49
+
+; Multiplying 3-dimensional vector by 3x4 matrix
+         %61 = OpVectorTimesMatrix %7 %39 %50
+
+; Multiplying 4-dimensional vector by 4x2 matrix
+         %62 = OpVectorTimesMatrix %5 %41 %51
+
+; Multiplying 4-dimensional vector by 4x3 matrix
+         %63 = OpVectorTimesMatrix %6 %42 %52
+
+; Multiplying 4-dimensional vector by 4x4 matrix
+         %64 = OpVectorTimesMatrix %7 %43 %53
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+TEST(TransformationReplaceLinearAlgebraInstructionTest,
+     ReplaceOpMatrixTimesVector) {
+  std::string reference_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %54 "main"
+               OpExecutionMode %54 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %54 "main"
+
+; Types
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeFloat 32
+          %5 = OpTypeVector %4 2
+          %6 = OpTypeVector %4 3
+          %7 = OpTypeVector %4 4
+          %8 = OpTypeMatrix %5 2
+          %9 = OpTypeMatrix %5 3
+         %10 = OpTypeMatrix %5 4
+         %11 = OpTypeMatrix %6 2
+         %12 = OpTypeMatrix %6 3
+         %13 = OpTypeMatrix %6 4
+         %14 = OpTypeMatrix %7 2
+         %15 = OpTypeMatrix %7 3
+         %16 = OpTypeMatrix %7 4
+
+; Constant scalars
+         %17 = OpConstant %4 1
+         %18 = OpConstant %4 2
+         %19 = OpConstant %4 3
+         %20 = OpConstant %4 4
+         %21 = OpConstant %4 5
+         %22 = OpConstant %4 6
+         %23 = OpConstant %4 7
+         %24 = OpConstant %4 8
+         %25 = OpConstant %4 9
+         %26 = OpConstant %4 10
+         %27 = OpConstant %4 11
+         %28 = OpConstant %4 12
+         %29 = OpConstant %4 13
+         %30 = OpConstant %4 14
+         %31 = OpConstant %4 15
+         %32 = OpConstant %4 16
+
+; Constant vectors
+         %33 = OpConstantComposite %5 %17 %18
+         %34 = OpConstantComposite %5 %19 %20
+         %35 = OpConstantComposite %5 %21 %22
+         %36 = OpConstantComposite %5 %23 %24
+         %37 = OpConstantComposite %6 %17 %18 %19
+         %38 = OpConstantComposite %6 %20 %21 %22
+         %39 = OpConstantComposite %6 %23 %24 %25
+         %40 = OpConstantComposite %6 %26 %27 %28
+         %41 = OpConstantComposite %7 %17 %18 %19 %20
+         %42 = OpConstantComposite %7 %21 %22 %23 %24
+         %43 = OpConstantComposite %7 %25 %26 %27 %28
+         %44 = OpConstantComposite %7 %29 %30 %31 %32
+
+; Constant matrices
+         %45 = OpConstantComposite %8 %33 %34
+         %46 = OpConstantComposite %9 %33 %34 %35
+         %47 = OpConstantComposite %10 %33 %34 %35 %36
+         %48 = OpConstantComposite %11 %37 %38
+         %49 = OpConstantComposite %12 %37 %38 %39
+         %50 = OpConstantComposite %13 %37 %38 %39 %40
+         %51 = OpConstantComposite %14 %41 %42
+         %52 = OpConstantComposite %15 %41 %42 %43
+         %53 = OpConstantComposite %16 %41 %42 %43 %44
+
+; main function
+         %54 = OpFunction %2 None %3
+         %55 = OpLabel
+
+; Multiplying 2x2 matrix by 2-dimensional vector
+         %56 = OpMatrixTimesVector %5 %45 %33
+
+; Multiplying 3x2 matrix by 2-dimensional vector
+         %57 = OpMatrixTimesVector %6 %48 %34
+
+; Multiplying 4x2 matrix by 2-dimensional vector
+         %58 = OpMatrixTimesVector %7 %51 %35
+
+; Multiplying 2x3 matrix by 3-dimensional vector
+         %59 = OpMatrixTimesVector %5 %46 %37
+
+; Multiplying 3x3 matrix by 3-dimensional vector
+         %60 = OpMatrixTimesVector %6 %49 %38
+
+; Multiplying 4x3 matrix by 3-dimensional vector
+         %61 = OpMatrixTimesVector %7 %52 %39
+
+; Multiplying 2x4 matrix by 4-dimensional vector
+         %62 = OpMatrixTimesVector %5 %47 %41
+
+; Multiplying 3x4 matrix by 4-dimensional vector
+         %63 = OpMatrixTimesVector %6 %50 %42
+
+; Multiplying 4x4 matrix by 4-dimensional vector
+         %64 = OpMatrixTimesVector %7 %53 %43
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(56, SpvOpMatrixTimesVector, 0);
+  auto transformation = TransformationReplaceLinearAlgebraInstruction(
+      {65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78},
+      instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(57, SpvOpMatrixTimesVector, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
+       97},
+      instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(58, SpvOpMatrixTimesVector, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {98,  99,  100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+       110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121},
+      instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(59, SpvOpMatrixTimesVector, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+       133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143},
+      instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  std::string variant_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %54 "main"
+               OpExecutionMode %54 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %54 "main"
+
+; Types
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeFloat 32
+          %5 = OpTypeVector %4 2
+          %6 = OpTypeVector %4 3
+          %7 = OpTypeVector %4 4
+          %8 = OpTypeMatrix %5 2
+          %9 = OpTypeMatrix %5 3
+         %10 = OpTypeMatrix %5 4
+         %11 = OpTypeMatrix %6 2
+         %12 = OpTypeMatrix %6 3
+         %13 = OpTypeMatrix %6 4
+         %14 = OpTypeMatrix %7 2
+         %15 = OpTypeMatrix %7 3
+         %16 = OpTypeMatrix %7 4
+
+; Constant scalars
+         %17 = OpConstant %4 1
+         %18 = OpConstant %4 2
+         %19 = OpConstant %4 3
+         %20 = OpConstant %4 4
+         %21 = OpConstant %4 5
+         %22 = OpConstant %4 6
+         %23 = OpConstant %4 7
+         %24 = OpConstant %4 8
+         %25 = OpConstant %4 9
+         %26 = OpConstant %4 10
+         %27 = OpConstant %4 11
+         %28 = OpConstant %4 12
+         %29 = OpConstant %4 13
+         %30 = OpConstant %4 14
+         %31 = OpConstant %4 15
+         %32 = OpConstant %4 16
+
+; Constant vectors
+         %33 = OpConstantComposite %5 %17 %18
+         %34 = OpConstantComposite %5 %19 %20
+         %35 = OpConstantComposite %5 %21 %22
+         %36 = OpConstantComposite %5 %23 %24
+         %37 = OpConstantComposite %6 %17 %18 %19
+         %38 = OpConstantComposite %6 %20 %21 %22
+         %39 = OpConstantComposite %6 %23 %24 %25
+         %40 = OpConstantComposite %6 %26 %27 %28
+         %41 = OpConstantComposite %7 %17 %18 %19 %20
+         %42 = OpConstantComposite %7 %21 %22 %23 %24
+         %43 = OpConstantComposite %7 %25 %26 %27 %28
+         %44 = OpConstantComposite %7 %29 %30 %31 %32
+
+; Constant matrices
+         %45 = OpConstantComposite %8 %33 %34
+         %46 = OpConstantComposite %9 %33 %34 %35
+         %47 = OpConstantComposite %10 %33 %34 %35 %36
+         %48 = OpConstantComposite %11 %37 %38
+         %49 = OpConstantComposite %12 %37 %38 %39
+         %50 = OpConstantComposite %13 %37 %38 %39 %40
+         %51 = OpConstantComposite %14 %41 %42
+         %52 = OpConstantComposite %15 %41 %42 %43
+         %53 = OpConstantComposite %16 %41 %42 %43 %44
+
+; main function
+         %54 = OpFunction %2 None %3
+         %55 = OpLabel
+
+; Multiplying 2x2 matrix by 2-dimensional vector
+         %65 = OpCompositeExtract %5 %45 0
+         %66 = OpCompositeExtract %5 %45 1
+         %67 = OpCompositeExtract %4 %33 0
+         %68 = OpCompositeExtract %4 %33 1
+         %69 = OpCompositeExtract %4 %65 0
+         %70 = OpFMul %4 %69 %67
+         %71 = OpCompositeExtract %4 %66 0
+         %72 = OpFMul %4 %71 %68
+         %73 = OpFAdd %4 %70 %72
+         %74 = OpCompositeExtract %4 %65 1
+         %75 = OpFMul %4 %74 %67
+         %76 = OpCompositeExtract %4 %66 1
+         %77 = OpFMul %4 %76 %68
+         %78 = OpFAdd %4 %75 %77
+         %56 = OpCompositeConstruct %5 %73 %78
+
+; Multiplying 3x2 matrix by 2-dimensional vector
+         %79 = OpCompositeExtract %6 %48 0
+         %80 = OpCompositeExtract %6 %48 1
+         %81 = OpCompositeExtract %4 %34 0
+         %82 = OpCompositeExtract %4 %34 1
+         %83 = OpCompositeExtract %4 %79 0
+         %84 = OpFMul %4 %83 %81
+         %85 = OpCompositeExtract %4 %80 0
+         %86 = OpFMul %4 %85 %82
+         %87 = OpFAdd %4 %84 %86
+         %88 = OpCompositeExtract %4 %79 1
+         %89 = OpFMul %4 %88 %81
+         %90 = OpCompositeExtract %4 %80 1
+         %91 = OpFMul %4 %90 %82
+         %92 = OpFAdd %4 %89 %91
+         %93 = OpCompositeExtract %4 %79 2
+         %94 = OpFMul %4 %93 %81
+         %95 = OpCompositeExtract %4 %80 2
+         %96 = OpFMul %4 %95 %82
+         %97 = OpFAdd %4 %94 %96
+         %57 = OpCompositeConstruct %6 %87 %92 %97
+
+; Multiplying 4x2 matrix by 2-dimensional vector
+         %98 = OpCompositeExtract %7 %51 0
+         %99 = OpCompositeExtract %7 %51 1
+        %100 = OpCompositeExtract %4 %35 0
+        %101 = OpCompositeExtract %4 %35 1
+        %102 = OpCompositeExtract %4 %98 0
+        %103 = OpFMul %4 %102 %100
+        %104 = OpCompositeExtract %4 %99 0
+        %105 = OpFMul %4 %104 %101
+        %106 = OpFAdd %4 %103 %105
+        %107 = OpCompositeExtract %4 %98 1
+        %108 = OpFMul %4 %107 %100
+        %109 = OpCompositeExtract %4 %99 1
+        %110 = OpFMul %4 %109 %101
+        %111 = OpFAdd %4 %108 %110
+        %112 = OpCompositeExtract %4 %98 2
+        %113 = OpFMul %4 %112 %100
+        %114 = OpCompositeExtract %4 %99 2
+        %115 = OpFMul %4 %114 %101
+        %116 = OpFAdd %4 %113 %115
+        %117 = OpCompositeExtract %4 %98 3
+        %118 = OpFMul %4 %117 %100
+        %119 = OpCompositeExtract %4 %99 3
+        %120 = OpFMul %4 %119 %101
+        %121 = OpFAdd %4 %118 %120
+         %58 = OpCompositeConstruct %7 %106 %111 %116 %121
+
+; Multiplying 2x3 matrix by 3-dimensional vector
+        %122 = OpCompositeExtract %5 %46 0
+        %123 = OpCompositeExtract %5 %46 1
+        %124 = OpCompositeExtract %5 %46 2
+        %125 = OpCompositeExtract %4 %37 0
+        %126 = OpCompositeExtract %4 %37 1
+        %127 = OpCompositeExtract %4 %37 2
+        %128 = OpCompositeExtract %4 %122 0
+        %129 = OpFMul %4 %128 %125
+        %130 = OpCompositeExtract %4 %123 0
+        %131 = OpFMul %4 %130 %126
+        %132 = OpCompositeExtract %4 %124 0
+        %133 = OpFMul %4 %132 %127
+        %134 = OpFAdd %4 %129 %131
+        %135 = OpFAdd %4 %133 %134
+        %136 = OpCompositeExtract %4 %122 1
+        %137 = OpFMul %4 %136 %125
+        %138 = OpCompositeExtract %4 %123 1
+        %139 = OpFMul %4 %138 %126
+        %140 = OpCompositeExtract %4 %124 1
+        %141 = OpFMul %4 %140 %127
+        %142 = OpFAdd %4 %137 %139
+        %143 = OpFAdd %4 %141 %142
+         %59 = OpCompositeConstruct %5 %135 %143
+
+; Multiplying 3x3 matrix by 3-dimensional vector
+         %60 = OpMatrixTimesVector %6 %49 %38
+
+; Multiplying 4x3 matrix by 3-dimensional vector
+         %61 = OpMatrixTimesVector %7 %52 %39
+
+; Multiplying 2x4 matrix by 4-dimensional vector
+         %62 = OpMatrixTimesVector %5 %47 %41
+
+; Multiplying 3x4 matrix by 4-dimensional vector
+         %63 = OpMatrixTimesVector %6 %50 %42
+
+; Multiplying 4x4 matrix by 4-dimensional vector
+         %64 = OpMatrixTimesVector %7 %53 %43
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+TEST(TransformationReplaceLinearAlgebraInstructionTest,
+     ReplaceOpMatrixTimesMatrix) {
+  std::string reference_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %54 "main"
+               OpExecutionMode %54 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %54 "main"
+
+; Types
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeFloat 32
+          %5 = OpTypeVector %4 2
+          %6 = OpTypeVector %4 3
+          %7 = OpTypeVector %4 4
+          %8 = OpTypeMatrix %5 2
+          %9 = OpTypeMatrix %5 3
+         %10 = OpTypeMatrix %5 4
+         %11 = OpTypeMatrix %6 2
+         %12 = OpTypeMatrix %6 3
+         %13 = OpTypeMatrix %6 4
+         %14 = OpTypeMatrix %7 2
+         %15 = OpTypeMatrix %7 3
+         %16 = OpTypeMatrix %7 4
+
+; Constant scalars
+         %17 = OpConstant %4 1
+         %18 = OpConstant %4 2
+         %19 = OpConstant %4 3
+         %20 = OpConstant %4 4
+         %21 = OpConstant %4 5
+         %22 = OpConstant %4 6
+         %23 = OpConstant %4 7
+         %24 = OpConstant %4 8
+         %25 = OpConstant %4 9
+         %26 = OpConstant %4 10
+         %27 = OpConstant %4 11
+         %28 = OpConstant %4 12
+         %29 = OpConstant %4 13
+         %30 = OpConstant %4 14
+         %31 = OpConstant %4 15
+         %32 = OpConstant %4 16
+
+; Constant vectors
+         %33 = OpConstantComposite %5 %17 %18
+         %34 = OpConstantComposite %5 %19 %20
+         %35 = OpConstantComposite %5 %21 %22
+         %36 = OpConstantComposite %5 %23 %24
+         %37 = OpConstantComposite %6 %17 %18 %19
+         %38 = OpConstantComposite %6 %20 %21 %22
+         %39 = OpConstantComposite %6 %23 %24 %25
+         %40 = OpConstantComposite %6 %26 %27 %28
+         %41 = OpConstantComposite %7 %17 %18 %19 %20
+         %42 = OpConstantComposite %7 %21 %22 %23 %24
+         %43 = OpConstantComposite %7 %25 %26 %27 %28
+         %44 = OpConstantComposite %7 %29 %30 %31 %32
+
+; Constant matrices
+         %45 = OpConstantComposite %8 %33 %34
+         %46 = OpConstantComposite %9 %33 %34 %35
+         %47 = OpConstantComposite %10 %33 %34 %35 %36
+         %48 = OpConstantComposite %11 %37 %38
+         %49 = OpConstantComposite %12 %37 %38 %39
+         %50 = OpConstantComposite %13 %37 %38 %39 %40
+         %51 = OpConstantComposite %14 %41 %42
+         %52 = OpConstantComposite %15 %41 %42 %43
+         %53 = OpConstantComposite %16 %41 %42 %43 %44
+
+; main function
+         %54 = OpFunction %2 None %3
+         %55 = OpLabel
+
+; Multiplying 2x2 matrix by 2x2 matrix
+         %56 = OpMatrixTimesMatrix %8 %45 %45
+
+; Multiplying 2x2 matrix by 2x3 matrix
+         %57 = OpMatrixTimesMatrix %9 %45 %46
+
+; Multiplying 2x2 matrix by 2x4 matrix
+         %58 = OpMatrixTimesMatrix %10 %45 %47
+
+; Multiplying 2x3 matrix by 3x2 matrix
+         %59 = OpMatrixTimesMatrix %8 %46 %48
+
+; Multiplying 2x3 matrix by 3x3 matrix
+         %60 = OpMatrixTimesMatrix %9 %46 %49
+
+; Multiplying 2x3 matrix by 3x4 matrix
+         %61 = OpMatrixTimesMatrix %10 %46 %50
+
+; Multiplying 2x4 matrix by 4x2 matrix
+         %62 = OpMatrixTimesMatrix %8 %47 %51
+
+; Multiplying 2x4 matrix by 4x3 matrix
+         %63 = OpMatrixTimesMatrix %9 %47 %52
+
+; Multiplying 2x4 matrix by 4x4 matrix
+         %64 = OpMatrixTimesMatrix %10 %47 %53
+
+; Multiplying 3x2 matrix by 2x2 matrix
+         %65 = OpMatrixTimesMatrix %11 %48 %45
+
+; Multiplying 3x2 matrix by 2x3 matrix
+         %66 = OpMatrixTimesMatrix %12 %48 %46
+
+; Multiplying 3x2 matrix by 2x4 matrix
+         %67 = OpMatrixTimesMatrix %13 %48 %47
+
+; Multiplying 3x3 matrix by 3x2 matrix
+         %68 = OpMatrixTimesMatrix %11 %49 %48
+
+; Multiplying 3x3 matrix by 3x3 matrix
+         %69 = OpMatrixTimesMatrix %12 %49 %49
+
+; Multiplying 3x3 matrix by 3x4 matrix
+         %70 = OpMatrixTimesMatrix %13 %49 %50
+
+; Multiplying 3x4 matrix by 4x2 matrix
+         %71 = OpMatrixTimesMatrix %11 %50 %51
+
+; Multiplying 3x4 matrix by 4x3 matrix
+         %72 = OpMatrixTimesMatrix %12 %50 %52
+
+; Multiplying 3x4 matrix by 4x4 matrix
+         %73 = OpMatrixTimesMatrix %13 %50 %53
+
+; Multiplying 4x2 matrix by 2x2 matrix
+         %74 = OpMatrixTimesMatrix %14 %51 %45
+
+; Multiplying 4x2 matrix by 2x3 matrix
+         %75 = OpMatrixTimesMatrix %15 %51 %46
+
+; Multiplying 4x2 matrix by 2x4 matrix
+         %76 = OpMatrixTimesMatrix %16 %51 %47
+
+; Multiplying 4x3 matrix by 3x2 matrix
+         %77 = OpMatrixTimesMatrix %14 %52 %48
+
+; Multiplying 4x3 matrix by 3x3 matrix
+         %78 = OpMatrixTimesMatrix %15 %52 %49
+
+; Multiplying 4x3 matrix by 3x4 matrix
+         %79 = OpMatrixTimesMatrix %16 %52 %50
+
+; Multiplying 4x4 matrix by 4x2 matrix
+         %80 = OpMatrixTimesMatrix %14 %53 %51
+
+; Multiplying 4x4 matrix by 4x3 matrix
+         %81 = OpMatrixTimesMatrix %15 %53 %52
+
+; Multiplying 4x4 matrix by 4x4 matrix
+         %82 = OpMatrixTimesMatrix %16 %53 %53
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(56, SpvOpMatrixTimesMatrix, 0);
+  auto transformation = TransformationReplaceLinearAlgebraInstruction(
+      {83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,  96,
+       97,  98,  99,  100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+       111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122},
+      instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(57, SpvOpMatrixTimesMatrix, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+       135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146,
+       147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158,
+       159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170,
+       171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182},
+      instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(58, SpvOpMatrixTimesMatrix, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196,
+       197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
+       211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+       225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238,
+       239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252,
+       253, 254, 255, 256, 257, 258, 259, 260, 261, 262},
+      instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  std::string variant_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %54 "main"
+               OpExecutionMode %54 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %54 "main"
+
+; Types
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeFloat 32
+          %5 = OpTypeVector %4 2
+          %6 = OpTypeVector %4 3
+          %7 = OpTypeVector %4 4
+          %8 = OpTypeMatrix %5 2
+          %9 = OpTypeMatrix %5 3
+         %10 = OpTypeMatrix %5 4
+         %11 = OpTypeMatrix %6 2
+         %12 = OpTypeMatrix %6 3
+         %13 = OpTypeMatrix %6 4
+         %14 = OpTypeMatrix %7 2
+         %15 = OpTypeMatrix %7 3
+         %16 = OpTypeMatrix %7 4
+
+; Constant scalars
+         %17 = OpConstant %4 1
+         %18 = OpConstant %4 2
+         %19 = OpConstant %4 3
+         %20 = OpConstant %4 4
+         %21 = OpConstant %4 5
+         %22 = OpConstant %4 6
+         %23 = OpConstant %4 7
+         %24 = OpConstant %4 8
+         %25 = OpConstant %4 9
+         %26 = OpConstant %4 10
+         %27 = OpConstant %4 11
+         %28 = OpConstant %4 12
+         %29 = OpConstant %4 13
+         %30 = OpConstant %4 14
+         %31 = OpConstant %4 15
+         %32 = OpConstant %4 16
+
+; Constant vectors
+         %33 = OpConstantComposite %5 %17 %18
+         %34 = OpConstantComposite %5 %19 %20
+         %35 = OpConstantComposite %5 %21 %22
+         %36 = OpConstantComposite %5 %23 %24
+         %37 = OpConstantComposite %6 %17 %18 %19
+         %38 = OpConstantComposite %6 %20 %21 %22
+         %39 = OpConstantComposite %6 %23 %24 %25
+         %40 = OpConstantComposite %6 %26 %27 %28
+         %41 = OpConstantComposite %7 %17 %18 %19 %20
+         %42 = OpConstantComposite %7 %21 %22 %23 %24
+         %43 = OpConstantComposite %7 %25 %26 %27 %28
+         %44 = OpConstantComposite %7 %29 %30 %31 %32
+
+; Constant matrices
+         %45 = OpConstantComposite %8 %33 %34
+         %46 = OpConstantComposite %9 %33 %34 %35
+         %47 = OpConstantComposite %10 %33 %34 %35 %36
+         %48 = OpConstantComposite %11 %37 %38
+         %49 = OpConstantComposite %12 %37 %38 %39
+         %50 = OpConstantComposite %13 %37 %38 %39 %40
+         %51 = OpConstantComposite %14 %41 %42
+         %52 = OpConstantComposite %15 %41 %42 %43
+         %53 = OpConstantComposite %16 %41 %42 %43 %44
+
+; main function
+         %54 = OpFunction %2 None %3
+         %55 = OpLabel
+
+; Multiplying 2x2 matrix by 2x2 matrix
+         %83 = OpCompositeExtract %5 %45 0 ; matrix 2 column 0
+         %84 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+         %85 = OpCompositeExtract %4 %84 0 ; matrix 1 row 0 column 0
+         %86 = OpCompositeExtract %4 %83 0 ; matrix 2 row 0 column 0
+         %87 = OpFMul %4 %85 %86
+         %88 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+         %89 = OpCompositeExtract %4 %88 0 ; matrix 1 row 0 column 1
+         %90 = OpCompositeExtract %4 %83 1 ; matrix 2 row 1 column 0
+         %91 = OpFMul %4 %89 %90
+         %92 = OpFAdd %4 %87 %91
+         %93 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+         %94 = OpCompositeExtract %4 %93 1 ; matrix 1 row 1 column 0
+         %95 = OpCompositeExtract %4 %83 0 ; matrix 2 row 0 column 0
+         %96 = OpFMul %4 %94 %95
+         %97 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+         %98 = OpCompositeExtract %4 %97 1 ; matrix 1 row 1 column 1
+         %99 = OpCompositeExtract %4 %83 1 ; matrix 2 row 1 column 0
+        %100 = OpFMul %4 %98 %99
+        %101 = OpFAdd %4 %96 %100
+        %102 = OpCompositeConstruct %5 %92 %101 ; resulting matrix column 0
+        %103 = OpCompositeExtract %5 %45 1 ; matrix 2 column 1
+        %104 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+        %105 = OpCompositeExtract %4 %104 0 ; matrix 1 row 0 column 0
+        %106 = OpCompositeExtract %4 %103 0 ; matrix 2 row 0 column 1
+        %107 = OpFMul %4 %105 %106
+        %108 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+        %109 = OpCompositeExtract %4 %108 0 ; matrix 1 row 0 column 1
+        %110 = OpCompositeExtract %4 %103 1 ; matrix 2 row 1 column 1
+        %111 = OpFMul %4 %109 %110
+        %112 = OpFAdd %4 %107 %111
+        %113 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+        %114 = OpCompositeExtract %4 %113 1 ; matrix 1 row 1 column 0
+        %115 = OpCompositeExtract %4 %103 0 ; matrix 2 row 0 column 1
+        %116 = OpFMul %4 %114 %115
+        %117 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+        %118 = OpCompositeExtract %4 %117 1 ; matrix 1 row 1 column 1
+        %119 = OpCompositeExtract %4 %103 1 ; matrix 2 row 1 column 1
+        %120 = OpFMul %4 %118 %119
+        %121 = OpFAdd %4 %116 %120
+        %122 = OpCompositeConstruct %5 %112 %121 ; resulting matrix column 1
+         %56 = OpCompositeConstruct %8 %102 %122 ; resulting matrix
+
+; Multiplying 2x2 matrix by 2x3 matrix
+        %123 = OpCompositeExtract %5 %46 0 ; matrix 2 column 0
+        %124 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+        %125 = OpCompositeExtract %4 %124 0 ; matrix 1 row 0 column 0
+        %126 = OpCompositeExtract %4 %123 0 ; matrix 2 row 0 column 0
+        %127 = OpFMul %4 %125 %126
+        %128 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+        %129 = OpCompositeExtract %4 %128 0 ; matrix 1 row 0 column 1
+        %130 = OpCompositeExtract %4 %123 1 ; matrix 2 row 1 column 0
+        %131 = OpFMul %4 %129 %130
+        %132 = OpFAdd %4 %127 %131
+        %133 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+        %134 = OpCompositeExtract %4 %133 1 ; matrix 1 row 1 column 0
+        %135 = OpCompositeExtract %4 %123 0 ; matrix 2 row 0 column 0
+        %136 = OpFMul %4 %134 %135
+        %137 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+        %138 = OpCompositeExtract %4 %137 1 ; matrix 1 row 1 column 1
+        %139 = OpCompositeExtract %4 %123 1 ; matrix 2 row 1 column 0
+        %140 = OpFMul %4 %138 %139
+        %141 = OpFAdd %4 %136 %140
+        %142 = OpCompositeConstruct %5 %132 %141 ; resulting matrix column 0
+        %143 = OpCompositeExtract %5 %46 1 ; matrix 2 column 1
+        %144 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+        %145 = OpCompositeExtract %4 %144 0 ; matrix 1 row 0 column 0
+        %146 = OpCompositeExtract %4 %143 0 ; matrix 2 row 0 column 1
+        %147 = OpFMul %4 %145 %146
+        %148 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+        %149 = OpCompositeExtract %4 %148 0 ; matrix 1 row 0 column 1
+        %150 = OpCompositeExtract %4 %143 1 ; matrix 2 row 1 column 1
+        %151 = OpFMul %4 %149 %150
+        %152 = OpFAdd %4 %147 %151
+        %153 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+        %154 = OpCompositeExtract %4 %153 1 ; matrix 1 row 1 column 0
+        %155 = OpCompositeExtract %4 %143 0 ; matrix 2 row 0 column 1
+        %156 = OpFMul %4 %154 %155
+        %157 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+        %158 = OpCompositeExtract %4 %157 1 ; matrix 1 row 1 column 1
+        %159 = OpCompositeExtract %4 %143 1 ; matrix 2 row 1 column 1
+        %160 = OpFMul %4 %158 %159
+        %161 = OpFAdd %4 %156 %160
+        %162 = OpCompositeConstruct %5 %152 %161 ; resulting matrix column 1
+        %163 = OpCompositeExtract %5 %46 2 ; matrix 2 column 2
+        %164 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+        %165 = OpCompositeExtract %4 %164 0 ; matrix 1 row 0 column 0
+        %166 = OpCompositeExtract %4 %163 0 ; matrix 2 row 0 column 2
+        %167 = OpFMul %4 %165 %166
+        %168 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+        %169 = OpCompositeExtract %4 %168 0 ; matrix 1 row 0 column 1
+        %170 = OpCompositeExtract %4 %163 1 ; matrix 2 row 1 column 2
+        %171 = OpFMul %4 %169 %170
+        %172 = OpFAdd %4 %167 %171
+        %173 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+        %174 = OpCompositeExtract %4 %173 1 ; matrix 1 row 1 column 0
+        %175 = OpCompositeExtract %4 %163 0 ; matrix 2 row 0 column 2
+        %176 = OpFMul %4 %174 %175
+        %177 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+        %178 = OpCompositeExtract %4 %177 1 ; matrix 1 row 1 column 1
+        %179 = OpCompositeExtract %4 %163 1 ; matrix 2 row 1 column 2
+        %180 = OpFMul %4 %178 %179
+        %181 = OpFAdd %4 %176 %180
+        %182 = OpCompositeConstruct %5 %172 %181 ; resulting matrix column 2
+         %57 = OpCompositeConstruct %9 %142 %162 %182
+
+; Multiplying 2x2 matrix by 2x4 matrix
+        %183 = OpCompositeExtract %5 %47 0 ; matrix 2 column 0
+        %184 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+        %185 = OpCompositeExtract %4 %184 0 ; matrix 1 row 0 column 0
+        %186 = OpCompositeExtract %4 %183 0 ; matrix 2 row 0 column 0
+        %187 = OpFMul %4 %185 %186
+        %188 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+        %189 = OpCompositeExtract %4 %188 0 ; matrix 1 row 0 column 1
+        %190 = OpCompositeExtract %4 %183 1 ; matrix 2 row 1 column 0
+        %191 = OpFMul %4 %189 %190
+        %192 = OpFAdd %4 %187 %191
+        %193 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+        %194 = OpCompositeExtract %4 %193 1 ; matrix 1 row 1 column 0
+        %195 = OpCompositeExtract %4 %183 0 ; matrix 2 row 0 column 0
+        %196 = OpFMul %4 %194 %195
+        %197 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+        %198 = OpCompositeExtract %4 %197 1 ; matrix 1 row 1 column 1
+        %199 = OpCompositeExtract %4 %183 1 ; matrix 2 row 1 column 0
+        %200 = OpFMul %4 %198 %199
+        %201 = OpFAdd %4 %196 %200
+        %202 = OpCompositeConstruct %5 %192 %201 ; resulting matrix column 0
+        %203 = OpCompositeExtract %5 %47 1 ; matrix 2 column 1
+        %204 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+        %205 = OpCompositeExtract %4 %204 0 ; matrix 1 row 0 column 0
+        %206 = OpCompositeExtract %4 %203 0 ; matrix 2 row 0 column 1
+        %207 = OpFMul %4 %205 %206
+        %208 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+        %209 = OpCompositeExtract %4 %208 0 ; matrix 1 row 0 column 1
+        %210 = OpCompositeExtract %4 %203 1 ; matrix 2 row 1 column 1
+        %211 = OpFMul %4 %209 %210
+        %212 = OpFAdd %4 %207 %211
+        %213 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+        %214 = OpCompositeExtract %4 %213 1 ; matrix 1 row 1 column 0
+        %215 = OpCompositeExtract %4 %203 0 ; matrix 2 row 0 column 1
+        %216 = OpFMul %4 %214 %215
+        %217 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+        %218 = OpCompositeExtract %4 %217 1 ; matrix 1 row 1 column 1
+        %219 = OpCompositeExtract %4 %203 1 ; matrix 2 row 1 column 1
+        %220 = OpFMul %4 %218 %219
+        %221 = OpFAdd %4 %216 %220
+        %222 = OpCompositeConstruct %5 %212 %221 ; resulting matrix column 1
+        %223 = OpCompositeExtract %5 %47 2 ; matrix 2 column 2
+        %224 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+        %225 = OpCompositeExtract %4 %224 0 ; matrix 1 row 0 column 0
+        %226 = OpCompositeExtract %4 %223 0 ; matrix 2 row 0 column 2
+        %227 = OpFMul %4 %225 %226
+        %228 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+        %229 = OpCompositeExtract %4 %228 0 ; matrix 1 row 0 column 1
+        %230 = OpCompositeExtract %4 %223 1 ; matrix 2 row 1 column 2
+        %231 = OpFMul %4 %229 %230
+        %232 = OpFAdd %4 %227 %231
+        %233 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+        %234 = OpCompositeExtract %4 %233 1 ; matrix 1 row 1 column 0
+        %235 = OpCompositeExtract %4 %223 0 ; matrix 2 row 0 column 2
+        %236 = OpFMul %4 %234 %235
+        %237 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+        %238 = OpCompositeExtract %4 %237 1 ; matrix 1 row 1 column 1
+        %239 = OpCompositeExtract %4 %223 1 ; matrix 2 row 1 column 2
+        %240 = OpFMul %4 %238 %239
+        %241 = OpFAdd %4 %236 %240
+        %242 = OpCompositeConstruct %5 %232 %241 ; resulting matrix column 2
+        %243 = OpCompositeExtract %5 %47 3 ; matrix 2 column 3
+        %244 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+        %245 = OpCompositeExtract %4 %244 0 ; matrix 1 row 0 column 0
+        %246 = OpCompositeExtract %4 %243 0 ; matrix 2 row 0 column 3
+        %247 = OpFMul %4 %245 %246
+        %248 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+        %249 = OpCompositeExtract %4 %248 0 ; matrix 1 row 0 column 1
+        %250 = OpCompositeExtract %4 %243 1 ; matrix 2 row 1 column 3
+        %251 = OpFMul %4 %249 %250
+        %252 = OpFAdd %4 %247 %251
+        %253 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+        %254 = OpCompositeExtract %4 %253 1 ; matrix 1 row 1 column 0
+        %255 = OpCompositeExtract %4 %243 0 ; matrix 2 row 0 column 3
+        %256 = OpFMul %4 %254 %255
+        %257 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+        %258 = OpCompositeExtract %4 %257 1 ; matrix 1 row 1 column 1
+        %259 = OpCompositeExtract %4 %243 1 ; matrix 2 row 1 column 3
+        %260 = OpFMul %4 %258 %259
+        %261 = OpFAdd %4 %256 %260
+        %262 = OpCompositeConstruct %5 %252 %261 ; resulting matrix column 3
+         %58 = OpCompositeConstruct %10 %202 %222 %242 %262
+
+; Multiplying 2x3 matrix by 3x2 matrix
+         %59 = OpMatrixTimesMatrix %8 %46 %48
+
+; Multiplying 2x3 matrix by 3x3 matrix
+         %60 = OpMatrixTimesMatrix %9 %46 %49
+
+; Multiplying 2x3 matrix by 3x4 matrix
+         %61 = OpMatrixTimesMatrix %10 %46 %50
+
+; Multiplying 2x4 matrix by 4x2 matrix
+         %62 = OpMatrixTimesMatrix %8 %47 %51
+
+; Multiplying 2x4 matrix by 4x3 matrix
+         %63 = OpMatrixTimesMatrix %9 %47 %52
+
+; Multiplying 2x4 matrix by 4x4 matrix
+         %64 = OpMatrixTimesMatrix %10 %47 %53
+
+; Multiplying 3x2 matrix by 2x2 matrix
+         %65 = OpMatrixTimesMatrix %11 %48 %45
+
+; Multiplying 3x2 matrix by 2x3 matrix
+         %66 = OpMatrixTimesMatrix %12 %48 %46
+
+; Multiplying 3x2 matrix by 2x4 matrix
+         %67 = OpMatrixTimesMatrix %13 %48 %47
+
+; Multiplying 3x3 matrix by 3x2 matrix
+         %68 = OpMatrixTimesMatrix %11 %49 %48
+
+; Multiplying 3x3 matrix by 3x3 matrix
+         %69 = OpMatrixTimesMatrix %12 %49 %49
+
+; Multiplying 3x3 matrix by 3x4 matrix
+         %70 = OpMatrixTimesMatrix %13 %49 %50
+
+; Multiplying 3x4 matrix by 4x2 matrix
+         %71 = OpMatrixTimesMatrix %11 %50 %51
+
+; Multiplying 3x4 matrix by 4x3 matrix
+         %72 = OpMatrixTimesMatrix %12 %50 %52
+
+; Multiplying 3x4 matrix by 4x4 matrix
+         %73 = OpMatrixTimesMatrix %13 %50 %53
+
+; Multiplying 4x2 matrix by 2x2 matrix
+         %74 = OpMatrixTimesMatrix %14 %51 %45
+
+; Multiplying 4x2 matrix by 2x3 matrix
+         %75 = OpMatrixTimesMatrix %15 %51 %46
+
+; Multiplying 4x2 matrix by 2x4 matrix
+         %76 = OpMatrixTimesMatrix %16 %51 %47
+
+; Multiplying 4x3 matrix by 3x2 matrix
+         %77 = OpMatrixTimesMatrix %14 %52 %48
+
+; Multiplying 4x3 matrix by 3x3 matrix
+         %78 = OpMatrixTimesMatrix %15 %52 %49
+
+; Multiplying 4x3 matrix by 3x4 matrix
+         %79 = OpMatrixTimesMatrix %16 %52 %50
+
+; Multiplying 4x4 matrix by 4x2 matrix
+         %80 = OpMatrixTimesMatrix %14 %53 %51
+
+; Multiplying 4x4 matrix by 4x3 matrix
+         %81 = OpMatrixTimesMatrix %15 %53 %52
+
+; Multiplying 4x4 matrix by 4x4 matrix
+         %82 = OpMatrixTimesMatrix %16 %53 %53
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+TEST(TransformationReplaceLinearAlgebraInstructionTest, ReplaceOpDot) {
+  std::string reference_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %22 "main"
+               OpExecutionMode %22 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %22 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeFloat 32
+          %5 = OpTypeVector %4 2
+          %6 = OpTypeVector %4 3
+          %7 = OpTypeVector %4 4
+          %8 = OpConstant %4 1
+          %9 = OpConstant %4 2
+         %10 = OpConstant %4 3
+         %11 = OpConstant %4 4
+         %12 = OpConstant %4 5
+         %13 = OpConstant %4 6
+         %14 = OpConstant %4 7
+         %15 = OpConstant %4 8
+         %16 = OpConstantComposite %5 %8 %9
+         %17 = OpConstantComposite %5 %10 %11
+         %18 = OpConstantComposite %6 %8 %9 %10
+         %19 = OpConstantComposite %6 %11 %12 %13
+         %20 = OpConstantComposite %7 %8 %9 %10 %11
+         %21 = OpConstantComposite %7 %12 %13 %14 %15
+         %22 = OpFunction %2 None %3
+         %23 = OpLabel
+         %24 = OpDot %4 %16 %17
+         %25 = OpDot %4 %18 %19
+         %26 = OpDot %4 %20 %21
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto instruction_descriptor = MakeInstructionDescriptor(24, SpvOpDot, 0);
+  auto transformation = TransformationReplaceLinearAlgebraInstruction(
+      {27, 28, 29, 30, 31, 32}, instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor = MakeInstructionDescriptor(25, SpvOpDot, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {33, 34, 35, 36, 37, 38, 39, 40, 41, 42}, instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor = MakeInstructionDescriptor(26, SpvOpDot, 0);
+  transformation = TransformationReplaceLinearAlgebraInstruction(
+      {43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56},
+      instruction_descriptor);
+  transformation.Apply(context.get(), &transformation_context);
+
+  std::string variant_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %22 "main"
+               OpExecutionMode %22 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %22 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeFloat 32
+          %5 = OpTypeVector %4 2
+          %6 = OpTypeVector %4 3
+          %7 = OpTypeVector %4 4
+          %8 = OpConstant %4 1
+          %9 = OpConstant %4 2
+         %10 = OpConstant %4 3
+         %11 = OpConstant %4 4
+         %12 = OpConstant %4 5
+         %13 = OpConstant %4 6
+         %14 = OpConstant %4 7
+         %15 = OpConstant %4 8
+         %16 = OpConstantComposite %5 %8 %9
+         %17 = OpConstantComposite %5 %10 %11
+         %18 = OpConstantComposite %6 %8 %9 %10
+         %19 = OpConstantComposite %6 %11 %12 %13
+         %20 = OpConstantComposite %7 %8 %9 %10 %11
+         %21 = OpConstantComposite %7 %12 %13 %14 %15
+         %22 = OpFunction %2 None %3
+         %23 = OpLabel
+         %27 = OpCompositeExtract %4 %16 0
+         %28 = OpCompositeExtract %4 %17 0
+         %29 = OpFMul %4 %27 %28
+         %30 = OpCompositeExtract %4 %16 1
+         %31 = OpCompositeExtract %4 %17 1
+         %32 = OpFMul %4 %30 %31
+         %24 = OpFAdd %4 %29 %32
+         %33 = OpCompositeExtract %4 %18 0
+         %34 = OpCompositeExtract %4 %19 0
+         %35 = OpFMul %4 %33 %34
+         %36 = OpCompositeExtract %4 %18 1
+         %37 = OpCompositeExtract %4 %19 1
+         %38 = OpFMul %4 %36 %37
+         %39 = OpCompositeExtract %4 %18 2
+         %40 = OpCompositeExtract %4 %19 2
+         %41 = OpFMul %4 %39 %40
+         %42 = OpFAdd %4 %35 %38
+         %25 = OpFAdd %4 %41 %42
+         %43 = OpCompositeExtract %4 %20 0
+         %44 = OpCompositeExtract %4 %21 0
+         %45 = OpFMul %4 %43 %44
+         %46 = OpCompositeExtract %4 %20 1
+         %47 = OpCompositeExtract %4 %21 1
+         %48 = OpFMul %4 %46 %47
+         %49 = OpCompositeExtract %4 %20 2
+         %50 = OpCompositeExtract %4 %21 2
+         %51 = OpFMul %4 %49 %50
+         %52 = OpCompositeExtract %4 %20 3
+         %53 = OpCompositeExtract %4 %21 3
+         %54 = OpFMul %4 %52 %53
+         %55 = OpFAdd %4 %45 %48
+         %56 = OpFAdd %4 %51 %55
+         %26 = OpFAdd %4 %54 %56
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_load_store_with_copy_memory_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_load_store_with_copy_memory_test.cpp
new file mode 100644
index 0000000..aa3d1fc
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_load_store_with_copy_memory_test.cpp
@@ -0,0 +1,197 @@
+// 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_replace_load_store_with_copy_memory.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationReplaceLoadStoreWithCopyMemoryTest, BasicScenarios) {
+  // This is a simple transformation and this test handles the main cases.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "b"
+               OpName %12 "c"
+               OpName %14 "d"
+               OpName %18 "e"
+               OpName %20 "f"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+         %11 = OpConstant %6 3
+         %13 = OpConstant %6 4
+         %15 = OpConstant %6 5
+         %16 = OpTypeFloat 32
+         %17 = OpTypePointer Function %16
+         %19 = OpConstant %16 2
+         %21 = OpConstant %16 3
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %12 = OpVariable %7 Function
+         %14 = OpVariable %7 Function
+         %18 = OpVariable %17 Function
+         %20 = OpVariable %17 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %12 %13
+               OpStore %14 %15
+               OpStore %18 %19
+               OpStore %20 %21
+         %22 = OpLoad %6 %8
+               OpCopyMemory %10 %8
+               OpStore %10 %22
+         %23 = OpLoad %6 %12
+               OpStore %14 %23
+         %24 = OpLoad %16 %18
+               OpStore %20 %24
+               OpReturn
+               OpFunctionEnd
+    )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto bad_instruction_descriptor_1 =
+      MakeInstructionDescriptor(5, SpvOpVariable, 0);
+
+  auto load_instruction_descriptor_1 =
+      MakeInstructionDescriptor(5, SpvOpLoad, 0);
+  auto load_instruction_descriptor_2 =
+      MakeInstructionDescriptor(5, SpvOpLoad, 1);
+  auto load_instruction_descriptor_3 =
+      MakeInstructionDescriptor(5, SpvOpLoad, 2);
+  auto store_instruction_descriptor_1 =
+      MakeInstructionDescriptor(22, SpvOpStore, 0);
+  auto store_instruction_descriptor_2 =
+      MakeInstructionDescriptor(23, SpvOpStore, 0);
+  auto store_instruction_descriptor_3 =
+      MakeInstructionDescriptor(24, SpvOpStore, 0);
+
+  // Bad: |load_instruction_descriptor| is incorrect.
+  auto transformation_bad_1 = TransformationReplaceLoadStoreWithCopyMemory(
+      bad_instruction_descriptor_1, store_instruction_descriptor_1);
+  ASSERT_FALSE(
+      transformation_bad_1.IsApplicable(context.get(), transformation_context));
+
+  // Bad: |store_instruction_descriptor| is incorrect.
+  auto transformation_bad_2 = TransformationReplaceLoadStoreWithCopyMemory(
+      load_instruction_descriptor_1, bad_instruction_descriptor_1);
+  ASSERT_FALSE(
+      transformation_bad_2.IsApplicable(context.get(), transformation_context));
+
+  // Bad: Intermediate values of the OpLoad and the OpStore don't match.
+  auto transformation_bad_3 = TransformationReplaceLoadStoreWithCopyMemory(
+      load_instruction_descriptor_1, store_instruction_descriptor_2);
+  ASSERT_FALSE(
+      transformation_bad_3.IsApplicable(context.get(), transformation_context));
+
+  // Bad: There is a interfering OpCopyMemory instruction between the OpLoad and
+  // the OpStore.
+  auto transformation_bad_4 = TransformationReplaceLoadStoreWithCopyMemory(
+      load_instruction_descriptor_1, store_instruction_descriptor_1);
+  ASSERT_FALSE(
+      transformation_bad_4.IsApplicable(context.get(), transformation_context));
+
+  auto transformation_good_1 = TransformationReplaceLoadStoreWithCopyMemory(
+      load_instruction_descriptor_2, store_instruction_descriptor_2);
+  ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
+                                                 transformation_context));
+  transformation_good_1.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto transformation_good_2 = TransformationReplaceLoadStoreWithCopyMemory(
+      load_instruction_descriptor_3, store_instruction_descriptor_3);
+  ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(),
+                                                 transformation_context));
+  transformation_good_2.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformations = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "b"
+               OpName %12 "c"
+               OpName %14 "d"
+               OpName %18 "e"
+               OpName %20 "f"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+         %11 = OpConstant %6 3
+         %13 = OpConstant %6 4
+         %15 = OpConstant %6 5
+         %16 = OpTypeFloat 32
+         %17 = OpTypePointer Function %16
+         %19 = OpConstant %16 2
+         %21 = OpConstant %16 3
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %12 = OpVariable %7 Function
+         %14 = OpVariable %7 Function
+         %18 = OpVariable %17 Function
+         %20 = OpVariable %17 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %12 %13
+               OpStore %14 %15
+               OpStore %18 %19
+               OpStore %20 %21
+         %22 = OpLoad %6 %8
+               OpCopyMemory %10 %8
+               OpStore %10 %22
+         %23 = OpLoad %6 %12
+               OpCopyMemory %14 %12
+         %24 = OpLoad %16 %18
+               OpCopyMemory %20 %18
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_parameter_with_global_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_parameter_with_global_test.cpp
new file mode 100644
index 0000000..25e25db
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_parameter_with_global_test.cpp
@@ -0,0 +1,353 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_parameter_with_global.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationReplaceParameterWithGlobalTest, 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
+               OpMemberDecorate %13 0 RelaxedPrecision
+               OpDecorate %16 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Private %6
+          %8 = OpTypeFloat 32
+          %9 = OpTypePointer Private %8
+         %10 = OpTypeVector %8 2
+         %11 = OpTypePointer Private %10
+         %12 = OpTypeBool
+         %71 = OpTypeFunction %2 %6
+         %83 = OpTypeFunction %2 %6 %12
+         %93 = OpTypeFunction %2 %10
+         %94 = OpTypeFunction %2 %8 %10
+         %40 = OpTypePointer Function %12
+         %13 = OpTypeStruct %6 %8
+         %14 = OpTypePointer Private %13
+         %15 = OpTypeFunction %2 %6 %8 %10 %13 %40 %12
+         %22 = OpConstant %6 0
+         %23 = OpConstant %8 0
+         %26 = OpConstantComposite %10 %23 %23
+         %27 = OpConstantTrue %12
+         %28 = OpConstantComposite %13 %22 %23
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %41 = OpVariable %40 Function %27
+         %33 = OpFunctionCall %2 %20 %22 %23 %26 %28 %41 %27
+               OpReturn
+               OpFunctionEnd
+
+         ; adjust type of the function in-place
+         %20 = OpFunction %2 None %15
+         %16 = OpFunctionParameter %6
+         %17 = OpFunctionParameter %8
+         %18 = OpFunctionParameter %10
+         %19 = OpFunctionParameter %13
+         %42 = OpFunctionParameter %40
+         %43 = OpFunctionParameter %12
+         %21 = OpLabel
+               OpReturn
+               OpFunctionEnd
+
+         ; reuse an existing function type
+         %70 = OpFunction %2 None %71
+         %72 = OpFunctionParameter %6
+         %73 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %74 = OpFunction %2 None %71
+         %75 = OpFunctionParameter %6
+         %76 = OpLabel
+               OpReturn
+               OpFunctionEnd
+
+         ; create a new function type
+         %77 = OpFunction %2 None %83
+         %78 = OpFunctionParameter %6
+         %84 = OpFunctionParameter %12
+         %79 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %80 = OpFunction %2 None %83
+         %81 = OpFunctionParameter %6
+         %85 = OpFunctionParameter %12
+         %82 = OpLabel
+               OpReturn
+               OpFunctionEnd
+
+         ; don't adjust the type of the function if it creates a duplicate
+         %86 = OpFunction %2 None %93
+         %87 = OpFunctionParameter %10
+         %89 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %90 = OpFunction %2 None %94
+         %91 = OpFunctionParameter %8
+         %95 = OpFunctionParameter %10
+         %92 = 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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Parameter id is invalid.
+  ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 50, 51)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Parameter id is not a result id of an OpFunctionParameter instruction.
+  ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 21, 51)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Parameter has unsupported type.
+  ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 42, 51)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Initializer for a global variable doesn't exist in the module.
+  ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 43, 51)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Pointer type for a global variable doesn't exist in the module.
+  ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 43, 51)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Function type id is not fresh.
+  ASSERT_FALSE(TransformationReplaceParameterWithGlobal(16, 16, 51)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Global variable id is not fresh.
+  ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 16, 16)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Function type fresh id and global variable fresh id are equal.
+  ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 16, 50)
+                   .IsApplicable(context.get(), transformation_context));
+
+  {
+    TransformationReplaceParameterWithGlobal transformation(50, 16, 51);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationReplaceParameterWithGlobal transformation(52, 17, 53);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationReplaceParameterWithGlobal transformation(54, 18, 55);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationReplaceParameterWithGlobal transformation(56, 19, 57);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationReplaceParameterWithGlobal transformation(58, 75, 59);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationReplaceParameterWithGlobal transformation(60, 81, 61);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationReplaceParameterWithGlobal transformation(62, 91, 63);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string expected_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpMemberDecorate %13 0 RelaxedPrecision
+               OpDecorate %16 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Private %6
+          %8 = OpTypeFloat 32
+          %9 = OpTypePointer Private %8
+         %10 = OpTypeVector %8 2
+         %11 = OpTypePointer Private %10
+         %12 = OpTypeBool
+         %71 = OpTypeFunction %2 %6
+         %83 = OpTypeFunction %2 %6 %12
+         %93 = OpTypeFunction %2 %10
+         %40 = OpTypePointer Function %12
+         %13 = OpTypeStruct %6 %8
+         %14 = OpTypePointer Private %13
+         %22 = OpConstant %6 0
+         %23 = OpConstant %8 0
+         %26 = OpConstantComposite %10 %23 %23
+         %27 = OpConstantTrue %12
+         %28 = OpConstantComposite %13 %22 %23
+         %51 = OpVariable %7 Private %22
+         %53 = OpVariable %9 Private %23
+         %55 = OpVariable %11 Private %26
+         %57 = OpVariable %14 Private %28
+         %15 = OpTypeFunction %2 %40 %12
+         %59 = OpVariable %7 Private %22
+         %61 = OpVariable %7 Private %22
+         %60 = OpTypeFunction %2 %12
+         %63 = OpVariable %9 Private %23
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %41 = OpVariable %40 Function %27
+               OpStore %51 %22
+               OpStore %53 %23
+               OpStore %55 %26
+               OpStore %57 %28
+         %33 = OpFunctionCall %2 %20 %41 %27
+               OpReturn
+               OpFunctionEnd
+         %20 = OpFunction %2 None %15
+         %42 = OpFunctionParameter %40
+         %43 = OpFunctionParameter %12
+         %21 = OpLabel
+         %19 = OpLoad %13 %57
+         %18 = OpLoad %10 %55
+         %17 = OpLoad %8 %53
+         %16 = OpLoad %6 %51
+               OpReturn
+               OpFunctionEnd
+         %70 = OpFunction %2 None %71
+         %72 = OpFunctionParameter %6
+         %73 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %74 = OpFunction %2 None %3
+         %76 = OpLabel
+         %75 = OpLoad %6 %59
+               OpReturn
+               OpFunctionEnd
+         %77 = OpFunction %2 None %83
+         %78 = OpFunctionParameter %6
+         %84 = OpFunctionParameter %12
+         %79 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %80 = OpFunction %2 None %60
+         %85 = OpFunctionParameter %12
+         %82 = OpLabel
+         %81 = OpLoad %6 %61
+               OpReturn
+               OpFunctionEnd
+         %86 = OpFunction %2 None %93
+         %87 = OpFunctionParameter %10
+         %89 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %90 = OpFunction %2 None %93
+         %95 = OpFunctionParameter %10
+         %92 = OpLabel
+         %91 = OpLoad %8 %63
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationReplaceParameterWithGlobalTest,
+     HandlesIrrelevantParameters) {
+  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
+          %9 = OpTypeInt 32 1
+          %3 = OpTypeFunction %2
+          %7 = OpTypeFunction %2 %9 %9
+         %12 = OpTypePointer Private %9
+         %13 = OpConstant %9 0
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %7
+         %10 = OpFunctionParameter %9
+         %11 = OpFunctionParameter %9
+          %8 = 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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  fact_manager.AddFactIdIsIrrelevant(10);
+
+  {
+    TransformationReplaceParameterWithGlobal transformation(20, 10, 21);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+    ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(21));
+  }
+  {
+    TransformationReplaceParameterWithGlobal transformation(22, 11, 23);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(23));
+  }
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_params_with_struct_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_params_with_struct_test.cpp
new file mode 100644
index 0000000..e59f6ea
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_params_with_struct_test.cpp
@@ -0,0 +1,338 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_params_with_struct.h"
+
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationReplaceParamsWithStructTest, 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
+               OpMemberDecorate %13 0 RelaxedPrecision
+               OpDecorate %16 RelaxedPrecision
+          %2 = OpTypeVoid
+          %6 = OpTypeInt 32 1
+          %3 = OpTypeFunction %2
+          %7 = OpTypePointer Private %6
+          %8 = OpTypeFloat 32
+          %9 = OpTypePointer Private %8
+         %10 = OpTypeVector %8 2
+         %11 = OpTypePointer Private %10
+         %12 = OpTypeBool
+         %51 = OpTypeFunction %2 %12
+         %64 = OpTypeStruct %6
+         %63 = OpTypeFunction %2 %64
+         %65 = OpTypeFunction %2 %6
+         %75 = OpTypeStruct %8
+         %76 = OpTypeFunction %2 %75
+         %77 = OpTypeFunction %2 %8
+         %40 = OpTypePointer Function %12
+         %13 = OpTypeStruct %6 %8
+         %45 = OpTypeStruct %6 %10 %13
+         %46 = OpTypeStruct %12
+         %47 = OpTypeStruct %8 %45 %46
+         %14 = OpTypePointer Private %13
+         %15 = OpTypeFunction %2 %6 %8 %10 %13 %40 %12
+         %22 = OpConstant %6 0
+         %23 = OpConstant %8 0
+         %26 = OpConstantComposite %10 %23 %23
+         %27 = OpConstantTrue %12
+         %28 = OpConstantComposite %13 %22 %23
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %41 = OpVariable %40 Function %27
+         %33 = OpFunctionCall %2 %20 %22 %23 %26 %28 %41 %27
+               OpReturn
+               OpFunctionEnd
+
+         ; adjust type of the function in-place
+         %20 = OpFunction %2 None %15
+         %16 = OpFunctionParameter %6
+         %17 = OpFunctionParameter %8
+         %18 = OpFunctionParameter %10
+         %19 = OpFunctionParameter %13
+         %42 = OpFunctionParameter %40
+         %43 = OpFunctionParameter %12
+         %21 = OpLabel
+               OpReturn
+               OpFunctionEnd
+
+         ; create a new function type
+         %50 = OpFunction %2 None %51
+         %52 = OpFunctionParameter %12
+         %53 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %54 = OpFunction %2 None %51
+         %55 = OpFunctionParameter %12
+         %56 = OpLabel
+               OpReturn
+               OpFunctionEnd
+
+         ; reuse an existing function type
+         %57 = OpFunction %2 None %63
+         %58 = OpFunctionParameter %64
+         %59 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %60 = OpFunction %2 None %65
+         %61 = OpFunctionParameter %6
+         %62 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %66 = OpFunction %2 None %65
+         %67 = OpFunctionParameter %6
+         %68 = OpLabel
+               OpReturn
+               OpFunctionEnd
+
+         ; don't adjust the type of the function if it creates a duplicate
+         %69 = OpFunction %2 None %76
+         %70 = OpFunctionParameter %75
+         %71 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %72 = OpFunction %2 None %77
+         %73 = OpFunctionParameter %8
+         %74 = 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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // |parameter_id| is empty.
+  ASSERT_FALSE(
+      TransformationReplaceParamsWithStruct({}, 90, 91, {{33, 92}, {90, 93}})
+          .IsApplicable(context.get(), transformation_context));
+
+  // |parameter_id| has duplicates.
+  ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 16, 17}, 90, 91,
+                                                     {{33, 92}, {90, 93}})
+                   .IsApplicable(context.get(), transformation_context));
+
+  // |parameter_id| has invalid values.
+  ASSERT_FALSE(TransformationReplaceParamsWithStruct({21, 16, 17}, 90, 91,
+                                                     {{33, 92}, {90, 93}})
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 90, 17}, 90, 91,
+                                                     {{33, 92}, {90, 93}})
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Parameter's belong to different functions.
+  ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17, 52}, 90, 91,
+                                                     {{33, 92}, {90, 93}})
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Parameter has unsupported type.
+  ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17, 42, 43}, 90, 91,
+                                                     {{33, 92}, {90, 93}})
+                   .IsApplicable(context.get(), transformation_context));
+
+  // OpTypeStruct does not exist in the module.
+  ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 43}, 90, 91,
+                                                     {{33, 92}, {90, 93}})
+                   .IsApplicable(context.get(), transformation_context));
+
+  // |caller_id_to_fresh_composite_id| misses values.
+  ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
+                                                     {{33, 92}, {90, 93}})
+                   .IsApplicable(context.get(), transformation_context));
+
+  // All fresh ids must be unique.
+  ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 90,
+                                                     {{33, 92}, {90, 93}})
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
+                                                     {{33, 90}, {90, 93}})
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
+                                                     {{33, 92}, {90, 92}})
+                   .IsApplicable(context.get(), transformation_context));
+
+  // All 'fresh' ids must be fresh.
+  ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
+                                                     {{33, 33}, {90, 93}})
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 33, 91,
+                                                     {{33, 92}, {90, 93}})
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 33,
+                                                     {{33, 92}, {90, 93}})
+                   .IsApplicable(context.get(), transformation_context));
+
+  {
+    TransformationReplaceParamsWithStruct transformation({16, 18, 19}, 90, 91,
+                                                         {{33, 92}, {90, 93}});
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationReplaceParamsWithStruct transformation({43}, 93, 94,
+                                                         {{33, 95}});
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationReplaceParamsWithStruct transformation({17, 91, 94}, 96, 97,
+                                                         {{33, 98}});
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationReplaceParamsWithStruct transformation({55}, 99, 100, {{}});
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationReplaceParamsWithStruct transformation({61}, 101, 102, {{}});
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  {
+    TransformationReplaceParamsWithStruct transformation({73}, 103, 104, {{}});
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string expected_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpMemberDecorate %13 0 RelaxedPrecision
+               OpDecorate %16 RelaxedPrecision
+          %2 = OpTypeVoid
+          %6 = OpTypeInt 32 1
+          %3 = OpTypeFunction %2
+          %7 = OpTypePointer Private %6
+          %8 = OpTypeFloat 32
+          %9 = OpTypePointer Private %8
+         %10 = OpTypeVector %8 2
+         %11 = OpTypePointer Private %10
+         %12 = OpTypeBool
+         %51 = OpTypeFunction %2 %12
+         %64 = OpTypeStruct %6
+         %63 = OpTypeFunction %2 %64
+         %65 = OpTypeFunction %2 %6
+         %75 = OpTypeStruct %8
+         %76 = OpTypeFunction %2 %75
+         %40 = OpTypePointer Function %12
+         %13 = OpTypeStruct %6 %8
+         %45 = OpTypeStruct %6 %10 %13
+         %46 = OpTypeStruct %12
+         %47 = OpTypeStruct %8 %45 %46
+         %14 = OpTypePointer Private %13
+         %22 = OpConstant %6 0
+         %23 = OpConstant %8 0
+         %26 = OpConstantComposite %10 %23 %23
+         %27 = OpConstantTrue %12
+         %28 = OpConstantComposite %13 %22 %23
+         %15 = OpTypeFunction %2 %40 %47
+         %99 = OpTypeFunction %2 %46
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %41 = OpVariable %40 Function %27
+         %92 = OpCompositeConstruct %45 %22 %26 %28
+         %95 = OpCompositeConstruct %46 %27
+         %98 = OpCompositeConstruct %47 %23 %92 %95
+         %33 = OpFunctionCall %2 %20 %41 %98
+               OpReturn
+               OpFunctionEnd
+         %20 = OpFunction %2 None %15
+         %42 = OpFunctionParameter %40
+         %97 = OpFunctionParameter %47
+         %21 = OpLabel
+         %94 = OpCompositeExtract %46 %97 2
+         %91 = OpCompositeExtract %45 %97 1
+         %17 = OpCompositeExtract %8 %97 0
+         %43 = OpCompositeExtract %12 %94 0
+         %19 = OpCompositeExtract %13 %91 2
+         %18 = OpCompositeExtract %10 %91 1
+         %16 = OpCompositeExtract %6 %91 0
+               OpReturn
+               OpFunctionEnd
+         %50 = OpFunction %2 None %51
+         %52 = OpFunctionParameter %12
+         %53 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %54 = OpFunction %2 None %99
+        %100 = OpFunctionParameter %46
+         %56 = OpLabel
+         %55 = OpCompositeExtract %12 %100 0
+               OpReturn
+               OpFunctionEnd
+         %57 = OpFunction %2 None %63
+         %58 = OpFunctionParameter %64
+         %59 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %60 = OpFunction %2 None %63
+        %102 = OpFunctionParameter %64
+         %62 = OpLabel
+         %61 = OpCompositeExtract %6 %102 0
+               OpReturn
+               OpFunctionEnd
+         %66 = OpFunction %2 None %65
+         %67 = OpFunctionParameter %6
+         %68 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %69 = OpFunction %2 None %76
+         %70 = OpFunctionParameter %75
+         %71 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %72 = OpFunction %2 None %76
+        %104 = OpFunctionParameter %75
+         %74 = OpLabel
+         %73 = OpCompositeExtract %8 %104 0
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_set_memory_operands_mask_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_set_memory_operands_mask_test.cpp
index c02d8d4..518ce9d 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_set_memory_operands_mask_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_set_memory_operands_mask_test.cpp
@@ -75,6 +75,7 @@
          %21 = OpAccessChain %20 %17 %19
                OpCopyMemory %12 %21 Aligned 16
                OpCopyMemory %133 %12 Volatile
+               OpCopyMemory %133 %12
         %136 = OpAccessChain %135 %17 %30
         %138 = OpAccessChain %24 %12 %19
                OpCopyMemory %138 %136 None
@@ -109,12 +110,14 @@
                    0)
                    .IsApplicable(context.get(), transformation_context));
 
-  TransformationSetMemoryOperandsMask transformation1(
-      MakeInstructionDescriptor(147, SpvOpLoad, 0),
-      SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0);
-  ASSERT_TRUE(
-      transformation1.IsApplicable(context.get(), transformation_context));
-  transformation1.Apply(context.get(), &transformation_context);
+  {
+    TransformationSetMemoryOperandsMask transformation(
+        MakeInstructionDescriptor(147, SpvOpLoad, 0),
+        SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
 
   // Not OK to remove Aligned
   ASSERT_FALSE(TransformationSetMemoryOperandsMask(
@@ -128,15 +131,17 @@
                   SpvMemoryAccessAlignedMask, 0)
                   .IsApplicable(context.get(), transformation_context));
 
-  // OK: adds Nontemporal and Volatile
-  TransformationSetMemoryOperandsMask transformation2(
-      MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
-      SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask |
-          SpvMemoryAccessVolatileMask,
-      0);
-  ASSERT_TRUE(
-      transformation2.IsApplicable(context.get(), transformation_context));
-  transformation2.Apply(context.get(), &transformation_context);
+  {
+    // OK: adds Nontemporal and Volatile
+    TransformationSetMemoryOperandsMask transformation(
+        MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
+        SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask |
+            SpvMemoryAccessVolatileMask,
+        0);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
 
   // Not OK to remove Volatile
   ASSERT_FALSE(TransformationSetMemoryOperandsMask(
@@ -150,29 +155,45 @@
                    SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0)
                    .IsApplicable(context.get(), transformation_context));
 
-  // OK: adds Nontemporal
-  TransformationSetMemoryOperandsMask transformation3(
-      MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
-      SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
-  ASSERT_TRUE(
-      transformation3.IsApplicable(context.get(), transformation_context));
-  transformation3.Apply(context.get(), &transformation_context);
+  {
+    // OK: adds Nontemporal
+    TransformationSetMemoryOperandsMask transformation(
+        MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
+        SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
 
-  // OK: adds Nontemporal and Volatile
-  TransformationSetMemoryOperandsMask transformation4(
-      MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
-      SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
-  ASSERT_TRUE(
-      transformation4.IsApplicable(context.get(), transformation_context));
-  transformation4.Apply(context.get(), &transformation_context);
+  {
+    // OK: adds Nontemporal (creates new operand)
+    TransformationSetMemoryOperandsMask transformation(
+        MakeInstructionDescriptor(21, SpvOpCopyMemory, 2),
+        SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
 
-  // OK: removes Nontemporal, adds Volatile
-  TransformationSetMemoryOperandsMask transformation5(
-      MakeInstructionDescriptor(148, SpvOpStore, 0),
-      SpvMemoryAccessVolatileMask, 0);
-  ASSERT_TRUE(
-      transformation5.IsApplicable(context.get(), transformation_context));
-  transformation5.Apply(context.get(), &transformation_context);
+  {
+    // OK: adds Nontemporal and Volatile
+    TransformationSetMemoryOperandsMask transformation(
+        MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
+        SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+
+  {
+    // OK: removes Nontemporal, adds Volatile
+    TransformationSetMemoryOperandsMask transformation(
+        MakeInstructionDescriptor(148, SpvOpStore, 0),
+        SpvMemoryAccessVolatileMask, 0);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -228,6 +249,7 @@
          %21 = OpAccessChain %20 %17 %19
                OpCopyMemory %12 %21 Aligned|Nontemporal|Volatile 16
                OpCopyMemory %133 %12 Nontemporal|Volatile
+               OpCopyMemory %133 %12 Nontemporal|Volatile
         %136 = OpAccessChain %135 %17 %30
         %138 = OpAccessChain %24 %12 %19
                OpCopyMemory %138 %136 Nontemporal|Volatile
@@ -296,6 +318,8 @@
          %21 = OpAccessChain %20 %17 %19
                OpCopyMemory %12 %21 Aligned 16 Nontemporal|Aligned 16
                OpCopyMemory %133 %12 Volatile
+               OpCopyMemory %133 %12
+               OpCopyMemory %133 %12
         %136 = OpAccessChain %135 %17 %30
         %138 = OpAccessChain %24 %12 %19
                OpCopyMemory %138 %136 None Aligned 16
@@ -318,63 +342,95 @@
   TransformationContext transformation_context(&fact_manager,
                                                validator_options);
 
-  TransformationSetMemoryOperandsMask transformation1(
-      MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
-      SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 1);
-  // Bad: cannot remove aligned
-  ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                   MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
-                   SpvMemoryAccessVolatileMask, 1)
-                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      transformation1.IsApplicable(context.get(), transformation_context));
-  transformation1.Apply(context.get(), &transformation_context);
+  {
+    TransformationSetMemoryOperandsMask transformation(
+        MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
+        SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 1);
+    // Bad: cannot remove aligned
+    ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                     MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
+                     SpvMemoryAccessVolatileMask, 1)
+                     .IsApplicable(context.get(), transformation_context));
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
 
-  TransformationSetMemoryOperandsMask transformation2(
-      MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
-      SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
-  // Bad: cannot remove volatile
-  ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                   MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
-                   SpvMemoryAccessNontemporalMask, 0)
-                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      transformation2.IsApplicable(context.get(), transformation_context));
-  transformation2.Apply(context.get(), &transformation_context);
+  {
+    TransformationSetMemoryOperandsMask transformation(
+        MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
+        SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
+    // Bad: cannot remove volatile
+    ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                     MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
+                     SpvMemoryAccessNontemporalMask, 0)
+                     .IsApplicable(context.get(), transformation_context));
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
 
-  TransformationSetMemoryOperandsMask transformation3(
-      MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
-      SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 1);
-  // Bad: the first mask is None, so Aligned cannot be added to it.
-  ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                   MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
-                   SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask,
-                   0)
-                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      transformation3.IsApplicable(context.get(), transformation_context));
-  transformation3.Apply(context.get(), &transformation_context);
+  {
+    // Creates the first operand.
+    TransformationSetMemoryOperandsMask transformation(
+        MakeInstructionDescriptor(21, SpvOpCopyMemory, 2),
+        SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
 
-  TransformationSetMemoryOperandsMask transformation4(
-      MakeInstructionDescriptor(138, SpvOpCopyMemory, 1),
-      SpvMemoryAccessVolatileMask, 1);
-  ASSERT_TRUE(
-      transformation4.IsApplicable(context.get(), transformation_context));
-  transformation4.Apply(context.get(), &transformation_context);
+  {
+    // Creates both operands.
+    TransformationSetMemoryOperandsMask transformation(
+        MakeInstructionDescriptor(21, SpvOpCopyMemory, 3),
+        SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
 
-  TransformationSetMemoryOperandsMask transformation5(
-      MakeInstructionDescriptor(147, SpvOpLoad, 0),
-      SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, 0);
-  ASSERT_TRUE(
-      transformation5.IsApplicable(context.get(), transformation_context));
-  transformation5.Apply(context.get(), &transformation_context);
+  {
+    TransformationSetMemoryOperandsMask transformation(
+        MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
+        SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 1);
+    // Bad: the first mask is None, so Aligned cannot be added to it.
+    ASSERT_FALSE(
+        TransformationSetMemoryOperandsMask(
+            MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
+            SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 0)
+            .IsApplicable(context.get(), transformation_context));
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
 
-  TransformationSetMemoryOperandsMask transformation6(
-      MakeInstructionDescriptor(148, SpvOpStore, 0), SpvMemoryAccessMaskNone,
-      0);
-  ASSERT_TRUE(
-      transformation6.IsApplicable(context.get(), transformation_context));
-  transformation6.Apply(context.get(), &transformation_context);
+  {
+    TransformationSetMemoryOperandsMask transformation(
+        MakeInstructionDescriptor(138, SpvOpCopyMemory, 1),
+        SpvMemoryAccessVolatileMask, 1);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+
+  {
+    TransformationSetMemoryOperandsMask transformation(
+        MakeInstructionDescriptor(147, SpvOpLoad, 0),
+        SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, 0);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+
+  {
+    TransformationSetMemoryOperandsMask transformation(
+        MakeInstructionDescriptor(148, SpvOpStore, 0), SpvMemoryAccessMaskNone,
+        0);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -430,6 +486,8 @@
          %21 = OpAccessChain %20 %17 %19
                OpCopyMemory %12 %21 Aligned 16 Aligned|Volatile 16
                OpCopyMemory %133 %12 Volatile Nontemporal|Volatile
+               OpCopyMemory %133 %12 Nontemporal|Volatile
+               OpCopyMemory %133 %12 None Nontemporal|Volatile
         %136 = OpAccessChain %135 %17 %30
         %138 = OpAccessChain %24 %12 %19
                OpCopyMemory %138 %136 None Aligned|Nontemporal 16
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp
new file mode 100644
index 0000000..4383e07
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp
@@ -0,0 +1,145 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationSwapConditionalBranchOperandsTest, 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
+          %9 = OpConstant %6 0
+         %11 = OpConstant %6 1
+         %14 = OpTypeBool
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+               OpStore %8 %9
+               OpStore %10 %11
+         %12 = OpLoad %6 %8
+         %13 = OpLoad %6 %10
+         %15 = OpSLessThan %14 %12 %13
+               OpSelectionMerge %17 None
+               OpBranchConditional %15 %16 %21 10 20
+         %16 = OpLabel
+         %18 = OpLoad %6 %10
+         %19 = OpLoad %6 %8
+         %20 = OpIAdd %6 %19 %18
+               OpBranch %17
+         %21 = OpLabel
+         %22 = OpLoad %6 %10
+         %23 = OpLoad %6 %8
+         %24 = OpISub %6 %23 %22
+               OpBranch %17
+         %17 = OpLabel
+         %25 = OpPhi %6 %20 %16 %24 %21
+               OpStore %8 %25
+               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;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Invalid instruction descriptor.
+  ASSERT_FALSE(TransformationSwapConditionalBranchOperands(
+                   MakeInstructionDescriptor(26, SpvOpPhi, 0), 26)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Descriptor for a wrong instruction.
+  ASSERT_FALSE(TransformationSwapConditionalBranchOperands(
+                   MakeInstructionDescriptor(25, SpvOpPhi, 0), 26)
+                   .IsApplicable(context.get(), transformation_context));
+
+  // Fresh id is not fresh.
+  ASSERT_FALSE(TransformationSwapConditionalBranchOperands(
+                   MakeInstructionDescriptor(15, SpvOpBranchConditional, 0), 25)
+                   .IsApplicable(context.get(), transformation_context));
+
+  TransformationSwapConditionalBranchOperands transformation(
+      MakeInstructionDescriptor(15, SpvOpBranchConditional, 0), 26);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %11 = OpConstant %6 1
+         %14 = OpTypeBool
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+               OpStore %8 %9
+               OpStore %10 %11
+         %12 = OpLoad %6 %8
+         %13 = OpLoad %6 %10
+         %15 = OpSLessThan %14 %12 %13
+         %26 = OpLogicalNot %14 %15
+               OpSelectionMerge %17 None
+               OpBranchConditional %26 %21 %16 20 10
+         %16 = OpLabel
+         %18 = OpLoad %6 %10
+         %19 = OpLoad %6 %8
+         %20 = OpIAdd %6 %19 %18
+               OpBranch %17
+         %21 = OpLabel
+         %22 = OpLoad %6 %10
+         %23 = OpLoad %6 %8
+         %24 = OpISub %6 %23 %22
+               OpBranch %17
+         %17 = OpLabel
+         %25 = OpPhi %6 %20 %16 %24 %21
+               OpStore %8 %25
+               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_vector_shuffle_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_vector_shuffle_test.cpp
index a29c511..f72fc95 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_vector_shuffle_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_vector_shuffle_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/transformation_vector_shuffle.h"
+
 #include "source/fuzz/instruction_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 
@@ -540,6 +541,171 @@
                    .IsApplicable(context.get(), transformation_context));
 }
 
+TEST(TransformationVectorShuffle, HandlesIrrelevantIds1) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypeVector %6 2
+         %10 = OpConstantTrue %6
+         %11 = OpConstantFalse %6
+         %12 = OpConstantComposite %7 %10 %11
+        %112 = OpConstantComposite %7 %11 %10
+         %13 = OpTypeVector %6 3
+         %16 = OpConstantComposite %13 %10 %11 %10
+         %17 = OpTypeVector %6 4
+         %20 = OpConstantComposite %17 %10 %11 %10 %11
+         %21 = OpTypeInt 32 1
+         %22 = OpTypeVector %21 2
+         %25 = OpConstant %21 1
+         %26 = OpConstant %21 0
+         %27 = OpConstantComposite %22 %25 %26
+         %28 = OpTypeVector %21 3
+         %31 = OpConstantComposite %28 %25 %26 %25
+         %32 = OpTypeVector %21 4
+         %33 = OpTypePointer Function %32
+         %35 = OpConstantComposite %32 %25 %26 %25 %26
+         %36 = OpTypeInt 32 0
+         %37 = OpTypeVector %36 2
+         %40 = OpConstant %36 1
+         %41 = OpConstant %36 0
+         %42 = OpConstantComposite %37 %40 %41
+         %43 = OpTypeVector %36 3
+         %46 = OpConstantComposite %43 %40 %41 %40
+         %47 = OpTypeVector %36 4
+         %50 = OpConstantComposite %47 %40 %41 %40 %41
+         %51 = OpTypeFloat 32
+         %55 = OpConstant %51 1
+         %56 = OpConstant %51 0
+         %58 = OpTypeVector %51 3
+         %61 = OpConstantComposite %58 %55 %56 %55
+         %62 = OpTypeVector %51 4
+         %65 = OpConstantComposite %62 %55 %56 %55 %56
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %100 None
+               OpBranchConditional %10 %101 %102
+        %101 = OpLabel
+        %103 = OpCompositeConstruct %62 %55 %55 %55 %56
+               OpBranch %100
+        %102 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  TransformationVectorShuffle transformation(
+      MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {2, 0});
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(12, {0}),
+                                        MakeDataDescriptor(200, {1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(112, {0}),
+                                        MakeDataDescriptor(200, {0})));
+}
+
+TEST(TransformationVectorShuffle, HandlesIrrelevantIds2) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypeVector %6 2
+         %10 = OpConstantTrue %6
+         %11 = OpConstantFalse %6
+         %12 = OpConstantComposite %7 %10 %11
+        %112 = OpConstantComposite %7 %11 %10
+         %13 = OpTypeVector %6 3
+         %16 = OpConstantComposite %13 %10 %11 %10
+         %17 = OpTypeVector %6 4
+         %20 = OpConstantComposite %17 %10 %11 %10 %11
+         %21 = OpTypeInt 32 1
+         %22 = OpTypeVector %21 2
+         %25 = OpConstant %21 1
+         %26 = OpConstant %21 0
+         %27 = OpConstantComposite %22 %25 %26
+         %28 = OpTypeVector %21 3
+         %31 = OpConstantComposite %28 %25 %26 %25
+         %32 = OpTypeVector %21 4
+         %33 = OpTypePointer Function %32
+         %35 = OpConstantComposite %32 %25 %26 %25 %26
+         %36 = OpTypeInt 32 0
+         %37 = OpTypeVector %36 2
+         %40 = OpConstant %36 1
+         %41 = OpConstant %36 0
+         %42 = OpConstantComposite %37 %40 %41
+         %43 = OpTypeVector %36 3
+         %46 = OpConstantComposite %43 %40 %41 %40
+         %47 = OpTypeVector %36 4
+         %50 = OpConstantComposite %47 %40 %41 %40 %41
+         %51 = OpTypeFloat 32
+         %55 = OpConstant %51 1
+         %56 = OpConstant %51 0
+         %58 = OpTypeVector %51 3
+         %61 = OpConstantComposite %58 %55 %56 %55
+         %62 = OpTypeVector %51 4
+         %65 = OpConstantComposite %62 %55 %56 %55 %56
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %100 None
+               OpBranchConditional %10 %101 %102
+        %101 = OpLabel
+        %103 = OpCompositeConstruct %62 %55 %55 %55 %56
+               OpBranch %100
+        %102 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  fact_manager.AddFactIdIsIrrelevant(112);
+  TransformationVectorShuffle transformation(
+      MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {2, 0});
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(12, {0}),
+                                        MakeDataDescriptor(200, {1})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(112, {0}),
+                                         MakeDataDescriptor(200, {0})));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/name_mapper_test.cpp b/third_party/SPIRV-Tools/test/name_mapper_test.cpp
index 00fbeed..759c705 100644
--- a/third_party/SPIRV-Tools/test/name_mapper_test.cpp
+++ b/third_party/SPIRV-Tools/test/name_mapper_test.cpp
@@ -270,6 +270,7 @@
         BuiltInCase("SubgroupLocalInvocationId"),
         BuiltInGLCase("VertexIndex"),
         BuiltInGLCase("InstanceIndex"),
+        BuiltInGLCase("BaseInstance"),
         BuiltInCase("SubgroupEqMaskKHR"),
         BuiltInCase("SubgroupGeMaskKHR"),
         BuiltInCase("SubgroupGtMaskKHR"),
diff --git a/third_party/SPIRV-Tools/test/opt/aggressive_dead_code_elim_test.cpp b/third_party/SPIRV-Tools/test/opt/aggressive_dead_code_elim_test.cpp
index e7303ba..125543d 100644
--- a/third_party/SPIRV-Tools/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/aggressive_dead_code_elim_test.cpp
@@ -413,7 +413,7 @@
       predefs1 + names_after + predefs2 + func_after, true, true);
 }
 
-TEST_F(AggressiveDCETest, OptWhitelistExtension) {
+TEST_F(AggressiveDCETest, OptAllowListExtension) {
   //  #version 140
   //
   //  in vec4 BaseColor;
@@ -498,7 +498,7 @@
       predefs1 + names_after + predefs2 + func_after, true, true);
 }
 
-TEST_F(AggressiveDCETest, NoOptBlacklistExtension) {
+TEST_F(AggressiveDCETest, NoOptDenyListExtension) {
   //  #version 140
   //
   //  in vec4 BaseColor;
@@ -6761,6 +6761,770 @@
       predefs1 + names_after + predefs2_after + func_after, true, true);
 }
 
+TEST_F(AggressiveDCETest, MultipleFunctionProcessIndependently) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %entryHistogram "entryHistogram" %gl_GlobalInvocationID %gl_LocalInvocationIndex
+               OpEntryPoint GLCompute %entryAverage "entryAverage" %gl_GlobalInvocationID %gl_LocalInvocationIndex
+               OpExecutionMode %entryHistogram LocalSize 16 16 1
+               OpExecutionMode %entryAverage LocalSize 256 1 1
+               OpSource HLSL 640
+               OpName %type_RWStructuredBuffer_uint "type.RWStructuredBuffer.uint"
+               OpName %uHistogram "uHistogram"
+               OpName %type_ACSBuffer_counter "type.ACSBuffer.counter"
+               OpMemberName %type_ACSBuffer_counter 0 "counter"
+               OpName %counter_var_uHistogram "counter.var.uHistogram"
+               OpName %sharedHistogram "sharedHistogram"
+               OpName %entryHistogram "entryHistogram"
+               OpName %param_var_id "param.var.id"
+               OpName %param_var_idx "param.var.idx"
+               OpName %entryAverage "entryAverage"
+               OpName %param_var_id_0 "param.var.id"
+               OpName %param_var_idx_0 "param.var.idx"
+               OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+               OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex
+               OpDecorate %uHistogram DescriptorSet 0
+               OpDecorate %uHistogram Binding 0
+               OpDecorate %counter_var_uHistogram DescriptorSet 0
+               OpDecorate %counter_var_uHistogram Binding 1
+               OpDecorate %_runtimearr_uint ArrayStride 4
+               OpMemberDecorate %type_RWStructuredBuffer_uint 0 Offset 0
+               OpDecorate %type_RWStructuredBuffer_uint BufferBlock
+               OpMemberDecorate %type_ACSBuffer_counter 0 Offset 0
+               OpDecorate %type_ACSBuffer_counter BufferBlock
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_4 = OpConstant %uint 4
+     %uint_8 = OpConstant %uint 8
+    %uint_16 = OpConstant %uint 16
+    %uint_32 = OpConstant %uint 32
+    %uint_64 = OpConstant %uint 64
+   %uint_128 = OpConstant %uint 128
+   %uint_256 = OpConstant %uint 256
+   %uint_512 = OpConstant %uint 512
+   %uint_254 = OpConstant %uint 254
+   %uint_255 = OpConstant %uint 255
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%type_RWStructuredBuffer_uint = OpTypeStruct %_runtimearr_uint
+%_ptr_Uniform_type_RWStructuredBuffer_uint = OpTypePointer Uniform %type_RWStructuredBuffer_uint
+%type_ACSBuffer_counter = OpTypeStruct %int
+%_ptr_Uniform_type_ACSBuffer_counter = OpTypePointer Uniform %type_ACSBuffer_counter
+%_arr_uint_uint_256 = OpTypeArray %uint %uint_256
+%_ptr_Workgroup__arr_uint_uint_256 = OpTypePointer Workgroup %_arr_uint_uint_256
+     %v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%_ptr_Input_uint = OpTypePointer Input %uint
+       %void = OpTypeVoid
+         %49 = OpTypeFunction %void
+%_ptr_Function_v3uint = OpTypePointer Function %v3uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %52 = OpTypeFunction %void %_ptr_Function_v3uint %_ptr_Function_uint
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+   %uint_264 = OpConstant %uint 264
+       %bool = OpTypeBool
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+ %uHistogram = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_uint Uniform
+%counter_var_uHistogram = OpVariable %_ptr_Uniform_type_ACSBuffer_counter Uniform
+%sharedHistogram = OpVariable %_ptr_Workgroup__arr_uint_uint_256 Workgroup
+%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
+%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
+%entryHistogram = OpFunction %void None %49
+         %57 = OpLabel
+%param_var_id = OpVariable %_ptr_Function_v3uint Function
+%param_var_idx = OpVariable %_ptr_Function_uint Function
+         %58 = OpLoad %v3uint %gl_GlobalInvocationID
+         %59 = OpLoad %uint %gl_LocalInvocationIndex
+         %79 = OpAccessChain %_ptr_Workgroup_uint %sharedHistogram %int_0
+         %80 = OpAtomicIAdd %uint %79 %uint_1 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%entryAverage = OpFunction %void None %49
+         %63 = OpLabel
+%param_var_id_0 = OpVariable %_ptr_Function_v3uint Function
+%param_var_idx_0 = OpVariable %_ptr_Function_uint Function
+         %64 = OpLoad %v3uint %gl_GlobalInvocationID
+         %65 = OpLoad %uint %gl_LocalInvocationIndex
+               OpStore %param_var_idx_0 %65
+         %83 = OpAccessChain %_ptr_Workgroup_uint %sharedHistogram %65
+               OpStore %83 %uint_0
+
+; CHECK:      [[ieq:%\w+]] = OpIEqual
+; CHECK-NEXT: OpSelectionMerge [[merge:%\w+]]
+; CHECK-NEXT: OpBranchConditional [[ieq]] [[not_elim:%\w+]] [[merge]]
+; CHECK-NEXT: [[not_elim]] = OpLabel
+; CHECK:      [[merge]] = OpLabel
+
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %85 = OpIEqual %bool %65 %uint_0
+               OpSelectionMerge %89 None
+               OpBranchConditional %85 %86 %89
+         %86 = OpLabel
+         %88 = OpAccessChain %_ptr_Workgroup_uint %sharedHistogram %65
+               OpStore %88 %uint_1
+               OpBranch %89
+         %89 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %91 = OpAccessChain %_ptr_Workgroup_uint %sharedHistogram %65
+         %92 = OpLoad %uint %91
+         %94 = OpAccessChain %_ptr_Uniform_uint %uHistogram %int_0 %65
+               OpStore %94 %92
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_3);
+
+  SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
+TEST_F(AggressiveDCETest, DebugInfoKeepInFunctionElimStoreVar) {
+  // Verify that dead local variable tc and store eliminated but all
+  // in-function debuginfo kept.
+  //
+  // The SPIR-V has been inlined and local single store eliminated
+  //
+  // Texture2D g_tColor;
+  // SamplerState g_sAniso;
+  //
+  // struct PS_INPUT {
+  //   float2 vTextureCoords : TEXCOORD2;
+  // };
+  //
+  // struct PS_OUTPUT {
+  //   float4 vColor : SV_Target0;
+  // };
+  //
+  // PS_OUTPUT MainPs(PS_INPUT i) {
+  //   PS_OUTPUT ps_output;
+  //   float2 tc = i.vTextureCoords.xy;
+  //   ps_output.vColor = g_tColor.Sample(g_sAniso, tc);
+  //   return ps_output;
+  // }
+
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0
+               OpExecutionMode %MainPs OriginUpperLeft
+          %7 = OpString "foo.frag"
+          %8 = OpString "PS_OUTPUT"
+          %9 = OpString "float"
+         %10 = OpString "vColor"
+         %11 = OpString "PS_INPUT"
+         %12 = OpString "vTextureCoords"
+         %13 = OpString "@type.2d.image"
+         %14 = OpString "type.2d.image"
+         %15 = OpString "Texture2D.TemplateParam"
+         %16 = OpString "src.MainPs"
+         %17 = OpString "tc"
+         %18 = OpString "ps_output"
+         %19 = OpString "i"
+         %20 = OpString "@type.sampler"
+         %21 = OpString "type.sampler"
+         %22 = OpString "g_sAniso"
+         %23 = OpString "g_tColor"
+               OpName %type_2d_image "type.2d.image"
+               OpName %g_tColor "g_tColor"
+               OpName %type_sampler "type.sampler"
+               OpName %g_sAniso "g_sAniso"
+               OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2"
+               OpName %out_var_SV_Target0 "out.var.SV_Target0"
+               OpName %MainPs "MainPs"
+               OpName %PS_INPUT "PS_INPUT"
+               OpMemberName %PS_INPUT 0 "vTextureCoords"
+               OpName %param_var_i "param.var.i"
+               OpName %PS_OUTPUT "PS_OUTPUT"
+               OpMemberName %PS_OUTPUT 0 "vColor"
+               OpName %type_sampled_image "type.sampled.image"
+               OpDecorate %in_var_TEXCOORD2 Location 0
+               OpDecorate %out_var_SV_Target0 Location 0
+               OpDecorate %g_tColor DescriptorSet 0
+               OpDecorate %g_tColor Binding 0
+               OpDecorate %g_sAniso DescriptorSet 0
+               OpDecorate %g_sAniso Binding 1
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+      %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+    %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+   %uint_128 = OpConstant %uint 128
+     %uint_0 = OpConstant %uint 0
+    %uint_64 = OpConstant %uint 64
+         %45 = OpTypeFunction %void
+   %PS_INPUT = OpTypeStruct %v2float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+  %PS_OUTPUT = OpTypeStruct %v4float
+         %47 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+   %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+   %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+         %51 = OpExtInst %void %1 DebugInfoNone
+         %52 = OpExtInst %void %1 DebugExpression
+         %53 = OpExtInst %void %1 DebugOperation Deref
+         %54 = OpExtInst %void %1 DebugExpression %53
+         %55 = OpExtInst %void %1 DebugSource %7
+         %56 = OpExtInst %void %1 DebugCompilationUnit 1 4 %55 HLSL
+         %57 = OpExtInst %void %1 DebugTypeComposite %8 Structure %55 10 1 %56 %8 %uint_128 FlagIsProtected|FlagIsPrivate %58
+         %59 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 Float
+         %60 = OpExtInst %void %1 DebugTypeVector %59 4
+         %58 = OpExtInst %void %1 DebugTypeMember %10 %60 %55 12 5 %57 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+         %61 = OpExtInst %void %1 DebugTypeComposite %11 Structure %55 5 1 %56 %11 %uint_64 FlagIsProtected|FlagIsPrivate %62
+         %63 = OpExtInst %void %1 DebugTypeVector %59 2
+         %62 = OpExtInst %void %1 DebugTypeMember %12 %63 %55 7 5 %61 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate
+         %64 = OpExtInst %void %1 DebugTypeComposite %13 Class %55 0 0 %56 %14 %51 FlagIsProtected|FlagIsPrivate
+         %65 = OpExtInst %void %1 DebugTypeTemplateParameter %15 %59 %51 %55 0 0
+         %66 = OpExtInst %void %1 DebugTypeTemplate %64 %65
+         %67 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %57 %61
+         %68 = OpExtInst %void %1 DebugFunction %16 %67 %55 15 1 %56 %16 FlagIsProtected|FlagIsPrivate 16 %51
+         %69 = OpExtInst %void %1 DebugLexicalBlock %55 16 1 %68
+         %70 = OpExtInst %void %1 DebugLocalVariable %17 %63 %55 19 12 %69 FlagIsLocal
+         %71 = OpExtInst %void %1 DebugLocalVariable %18 %57 %55 17 15 %69 FlagIsLocal
+         %72 = OpExtInst %void %1 DebugLocalVariable %19 %61 %55 15 29 %68 FlagIsLocal 1
+         %73 = OpExtInst %void %1 DebugTypeComposite %20 Structure %55 0 0 %56 %21 %51 FlagIsProtected|FlagIsPrivate
+         %74 = OpExtInst %void %1 DebugGlobalVariable %22 %73 %55 3 14 %56 %22 %g_sAniso FlagIsDefinition
+         %75 = OpExtInst %void %1 DebugGlobalVariable %23 %64 %55 1 11 %56 %23 %g_tColor FlagIsDefinition
+     %MainPs = OpFunction %void None %45
+         %76 = OpLabel
+        %107 = OpExtInst %void %1 DebugScope %69
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %69
+         %78 = OpVariable %_ptr_Function_PS_OUTPUT Function
+         %79 = OpVariable %_ptr_Function_v2float Function
+        %108 = OpExtInst %void %1 DebugNoScope
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoScope
+         %81 = OpVariable %_ptr_Function_PS_OUTPUT Function
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+         %82 = OpLoad %v2float %in_var_TEXCOORD2
+         %83 = OpCompositeConstruct %PS_INPUT %82
+               OpStore %param_var_i %83
+        %109 = OpExtInst %void %1 DebugScope %68
+         %85 = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52
+        %110 = OpExtInst %void %1 DebugScope %69
+         %87 = OpExtInst %void %1 DebugDeclare %71 %78 %52
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %68
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %69
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %71 %78 %52
+               OpLine %7 19 17
+         %88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
+         %89 = OpLoad %v2float %88
+               OpLine %7 19 12
+               OpStore %79 %89
+;CHECK-NOT:    OpStore %79 %89
+               OpLine %7 19 12
+        %106 = OpExtInst %void %1 DebugValue %70 %89 %52
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugValue %70 %89 %52
+               OpLine %7 20 26
+         %91 = OpLoad %type_2d_image %g_tColor
+               OpLine %7 20 46
+         %92 = OpLoad %type_sampler %g_sAniso
+               OpLine %7 20 26
+         %94 = OpSampledImage %type_sampled_image %91 %92
+         %95 = OpImageSampleImplicitLod %v4float %94 %89 None
+               OpLine %7 20 5
+         %96 = OpAccessChain %_ptr_Function_v4float %78 %int_0
+               OpStore %96 %95
+               OpLine %7 21 12
+         %97 = OpLoad %PS_OUTPUT %78
+               OpLine %7 21 5
+               OpStore %81 %97
+        %111 = OpExtInst %void %1 DebugNoScope
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoScope
+        %100 = OpCompositeExtract %v4float %97 0
+               OpStore %out_var_SV_Target0 %100
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
+}
+
+TEST_F(AggressiveDCETest, DebugInfoDeclareKeepsStore) {
+  // Verify that local variable tc and its store are kept by DebugDeclare.
+  //
+  // Same shader source as DebugInfoInFunctionKeepStoreVarElim. The SPIR-V
+  // has just been inlined.
+
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0
+               OpExecutionMode %MainPs OriginUpperLeft
+         %20 = OpString "foo.frag"
+         %24 = OpString "PS_OUTPUT"
+         %28 = OpString "float"
+         %31 = OpString "vColor"
+         %33 = OpString "PS_INPUT"
+         %38 = OpString "vTextureCoords"
+         %40 = OpString "@type.2d.image"
+         %41 = OpString "type.2d.image"
+         %43 = OpString "Texture2D.TemplateParam"
+         %47 = OpString "src.MainPs"
+         %51 = OpString "tc"
+         %53 = OpString "ps_output"
+         %56 = OpString "i"
+         %58 = OpString "@type.sampler"
+         %59 = OpString "type.sampler"
+         %61 = OpString "g_sAniso"
+         %63 = OpString "g_tColor"
+               OpName %type_2d_image "type.2d.image"
+               OpName %g_tColor "g_tColor"
+               OpName %type_sampler "type.sampler"
+               OpName %g_sAniso "g_sAniso"
+               OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2"
+               OpName %out_var_SV_Target0 "out.var.SV_Target0"
+               OpName %MainPs "MainPs"
+               OpName %PS_INPUT "PS_INPUT"
+               OpMemberName %PS_INPUT 0 "vTextureCoords"
+               OpName %param_var_i "param.var.i"
+               OpName %PS_OUTPUT "PS_OUTPUT"
+               OpMemberName %PS_OUTPUT 0 "vColor"
+               OpName %type_sampled_image "type.sampled.image"
+               OpDecorate %in_var_TEXCOORD2 Location 0
+               OpDecorate %out_var_SV_Target0 Location 0
+               OpDecorate %g_tColor DescriptorSet 0
+               OpDecorate %g_tColor Binding 0
+               OpDecorate %g_sAniso DescriptorSet 0
+               OpDecorate %g_sAniso Binding 1
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+      %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+    %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+   %uint_128 = OpConstant %uint 128
+     %uint_0 = OpConstant %uint 0
+    %uint_64 = OpConstant %uint 64
+         %65 = OpTypeFunction %void
+   %PS_INPUT = OpTypeStruct %v2float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+  %PS_OUTPUT = OpTypeStruct %v4float
+         %75 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+   %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+   %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+         %39 = OpExtInst %void %1 DebugInfoNone
+         %55 = OpExtInst %void %1 DebugExpression
+         %22 = OpExtInst %void %1 DebugSource %20
+         %23 = OpExtInst %void %1 DebugCompilationUnit 1 4 %22 HLSL
+         %26 = OpExtInst %void %1 DebugTypeComposite %24 Structure %22 10 1 %23 %24 %uint_128 FlagIsProtected|FlagIsPrivate %27
+         %29 = OpExtInst %void %1 DebugTypeBasic %28 %uint_32 Float
+         %30 = OpExtInst %void %1 DebugTypeVector %29 4
+         %27 = OpExtInst %void %1 DebugTypeMember %31 %30 %22 12 5 %26 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+         %35 = OpExtInst %void %1 DebugTypeComposite %33 Structure %22 5 1 %23 %33 %uint_64 FlagIsProtected|FlagIsPrivate %36
+         %37 = OpExtInst %void %1 DebugTypeVector %29 2
+         %36 = OpExtInst %void %1 DebugTypeMember %38 %37 %22 7 5 %35 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate
+         %42 = OpExtInst %void %1 DebugTypeComposite %40 Class %22 0 0 %23 %41 %39 FlagIsProtected|FlagIsPrivate
+         %44 = OpExtInst %void %1 DebugTypeTemplateParameter %43 %29 %39 %22 0 0
+         %45 = OpExtInst %void %1 DebugTypeTemplate %42 %44
+         %46 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %26 %35
+         %48 = OpExtInst %void %1 DebugFunction %47 %46 %22 15 1 %23 %47 FlagIsProtected|FlagIsPrivate 16 %39
+         %50 = OpExtInst %void %1 DebugLexicalBlock %22 16 1 %48
+         %52 = OpExtInst %void %1 DebugLocalVariable %51 %37 %22 19 12 %50 FlagIsLocal
+         %54 = OpExtInst %void %1 DebugLocalVariable %53 %26 %22 17 15 %50 FlagIsLocal
+         %57 = OpExtInst %void %1 DebugLocalVariable %56 %35 %22 15 29 %48 FlagIsLocal 1
+         %60 = OpExtInst %void %1 DebugTypeComposite %58 Structure %22 0 0 %23 %59 %39 FlagIsProtected|FlagIsPrivate
+         %62 = OpExtInst %void %1 DebugGlobalVariable %61 %60 %22 3 14 %23 %61 %g_sAniso FlagIsDefinition
+         %64 = OpExtInst %void %1 DebugGlobalVariable %63 %42 %22 1 11 %23 %63 %g_tColor FlagIsDefinition
+     %MainPs = OpFunction %void None %65
+         %66 = OpLabel
+        %114 = OpExtInst %void %1 DebugScope %50
+         %98 = OpVariable %_ptr_Function_PS_OUTPUT Function
+         %99 = OpVariable %_ptr_Function_v2float Function
+        %115 = OpExtInst %void %1 DebugNoScope
+        %100 = OpVariable %_ptr_Function_PS_OUTPUT Function
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+         %70 = OpLoad %v2float %in_var_TEXCOORD2
+         %71 = OpCompositeConstruct %PS_INPUT %70
+               OpStore %param_var_i %71
+        %116 = OpExtInst %void %1 DebugScope %48
+        %102 = OpExtInst %void %1 DebugDeclare %57 %param_var_i %55
+        %117 = OpExtInst %void %1 DebugScope %50
+        %103 = OpExtInst %void %1 DebugDeclare %54 %98 %55
+               OpLine %20 19 17
+        %104 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
+        %105 = OpLoad %v2float %104
+               OpLine %20 19 12
+               OpStore %99 %105
+;CHECK:        OpStore %99 %105
+        %106 = OpExtInst %void %1 DebugDeclare %52 %99 %55
+               OpLine %20 20 26
+        %107 = OpLoad %type_2d_image %g_tColor
+               OpLine %20 20 46
+        %108 = OpLoad %type_sampler %g_sAniso
+               OpLine %20 20 26
+        %110 = OpSampledImage %type_sampled_image %107 %108
+        %111 = OpImageSampleImplicitLod %v4float %110 %105 None
+               OpLine %20 20 5
+        %112 = OpAccessChain %_ptr_Function_v4float %98 %int_0
+               OpStore %112 %111
+               OpLine %20 21 12
+        %113 = OpLoad %PS_OUTPUT %98
+               OpLine %20 21 5
+               OpStore %100 %113
+        %118 = OpExtInst %void %1 DebugNoScope
+         %73 = OpLoad %PS_OUTPUT %100
+         %74 = OpCompositeExtract %v4float %73 0
+               OpStore %out_var_SV_Target0 %74
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
+}
+
+TEST_F(AggressiveDCETest, DebugInfoValueDerefKeepsStore) {
+  // Verify that local variable tc and its store are kept by DebugValue with
+  // Deref.
+  //
+  // Same shader source as DebugInfoInFunctionKeepStoreVarElim. The SPIR-V
+  // has just been inlined and edited to replace the DebugDeclare with the
+  // DebugValue/Deref.
+
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0
+               OpExecutionMode %MainPs OriginUpperLeft
+          %7 = OpString "foo.frag"
+          %8 = OpString "PS_OUTPUT"
+          %9 = OpString "float"
+         %10 = OpString "vColor"
+         %11 = OpString "PS_INPUT"
+         %12 = OpString "vTextureCoords"
+         %13 = OpString "@type.2d.image"
+         %14 = OpString "type.2d.image"
+         %15 = OpString "Texture2D.TemplateParam"
+         %16 = OpString "src.MainPs"
+         %17 = OpString "tc"
+         %18 = OpString "ps_output"
+         %19 = OpString "i"
+         %20 = OpString "@type.sampler"
+         %21 = OpString "type.sampler"
+         %22 = OpString "g_sAniso"
+         %23 = OpString "g_tColor"
+               OpName %type_2d_image "type.2d.image"
+               OpName %g_tColor "g_tColor"
+               OpName %type_sampler "type.sampler"
+               OpName %g_sAniso "g_sAniso"
+               OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2"
+               OpName %out_var_SV_Target0 "out.var.SV_Target0"
+               OpName %MainPs "MainPs"
+               OpName %PS_INPUT "PS_INPUT"
+               OpMemberName %PS_INPUT 0 "vTextureCoords"
+               OpName %param_var_i "param.var.i"
+               OpName %PS_OUTPUT "PS_OUTPUT"
+               OpMemberName %PS_OUTPUT 0 "vColor"
+               OpName %type_sampled_image "type.sampled.image"
+               OpDecorate %in_var_TEXCOORD2 Location 0
+               OpDecorate %out_var_SV_Target0 Location 0
+               OpDecorate %g_tColor DescriptorSet 0
+               OpDecorate %g_tColor Binding 0
+               OpDecorate %g_sAniso DescriptorSet 0
+               OpDecorate %g_sAniso Binding 1
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+      %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+    %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+   %uint_128 = OpConstant %uint 128
+     %uint_0 = OpConstant %uint 0
+    %uint_64 = OpConstant %uint 64
+         %45 = OpTypeFunction %void
+   %PS_INPUT = OpTypeStruct %v2float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+  %PS_OUTPUT = OpTypeStruct %v4float
+         %47 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+   %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+   %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+         %51 = OpExtInst %void %1 DebugInfoNone
+         %52 = OpExtInst %void %1 DebugExpression
+         %53 = OpExtInst %void %1 DebugOperation Deref
+         %54 = OpExtInst %void %1 DebugExpression %53
+         %55 = OpExtInst %void %1 DebugSource %7
+         %56 = OpExtInst %void %1 DebugCompilationUnit 1 4 %55 HLSL
+         %57 = OpExtInst %void %1 DebugTypeComposite %8 Structure %55 10 1 %56 %8 %uint_128 FlagIsProtected|FlagIsPrivate %58
+         %59 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 Float
+         %60 = OpExtInst %void %1 DebugTypeVector %59 4
+         %58 = OpExtInst %void %1 DebugTypeMember %10 %60 %55 12 5 %57 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+         %61 = OpExtInst %void %1 DebugTypeComposite %11 Structure %55 5 1 %56 %11 %uint_64 FlagIsProtected|FlagIsPrivate %62
+         %63 = OpExtInst %void %1 DebugTypeVector %59 2
+         %62 = OpExtInst %void %1 DebugTypeMember %12 %63 %55 7 5 %61 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate
+         %64 = OpExtInst %void %1 DebugTypeComposite %13 Class %55 0 0 %56 %14 %51 FlagIsProtected|FlagIsPrivate
+         %65 = OpExtInst %void %1 DebugTypeTemplateParameter %15 %59 %51 %55 0 0
+         %66 = OpExtInst %void %1 DebugTypeTemplate %64 %65
+         %67 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %57 %61
+         %68 = OpExtInst %void %1 DebugFunction %16 %67 %55 15 1 %56 %16 FlagIsProtected|FlagIsPrivate 16 %51
+         %69 = OpExtInst %void %1 DebugLexicalBlock %55 16 1 %68
+         %70 = OpExtInst %void %1 DebugLocalVariable %17 %63 %55 19 12 %69 FlagIsLocal
+         %71 = OpExtInst %void %1 DebugLocalVariable %18 %57 %55 17 15 %69 FlagIsLocal
+         %72 = OpExtInst %void %1 DebugLocalVariable %19 %61 %55 15 29 %68 FlagIsLocal 1
+         %73 = OpExtInst %void %1 DebugTypeComposite %20 Structure %55 0 0 %56 %21 %51 FlagIsProtected|FlagIsPrivate
+         %74 = OpExtInst %void %1 DebugGlobalVariable %22 %73 %55 3 14 %56 %22 %g_sAniso FlagIsDefinition
+         %75 = OpExtInst %void %1 DebugGlobalVariable %23 %64 %55 1 11 %56 %23 %g_tColor FlagIsDefinition
+     %MainPs = OpFunction %void None %45
+         %76 = OpLabel
+        %101 = OpExtInst %void %1 DebugScope %69
+         %78 = OpVariable %_ptr_Function_PS_OUTPUT Function
+         %79 = OpVariable %_ptr_Function_v2float Function
+        %102 = OpExtInst %void %1 DebugNoScope
+         %81 = OpVariable %_ptr_Function_PS_OUTPUT Function
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+         %82 = OpLoad %v2float %in_var_TEXCOORD2
+         %83 = OpCompositeConstruct %PS_INPUT %82
+               OpStore %param_var_i %83
+        %103 = OpExtInst %void %1 DebugScope %68
+         %85 = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52
+        %104 = OpExtInst %void %1 DebugScope %69
+         %87 = OpExtInst %void %1 DebugDeclare %71 %78 %52
+               OpLine %7 19 17
+         %88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
+         %89 = OpLoad %v2float %88
+               OpLine %7 19 12
+               OpStore %79 %89
+;CHECK:        OpStore %79 %89
+         %90 = OpExtInst %void %1 DebugValue %70 %79 %54
+               OpLine %7 20 26
+         %91 = OpLoad %type_2d_image %g_tColor
+               OpLine %7 20 46
+         %92 = OpLoad %type_sampler %g_sAniso
+               OpLine %7 20 26
+         %94 = OpSampledImage %type_sampled_image %91 %92
+         %95 = OpImageSampleImplicitLod %v4float %94 %89 None
+               OpLine %7 20 5
+         %96 = OpAccessChain %_ptr_Function_v4float %78 %int_0
+               OpStore %96 %95
+               OpLine %7 21 12
+         %97 = OpLoad %PS_OUTPUT %78
+               OpLine %7 21 5
+               OpStore %81 %97
+        %105 = OpExtInst %void %1 DebugNoScope
+         %99 = OpLoad %PS_OUTPUT %81
+        %100 = OpCompositeExtract %v4float %99 0
+               OpStore %out_var_SV_Target0 %100
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
+}
+
+TEST_F(AggressiveDCETest, DebugInfoElimUnusedTextureKeepGlobalVariable) {
+  // Verify that unused texture g_tColor2 is eliminated but its
+  // DebugGlobalVariable is retained but with DebugInfoNone for its Variable.
+  //
+  // Same shader source as DebugInfoInFunctionKeepStoreVarElim but with unused
+  // g_tColor2 added.
+
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_tColor2 %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0
+               OpExecutionMode %MainPs OriginUpperLeft
+         %21 = OpString "foo6.frag"
+         %25 = OpString "PS_OUTPUT"
+         %29 = OpString "float"
+         %32 = OpString "vColor"
+         %34 = OpString "PS_INPUT"
+         %39 = OpString "vTextureCoords"
+         %41 = OpString "@type.2d.image"
+         %42 = OpString "type.2d.image"
+         %44 = OpString "Texture2D.TemplateParam"
+         %48 = OpString "src.MainPs"
+         %52 = OpString "tc"
+         %54 = OpString "ps_output"
+         %57 = OpString "i"
+         %59 = OpString "@type.sampler"
+         %60 = OpString "type.sampler"
+         %62 = OpString "g_sAniso"
+         %64 = OpString "g_tColor2"
+         %66 = OpString "g_tColor"
+               OpName %type_2d_image "type.2d.image"
+               OpName %g_tColor "g_tColor"
+               OpName %g_tColor2 "g_tColor2"
+               OpName %type_sampler "type.sampler"
+               OpName %g_sAniso "g_sAniso"
+               OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2"
+               OpName %out_var_SV_Target0 "out.var.SV_Target0"
+               OpName %MainPs "MainPs"
+               OpName %PS_INPUT "PS_INPUT"
+               OpMemberName %PS_INPUT 0 "vTextureCoords"
+               OpName %param_var_i "param.var.i"
+               OpName %PS_OUTPUT "PS_OUTPUT"
+               OpMemberName %PS_OUTPUT 0 "vColor"
+               OpName %type_sampled_image "type.sampled.image"
+               OpDecorate %in_var_TEXCOORD2 Location 0
+               OpDecorate %out_var_SV_Target0 Location 0
+               OpDecorate %g_tColor DescriptorSet 0
+               OpDecorate %g_tColor Binding 0
+               OpDecorate %g_tColor2 DescriptorSet 0
+               OpDecorate %g_tColor2 Binding 1
+               OpDecorate %g_sAniso DescriptorSet 0
+               OpDecorate %g_sAniso Binding 2
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+      %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+    %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+   %uint_128 = OpConstant %uint 128
+     %uint_0 = OpConstant %uint 0
+    %uint_64 = OpConstant %uint 64
+         %68 = OpTypeFunction %void
+   %PS_INPUT = OpTypeStruct %v2float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+  %PS_OUTPUT = OpTypeStruct %v4float
+         %78 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+   %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+  %g_tColor2 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+;CHECK-NOT: %g_tColor2 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+   %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+         %40 = OpExtInst %void %1 DebugInfoNone
+         %56 = OpExtInst %void %1 DebugExpression
+         %23 = OpExtInst %void %1 DebugSource %21
+         %24 = OpExtInst %void %1 DebugCompilationUnit 1 4 %23 HLSL
+         %27 = OpExtInst %void %1 DebugTypeComposite %25 Structure %23 11 1 %24 %25 %uint_128 FlagIsProtected|FlagIsPrivate %28
+         %30 = OpExtInst %void %1 DebugTypeBasic %29 %uint_32 Float
+         %31 = OpExtInst %void %1 DebugTypeVector %30 4
+         %28 = OpExtInst %void %1 DebugTypeMember %32 %31 %23 13 5 %27 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+         %36 = OpExtInst %void %1 DebugTypeComposite %34 Structure %23 6 1 %24 %34 %uint_64 FlagIsProtected|FlagIsPrivate %37
+         %38 = OpExtInst %void %1 DebugTypeVector %30 2
+         %37 = OpExtInst %void %1 DebugTypeMember %39 %38 %23 8 5 %36 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate
+         %43 = OpExtInst %void %1 DebugTypeComposite %41 Class %23 0 0 %24 %42 %40 FlagIsProtected|FlagIsPrivate
+         %45 = OpExtInst %void %1 DebugTypeTemplateParameter %44 %30 %40 %23 0 0
+         %46 = OpExtInst %void %1 DebugTypeTemplate %43 %45
+         %47 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %27 %36
+         %49 = OpExtInst %void %1 DebugFunction %48 %47 %23 16 1 %24 %48 FlagIsProtected|FlagIsPrivate 17 %40
+         %51 = OpExtInst %void %1 DebugLexicalBlock %23 17 1 %49
+         %53 = OpExtInst %void %1 DebugLocalVariable %52 %38 %23 20 12 %51 FlagIsLocal
+         %55 = OpExtInst %void %1 DebugLocalVariable %54 %27 %23 18 15 %51 FlagIsLocal
+         %58 = OpExtInst %void %1 DebugLocalVariable %57 %36 %23 16 29 %49 FlagIsLocal 1
+         %61 = OpExtInst %void %1 DebugTypeComposite %59 Structure %23 0 0 %24 %60 %40 FlagIsProtected|FlagIsPrivate
+         %63 = OpExtInst %void %1 DebugGlobalVariable %62 %61 %23 4 14 %24 %62 %g_sAniso FlagIsDefinition
+         %65 = OpExtInst %void %1 DebugGlobalVariable %64 %43 %23 2 11 %24 %64 %g_tColor2 FlagIsDefinition
+;CHECK-NOT: %65 = OpExtInst %void %1 DebugGlobalVariable %64 %43 %23 2 11 %24 %64 %g_tColor2 FlagIsDefinition
+;CHECK:     %65 = OpExtInst %void %1 DebugGlobalVariable %64 %43 %23 2 11 %24 %64 %40 FlagIsDefinition
+         %67 = OpExtInst %void %1 DebugGlobalVariable %66 %43 %23 1 11 %24 %66 %g_tColor FlagIsDefinition
+     %MainPs = OpFunction %void None %68
+         %69 = OpLabel
+        %117 = OpExtInst %void %1 DebugScope %51
+        %101 = OpVariable %_ptr_Function_PS_OUTPUT Function
+        %102 = OpVariable %_ptr_Function_v2float Function
+        %118 = OpExtInst %void %1 DebugNoScope
+        %103 = OpVariable %_ptr_Function_PS_OUTPUT Function
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+         %73 = OpLoad %v2float %in_var_TEXCOORD2
+         %74 = OpCompositeConstruct %PS_INPUT %73
+               OpStore %param_var_i %74
+        %119 = OpExtInst %void %1 DebugScope %49
+        %105 = OpExtInst %void %1 DebugDeclare %58 %param_var_i %56
+        %120 = OpExtInst %void %1 DebugScope %51
+        %106 = OpExtInst %void %1 DebugDeclare %55 %101 %56
+               OpLine %21 20 17
+        %107 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
+        %108 = OpLoad %v2float %107
+               OpLine %21 20 12
+               OpStore %102 %108
+        %109 = OpExtInst %void %1 DebugDeclare %53 %102 %56
+               OpLine %21 21 26
+        %110 = OpLoad %type_2d_image %g_tColor
+               OpLine %21 21 46
+        %111 = OpLoad %type_sampler %g_sAniso
+               OpLine %21 21 57
+        %112 = OpLoad %v2float %102
+               OpLine %21 21 26
+        %113 = OpSampledImage %type_sampled_image %110 %111
+        %114 = OpImageSampleImplicitLod %v4float %113 %112 None
+               OpLine %21 21 5
+        %115 = OpAccessChain %_ptr_Function_v4float %101 %int_0
+               OpStore %115 %114
+               OpLine %21 22 12
+        %116 = OpLoad %PS_OUTPUT %101
+               OpLine %21 22 5
+               OpStore %103 %116
+        %121 = OpExtInst %void %1 DebugNoScope
+         %76 = OpLoad %PS_OUTPUT %103
+         %77 = OpCompositeExtract %v4float %76 0
+               OpStore %out_var_SV_Target0 %77
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Check that logical addressing required
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 f1460c5..7381908 100644
--- a/third_party/SPIRV-Tools/test/opt/block_merge_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/block_merge_test.cpp
@@ -639,6 +639,40 @@
   SinglePassRunAndMatch<BlockMergePass>(text, true);
 }
 
+TEST_F(BlockMergeTest, DontMergeTerminateInvocation) {
+  const std::string text = R"(
+; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None
+; CHECK-NEXT: OpBranch [[ret:%\w+]]
+; CHECK: [[ret:%\w+]] = OpLabel
+; CHECK-NEXT: OpTerminateInvocation
+; CHECK-DAG: [[cont]] = OpLabel
+; CHECK-DAG: [[merge]] = OpLabel
+OpCapability Shader
+OpExtension "SPV_KHR_terminate_invocation"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %func "func"
+OpExecutionMode %func OriginUpperLeft
+%void = OpTypeVoid
+%bool = OpTypeBool
+%functy = OpTypeFunction %void
+%func = OpFunction %void None %functy
+%1 = OpLabel
+OpBranch %2
+%2 = OpLabel
+OpLoopMerge %3 %4 None
+OpBranch %5
+%5 = OpLabel
+OpTerminateInvocation
+%4 = OpLabel
+OpBranch %2
+%3 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<BlockMergePass>(text, true);
+}
+
 TEST_F(BlockMergeTest, DontMergeUnreachable) {
   const std::string text = R"(
 ; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None
diff --git a/third_party/SPIRV-Tools/test/opt/ccp_test.cpp b/third_party/SPIRV-Tools/test/opt/ccp_test.cpp
index 920c0f4..52a291c 100644
--- a/third_party/SPIRV-Tools/test/opt/ccp_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/ccp_test.cpp
@@ -925,6 +925,137 @@
   SinglePassRunAndMatch<CCPPass>(text, true);
 }
 
+TEST_F(CCPTest, DebugSimpleFoldConstant) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpCapability Linkage
+        %ext = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+  %file_name = OpString "test"
+ %float_name = OpString "float"
+  %main_name = OpString "main"
+     %f_name = OpString "f"
+               OpDecorate %1 LinkageAttributes "func" Export
+       %void = OpTypeVoid
+       %bool = OpTypeBool
+      %float = OpTypeFloat 32
+    %float_0 = OpConstant %float 0
+
+; CHECK: [[float1:%\w+]] = OpConstant {{%\w+}} 1
+    %float_1 = OpConstant %float 1
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+          %8 = OpTypeFunction %float
+  %null_expr = OpExtInst %void %ext DebugExpression
+        %src = OpExtInst %void %ext DebugSource %file_name
+         %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+     %dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+    %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+   %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %1
+      %dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+          %1 = OpFunction %float None %8
+         %10 = OpLabel
+
+; CHECK: OpExtInst %void [[ext:%\w+]] DebugScope
+; CHECK: OpLine [[file:%\w+]] 1 0
+; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %float_1
+         %s0 = OpExtInst %void %ext DebugScope %dbg_main
+               OpLine %file_name 1 0
+         %17 = OpFAdd %float %float_0 %float_1
+        %val = OpExtInst %void %ext DebugValue %dbg_f %17 %null_expr
+
+; CHECK: OpLine [[file]] 2 0
+; CHECK: OpReturnValue [[float1]]
+               OpLine %file_name 2 0
+               OpReturnValue %17
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<CCPPass>(text, true);
+}
+
+TEST_F(CCPTest, DebugFoldMultipleForSingleConstant) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+        %ext = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %outparm
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+  %file_name = OpString "test"
+ %float_name = OpString "float"
+  %main_name = OpString "main"
+     %f_name = OpString "f"
+               OpName %main "main"
+               OpName %outparm "outparm"
+               OpDecorate %outparm Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+       %bool = OpTypeBool
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_4 = OpConstant %int 4
+      %int_3 = OpConstant %int 3
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+%_ptr_Output_int = OpTypePointer Output %int
+    %outparm = OpVariable %_ptr_Output_int Output
+  %null_expr = OpExtInst %void %ext DebugExpression
+        %src = OpExtInst %void %ext DebugSource %file_name
+         %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+     %dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+    %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+   %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+        %bb0 = OpExtInst %void %ext DebugLexicalBlock %src 0 0 %dbg_main
+        %bb1 = OpExtInst %void %ext DebugLexicalBlock %src 1 0 %dbg_main
+        %bb2 = OpExtInst %void %ext DebugLexicalBlock %src 2 0 %dbg_main
+        %bb3 = OpExtInst %void %ext DebugLexicalBlock %src 3 0 %dbg_main
+      %dbg_f0 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+      %dbg_f1 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 1 0 %dbg_main FlagIsLocal
+      %dbg_f2 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 2 0 %dbg_main FlagIsLocal
+       %main = OpFunction %void None %3
+          %4 = OpLabel
+
+; CHECK: OpExtInst %void [[ext:%\w+]] DebugScope
+; CHECK: OpLine [[file:%\w+]] 1 0
+; CHECK: OpIAdd %int %int_4 %int_3
+; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %int_7
+         %s0 = OpExtInst %void %ext DebugScope %bb0
+               OpLine %file_name 1 0
+          %9 = OpIAdd %int %int_4 %int_3
+       %val0 = OpExtInst %void %ext DebugValue %dbg_f0 %9 %null_expr
+
+; CHECK: OpLine [[file]] 2 0
+; CHECK: OpSGreaterThan %bool %int_7 %int_3
+; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %true
+               OpLine %file_name 2 0
+          %6 = OpSGreaterThan %bool %9 %int_3
+       %val1 = OpExtInst %void %ext DebugValue %dbg_f1 %6 %null_expr
+
+               OpSelectionMerge %25 None
+               OpBranchConditional %6 %22 %23
+         %22 = OpLabel
+         %s1 = OpExtInst %void %ext DebugScope %bb1
+          %7 = OpCopyObject %int %9
+       %val2 = OpExtInst %void %ext DebugValue %dbg_f2 %7 %null_expr
+               OpBranch %25
+         %23 = OpLabel
+         %s2 = OpExtInst %void %ext DebugScope %bb2
+          %8 = OpCopyObject %int %int_4
+               OpBranch %25
+         %25 = OpLabel
+         %s3 = OpExtInst %void %ext DebugScope %bb3
+         %35 = OpPhi %int %7 %22 %8 %23
+               OpStore %outparm %35
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<CCPPass>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/copy_prop_array_test.cpp b/third_party/SPIRV-Tools/test/opt/copy_prop_array_test.cpp
index 1afee9c..72bc7f6 100644
--- a/third_party/SPIRV-Tools/test/opt/copy_prop_array_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/copy_prop_array_test.cpp
@@ -1622,6 +1622,198 @@
   SinglePassRunAndMatch<CopyPropagateArrays>(text, true);
 }
 
+TEST_F(CopyPropArrayPassTest, DebugDeclare) {
+  const std::string before =
+      R"(OpCapability Shader
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 600
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+OpName %type_MyCBuffer "type.MyCBuffer"
+OpMemberName %type_MyCBuffer 0 "Data"
+OpName %MyCBuffer "MyCBuffer"
+OpName %main "main"
+OpName %in_var_INDEX "in.var.INDEX"
+OpName %out_var_SV_Target "out.var.SV_Target"
+OpDecorate %_arr_v4float_uint_8 ArrayStride 16
+OpMemberDecorate %type_MyCBuffer 0 Offset 0
+OpDecorate %type_MyCBuffer Block
+OpDecorate %in_var_INDEX Flat
+OpDecorate %in_var_INDEX Location 0
+OpDecorate %out_var_SV_Target Location 0
+OpDecorate %MyCBuffer DescriptorSet 0
+OpDecorate %MyCBuffer Binding 0
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%uint = OpTypeInt 32 0
+%uint_8 = OpConstant %uint 8
+%uint_32 = OpConstant %uint 32
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8
+%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer
+%void = OpTypeVoid
+%13 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8
+%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0
+%int_0 = OpConstant %int 0
+%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform
+%in_var_INDEX = OpVariable %_ptr_Input_int Input
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+
+; CHECK: [[deref:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugOperation Deref
+; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+
+; CHECK: [[deref_expr:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref]]
+; CHECK: OpAccessChain
+; CHECK: [[newptr:%\w+]] = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0
+; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[newptr]] [[deref_expr]]
+; CHECK: [[element_ptr:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[newptr]] %24
+; CHECK: [[load:%\w+]] = OpLoad %v4float [[element_ptr]]
+; CHECK: OpStore %out_var_SV_Target [[load]]
+%main = OpFunction %void None %13
+%22 = OpLabel
+%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function
+%24 = OpLoad %int %in_var_INDEX
+%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0
+%26 = OpLoad %_arr_v4float_uint_8 %25
+%27 = OpCompositeExtract %v4float %26 0
+%28 = OpCompositeExtract %v4float %26 1
+%29 = OpCompositeExtract %v4float %26 2
+%30 = OpCompositeExtract %v4float %26 3
+%31 = OpCompositeExtract %v4float %26 4
+%32 = OpCompositeExtract %v4float %26 5
+%33 = OpCompositeExtract %v4float %26 6
+%34 = OpCompositeExtract %v4float %26 7
+%35 = OpCompositeConstruct %_arr_v4float_uint_8_0 %27 %28 %29 %30 %31 %32 %33 %34
+OpStore %23 %35
+%decl = OpExtInst %void %ext DebugDeclare %dbg_f %23 %null_expr
+%36 = OpAccessChain %_ptr_Function_v4float %23 %24
+%37 = OpLoad %v4float %36
+OpStore %out_var_SV_Target %37
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+                        SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+  SinglePassRunAndMatch<CopyPropagateArrays>(before, false);
+}
+
+TEST_F(CopyPropArrayPassTest, DebugValue) {
+  const std::string before =
+      R"(OpCapability Shader
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 600
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+OpName %type_MyCBuffer "type.MyCBuffer"
+OpMemberName %type_MyCBuffer 0 "Data"
+OpName %MyCBuffer "MyCBuffer"
+OpName %main "main"
+OpName %in_var_INDEX "in.var.INDEX"
+OpName %out_var_SV_Target "out.var.SV_Target"
+OpDecorate %_arr_v4float_uint_8 ArrayStride 16
+OpMemberDecorate %type_MyCBuffer 0 Offset 0
+OpDecorate %type_MyCBuffer Block
+OpDecorate %in_var_INDEX Flat
+OpDecorate %in_var_INDEX Location 0
+OpDecorate %out_var_SV_Target Location 0
+OpDecorate %MyCBuffer DescriptorSet 0
+OpDecorate %MyCBuffer Binding 0
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%uint = OpTypeInt 32 0
+%uint_8 = OpConstant %uint 8
+%uint_32 = OpConstant %uint 32
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8
+%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer
+%void = OpTypeVoid
+%13 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8
+%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0
+%int_0 = OpConstant %int 0
+%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform
+%in_var_INDEX = OpVariable %_ptr_Input_int Input
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+
+; CHECK: [[deref:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugOperation Deref
+; CHECK: [[deref_expr:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref]]
+%deref = OpExtInst %void %ext DebugOperation Deref
+%expr = OpExtInst %void %ext DebugExpression %deref
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+
+; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+%main = OpFunction %void None %13
+%22 = OpLabel
+%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function
+%24 = OpLoad %int %in_var_INDEX
+%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0
+%26 = OpLoad %_arr_v4float_uint_8 %25
+%27 = OpCompositeExtract %v4float %26 0
+%28 = OpCompositeExtract %v4float %26 1
+%29 = OpCompositeExtract %v4float %26 2
+%30 = OpCompositeExtract %v4float %26 3
+%31 = OpCompositeExtract %v4float %26 4
+%32 = OpCompositeExtract %v4float %26 5
+%33 = OpCompositeExtract %v4float %26 6
+%34 = OpCompositeExtract %v4float %26 7
+%35 = OpCompositeConstruct %_arr_v4float_uint_8_0 %27 %28 %29 %30 %31 %32 %33 %34
+OpStore %23 %35
+
+; CHECK: OpAccessChain
+; CHECK: [[newptr:%\w+]] = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0
+; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[newptr]] [[deref_expr]]
+; CHECK: [[element_ptr:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[newptr]] %24
+; CHECK: [[load:%\w+]] = OpLoad %v4float [[element_ptr]]
+; CHECK: OpStore %out_var_SV_Target [[load]]
+%decl = OpExtInst %void %ext DebugValue %dbg_f %23 %expr
+%36 = OpAccessChain %_ptr_Function_v4float %23 %24
+%37 = OpLoad %v4float %36
+OpStore %out_var_SV_Target %37
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+                        SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+  SinglePassRunAndMatch<CopyPropagateArrays>(before, false);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
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 3dcc0f7..3abb53d 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
@@ -186,6 +186,81 @@
                                             true, true);
 }
 
+TEST_F(DeadBranchElimTest, IfThenElseNull) {
+  // For booleans OpConstantNull should be treated similar to OpConstantFalse.
+  //
+  // From the SPIR-V spec:
+  // OpConstantNull: Declares a new null constant value.
+  // The null value is type dependent, defined as follows:
+  // - Scalar Boolean: false
+  // ...
+
+  const std::string predefs =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %v "v"
+OpName %gl_FragColor "gl_FragColor"
+OpName %BaseColor "BaseColor"
+%void = OpTypeVoid
+%7 = OpTypeFunction %void
+%bool = OpTypeBool
+%9 = OpConstantNull %bool
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%float_0 = OpConstant %float 0
+%14 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%float_1 = OpConstant %float 1
+%16 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+)";
+
+  const std::string before =
+      R"(%main = OpFunction %void None %7
+%19 = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+OpSelectionMerge %20 None
+OpBranchConditional %9 %21 %22
+%21 = OpLabel
+OpStore %v %14
+OpBranch %20
+%22 = OpLabel
+OpStore %v %16
+OpBranch %20
+%20 = OpLabel
+%23 = OpLoad %v4float %v
+OpStore %gl_FragColor %23
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(%main = OpFunction %void None %7
+%19 = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+OpBranch %22
+%22 = OpLabel
+OpStore %v %16
+OpBranch %20
+%20 = OpLabel
+%23 = OpLoad %v4float %v
+OpStore %gl_FragColor %23
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndCheck<DeadBranchElimPass>(predefs + before, predefs + after,
+                                            true, true);
+}
+
 TEST_F(DeadBranchElimTest, IfThenTrue) {
   // #version 140
   //
@@ -3229,6 +3304,101 @@
   SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
 }
 
+TEST_F(DeadBranchElimTest, DebugInformation) {
+  const std::string text = R"(
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+%name = OpString "test"
+OpName %main "main"
+OpName %gl_FragColor "gl_FragColor"
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%float_0 = OpConstant %float 0
+
+; CHECK: [[value:%\w+]] = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%12 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%float_1 = OpConstant %float 1
+%14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void
+%dbg_main = OpExtInst %void %ext DebugFunction %name %ty %src 0 0 %cu %name FlagIsProtected|FlagIsPrivate 0 %main
+
+; CHECK: [[bb1:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugLexicalBlock [[src:%\w+]] 1 0 [[dbg_main:%\w+]]
+; CHECK: [[bb2:%\w+]] = OpExtInst %void [[ext]] DebugLexicalBlock [[src]] 2 0 [[dbg_main]]
+; CHECK: [[bb3:%\w+]] = OpExtInst %void [[ext]] DebugLexicalBlock [[src]] 3 0 [[dbg_main]]
+%bb1 = OpExtInst %void %ext DebugLexicalBlock %src 1 0 %dbg_main
+%bb2 = OpExtInst %void %ext DebugLexicalBlock %src 2 0 %dbg_main
+%bb3 = OpExtInst %void %ext DebugLexicalBlock %src 3 0 %dbg_main
+
+%dbg_f = OpExtInst %void %ext DebugTypeBasic %name %uint_32 Float
+; CHECK: [[dbg_foo:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable {{%\w+}} [[ty:%\w+]] [[src]] 0 0 [[dbg_main]]
+%dbg_foo = OpExtInst %void %ext DebugLocalVariable %name %dbg_f %src 0 0 %dbg_main FlagIsLocal
+; CHECK: [[dbg_bar:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable {{%\w+}} [[ty]] [[src]] 1 0 [[bb3]]
+%dbg_bar = OpExtInst %void %ext DebugLocalVariable %name %dbg_f %src 1 0 %bb3 FlagIsLocal
+
+%main = OpFunction %void None %5
+%17 = OpLabel
+; CHECK-NOT: DebugScope [[dbg_main]]
+; CHECK-NOT: OpLine {{%\w+}} 0 0
+%scope0 = OpExtInst %void %ext DebugScope %dbg_main
+OpLine %name 0 0
+OpSelectionMerge %18 None
+OpBranchConditional %true %19 %20
+%19 = OpLabel
+; CHECK: DebugScope [[bb1]]
+; CHECK: OpLine {{%\w+}} 1 0
+%scope1 = OpExtInst %void %ext DebugScope %bb1
+OpLine %name 1 0
+OpBranch %18
+%20 = OpLabel
+; CHECK-NOT: DebugScope [[bb2]]
+; CHECK-NOT: OpLine {{%\w+}} 2 0
+%scope2 = OpExtInst %void %ext DebugScope %bb2
+OpLine %name 2 0
+OpBranch %18
+%18 = OpLabel
+
+; CHECK: DebugScope [[bb3]]
+; CHECK-NOT: OpLine {{%\w+}} 3 0
+; CHECK: DebugValue [[dbg_foo]] [[value]]
+; CHECK: OpLine {{%\w+}} 4 0
+; CHECK: OpStore %gl_FragColor [[value]]
+; CHECK: DebugDeclare [[dbg_bar]] %gl_FragColor
+; CHECK: DebugValue [[dbg_bar]] [[value]]
+%scope3 = OpExtInst %void %ext DebugScope %bb3
+OpLine %name 3 0
+%21 = OpPhi %v4float %12 %19 %14 %20
+%decl0 = OpExtInst %void %ext DebugValue %dbg_foo %21 %null_expr
+OpLine %name 4 0
+OpStore %gl_FragColor %21
+%decl1 = OpExtInst %void %ext DebugDeclare %dbg_bar %gl_FragColor %null_expr
+%decl2 = OpExtInst %void %ext DebugValue %dbg_bar %21 %null_expr
+OpLine %name 5 0
+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/debug_info_manager_test.cpp b/third_party/SPIRV-Tools/test/opt/debug_info_manager_test.cpp
index f19737f..82890f7 100644
--- a/third_party/SPIRV-Tools/test/opt/debug_info_manager_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/debug_info_manager_test.cpp
@@ -431,6 +431,74 @@
   EXPECT_EQ(inst, before_100);
 }
 
+TEST(DebugInfoManager, KillDebugDeclares) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %in_var_COLOR
+               OpExecutionMode %main OriginUpperLeft
+          %5 = OpString "ps.hlsl"
+         %14 = OpString "#line 1 \"ps.hlsl\"
+void main(float in_var_color : COLOR) {
+  float color = in_var_color;
+}
+"
+         %17 = OpString "float"
+         %21 = OpString "main"
+         %24 = OpString "color"
+               OpName %in_var_COLOR "in.var.COLOR"
+               OpName %main "main"
+               OpDecorate %in_var_COLOR Location 0
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+      %float = OpTypeFloat 32
+%_ptr_Input_float = OpTypePointer Input %float
+       %void = OpTypeVoid
+         %27 = OpTypeFunction %void
+%_ptr_Function_float = OpTypePointer Function %float
+%in_var_COLOR = OpVariable %_ptr_Input_float Input
+         %13 = OpExtInst %void %1 DebugExpression
+         %15 = OpExtInst %void %1 DebugSource %5 %14
+         %16 = OpExtInst %void %1 DebugCompilationUnit 1 4 %15 HLSL
+         %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 Float
+         %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18
+         %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %main
+         %12 = OpExtInst %void %1 DebugInfoNone
+         %25 = OpExtInst %void %1 DebugLocalVariable %24 %18 %15 1 20 %22 FlagIsLocal 0
+       %main = OpFunction %void None %27
+         %28 = OpLabel
+        %100 = OpVariable %_ptr_Function_float Function
+         %31 = OpLoad %float %in_var_COLOR
+               OpStore %100 %31
+         %36 = OpExtInst %void %1 DebugDeclare %25 %100 %13
+         %37 = OpExtInst %void %1 DebugDeclare %25 %100 %13
+         %38 = OpExtInst %void %1 DebugDeclare %25 %100 %13
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  auto* dbg_info_mgr = context->get_debug_info_mgr();
+  auto* def_use_mgr = context->get_def_use_mgr();
+
+  EXPECT_TRUE(dbg_info_mgr->IsDebugDeclared(100));
+  EXPECT_EQ(def_use_mgr->GetDef(36)->GetOpenCL100DebugOpcode(),
+            OpenCLDebugInfo100DebugDeclare);
+  EXPECT_EQ(def_use_mgr->GetDef(37)->GetOpenCL100DebugOpcode(),
+            OpenCLDebugInfo100DebugDeclare);
+  EXPECT_EQ(def_use_mgr->GetDef(38)->GetOpenCL100DebugOpcode(),
+            OpenCLDebugInfo100DebugDeclare);
+
+  dbg_info_mgr->KillDebugDeclares(100);
+  EXPECT_EQ(def_use_mgr->GetDef(36), nullptr);
+  EXPECT_EQ(def_use_mgr->GetDef(37), nullptr);
+  EXPECT_EQ(def_use_mgr->GetDef(38), nullptr);
+  EXPECT_FALSE(dbg_info_mgr->IsDebugDeclared(100));
+}
+
 }  // namespace
 }  // namespace analysis
 }  // namespace opt
diff --git a/third_party/SPIRV-Tools/test/opt/desc_sroa_test.cpp b/third_party/SPIRV-Tools/test/opt/desc_sroa_test.cpp
index 11074c3..cdcc9a8 100644
--- a/third_party/SPIRV-Tools/test/opt/desc_sroa_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/desc_sroa_test.cpp
@@ -25,7 +25,116 @@
 
 using DescriptorScalarReplacementTest = PassTest<::testing::Test>;
 
-TEST_F(DescriptorScalarReplacementTest, ExpandTexture) {
+std::string GetStructureArrayTestSpirv() {
+  // The SPIR-V for the following high-level shader:
+  // Flattening structures and arrays should result in the following binding
+  // numbers. Only the ones that are actually used in the shader should be in
+  // the final SPIR-V.
+  //
+  // globalS[0][0].t[0]  0 (used)
+  // globalS[0][0].t[1]  1
+  // globalS[0][0].s[0]  2 (used)
+  // globalS[0][0].s[1]  3
+  // globalS[0][1].t[0]  4
+  // globalS[0][1].t[1]  5
+  // globalS[0][1].s[0]  6
+  // globalS[0][1].s[1]  7
+  // globalS[1][0].t[0]  8
+  // globalS[1][0].t[1]  9
+  // globalS[1][0].s[0]  10
+  // globalS[1][0].s[1]  11
+  // globalS[1][1].t[0]  12
+  // globalS[1][1].t[1]  13 (used)
+  // globalS[1][1].s[0]  14
+  // globalS[1][1].s[1]  15 (used)
+
+  /*
+    struct S {
+      Texture2D t[2];
+      SamplerState s[2];
+    };
+
+    S globalS[2][2];
+
+    float4 main() : SV_Target {
+      return globalS[0][0].t[0].Sample(globalS[0][0].s[0], float2(0,0)) +
+             globalS[1][1].t[1].Sample(globalS[1][1].s[1], float2(0,0));
+    }
+  */
+
+  return R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %out_var_SV_Target
+               OpExecutionMode %main OriginUpperLeft
+               OpName %S "S"
+               OpMemberName %S 0 "t"
+               OpMemberName %S 1 "s"
+               OpName %type_2d_image "type.2d.image"
+               OpName %type_sampler "type.sampler"
+               OpName %globalS "globalS"
+               OpName %out_var_SV_Target "out.var.SV_Target"
+               OpName %main "main"
+               OpName %src_main "src.main"
+               OpName %bb_entry "bb.entry"
+               OpName %type_sampled_image "type.sampled.image"
+               OpDecorate %out_var_SV_Target Location 0
+               OpDecorate %globalS DescriptorSet 0
+               OpDecorate %globalS Binding 0
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+      %float = OpTypeFloat 32
+    %float_0 = OpConstant %float 0
+    %v2float = OpTypeVector %float 2
+         %10 = OpConstantComposite %v2float %float_0 %float_0
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_arr_type_2d_image_uint_2 = OpTypeArray %type_2d_image %uint_2
+%type_sampler = OpTypeSampler
+%_arr_type_sampler_uint_2 = OpTypeArray %type_sampler %uint_2
+          %S = OpTypeStruct %_arr_type_2d_image_uint_2 %_arr_type_sampler_uint_2
+%_arr_S_uint_2 = OpTypeArray %S %uint_2
+%_arr__arr_S_uint_2_uint_2 = OpTypeArray %_arr_S_uint_2 %uint_2
+%_ptr_UniformConstant__arr__arr_S_uint_2_uint_2 = OpTypePointer UniformConstant %_arr__arr_S_uint_2_uint_2
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+         %24 = OpTypeFunction %void
+         %28 = OpTypeFunction %v4float
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+    %globalS = OpVariable %_ptr_UniformConstant__arr__arr_S_uint_2_uint_2 UniformConstant
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+       %main = OpFunction %void None %24
+         %25 = OpLabel
+         %26 = OpFunctionCall %v4float %src_main
+               OpStore %out_var_SV_Target %26
+               OpReturn
+               OpFunctionEnd
+   %src_main = OpFunction %v4float None %28
+   %bb_entry = OpLabel
+         %31 = OpAccessChain %_ptr_UniformConstant_type_2d_image %globalS %int_0 %int_0 %int_0 %int_0
+         %32 = OpLoad %type_2d_image %31
+         %34 = OpAccessChain %_ptr_UniformConstant_type_sampler %globalS %int_0 %int_0 %int_1 %int_0
+         %35 = OpLoad %type_sampler %34
+         %37 = OpSampledImage %type_sampled_image %32 %35
+         %38 = OpImageSampleImplicitLod %v4float %37 %10 None
+         %39 = OpAccessChain %_ptr_UniformConstant_type_2d_image %globalS %int_1 %int_1 %int_0 %int_1
+         %40 = OpLoad %type_2d_image %39
+         %41 = OpAccessChain %_ptr_UniformConstant_type_sampler %globalS %int_1 %int_1 %int_1 %int_1
+         %42 = OpLoad %type_sampler %41
+         %43 = OpSampledImage %type_sampled_image %40 %42
+         %44 = OpImageSampleImplicitLod %v4float %43 %10 None
+         %45 = OpFAdd %v4float %38 %44
+               OpReturnValue %45
+               OpFunctionEnd
+  )";
+}
+
+TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfTextures) {
   const std::string text = R"(
 ; CHECK: OpDecorate [[var1:%\w+]] DescriptorSet 0
 ; CHECK: OpDecorate [[var1]] Binding 0
@@ -94,7 +203,7 @@
   SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
 }
 
-TEST_F(DescriptorScalarReplacementTest, ExpandSampler) {
+TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfSamplers) {
   const std::string text = R"(
 ; CHECK: OpDecorate [[var1:%\w+]] DescriptorSet 0
 ; CHECK: OpDecorate [[var1]] Binding 1
@@ -145,7 +254,7 @@
   SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
 }
 
-TEST_F(DescriptorScalarReplacementTest, ExpandSSBO) {
+TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfSSBOs) {
   // Tests the expansion of an SSBO.  Also check that an access chain with more
   // than 1 index is correctly handled.
   const std::string text = R"(
@@ -265,6 +374,361 @@
 
   SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
 }
+
+TEST_F(DescriptorScalarReplacementTest, DontExpandCBuffers) {
+  // Checks that constant buffers are not expanded.
+  // Constant buffers are represented as global structures, but they should not
+  // be replaced with new variables for their elements.
+  /*
+    cbuffer MyCbuffer : register(b1) {
+      float2    a;
+      float2   b;
+    };
+    float main() : A {
+      return a.x + b.y;
+    }
+  */
+  const std::string text = R"(
+; CHECK: OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_0 %int_0
+; CHECK: OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_1 %int_1
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %out_var_A
+               OpSource HLSL 600
+               OpName %type_MyCbuffer "type.MyCbuffer"
+               OpMemberName %type_MyCbuffer 0 "a"
+               OpMemberName %type_MyCbuffer 1 "b"
+               OpName %MyCbuffer "MyCbuffer"
+               OpName %out_var_A "out.var.A"
+               OpName %main "main"
+               OpDecorate %out_var_A Location 0
+               OpDecorate %MyCbuffer DescriptorSet 0
+               OpDecorate %MyCbuffer Binding 1
+               OpMemberDecorate %type_MyCbuffer 0 Offset 0
+               OpMemberDecorate %type_MyCbuffer 1 Offset 8
+               OpDecorate %type_MyCbuffer Block
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+%type_MyCbuffer = OpTypeStruct %v2float %v2float
+%_ptr_Uniform_type_MyCbuffer = OpTypePointer Uniform %type_MyCbuffer
+%_ptr_Output_float = OpTypePointer Output %float
+       %void = OpTypeVoid
+         %13 = OpTypeFunction %void
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+  %MyCbuffer = OpVariable %_ptr_Uniform_type_MyCbuffer Uniform
+  %out_var_A = OpVariable %_ptr_Output_float Output
+       %main = OpFunction %void None %13
+         %15 = OpLabel
+         %16 = OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_0 %int_0
+         %17 = OpLoad %float %16
+         %18 = OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_1 %int_1
+         %19 = OpLoad %float %18
+         %20 = OpFAdd %float %17 %19
+               OpStore %out_var_A %20
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
+}
+
+TEST_F(DescriptorScalarReplacementTest, DontExpandStructuredBuffers) {
+  // Checks that structured buffers are not expanded.
+  // Structured buffers are represented as global structures, that have one
+  // member which is a runtime array.
+  /*
+    struct S {
+      float2   a;
+      float2   b;
+    };
+    RWStructuredBuffer<S> sb;
+    float main() : A {
+      return sb[0].a.x + sb[0].b.x;
+    }
+  */
+  const std::string text = R"(
+; CHECK: OpAccessChain %_ptr_Uniform_float %sb %int_0 %uint_0 %int_0 %int_0
+; CHECK: OpAccessChain %_ptr_Uniform_float %sb %int_0 %uint_0 %int_1 %int_0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %out_var_A
+               OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S"
+               OpName %S "S"
+               OpMemberName %S 0 "a"
+               OpMemberName %S 1 "b"
+               OpName %sb "sb"
+               OpName %out_var_A "out.var.A"
+               OpName %main "main"
+               OpDecorate %out_var_A Location 0
+               OpDecorate %sb DescriptorSet 0
+               OpDecorate %sb Binding 0
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 8
+               OpDecorate %_runtimearr_S ArrayStride 16
+               OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0
+               OpDecorate %type_RWStructuredBuffer_S BufferBlock
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+          %S = OpTypeStruct %v2float %v2float
+%_runtimearr_S = OpTypeRuntimeArray %S
+%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S
+%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S
+%_ptr_Output_float = OpTypePointer Output %float
+       %void = OpTypeVoid
+         %17 = OpTypeFunction %void
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+         %sb = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform
+  %out_var_A = OpVariable %_ptr_Output_float Output
+       %main = OpFunction %void None %17
+         %19 = OpLabel
+         %20 = OpAccessChain %_ptr_Uniform_float %sb %int_0 %uint_0 %int_0 %int_0
+         %21 = OpLoad %float %20
+         %22 = OpAccessChain %_ptr_Uniform_float %sb %int_0 %uint_0 %int_1 %int_0
+         %23 = OpLoad %float %22
+         %24 = OpFAdd %float %21 %23
+               OpStore %out_var_A %24
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
+}
+
+TEST_F(DescriptorScalarReplacementTest, StructureArrayNames) {
+  // Checks that names are properly generated for multi-dimension arrays and
+  // structure members.
+  const std::string checks = R"(
+; CHECK: OpName %globalS_0__0__t_0_ "globalS[0][0].t[0]"
+; CHECK: OpName %globalS_0__0__s_0_ "globalS[0][0].s[0]"
+; CHECK: OpName %globalS_1__1__t_1_ "globalS[1][1].t[1]"
+; CHECK: OpName %globalS_1__1__s_1_ "globalS[1][1].s[1]"
+  )";
+
+  const std::string text = checks + GetStructureArrayTestSpirv();
+  SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
+}
+
+TEST_F(DescriptorScalarReplacementTest, StructureArrayBindings) {
+  // Checks that flattening structures and arrays results in correct binding
+  // numbers.
+  const std::string checks = R"(
+; CHECK: OpDecorate %globalS_0__0__t_0_ Binding 0
+; CHECK: OpDecorate %globalS_0__0__s_0_ Binding 2
+; CHECK: OpDecorate %globalS_1__1__t_1_ Binding 13
+; CHECK: OpDecorate %globalS_1__1__s_1_ Binding 15
+  )";
+
+  const std::string text = checks + GetStructureArrayTestSpirv();
+  SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
+}
+
+TEST_F(DescriptorScalarReplacementTest, StructureArrayReplacements) {
+  // Checks that all access chains indexing into structures and/or arrays are
+  // replaced with direct access to replacement variables.
+  const std::string checks = R"(
+; CHECK-NOT: OpAccessChain
+; CHECK: OpLoad %type_2d_image %globalS_0__0__t_0_
+; CHECK: OpLoad %type_sampler %globalS_0__0__s_0_
+; CHECK: OpLoad %type_2d_image %globalS_1__1__t_1_
+; CHECK: OpLoad %type_sampler %globalS_1__1__s_1_
+  )";
+
+  const std::string text = checks + GetStructureArrayTestSpirv();
+  SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
+}
+
+TEST_F(DescriptorScalarReplacementTest, ResourceStructAsFunctionParam) {
+  // Checks that a mix of OpAccessChain, OpLoad, and OpCompositeExtract patterns
+  // can be properly replaced with replacement variables.
+  // This pattern can be seen when a global structure of resources is passed to
+  // a function.
+
+  /* High-level source:
+  // globalS[0].t[0]        binding: 0  (used)
+  // globalS[0].t[1]        binding: 1  (used)
+  // globalS[0].tt[0].s[0]  binding: 2
+  // globalS[0].tt[0].s[1]  binding: 3  (used)
+  // globalS[0].tt[0].s[2]  binding: 4
+  // globalS[0].tt[1].s[0]  binding: 5
+  // globalS[0].tt[1].s[1]  binding: 6
+  // globalS[0].tt[1].s[2]  binding: 7  (used)
+  // globalS[1].t[0]        binding: 8  (used)
+  // globalS[1].t[1]        binding: 9  (used)
+  // globalS[1].tt[0].s[0]  binding: 10
+  // globalS[1].tt[0].s[1]  binding: 11 (used)
+  // globalS[1].tt[0].s[2]  binding: 12
+  // globalS[1].tt[1].s[0]  binding: 13
+  // globalS[1].tt[1].s[1]  binding: 14
+  // globalS[1].tt[1].s[2]  binding: 15 (used)
+
+  struct T {
+    SamplerState s[3];
+  };
+
+  struct S {
+    Texture2D t[2];
+    T tt[2];
+  };
+
+  float4 tex2D(S x, float2 v) {
+    return x.t[0].Sample(x.tt[0].s[1], v) + x.t[1].Sample(x.tt[1].s[2], v);
+  }
+
+  S globalS[2];
+
+  float4 main() : SV_Target {
+    return tex2D(globalS[0], float2(0,0)) + tex2D(globalS[1], float2(0,0)) ;
+  }
+  */
+  const std::string shader = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %out_var_SV_Target
+               OpExecutionMode %main OriginUpperLeft
+               OpName %S "S"
+               OpMemberName %S 0 "t"
+               OpMemberName %S 1 "tt"
+               OpName %type_2d_image "type.2d.image"
+               OpName %T "T"
+               OpMemberName %T 0 "s"
+               OpName %type_sampler "type.sampler"
+               OpName %globalS "globalS"
+               OpName %out_var_SV_Target "out.var.SV_Target"
+               OpName %main "main"
+               OpName %type_sampled_image "type.sampled.image"
+               OpDecorate %out_var_SV_Target Location 0
+               OpDecorate %globalS DescriptorSet 0
+               OpDecorate %globalS Binding 0
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %float = OpTypeFloat 32
+    %float_0 = OpConstant %float 0
+    %v2float = OpTypeVector %float 2
+         %14 = OpConstantComposite %v2float %float_0 %float_0
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_arr_type_2d_image_uint_2 = OpTypeArray %type_2d_image %uint_2
+     %uint_3 = OpConstant %uint 3
+%type_sampler = OpTypeSampler
+%_arr_type_sampler_uint_3 = OpTypeArray %type_sampler %uint_3
+          %T = OpTypeStruct %_arr_type_sampler_uint_3
+%_arr_T_uint_2 = OpTypeArray %T %uint_2
+          %S = OpTypeStruct %_arr_type_2d_image_uint_2 %_arr_T_uint_2
+%_arr_S_uint_2 = OpTypeArray %S %uint_2
+%_ptr_UniformConstant__arr_S_uint_2 = OpTypePointer UniformConstant %_arr_S_uint_2
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+         %27 = OpTypeFunction %void
+%_ptr_UniformConstant_S = OpTypePointer UniformConstant %S
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+    %globalS = OpVariable %_ptr_UniformConstant__arr_S_uint_2 UniformConstant
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+       %main = OpFunction %void None %27
+         %29 = OpLabel
+         %30 = OpAccessChain %_ptr_UniformConstant_S %globalS %int_0
+         %31 = OpLoad %S %30
+         %32 = OpCompositeExtract %_arr_type_2d_image_uint_2 %31 0
+         %33 = OpCompositeExtract %type_2d_image %32 0
+         %34 = OpCompositeExtract %type_2d_image %32 1
+         %35 = OpCompositeExtract %_arr_T_uint_2 %31 1
+         %36 = OpCompositeExtract %T %35 0
+         %37 = OpCompositeExtract %_arr_type_sampler_uint_3 %36 0
+         %38 = OpCompositeExtract %type_sampler %37 1
+         %39 = OpCompositeExtract %T %35 1
+         %40 = OpCompositeExtract %_arr_type_sampler_uint_3 %39 0
+         %41 = OpCompositeExtract %type_sampler %40 2
+         %42 = OpSampledImage %type_sampled_image %33 %38
+         %43 = OpImageSampleImplicitLod %v4float %42 %14 None
+         %44 = OpSampledImage %type_sampled_image %34 %41
+         %45 = OpImageSampleImplicitLod %v4float %44 %14 None
+         %46 = OpFAdd %v4float %43 %45
+         %47 = OpAccessChain %_ptr_UniformConstant_S %globalS %int_1
+         %48 = OpLoad %S %47
+         %49 = OpCompositeExtract %_arr_type_2d_image_uint_2 %48 0
+         %50 = OpCompositeExtract %type_2d_image %49 0
+         %51 = OpCompositeExtract %type_2d_image %49 1
+         %52 = OpCompositeExtract %_arr_T_uint_2 %48 1
+         %53 = OpCompositeExtract %T %52 0
+         %54 = OpCompositeExtract %_arr_type_sampler_uint_3 %53 0
+         %55 = OpCompositeExtract %type_sampler %54 1
+         %56 = OpCompositeExtract %T %52 1
+         %57 = OpCompositeExtract %_arr_type_sampler_uint_3 %56 0
+         %58 = OpCompositeExtract %type_sampler %57 2
+         %59 = OpSampledImage %type_sampled_image %50 %55
+         %60 = OpImageSampleImplicitLod %v4float %59 %14 None
+         %61 = OpSampledImage %type_sampled_image %51 %58
+         %62 = OpImageSampleImplicitLod %v4float %61 %14 None
+         %63 = OpFAdd %v4float %60 %62
+         %64 = OpFAdd %v4float %46 %63
+               OpStore %out_var_SV_Target %64
+               OpReturn
+               OpFunctionEnd
+)";
+
+  const std::string checks = R"(
+; CHECK: OpName %globalS_0__t_0_ "globalS[0].t[0]"
+; CHECK: OpName %globalS_0__t_1_ "globalS[0].t[1]"
+; CHECK: OpName %globalS_1__t_0_ "globalS[1].t[0]"
+; CHECK: OpName %globalS_1__t_1_ "globalS[1].t[1]"
+; CHECK: OpName %globalS_0__tt_0__s_1_ "globalS[0].tt[0].s[1]"
+; CHECK: OpName %globalS_0__tt_1__s_2_ "globalS[0].tt[1].s[2]"
+; CHECK: OpName %globalS_1__tt_0__s_1_ "globalS[1].tt[0].s[1]"
+; CHECK: OpName %globalS_1__tt_1__s_2_ "globalS[1].tt[1].s[2]"
+; CHECK: OpDecorate %globalS_0__t_0_ Binding 0
+; CHECK: OpDecorate %globalS_0__t_1_ Binding 1
+; CHECK: OpDecorate %globalS_1__t_0_ Binding 8
+; CHECK: OpDecorate %globalS_1__t_1_ Binding 9
+; CHECK: OpDecorate %globalS_0__tt_0__s_1_ Binding 3
+; CHECK: OpDecorate %globalS_0__tt_1__s_2_ Binding 7
+; CHECK: OpDecorate %globalS_1__tt_0__s_1_ Binding 11
+; CHECK: OpDecorate %globalS_1__tt_1__s_2_ Binding 15
+
+; CHECK: %globalS_0__t_0_ = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+; CHECK: %globalS_0__t_1_ = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+; CHECK: %globalS_1__t_0_ = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+; CHECK: %globalS_1__t_1_ = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+; CHECK: %globalS_0__tt_0__s_1_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+; CHECK: %globalS_0__tt_1__s_2_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+; CHECK: %globalS_1__tt_0__s_1_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+; CHECK: %globalS_1__tt_1__s_2_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+
+; CHECK:     [[img_1:%\w+]] = OpLoad %type_2d_image %globalS_0__t_0_
+; CHECK:     [[img_2:%\w+]] = OpLoad %type_2d_image %globalS_0__t_1_
+; CHECK: [[sampler_1:%\w+]] = OpLoad %type_sampler %globalS_0__tt_0__s_1_
+; CHECK: [[sampler_2:%\w+]] = OpLoad %type_sampler %globalS_0__tt_1__s_2_
+
+; CHECK: [[sampled_img_1:%\w+]] = OpSampledImage %type_sampled_image [[img_1]] [[sampler_1]]
+; CHECK:      [[sample_1:%\w+]] = OpImageSampleImplicitLod %v4float [[sampled_img_1]]
+; CHECK: [[sampled_img_2:%\w+]] = OpSampledImage %type_sampled_image [[img_2]] [[sampler_2]]
+; CHECK:      [[sample_2:%\w+]] = OpImageSampleImplicitLod %v4float [[sampled_img_2]]
+; CHECK:                          OpFAdd %v4float [[sample_1]] [[sample_2]]
+
+; CHECK:     [[img_3:%\w+]] = OpLoad %type_2d_image %globalS_1__t_0_
+; CHECK:     [[img_4:%\w+]] = OpLoad %type_2d_image %globalS_1__t_1_
+; CHECK: [[sampler_3:%\w+]] = OpLoad %type_sampler %globalS_1__tt_0__s_1_
+; CHECK: [[sampler_4:%\w+]] = OpLoad %type_sampler %globalS_1__tt_1__s_2_
+
+; CHECK: [[sampled_img_3:%\w+]] = OpSampledImage %type_sampled_image [[img_3]] [[sampler_3]]
+; CHECK:      [[sample_3:%\w+]] = OpImageSampleImplicitLod %v4float [[sampled_img_3]]
+; CHECK: [[sampled_img_4:%\w+]] = OpSampledImage %type_sampled_image [[img_4]] [[sampler_4]]
+; CHECK:      [[sample_4:%\w+]] = OpImageSampleImplicitLod %v4float [[sampled_img_4]]
+; CHECK:                          OpFAdd %v4float [[sample_3]] [[sample_4]]
+)";
+
+  SinglePassRunAndMatch<DescriptorScalarReplacement>(checks + shader, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/dominator_tree/generated.cpp b/third_party/SPIRV-Tools/test/opt/dominator_tree/generated.cpp
index 43b723e..534f770 100644
--- a/third_party/SPIRV-Tools/test/opt/dominator_tree/generated.cpp
+++ b/third_party/SPIRV-Tools/test/opt/dominator_tree/generated.cpp
@@ -895,6 +895,126 @@
   }
 }
 
+TEST_F(PassClassTest, DominationForInstructions) {
+  const std::string text = 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 = OpTypeBool
+          %8 = OpConstantTrue %7
+          %9 = OpConstant %6 37
+         %10 = OpConstant %6 3
+         %13 = OpConstant %6 5
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %12 = OpIAdd %6 %9 %10
+         %15 = OpISub %6 %12 %13
+               OpSelectionMerge %18 None
+               OpBranchConditional %8 %16 %17
+         %16 = OpLabel
+         %20 = OpISub %6 %12 %13
+               OpBranch %18
+         %17 = OpLabel
+         %21 = OpISub %6 %12 %13
+               OpBranch %18
+         %18 = OpLabel
+         %22 = OpISub %6 %12 %13
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  // clang-format on
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  EXPECT_NE(nullptr, context->module()) << "Assembling failed for shader:\n"
+                                        << text << std::endl;
+
+  {
+    const DominatorAnalysis* dominator_analysis = context->GetDominatorAnalysis(
+        spvtest::GetFunction(context->module(), 4));
+
+    EXPECT_TRUE(
+        dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(12),
+                                      context->get_def_use_mgr()->GetDef(15)));
+    EXPECT_FALSE(
+        dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15),
+                                      context->get_def_use_mgr()->GetDef(12)));
+    EXPECT_TRUE(
+        dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(5),
+                                      context->get_def_use_mgr()->GetDef(12)));
+    EXPECT_FALSE(
+        dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(12),
+                                      context->get_def_use_mgr()->GetDef(5)));
+    EXPECT_TRUE(
+        dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15),
+                                      context->get_def_use_mgr()->GetDef(16)));
+    EXPECT_TRUE(
+        dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15),
+                                      context->get_def_use_mgr()->GetDef(21)));
+    EXPECT_TRUE(
+        dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15),
+                                      context->get_def_use_mgr()->GetDef(18)));
+    EXPECT_TRUE(
+        dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15),
+                                      context->get_def_use_mgr()->GetDef(22)));
+    EXPECT_FALSE(
+        dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(20),
+                                      context->get_def_use_mgr()->GetDef(22)));
+    EXPECT_FALSE(
+        dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(21),
+                                      context->get_def_use_mgr()->GetDef(22)));
+    EXPECT_TRUE(
+        dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15),
+                                      context->get_def_use_mgr()->GetDef(15)));
+  }
+  {
+    const PostDominatorAnalysis* post_dominator_analysis =
+        context->GetPostDominatorAnalysis(
+            spvtest::GetFunction(context->module(), 4));
+
+    EXPECT_TRUE(post_dominator_analysis->Dominates(
+        context->get_def_use_mgr()->GetDef(15),
+        context->get_def_use_mgr()->GetDef(12)));
+    EXPECT_FALSE(post_dominator_analysis->Dominates(
+        context->get_def_use_mgr()->GetDef(12),
+        context->get_def_use_mgr()->GetDef(15)));
+    EXPECT_TRUE(post_dominator_analysis->Dominates(
+        context->get_def_use_mgr()->GetDef(12),
+        context->get_def_use_mgr()->GetDef(5)));
+    EXPECT_FALSE(post_dominator_analysis->Dominates(
+        context->get_def_use_mgr()->GetDef(5),
+        context->get_def_use_mgr()->GetDef(12)));
+    EXPECT_FALSE(post_dominator_analysis->Dominates(
+        context->get_def_use_mgr()->GetDef(16),
+        context->get_def_use_mgr()->GetDef(15)));
+    EXPECT_FALSE(post_dominator_analysis->Dominates(
+        context->get_def_use_mgr()->GetDef(21),
+        context->get_def_use_mgr()->GetDef(15)));
+    EXPECT_TRUE(post_dominator_analysis->Dominates(
+        context->get_def_use_mgr()->GetDef(18),
+        context->get_def_use_mgr()->GetDef(15)));
+    EXPECT_TRUE(post_dominator_analysis->Dominates(
+        context->get_def_use_mgr()->GetDef(22),
+        context->get_def_use_mgr()->GetDef(15)));
+    EXPECT_TRUE(post_dominator_analysis->Dominates(
+        context->get_def_use_mgr()->GetDef(22),
+        context->get_def_use_mgr()->GetDef(20)));
+    EXPECT_TRUE(post_dominator_analysis->Dominates(
+        context->get_def_use_mgr()->GetDef(22),
+        context->get_def_use_mgr()->GetDef(21)));
+    EXPECT_TRUE(post_dominator_analysis->Dominates(
+        context->get_def_use_mgr()->GetDef(15),
+        context->get_def_use_mgr()->GetDef(15)));
+  }
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/inline_test.cpp b/third_party/SPIRV-Tools/test/opt/inline_test.cpp
index fc2197c..ffd3e38 100644
--- a/third_party/SPIRV-Tools/test/opt/inline_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/inline_test.cpp
@@ -2453,6 +2453,103 @@
   SinglePassRunAndCheck<InlineExhaustivePass>(before, after, false, true);
 }
 
+TEST_F(InlineTest, DontInlineFuncWithOpTerminateInvocationInContinue) {
+  const std::string test =
+      R"(OpCapability Shader
+OpExtension "SPV_KHR_terminate_invocation"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 330
+OpName %main "main"
+OpName %kill_ "kill("
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %void None %3
+%5 = OpLabel
+OpBranch %9
+%9 = OpLabel
+OpLoopMerge %11 %12 None
+OpBranch %13
+%13 = OpLabel
+OpBranchConditional %true %10 %11
+%10 = OpLabel
+OpBranch %12
+%12 = OpLabel
+%16 = OpFunctionCall %void %kill_
+OpBranch %9
+%11 = OpLabel
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %void None %3
+%7 = OpLabel
+OpTerminateInvocation
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<InlineExhaustivePass>(test, test, false, true);
+}
+
+TEST_F(InlineTest, InlineFuncWithOpTerminateInvocationNotInContinue) {
+  const std::string before =
+      R"(OpCapability Shader
+OpExtension "SPV_KHR_terminate_invocation"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 330
+OpName %main "main"
+OpName %kill_ "kill("
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %void None %3
+%5 = OpLabel
+%16 = OpFunctionCall %void %kill_
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %void None %3
+%7 = OpLabel
+OpTerminateInvocation
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(OpCapability Shader
+OpExtension "SPV_KHR_terminate_invocation"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 330
+OpName %main "main"
+OpName %kill_ "kill("
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %void None %3
+%5 = OpLabel
+OpTerminateInvocation
+%18 = OpLabel
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %void None %3
+%7 = OpLabel
+OpTerminateInvocation
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<InlineExhaustivePass>(before, after, false, true);
+}
+
 TEST_F(InlineTest, EarlyReturnFunctionInlined) {
   // #version 140
   //
diff --git a/third_party/SPIRV-Tools/test/opt/ir_context_test.cpp b/third_party/SPIRV-Tools/test/opt/ir_context_test.cpp
index e72561c..437fe73 100644
--- a/third_party/SPIRV-Tools/test/opt/ir_context_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/ir_context_test.cpp
@@ -26,6 +26,9 @@
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
+static const uint32_t kDebugDeclareOperandVariableIndex = 5;
+static const uint32_t kDebugValueOperandValueIndex = 5;
+
 namespace spvtools {
 namespace opt {
 namespace {
@@ -867,6 +870,235 @@
       << bb->id();  // Make sure asan does not complain about use after free.
 }
 
+TEST_F(IRContextTest, DebugInstructionReplaceSingleUse) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+%2 = OpString "test"
+%3 = OpTypeVoid
+%4 = OpTypeFunction %3
+%5 = OpTypeFloat 32
+%6 = OpTypePointer Function %5
+%7 = OpConstant %5 0
+%8 = OpTypeInt 32 0
+%9 = OpConstant %8 32
+%10 = OpExtInst %3 %1 DebugExpression
+%11 = OpExtInst %3 %1 DebugSource %2
+%12 = OpExtInst %3 %1 DebugCompilationUnit 1 4 %11 HLSL
+%13 = OpExtInst %3 %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %3
+%14 = OpExtInst %3 %1 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %17
+%15 = OpExtInst %3 %1 DebugTypeBasic %2 %9 Float
+%16 = OpExtInst %3 %1 DebugLocalVariable %2 %15 %11 0 0 %14 FlagIsLocal
+%17 = OpFunction %3 None %4
+%18 = OpLabel
+%19 = OpExtInst %3 %1 DebugScope %14
+%20 = OpVariable %6 Function
+%26 = OpVariable %6 Function
+OpBranch %21
+%21 = OpLabel
+%22 = OpPhi %5 %7 %18
+OpBranch %23
+%23 = OpLabel
+OpLine %2 0 0
+OpStore %20 %7
+%24 = OpExtInst %3 %1 DebugValue %16 %22 %10
+%25 = OpExtInst %3 %1 DebugDeclare %16 %26 %10
+OpReturn
+OpFunctionEnd)";
+
+  std::unique_ptr<IRContext> ctx =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ctx->BuildInvalidAnalyses(IRContext::kAnalysisDebugInfo);
+  DummyPassPreservesAll pass(Pass::Status::SuccessWithChange);
+  pass.Run(ctx.get());
+  EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDebugInfo));
+
+  auto* dbg_value = ctx->get_def_use_mgr()->GetDef(24);
+  EXPECT_TRUE(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
+              22);
+  EXPECT_TRUE(ctx->ReplaceAllUsesWith(22, 7));
+  dbg_value = ctx->get_def_use_mgr()->GetDef(24);
+  EXPECT_TRUE(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
+              7);
+
+  auto* dbg_decl = ctx->get_def_use_mgr()->GetDef(25);
+  EXPECT_TRUE(
+      dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 26);
+  EXPECT_TRUE(ctx->ReplaceAllUsesWith(26, 20));
+  dbg_decl = ctx->get_def_use_mgr()->GetDef(25);
+  EXPECT_TRUE(
+      dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 20);
+}
+
+TEST_F(IRContextTest, DebugInstructionReplaceAllUses) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+%2 = OpString "test"
+%3 = OpTypeVoid
+%4 = OpTypeFunction %3
+%5 = OpTypeFloat 32
+%6 = OpTypePointer Function %5
+%7 = OpConstant %5 0
+%8 = OpTypeInt 32 0
+%9 = OpConstant %8 32
+%10 = OpExtInst %3 %1 DebugExpression
+%11 = OpExtInst %3 %1 DebugSource %2
+%12 = OpExtInst %3 %1 DebugCompilationUnit 1 4 %11 HLSL
+%13 = OpExtInst %3 %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %3
+%14 = OpExtInst %3 %1 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %17
+%15 = OpExtInst %3 %1 DebugTypeBasic %2 %9 Float
+%16 = OpExtInst %3 %1 DebugLocalVariable %2 %15 %11 0 0 %14 FlagIsLocal
+%27 = OpExtInst %3 %1 DebugLocalVariable %2 %15 %11 1 0 %14 FlagIsLocal
+%17 = OpFunction %3 None %4
+%18 = OpLabel
+%19 = OpExtInst %3 %1 DebugScope %14
+%20 = OpVariable %6 Function
+%26 = OpVariable %6 Function
+OpBranch %21
+%21 = OpLabel
+%22 = OpPhi %5 %7 %18
+OpBranch %23
+%23 = OpLabel
+OpLine %2 0 0
+OpStore %20 %7
+%24 = OpExtInst %3 %1 DebugValue %16 %22 %10
+%25 = OpExtInst %3 %1 DebugDeclare %16 %26 %10
+%28 = OpExtInst %3 %1 DebugValue %27 %22 %10
+%29 = OpExtInst %3 %1 DebugDeclare %27 %26 %10
+OpReturn
+OpFunctionEnd)";
+
+  std::unique_ptr<IRContext> ctx =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ctx->BuildInvalidAnalyses(IRContext::kAnalysisDebugInfo);
+  DummyPassPreservesAll pass(Pass::Status::SuccessWithChange);
+  pass.Run(ctx.get());
+  EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDebugInfo));
+
+  auto* dbg_value0 = ctx->get_def_use_mgr()->GetDef(24);
+  auto* dbg_value1 = ctx->get_def_use_mgr()->GetDef(28);
+  EXPECT_TRUE(dbg_value0->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
+              22);
+  EXPECT_TRUE(dbg_value1->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
+              22);
+  EXPECT_TRUE(ctx->ReplaceAllUsesWith(22, 7));
+  dbg_value0 = ctx->get_def_use_mgr()->GetDef(24);
+  dbg_value1 = ctx->get_def_use_mgr()->GetDef(28);
+  EXPECT_TRUE(dbg_value0->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
+              7);
+  EXPECT_TRUE(dbg_value1->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
+              7);
+
+  auto* dbg_decl0 = ctx->get_def_use_mgr()->GetDef(25);
+  auto* dbg_decl1 = ctx->get_def_use_mgr()->GetDef(29);
+  EXPECT_TRUE(
+      dbg_decl0->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 26);
+  EXPECT_TRUE(
+      dbg_decl1->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 26);
+  EXPECT_TRUE(ctx->ReplaceAllUsesWith(26, 20));
+  dbg_decl0 = ctx->get_def_use_mgr()->GetDef(25);
+  dbg_decl1 = ctx->get_def_use_mgr()->GetDef(29);
+  EXPECT_TRUE(
+      dbg_decl0->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 20);
+  EXPECT_TRUE(
+      dbg_decl1->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 20);
+}
+
+TEST_F(IRContextTest, AddDebugValueAfterReplaceUse) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+%2 = OpString "test"
+%3 = OpTypeVoid
+%4 = OpTypeFunction %3
+%5 = OpTypeFloat 32
+%6 = OpTypePointer Function %5
+%7 = OpConstant %5 0
+%8 = OpTypeInt 32 0
+%9 = OpConstant %8 32
+%10 = OpExtInst %3 %1 DebugExpression
+%11 = OpExtInst %3 %1 DebugSource %2
+%12 = OpExtInst %3 %1 DebugCompilationUnit 1 4 %11 HLSL
+%13 = OpExtInst %3 %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %3
+%14 = OpExtInst %3 %1 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %17
+%15 = OpExtInst %3 %1 DebugTypeBasic %2 %9 Float
+%16 = OpExtInst %3 %1 DebugLocalVariable %2 %15 %11 0 0 %14 FlagIsLocal
+%17 = OpFunction %3 None %4
+%18 = OpLabel
+%19 = OpExtInst %3 %1 DebugScope %14
+%20 = OpVariable %6 Function
+%26 = OpVariable %6 Function
+OpBranch %21
+%21 = OpLabel
+%27 = OpExtInst %3 %1 DebugScope %14
+%22 = OpPhi %5 %7 %18
+OpBranch %23
+%23 = OpLabel
+%28 = OpExtInst %3 %1 DebugScope %14
+OpLine %2 0 0
+OpStore %20 %7
+%24 = OpExtInst %3 %1 DebugValue %16 %22 %10
+%25 = OpExtInst %3 %1 DebugDeclare %16 %26 %10
+OpReturn
+OpFunctionEnd)";
+
+  std::unique_ptr<IRContext> ctx =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ctx->BuildInvalidAnalyses(IRContext::kAnalysisDebugInfo);
+  DummyPassPreservesAll pass(Pass::Status::SuccessWithChange);
+  pass.Run(ctx.get());
+  EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDebugInfo));
+
+  // Replace all uses of result it '26' with '20'
+  auto* dbg_decl = ctx->get_def_use_mgr()->GetDef(25);
+  EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex),
+            26);
+  EXPECT_TRUE(ctx->ReplaceAllUsesWith(26, 20));
+  dbg_decl = ctx->get_def_use_mgr()->GetDef(25);
+  EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex),
+            20);
+
+  // No DebugValue should be added because result id '26' is not used for
+  // DebugDeclare.
+  ctx->get_debug_info_mgr()->AddDebugValue(dbg_decl, 26, 22, dbg_decl);
+  EXPECT_EQ(dbg_decl->NextNode()->opcode(), SpvOpReturn);
+
+  // DebugValue should be added because result id '20' is used for DebugDeclare.
+  ctx->get_debug_info_mgr()->AddDebugValue(dbg_decl, 20, 22, dbg_decl);
+  EXPECT_EQ(dbg_decl->NextNode()->GetOpenCL100DebugOpcode(),
+            OpenCLDebugInfo100DebugValue);
+
+  // Replace all uses of result it '20' with '26'
+  EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex),
+            20);
+  EXPECT_TRUE(ctx->ReplaceAllUsesWith(20, 26));
+  EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex),
+            26);
+
+  // No DebugValue should be added because result id '20' is not used for
+  // DebugDeclare.
+  ctx->get_debug_info_mgr()->AddDebugValue(dbg_decl, 20, 7, dbg_decl);
+  Instruction* dbg_value = dbg_decl->NextNode();
+  EXPECT_EQ(dbg_value->GetOpenCL100DebugOpcode(), OpenCLDebugInfo100DebugValue);
+  EXPECT_EQ(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex), 22);
+
+  // DebugValue should be added because result id '26' is used for DebugDeclare.
+  ctx->get_debug_info_mgr()->AddDebugValue(dbg_decl, 26, 7, dbg_decl);
+  dbg_value = dbg_decl->NextNode();
+  EXPECT_EQ(dbg_value->GetOpenCL100DebugOpcode(), OpenCLDebugInfo100DebugValue);
+  EXPECT_EQ(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex), 7);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/local_single_block_elim.cpp b/third_party/SPIRV-Tools/test/opt/local_single_block_elim.cpp
index 402352d..8e1cee6 100644
--- a/third_party/SPIRV-Tools/test/opt/local_single_block_elim.cpp
+++ b/third_party/SPIRV-Tools/test/opt/local_single_block_elim.cpp
@@ -1118,6 +1118,340 @@
   )";
   SinglePassRunAndMatch<LocalSingleBlockLoadStoreElimPass>(text, false);
 }
+
+TEST_F(LocalSingleBlockLoadStoreElimTest, DebugDeclareTest) {
+  // If OpenCL.DebugInfo.100 enabled, check that store/load is still
+  // optimized, but stores are not deleted for store/store.
+  //
+  // struct PS_INPUT {
+  //   float4 c0 : COLOR0;
+  //   float4 c1 : COLOR1;
+  // };
+  //
+  // struct PS_OUTPUT {
+  //   float4 vColor : SV_Target0;
+  // };
+  //
+  // PS_OUTPUT MainPs(PS_INPUT i) {
+  //   PS_OUTPUT ps_output;
+  //   float4 c;
+  //   c = i.c0;
+  //   c += i.c1;
+  //   c /= 2.0;
+  //   ps_output.vColor = c;
+  //   return ps_output;
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %MainPs "MainPs" %in_var_COLOR0 %in_var_COLOR1 %out_var_SV_Target0
+               OpExecutionMode %MainPs OriginUpperLeft
+          %6 = OpString "foo3.frag"
+          %7 = OpString "PS_OUTPUT"
+          %8 = OpString "float"
+          %9 = OpString "vColor"
+         %10 = OpString "PS_INPUT"
+         %11 = OpString "c1"
+         %12 = OpString "c0"
+         %13 = OpString "src.MainPs"
+         %14 = OpString "c"
+         %15 = OpString "ps_output"
+         %16 = OpString "i"
+               OpName %in_var_COLOR0 "in.var.COLOR0"
+               OpName %in_var_COLOR1 "in.var.COLOR1"
+               OpName %out_var_SV_Target0 "out.var.SV_Target0"
+               OpName %MainPs "MainPs"
+               OpName %PS_INPUT "PS_INPUT"
+               OpMemberName %PS_INPUT 0 "c0"
+               OpMemberName %PS_INPUT 1 "c1"
+               OpName %param_var_i "param.var.i"
+               OpName %PS_OUTPUT "PS_OUTPUT"
+               OpMemberName %PS_OUTPUT 0 "vColor"
+               OpName %src_MainPs "src.MainPs"
+               OpName %i "i"
+               OpName %bb_entry "bb.entry"
+               OpName %ps_output "ps_output"
+               OpName %c "c"
+               OpDecorate %in_var_COLOR0 Location 0
+               OpDecorate %in_var_COLOR1 Location 1
+               OpDecorate %out_var_SV_Target0 Location 0
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+      %float = OpTypeFloat 32
+    %float_2 = OpConstant %float 2
+    %v4float = OpTypeVector %float 4
+         %31 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+   %uint_128 = OpConstant %uint 128
+     %uint_0 = OpConstant %uint 0
+   %uint_256 = OpConstant %uint 256
+         %40 = OpTypeFunction %void
+   %PS_INPUT = OpTypeStruct %v4float %v4float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+  %PS_OUTPUT = OpTypeStruct %v4float
+         %42 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%in_var_COLOR0 = OpVariable %_ptr_Input_v4float Input
+%in_var_COLOR1 = OpVariable %_ptr_Input_v4float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+         %45 = OpExtInst %void %1 DebugSource %6
+         %46 = OpExtInst %void %1 DebugCompilationUnit 1 4 %45 HLSL
+         %47 = OpExtInst %void %1 DebugTypeComposite %7 Structure %45 8 1 %46 %7 %uint_128 FlagIsProtected|FlagIsPrivate %48
+         %49 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 Float
+         %50 = OpExtInst %void %1 DebugTypeVector %49 4
+         %48 = OpExtInst %void %1 DebugTypeMember %9 %50 %45 10 5 %47 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+         %51 = OpExtInst %void %1 DebugTypeComposite %10 Structure %45 2 1 %46 %10 %uint_256 FlagIsProtected|FlagIsPrivate %52 %53
+         %53 = OpExtInst %void %1 DebugTypeMember %11 %50 %45 5 5 %51 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate
+         %52 = OpExtInst %void %1 DebugTypeMember %12 %50 %45 4 5 %51 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+         %54 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %47 %51
+         %55 = OpExtInst %void %1 DebugFunction %13 %54 %45 13 1 %46 %13 FlagIsProtected|FlagIsPrivate 14 %src_MainPs
+         %56 = OpExtInst %void %1 DebugLexicalBlock %45 14 1 %55
+         %57 = OpExtInst %void %1 DebugLocalVariable %14 %50 %45 16 12 %56 FlagIsLocal
+         %58 = OpExtInst %void %1 DebugLocalVariable %15 %47 %45 15 15 %56 FlagIsLocal
+         %59 = OpExtInst %void %1 DebugExpression
+         %60 = OpExtInst %void %1 DebugLocalVariable %16 %51 %45 13 29 %55 FlagIsLocal 1
+         %61 = OpExtInst %void %1 DebugLocalVariable %14 %50 %45 16 12 %55 FlagIsLocal 1
+     %MainPs = OpFunction %void None %40
+         %62 = OpLabel
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+         %63 = OpLoad %v4float %in_var_COLOR0
+         %64 = OpLoad %v4float %in_var_COLOR1
+         %65 = OpCompositeConstruct %PS_INPUT %63 %64
+               OpStore %param_var_i %65
+         %66 = OpFunctionCall %PS_OUTPUT %src_MainPs %param_var_i
+         %67 = OpCompositeExtract %v4float %66 0
+               OpStore %out_var_SV_Target0 %67
+               OpReturn
+               OpFunctionEnd
+               OpLine %6 13 1
+ %src_MainPs = OpFunction %PS_OUTPUT None %42
+         %83 = OpExtInst %void %1 DebugScope %55
+               OpLine %6 13 29
+          %i = OpFunctionParameter %_ptr_Function_PS_INPUT
+         %69 = OpExtInst %void %1 DebugDeclare %60 %i %59
+         %84 = OpExtInst %void %1 DebugNoScope
+   %bb_entry = OpLabel
+         %85 = OpExtInst %void %1 DebugScope %56
+  %ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function
+          %c = OpVariable %_ptr_Function_v4float Function
+         %71 = OpExtInst %void %1 DebugDeclare %61 %c %59
+               OpLine %6 18 9
+         %72 = OpAccessChain %_ptr_Function_v4float %i %int_0
+               OpLine %6 18 13
+         %73 = OpLoad %v4float %72
+               OpLine %6 18 5
+               OpStore %c %73
+;CHECK:        OpStore %c %73
+               OpLine %6 19 10
+         %74 = OpAccessChain %_ptr_Function_v4float %i %int_1
+               OpLine %6 19 14
+         %75 = OpLoad %v4float %74
+               OpLine %6 19 5
+         %76 = OpLoad %v4float %c
+;CHECK-NOT:       OpLine %6 19 5
+;CHECK-NOT: %76 = OpLoad %v4float %c
+               OpLine %6 19 7
+         %77 = OpFAdd %v4float %76 %75
+;CHECK-NOT: %77 = OpFAdd %v4float %76 %75
+;CHECK:     %77 = OpFAdd %v4float %73 %75
+               OpLine %6 19 5
+               OpStore %c %77
+;CHECK:        OpStore %c %77
+               OpLine %6 20 5
+         %78 = OpLoad %v4float %c
+;CHECK-NOT:       OpLine %6 20 5
+;CHECK-NOT: %78 = OpLoad %v4float %c
+               OpLine %6 20 7
+         %79 = OpFDiv %v4float %78 %31
+;CHECK-NOT %79 = OpFDiv %v4float %78 %31
+;CHECK:    %79 = OpFDiv %v4float %77 %31
+               OpLine %6 20 5
+               OpStore %c %79
+;CHECK:        OpStore %c %79
+               OpLine %6 22 26
+         %80 = OpLoad %v4float %c
+;CHECK-NOT:       OpLine %6 22 26
+;CHECK-NOT: %80 = OpLoad %v4float %c
+               OpLine %6 22 5
+         %81 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0
+               OpStore %81 %80
+;CHECK-NOT:    OpStore %81 %80
+;CHECK:        OpStore %81 %79
+               OpLine %6 23 12
+         %82 = OpLoad %PS_OUTPUT %ps_output
+               OpLine %6 23 5
+               OpReturnValue %82
+         %86 = OpExtInst %void %1 DebugNoScope
+               OpFunctionEnd
+  )";
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<LocalSingleBlockLoadStoreElimPass>(text, false);
+}
+TEST_F(LocalSingleBlockLoadStoreElimTest, DebugValueTest) {
+  // If OpenCL.DebugInfo.100 enabled, check that store/load is still
+  // optimized, but stores are not deleted for store/store.
+  // Same source as DebugDeclareTest; DebugDeclare replaced with
+  // equivalent DebugValue
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %MainPs "MainPs" %in_var_COLOR0 %in_var_COLOR1 %out_var_SV_Target0
+               OpExecutionMode %MainPs OriginUpperLeft
+          %6 = OpString "foo3.frag"
+          %7 = OpString "PS_OUTPUT"
+          %8 = OpString "float"
+          %9 = OpString "vColor"
+         %10 = OpString "PS_INPUT"
+         %11 = OpString "c1"
+         %12 = OpString "c0"
+         %13 = OpString "src.MainPs"
+         %14 = OpString "c"
+         %15 = OpString "ps_output"
+         %16 = OpString "i"
+               OpName %in_var_COLOR0 "in.var.COLOR0"
+               OpName %in_var_COLOR1 "in.var.COLOR1"
+               OpName %out_var_SV_Target0 "out.var.SV_Target0"
+               OpName %MainPs "MainPs"
+               OpName %PS_INPUT "PS_INPUT"
+               OpMemberName %PS_INPUT 0 "c0"
+               OpMemberName %PS_INPUT 1 "c1"
+               OpName %param_var_i "param.var.i"
+               OpName %PS_OUTPUT "PS_OUTPUT"
+               OpMemberName %PS_OUTPUT 0 "vColor"
+               OpName %src_MainPs "src.MainPs"
+               OpName %i "i"
+               OpName %bb_entry "bb.entry"
+               OpName %ps_output "ps_output"
+               OpName %c "c"
+               OpDecorate %in_var_COLOR0 Location 0
+               OpDecorate %in_var_COLOR1 Location 1
+               OpDecorate %out_var_SV_Target0 Location 0
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+      %float = OpTypeFloat 32
+    %float_2 = OpConstant %float 2
+    %v4float = OpTypeVector %float 4
+         %31 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+   %uint_128 = OpConstant %uint 128
+     %uint_0 = OpConstant %uint 0
+   %uint_256 = OpConstant %uint 256
+         %40 = OpTypeFunction %void
+   %PS_INPUT = OpTypeStruct %v4float %v4float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+  %PS_OUTPUT = OpTypeStruct %v4float
+         %42 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%in_var_COLOR0 = OpVariable %_ptr_Input_v4float Input
+%in_var_COLOR1 = OpVariable %_ptr_Input_v4float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+         %45 = OpExtInst %void %1 DebugSource %6
+         %46 = OpExtInst %void %1 DebugCompilationUnit 1 4 %45 HLSL
+         %47 = OpExtInst %void %1 DebugTypeComposite %7 Structure %45 8 1 %46 %7 %uint_128 FlagIsProtected|FlagIsPrivate %48
+         %49 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 Float
+         %50 = OpExtInst %void %1 DebugTypeVector %49 4
+         %48 = OpExtInst %void %1 DebugTypeMember %9 %50 %45 10 5 %47 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+         %51 = OpExtInst %void %1 DebugTypeComposite %10 Structure %45 2 1 %46 %10 %uint_256 FlagIsProtected|FlagIsPrivate %52 %53
+         %53 = OpExtInst %void %1 DebugTypeMember %11 %50 %45 5 5 %51 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate
+         %52 = OpExtInst %void %1 DebugTypeMember %12 %50 %45 4 5 %51 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+         %54 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %47 %51
+         %55 = OpExtInst %void %1 DebugFunction %13 %54 %45 13 1 %46 %13 FlagIsProtected|FlagIsPrivate 14 %src_MainPs
+         %56 = OpExtInst %void %1 DebugLexicalBlock %45 14 1 %55
+         %57 = OpExtInst %void %1 DebugLocalVariable %14 %50 %45 16 12 %56 FlagIsLocal
+         %58 = OpExtInst %void %1 DebugLocalVariable %15 %47 %45 15 15 %56 FlagIsLocal
+         %59 = OpExtInst %void %1 DebugExpression %60 = OpExtInst %void %1 DebugOperation Deref %61 = OpExtInst
+%void %1 DebugExpression %60 %62 = OpExtInst %void %1 DebugLocalVariable %16 %51
+%45 13 29 %55 FlagIsLocal 1 %63 = OpExtInst %void %1 DebugLocalVariable %14 %50
+%45 16 12 %55 FlagIsLocal 1 %MainPs = OpFunction %void None %40 %64 = OpLabel
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+         %65 = OpLoad %v4float %in_var_COLOR0
+         %66 = OpLoad %v4float %in_var_COLOR1
+         %67 = OpCompositeConstruct %PS_INPUT %65 %66
+               OpStore %param_var_i %67
+         %68 = OpFunctionCall %PS_OUTPUT %src_MainPs %param_var_i
+         %69 = OpCompositeExtract %v4float %68 0
+               OpStore %out_var_SV_Target0 %69
+               OpReturn
+               OpFunctionEnd
+               OpLine %6 13 1
+ %src_MainPs = OpFunction %PS_OUTPUT None %42
+         %70 = OpExtInst %void %1 DebugScope %55
+               OpLine %6 13 29
+          %i = OpFunctionParameter %_ptr_Function_PS_INPUT
+         %71 = OpExtInst %void %1 DebugDeclare %62 %i %59
+         %72 = OpExtInst %void %1 DebugNoScope
+   %bb_entry = OpLabel
+         %73 = OpExtInst %void %1 DebugScope %56
+  %ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function
+          %c = OpVariable %_ptr_Function_v4float Function
+         %74 = OpExtInst %void %1 DebugValue %63 %c %61
+               OpLine %6 18 9
+         %75 = OpAccessChain %_ptr_Function_v4float %i %int_0
+               OpLine %6 18 13
+         %76 = OpLoad %v4float %75
+               OpLine %6 18 5
+               OpStore %c %76
+;CHECK:        OpStore %c %76
+               OpLine %6 19 10
+         %77 = OpAccessChain %_ptr_Function_v4float %i %int_1
+               OpLine %6 19 14
+         %78 = OpLoad %v4float %77
+               OpLine %6 19 5
+         %79 = OpLoad %v4float %c
+;CHECK-NOT:       OpLine %6 19 5
+;CHECK-NOT: %79 = OpLoad %v4float %c
+               OpLine %6 19 7
+         %80 = OpFAdd %v4float %79 %78
+;CHECK-NOT: %80 = OpFAdd %v4float %79 %78
+;CHECK:     %80 = OpFAdd %v4float %76 %78
+               OpLine %6 19 5
+               OpStore %c %80
+;CHECK:        OpStore %c %80
+               OpLine %6 20 5
+         %81 = OpLoad %v4float %c
+;CHECK-NOT:       OpLine %6 20 5
+;CHECK-NOT: %81 = OpLoad %v4float %c
+               OpLine %6 20 7
+         %82 = OpFDiv %v4float %81 %31
+;CHECK-NOT: %82 = OpFDiv %v4float %81 %31
+;CHECK:     %82 = OpFDiv %v4float %80 %31
+               OpLine %6 20 5
+               OpStore %c %82
+;CHECK:        OpStore %c %82
+               OpLine %6 22 26
+         %83 = OpLoad %v4float %c
+;CHECK-NOT:       OpLine %6 22 26
+;CHECK-NOT: %83 = OpLoad %v4float %c
+               OpLine %6 22 5
+         %84 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0
+               OpStore %84 %83
+;CHECK-NOT:    OpStore %84 %83
+;CHECK:        OpStore %84 %82
+               OpLine %6 23 12
+         %85 = OpLoad %PS_OUTPUT %ps_output
+               OpLine %6 23 5
+               OpReturnValue %85
+         %86 = OpExtInst %void %1 DebugNoScope
+               OpFunctionEnd
+  )";
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<LocalSingleBlockLoadStoreElimPass>(text, false);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Other target variable types
diff --git a/third_party/SPIRV-Tools/test/opt/local_single_store_elim_test.cpp b/third_party/SPIRV-Tools/test/opt/local_single_store_elim_test.cpp
index 5a1650b..ebc89a6 100644
--- a/third_party/SPIRV-Tools/test/opt/local_single_store_elim_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/local_single_store_elim_test.cpp
@@ -902,6 +902,315 @@
   )";
   SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
 }
+
+TEST_F(LocalSingleStoreElimTest, DebugDeclareTest) {
+  // If OpenCL.DebugInfo.100 enabled, check that store/load is still
+  // optimized, DebugValue placed after the store and the associated
+  // DebugDeclare is removed.
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0
+               OpExecutionMode %MainPs OriginUpperLeft
+         %20 = OpString "foo.frag"
+         %24 = OpString "PS_OUTPUT"
+         %28 = OpString "float"
+         %31 = OpString "vColor"
+         %33 = OpString "PS_INPUT"
+         %38 = OpString "vTextureCoords"
+         %40 = OpString "@type.2d.image"
+         %41 = OpString "type.2d.image"
+         %43 = OpString "Texture2D.TemplateParam"
+         %47 = OpString "src.MainPs"
+         %51 = OpString "tc"
+         %53 = OpString "ps_output"
+         %56 = OpString "i"
+         %58 = OpString "@type.sampler"
+         %59 = OpString "type.sampler"
+         %61 = OpString "g_sAniso"
+         %63 = OpString "g_tColor"
+               OpName %type_2d_image "type.2d.image"
+               OpName %g_tColor "g_tColor"
+               OpName %type_sampler "type.sampler"
+               OpName %g_sAniso "g_sAniso"
+               OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2"
+               OpName %out_var_SV_Target0 "out.var.SV_Target0"
+               OpName %MainPs "MainPs"
+               OpName %PS_INPUT "PS_INPUT"
+               OpMemberName %PS_INPUT 0 "vTextureCoords"
+               OpName %param_var_i "param.var.i"
+               OpName %PS_OUTPUT "PS_OUTPUT"
+               OpMemberName %PS_OUTPUT 0 "vColor"
+               OpName %type_sampled_image "type.sampled.image"
+               OpDecorate %in_var_TEXCOORD2 Location 0
+               OpDecorate %out_var_SV_Target0 Location 0
+               OpDecorate %g_tColor DescriptorSet 0
+               OpDecorate %g_tColor Binding 0
+               OpDecorate %g_sAniso DescriptorSet 0
+               OpDecorate %g_sAniso Binding 1
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+      %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+    %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+   %uint_128 = OpConstant %uint 128
+     %uint_0 = OpConstant %uint 0
+    %uint_64 = OpConstant %uint 64
+         %65 = OpTypeFunction %void
+   %PS_INPUT = OpTypeStruct %v2float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+  %PS_OUTPUT = OpTypeStruct %v4float
+         %75 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+   %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+   %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+         %39 = OpExtInst %void %1 DebugInfoNone
+         %55 = OpExtInst %void %1 DebugExpression
+         %22 = OpExtInst %void %1 DebugSource %20
+         %23 = OpExtInst %void %1 DebugCompilationUnit 1 4 %22 HLSL
+         %26 = OpExtInst %void %1 DebugTypeComposite %24 Structure %22 10 1 %23 %24 %uint_128 FlagIsProtected|FlagIsPrivate %27
+         %29 = OpExtInst %void %1 DebugTypeBasic %28 %uint_32 Float
+         %30 = OpExtInst %void %1 DebugTypeVector %29 4
+         %27 = OpExtInst %void %1 DebugTypeMember %31 %30 %22 12 5 %26 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+         %35 = OpExtInst %void %1 DebugTypeComposite %33 Structure %22 5 1 %23 %33 %uint_64 FlagIsProtected|FlagIsPrivate %36
+         %37 = OpExtInst %void %1 DebugTypeVector %29 2
+         %36 = OpExtInst %void %1 DebugTypeMember %38 %37 %22 7 5 %35 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate
+         %42 = OpExtInst %void %1 DebugTypeComposite %40 Class %22 0 0 %23 %41 %39 FlagIsProtected|FlagIsPrivate
+         %44 = OpExtInst %void %1 DebugTypeTemplateParameter %43 %29 %39 %22 0 0
+         %45 = OpExtInst %void %1 DebugTypeTemplate %42 %44
+         %46 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %26 %35
+         %48 = OpExtInst %void %1 DebugFunction %47 %46 %22 15 1 %23 %47 FlagIsProtected|FlagIsPrivate 16 %39
+         %50 = OpExtInst %void %1 DebugLexicalBlock %22 16 1 %48
+         %52 = OpExtInst %void %1 DebugLocalVariable %51 %37 %22 19 12 %50 FlagIsLocal
+         %54 = OpExtInst %void %1 DebugLocalVariable %53 %26 %22 17 15 %50 FlagIsLocal
+         %57 = OpExtInst %void %1 DebugLocalVariable %56 %35 %22 15 29 %48 FlagIsLocal 1
+         %60 = OpExtInst %void %1 DebugTypeComposite %58 Structure %22 0 0 %23 %59 %39 FlagIsProtected|FlagIsPrivate
+         %62 = OpExtInst %void %1 DebugGlobalVariable %61 %60 %22 3 14 %23 %61 %g_sAniso FlagIsDefinition
+         %64 = OpExtInst %void %1 DebugGlobalVariable %63 %42 %22 1 11 %23 %63 %g_tColor FlagIsDefinition
+     %MainPs = OpFunction %void None %65
+         %66 = OpLabel
+        %114 = OpExtInst %void %1 DebugScope %50
+         %98 = OpVariable %_ptr_Function_PS_OUTPUT Function
+         %99 = OpVariable %_ptr_Function_v2float Function
+        %115 = OpExtInst %void %1 DebugNoScope
+        %100 = OpVariable %_ptr_Function_PS_OUTPUT Function
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+         %70 = OpLoad %v2float %in_var_TEXCOORD2
+         %71 = OpCompositeConstruct %PS_INPUT %70
+               OpStore %param_var_i %71
+        %116 = OpExtInst %void %1 DebugScope %48
+        %102 = OpExtInst %void %1 DebugDeclare %57 %param_var_i %55
+        %117 = OpExtInst %void %1 DebugScope %50
+        %103 = OpExtInst %void %1 DebugDeclare %54 %98 %55
+               OpLine %20 19 17
+        %104 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
+        %105 = OpLoad %v2float %104
+               OpLine %20 19 12
+               OpStore %99 %105
+        %106 = OpExtInst %void %1 DebugDeclare %52 %99 %55
+;CHECK-NOT: %106 = OpExtInst %void %1 DebugDeclare %52 %99 %55
+;CHECK:     %119 = OpExtInst %void %1 DebugValue %52 %105 %55
+               OpLine %20 20 26
+        %107 = OpLoad %type_2d_image %g_tColor
+               OpLine %20 20 46
+        %108 = OpLoad %type_sampler %g_sAniso
+               OpLine %20 20 57
+        %109 = OpLoad %v2float %99
+;CHECK-NOT: %109 = OpLoad %v2float %99
+               OpLine %20 20 26
+        %110 = OpSampledImage %type_sampled_image %107 %108
+        %111 = OpImageSampleImplicitLod %v4float %110 %109 None
+;CHECK-NOT: %111 = OpImageSampleImplicitLod %v4float %110 %109 None
+;CHECK:     %111 = OpImageSampleImplicitLod %v4float %110 %105 None
+               OpLine %20 20 5
+        %112 = OpAccessChain %_ptr_Function_v4float %98 %int_0
+               OpStore %112 %111
+               OpLine %20 21 12
+        %113 = OpLoad %PS_OUTPUT %98
+               OpLine %20 21 5
+               OpStore %100 %113
+        %118 = OpExtInst %void %1 DebugNoScope
+         %73 = OpLoad %PS_OUTPUT %100
+         %74 = OpCompositeExtract %v4float %73 0
+               OpStore %out_var_SV_Target0 %74
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
+}
+
+TEST_F(LocalSingleStoreElimTest, DebugValueTest) {
+  // If OpenCL.DebugInfo.100 enabled, check that store/load is still
+  // optimized, DebugValue placed after the store and the associated
+  // DebugValue Deref is removed.
+  const std::string text = R"(
+                 OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0
+               OpExecutionMode %MainPs OriginUpperLeft
+          %7 = OpString "foo.frag"
+          %8 = OpString "PS_OUTPUT"
+          %9 = OpString "float"
+         %10 = OpString "vColor"
+         %11 = OpString "PS_INPUT"
+         %12 = OpString "vTextureCoords"
+         %13 = OpString "@type.2d.image"
+         %14 = OpString "type.2d.image"
+         %15 = OpString "Texture2D.TemplateParam"
+         %16 = OpString "src.MainPs"
+         %17 = OpString "tc"
+         %18 = OpString "ps_output"
+         %19 = OpString "i"
+         %20 = OpString "@type.sampler"
+         %21 = OpString "type.sampler"
+         %22 = OpString "g_sAniso"
+         %23 = OpString "g_tColor"
+               OpName %type_2d_image "type.2d.image"
+               OpName %g_tColor "g_tColor"
+               OpName %type_sampler "type.sampler"
+               OpName %g_sAniso "g_sAniso"
+               OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2"
+               OpName %out_var_SV_Target0 "out.var.SV_Target0"
+               OpName %MainPs "MainPs"
+               OpName %PS_INPUT "PS_INPUT"
+               OpMemberName %PS_INPUT 0 "vTextureCoords"
+               OpName %param_var_i "param.var.i"
+               OpName %PS_OUTPUT "PS_OUTPUT"
+               OpMemberName %PS_OUTPUT 0 "vColor"
+               OpName %type_sampled_image "type.sampled.image"
+               OpDecorate %in_var_TEXCOORD2 Location 0
+               OpDecorate %out_var_SV_Target0 Location 0
+               OpDecorate %g_tColor DescriptorSet 0
+               OpDecorate %g_tColor Binding 0
+               OpDecorate %g_sAniso DescriptorSet 0
+               OpDecorate %g_sAniso Binding 1
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+      %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+    %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+   %uint_128 = OpConstant %uint 128
+     %uint_0 = OpConstant %uint 0
+    %uint_64 = OpConstant %uint 64
+         %45 = OpTypeFunction %void
+   %PS_INPUT = OpTypeStruct %v2float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+  %PS_OUTPUT = OpTypeStruct %v4float
+         %47 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+   %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+   %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+         %51 = OpExtInst %void %1 DebugInfoNone
+         %52 = OpExtInst %void %1 DebugExpression
+         %53 = OpExtInst %void %1 DebugOperation Deref
+         %54 = OpExtInst %void %1 DebugExpression %53
+         %55 = OpExtInst %void %1 DebugSource %7
+         %56 = OpExtInst %void %1 DebugCompilationUnit 1 4 %55 HLSL
+         %57 = OpExtInst %void %1 DebugTypeComposite %8 Structure %55 10 1 %56 %8 %uint_128 FlagIsProtected|FlagIsPrivate %58
+         %59 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 Float
+         %60 = OpExtInst %void %1 DebugTypeVector %59 4
+         %58 = OpExtInst %void %1 DebugTypeMember %10 %60 %55 12 5 %57 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+         %61 = OpExtInst %void %1 DebugTypeComposite %11 Structure %55 5 1 %56 %11 %uint_64 FlagIsProtected|FlagIsPrivate %62
+         %63 = OpExtInst %void %1 DebugTypeVector %59 2
+         %62 = OpExtInst %void %1 DebugTypeMember %12 %63 %55 7 5 %61 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate
+         %64 = OpExtInst %void %1 DebugTypeComposite %13 Class %55 0 0 %56 %14 %51 FlagIsProtected|FlagIsPrivate
+         %65 = OpExtInst %void %1 DebugTypeTemplateParameter %15 %59 %51 %55 0 0
+         %66 = OpExtInst %void %1 DebugTypeTemplate %64 %65
+         %67 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %57 %61
+         %68 = OpExtInst %void %1 DebugFunction %16 %67 %55 15 1 %56 %16 FlagIsProtected|FlagIsPrivate 16 %51
+         %69 = OpExtInst %void %1 DebugLexicalBlock %55 16 1 %68
+         %70 = OpExtInst %void %1 DebugLocalVariable %17 %63 %55 19 12 %69 FlagIsLocal
+         %71 = OpExtInst %void %1 DebugLocalVariable %18 %57 %55 17 15 %69 FlagIsLocal
+         %72 = OpExtInst %void %1 DebugLocalVariable %19 %61 %55 15 29 %68 FlagIsLocal 1
+         %73 = OpExtInst %void %1 DebugTypeComposite %20 Structure %55 0 0 %56 %21 %51 FlagIsProtected|FlagIsPrivate
+         %74 = OpExtInst %void %1 DebugGlobalVariable %22 %73 %55 3 14 %56 %22 %g_sAniso FlagIsDefinition
+         %75 = OpExtInst %void %1 DebugGlobalVariable %23 %64 %55 1 11 %56 %23 %g_tColor FlagIsDefinition
+     %MainPs = OpFunction %void None %45
+         %76 = OpLabel
+        %101 = OpExtInst %void %1 DebugScope %69
+         %78 = OpVariable %_ptr_Function_PS_OUTPUT Function
+         %79 = OpVariable %_ptr_Function_v2float Function
+        %102 = OpExtInst %void %1 DebugNoScope
+         %81 = OpVariable %_ptr_Function_PS_OUTPUT Function
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+         %82 = OpLoad %v2float %in_var_TEXCOORD2
+         %83 = OpCompositeConstruct %PS_INPUT %82
+               OpStore %param_var_i %83
+        %103 = OpExtInst %void %1 DebugScope %68
+         %85 = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52
+        %104 = OpExtInst %void %1 DebugScope %69
+         %87 = OpExtInst %void %1 DebugDeclare %71 %78 %52
+               OpLine %7 19 17
+         %88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
+         %89 = OpLoad %v2float %88
+               OpLine %7 19 12
+               OpStore %79 %89
+         %90 = OpExtInst %void %1 DebugValue %70 %79 %54
+;CHECK-NOT: %90 = OpExtInst %void %1 DebugValue %70 %79 %54
+;CHECK:    %106 = OpExtInst %void %1 DebugValue %70 %89 %52
+               OpLine %7 20 26
+         %91 = OpLoad %type_2d_image %g_tColor
+               OpLine %7 20 46
+         %92 = OpLoad %type_sampler %g_sAniso
+               OpLine %7 20 57
+         %93 = OpLoad %v2float %79
+;CHECK-NOT: %93 = OpLoad %v2float %79
+               OpLine %7 20 26
+         %94 = OpSampledImage %type_sampled_image %91 %92
+         %95 = OpImageSampleImplicitLod %v4float %94 %93 None
+;CHECK-NOT: %95 = OpImageSampleImplicitLod %v4float %94 %93 None
+;CHECK:     %95 = OpImageSampleImplicitLod %v4float %94 %89 None
+               OpLine %7 20 5
+         %96 = OpAccessChain %_ptr_Function_v4float %78 %int_0
+               OpStore %96 %95
+               OpLine %7 21 12
+         %97 = OpLoad %PS_OUTPUT %78
+               OpLine %7 21 5
+               OpStore %81 %97
+        %105 = OpExtInst %void %1 DebugNoScope
+         %99 = OpLoad %PS_OUTPUT %81
+        %100 = OpCompositeExtract %v4float %99 0
+               OpStore %out_var_SV_Target0 %100
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Other types
diff --git a/third_party/SPIRV-Tools/test/opt/local_ssa_elim_test.cpp b/third_party/SPIRV-Tools/test/opt/local_ssa_elim_test.cpp
index d29a554..6581ebf 100644
--- a/third_party/SPIRV-Tools/test/opt/local_ssa_elim_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/local_ssa_elim_test.cpp
@@ -2024,6 +2024,1344 @@
   SinglePassRunToBinary<SSARewritePass>(text, false);
 }
 
+TEST_F(LocalSSAElimTest, DebugForLoop) {
+  // #version 140
+  //
+  // in vec4 BC;
+  // out float fo;
+  //
+  // void main()
+  // {
+  //     float f = 0.0;
+  //     for (int i=0; i<4; i++) {
+  //       f = f + BC[i];
+  //     }
+  //     fo = f;
+  // }
+
+  const std::string text = R"(
+; CHECK: [[f_name:%\w+]] = OpString "f"
+; CHECK: [[i_name:%\w+]] = OpString "i"
+; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugLocalVariable [[f_name]]
+; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]]
+
+; CHECK:      OpStore %f %float_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] %float_0
+; CHECK-NEXT: OpStore %i %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] %int_0
+
+; CHECK-NOT:  DebugDeclare
+
+; CHECK:      [[loop_head:%\w+]] = OpLabel
+; CHECK:      [[phi0:%\w+]] = OpPhi %float %float_0
+; CHECK:      [[phi1:%\w+]] = OpPhi %int %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[phi0]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[phi1]]
+; CHECK:      OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] None
+; CHECK-NEXT: OpBranch [[loop_body:%\w+]]
+
+; CHECK-NEXT: [[loop_body]] = OpLabel
+; CHECK:      OpBranchConditional {{%\w+}} [[bb:%\w+]] [[loop_merge]]
+
+; CHECK:      [[bb]] = OpLabel
+; CHECK:      OpStore %f [[f_val:%\w+]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_val]]
+; CHECK-NEXT: OpBranch [[loop_cont]]
+
+; CHECK:      [[loop_cont]] = OpLabel
+; CHECK:      OpStore %i [[i_val:%\w+]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[i_val]]
+; CHECK-NEXT: OpBranch [[loop_head]]
+
+; CHECK:      [[loop_merge]] = OpLabel
+
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BC %fo
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+OpSource GLSL 140
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+%i_name = OpString "i"
+OpName %main "main"
+OpName %f "f"
+OpName %i "i"
+OpName %BC "BC"
+OpName %fo "fo"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BC = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf 4
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal
+%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal
+%main = OpFunction %void None %8
+%22 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%f = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+OpStore %f %float_0
+OpStore %i %int_0
+%decl0 = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr
+%decl1 = OpExtInst %void %ext DebugDeclare %dbg_i %i %null_expr
+OpBranch %23
+%23 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpLoopMerge %24 %25 None
+OpBranch %26
+%26 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%27 = OpLoad %int %i
+%28 = OpSLessThan %bool %27 %int_4
+OpBranchConditional %28 %29 %24
+%29 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%30 = OpLoad %float %f
+%31 = OpLoad %int %i
+%32 = OpAccessChain %_ptr_Input_float %BC %31
+%33 = OpLoad %float %32
+%34 = OpFAdd %float %30 %33
+OpStore %f %34
+OpBranch %25
+%25 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%35 = OpLoad %int %i
+%36 = OpIAdd %int %35 %int_1
+OpStore %i %36
+OpBranch %23
+%24 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpLoad %float %f
+OpStore %fo %37
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugValueForReferenceVariable) {
+  // #version 140
+  //
+  // in vec4 BC;
+  // out float fo;
+  //
+  // void main()
+  // {
+  //     float f = 0.0;
+  //     float& x = f;
+  //     for (int i=0; i<4; i++) {
+  //       x = x + BC[i];
+  //     }
+  //     fo = f;
+  // }
+
+  const std::string text = R"(
+; CHECK: [[f_name:%\w+]] = OpString "f"
+; CHECK: [[i_name:%\w+]] = OpString "i"
+; CHECK: [[x_name:%\w+]] = OpString "x"
+; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugLocalVariable [[f_name]]
+; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]]
+; CHECK: [[dbg_x:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[x_name]]
+
+; CHECK:      OpStore %f %float_0
+; CHECK-DAG:  OpExtInst %void [[ext]] DebugValue [[dbg_f]] %float_0
+; CHECK-DAG:  OpExtInst %void [[ext]] DebugValue [[dbg_x]] %float_0
+; CHECK:      OpStore %i %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] %int_0
+
+; CHECK-NOT:  DebugDeclare
+
+; CHECK:      [[loop_head:%\w+]] = OpLabel
+; CHECK:      [[phi0:%\w+]] = OpPhi %float %float_0
+; CHECK:      [[phi1:%\w+]] = OpPhi %int %int_0
+; CHECK-DAG:  OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[phi0]]
+; CHECK-DAG:  OpExtInst %void [[ext]] DebugValue [[dbg_x]] [[phi0]]
+; CHECK:      OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[phi1]]
+; CHECK:      OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] None
+; CHECK-NEXT: OpBranch [[loop_body:%\w+]]
+
+; CHECK:      [[loop_body]] = OpLabel
+; CHECK:      OpBranchConditional {{%\w+}} [[bb:%\w+]] [[loop_merge]]
+
+; CHECK:      [[bb]] = OpLabel
+; CHECK:      OpStore %f [[f_val:%\w+]]
+; CHECK-DAG:  OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_val]]
+; CHECK-DAG:  OpExtInst %void [[ext]] DebugValue [[dbg_x]] [[f_val]]
+; CHECK:      OpBranch [[loop_cont]]
+
+; CHECK:      [[loop_cont]] = OpLabel
+; CHECK:      OpStore %i [[i_val:%\w+]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[i_val]]
+; CHECK-NEXT: OpBranch [[loop_head]]
+
+; CHECK:      [[loop_merge]] = OpLabel
+
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BC %fo
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+OpSource GLSL 140
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+%i_name = OpString "i"
+%x_name = OpString "x"
+OpName %main "main"
+OpName %f "f"
+OpName %i "i"
+OpName %BC "BC"
+OpName %fo "fo"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BC = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf 4
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal
+%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src 1 0 %dbg_main FlagIsLocal
+%dbg_x = OpExtInst %void %ext DebugLocalVariable %x_name %dbg_v4f %src 2 0 %dbg_main FlagIsLocal
+%main = OpFunction %void None %8
+%22 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%f = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+OpStore %f %float_0
+OpStore %i %int_0
+%decl0 = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr
+%decl1 = OpExtInst %void %ext DebugDeclare %dbg_i %i %null_expr
+%decl2 = OpExtInst %void %ext DebugDeclare %dbg_x %f %null_expr
+OpBranch %23
+%23 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpLoopMerge %24 %25 None
+OpBranch %26
+%26 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%27 = OpLoad %int %i
+%28 = OpSLessThan %bool %27 %int_4
+OpBranchConditional %28 %29 %24
+%29 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%30 = OpLoad %float %f
+%31 = OpLoad %int %i
+%32 = OpAccessChain %_ptr_Input_float %BC %31
+%33 = OpLoad %float %32
+%34 = OpFAdd %float %30 %33
+OpStore %f %34
+OpBranch %25
+%25 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%35 = OpLoad %int %i
+%36 = OpIAdd %int %35 %int_1
+OpStore %i %36
+OpBranch %23
+%24 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpLoad %float %f
+OpStore %fo %37
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugValueForReferenceVariableInBB) {
+  // #version 140
+  //
+  // in vec4 BC;
+  // out float fo;
+  //
+  // void main()
+  // {
+  //     float f = 0.0;
+  //     for (int i=0; i<4; i++) {
+  //       float& x = f;
+  //       x = x + BC[i];
+  //       {
+  //         x = x + BC[i];
+  //       }
+  //     }
+  //     fo = f;
+  // }
+
+  const std::string text = R"(
+; CHECK: [[f_name:%\w+]] = OpString "f"
+; CHECK: [[i_name:%\w+]] = OpString "i"
+; CHECK: [[x_name:%\w+]] = OpString "x"
+; CHECK: [[dbg_main:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugFunction
+; CHECK: [[dbg_bb:%\w+]] = OpExtInst %void [[ext]] DebugLexicalBlock
+; CHECK: [[dbg_bb_child:%\w+]] = OpExtInst %void [[ext]] DebugLexicalBlock
+; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[f_name]]
+; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]]
+; CHECK: [[dbg_x:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[x_name]]
+
+; CHECK:      OpExtInst %void [[ext]] DebugScope [[dbg_main]]
+; CHECK:      OpStore %f %float_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] %float_0
+; CHECK-NEXT: OpStore %i %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] %int_0
+
+; CHECK-NOT:  DebugDeclare
+
+; CHECK:      [[loop_head:%\w+]] = OpLabel
+; CHECK:      OpExtInst %void [[ext]] DebugScope [[dbg_main]]
+; CHECK:      [[phi0:%\w+]] = OpPhi %float %float_0
+; CHECK:      [[phi1:%\w+]] = OpPhi %int %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[phi0]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[phi1]]
+; CHECK:      OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] None
+; CHECK-NEXT: OpBranch [[loop_body:%\w+]]
+
+; CHECK-NEXT: [[loop_body]] = OpLabel
+; CHECK:      OpBranchConditional {{%\w+}} [[bb:%\w+]] [[loop_merge]]
+
+; CHECK:      [[bb]] = OpLabel
+; CHECK:      OpExtInst %void [[ext]] DebugScope [[dbg_bb]]
+; CHECK:      OpStore %f [[f_val:%\w+]]
+; CHECK-DAG:  OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_val]]
+; CHECK-DAG:  OpExtInst %void [[ext]] DebugValue [[dbg_x]] [[f_val]]
+; CHECK:      OpBranch [[bb_child:%\w+]]
+
+; CHECK:      [[bb_child]] = OpLabel
+; CHECK:      OpExtInst %void [[ext]] DebugScope [[dbg_bb_child]]
+; CHECK:      OpStore %f [[new_f_val:%\w+]]
+; CHECK-DAG:  OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[new_f_val]]
+; CHECK-DAG:  OpExtInst %void [[ext]] DebugValue [[dbg_x]] [[new_f_val]]
+; CHECK:      OpBranch [[loop_cont]]
+
+; CHECK:      [[loop_cont]] = OpLabel
+; CHECK:      OpStore %i [[i_val:%\w+]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[i_val]]
+; CHECK-NEXT: OpBranch [[loop_head]]
+
+; CHECK:      [[loop_merge]] = OpLabel
+
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BC %fo
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+OpSource GLSL 140
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+%i_name = OpString "i"
+%x_name = OpString "x"
+OpName %main "main"
+OpName %f "f"
+OpName %i "i"
+OpName %BC "BC"
+OpName %fo "fo"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BC = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf 4
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%bb = OpExtInst %void %ext DebugLexicalBlock %src 0 0 %dbg_main
+%bb_child = OpExtInst %void %ext DebugLexicalBlock %src 1 0 %bb
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal
+%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src 1 0 %dbg_main FlagIsLocal
+%dbg_x = OpExtInst %void %ext DebugLocalVariable %x_name %dbg_v4f %src 2 0 %bb FlagIsLocal
+%main = OpFunction %void None %8
+%22 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%f = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+OpStore %f %float_0
+OpStore %i %int_0
+%decl0 = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr
+%decl1 = OpExtInst %void %ext DebugDeclare %dbg_i %i %null_expr
+OpBranch %23
+%23 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpLoopMerge %24 %25 None
+OpBranch %26
+%26 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%27 = OpLoad %int %i
+%28 = OpSLessThan %bool %27 %int_4
+OpBranchConditional %28 %29 %24
+%29 = OpLabel
+%scope = OpExtInst %void %ext DebugScope %bb
+%decl2 = OpExtInst %void %ext DebugDeclare %dbg_x %f %null_expr
+%30 = OpLoad %float %f
+%31 = OpLoad %int %i
+%32 = OpAccessChain %_ptr_Input_float %BC %31
+%33 = OpLoad %float %32
+%34 = OpFAdd %float %30 %33
+OpStore %f %34
+OpBranch %38
+%38 = OpLabel
+%child_scope = OpExtInst %void %ext DebugScope %bb_child
+%39 = OpLoad %float %f
+%40 = OpFAdd %float %39 %33
+OpStore %f %40
+OpBranch %25
+%25 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%35 = OpLoad %int %i
+%36 = OpIAdd %int %35 %int_1
+OpStore %i %36
+OpBranch %23
+%24 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpLoad %float %f
+OpStore %fo %37
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugForLoopUseDebugValueInsteadOfDebugDeclare) {
+  // #version 140
+  //
+  // in vec4 BC;
+  // out float fo;
+  //
+  // struct S {
+  //     float f;
+  //     int i;
+  // };
+  //
+  // void main()
+  // {
+  //     S foo = {0.0, 0};
+  //     for (; foo.i<4; foo.i++) {
+  //       foo.f = foo.f + BC[foo.i];
+  //     }
+  //     fo = foo.f;
+  // }
+
+  const std::string text = R"(
+; CHECK: [[f_name:%\w+]] = OpString "f"
+; CHECK: [[empty_expr:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugExpression
+; CHECK: [[deref_op:%\w+]] = OpExtInst %void [[ext]] DebugOperation Deref
+; CHECK: [[deref:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref_op]]
+; CHECK: [[dbg_foo:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[f_name]]
+
+; CHECK:      OpStore %f %float_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] %float_0 [[empty_expr]] %uint_0
+; CHECK-NEXT: OpStore %i %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] %int_0 [[empty_expr]] %uint_1
+
+; CHECK:      [[loop_head:%\w+]] = OpLabel
+; CHECK:      [[phi0:%\w+]] = OpPhi %float %float_0
+; CHECK:      [[phi1:%\w+]] = OpPhi %int %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] [[phi0]] [[empty_expr]] %uint_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] [[phi1]] [[empty_expr]] %uint_1
+; CHECK:      OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] None
+; CHECK-NEXT: OpBranch [[loop_body:%\w+]]
+
+; CHECK:      [[loop_body]] = OpLabel
+; CHECK:      OpBranchConditional {{%\w+}} [[bb:%\w+]] [[loop_merge]]
+
+; CHECK:      [[bb]] = OpLabel
+; CHECK:      OpStore %f [[f_val:%\w+]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] [[f_val]] [[empty_expr]] %uint_0
+; CHECK-NEXT: OpBranch [[loop_cont]]
+
+; CHECK:      [[loop_cont]] = OpLabel
+; CHECK:      OpStore %i [[i_val:%\w+]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] [[i_val]] [[empty_expr]] %uint_1
+; CHECK-NEXT: OpBranch [[loop_head]]
+
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BC %fo
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+OpSource GLSL 140
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+OpName %main "main"
+OpName %f "f"
+OpName %i "i"
+OpName %BC "BC"
+OpName %fo "fo"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BC = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+%deref_op = OpExtInst %void %ext DebugOperation Deref
+%deref = OpExtInst %void %ext DebugExpression %deref_op
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf 4
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_foo = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal
+%main = OpFunction %void None %8
+%22 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%f = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+OpStore %f %float_0
+OpStore %i %int_0
+%decl0 = OpExtInst %void %ext DebugValue %dbg_foo %f %deref %uint_0
+%decl1 = OpExtInst %void %ext DebugValue %dbg_foo %i %deref %uint_1
+OpBranch %23
+%23 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpLoopMerge %24 %25 None
+OpBranch %26
+%26 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%27 = OpLoad %int %i
+%28 = OpSLessThan %bool %27 %int_4
+OpBranchConditional %28 %29 %24
+%29 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%30 = OpLoad %float %f
+%31 = OpLoad %int %i
+%32 = OpAccessChain %_ptr_Input_float %BC %31
+%33 = OpLoad %float %32
+%34 = OpFAdd %float %30 %33
+OpStore %f %34
+OpBranch %25
+%25 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%35 = OpLoad %int %i
+%36 = OpIAdd %int %35 %int_1
+OpStore %i %36
+OpBranch %23
+%24 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpLoad %float %f
+OpStore %fo %37
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugValueNotUsedForDebugDeclare) {
+  // #version 140
+  //
+  // in vec4 BC;
+  // out float fo;
+  //
+  // void main()
+  // {
+  //     float f = 0.0;
+  //     for (int i=0; i<4; i++) {
+  //       f = f + BC[i];
+  //     }
+  //     fo = f;
+  // }
+
+  const std::string text = R"(
+; CHECK: [[f_name:%\w+]] = OpString "f"
+; CHECK: [[i_name:%\w+]] = OpString "i"
+; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugLocalVariable [[f_name]]
+; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]]
+
+; CHECK:      OpStore %f %float_0
+; CHECK-NEXT: OpStore %i %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] %f
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] %i
+
+; CHECK-NOT:  DebugValue
+; CHECK-NOT:  DebugDeclare
+
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BC %fo
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+OpSource GLSL 140
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+%i_name = OpString "i"
+OpName %main "main"
+OpName %f "f"
+OpName %i "i"
+OpName %BC "BC"
+OpName %fo "fo"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BC = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf 4
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal
+%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src 1 0 %dbg_main FlagIsLocal
+%main = OpFunction %void None %8
+%22 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%f = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+OpStore %f %float_0
+OpStore %i %int_0
+%decl0 = OpExtInst %void %ext DebugValue %dbg_f %f %null_expr
+%decl1 = OpExtInst %void %ext DebugValue %dbg_i %i %null_expr
+OpBranch %23
+%23 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpLoopMerge %24 %25 None
+OpBranch %26
+%26 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%27 = OpLoad %int %i
+%28 = OpSLessThan %bool %27 %int_4
+OpBranchConditional %28 %29 %24
+%29 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%30 = OpLoad %float %f
+%31 = OpLoad %int %i
+%32 = OpAccessChain %_ptr_Input_float %BC %31
+%33 = OpLoad %float %32
+%34 = OpFAdd %float %30 %33
+OpStore %f %34
+OpBranch %25
+%25 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%35 = OpLoad %int %i
+%36 = OpIAdd %int %35 %int_1
+OpStore %i %36
+OpBranch %23
+%24 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpLoad %float %f
+OpStore %fo %37
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugNestedForLoop) {
+  const std::string text = R"(
+; CHECK: = OpFunction
+; CHECK-NEXT: [[entry:%\w+]] = OpLabel
+; CHECK: OpStore %f %float_0
+; CHECK-NEXT: = OpExtInst %void [[ext:%\w+]] DebugValue [[dbg_f:%\w+]] %float_0
+
+; CHECK: [[outer_header:%\w+]] = OpLabel
+; CHECK: [[outer_f:%\w+]] = OpPhi %float %float_0 [[entry]] [[inner_f:%\w+]] [[outer_be:%\w+]]
+; CHECK: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[outer_f]]
+
+; CHECK: [[inner_pre_header:%\w+]] = OpLabel
+; CHECK: [[inner_header:%\w+]] = OpLabel
+; CHECK: [[inner_f]] = OpPhi %float [[outer_f]] [[inner_pre_header]] [[f_next:%\w+]] [[inner_be:%\w+]]
+; CHECK: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[inner_f]]
+
+; CHECK: [[inner_be]] = OpLabel
+; CHECK: [[f_next]] = OpFAdd %float [[inner_f]]
+; CHECK-NEXT: OpStore %f [[f_next]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_next]]
+
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BC %fo
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+OpSource GLSL 450
+OpName %main "main"
+OpName %f "f"
+OpName %i "i"
+OpName %j "j"
+OpName %BC "BC"
+OpName %fo "fo"
+OpDecorate %BC Location 0
+OpDecorate %fo Location 0
+%void = OpTypeVoid
+%9 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%mat4v4float = OpTypeMatrix %v4float 4
+%_ptr_Input_mat4v4float = OpTypePointer Input %mat4v4float
+%BC = OpVariable %_ptr_Input_mat4v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+
+; Debug information
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+
+%main = OpFunction %void None %9
+%24 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+%f = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+%j = OpVariable %_ptr_Function_int Function
+
+; DebugDeclare
+OpStore %f %float_0
+%decl = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr
+
+OpStore %i %int_0
+OpBranch %25
+%25 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%26 = OpLoad %int %i
+%27 = OpSLessThan %bool %26 %int_4
+OpLoopMerge %28 %29 None
+OpBranchConditional %27 %30 %28
+%30 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+OpStore %j %int_0
+OpBranch %31
+%31 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%32 = OpLoad %int %j
+%33 = OpSLessThan %bool %32 %int_4
+OpLoopMerge %50 %34 None
+OpBranchConditional %33 %34 %50
+%34 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%35 = OpLoad %float %f
+%36 = OpLoad %int %i
+%37 = OpLoad %int %j
+%38 = OpAccessChain %_ptr_Input_float %BC %36 %37
+%39 = OpLoad %float %38
+%40 = OpFAdd %float %35 %39
+OpStore %f %40
+%41 = OpLoad %int %j
+%42 = OpIAdd %int %41 %int_1
+OpStore %j %42
+OpBranch %31
+%50 = OpLabel
+%s6 = OpExtInst %void %ext DebugScope %dbg_main
+OpBranch %29
+%29 = OpLabel
+%s7 = OpExtInst %void %ext DebugScope %dbg_main
+%43 = OpLoad %int %i
+%44 = OpIAdd %int %43 %int_1
+OpStore %i %44
+OpBranch %25
+%28 = OpLabel
+%s8 = OpExtInst %void %ext DebugScope %dbg_main
+%45 = OpLoad %float %f
+OpStore %fo %45
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugForLoopWithContinue) {
+  const std::string text = R"(
+; CHECK: = OpFunction
+; CHECK-NEXT: [[entry:%\w+]] = OpLabel
+; CHECK: OpStore %f %float_0
+; CHECK-NEXT: = OpExtInst %void [[ext:%\w+]] DebugValue [[dbg_f:%\w+]] %float_0
+
+; CHECK: [[outer_header:%\w+]] = OpLabel
+; CHECK: [[outer_f:%\w+]] = OpPhi %float %float_0 [[entry]] [[inner_f:%\w+]] [[cont:%\w+]]
+; CHECK: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[outer_f]]
+
+; CHECK: [[f_next:%\w+]] = OpFAdd %float [[outer_f]]
+; CHECK-NEXT: OpStore %f [[f_next]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_next]]
+
+; CHECK: [[cont]] = OpLabel
+; CHECK: [[inner_f]] = OpPhi %float [[outer_f]] {{%\d+}} [[f_next]] {{%\d+}}
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[inner_f]]
+
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BC %fo
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+OpSource GLSL 140
+OpName %main "main"
+OpName %f "f"
+OpName %i "i"
+OpName %t "t"
+OpName %BC "BC"
+OpName %fo "fo"
+%void = OpTypeVoid
+%9 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BC = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+
+; Debug information
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+
+%main = OpFunction %void None %9
+%23 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%f = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+%t = OpVariable %_ptr_Function_float Function
+
+; DebugDeclare
+OpStore %f %float_0
+%decl = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr
+
+OpStore %i %int_0
+OpBranch %24
+%24 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpLoopMerge %25 %26 None
+OpBranch %27
+%27 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%28 = OpLoad %int %i
+%29 = OpSLessThan %bool %28 %int_4
+OpBranchConditional %29 %30 %25
+%30 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%31 = OpLoad %int %i
+%32 = OpAccessChain %_ptr_Input_float %BC %31
+%33 = OpLoad %float %32
+OpStore %t %33
+%34 = OpLoad %float %t
+%35 = OpFOrdLessThan %bool %34 %float_0
+OpSelectionMerge %36 None
+OpBranchConditional %35 %37 %36
+%37 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+OpBranch %26
+%36 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%38 = OpLoad %float %f
+%39 = OpLoad %float %t
+%40 = OpFAdd %float %38 %39
+OpStore %f %40
+OpBranch %26
+%26 = OpLabel
+%s6 = OpExtInst %void %ext DebugScope %dbg_main
+%41 = OpLoad %int %i
+%42 = OpIAdd %int %41 %int_1
+OpStore %i %42
+OpBranch %24
+%25 = OpLabel
+%s7 = OpExtInst %void %ext DebugScope %dbg_main
+%43 = OpLoad %float %f
+OpStore %fo %43
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugIfElse) {
+  const std::string text = R"(
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %f %BaseColor %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%v_name = OpString "v"
+OpSource GLSL 140
+OpName %main "main"
+OpName %f "f"
+OpName %v "v"
+OpName %BaseColor "BaseColor"
+OpName %gl_FragColor "gl_FragColor"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Input_float = OpTypePointer Input %float
+%f = OpVariable %_ptr_Input_float Input
+%float_0 = OpConstant %float 0
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%float_0_5 = OpConstant %float 0.5
+%float_1 = OpConstant %float 1
+%18 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+
+; Debug information
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_v = OpExtInst %void %ext DebugLocalVariable %v_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+
+%main = OpFunction %void None %8
+%20 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+
+; DebugDeclare
+%v = OpVariable %_ptr_Function_v4float Function
+%decl = OpExtInst %void %ext DebugDeclare %dbg_v %v %null_expr
+
+%21 = OpLoad %float %f
+%22 = OpFOrdGreaterThanEqual %bool %21 %float_0
+OpSelectionMerge %23 None
+OpBranchConditional %22 %24 %25
+
+; CHECK: OpBranchConditional
+; CHECK-NEXT: [[br0:%\w+]] = OpLabel
+; CHECK: OpStore %v [[v0:%\w+]]
+; CHECK-NEXT: = OpExtInst %void [[ext:%\w+]] DebugValue [[dbg_v:%\w+]] [[v0]]
+%24 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+%26 = OpLoad %v4float %BaseColor
+%27 = OpVectorTimesScalar %v4float %26 %float_0_5
+OpStore %v %27
+OpBranch %23
+
+; CHECK: [[br1:%\w+]] = OpLabel
+; CHECK: OpStore %v [[v1:%\w+]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[v1]]
+%25 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%28 = OpLoad %v4float %BaseColor
+%29 = OpFAdd %v4float %28 %18
+OpStore %v %29
+OpBranch %23
+
+; CHECK: [[phi:%\w+]] = OpPhi %v4float [[v0]] [[br0]] [[v1]] [[br1]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[phi]]
+%23 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%30 = OpLoad %v4float %v
+OpStore %gl_FragColor %30
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugSwitch) {
+  const std::string text = R"(
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BaseColor %f %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%v_name = OpString "v"
+OpSource GLSL 140
+OpName %main "main"
+OpName %v "v"
+OpName %BaseColor "BaseColor"
+OpName %i "i"
+OpName %f "f"
+OpName %gl_FragColor "gl_FragColor"
+%void = OpTypeVoid
+%9 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%_ptr_Input_float = OpTypePointer Input %float
+%f = OpVariable %_ptr_Input_float Input
+%float_0_25 = OpConstant %float 0.25
+%float_0_75 = OpConstant %float 0.75
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+
+; Debug information
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_v = OpExtInst %void %ext DebugLocalVariable %v_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+
+%main = OpFunction %void None %9
+%20 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%v = OpVariable %_ptr_Function_v4float Function
+%i = OpVariable %_ptr_Function_int Function
+%21 = OpLoad %v4float %BaseColor
+
+; DebugDeclare
+OpStore %v %21
+%decl = OpExtInst %void %ext DebugDeclare %dbg_v %v %null_expr
+
+; CHECK: %main = OpFunction %void None
+; CHECK-NEXT: [[entry:%\w+]] = OpLabel
+; CHECK: OpStore %v [[v0:%\w+]]
+; CHECK-NEXT: = OpExtInst %void [[ext:%\w+]] DebugValue [[dbg_v:%\w+]] [[v0]]
+; CHECK: OpSwitch {{%\w+}} [[case0:%\w+]] 0 [[case1:%\w+]] 1 [[case2:%\w+]] 2 [[case3:%\w+]]
+; CHECK: OpStore %v [[v1:%\w+]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[v1]]
+; CHECK: OpStore %v [[v2:%\w+]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[v2]]
+; CHECK: [[phi0:%\w+]] = OpPhi %v4float [[v0]] [[entry]] [[v2]] [[case2]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[phi0]]
+; CHECK: OpStore %v [[v3:%\w+]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[v3]]
+; CHECK: [[phi1:%\w+]] = OpPhi %v4float [[v0]] [[case0]] [[v1]] [[case1]] [[v3]] [[case3]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[phi1]]
+
+%22 = OpLoad %float %f
+%23 = OpConvertFToS %int %22
+OpStore %i %23
+%24 = OpLoad %int %i
+OpSelectionMerge %25 None
+OpSwitch %24 %26 0 %27 1 %28 2 %29
+%26 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpBranch %25
+%27 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%30 = OpLoad %v4float %v
+%31 = OpVectorTimesScalar %v4float %30 %float_0_25
+OpStore %v %31
+OpBranch %25
+%28 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%32 = OpLoad %v4float %v
+%33 = OpCompositeConstruct %v4float %float_0_25 %float_0_25 %float_0_25 %float_0_25
+%34 = OpFAdd %v4float %32 %33
+OpStore %v %34
+OpBranch %29
+%29 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%35 = OpLoad %v4float %v
+%36 = OpVectorTimesScalar %v4float %35 %float_0_75
+OpStore %v %36
+OpBranch %25
+%25 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpLoad %v4float %v
+OpStore %gl_FragColor %37
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugSwapProblem) {
+  // #version 140
+  //
+  // in float fe;
+  // out float fo;
+  //
+  // void main()
+  // {
+  //     float f1 = 0.0;
+  //     float f2 = 1.0;
+  //     int ie = int(fe);
+  //     for (int i=0; i<ie; i++) {
+  //       float t = f1;
+  //       f1 = f2;
+  //       f2 = t;
+  //     }
+  //     fo = f1;
+  // }
+  //
+  // Because of the swap in the for loop, it generates the following phi
+  // instructions:
+  //
+  // [[phi_f2]] = OpPhi %float %float_1 [[entry]] [[phi_f1]] ..
+  // [[phi_f1]] = OpPhi %float %float_0 [[entry]] [[phi_f2]] ..
+  //
+  // Since they are used as operands by each other, we want to clearly check
+  // what DebugValue we have to add for them.
+
+  const std::string text = R"(
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %fe %fo
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%t_name = OpString "t"
+OpName %main "main"
+OpName %f1 "f1"
+OpName %f2 "f2"
+OpName %ie "ie"
+OpName %fe "fe"
+OpName %i "i"
+OpName %t "t"
+OpName %fo "fo"
+%void = OpTypeVoid
+%11 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%float_1 = OpConstant %float 1
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%_ptr_Input_float = OpTypePointer Input %float
+%fe = OpVariable %_ptr_Input_float Input
+%int_0 = OpConstant %int 0
+%bool = OpTypeBool
+%int_1 = OpConstant %int 1
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+
+; Debug information
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_f1 = OpExtInst %void %ext DebugLocalVariable %t_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+%dbg_f2 = OpExtInst %void %ext DebugLocalVariable %t_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+%dbg_i = OpExtInst %void %ext DebugLocalVariable %t_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+
+%main = OpFunction %void None %11
+%23 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%f1 = OpVariable %_ptr_Function_float Function
+%f2 = OpVariable %_ptr_Function_float Function
+%ie = OpVariable %_ptr_Function_int Function
+%i = OpVariable %_ptr_Function_int Function
+%t = OpVariable %_ptr_Function_float Function
+OpStore %f1 %float_0
+OpStore %f2 %float_1
+%24 = OpLoad %float %fe
+%25 = OpConvertFToS %int %24
+OpStore %ie %25
+OpStore %i %int_0
+
+; DebugDeclare
+%decl0 = OpExtInst %void %ext DebugDeclare %dbg_f1 %f1 %null_expr
+%decl1 = OpExtInst %void %ext DebugDeclare %dbg_f2 %f2 %null_expr
+%decl2 = OpExtInst %void %ext DebugDeclare %dbg_i  %i  %null_expr
+
+; CHECK: %main = OpFunction %void None
+; CHECK-NEXT: [[entry:%\w+]] = OpLabel
+
+; CHECK: OpStore %f1 %float_0
+; CHECK-NEXT: = OpExtInst %void [[ext:%\w+]] DebugValue [[dbg_f1:%\w+]] %float_0
+; CHECK: OpStore %f2 %float_1
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_f2:%\w+]] %float_1
+; CHECK: OpStore %i %int_0
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_i:%\w+]] %int_0
+
+; CHECK: [[phi_f2:%\w+]] = OpPhi %float %float_1 [[entry]] [[phi_f1:%\w+]]
+; CHECK: [[phi_f1]] = OpPhi %float %float_0 [[entry]] [[phi_f2]]
+; CHECK: [[phi_i:%\w+]] = OpPhi %int %int_0 [[entry]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_f2]] [[phi_f2]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_f1]] [[phi_f1]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[phi_i]]
+
+OpBranch %26
+%26 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpLoopMerge %27 %28 None
+OpBranch %29
+%29 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%30 = OpLoad %int %i
+%31 = OpLoad %int %ie
+%32 = OpSLessThan %bool %30 %31
+OpBranchConditional %32 %33 %27
+%33 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%34 = OpLoad %float %f1
+OpStore %t %34
+%35 = OpLoad %float %f2
+OpStore %f1 %35
+%36 = OpLoad %float %t
+OpStore %f2 %36
+OpBranch %28
+%28 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpLoad %int %i
+%38 = OpIAdd %int %37 %int_1
+OpStore %i %38
+OpBranch %26
+%27 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%39 = OpLoad %float %f1
+OpStore %fo %39
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    No optimization in the presence of
diff --git a/third_party/SPIRV-Tools/test/opt/loop_optimizations/loop_fission.cpp b/third_party/SPIRV-Tools/test/opt/loop_optimizations/loop_fission.cpp
index e513f42..55b9c26 100644
--- a/third_party/SPIRV-Tools/test/opt/loop_optimizations/loop_fission.cpp
+++ b/third_party/SPIRV-Tools/test/opt/loop_optimizations/loop_fission.cpp
@@ -2203,11 +2203,11 @@
 %45 = OpAccessChain %18 %4 %39
 OpStore %45 %44
 %46 = OpIEqual %12 %39 %19
-OpSelectionMerge %47 None
-OpBranchConditional %46 %51 %47
+OpSelectionMerge %48 None
+OpBranchConditional %46 %47 %48
 %47 = OpLabel
 OpBranch %52
-%51 = OpLabel
+%48 = OpLabel
 OpBranch %52
 %52 = OpLabel
 %53 = OpIAdd %8 %39 %19
@@ -2242,16 +2242,16 @@
 OpReturn
 OpFunctionEnd
 )";
-  // clang-format on
-  std::unique_ptr<IRContext> context =
-      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source,
-                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  Module* module = context->module();
-  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
-                             << source << std::endl;
+// clang-format on
+std::unique_ptr<IRContext> context =
+    BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source,
+                SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+Module* module = context->module();
+EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                           << source << std::endl;
 
-  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
-  SinglePassRunAndCheck<LoopFissionPass>(source, expected, true);
+SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+SinglePassRunAndCheck<LoopFissionPass>(source, expected, true);
 }
 
 /*
diff --git a/third_party/SPIRV-Tools/test/opt/loop_optimizations/unroll_assumptions.cpp b/third_party/SPIRV-Tools/test/opt/loop_optimizations/unroll_assumptions.cpp
index 62f77d7..0f93302 100644
--- a/third_party/SPIRV-Tools/test/opt/loop_optimizations/unroll_assumptions.cpp
+++ b/third_party/SPIRV-Tools/test/opt/loop_optimizations/unroll_assumptions.cpp
@@ -467,6 +467,73 @@
   SinglePassRunAndCheck<LoopUnroller>(text, text, false);
 }
 
+TEST_F(PassClassTest, KillInBody) {
+  const std::string text = R"(OpCapability Shader
+OpMemoryModel Logical Simple
+OpEntryPoint Fragment %1 "main"
+OpExecutionMode %1 OriginUpperLeft
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%4 = OpTypeBool
+%5 = OpTypeInt 32 0
+%6 = OpConstant %5 0
+%7 = OpConstant %5 1
+%8 = OpConstant %5 5
+%1 = OpFunction %2 None %3
+%9 = OpLabel
+OpBranch %10
+%10 = OpLabel
+%11 = OpPhi %5 %6 %9 %12 %13
+%14 = OpULessThan %4 %11 %8
+OpLoopMerge %15 %13 Unroll
+OpBranchConditional %14 %16 %15
+%16 = OpLabel
+OpKill
+%13 = OpLabel
+%12 = OpIAdd %5 %11 %7
+OpBranch %10
+%15 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndCheck<LoopUnroller>(text, text, false);
+}
+
+TEST_F(PassClassTest, TerminateInvocationInBody) {
+  const std::string text = R"(OpCapability Shader
+OpExtension "SPV_KHR_terminate_invocation"
+OpMemoryModel Logical Simple
+OpEntryPoint Fragment %1 "main"
+OpExecutionMode %1 OriginUpperLeft
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%4 = OpTypeBool
+%5 = OpTypeInt 32 0
+%6 = OpConstant %5 0
+%7 = OpConstant %5 1
+%8 = OpConstant %5 5
+%1 = OpFunction %2 None %3
+%9 = OpLabel
+OpBranch %10
+%10 = OpLabel
+%11 = OpPhi %5 %6 %9 %12 %13
+%14 = OpULessThan %4 %11 %8
+OpLoopMerge %15 %13 Unroll
+OpBranchConditional %14 %16 %15
+%16 = OpLabel
+OpTerminateInvocation
+%13 = OpLabel
+%12 = OpIAdd %5 %11 %7
+OpBranch %10
+%15 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndCheck<LoopUnroller>(text, text, false);
+}
+
 /*
 Generated from the following GLSL
 #version 440 core
diff --git a/third_party/SPIRV-Tools/test/opt/loop_optimizations/unroll_simple.cpp b/third_party/SPIRV-Tools/test/opt/loop_optimizations/unroll_simple.cpp
index f551e7c..6030135 100644
--- a/third_party/SPIRV-Tools/test/opt/loop_optimizations/unroll_simple.cpp
+++ b/third_party/SPIRV-Tools/test/opt/loop_optimizations/unroll_simple.cpp
@@ -2998,6 +2998,76 @@
                                            kUnrollFactor);
 }
 
+// Test that a loop containing an unreachable merge block can still be unrolled
+// correctly.
+TEST_F(PassClassTest, UnreachableMerge) {
+  const std::string text = R"(
+; Identify the first iteration of the unrolled loop, and make sure it contains
+; the unreachable merge block.
+; The first SelectionMerge corresponds to the original loop merge.
+; The second is the branch in the loop.
+; CHECK: OpSelectionMerge {{%\w+}} None
+; CHECK: OpSelectionMerge [[unrch1:%\w+]] None
+; CHECK: [[unrch1]] = OpLabel
+; CHECK-NEXT: OpUnreachable
+; Identify the second iteration of the unrolled loop, and make sure it contains
+; the unreachable merge block.
+; The first SelectionMerge corresponds to the original loop merge
+; The second is the branch in the loop.
+; CHECK: OpSelectionMerge {{%\w+}} None
+; CHECK: OpSelectionMerge [[unrch2:%\w+]] None
+; CHECK: [[unrch2]] = OpLabel
+; CHECK-NEXT: OpUnreachable
+
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 64 1 1
+               OpSource HLSL 600
+               OpName %main "main"
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_2 = OpConstant %uint 2
+     %uint_1 = OpConstant %uint 1
+       %bool = OpTypeBool
+       %void = OpTypeVoid
+         %18 = OpTypeFunction %void
+       %main = OpFunction %void None %18
+         %23 = OpLabel
+               OpBranch %24
+         %24 = OpLabel
+         %28 = OpPhi %uint %uint_0 %23 %29 %27
+         %30 = OpULessThan %bool %28 %uint_2
+               OpLoopMerge %31 %27 Unroll
+               OpBranchConditional %30 %32 %31
+         %32 = OpLabel
+               OpSelectionMerge %33 None
+               OpSwitch %uint_0 %34
+         %34 = OpLabel
+         %35 = OpUndef %bool
+               OpSelectionMerge %36 None
+               OpBranchConditional %35 %37 %38
+         %38 = OpLabel
+               OpBranch %33
+         %37 = OpLabel
+               OpBranch %33
+         %36 = OpLabel
+               OpUnreachable
+         %33 = OpLabel
+               OpBranch %27
+         %27 = OpLabel
+         %29 = OpIAdd %uint %28 %uint_1
+               OpBranch %24
+         %31 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const bool kFullyUnroll = true;
+  const uint32_t kUnrollFactor = 0;
+  SinglePassRunAndMatch<opt::LoopUnroller>(text, true, kFullyUnroll,
+                                           kUnrollFactor);
+}
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
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 e55a48f..f426904 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
@@ -99,6 +99,78 @@
   SinglePassRunAndCheck<MergeReturnPass>(before, after, false, true);
 }
 
+TEST_F(MergeReturnPassTest, DebugTwoReturnsNoValue) {
+  const std::string before =
+      R"(OpCapability Addresses
+OpCapability Kernel
+OpCapability GenericPointer
+OpCapability Linkage
+%10 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Physical32 OpenCL
+OpEntryPoint Kernel %6 "simple_kernel"
+%11 = OpString "test"
+%2 = OpTypeVoid
+%3 = OpTypeBool
+%4 = OpConstantFalse %3
+%1 = OpTypeFunction %2
+%12 = OpExtInst %2 %10 DebugSource %11
+%13 = OpExtInst %2 %10 DebugCompilationUnit 1 4 %12 HLSL
+%14 = OpExtInst %2 %10 DebugTypeFunction FlagIsProtected|FlagIsPrivate %2
+%15 = OpExtInst %2 %10 DebugFunction %11 %14 %12 0 0 %13 %11 FlagIsProtected|FlagIsPrivate 0 %6
+%6 = OpFunction %2 None %1
+%7 = OpLabel
+OpBranchConditional %4 %8 %9
+%8 = OpLabel
+%16 = OpExtInst %2 %10 DebugScope %15
+OpLine %11 100 0
+OpReturn
+%9 = OpLabel
+%17 = OpExtInst %2 %10 DebugScope %13
+OpLine %11 200 0
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(OpCapability Addresses
+OpCapability Kernel
+OpCapability GenericPointer
+OpCapability Linkage
+%10 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Physical32 OpenCL
+OpEntryPoint Kernel %6 "simple_kernel"
+%11 = OpString "test"
+%2 = OpTypeVoid
+%3 = OpTypeBool
+%4 = OpConstantFalse %3
+%1 = OpTypeFunction %2
+%12 = OpExtInst %2 %10 DebugSource %11
+%13 = OpExtInst %2 %10 DebugCompilationUnit 1 4 %12 HLSL
+%14 = OpExtInst %2 %10 DebugTypeFunction FlagIsProtected|FlagIsPrivate %2
+%15 = OpExtInst %2 %10 DebugFunction %11 %14 %12 0 0 %13 %11 FlagIsProtected|FlagIsPrivate 0 %6
+%6 = OpFunction %2 None %1
+%7 = OpLabel
+OpBranchConditional %4 %8 %9
+%8 = OpLabel
+%19 = OpExtInst %2 %10 DebugScope %15
+OpLine %11 100 0
+OpBranch %18
+%20 = OpExtInst %2 %10 DebugNoScope
+%9 = OpLabel
+%21 = OpExtInst %2 %10 DebugScope %13
+OpLine %11 200 0
+OpBranch %18
+%22 = OpExtInst %2 %10 DebugNoScope
+%18 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndCheck<MergeReturnPass>(before, after, false, true);
+}
+
 TEST_F(MergeReturnPassTest, TwoReturnsWithValues) {
   const std::string before =
       R"(OpCapability Linkage
@@ -261,6 +333,92 @@
   SinglePassRunAndCheck<MergeReturnPass>(before, after, false, true);
 }
 
+TEST_F(MergeReturnPassTest, DebugUnreachableReturnsWithValues) {
+  const std::string before =
+      R"(OpCapability Linkage
+OpCapability Kernel
+%13 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical OpenCL
+%14 = OpString "test"
+OpDecorate %7 LinkageAttributes "simple_kernel" Export
+%1 = OpTypeInt 32 0
+%20 = OpTypeVoid
+%2 = OpTypeBool
+%3 = OpConstantFalse %2
+%4 = OpConstant %1 0
+%5 = OpConstant %1 1
+%6 = OpTypeFunction %1
+%15 = OpExtInst %20 %13 DebugSource %14
+%16 = OpExtInst %20 %13 DebugCompilationUnit 1 4 %15 HLSL
+%17 = OpExtInst %20 %13 DebugTypeFunction FlagIsProtected|FlagIsPrivate %20
+%18 = OpExtInst %20 %13 DebugFunction %14 %17 %15 0 0 %16 %14 FlagIsProtected|FlagIsPrivate 0 %7
+%7 = OpFunction %1 None %6
+%8 = OpLabel
+%9 = OpIAdd %1 %4 %5
+%19 = OpExtInst %20 %13 DebugScope %18
+OpLine %14 100 0
+OpReturnValue %9
+%10 = OpLabel
+OpBranchConditional %3 %11 %12
+%11 = OpLabel
+%21 = OpExtInst %20 %13 DebugScope %16
+OpLine %14 200 0
+OpReturnValue %4
+%12 = OpLabel
+%22 = OpExtInst %20 %13 DebugScope %18
+OpLine %14 300 0
+OpReturnValue %5
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(OpCapability Linkage
+OpCapability Kernel
+%13 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical OpenCL
+%14 = OpString "test"
+OpDecorate %7 LinkageAttributes "simple_kernel" Export
+%1 = OpTypeInt 32 0
+%20 = OpTypeVoid
+%2 = OpTypeBool
+%3 = OpConstantFalse %2
+%4 = OpConstant %1 0
+%5 = OpConstant %1 1
+%6 = OpTypeFunction %1
+%15 = OpExtInst %20 %13 DebugSource %14
+%16 = OpExtInst %20 %13 DebugCompilationUnit 1 4 %15 HLSL
+%17 = OpExtInst %20 %13 DebugTypeFunction FlagIsProtected|FlagIsPrivate %20
+%18 = OpExtInst %20 %13 DebugFunction %14 %17 %15 0 0 %16 %14 FlagIsProtected|FlagIsPrivate 0 %7
+%7 = OpFunction %1 None %6
+%8 = OpLabel
+%9 = OpIAdd %1 %4 %5
+%25 = OpExtInst %20 %13 DebugScope %18
+OpLine %14 100 0
+OpBranch %23
+%26 = OpExtInst %20 %13 DebugNoScope
+%10 = OpLabel
+OpBranchConditional %3 %11 %12
+%11 = OpLabel
+%27 = OpExtInst %20 %13 DebugScope %16
+OpLine %14 200 0
+OpBranch %23
+%28 = OpExtInst %20 %13 DebugNoScope
+%12 = OpLabel
+%29 = OpExtInst %20 %13 DebugScope %18
+OpLine %14 300 0
+OpBranch %23
+%30 = OpExtInst %20 %13 DebugNoScope
+%23 = OpLabel
+%24 = OpPhi %1 %9 %8 %4 %11 %5 %12
+OpReturnValue %24
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndCheck<MergeReturnPass>(before, after, false, true);
+}
+
 TEST_F(MergeReturnPassTest, StructuredControlFlowWithUnreachableMerge) {
   const std::string before =
       R"(
@@ -307,6 +465,68 @@
   SinglePassRunAndMatch<MergeReturnPass>(before, false);
 }
 
+TEST_F(MergeReturnPassTest, DebugStructuredControlFlowWithUnreachableMerge) {
+  const std::string before =
+      R"(
+; CHECK: [[false:%\w+]] = OpConstantFalse
+; CHECK: [[true:%\w+]] = OpConstantTrue
+; CHECK: OpFunction
+; CHECK: [[var:%\w+]] = OpVariable [[:%\w+]] Function [[false]]
+; CHECK: OpSelectionMerge [[return_block:%\w+]]
+; CHECK: OpSelectionMerge [[merge_lab:%\w+]]
+; CHECK: OpBranchConditional [[cond:%\w+]] [[if_lab:%\w+]] [[then_lab:%\w+]]
+; CHECK: [[if_lab]] = OpLabel
+; CHECK-NEXT: OpStore [[var]] [[true]]
+; CHECK-NEXT: DebugScope
+; CHECK-NEXT: OpLine {{%\d+}} 100 0
+; CHECK-NEXT: OpBranch [[return_block]]
+; CHECK: [[then_lab]] = OpLabel
+; CHECK-NEXT: OpStore [[var]] [[true]]
+; CHECK-NEXT: DebugScope
+; CHECK-NEXT: OpLine {{%\d+}} 200 0
+; CHECK-NEXT: OpBranch [[return_block]]
+; CHECK: [[merge_lab]] = OpLabel
+; CHECK-NEXT: DebugScope
+; CHECK-NEXT: OpLine {{%\d+}} 300 0
+; CHECK-NEXT: OpBranch [[return_block]]
+; CHECK: [[return_block]] = OpLabel
+; CHECK-NEXT: OpReturn
+OpCapability Addresses
+OpCapability Shader
+OpCapability Linkage
+%12 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %6 "simple_shader"
+%11 = OpString "test"
+%2 = OpTypeVoid
+%3 = OpTypeBool
+%4 = OpConstantFalse %3
+%1 = OpTypeFunction %2
+%13 = OpExtInst %2 %12 DebugSource %11
+%14 = OpExtInst %2 %12 DebugCompilationUnit 1 4 %13 HLSL
+%6 = OpFunction %2 None %1
+%7 = OpLabel
+OpSelectionMerge %10 None
+OpBranchConditional %4 %8 %9
+%8 = OpLabel
+%15 = OpExtInst %2 %12 DebugScope %14
+OpLine %11 100 0
+OpReturn
+%9 = OpLabel
+%16 = OpExtInst %2 %12 DebugScope %14
+OpLine %11 200 0
+OpReturn
+%10 = OpLabel
+%17 = OpExtInst %2 %12 DebugScope %14
+OpLine %11 300 0
+OpUnreachable
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<MergeReturnPass>(before, false);
+}
+
 TEST_F(MergeReturnPassTest, StructuredControlFlowAddPhi) {
   const std::string before =
       R"(
@@ -451,6 +671,89 @@
   SinglePassRunAndMatch<MergeReturnPass>(before, false);
 }
 
+TEST_F(MergeReturnPassTest, DebugSplitBlockUsedInPhi) {
+  const std::string before =
+      R"(
+; CHECK:      DebugScope
+; CHECK-NEXT: OpLine {{%\d+}} 100 0
+; CHECK:      OpLoopMerge
+
+; CHECK:      OpStore [[return_in_loop:%\w+]] %true
+; CHECK-NEXT: DebugScope
+; CHECK-NEXT: OpLine {{%\d+}} 200 0
+; CHECK-NEXT: OpBranch [[check_early_return:%\w+]]
+
+; CHECK:      [[check_early_return]] = OpLabel
+; CHECK-NEXT: [[early_return:%\w+]] = OpLoad %bool [[return_in_loop]]
+; CHECK-NEXT: OpSelectionMerge [[not_early_return:%\w+]] None
+; CHECK-NEXT: OpBranchConditional [[early_return]] {{%\d+}} [[not_early_return]]
+
+; CHECK:      [[not_early_return]] = OpLabel
+; CHECK-NEXT: DebugScope
+; CHECK-NEXT: OpLine {{%\d+}} 400 0
+; CHECK:      OpSelectionMerge [[merge2:%\w+]] None
+
+; CHECK:      [[merge2]] = OpLabel
+; CHECK-NEXT: DebugScope
+; CHECK-NEXT: OpLine {{%\d+}} 600 0
+; CHECK-NEXT: [[phi:%\w+]] = OpPhi %bool %false {{%\d+}} %true [[not_early_return]]
+; CHECK-NEXT: DebugValue {{%\d+}} [[phi]]
+
+               OpCapability Addresses
+               OpCapability Shader
+               OpCapability Linkage
+        %ext = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "simple_shader"
+         %tn = OpString "test"
+       %void = OpTypeVoid
+       %bool = OpTypeBool
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+      %false = OpConstantFalse %bool
+       %true = OpConstantTrue %bool
+          %6 = OpTypeFunction %void
+        %src = OpExtInst %void %ext DebugSource %tn
+         %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+         %ty = OpExtInst %void %ext DebugTypeBasic %tn %uint_8 Boolean
+          %v = OpExtInst %void %ext DebugLocalVariable %tn %ty %src 0 0 %cu FlagIsLocal
+       %expr = OpExtInst %void %ext DebugExpression
+          %1 = OpFunction %void None %6
+          %7 = OpLabel
+         %s0 = OpExtInst %void %ext DebugScope %cu
+               OpLine %tn 100 0
+               OpLoopMerge %merge %cont None
+               OpBranchConditional %false %9 %merge
+          %9 = OpLabel
+         %s1 = OpExtInst %void %ext DebugScope %cu
+               OpLine %tn 200 0
+               OpReturn
+       %cont = OpLabel
+         %s2 = OpExtInst %void %ext DebugScope %cu
+               OpLine %tn 300 0
+               OpBranch %7
+      %merge = OpLabel
+         %s3 = OpExtInst %void %ext DebugScope %cu
+               OpLine %tn 400 0
+               OpSelectionMerge %merge2 None
+               OpBranchConditional %false %if %merge2
+         %if = OpLabel
+         %s4 = OpExtInst %void %ext DebugScope %cu
+               OpLine %tn 500 0
+               OpBranch %merge2
+     %merge2 = OpLabel
+         %s5 = OpExtInst %void %ext DebugScope %cu
+               OpLine %tn 600 0
+         %12 = OpPhi %bool %false %if %true %merge
+         %dv = OpExtInst %void %ext DebugValue %v %12 %expr
+               OpLine %tn 900 0
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<MergeReturnPass>(before, false);
+}
+
 // TODO(#1861): Reenable these test when the breaks from selection constructs
 // are reenabled.
 /*
@@ -2077,6 +2380,193 @@
   SinglePassRunAndMatch<MergeReturnPass>(before, true);
 }
 
+TEST_F(MergeReturnPassTest, PointerUsedAfterLoop) {
+  // Make sure that a Phi instruction is not generated for an id whose type is a
+  // pointer.  It needs to be regenerated.
+  const std::string before =
+      R"(
+; CHECK: OpFunction %void
+; CHECK: OpFunction %void
+; CHECK-NEXT: [[param:%\w+]] = OpFunctionParameter %_ptr_Function_v2uint
+; CHECK: OpLoopMerge [[merge_bb:%\w+]]
+; CHECK: [[merge_bb]] = OpLabel
+; CHECK-NEXT: [[ac:%\w+]] = OpAccessChain %_ptr_Function_uint [[param]] %uint_1
+; CHECK: OpStore [[ac]] %uint_1
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 310
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v2uint = OpTypeVector %uint 2
+%_ptr_Function_v2uint = OpTypePointer Function %v2uint
+          %8 = OpTypeFunction %void %_ptr_Function_v2uint
+     %uint_1 = OpConstant %uint 1
+       %bool = OpTypeBool
+%_ptr_Function_uint = OpTypePointer Function %uint
+      %false = OpConstantFalse %bool
+          %2 = OpFunction %void None %4
+         %13 = OpLabel
+         %14 = OpVariable %_ptr_Function_v2uint Function
+         %15 = OpFunctionCall %void %16 %14
+               OpReturn
+               OpFunctionEnd
+         %16 = OpFunction %void None %8
+         %17 = OpFunctionParameter %_ptr_Function_v2uint
+         %18 = OpLabel
+               OpBranch %19
+         %19 = OpLabel
+               OpLoopMerge %20 %21 None
+               OpBranch %22
+         %22 = OpLabel
+               OpSelectionMerge %23 None
+               OpBranchConditional %false %24 %23
+         %24 = OpLabel
+               OpReturn
+         %23 = OpLabel
+               OpBranch %21
+         %21 = OpLabel
+         %25 = OpAccessChain %_ptr_Function_uint %17 %uint_1
+               OpBranchConditional %false %19 %20
+         %20 = OpLabel
+               OpStore %25 %uint_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<MergeReturnPass>(before, true);
+}
+
+TEST_F(MergeReturnPassTest, VariablePointerFunctionScope) {
+  // Make sure that a Phi instruction is not generated for an id whose type is a
+  // function scope pointer, even if the VariablePointers capability is
+  // available.  It needs to be regenerated.
+  const std::string before =
+      R"(
+; CHECK: OpFunction %void
+; CHECK: OpFunction %void
+; CHECK-NEXT: [[param:%\w+]] = OpFunctionParameter %_ptr_Function_v2uint
+; CHECK: OpLoopMerge [[merge_bb:%\w+]]
+; CHECK: [[merge_bb]] = OpLabel
+; CHECK-NEXT: [[ac:%\w+]] = OpAccessChain %_ptr_Function_uint [[param]] %uint_1
+; CHECK: OpStore [[ac]] %uint_1
+               OpCapability Shader
+               OpCapability VariablePointers
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 310
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v2uint = OpTypeVector %uint 2
+%_ptr_Function_v2uint = OpTypePointer Function %v2uint
+          %8 = OpTypeFunction %void %_ptr_Function_v2uint
+     %uint_1 = OpConstant %uint 1
+       %bool = OpTypeBool
+%_ptr_Function_uint = OpTypePointer Function %uint
+      %false = OpConstantFalse %bool
+          %2 = OpFunction %void None %4
+         %13 = OpLabel
+         %14 = OpVariable %_ptr_Function_v2uint Function
+         %15 = OpFunctionCall %void %16 %14
+               OpReturn
+               OpFunctionEnd
+         %16 = OpFunction %void None %8
+         %17 = OpFunctionParameter %_ptr_Function_v2uint
+         %18 = OpLabel
+               OpBranch %19
+         %19 = OpLabel
+               OpLoopMerge %20 %21 None
+               OpBranch %22
+         %22 = OpLabel
+               OpSelectionMerge %23 None
+               OpBranchConditional %false %24 %23
+         %24 = OpLabel
+               OpReturn
+         %23 = OpLabel
+               OpBranch %21
+         %21 = OpLabel
+         %25 = OpAccessChain %_ptr_Function_uint %17 %uint_1
+               OpBranchConditional %false %19 %20
+         %20 = OpLabel
+               OpStore %25 %uint_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<MergeReturnPass>(before, true);
+}
+
+TEST_F(MergeReturnPassTest, ChainedPointerUsedAfterLoop) {
+  // Make sure that a Phi instruction is not generated for an id whose type is a
+  // pointer.  It needs to be regenerated.
+  const std::string before =
+      R"(
+; CHECK: OpFunction %void
+; CHECK: OpFunction %void
+; CHECK-NEXT: [[param:%\w+]] = OpFunctionParameter %_ptr_Function_
+; CHECK: OpLoopMerge [[merge_bb:%\w+]]
+; CHECK: [[merge_bb]] = OpLabel
+; CHECK-NEXT: [[ac1:%\w+]] = OpAccessChain %_ptr_Function_v2uint [[param]] %uint_1
+; CHECK-NEXT: [[ac2:%\w+]] = OpAccessChain %_ptr_Function_uint [[ac1]] %uint_1
+; CHECK: OpStore [[ac2]] %uint_1
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 310
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %v2uint = OpTypeVector %uint 2
+%_arr_v2uint_uint_2 = OpTypeArray %v2uint %uint_2
+%_ptr_Function_v2uint = OpTypePointer Function %v2uint
+%_ptr_Function__arr_v2uint_uint_2 = OpTypePointer Function %_arr_v2uint_uint_2
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %13 = OpTypeFunction %void %_ptr_Function__arr_v2uint_uint_2
+       %bool = OpTypeBool
+      %false = OpConstantFalse %bool
+          %2 = OpFunction %void None %4
+         %16 = OpLabel
+         %17 = OpVariable %_ptr_Function__arr_v2uint_uint_2 Function
+         %18 = OpFunctionCall %void %19 %17
+               OpReturn
+               OpFunctionEnd
+         %19 = OpFunction %void None %13
+         %20 = OpFunctionParameter %_ptr_Function__arr_v2uint_uint_2
+         %21 = OpLabel
+               OpBranch %22
+         %22 = OpLabel
+               OpLoopMerge %23 %24 None
+               OpBranch %25
+         %25 = OpLabel
+               OpSelectionMerge %26 None
+               OpBranchConditional %false %27 %26
+         %27 = OpLabel
+               OpReturn
+         %26 = OpLabel
+               OpBranch %24
+         %24 = OpLabel
+         %28 = OpAccessChain %_ptr_Function_v2uint %20 %uint_1
+         %29 = OpAccessChain %_ptr_Function_uint %28 %uint_1
+               OpBranchConditional %false %22 %23
+         %23 = OpLabel
+               OpStore %29 %uint_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<MergeReturnPass>(before, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/private_to_local_test.cpp b/third_party/SPIRV-Tools/test/opt/private_to_local_test.cpp
index 1230652..8b5ec59 100644
--- a/third_party/SPIRV-Tools/test/opt/private_to_local_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/private_to_local_test.cpp
@@ -452,6 +452,50 @@
   EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
 }
 
+TEST_F(PrivateToLocalTest, DebugPrivateToLocal) {
+  // Debug instructions must not have any impact on changing the private
+  // variable to a local.
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+         %10 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+         %11 = OpString "test"
+               OpSource GLSL 430
+         %13 = OpTypeInt 32 0
+         %14 = OpConstant %13 32
+          %3 = OpTypeVoid
+          %4 = OpTypeFunction %3
+; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat 32
+          %5 = OpTypeFloat 32
+; CHECK: [[newtype:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]]
+          %6 = OpTypePointer Private %5
+; CHECK-NOT: OpVariable [[.+]] Private
+          %8 = OpVariable %6 Private
+
+         %12 = OpExtInst %3 %10 DebugTypeBasic %11 %14 Float
+         %15 = OpExtInst %3 %10 DebugSource %11
+         %16 = OpExtInst %3 %10 DebugCompilationUnit 1 4 %15 GLSL
+; CHECK-NOT: DebugGlobalVariable
+; CHECK: [[dbg_newvar:%[a-zA-Z_\d]+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable
+         %17 = OpExtInst %3 %10 DebugGlobalVariable %11 %12 %15 0 0 %16 %11 %8 FlagIsDefinition
+
+; CHECK: OpFunction
+          %2 = OpFunction %3 None %4
+; CHECK: OpLabel
+          %7 = OpLabel
+; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[newtype]] Function
+; CHECK-NEXT: DebugDeclare [[dbg_newvar]] [[newvar]]
+; CHECK: OpLoad [[float]] [[newvar]]
+          %9 = OpLoad %5 %8
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<PrivateToLocalPass>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/reduce_load_size_test.cpp b/third_party/SPIRV-Tools/test/opt/reduce_load_size_test.cpp
index 50dc501..7672e8f 100644
--- a/third_party/SPIRV-Tools/test/opt/reduce_load_size_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/reduce_load_size_test.cpp
@@ -107,6 +107,104 @@
   SinglePassRunAndMatch<ReduceLoadSize>(test, false);
 }
 
+TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_not_affected_by_debug_instr) {
+  // Originally from the following HLSL:
+  //   struct S {
+  //     uint f;
+  //   };
+  //
+  //
+  //   cbuffer gBuffer { uint a[32]; };
+  //
+  //   RWStructuredBuffer<S> gRWSBuffer;
+  //
+  //   uint foo(uint p[32]) {
+  //     return p[1];
+  //   }
+  //
+  //   [numthreads(1,1,1)]
+  //   void main() {
+  //      gRWSBuffer[0].f = foo(a);
+  //   }
+  const std::string test =
+      R"(
+               OpCapability Shader
+        %ext = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource HLSL 600
+  %file_name = OpString "test"
+ %float_name = OpString "float"
+  %main_name = OpString "main"
+     %f_name = OpString "f"
+               OpName %type_gBuffer "type.gBuffer"
+               OpMemberName %type_gBuffer 0 "a"
+               OpName %gBuffer "gBuffer"
+               OpName %S "S"
+               OpMemberName %S 0 "f"
+               OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S"
+               OpName %gRWSBuffer "gRWSBuffer"
+               OpName %main "main"
+               OpDecorate %_arr_uint_uint_32 ArrayStride 16
+               OpMemberDecorate %type_gBuffer 0 Offset 0
+               OpDecorate %type_gBuffer Block
+               OpMemberDecorate %S 0 Offset 0
+               OpDecorate %_runtimearr_S ArrayStride 4
+               OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0
+               OpDecorate %type_RWStructuredBuffer_S BufferBlock
+               OpDecorate %gBuffer DescriptorSet 0
+               OpDecorate %gBuffer Binding 0
+               OpDecorate %gRWSBuffer DescriptorSet 0
+               OpDecorate %gRWSBuffer Binding 1
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+%_arr_uint_uint_32 = OpTypeArray %uint %uint_32
+%type_gBuffer = OpTypeStruct %_arr_uint_uint_32
+%_ptr_Uniform_type_gBuffer = OpTypePointer Uniform %type_gBuffer
+          %S = OpTypeStruct %uint
+%_runtimearr_S = OpTypeRuntimeArray %S
+%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S
+%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S
+        %int = OpTypeInt 32 1
+       %void = OpTypeVoid
+         %15 = OpTypeFunction %void
+      %int_0 = OpConstant %int 0
+%_ptr_Uniform__arr_uint_uint_32 = OpTypePointer Uniform %_arr_uint_uint_32
+     %uint_0 = OpConstant %uint 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+    %gBuffer = OpVariable %_ptr_Uniform_type_gBuffer Uniform
+ %gRWSBuffer = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform
+  %null_expr = OpExtInst %void %ext DebugExpression
+        %src = OpExtInst %void %ext DebugSource %file_name
+         %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+     %dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+    %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+   %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+      %dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+       %main = OpFunction %void None %15
+         %20 = OpLabel
+          %s = OpExtInst %void %ext DebugScope %dbg_main
+; CHECK: [[ac1:%\w+]] = OpAccessChain {{%\w+}} %gBuffer %int_0
+; CHECK: [[ac2:%\w+]] = OpAccessChain {{%\w+}} [[ac1]] %uint_1
+; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[ac2]]
+; CHECK: OpStore {{%\w+}} [[ld]]
+         %21 = OpAccessChain %_ptr_Uniform__arr_uint_uint_32 %gBuffer %int_0
+         %22 = OpLoad %_arr_uint_uint_32 %21    ; Load of 32-element array.
+      %value = OpExtInst %void %ext DebugValue %dbg_f %22 %null_expr
+         %23 = OpCompositeExtract %uint %22 1
+         %24 = OpAccessChain %_ptr_Uniform_uint %gRWSBuffer %int_0 %uint_0 %int_0
+               OpStore %24 %23
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+                        SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+  SinglePassRunAndMatch<ReduceLoadSize>(test, false);
+}
+
 TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_vector) {
   // Originally from the following HLSL:
   //   struct S {
diff --git a/third_party/SPIRV-Tools/test/opt/vector_dce_test.cpp b/third_party/SPIRV-Tools/test/opt/vector_dce_test.cpp
index 594995c..9bdad37 100644
--- a/third_party/SPIRV-Tools/test/opt/vector_dce_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/vector_dce_test.cpp
@@ -1158,6 +1158,199 @@
   SinglePassRunAndCheck<VectorDCE>(text, text, true, true);
 }
 
+TEST_F(VectorDCETest, NotAffectedByDebugValue) {
+  // It tests that an OpenCL.DebugInfo.100 DebugValue instruction does
+  // not change the vector DCE pass result. If the composite used for
+  // the value of DebugValue is killed, the DebugValue must be killed as well.
+  const std::string text = R"(
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %In2 %In0 %In1 %OutColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+OpName %main "main"
+OpName %In2 "In2"
+OpName %In0 "In0"
+OpName %In1 "In1"
+OpName %OutColor "OutColor"
+OpName %_Globals_ "_Globals_"
+OpMemberName %_Globals_ 0 "g_b"
+OpMemberName %_Globals_ 1 "g_n"
+OpName %_ ""
+OpDecorate %In2 Location 2
+OpDecorate %In0 Location 0
+OpDecorate %In1 Location 1
+OpDecorate %OutColor Location 0
+OpMemberDecorate %_Globals_ 0 Offset 0
+OpMemberDecorate %_Globals_ 1 Offset 4
+OpDecorate %_Globals_ Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 0
+%void = OpTypeVoid
+%11 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+%In2 = OpVariable %_ptr_Input_v2float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%In0 = OpVariable %_ptr_Input_float Input
+%In1 = OpVariable %_ptr_Input_float Input
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%OutColor = OpVariable %_ptr_Output_v4float Output
+%int = OpTypeInt 32 1
+%_Globals_ = OpTypeStruct %uint %int
+%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
+%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+%main = OpFunction %void None %11
+%25 = OpLabel
+%s = OpExtInst %void %ext DebugScope %dbg_main
+
+; CHECK: [[in2:%\w+]] = OpLoad %v2float %In2
+%26 = OpLoad %v2float %In2
+%27 = OpLoad %float %In0
+%28 = OpLoad %float %In1
+%29 = OpFAdd %float %27 %28
+
+; CHECK:      OpCompositeInsert %v2float {{%\w+}} [[in2]] 0
+; CHECK-NEXT: OpCompositeInsert %v2float {{%\w+}} [[in2]] 0
+; CHECK-NOT:  DebugValue
+%35 = OpCompositeInsert %v2float %29 %26 0
+%value = OpExtInst %void %ext DebugValue %dbg_f %35 %null_expr
+%37 = OpCompositeInsert %v2float %float_0 %35 0
+%33 = OpVectorShuffle %v4float %37 %37 0 1 0 1
+OpStore %OutColor %33
+OpReturn
+OpFunctionEnd
+)";
+
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+                        SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+  SinglePassRunAndMatch<VectorDCE>(text, true);
+}
+
+TEST_F(VectorDCETest, RemoveDebugValueUsesKilledInstr) {
+  // It tests that the vector DCE pass removes the OpenCL.DebugInfo.100
+  // DebugValue instruction using a killed instruction.
+  const std::string text = R"(
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %In0 %OutColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
+OpSourceExtension "GL_GOOGLE_include_directive"
+OpName %main "main"
+OpName %In0 "In0"
+OpName %OutColor "OutColor"
+OpDecorate %In0 Location 0
+OpDecorate %OutColor Location 0
+%void = OpTypeVoid
+%6 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%In0 = OpVariable %_ptr_Input_v4float Input
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Input_float = OpTypePointer Input %float
+%uint_1 = OpConstant %uint 1
+%uint_2 = OpConstant %uint 2
+%v3float = OpTypeVector %float 3
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%int_20 = OpConstant %int 20
+%bool = OpTypeBool
+%float_1 = OpConstant %float 1
+%int_1 = OpConstant %int 1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%OutColor = OpVariable %_ptr_Output_v4float Output
+%23 = OpUndef %v3float
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+%main = OpFunction %void None %6
+%24 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%25 = OpAccessChain %_ptr_Input_float %In0 %uint_0
+%26 = OpLoad %float %25
+%27 = OpAccessChain %_ptr_Input_float %In0 %uint_1
+%28 = OpLoad %float %27
+
+; CHECK:     [[undef:%\w+]] = OpUndef %float
+; CHECK-NOT: DebugValue
+%value = OpExtInst %void %ext DebugValue %dbg_f %28 %null_expr
+%29 = OpAccessChain %_ptr_Input_float %In0 %uint_2
+%30 = OpLoad %float %29
+
+; CHECK:      [[composite:%\w+]] = OpCompositeConstruct %v3float {{%\w+}} [[undef]] [[undef]]
+; CHECK-NEXT: DebugValue {{%\w+}} [[composite]]
+%31 = OpCompositeConstruct %v3float %30 %28 %26
+%value_live = OpExtInst %void %ext DebugValue %dbg_f %31 %null_expr
+OpBranch %32
+%32 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+%33 = OpPhi %v3float %31 %24 %34 %35
+%36 = OpPhi %int %int_0 %24 %37 %35
+OpLoopMerge %38 %35 None
+OpBranch %39
+%39 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%40 = OpSLessThan %bool %36 %int_20
+OpBranchConditional %40 %41 %38
+%41 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%42 = OpCompositeExtract %float %33 0
+%43 = OpFAdd %float %42 %float_1
+%34 = OpCompositeInsert %v3float %43 %33 0
+OpBranch %35
+%35 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpIAdd %int %36 %int_1
+OpBranch %32
+%38 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%44 = OpCompositeExtract %float %33 0
+%45 = OpCompositeConstruct %v4float %44 %44 %44 %44
+OpStore %OutColor %45
+OpReturn
+OpFunctionEnd
+)";
+
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+                        SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+  SinglePassRunAndMatch<VectorDCE>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/wrap_opkill_test.cpp b/third_party/SPIRV-Tools/test/opt/wrap_opkill_test.cpp
index 33e52f0..e944109 100644
--- a/third_party/SPIRV-Tools/test/opt/wrap_opkill_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/wrap_opkill_test.cpp
@@ -193,6 +193,310 @@
   SinglePassRunAndMatch<WrapOpKill>(text, true);
 }
 
+TEST_F(WrapOpKillTest, SingleOpTerminateInvocation) {
+  const std::string text = R"(
+; CHECK: OpEntryPoint Fragment [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK: OpFunctionCall %void [[orig_kill:%\w+]]
+; CHECK: [[orig_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
+; CHECK-NEXT: OpReturn
+; CHECK: [[new_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpTerminateInvocation
+; CHECK-NEXT: OpFunctionEnd
+               OpCapability Shader
+               OpExtension "SPV_KHR_terminate_invocation"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 330
+               OpName %main "main"
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+       %main = OpFunction %void None %5
+          %8 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+               OpLoopMerge %10 %11 None
+               OpBranch %12
+         %12 = OpLabel
+               OpBranchConditional %true %13 %10
+         %13 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+         %14 = OpFunctionCall %void %kill_
+               OpBranch %9
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+      %kill_ = OpFunction %void None %5
+         %15 = OpLabel
+               OpTerminateInvocation
+               OpFunctionEnd
+  )";
+
+  SinglePassRunAndMatch<WrapOpKill>(text, true);
+}
+
+TEST_F(WrapOpKillTest, MultipleTerminateInvocationInSameFunc) {
+  const std::string text = R"(
+; CHECK: OpEntryPoint Fragment [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK: OpFunctionCall %void [[orig_kill:%\w+]]
+; CHECK: [[orig_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpSelectionMerge
+; CHECK-NEXT: OpBranchConditional
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
+; CHECK-NEXT: OpReturn
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill]]
+; CHECK-NEXT: OpReturn
+; CHECK: [[new_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpTerminateInvocation
+; CHECK-NEXT: OpFunctionEnd
+               OpCapability Shader
+               OpExtension "SPV_KHR_terminate_invocation"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 330
+               OpName %main "main"
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+       %main = OpFunction %void None %5
+          %8 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+               OpLoopMerge %10 %11 None
+               OpBranch %12
+         %12 = OpLabel
+               OpBranchConditional %true %13 %10
+         %13 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+         %14 = OpFunctionCall %void %kill_
+               OpBranch %9
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+      %kill_ = OpFunction %void None %5
+         %15 = OpLabel
+               OpSelectionMerge %16 None
+               OpBranchConditional %true %17 %18
+         %17 = OpLabel
+               OpTerminateInvocation
+         %18 = OpLabel
+               OpTerminateInvocation
+         %16 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SinglePassRunAndMatch<WrapOpKill>(text, true);
+}
+
+TEST_F(WrapOpKillTest, MultipleOpTerminateInvocationDifferentFunc) {
+  const std::string text = R"(
+; CHECK: OpEntryPoint Fragment [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK: OpFunctionCall %void [[orig_kill1:%\w+]]
+; CHECK-NEXT: OpFunctionCall %void [[orig_kill2:%\w+]]
+; CHECK: [[orig_kill1]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
+; CHECK-NEXT: OpReturn
+; CHECK: [[orig_kill2]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill]]
+; CHECK-NEXT: OpReturn
+; CHECK: [[new_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpTerminateInvocation
+; CHECK-NEXT: OpFunctionEnd
+               OpCapability Shader
+               OpExtension "SPV_KHR_terminate_invocation"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 330
+               OpName %main "main"
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+       %main = OpFunction %void None %4
+          %7 = OpLabel
+               OpBranch %8
+          %8 = OpLabel
+               OpLoopMerge %9 %10 None
+               OpBranch %11
+         %11 = OpLabel
+               OpBranchConditional %true %12 %9
+         %12 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+         %13 = OpFunctionCall %void %14
+         %15 = OpFunctionCall %void %16
+               OpBranch %8
+          %9 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %14 = OpFunction %void None %4
+         %17 = OpLabel
+               OpTerminateInvocation
+               OpFunctionEnd
+         %16 = OpFunction %void None %4
+         %18 = OpLabel
+               OpTerminateInvocation
+               OpFunctionEnd
+  )";
+
+  SinglePassRunAndMatch<WrapOpKill>(text, true);
+}
+
+TEST_F(WrapOpKillTest, KillAndTerminateInvocationSameFunc) {
+  const std::string text = R"(
+; CHECK: OpEntryPoint Fragment [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK: OpFunctionCall %void [[orig_kill:%\w+]]
+; CHECK: [[orig_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpSelectionMerge
+; CHECK-NEXT: OpBranchConditional
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
+; CHECK-NEXT: OpReturn
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_terminate:%\w+]]
+; CHECK-NEXT: OpReturn
+; CHECK: [[new_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpKill
+; CHECK-NEXT: OpFunctionEnd
+; CHECK-NEXT: [[new_terminate]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpTerminateInvocation
+; CHECK-NEXT: OpFunctionEnd
+               OpCapability Shader
+               OpExtension "SPV_KHR_terminate_invocation"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 330
+               OpName %main "main"
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+       %main = OpFunction %void None %5
+          %8 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+               OpLoopMerge %10 %11 None
+               OpBranch %12
+         %12 = OpLabel
+               OpBranchConditional %true %13 %10
+         %13 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+         %14 = OpFunctionCall %void %kill_
+               OpBranch %9
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+      %kill_ = OpFunction %void None %5
+         %15 = OpLabel
+               OpSelectionMerge %16 None
+               OpBranchConditional %true %17 %18
+         %17 = OpLabel
+               OpKill
+         %18 = OpLabel
+               OpTerminateInvocation
+         %16 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SinglePassRunAndMatch<WrapOpKill>(text, true);
+}
+
+TEST_F(WrapOpKillTest, KillAndTerminateInvocationDifferentFunc) {
+  const std::string text = R"(
+; CHECK: OpEntryPoint Fragment [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK: OpFunctionCall %void [[orig_kill1:%\w+]]
+; CHECK-NEXT: OpFunctionCall %void [[orig_kill2:%\w+]]
+; CHECK: [[orig_kill1]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_terminate:%\w+]]
+; CHECK-NEXT: OpReturn
+; CHECK: [[orig_kill2]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
+; CHECK-NEXT: OpReturn
+; CHECK: [[new_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpKill
+; CHECK-NEXT: OpFunctionEnd
+; CHECK-NEXT: [[new_terminate]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpTerminateInvocation
+; CHECK-NEXT: OpFunctionEnd
+               OpCapability Shader
+               OpExtension "SPV_KHR_terminate_invocation"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 330
+               OpName %main "main"
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+       %main = OpFunction %void None %4
+          %7 = OpLabel
+               OpBranch %8
+          %8 = OpLabel
+               OpLoopMerge %9 %10 None
+               OpBranch %11
+         %11 = OpLabel
+               OpBranchConditional %true %12 %9
+         %12 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+         %13 = OpFunctionCall %void %14
+         %15 = OpFunctionCall %void %16
+               OpBranch %8
+          %9 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %14 = OpFunction %void None %4
+         %17 = OpLabel
+               OpTerminateInvocation
+               OpFunctionEnd
+         %16 = OpFunction %void None %4
+         %18 = OpLabel
+               OpKill
+               OpFunctionEnd
+  )";
+
+  SinglePassRunAndMatch<WrapOpKill>(text, true);
+}
+
 TEST_F(WrapOpKillTest, FuncWithReturnValue) {
   const std::string text = R"(
 ; CHECK: OpEntryPoint Fragment [[main:%\w+]]
diff --git a/third_party/SPIRV-Tools/test/text_to_binary.constant_test.cpp b/third_party/SPIRV-Tools/test/text_to_binary.constant_test.cpp
index 679bee4..7ab4ca5 100644
--- a/third_party/SPIRV-Tools/test/text_to_binary.constant_test.cpp
+++ b/third_party/SPIRV-Tools/test/text_to_binary.constant_test.cpp
@@ -485,8 +485,15 @@
 const int64_t kMaxSigned48Bit = (int64_t(1) << 47) - 1;
 const int64_t kMinSigned48Bit = -kMaxSigned48Bit - 1;
 
+using ConstantRoundTripTest = RoundTripTest;
+
+TEST_P(ConstantRoundTripTest, DisassemblyEqualsAssemblyInput) {
+  const std::string assembly = GetParam();
+  EXPECT_THAT(EncodeAndDecodeSuccessfully(assembly), Eq(assembly)) << assembly;
+}
+
 INSTANTIATE_TEST_SUITE_P(
-    OpConstantRoundTrip, RoundTripTest,
+    OpConstantRoundTrip, ConstantRoundTripTest,
     ::testing::ValuesIn(std::vector<std::string>{
         // 16 bit
         "%1 = OpTypeInt 16 0\n%2 = OpConstant %1 0\n",
@@ -529,7 +536,7 @@
     }));
 
 INSTANTIATE_TEST_SUITE_P(
-    OpConstantHalfRoundTrip, RoundTripTest,
+    OpConstantHalfRoundTrip, ConstantRoundTripTest,
     ::testing::ValuesIn(std::vector<std::string>{
         "%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x0p+0\n",
         "%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x0p+0\n",
@@ -566,7 +573,7 @@
 // clang-format off
 // (Clang-format really wants to break up these strings across lines.
 INSTANTIATE_TEST_SUITE_P(
-    OpConstantRoundTripNonFinite, RoundTripTest,
+    OpConstantRoundTripNonFinite, ConstantRoundTripTest,
     ::testing::ValuesIn(std::vector<std::string>{
   "%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1p+128\n",         // -inf
   "%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1p+128\n",          // inf
@@ -596,7 +603,7 @@
 // clang-format on
 
 INSTANTIATE_TEST_SUITE_P(
-    OpSpecConstantRoundTrip, RoundTripTest,
+    OpSpecConstantRoundTrip, ConstantRoundTripTest,
     ::testing::ValuesIn(std::vector<std::string>{
         // 16 bit
         "%1 = OpTypeInt 16 0\n%2 = OpSpecConstant %1 0\n",
diff --git a/third_party/SPIRV-Tools/test/text_to_binary.control_flow_test.cpp b/third_party/SPIRV-Tools/test/text_to_binary.control_flow_test.cpp
index ae51f55..3e117b8 100644
--- a/third_party/SPIRV-Tools/test/text_to_binary.control_flow_test.cpp
+++ b/third_party/SPIRV-Tools/test/text_to_binary.control_flow_test.cpp
@@ -293,8 +293,15 @@
         MakeSwitchTestCase(64, 1, "0x700000123", {0x123, 7}, "12", {12, 0}),
     })));
 
+using ControlFlowRoundTripTest = RoundTripTest;
+
+TEST_P(ControlFlowRoundTripTest, DisassemblyEqualsAssemblyInput) {
+  const std::string assembly = GetParam();
+  EXPECT_THAT(EncodeAndDecodeSuccessfully(assembly), Eq(assembly)) << assembly;
+}
+
 INSTANTIATE_TEST_SUITE_P(
-    OpSwitchRoundTripUnsignedIntegers, RoundTripTest,
+    OpSwitchRoundTripUnsignedIntegers, ControlFlowRoundTripTest,
     ValuesIn(std::vector<std::string>({
         // Unsigned 16-bit.
         "%1 = OpTypeInt 16 0\n%2 = OpConstant %1 65535\nOpSwitch %2 %3\n",
@@ -310,7 +317,7 @@
     })));
 
 INSTANTIATE_TEST_SUITE_P(
-    OpSwitchRoundTripSignedIntegers, RoundTripTest,
+    OpSwitchRoundTripSignedIntegers, ControlFlowRoundTripTest,
     ValuesIn(std::vector<std::string>{
         // Signed 16-bit, with two non-default cases
         "%1 = OpTypeInt 16 1\n%2 = OpConstant %1 32767\n"
@@ -381,12 +388,35 @@
     }));
 // clang-format on
 
+using OpKillTest = spvtest::TextToBinaryTest;
+
+INSTANTIATE_TEST_SUITE_P(OpKillTest, ControlFlowRoundTripTest,
+                         Values("OpKill\n"));
+
+TEST_F(OpKillTest, ExtraArgsAssemblyError) {
+  const std::string input = "OpKill 1";
+  EXPECT_THAT(CompileFailure(input),
+              Eq("Expected <opcode> or <result-id> at the beginning of an "
+                 "instruction, found '1'."));
+}
+
+using OpTerminateInvocationTest = spvtest::TextToBinaryTest;
+
+INSTANTIATE_TEST_SUITE_P(OpTerminateInvocationTest, ControlFlowRoundTripTest,
+                         Values("OpTerminateInvocation\n"));
+
+TEST_F(OpTerminateInvocationTest, ExtraArgsAssemblyError) {
+  const std::string input = "OpTerminateInvocation 1";
+  EXPECT_THAT(CompileFailure(input),
+              Eq("Expected <opcode> or <result-id> at the beginning of an "
+                 "instruction, found '1'."));
+}
+
 // TODO(dneto): OpPhi
 // TODO(dneto): OpLoopMerge
 // TODO(dneto): OpLabel
 // TODO(dneto): OpBranch
 // TODO(dneto): OpSwitch
-// TODO(dneto): OpKill
 // TODO(dneto): OpReturn
 // TODO(dneto): OpReturnValue
 // TODO(dneto): OpUnreachable
diff --git a/third_party/SPIRV-Tools/test/unit_spirv.cpp b/third_party/SPIRV-Tools/test/unit_spirv.cpp
index 0854439..33af273 100644
--- a/third_party/SPIRV-Tools/test/unit_spirv.cpp
+++ b/third_party/SPIRV-Tools/test/unit_spirv.cpp
@@ -47,10 +47,5 @@
   EXPECT_THAT(s.str(), Eq("xx10 0x0000000a 0x00000010 xx11"));
 }
 
-TEST_P(RoundTripTest, Sample) {
-  EXPECT_THAT(EncodeAndDecodeSuccessfully(GetParam()), Eq(GetParam()))
-      << GetParam();
-}
-
 }  // namespace
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/CMakeLists.txt b/third_party/SPIRV-Tools/test/val/CMakeLists.txt
index 138e711..23d7a19 100644
--- a/third_party/SPIRV-Tools/test/val/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/test/val/CMakeLists.txt
@@ -38,6 +38,7 @@
        val_entry_point.cpp
        val_explicit_reserved_test.cpp
        val_extensions_test.cpp
+       val_extension_spv_khr_terminate_invocation.cpp
        val_ext_inst_test.cpp
        ${VAL_TEST_COMMON_SRCS}
   LIBS ${SPIRV_TOOLS}
diff --git a/third_party/SPIRV-Tools/test/val/val_atomics_test.cpp b/third_party/SPIRV-Tools/test/val/val_atomics_test.cpp
index cd723b2..aca0f3c 100644
--- a/third_party/SPIRV-Tools/test/val/val_atomics_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_atomics_test.cpp
@@ -238,6 +238,120 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
 }
 
+TEST_F(ValidateAtomics, AtomicAddIntVulkanWrongType1) {
+  const std::string body = R"(
+%val1 = OpAtomicIAdd %f32 %f32_var %device %relaxed %f32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicIAdd: "
+                        "expected Result Type to be int scalar type"));
+}
+
+TEST_F(ValidateAtomics, AtomicAddIntVulkanWrongType2) {
+  const std::string body = R"(
+%val1 = OpAtomicIAdd %f32vec4 %f32vec4_var %device %relaxed %f32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicIAdd: "
+                        "expected Result Type to be integer scalar type"));
+}
+
+TEST_F(ValidateAtomics, AtomicAddFloatVulkan) {
+  const std::string body = R"(
+%val1 = OpAtomicFAddEXT %f32 %f32_var %device %relaxed %f32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Opcode AtomicFAddEXT requires one of these capabilities: "
+                "AtomicFloat32AddEXT AtomicFloat64AddEXT"));
+}
+
+TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongType1) {
+  const std::string body = R"(
+%val1 = OpAtomicFAddEXT %f32vec4 %f32vec4_var %device %relaxed %f32_1
+)";
+  const std::string extra = R"(
+OpCapability AtomicFloat32AddEXT
+OpExtension "SPV_EXT_shader_atomic_float_add"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicFAddEXT: "
+                        "expected Result Type to be float scalar type"));
+}
+
+TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongType2) {
+  const std::string body = R"(
+%val1 = OpAtomicFAddEXT %u32 %u32_var %device %relaxed %u32_1
+)";
+  const std::string extra = R"(
+OpCapability AtomicFloat32AddEXT
+OpExtension "SPV_EXT_shader_atomic_float_add"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicFAddEXT: "
+                        "expected Result Type to be float scalar type"));
+}
+
+TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongType3) {
+  const std::string body = R"(
+%val1 = OpAtomicFAddEXT %u64 %u64_var %device %relaxed %u64_1
+)";
+  const std::string extra = R"(
+OpCapability AtomicFloat32AddEXT
+OpExtension "SPV_EXT_shader_atomic_float_add"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicFAddEXT: "
+                        "expected Result Type to be float scalar type"));
+}
+
+TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongCapability) {
+  const std::string body = R"(
+%val1 = OpAtomicFAddEXT %f32 %f32_var %device %relaxed %f32_1
+)";
+  const std::string extra = R"(
+OpCapability AtomicFloat64AddEXT
+OpExtension "SPV_EXT_shader_atomic_float_add"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicFAddEXT: float add atomics "
+                        "require the AtomicFloat32AddEXT capability"));
+}
+
+TEST_F(ValidateAtomics, AtomicAddFloatVulkanSuccess) {
+  const std::string body = R"(
+%val1 = OpAtomicFAddEXT %f32 %f32_var %device %relaxed %f32_1
+)";
+  const std::string extra = R"(
+OpCapability AtomicFloat32AddEXT
+OpExtension "SPV_EXT_shader_atomic_float_add"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
 TEST_F(ValidateAtomics, AtomicLoadFloatVulkan) {
   const std::string body = R"(
 %val1 = OpAtomicLoad %f32 %f32_var %device %relaxed
@@ -245,9 +359,25 @@
 )";
 
   CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("expected Result Type to be int scalar type"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateAtomics, AtomicStoreFloatVulkan) {
+  const std::string body = R"(
+OpAtomicStore %f32_var %device %relaxed %f32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateAtomics, AtomicExchangeFloatVulkan) {
+  const std::string body = R"(
+%val2 = OpAtomicExchange %f32 %f32_var %device %relaxed %f32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
 }
 
 TEST_F(ValidateAtomics, AtomicLoadInt64WithCapabilityVulkanSuccess) {
@@ -355,10 +485,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCode(body));
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("AtomicLoad: "
-                        "expected Result Type to be int scalar type"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_F(ValidateAtomics, AtomicLoadVulkanInt64) {
@@ -733,10 +860,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCode(body));
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("AtomicExchange: "
-                        "expected Result Type to be int scalar type"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_F(ValidateAtomics, AtomicExchangeWrongResultType) {
diff --git a/third_party/SPIRV-Tools/test/val/val_builtins_test.cpp b/third_party/SPIRV-Tools/test/val/val_builtins_test.cpp
index 58593dc..b1ab0c9 100644
--- a/third_party/SPIRV-Tools/test/val/val_builtins_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_builtins_test.cpp
@@ -1952,7 +1952,7 @@
                               "needs to be a 32-bit int", "is not an int"))));
 
 INSTANTIATE_TEST_SUITE_P(
-    WhitelistRejection,
+    AllowListRejection,
     ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult,
     Combine(Values("PointSize", "ClipDistance", "CullDistance", "VertexId",
                    "InstanceId", "PointCoord", "SampleMask", "HelperInvocation",
@@ -2532,7 +2532,9 @@
   CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
 
   generator.before_types_ = R"(
+OpDecorate %input_type Block
 OpMemberDecorate %input_type 0 BuiltIn Position
+OpDecorate %output_type Block
 OpMemberDecorate %output_type 0 BuiltIn Position
 )";
 
@@ -2543,8 +2545,7 @@
 %input = OpVariable %input_ptr Input
 %input_f32vec4_ptr = OpTypePointer Input %f32vec4
 %output_type = OpTypeStruct %f32vec4
-%arrayed_output_type = OpTypeArray %output_type %u32_3
-%output_ptr = OpTypePointer Output %arrayed_output_type
+%output_ptr = OpTypePointer Output %output_type
 %output = OpVariable %output_ptr Output
 %output_f32vec4_ptr = OpTypePointer Output %f32vec4
 )";
@@ -2555,7 +2556,7 @@
   entry_point.interfaces = "%input %output";
   entry_point.body = R"(
 %input_pos = OpAccessChain %input_f32vec4_ptr %input %u32_0 %u32_0
-%output_pos = OpAccessChain %output_f32vec4_ptr %output %u32_0 %u32_0
+%output_pos = OpAccessChain %output_f32vec4_ptr %output %u32_0
 %pos = OpLoad %f32vec4 %input_pos
 OpStore %output_pos %pos
 )";
@@ -2979,7 +2980,7 @@
 TEST_F(ValidateBuiltIns, ValidBuiltinsForMeshShader) {
   CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
   generator.capabilities_ += R"(
-OpCapability MeshShadingNV 
+OpCapability MeshShadingNV
 )";
 
   generator.extensions_ = R"(
@@ -3017,7 +3018,7 @@
 TEST_F(ValidateBuiltIns, InvalidBuiltinsForMeshShader) {
   CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
   generator.capabilities_ += R"(
-OpCapability MeshShadingNV 
+OpCapability MeshShadingNV
 )";
 
   generator.extensions_ = R"(
@@ -3062,6 +3063,8 @@
                       OpEntryPoint Fragment %4 "PSMa" %12 %17
                       OpExecutionMode %4 OriginUpperLeft
                       OpDecorate %gl_PointCoord BuiltIn PointCoord
+                      OpDecorate %12 Location 0
+                      OpDecorate %17 Location 0
               %void = OpTypeVoid
                  %3 = OpTypeFunction %void
              %float = OpTypeFloat 32
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 8580818..756f762 100644
--- a/third_party/SPIRV-Tools/test/val/val_capability_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_capability_test.cpp
@@ -1367,8 +1367,9 @@
           std::vector<std::string>{"GeometryStreams"}),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n"
-          "OpDecorate %intt Location 0\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpMemberDecorate %struct 0 Location 0\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%struct = OpTypeStruct %intt\n" + std::string(kVoidFVoid),
           ShaderDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n"
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 0d09642..630a19d 100644
--- a/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
@@ -1204,14 +1204,6 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(ValidateCFG, WebGPUUnreachableMergeWithBranchUse) {
-  CompileSuccessfully(
-      GetUnreachableMergeWithBranchUse(SpvCapabilityShader, SPV_ENV_WEBGPU_0));
-  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("cannot be the target of a branch."));
-}
-
 std::string GetUnreachableMergeWithMultipleUses(SpvCapability cap,
                                                 spv_target_env env) {
   std::string header =
@@ -4503,6 +4495,76 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateCFG, UnreachableIsStaticallyReachable) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%1 = OpTypeVoid
+%2 = OpTypeFunction %1
+%3 = OpFunction %1 None %2
+%4 = OpLabel
+OpBranch %5
+%5 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+
+  auto f = vstate_->function(3);
+  auto entry = f->GetBlock(4).first;
+  ASSERT_TRUE(entry->reachable());
+  auto end = f->GetBlock(5).first;
+  ASSERT_TRUE(end->reachable());
+}
+
+TEST_F(ValidateCFG, BlockOrderDoesNotAffectReachability) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%1 = OpTypeVoid
+%2 = OpTypeFunction %1
+%3 = OpTypeBool
+%4 = OpUndef %3
+%5 = OpFunction %1 None %2
+%6 = OpLabel
+OpBranch %7
+%7 = OpLabel
+OpSelectionMerge %8 None
+OpBranchConditional %4 %9 %10
+%8 = OpLabel
+OpReturn
+%9 = OpLabel
+OpBranch %8
+%10 = OpLabel
+OpBranch %8
+%11 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+
+  auto f = vstate_->function(5);
+  auto b6 = f->GetBlock(6).first;
+  auto b7 = f->GetBlock(7).first;
+  auto b8 = f->GetBlock(8).first;
+  auto b9 = f->GetBlock(9).first;
+  auto b10 = f->GetBlock(10).first;
+  auto b11 = f->GetBlock(11).first;
+
+  ASSERT_TRUE(b6->reachable());
+  ASSERT_TRUE(b7->reachable());
+  ASSERT_TRUE(b8->reachable());
+  ASSERT_TRUE(b9->reachable());
+  ASSERT_TRUE(b10->reachable());
+  ASSERT_FALSE(b11->reachable());
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_decoration_test.cpp b/third_party/SPIRV-Tools/test/val/val_decoration_test.cpp
index 204f468..e646162 100644
--- a/third_party/SPIRV-Tools/test/val/val_decoration_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_decoration_test.cpp
@@ -6353,7 +6353,7 @@
                               "requires one of these capabilities"))));
 
 INSTANTIATE_TEST_SUITE_P(
-    DecorationWhitelistFailure, ValidateWebGPUCombineDecorationResult,
+    DecorationAllowListFailure, ValidateWebGPUCombineDecorationResult,
     Combine(Values("RelaxedPrecision", "BufferBlock", "GLSLShared",
                    "GLSLPacked", "Invariant", "Volatile", "Coherent"),
             Values(TestResult(
@@ -7097,6 +7097,68 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
 }
 
+TEST_F(ValidateDecorations, LocationVariableGood) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %in_var Location 0
+%float = OpTypeFloat 32
+%ptr_input_float = OpTypePointer Input %float
+%in_var = OpVariable %ptr_input_float Input
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateDecorations, LocationStructMemberGood) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpMemberDecorate %struct 0 Location 0
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateDecorations, LocationStructBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %struct Location 0
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Location decoration can only be applied to a variable "
+                        "or member of a structure type"));
+}
+
+TEST_F(ValidateDecorations, LocationFloatBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %float Location 0
+%float = OpTypeFloat 32
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Location decoration can only be applied to a variable "
+                        "or member of a structure type"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_extension_spv_khr_terminate_invocation.cpp b/third_party/SPIRV-Tools/test/val/val_extension_spv_khr_terminate_invocation.cpp
new file mode 100644
index 0000000..4cabf9e
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/val/val_extension_spv_khr_terminate_invocation.cpp
@@ -0,0 +1,150 @@
+// 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.
+
+// Tests for OpExtension validator rules.
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "source/enum_string_mapping.h"
+#include "source/extensions.h"
+#include "source/spirv_target_env.h"
+#include "test/test_fixture.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Values;
+using ::testing::ValuesIn;
+
+using ValidateSpvKHRTerminateInvocation = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateSpvKHRTerminateInvocation, Valid) {
+  const std::string str = R"(
+    OpCapability Shader
+    OpExtension "SPV_KHR_terminate_invocation"
+    OpMemoryModel Logical Simple
+    OpEntryPoint Fragment %main "main"
+    OpExecutionMode %main OriginUpperLeft
+    
+    %void    = OpTypeVoid
+    %void_fn = OpTypeFunction %void
+
+    %main = OpFunction %void None %void_fn
+    %entry = OpLabel
+    OpTerminateInvocation
+    OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateSpvKHRTerminateInvocation, RequiresExtension) {
+  const std::string str = R"(
+    OpCapability Shader
+    OpMemoryModel Logical Simple
+    OpEntryPoint Fragment %main "main"
+    OpExecutionMode %main OriginUpperLeft
+    
+    %void    = OpTypeVoid
+    %void_fn = OpTypeFunction %void
+
+    %main = OpFunction %void None %void_fn
+    %entry = OpLabel
+    OpTerminateInvocation
+    OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("TerminateInvocation requires one of the following "
+                        "extensions: SPV_KHR_terminate_invocation"));
+}
+
+TEST_F(ValidateSpvKHRTerminateInvocation, RequiresShaderCapability) {
+  const std::string str = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpExtension "SPV_KHR_terminate_invocation"
+    OpMemoryModel Physical32 OpenCL
+    OpEntryPoint Kernel %main "main"
+    
+    %void    = OpTypeVoid
+    %void_fn = OpTypeFunction %void
+
+    %main = OpFunction %void None %void_fn
+    %entry = OpLabel
+    OpTerminateInvocation
+    OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "TerminateInvocation requires one of these capabilities: Shader \n"));
+}
+
+TEST_F(ValidateSpvKHRTerminateInvocation, RequiresFragmentShader) {
+  const std::string str = R"(
+    OpCapability Shader
+    OpExtension "SPV_KHR_terminate_invocation"
+    OpMemoryModel Logical Simple
+    OpEntryPoint GLCompute %main "main"
+    
+    %void    = OpTypeVoid
+    %void_fn = OpTypeFunction %void
+
+    %main = OpFunction %void None %void_fn
+    %entry = OpLabel
+    OpTerminateInvocation
+    OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpTerminateInvocation requires Fragment execution model"));
+}
+
+TEST_F(ValidateSpvKHRTerminateInvocation, IsTerminatorInstruction) {
+  const std::string str = R"(
+    OpCapability Shader
+    OpExtension "SPV_KHR_terminate_invocation"
+    OpMemoryModel Logical Simple
+    OpEntryPoint GLCompute %main "main"
+    
+    %void    = OpTypeVoid
+    %void_fn = OpTypeFunction %void
+
+    %main = OpFunction %void None %void_fn
+    %entry = OpLabel
+    OpTerminateInvocation
+    OpReturn
+    OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Return must appear in a block"));
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_extensions_test.cpp b/third_party/SPIRV-Tools/test/val/val_extensions_test.cpp
index 682c321..491a808 100644
--- a/third_party/SPIRV-Tools/test/val/val_extensions_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_extensions_test.cpp
@@ -62,7 +62,8 @@
         "SPV_EXT_shader_viewport_index_layer",
         "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_fragment_mask",
         "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1",
-        "SPV_NV_shader_subgroup_partitioned", "SPV_EXT_descriptor_indexing"));
+        "SPV_NV_shader_subgroup_partitioned", "SPV_EXT_descriptor_indexing",
+        "SPV_KHR_terminate_invocation"));
 
 INSTANTIATE_TEST_SUITE_P(FailSilently, ValidateUnknownExtensions,
                          Values("ERROR_unknown_extension", "SPV_KHR_",
diff --git a/third_party/SPIRV-Tools/test/val/val_interfaces_test.cpp b/third_party/SPIRV-Tools/test/val/val_interfaces_test.cpp
index 3410616..f2fb45a 100644
--- a/third_party/SPIRV-Tools/test/val/val_interfaces_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_interfaces_test.cpp
@@ -399,6 +399,986 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
 }
 
+TEST_F(ValidateInterfacesTest, VulkanLocationsDoubleAssignmentVariable) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var Location 0
+OpDecorate %var Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%ptr_input_float = OpTypePointer Input %float
+%var = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Variable has conflicting location decorations"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsVariableAndMemberAssigned) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var Location 0
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Location 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Members cannot be assigned a location"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsMemberAndSubMemberAssigned) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %outer Block
+OpMemberDecorate %outer 0 Location 0
+OpMemberDecorate %struct 0 Location 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float
+%outer = OpTypeStruct %struct
+%ptr_input_outer = OpTypePointer Input %outer
+%var = OpVariable %ptr_input_outer Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Members cannot be assigned a location"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsDoubleAssignmentStructMember) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 1 Location 0
+OpMemberDecorate %struct 1 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Member index 1 has conflicting location assignments"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsMissingAssignmentStructMember) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 1 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Member index 0 is missing a location assignment"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsMissingAssignmentNonBlockStruct) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Variable must be decorated with a location"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsVariableConflictInput) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var1 = OpVariable %ptr_input_struct Input
+%var2 = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Entry-point has conflicting input location assignment "
+                        "at location 0"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsVariableConflictOutput) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 1
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_output_struct = OpTypePointer Output %struct
+%var1 = OpVariable %ptr_output_struct Output
+%var2 = OpVariable %ptr_output_struct Output
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Entry-point has conflicting output location assignment "
+                "at location 1"));
+}
+
+TEST_F(ValidateInterfacesTest,
+       VulkanLocationsSameLocationInputAndOutputNoConflict) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 1
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_input_struct = OpTypePointer Input %struct
+%ptr_output_struct = OpTypePointer Output %struct
+%var1 = OpVariable %ptr_input_struct Input
+%var2 = OpVariable %ptr_output_struct Output
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsVariableInGap) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Location 0
+OpMemberDecorate %struct 1 Location 2
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_input_struct = OpTypePointer Input %struct
+%ptr_input_float = OpTypePointer Input %float
+%var1 = OpVariable %ptr_input_struct Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsLargeFloatVectorConflict) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Float64
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%double = OpTypeFloat 64
+%vector = OpTypeVector %double 3
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_vector = OpTypePointer Input %vector
+%var1 = OpVariable %ptr_input_vector Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Entry-point has conflicting input location assignment "
+                        "at location 1"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsLargeIntVectorConflict) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Int64
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%long = OpTypeInt 64 0
+%vector = OpTypeVector %long 4
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_vector = OpTypePointer Input %vector
+%var1 = OpVariable %ptr_input_vector Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Entry-point has conflicting input location assignment "
+                        "at location 1"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsMatrix2x2Conflict) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%vector = OpTypeVector %float 2
+%matrix = OpTypeMatrix %vector 2
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_matrix = OpTypePointer Input %matrix
+%var1 = OpVariable %ptr_input_matrix Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Entry-point has conflicting input location assignment "
+                        "at location 1"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsMatrix3x3Conflict) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 2
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%vector = OpTypeVector %float 3
+%matrix = OpTypeMatrix %vector 3
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_matrix = OpTypePointer Input %matrix
+%var1 = OpVariable %ptr_input_matrix Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Entry-point has conflicting input location assignment "
+                        "at location 2"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsMatrix4x4Conflict) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 3
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%vector = OpTypeVector %float 4
+%matrix = OpTypeMatrix %vector 4
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_matrix = OpTypePointer Input %matrix
+%var1 = OpVariable %ptr_input_matrix Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Entry-point has conflicting input location assignment "
+                        "at location 3"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsLargeMatrix2x2Conflict) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Float64
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%double = OpTypeFloat 64
+%vector = OpTypeVector %double 2
+%matrix = OpTypeMatrix %vector 2
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_matrix = OpTypePointer Input %matrix
+%var1 = OpVariable %ptr_input_matrix Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Entry-point has conflicting input location assignment "
+                        "at location 1"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsLargeMatrix3x3Conflict) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Float64
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 5
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%double = OpTypeFloat 64
+%vector = OpTypeVector %double 3
+%matrix = OpTypeMatrix %vector 3
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_matrix = OpTypePointer Input %matrix
+%var1 = OpVariable %ptr_input_matrix Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Entry-point has conflicting input location assignment "
+                        "at location 5"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsLargeMatrix4x4Conflict) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Float64
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 7
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%double = OpTypeFloat 64
+%vector = OpTypeVector %double 4
+%matrix = OpTypeMatrix %vector 4
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_matrix = OpTypePointer Input %matrix
+%var1 = OpVariable %ptr_input_matrix Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Entry-point has conflicting input location assignment "
+                        "at location 7"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsArray2Conflict) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_2 = OpConstant %int 2
+%array = OpTypeArray %int %int_2
+%struct = OpTypeStruct %array
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var1 = OpVariable %ptr_input_struct Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Entry-point has conflicting input location assignment "
+                        "at location 1"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsArray4Conflict) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 3
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %int %int_4
+%struct = OpTypeStruct %array
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var1 = OpVariable %ptr_input_struct Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Entry-point has conflicting input location assignment "
+                        "at location 3"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsMatrix4x4Array4Conflict) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 15
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%vector = OpTypeVector %float 4
+%matrix = OpTypeMatrix %vector 4
+%array = OpTypeArray %matrix %int_4
+%struct = OpTypeStruct %array
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var1 = OpVariable %ptr_input_struct Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Entry-point has conflicting input location assignment "
+                        "at location 15"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsComponentDisambiguates) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Location 0
+OpMemberDecorate %struct 0 Component 0
+OpMemberDecorate %struct 1 Location 0
+OpMemberDecorate %struct 1 Component 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var1 = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsComponentIn64BitVec3) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Float64
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Location 0
+OpMemberDecorate %struct 1 Location 1
+OpMemberDecorate %struct 1 Component 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%double = OpTypeFloat 64
+%double3 = OpTypeVector %double 3
+%struct = OpTypeStruct %double3 %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Entry-point has conflicting input location assignment "
+                        "at location 1, component 1"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsComponentAfter64BitVec3) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Float64
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Location 0
+OpMemberDecorate %struct 1 Location 1
+OpMemberDecorate %struct 1 Component 2
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%double = OpTypeFloat 64
+%double3 = OpTypeVector %double 3
+%struct = OpTypeStruct %double3 %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsConflictingComponentVariable) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var Location 0
+OpDecorate %var Component 0
+OpDecorate %var Component 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%ptr_input_float = OpTypePointer Input %float
+%var = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Variable has conflicting component decorations"));
+}
+
+TEST_F(ValidateInterfacesTest,
+       VulkanLocationsConflictingComponentStructMember) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Location 0
+OpMemberDecorate %struct 0 Component 0
+OpMemberDecorate %struct 0 Component 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Member index 0 has conflicting component assignments"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsVariableConflictOutputIndex1) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 1
+OpDecorate %var1 Index 1
+OpDecorate %var2 Location 1
+OpDecorate %var2 Index 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_output_struct = OpTypePointer Output %struct
+%var1 = OpVariable %ptr_output_struct Output
+%var2 = OpVariable %ptr_output_struct Output
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Entry-point has conflicting output location assignment "
+                "at location 1"));
+}
+
+TEST_F(ValidateInterfacesTest,
+       VulkanLocationsVariableNoConflictDifferentIndex) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 1
+OpDecorate %var1 Index 0
+OpDecorate %var2 Location 1
+OpDecorate %var2 Index 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_output_struct = OpTypePointer Output %struct
+%var1 = OpVariable %ptr_output_struct Output
+%var2 = OpVariable %ptr_output_struct Output
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsIndexGLCompute) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %var1
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var1 Location 1
+OpDecorate %var1 Index 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_output_struct = OpTypePointer Output %struct
+%var1 = OpVariable %ptr_output_struct Output
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Index can only be applied to Fragment output variables"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsIndexInput) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 1
+OpDecorate %var1 Index 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var1 = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Index can only be applied to Fragment output variables"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsArrayWithComponent) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main" %11 %18 %28 %36 %40
+OpExecutionMode %4 OriginUpperLeft
+OpDecorate %11 Location 0
+OpDecorate %18 Component 0
+OpDecorate %18 Location 0
+OpDecorate %28 Component 1
+OpDecorate %28 Location 0
+OpDecorate %36 Location 1
+OpDecorate %40 Component 0
+OpDecorate %40 Location 1
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%11 = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_float = OpTypePointer Output %float
+%18 = OpVariable %_ptr_Output_float Output
+%uint = OpTypeInt 32 0
+%v3float = OpTypeVector %float 3
+%uint_2 = OpConstant %uint 2
+%_arr_v3float_uint_2 = OpTypeArray %v3float %uint_2
+%_ptr_Output__arr_v3float_uint_2 = OpTypePointer Output %_arr_v3float_uint_2
+%28 = OpVariable %_ptr_Output__arr_v3float_uint_2 Output
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+%36 = OpVariable %_ptr_Input_v4float Input
+%40 = OpVariable %_ptr_Output_float Output
+%4 = OpFunction %void None %3
+%5 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsArrayWithComponentBad) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main" %11 %18 %28 %36 %40
+OpExecutionMode %4 OriginUpperLeft
+OpDecorate %11 Location 0
+OpDecorate %18 Component 0
+OpDecorate %18 Location 0
+OpDecorate %28 Component 1
+OpDecorate %28 Location 0
+OpDecorate %36 Location 1
+OpDecorate %40 Component 1
+OpDecorate %40 Location 1
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%11 = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_float = OpTypePointer Output %float
+%18 = OpVariable %_ptr_Output_float Output
+%uint = OpTypeInt 32 0
+%v3float = OpTypeVector %float 3
+%uint_2 = OpConstant %uint 2
+%_arr_v3float_uint_2 = OpTypeArray %v3float %uint_2
+%_ptr_Output__arr_v3float_uint_2 = OpTypePointer Output %_arr_v3float_uint_2
+%28 = OpVariable %_ptr_Output__arr_v3float_uint_2 Output
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+%36 = OpVariable %_ptr_Input_v4float Input
+%40 = OpVariable %_ptr_Output_float Output
+%4 = OpFunction %void None %3
+%5 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Entry-point has conflicting output location "
+                        "assignment at location 1, component 1"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsLargeLocation) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "????????" %17
+               OpExecutionMode %4 OriginUpperLeft
+               OpDecorate %17 Location 4227868160
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+         %17 = OpVariable %_ptr_Input_v3float Input
+          %4 = OpFunction %void None %3
+          %5 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_modes_test.cpp b/third_party/SPIRV-Tools/test/val/val_modes_test.cpp
index 688f433..0a1476e 100644
--- a/third_party/SPIRV-Tools/test/val/val_modes_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_modes_test.cpp
@@ -714,14 +714,14 @@
                    "SubgroupsPerWorkgroup 1", "SubgroupsPerWorkgroupId %int1"),
             Values(SPV_ENV_UNIVERSAL_1_3)));
 
-INSTANTIATE_TEST_SUITE_P(ValidateModeGLComputeWebGPUWhitelistGood,
+INSTANTIATE_TEST_SUITE_P(ValidateModeGLComputeWebGPUAllowListGood,
                          ValidateModeExecution,
                          Combine(Values(SPV_SUCCESS), Values(""),
                                  Values("GLCompute"), Values("LocalSize 1 1 1"),
                                  Values(SPV_ENV_WEBGPU_0)));
 
 INSTANTIATE_TEST_SUITE_P(
-    ValidateModeGLComputeWebGPUWhitelistBad, ValidateModeExecution,
+    ValidateModeGLComputeWebGPUAllowListBad, ValidateModeExecution,
     Combine(Values(SPV_ERROR_INVALID_DATA),
             Values("Execution mode must be one of OriginUpperLeft, "
                    "DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, "
@@ -730,14 +730,14 @@
             Values(SPV_ENV_WEBGPU_0)));
 
 INSTANTIATE_TEST_SUITE_P(
-    ValidateModeFragmentWebGPUWhitelistGood, ValidateModeExecution,
+    ValidateModeFragmentWebGPUAllowListGood, ValidateModeExecution,
     Combine(Values(SPV_SUCCESS), Values(""), Values("Fragment"),
             Values("OriginUpperLeft", "DepthReplacing", "DepthGreater",
                    "DepthLess", "DepthUnchanged"),
             Values(SPV_ENV_WEBGPU_0)));
 
 INSTANTIATE_TEST_SUITE_P(
-    ValidateModeFragmentWebGPUWhitelistBad, ValidateModeExecution,
+    ValidateModeFragmentWebGPUAllowListBad, ValidateModeExecution,
     Combine(Values(SPV_ERROR_INVALID_DATA),
             Values("Execution mode must be one of OriginUpperLeft, "
                    "DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, "
diff --git a/third_party/SPIRV-Tools/test/val/val_webgpu_test.cpp b/third_party/SPIRV-Tools/test/val/val_webgpu_test.cpp
index e81fc7c..62fa6a7 100644
--- a/third_party/SPIRV-Tools/test/val/val_webgpu_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_webgpu_test.cpp
@@ -239,22 +239,73 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
-TEST_F(ValidateWebGPU, NonVulkanKHRMemoryModelBad) {
+TEST_F(ValidateWebGPU, LogicalAddressingGLSL450MemoryGood) {
   std::string spirv = R"(
-     OpCapability Shader
-     OpMemoryModel Logical GLSL450
+          OpCapability Shader
+          OpMemoryModel Logical GLSL450
+          OpEntryPoint Vertex %func "shader"
+%void   = OpTypeVoid
+%void_f = OpTypeFunction %void
+%func   = OpFunction %void None %void_f
+%label  = OpLabel
+          OpReturn
+          OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
+}
+
+TEST_F(ValidateWebGPU, LogicalAddressingSimpleMemoryGood) {
+  std::string spirv = R"(
+          OpCapability Shader
+          OpMemoryModel Logical Simple
+          OpEntryPoint Vertex %func "shader"
+%void   = OpTypeVoid
+%void_f = OpTypeFunction %void
+%func   = OpFunction %void None %void_f
+%label  = OpLabel
+          OpReturn
+          OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
+}
+
+TEST_F(ValidateWebGPU, KernelIsBad) {
+  std::string spirv = R"(
+     OpCapability Kernel
+     OpMemoryModel Logical Simple
      OpNoLine
 )";
 
   CompileSuccessfully(spirv);
 
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_WEBGPU_0));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Memory model must be VulkanKHR for WebGPU "
-                        "environment.\n  OpMemoryModel Logical GLSL450\n"));
+              HasSubstr("Capability Kernel is not allowed by WebGPU "
+                        "specification (or requires extension)"));
 }
 
-TEST_F(ValidateWebGPU, WhitelistedExtendedInstructionsImportGood) {
+TEST_F(ValidateWebGPU, OpenCLMemoryModelBad) {
+  std::string spirv = R"(
+     OpCapability Shader
+     OpMemoryModel Logical OpenCL
+     OpNoLine
+)";
+
+  CompileSuccessfully(spirv);
+
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_WEBGPU_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Operand 2 of MemoryModel requires one of these "
+                        "capabilities: Kernel"));
+}
+
+TEST_F(ValidateWebGPU, AllowListedExtendedInstructionsImportGood) {
   std::string spirv = R"(
           OpCapability Shader
           OpCapability VulkanMemoryModelKHR
@@ -275,7 +326,7 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
-TEST_F(ValidateWebGPU, NonWhitelistedExtendedInstructionsImportBad) {
+TEST_F(ValidateWebGPU, NonAllowListedExtendedInstructionsImportBad) {
   std::string spirv = R"(
      OpCapability Shader
      OpCapability VulkanMemoryModelKHR
diff --git a/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp b/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp
index 2c9807d..61064d1 100644
--- a/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp
+++ b/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp
@@ -121,6 +121,18 @@
   --replay
                File from which to read a sequence of transformations to replay
                (instead of fuzzing)
+  --replay-range=
+               Signed 32-bit integer.  If set to a positive value N, only the
+               first N transformations will be applied during replay.  If set to
+               a negative value -N, all but the final N transformations will be
+               applied during replay.  If set to 0 (the default), all
+               transformations will be applied during replay.  Ignored unless
+               --replay is used.
+  --replay-validation
+               Run the validator after applying each transformation during
+               replay (including the replay that occurs during shrinking).
+               Aborts if an invalid binary is created.  Useful for debugging
+               spirv-fuzz.
   --seed=
                Unsigned 32-bit integer seed to control random number
                generation.
@@ -137,11 +149,6 @@
                extension will be added.  The default is "temp_", which will
                cause files like "temp_0001.spv" to be output to the current
                directory.  Ignored unless --shrink is used.
-  --replay-validation
-               Run the validator after applying each transformation during
-               replay (including the replay that occurs during shrinking).
-               Aborts if an invalid binary is created.  Useful for debugging
-               spirv-fuzz.
   --version
                Display fuzzer version information.
 
@@ -208,6 +215,15 @@
       } else if (0 == strncmp(cur_arg, "--replay=", sizeof("--replay=") - 1)) {
         const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
         *replay_transformations_file = std::string(split_flag.second);
+      } else if (0 == strncmp(cur_arg, "--replay-range=",
+                              sizeof("--replay-range=") - 1)) {
+        const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
+        char* end = nullptr;
+        errno = 0;
+        const auto replay_range =
+            static_cast<int32_t>(strtol(split_flag.second.c_str(), &end, 10));
+        assert(end != split_flag.second.c_str() && errno == 0);
+        fuzzer_options->set_replay_range(replay_range);
       } else if (0 == strncmp(cur_arg, "--replay-validation",
                               sizeof("--replay-validation") - 1)) {
         fuzzer_options->enable_replay_validation();
@@ -392,9 +408,27 @@
   spvtools::fuzz::Replayer replayer(
       target_env, fuzzer_options->replay_validation_enabled, validator_options);
   replayer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
-  auto replay_result_status =
-      replayer.Run(binary_in, initial_facts, transformation_sequence,
-                   binary_out, transformations_applied);
+
+  uint32_t num_transformations_to_apply;
+  if (fuzzer_options->replay_range > 0) {
+    // We have a positive replay range, N.  We would like transformations
+    // [0, N), truncated to the number of available transformations if N is too
+    // large.
+    num_transformations_to_apply = static_cast<uint32_t>(
+        std::min(fuzzer_options->replay_range,
+                 transformation_sequence.transformation_size()));
+  } else {
+    // We have non-positive replay range, -N (where N may be 0).  We would like
+    // transformations [0, num_transformations - N), or no transformations if N
+    // is too large.
+    num_transformations_to_apply = static_cast<uint32_t>(
+        std::max(0, transformation_sequence.transformation_size() +
+                        fuzzer_options->replay_range));
+  }
+
+  auto replay_result_status = replayer.Run(
+      binary_in, initial_facts, transformation_sequence,
+      num_transformations_to_apply, binary_out, transformations_applied);
   return !(replay_result_status !=
            spvtools::fuzz::Replayer::ReplayerResultStatus::kComplete);
 }
diff --git a/third_party/SPIRV-Tools/utils/check_copyright.py b/third_party/SPIRV-Tools/utils/check_copyright.py
index 4467a32..39d27cb 100755
--- a/third_party/SPIRV-Tools/utils/check_copyright.py
+++ b/third_party/SPIRV-Tools/utils/check_copyright.py
@@ -35,7 +35,8 @@
            'Samsung Inc',
            'André Perez Maselco',
            'Vasyl Teliman',
-           'Advanced Micro Devices, Inc.']
+           'Advanced Micro Devices, Inc.',
+           'Stefano Milizia']
 CURRENT_YEAR='2020'
 
 YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020)'
diff --git a/third_party/SPIRV-Tools/utils/check_symbol_exports.py b/third_party/SPIRV-Tools/utils/check_symbol_exports.py
index e14c2eb..bcd77da 100755
--- a/third_party/SPIRV-Tools/utils/check_symbol_exports.py
+++ b/third_party/SPIRV-Tools/utils/check_symbol_exports.py
@@ -55,11 +55,11 @@
     #   _Z[0-9]+spv[A-Z_] :  C++ symbol starting with spv[A-Z_]
     symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_Z[0-9]+spv[A-Z_])')
 
-    # In addition, the following pattern whitelists global functions that are added
+    # In addition, the following pattern allowlists global functions that are added
     # by the protobuf compiler:
     #   - AddDescriptors_spvtoolsfuzz_2eproto()
     #   - InitDefaults_spvtoolsfuzz_2eproto()
-    symbol_whitelist_pattern = re.compile(r'_Z[0-9]+(InitDefaults|AddDescriptors)_spvtoolsfuzz_2eprotov')
+    symbol_allowlist_pattern = re.compile(r'_Z[0-9]+(InitDefaults|AddDescriptors)_spvtoolsfuzz_2eprotov')
 
     seen = set()
     result = 0
@@ -70,7 +70,7 @@
             if symbol not in seen:
                 seen.add(symbol)
                 #print("look at '{}'".format(symbol))
-                if not (symbol_whitelist_pattern.match(symbol) or symbol_ok_pattern.match(symbol)):
+                if not (symbol_allowlist_pattern.match(symbol) or symbol_ok_pattern.match(symbol)):
                     print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol))
                     result = 1
     return result
diff --git a/third_party/SPIRV-Tools/utils/roll_deps.sh b/third_party/SPIRV-Tools/utils/roll_deps.sh
index 622afc9..7ecfdd3 100755
--- a/third_party/SPIRV-Tools/utils/roll_deps.sh
+++ b/third_party/SPIRV-Tools/utils/roll_deps.sh
@@ -13,19 +13,34 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Attempts to roll all entries in DEPS to origin/master and creates a
-# commit.
+# Attempts to roll all entries in DEPS to tip-of-tree and create a commit.
 #
 # Depends on roll-dep from depot_path being in PATH.
 
+effcee_dir="external/effcee/"
+effcee_trunk="origin/main"
+googletest_dir="external/googletest/"
+googletest_trunk="origin/master"
+re2_dir="external/re2/"
+re2_trunk="origin/master"
+spirv_headers_dir="external/spirv-headers/"
+spirv_headers_trunk="origin/master"
+
 # This script assumes it's parent directory is the repo root.
 repo_path=$(dirname "$0")/..
 
-effcee_dir="external/effcee/"
-googletest_dir="external/googletest/"
-re2_dir="external/re2/"
-spirv_headers_dir="external/spirv-headers/"
-
 cd "$repo_path"
 
-roll-dep "$@" "${effcee_dir}" "${googletest_dir}" "${re2_dir}" "${spirv_headers_dir}"
+if [[ $(git diff --stat) != '' ]]; then
+    echo "Working tree is dirty, commit changes before attempting to roll DEPS"
+    exit 1
+fi
+
+old_head=$(git rev-parse HEAD)
+
+roll-dep --ignore-dirty-tree --roll-to="${effcee_trunk}" "${effcee_dir}"
+roll-dep --ignore-dirty-tree --roll-to="${googletest_trunk}" "${googletest_dir}"
+roll-dep --ignore-dirty-tree --roll-to="${re2_trunk}" "${re2_dir}"
+roll-dep --ignore-dirty-tree --roll-to="${spirv_headers_trunk}" "${spirv_headers_dir}"
+
+git rebase --interactive "${old_head}"