Update SPIR-V Tools to 55af3902f

Changes:
    55af3902fc2 Fix function use (#3372)
    9cb2571a184 spirv-val: allow DebugInfoNone for DebugTypeComposite.Size (#3374)
    4386ef4234e Add validation support for ImageGatherBiasLodAMD (#3363)
    b0264b87ffb Fix validation failure on OpDecorationGroup (#3365)
    4410272bdda Remove deprecated interfaces from instrument passes (#3361)
    50b15578866 Preserve debug info in inline pass (#3349)
    4dbe18b0c86 Reject folding comparisons with unfoldable types. (#3370)
    55193b06e5e Improve build instructions for fuzzer (#3364)
    3c47dac2820 Add unrolling to performance passes (#3082)
    2b987c49a4e Handle OpConstantNull in ssa-rewrite (#3362)
    95df4c9643c Add in a bunch of missed files to the BUILD.gn (#3360)
    90930cb3115 Remove stale entries from BUILD.gn (#3358)
    18ba3d9a353 allow cross compiling for Windows Store, UWP, etc. (#3330)
    2f69ea849aa spirv-fuzz: Remove FuzzerPassAddUsefulConstructs (#3341)
    522561619a9 Add support for StorageBuffer (#3348)
    b75dbf82a69 Prevent Effcee install his things when build spirv-tools with testing enabled (#3256)
    85c7e7956bf Don't register edges twice in merge return (#3350)
    bd0a2da946c Revert "Revert "[spirv-opt] refactor inlining pass (#3328)" (#3342)" (#3345)
    31182763704 spirv-reduce: Remove unused struct members (#3329)
    a6b0e132ecc Add adjust branch weights transformation (#3336)
    d4fac3451b7 Revert "[spirv-opt] refactor inlining pass (#3328)" (#3342)
    233246bc9c5 [spirv-opt] refactor inlining pass (#3328)
    2992386ebea spirv-reduce: Remove unused uniforms and similar (#3321)
    a9f2a145e65 spirv-fuzz: Fix to fact manager (#3339)
    045a26e6e37 spirv-fuzz: Get rid of unnecessary template method (#3340)
    63fa9114a93 Do merge return if the return is not at the end of the function. (#3337)
    c8590c18bd0 Preserve debug info for wrap-opkill (#3331)
    d2b48621949 Validate ShaderCallKHR memory scope (#3332)
    2e1d208ed9d spirv-fuzz: Do not allow adding stores to read-only pointers (#3316)
    54fb17b2d30 reduce: increase default step limit (#3327)
    49842b88eec Generalize IsReadOnlyVariable() to apply to pointers (#3325)
    49ca250b44c Delete nullptr in function bb list immedietly (#3326)
    d0a87194f7b Set DebugScope for termination instructions (#3323)
    f278b467dfd spirv-fuzz: Do not outline regions that end with a loop header (#3312)
    23d68608b00 vscode: Handle '|' chains on BitEnum / ValueEnum (#3309)
    42268740c95 Add debug information analysis (#3305)
    eed48ae479d Add spvtools::opt::Operand::AsLiteralUint64 (#3320)
    94d6002dc53 spirv-fuzz: Pass on validator options during shrinking (#3317)
    88faf63ad3c spirv-fuzz: Clamp statically out-of-bounds accesses in code donation (#3315)
    b74199a22d4 spirv-fuzz: Fix memory management in the fact manager (#3313)
    d158ffe5405 spirv-fuzz: Do not replace the Sample argument in OpImageTexelPointer (#3311)
    5547553a0c7 Allow various validation options to be passed to spirv-opt (#3314)
    30ffe62e257 typo fix: in README.md exectuable->executable (#3306)
    67f4838659f spirv-fuzz: Make handling of synonym facts more efficient (#3301)
    61b7de3c39f Remove unreachable code. (#3304)
    ed96301c6c4 spirv-fuzz: Fix to outliner (#3302)
    c018fc6ae66 spirv-fuzz: Do not outline regions that produce pointer outputs (#3291)
    f460cca9dca spirv-fuzz: Handle OpRuntimeArray when replacing ids with synonyms (#3292)
    2f180468a71 spirv-fuzz: Handle image storage class in donation (#3290)
    f82d47003e7 spirv-fuzz: Respect rules for OpSampledImage (#3287)
    7ce2db1763b spirv-fuzz: Fix comment. (#3300)
    7d65bce0bbe Sampled images as read-only storage (#3295)
    2a2bdbd5d72 Remove implicit fallthrough (#3298)
    49566448944 Add tests for recently added command line option (#3297)
    ca5751590ed If SPIRV-Headers is in our tree, include it as subproject (#3299)
    e70d25f6fa5 Struct CFG analysus and single block loop (#3293)
    000040e707a Preserve debug info in eliminate-dead-functions (#3251)
    c531099eb34 Update acorn version (#3294)
    34be23373b9 Handle more cases in dead member elim (#3289)
    d0490ef080c Fix pch macro to ignore clang-cl (#3283)
    538512e8e89 spirv-fuzz: Improve the handling of equation facts (#3281)
    183e3242a36 spirv-fuzz: Handle more general SPIR-V in donation (#3280)
    4af38c49bfe spirv-fuzz: Improve support for compute shaders in donation (#3277)
    e95fbfb1f50 spirv-fuzz: Transformation to add OpConstantNull (#3273)
    5d491a7ed66 spirv-fuzz: Handle isomorphic types property in composite construction (#3262)
    bfd25ace084 spirv-fuzz: Limit adding of new variables to 'basic' types (#3257)
    f28cdeff16f spirv-fuzz: Only replace regular ids with synonyms (#3255)
    8d4261bc440 spirv-fuzz: Introduce TransformationContext (#3272)
    2fdea57d19d spirv-fuzz: Add validator options (#3254)
    af01d57b5e3 Update dominates to check for null nodes (#3271)
    f20c0d7971c Set wrapped kill basic block's parent (#3269)
    c37c94929bf Validate Buffer and BufferBlock apply only to struct types (#3259)

Commands:
    ./third_party/update-spirvtools.sh

Bug: b/123642959
Change-Id: Id033a5049da5a8b7c7fbf5f1896a985abfbd4ad8
diff --git a/third_party/SPIRV-Tools/BUILD.gn b/third_party/SPIRV-Tools/BUILD.gn
index d3107fd..fae7957 100644
--- a/third_party/SPIRV-Tools/BUILD.gn
+++ b/third_party/SPIRV-Tools/BUILD.gn
@@ -542,6 +542,8 @@
     "source/opt/decompose_initialized_variables_pass.h",
     "source/opt/decoration_manager.cpp",
     "source/opt/decoration_manager.h",
+    "source/opt/debug_info_manager.cpp",
+    "source/opt/debug_info_manager.h",
     "source/opt/def_use_manager.cpp",
     "source/opt/def_use_manager.h",
     "source/opt/desc_sroa.cpp",
@@ -790,8 +792,12 @@
     "source/reduce/remove_selection_reduction_opportunity.h",
     "source/reduce/remove_selection_reduction_opportunity_finder.cpp",
     "source/reduce/remove_selection_reduction_opportunity_finder.h",
-    "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp",
-    "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h",
+    "source/reduce/remove_struct_member_reduction_opportunity.cpp",
+    "source/reduce/remove_struct_member_reduction_opportunity.h",
+    "source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp",
+    "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h",
+    "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp",
+    "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h",
     "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp",
     "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h",
     "source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp",
diff --git a/third_party/SPIRV-Tools/CMakeLists.txt b/third_party/SPIRV-Tools/CMakeLists.txt
index ef9ad11..73248f2 100644
--- a/third_party/SPIRV-Tools/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/CMakeLists.txt
@@ -40,7 +40,7 @@
   set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS})
 elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Emscripten")
     add_definitions(-DSPIRV_EMSCRIPTEN)
-elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
+elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Windows")
   add_definitions(-DSPIRV_WINDOWS)
 elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "CYGWIN")
   add_definitions(-DSPIRV_WINDOWS)
@@ -249,7 +249,7 @@
 
 # Precompiled header macro. Parameters are source file list and filename for pch cpp file.
 macro(spvtools_pch SRCS PCHPREFIX)
-  if(MSVC AND CMAKE_GENERATOR MATCHES "^Visual Studio")
+  if(MSVC AND CMAKE_GENERATOR MATCHES "^Visual Studio" AND NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
     set(PCH_NAME "$(IntDir)\\${PCHPREFIX}.pch")
     # make source files use/depend on PCH_NAME
     set_source_files_properties(${${SRCS}} PROPERTIES COMPILE_FLAGS "/Yu${PCHPREFIX}.h /FI${PCHPREFIX}.h /Fp${PCH_NAME} /Zm300" OBJECT_DEPENDS "${PCH_NAME}")
diff --git a/third_party/SPIRV-Tools/README.md b/third_party/SPIRV-Tools/README.md
index 5714976..c82ca19 100644
--- a/third_party/SPIRV-Tools/README.md
+++ b/third_party/SPIRV-Tools/README.md
@@ -324,8 +324,7 @@
 contents of the repos under `external/` instead of manually maintaining them.
 
 ### Build using CMake
-You can build The project using [CMake][cmake] to generate platform-specific
-build configurations.
+You can build the project using [CMake][cmake]:
 
 ```sh
 cd <spirv-dir>
@@ -333,8 +332,36 @@
 cmake [-G <platform-generator>] <spirv-dir>
 ```
 
-Once the build files have been generated, build using your preferred
-development environment.
+Once the build files have been generated, build using the appropriate build
+command (e.g. `ninja`, `make`, `msbuild`, etc.; this depends on the platform
+generator used above), or use your IDE, or use CMake to run the appropriate build
+command for you:
+
+```sh
+cmake --build . [--config Debug]  # runs `make` or `ninja` or `msbuild` etc.
+```
+
+#### Note about the fuzzer
+
+The SPIR-V fuzzer, `spirv-fuzz`, can only be built via CMake, and is disabled by
+default. To build it, clone protobuf and use the `SPIRV_BUILD_FUZZER` CMake
+option, like so:
+
+```sh
+# In <spirv-dir> (the SPIRV-Tools repo root):
+git clone https://github.com/protocolbuffers/protobuf external/protobuf
+pushd external/protobuf
+git checkout v3.7.1
+popd
+
+# In your build directory:
+cmake [-G <platform-generator>] <spirv-dir> -DSPIRV_BUILD_FUZZER=ON
+cmake --build . --config Debug
+```
+
+You can also add `-DSPIRV_ENABLE_LONG_FUZZER_TESTS=ON` to build additional
+fuzzer tests.
+
 
 ### Build using Bazel
 You can also use [Bazel](https://bazel.build/) to build the project.
@@ -480,7 +507,7 @@
 
 The assembler reads the assembly language text, and emits the binary form.
 
-The standalone assembler is the exectuable called `spirv-as`, and is located in
+The standalone assembler is the executable called `spirv-as`, and is located in
 `<spirv-build-dir>/tools/spirv-as`.  The functionality of the assembler is implemented
 by the `spvTextToBinary` library function.
 
diff --git a/third_party/SPIRV-Tools/external/CMakeLists.txt b/third_party/SPIRV-Tools/external/CMakeLists.txt
index 8bde13c..56dd54f 100644
--- a/third_party/SPIRV-Tools/external/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/external/CMakeLists.txt
@@ -25,7 +25,17 @@
 endif()
 
 if (IS_DIRECTORY ${SPIRV_HEADER_DIR})
+  # TODO(dneto): We should not be modifying the parent scope.
   set(SPIRV_HEADER_INCLUDE_DIR ${SPIRV_HEADER_DIR}/include PARENT_SCOPE)
+
+  # Add SPIRV-Headers as a sub-project if it isn't already defined.
+  # Do this so enclosing projects can use SPIRV-Headers_SOURCE_DIR to find
+  # headers to include.
+  if (NOT DEFINED SPIRV-Headers_SOURCE_DIR)
+    set(SPIRV_HEADERS_SKIP_INSTALL ON)
+    set(SPIRV_HEADERS_SKIP_EXAMPLES ON)
+    add_subdirectory(${SPIRV_HEADER_DIR})
+  endif()
 else()
   message(FATAL_ERROR
     "SPIRV-Headers was not found - please checkout a copy under external/.")
@@ -78,7 +88,7 @@
     set(RE2_BUILD_TESTING OFF CACHE STRING "Run RE2 Tests")
     if (NOT RE2_SOURCE_DIR)
       if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/re2)
-	set(RE2_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/re2" CACHE STRING "RE2 source dir" )
+        set(RE2_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/re2" CACHE STRING "RE2 source dir" )
       endif()
     endif()
   endif()
@@ -88,13 +98,17 @@
     if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/effcee)
       # If we're configuring RE2 (via Effcee), then turn off RE2 testing.
       if (NOT TARGET re2)
-	set(RE2_BUILD_TESTING OFF)
+        set(RE2_BUILD_TESTING OFF)
       endif()
       if (MSVC)
-	# SPIRV-Tools uses the shared CRT with MSVC.  Tell Effcee to do the same.
-	set(EFFCEE_ENABLE_SHARED_CRT ON)
+        # SPIRV-Tools uses the shared CRT with MSVC.  Tell Effcee to do the same.
+        set(EFFCEE_ENABLE_SHARED_CRT ON)
       endif()
-      add_subdirectory(effcee)
+      set(EFFCEE_BUILD_SAMPLES OFF CACHE BOOL "Do not build Effcee examples")
+      if (NOT TARGET effcee)
+        set(EFFCEE_BUILD_TESTING OFF CACHE BOOL "Do not build Effcee test suite")
+      endif()
+      add_subdirectory(effcee EXCLUDE_FROM_ALL)
       set_property(TARGET effcee PROPERTY FOLDER Effcee)
       # Turn off warnings for effcee and re2
       set_property(TARGET effcee APPEND PROPERTY COMPILE_OPTIONS -w)
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp b/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp
index d3180e4..ef5136a 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp
+++ b/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp
@@ -35,10 +35,6 @@
 // generated by InstrumentPass::GenDebugStreamWrite. This method is utilized
 // by InstBindlessCheckPass.
 //
-// kInst2* values support version 2 of the output record format and were used
-// for the transition to this format. These values have now been transferred
-// to the original kInst* values. The kInst2* values are therefore DEPRECATED.
-//
 // The first member of the debug output buffer contains the next available word
 // in the data stream to be written. Shaders will atomically read and update
 // this value so as not to overwrite each others records. This value must be
@@ -94,10 +90,6 @@
 static const int kInstCompOutGlobalInvocationIdY = kInstCommonOutCnt + 1;
 static const int kInstCompOutGlobalInvocationIdZ = kInstCommonOutCnt + 2;
 
-// Compute Shader Output Record Offsets - Version 1 (DEPRECATED)
-static const int kInstCompOutGlobalInvocationId = kInstCommonOutCnt;
-static const int kInstCompOutUnused = kInstCommonOutCnt + 1;
-
 // Tessellation Control Shader Output Record Offsets
 static const int kInstTessCtlOutInvocationId = kInstCommonOutCnt;
 static const int kInstTessCtlOutPrimitiveId = kInstCommonOutCnt + 1;
@@ -108,10 +100,6 @@
 static const int kInstTessEvalOutTessCoordU = kInstCommonOutCnt + 1;
 static const int kInstTessEvalOutTessCoordV = kInstCommonOutCnt + 2;
 
-// Tessellation Shader Output Record Offsets - Version 1 (DEPRECATED)
-static const int kInstTessOutInvocationId = kInstCommonOutCnt;
-static const int kInstTessOutUnused = kInstCommonOutCnt + 1;
-
 // Geometry Shader Output Record Offsets
 static const int kInstGeomOutPrimitiveId = kInstCommonOutCnt;
 static const int kInstGeomOutInvocationId = kInstCommonOutCnt + 1;
@@ -124,14 +112,12 @@
 
 // Size of Common and Stage-specific Members
 static const int kInstStageOutCnt = kInstCommonOutCnt + 3;
-static const int kInst2StageOutCnt = kInstCommonOutCnt + 3;
 
 // Validation Error Code Offset
 //
 // This identifies the validation error. It also helps to identify
 // how many words follow in the record and their meaning.
 static const int kInstValidationOutError = kInstStageOutCnt;
-static const int kInst2ValidationOutError = kInst2StageOutCnt;
 
 // Validation-specific Output Record Offsets
 //
@@ -144,37 +130,19 @@
 static const int kInstBindlessBoundsOutDescBound = kInstStageOutCnt + 2;
 static const int kInstBindlessBoundsOutCnt = kInstStageOutCnt + 3;
 
-static const int kInst2BindlessBoundsOutDescIndex = kInst2StageOutCnt + 1;
-static const int kInst2BindlessBoundsOutDescBound = kInst2StageOutCnt + 2;
-static const int kInst2BindlessBoundsOutCnt = kInst2StageOutCnt + 3;
-
 // A bindless uninitialized error will output the index.
 static const int kInstBindlessUninitOutDescIndex = kInstStageOutCnt + 1;
 static const int kInstBindlessUninitOutUnused = kInstStageOutCnt + 2;
 static const int kInstBindlessUninitOutCnt = kInstStageOutCnt + 3;
 
-static const int kInst2BindlessUninitOutDescIndex = kInst2StageOutCnt + 1;
-static const int kInst2BindlessUninitOutUnused = kInst2StageOutCnt + 2;
-static const int kInst2BindlessUninitOutCnt = kInst2StageOutCnt + 3;
-
 // A buffer address unalloc error will output the 64-bit pointer in
 // two 32-bit pieces, lower bits first.
 static const int kInstBuffAddrUnallocOutDescPtrLo = kInstStageOutCnt + 1;
 static const int kInstBuffAddrUnallocOutDescPtrHi = kInstStageOutCnt + 2;
 static const int kInstBuffAddrUnallocOutCnt = kInstStageOutCnt + 3;
 
-static const int kInst2BuffAddrUnallocOutDescPtrLo = kInst2StageOutCnt + 1;
-static const int kInst2BuffAddrUnallocOutDescPtrHi = kInst2StageOutCnt + 2;
-static const int kInst2BuffAddrUnallocOutCnt = kInst2StageOutCnt + 3;
-
-// DEPRECATED
-static const int kInstBindlessOutDescIndex = kInstStageOutCnt + 1;
-static const int kInstBindlessOutDescBound = kInstStageOutCnt + 2;
-static const int kInstBindlessOutCnt = kInstStageOutCnt + 3;
-
 // Maximum Output Record Member Count
 static const int kInstMaxOutCnt = kInstStageOutCnt + 3;
-static const int kInst2MaxOutCnt = kInst2StageOutCnt + 3;
 
 // Validation Error Codes
 //
@@ -223,9 +191,6 @@
 // Data[ i + Data[ b + Data[ s + Data[ kDebugInputBindlessInitOffset ] ] ] ]
 static const int kDebugInputBindlessInitOffset = 0;
 
-// DEPRECATED
-static const int kDebugInputBindlessOffsetReserved = 0;
-
 // At offset kDebugInputBindlessOffsetLengths is some number of uints which
 // provide the bindless length data. More specifically, the number of
 // descriptors at (set=s, binding=b) is:
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp b/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
index b904923..d393495 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
+++ b/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
@@ -762,10 +762,9 @@
 // |input_length_enable| controls instrumentation of runtime descriptor array
 // references, and |input_init_enable| controls instrumentation of descriptor
 // initialization checking, both of which require input buffer support.
-// |version| specifies the buffer record format.
 Optimizer::PassToken CreateInstBindlessCheckPass(
     uint32_t desc_set, uint32_t shader_id, bool input_length_enable = false,
-    bool input_init_enable = false, uint32_t version = 2);
+    bool input_init_enable = false);
 
 // Create a pass to instrument physical buffer address checking
 // This pass instruments all physical buffer address references to check that
@@ -786,10 +785,8 @@
 // The instrumentation will read and write buffers in debug
 // descriptor set |desc_set|. It will write |shader_id| in each output record
 // to identify the shader module which generated the record.
-// |version| specifies the output buffer record format.
 Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
-                                                 uint32_t shader_id,
-                                                 uint32_t version = 2);
+                                                 uint32_t shader_id);
 
 // Create a pass to instrument OpDebugPrintf instructions.
 // This pass replaces all OpDebugPrintf instructions with instructions to write
diff --git a/third_party/SPIRV-Tools/kokoro/scripts/linux/build.sh b/third_party/SPIRV-Tools/kokoro/scripts/linux/build.sh
index f7b0fe1..8fb7bdd 100644
--- a/third_party/SPIRV-Tools/kokoro/scripts/linux/build.sh
+++ b/third_party/SPIRV-Tools/kokoro/scripts/linux/build.sh
@@ -46,7 +46,7 @@
 ADDITIONAL_CMAKE_FLAGS=""
 if [ $CONFIG = "ASAN" ]
 then
-  ADDITIONAL_CMAKE_FLAGS="SPIRV_USE_SANITIZER=address"
+  ADDITIONAL_CMAKE_FLAGS="SPIRV_USE_SANITIZER=address,bounds,null"
   [ $COMPILER = "clang" ] || { echo "$CONFIG requires clang"; exit 1; }
 elif [ $CONFIG = "COVERAGE" ]
 then
diff --git a/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt b/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
index 3a9d604..6582927 100644
--- a/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
@@ -49,7 +49,7 @@
         fuzzer_pass_add_local_variables.h
         fuzzer_pass_add_no_contraction_decorations.h
         fuzzer_pass_add_stores.h
-        fuzzer_pass_add_useful_constructs.h
+        fuzzer_pass_adjust_branch_weights.h
         fuzzer_pass_adjust_function_controls.h
         fuzzer_pass_adjust_loop_controls.h
         fuzzer_pass_adjust_memory_operands_masks.h
@@ -79,6 +79,7 @@
         transformation_access_chain.h
         transformation_add_constant_boolean.h
         transformation_add_constant_composite.h
+        transformation_add_constant_null.h
         transformation_add_constant_scalar.h
         transformation_add_dead_block.h
         transformation_add_dead_break.h
@@ -97,8 +98,11 @@
         transformation_add_type_pointer.h
         transformation_add_type_struct.h
         transformation_add_type_vector.h
+        transformation_adjust_branch_weights.h
         transformation_composite_construct.h
         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
@@ -141,7 +145,7 @@
         fuzzer_pass_add_local_variables.cpp
         fuzzer_pass_add_no_contraction_decorations.cpp
         fuzzer_pass_add_stores.cpp
-        fuzzer_pass_add_useful_constructs.cpp
+        fuzzer_pass_adjust_branch_weights.cpp
         fuzzer_pass_adjust_function_controls.cpp
         fuzzer_pass_adjust_loop_controls.cpp
         fuzzer_pass_adjust_memory_operands_masks.cpp
@@ -170,6 +174,7 @@
         transformation_access_chain.cpp
         transformation_add_constant_boolean.cpp
         transformation_add_constant_composite.cpp
+        transformation_add_constant_null.cpp
         transformation_add_constant_scalar.cpp
         transformation_add_dead_block.cpp
         transformation_add_dead_break.cpp
@@ -188,8 +193,11 @@
         transformation_add_type_pointer.cpp
         transformation_add_type_struct.cpp
         transformation_add_type_vector.cpp
+        transformation_adjust_branch_weights.cpp
         transformation_composite_construct.cpp
         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
diff --git a/third_party/SPIRV-Tools/source/fuzz/equivalence_relation.h b/third_party/SPIRV-Tools/source/fuzz/equivalence_relation.h
index 7bb8b66..6d0b63e 100644
--- a/third_party/SPIRV-Tools/source/fuzz/equivalence_relation.h
+++ b/third_party/SPIRV-Tools/source/fuzz/equivalence_relation.h
@@ -68,17 +68,14 @@
 template <typename T, typename PointerHashT, typename PointerEqualsT>
 class EquivalenceRelation {
  public:
-  // Merges the equivalence classes associated with |value1| and |value2|.
-  // If any of these values was not previously in the equivalence relation, it
-  // is added to the pool of values known to be in the relation.
+  // Requires that |value1| and |value2| are already registered in the
+  // equivalence relation.  Merges the equivalence classes associated with
+  // |value1| and |value2|.
   void MakeEquivalent(const T& value1, const T& value2) {
-    // Register each value if necessary.
-    for (auto value : {value1, value2}) {
-      if (!Exists(value)) {
-        // Register the value in the equivalence relation.
-        Register(value);
-      }
-    }
+    assert(Exists(value1) &&
+           "Precondition: value1 must already be registered.");
+    assert(Exists(value2) &&
+           "Precondition: value2 must already be registered.");
 
     // Look up canonical pointers to each of the values in the value pool.
     const T* value1_ptr = *value_set_.find(&value1);
@@ -105,7 +102,7 @@
   // Requires that |value| is not known to the equivalence relation. Registers
   // it in its own equivalence class and returns a pointer to the equivalence
   // class representative.
-  const T* Register(T& value) {
+  const T* Register(const T& value) {
     assert(!Exists(value));
 
     // This relies on T having a copy constructor.
diff --git a/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp b/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
index 31d3b94..0b41eeb 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
@@ -159,9 +159,26 @@
     uint32_t type_id) const {
   auto type = context->get_type_mgr()->GetType(type_id);
   assert(type != nullptr && "Unknown type id.");
-  auto constant = context->get_constant_mgr()->GetConstant(
-      type, GetConstantWords(constant_uniform_fact));
-  return context->get_constant_mgr()->FindDeclaredConstant(constant, type_id);
+  const opt::analysis::Constant* known_constant;
+  if (type->AsInteger()) {
+    opt::analysis::IntConstant candidate_constant(
+        type->AsInteger(), GetConstantWords(constant_uniform_fact));
+    known_constant =
+        context->get_constant_mgr()->FindConstant(&candidate_constant);
+  } else {
+    assert(
+        type->AsFloat() &&
+        "Uniform constant facts are only supported for int and float types.");
+    opt::analysis::FloatConstant candidate_constant(
+        type->AsFloat(), GetConstantWords(constant_uniform_fact));
+    known_constant =
+        context->get_constant_mgr()->FindConstant(&candidate_constant);
+  }
+  if (!known_constant) {
+    return 0;
+  }
+  return context->get_constant_mgr()->FindDeclaredConstant(known_constant,
+                                                           type_id);
 }
 
 std::vector<uint32_t> FactManager::ConstantUniformFacts::GetConstantWords(
@@ -416,19 +433,23 @@
 
   // See method in FactManager which delegates to this method.
   std::vector<const protobufs::DataDescriptor*> GetSynonymsForDataDescriptor(
-      const protobufs::DataDescriptor& data_descriptor,
-      opt::IRContext* context) const;
+      const protobufs::DataDescriptor& data_descriptor) const;
 
   // See method in FactManager which delegates to this method.
-  std::vector<uint32_t> GetIdsForWhichSynonymsAreKnown(
-      opt::IRContext* context) const;
+  std::vector<uint32_t> GetIdsForWhichSynonymsAreKnown() const;
 
   // See method in FactManager which delegates to this method.
   bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1,
-                    const protobufs::DataDescriptor& data_descriptor2,
-                    opt::IRContext* context) const;
+                    const protobufs::DataDescriptor& data_descriptor2) const;
+
+  // See method in FactManager which delegates to this method.
+  void ComputeClosureOfFacts(opt::IRContext* context,
+                             uint32_t maximum_equivalence_class_size);
 
  private:
+  using OperationSet =
+      std::unordered_set<Operation, OperationHash, OperationEquals>;
+
   // Adds the synonym |dd1| = |dd2| to the set of managed facts, and recurses
   // into sub-components of the data descriptors, if they are composites, to
   // record that their components are pairwise-synonymous.
@@ -436,14 +457,10 @@
                                    const protobufs::DataDescriptor& dd2,
                                    opt::IRContext* context);
 
-  // Inspects all known facts and adds corollary facts; e.g. if we know that
-  // a.x == b.x and a.y == b.y, where a and b have vec2 type, we can record
-  // that a == b holds.
-  //
-  // This method is expensive, and is thus called on demand: rather than
-  // computing the closure of facts each time a data synonym fact is added, we
-  // compute the closure only when a data synonym fact is *queried*.
-  void ComputeClosureOfFacts(opt::IRContext* context) const;
+  // 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,
+                      const protobufs::DataDescriptor& dd2);
 
   // Returns true if and only if |dd1| and |dd2| are valid data descriptors
   // whose associated data have the same type (modulo integer signedness).
@@ -451,11 +468,14 @@
       opt::IRContext* context, const protobufs::DataDescriptor& dd1,
       const protobufs::DataDescriptor& dd2) const;
 
+  OperationSet GetEquations(const protobufs::DataDescriptor* lhs) const;
+
   // Requires that |lhs_dd| and every element of |rhs_dds| is present in the
-  // |synonymous_| equivalence relation and is its own representative.  Records
-  // the fact that the equation "|lhs_dd| |opcode| |rhs_dds|" holds, and adds
-  // any corollaries, in the form of data synonym or equation facts, that
-  // follow from this and other known facts.
+  // |synonymous_| equivalence relation, but is not necessarily its own
+  // representative.  Records the fact that the equation
+  // "|lhs_dd| |opcode| |rhs_dds_non_canonical|" holds, and adds any
+  // corollaries, in the form of data synonym or equation facts, that follow
+  // from this and other known facts.
   void AddEquationFactRecursive(
       const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
       const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
@@ -463,28 +483,17 @@
 
   // The data descriptors that are known to be synonymous with one another are
   // captured by this equivalence relation.
-  //
-  // This member is mutable in order to allow the closure of facts captured by
-  // the relation to be computed lazily when a question about data synonym
-  // facts is asked.
-  mutable EquivalenceRelation<protobufs::DataDescriptor, DataDescriptorHash,
-                              DataDescriptorEquals>
+  EquivalenceRelation<protobufs::DataDescriptor, DataDescriptorHash,
+                      DataDescriptorEquals>
       synonymous_;
 
   // When a new synonym fact is added, it may be possible to deduce further
-  // synonym facts by computing a closure of all known facts.  However, there is
-  // no point computing this closure until a question regarding synonym facts is
-  // actually asked: if several facts are added in succession with no questions
-  // asked in between, we can avoid computing fact closures multiple times.
-  //
-  // This boolean tracks whether a closure computation is required - i.e.,
-  // whether a new fact has been added since the last time such a computation
-  // was performed.
-  //
-  // It is mutable to facilitate having const methods, that provide answers to
-  // questions about data synonym facts, triggering closure computation on
-  // demand.
-  mutable bool closure_computation_required_ = false;
+  // synonym facts by computing a closure of all known facts.  However, this is
+  // an expensive operation, so it should be performed sparingly and only there
+  // is some chance of new facts being deduced.  This boolean tracks whether a
+  // closure computation is required - i.e., whether a new fact has been added
+  // since the last time such a computation was performed.
+  bool closure_computation_required_ = false;
 
   // Represents a set of equations on data descriptors as a map indexed by
   // left-hand-side, mapping a left-hand-side to a set of operations, each of
@@ -493,9 +502,7 @@
   // All data descriptors occurring in equations are required to be present in
   // the |synonymous_| equivalence relation, and to be their own representatives
   // in that relation.
-  std::unordered_map<
-      const protobufs::DataDescriptor*,
-      std::unordered_set<Operation, OperationHash, OperationEquals>>
+  std::unordered_map<const protobufs::DataDescriptor*, OperationSet>
       id_equations_;
 };
 
@@ -510,12 +517,10 @@
     const protobufs::FactIdEquation& fact, opt::IRContext* context) {
   protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {});
 
-  // Register the LHS in the equivalence relation if needed, and get a pointer
-  // to its representative.
+  // Register the LHS in the equivalence relation if needed.
   if (!synonymous_.Exists(lhs_dd)) {
     synonymous_.Register(lhs_dd);
   }
-  const protobufs::DataDescriptor* lhs_dd_ptr = synonymous_.Find(&lhs_dd);
 
   // Get equivalence class representatives for all ids used on the RHS of the
   // equation.
@@ -529,38 +534,45 @@
     }
     rhs_dd_ptrs.push_back(synonymous_.Find(&rhs_dd));
   }
-  // We now have the equation in a form where it refers exclusively to
-  // equivalence class representatives.  Add it to our set of facts and work
-  // out any follow-on facts.
-  AddEquationFactRecursive(*lhs_dd_ptr, static_cast<SpvOp>(fact.opcode()),
+
+  // Now add the fact.
+  AddEquationFactRecursive(lhs_dd, static_cast<SpvOp>(fact.opcode()),
                            rhs_dd_ptrs, context);
 }
 
+FactManager::DataSynonymAndIdEquationFacts::OperationSet
+FactManager::DataSynonymAndIdEquationFacts::GetEquations(
+    const protobufs::DataDescriptor* lhs) const {
+  auto existing = id_equations_.find(lhs);
+  if (existing == id_equations_.end()) {
+    return OperationSet();
+  }
+  return existing->second;
+}
+
 void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
     const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
     const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
     opt::IRContext* context) {
-  // Precondition: all data descriptors referenced in this equation must be
-  // equivalence class representatives - i.e. the equation must be in canonical
-  // form.
-  assert(synonymous_.Exists(lhs_dd));
-  assert(synonymous_.Find(&lhs_dd) == &lhs_dd);
+  assert(synonymous_.Exists(lhs_dd) &&
+         "The LHS must be known to the equivalence relation.");
   for (auto rhs_dd : rhs_dds) {
-    (void)(rhs_dd);  // Keep compilers happy in release mode.
-    assert(synonymous_.Exists(*rhs_dd));
-    assert(synonymous_.Find(rhs_dd) == rhs_dd);
+    // Keep release compilers happy.
+    (void)(rhs_dd);
+    assert(synonymous_.Exists(*rhs_dd) &&
+           "The RHS operands must be known to the equivalence relation.");
   }
 
-  if (id_equations_.count(&lhs_dd) == 0) {
+  auto lhs_dd_representative = synonymous_.Find(&lhs_dd);
+
+  if (id_equations_.count(lhs_dd_representative) == 0) {
     // We have not seen an equation with this LHS before, so associate the LHS
     // with an initially empty set.
-    id_equations_.insert(
-        {&lhs_dd,
-         std::unordered_set<Operation, OperationHash, OperationEquals>()});
+    id_equations_.insert({lhs_dd_representative, OperationSet()});
   }
 
   {
-    auto existing_equations = id_equations_.find(&lhs_dd);
+    auto existing_equations = id_equations_.find(lhs_dd_representative);
     assert(existing_equations != id_equations_.end() &&
            "A set of operations should be present, even if empty.");
 
@@ -578,41 +590,29 @@
   switch (opcode) {
     case SpvOpIAdd: {
       // Equation form: "a = b + c"
-      {
-        auto existing_first_operand_equations = id_equations_.find(rhs_dds[0]);
-        if (existing_first_operand_equations != id_equations_.end()) {
-          for (auto equation : existing_first_operand_equations->second) {
-            if (equation.opcode == SpvOpISub) {
-              // Equation form: "a = (d - e) + c"
-              if (equation.operands[1] == rhs_dds[1]) {
-                // Equation form: "a = (d - c) + c"
-                // We can thus infer "a = d"
-                AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
-                                            context);
-              }
-              if (equation.operands[0] == rhs_dds[1]) {
-                // Equation form: "a = (c - e) + c"
-                // We can thus infer "a = -e"
-                AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
-                                         {equation.operands[1]}, context);
-              }
-            }
+      for (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])) {
+            // Equation form: "a = (d - c) + c"
+            // We can thus infer "a = d"
+            AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
+          }
+          if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
+            // Equation form: "a = (c - e) + c"
+            // We can thus infer "a = -e"
+            AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+                                     {equation.operands[1]}, context);
           }
         }
       }
-      {
-        auto existing_second_operand_equations = id_equations_.find(rhs_dds[1]);
-        if (existing_second_operand_equations != id_equations_.end()) {
-          for (auto equation : existing_second_operand_equations->second) {
-            if (equation.opcode == SpvOpISub) {
-              // Equation form: "a = b + (d - e)"
-              if (equation.operands[1] == rhs_dds[0]) {
-                // Equation form: "a = b + (d - b)"
-                // We can thus infer "a = d"
-                AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
-                                            context);
-              }
-            }
+      for (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])) {
+            // Equation form: "a = b + (d - b)"
+            // We can thus infer "a = d"
+            AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
           }
         }
       }
@@ -620,67 +620,54 @@
     }
     case SpvOpISub: {
       // Equation form: "a = b - c"
-      {
-        auto existing_first_operand_equations = id_equations_.find(rhs_dds[0]);
-        if (existing_first_operand_equations != id_equations_.end()) {
-          for (auto equation : existing_first_operand_equations->second) {
-            if (equation.opcode == SpvOpIAdd) {
-              // Equation form: "a = (d + e) - c"
-              if (equation.operands[0] == rhs_dds[1]) {
-                // Equation form: "a = (c + e) - c"
-                // We can thus infer "a = e"
-                AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1],
-                                            context);
-              }
-              if (equation.operands[1] == rhs_dds[1]) {
-                // Equation form: "a = (d + c) - c"
-                // We can thus infer "a = d"
-                AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
-                                            context);
-              }
-            }
+      for (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])) {
+            // Equation form: "a = (c + e) - c"
+            // We can thus infer "a = e"
+            AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1], context);
+          }
+          if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) {
+            // Equation form: "a = (d + c) - c"
+            // We can thus infer "a = d"
+            AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
+          }
+        }
 
-            if (equation.opcode == SpvOpISub) {
-              // Equation form: "a = (d - e) - c"
-              if (equation.operands[0] == rhs_dds[1]) {
-                // Equation form: "a = (c - e) - c"
-                // We can thus infer "a = -e"
-                AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
-                                         {equation.operands[1]}, context);
-              }
-            }
+        if (equation.opcode == SpvOpISub) {
+          // Equation form: "a = (d - e) - c"
+          if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
+            // Equation form: "a = (c - e) - c"
+            // We can thus infer "a = -e"
+            AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+                                     {equation.operands[1]}, context);
           }
         }
       }
 
-      {
-        auto existing_second_operand_equations = id_equations_.find(rhs_dds[1]);
-        if (existing_second_operand_equations != id_equations_.end()) {
-          for (auto equation : existing_second_operand_equations->second) {
-            if (equation.opcode == SpvOpIAdd) {
-              // Equation form: "a = b - (d + e)"
-              if (equation.operands[0] == rhs_dds[0]) {
-                // Equation form: "a = b - (b + e)"
-                // We can thus infer "a = -e"
-                AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
-                                         {equation.operands[1]}, context);
-              }
-              if (equation.operands[1] == rhs_dds[0]) {
-                // Equation form: "a = b - (d + b)"
-                // We can thus infer "a = -d"
-                AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
-                                         {equation.operands[0]}, context);
-              }
-            }
-            if (equation.opcode == SpvOpISub) {
-              // Equation form: "a = b - (d - e)"
-              if (equation.operands[0] == rhs_dds[0]) {
-                // Equation form: "a = b - (b - e)"
-                // We can thus infer "a = e"
-                AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1],
-                                            context);
-              }
-            }
+      for (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])) {
+            // Equation form: "a = b - (b + e)"
+            // We can thus infer "a = -e"
+            AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+                                     {equation.operands[1]}, context);
+          }
+          if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) {
+            // Equation form: "a = b - (d + b)"
+            // We can thus infer "a = -d"
+            AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+                                     {equation.operands[0]}, context);
+          }
+        }
+        if (equation.opcode == SpvOpISub) {
+          // Equation form: "a = b - (d - e)"
+          if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) {
+            // Equation form: "a = b - (b - e)"
+            // We can thus infer "a = e"
+            AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1], context);
           }
         }
       }
@@ -689,14 +676,11 @@
     case SpvOpLogicalNot:
     case SpvOpSNegate: {
       // Equation form: "a = !b" or "a = -b"
-      auto existing_equations = id_equations_.find(rhs_dds[0]);
-      if (existing_equations != id_equations_.end()) {
-        for (auto equation : existing_equations->second) {
-          if (equation.opcode == opcode) {
-            // Equation form: "a = !!b" or "a = -(-b)"
-            // We can thus infer "a = b"
-            AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
-          }
+      for (auto equation : GetEquations(rhs_dds[0])) {
+        if (equation.opcode == opcode) {
+          // Equation form: "a = !!b" or "a = -(-b)"
+          // We can thus infer "a = b"
+          AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
         }
       }
       break;
@@ -712,12 +696,7 @@
   assert(DataDescriptorsAreWellFormedAndComparable(context, dd1, dd2));
 
   // Record that the data descriptors provided in the fact are equivalent.
-  synonymous_.MakeEquivalent(dd1, dd2);
-  // As we have updated the equivalence relation, we might be able to deduce
-  // more facts by performing a closure computation, so we record that such a
-  // computation is required; it will be performed next time a method answering
-  // a data synonym fact-related question is invoked.
-  closure_computation_required_ = true;
+  MakeEquivalent(dd1, dd2);
 
   // We now check whether this is a synonym about composite objects.  If it is,
   // we can recursively add synonym facts about their associated sub-components.
@@ -754,7 +733,17 @@
   //   obj_1[a_1, ..., a_m] == obj_2[b_1, ..., b_n]
   // then for each composite index i, we add a fact of the form:
   //   obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i]
-  for (uint32_t i = 0; i < num_composite_elements; i++) {
+  //
+  // However, to avoid adding a large number of synonym facts e.g. in the case
+  // of arrays, we bound the number of composite elements to which this is
+  // applied.  Nevertheless, we always add a synonym fact for the final
+  // components, as this may be an interesting edge case.
+
+  // The bound on the number of indices of the composite pair to note as being
+  // synonymous.
+  const uint32_t kCompositeElementBound = 10;
+
+  for (uint32_t i = 0; i < num_composite_elements;) {
     std::vector<uint32_t> extended_indices1 =
         fuzzerutil::RepeatedFieldToVector(dd1.index());
     extended_indices1.push_back(i);
@@ -765,11 +754,21 @@
         MakeDataDescriptor(dd1.object(), std::move(extended_indices1)),
         MakeDataDescriptor(dd2.object(), std::move(extended_indices2)),
         context);
+
+    if (i < kCompositeElementBound - 1 || i == num_composite_elements - 1) {
+      // We have not reached the bound yet, or have already skipped ahead to the
+      // last element, so increment the loop counter as standard.
+      i++;
+    } else {
+      // We have reached the bound, so skip ahead to the last element.
+      assert(i == kCompositeElementBound - 1);
+      i = num_composite_elements - 1;
+    }
   }
 }
 
 void FactManager::DataSynonymAndIdEquationFacts::ComputeClosureOfFacts(
-    opt::IRContext* context) const {
+    opt::IRContext* context, uint32_t maximum_equivalence_class_size) {
   // Suppose that obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n] are distinct
   // data descriptors that describe objects of the same composite type, and that
   // the composite type is comprised of k components.
@@ -855,6 +854,13 @@
          synonymous_.GetEquivalenceClassRepresentatives()) {
       auto equivalence_class = synonymous_.GetEquivalenceClass(*representative);
 
+      if (equivalence_class.size() > maximum_equivalence_class_size) {
+        // This equivalence class is larger than the maximum size we are willing
+        // to consider, so we skip it.  This potentially leads to missed fact
+        // deductions, but avoids excessive runtime for closure computation.
+        continue;
+      }
+
       // Consider every data descriptor in the equivalence class.
       for (auto dd1_it = equivalence_class.begin();
            dd1_it != equivalence_class.end(); ++dd1_it) {
@@ -1029,10 +1035,7 @@
             // synonymous.
             assert(DataDescriptorsAreWellFormedAndComparable(
                 context, dd1_prefix, dd2_prefix));
-            synonymous_.MakeEquivalent(dd1_prefix, dd2_prefix);
-            // As we have added a new synonym fact, we might benefit from doing
-            // another pass over the equivalence relation.
-            closure_computation_required_ = true;
+            MakeEquivalent(dd1_prefix, dd2_prefix);
             // Now that we know this pair of data descriptors are synonymous,
             // there is no point recording how close they are to being
             // synonymous.
@@ -1044,6 +1047,82 @@
   }
 }
 
+void FactManager::DataSynonymAndIdEquationFacts::MakeEquivalent(
+    const protobufs::DataDescriptor& dd1,
+    const protobufs::DataDescriptor& dd2) {
+  // Register the data descriptors if they are not already known to the
+  // equivalence relation.
+  for (const auto& dd : {dd1, dd2}) {
+    if (!synonymous_.Exists(dd)) {
+      synonymous_.Register(dd);
+    }
+  }
+
+  if (synonymous_.IsEquivalent(dd1, dd2)) {
+    // The data descriptors are already known to be equivalent, so there is
+    // nothing to do.
+    return;
+  }
+
+  // We must make the data descriptors equivalent, and also make sure any
+  // equation facts known about their representatives are merged.
+
+  // Record the original equivalence class representatives of the data
+  // descriptors.
+  auto dd1_original_representative = synonymous_.Find(&dd1);
+  auto dd2_original_representative = synonymous_.Find(&dd2);
+
+  // Make the data descriptors equivalent.
+  synonymous_.MakeEquivalent(dd1, dd2);
+  // As we have updated the equivalence relation, we might be able to deduce
+  // more facts by performing a closure computation, so we record that such a
+  // computation is required.
+  closure_computation_required_ = true;
+
+  // At this point, exactly one of |dd1_original_representative| and
+  // |dd2_original_representative| will be the representative of the combined
+  // equivalence class.  We work out which one of them is still the class
+  // representative and which one is no longer the class representative.
+
+  auto still_representative = synonymous_.Find(dd1_original_representative) ==
+                                      dd1_original_representative
+                                  ? dd1_original_representative
+                                  : dd2_original_representative;
+  auto no_longer_representative =
+      still_representative == dd1_original_representative
+          ? dd2_original_representative
+          : dd1_original_representative;
+
+  assert(no_longer_representative != still_representative &&
+         "The current and former representatives cannot be the same.");
+
+  // We now need to add all equations about |no_longer_representative| to the
+  // set of equations known about |still_representative|.
+
+  // Get the equations associated with |no_longer_representative|.
+  auto no_longer_representative_id_equations =
+      id_equations_.find(no_longer_representative);
+  if (no_longer_representative_id_equations != id_equations_.end()) {
+    // There are some equations to transfer.  There might not yet be any
+    // equations about |still_representative|; create an empty set of equations
+    // if this is the case.
+    if (!id_equations_.count(still_representative)) {
+      id_equations_.insert({still_representative, OperationSet()});
+    }
+    auto still_representative_id_equations =
+        id_equations_.find(still_representative);
+    assert(still_representative_id_equations != id_equations_.end() &&
+           "At this point there must be a set of equations.");
+    // Add all the equations known about |no_longer_representative| to the set
+    // of equations known about |still_representative|.
+    still_representative_id_equations->second.insert(
+        no_longer_representative_id_equations->second.begin(),
+        no_longer_representative_id_equations->second.end());
+  }
+  // Delete the no longer-relevant equations about |no_longer_representative|.
+  id_equations_.erase(no_longer_representative);
+}
+
 bool FactManager::DataSynonymAndIdEquationFacts::
     DataDescriptorsAreWellFormedAndComparable(
         opt::IRContext* context, const protobufs::DataDescriptor& dd1,
@@ -1094,9 +1173,7 @@
 
 std::vector<const protobufs::DataDescriptor*>
 FactManager::DataSynonymAndIdEquationFacts::GetSynonymsForDataDescriptor(
-    const protobufs::DataDescriptor& data_descriptor,
-    opt::IRContext* context) const {
-  ComputeClosureOfFacts(context);
+    const protobufs::DataDescriptor& data_descriptor) const {
   if (synonymous_.Exists(data_descriptor)) {
     return synonymous_.GetEquivalenceClass(data_descriptor);
   }
@@ -1104,9 +1181,8 @@
 }
 
 std::vector<uint32_t>
-FactManager::DataSynonymAndIdEquationFacts ::GetIdsForWhichSynonymsAreKnown(
-    opt::IRContext* context) const {
-  ComputeClosureOfFacts(context);
+FactManager::DataSynonymAndIdEquationFacts::GetIdsForWhichSynonymsAreKnown()
+    const {
   std::vector<uint32_t> result;
   for (auto& data_descriptor : synonymous_.GetAllKnownValues()) {
     if (data_descriptor->index().empty()) {
@@ -1118,10 +1194,7 @@
 
 bool FactManager::DataSynonymAndIdEquationFacts::IsSynonymous(
     const protobufs::DataDescriptor& data_descriptor1,
-    const protobufs::DataDescriptor& data_descriptor2,
-    opt::IRContext* context) const {
-  const_cast<FactManager::DataSynonymAndIdEquationFacts*>(this)
-      ->ComputeClosureOfFacts(context);
+    const protobufs::DataDescriptor& data_descriptor2) const {
   return synonymous_.Exists(data_descriptor1) &&
          synonymous_.Exists(data_descriptor2) &&
          synonymous_.IsEquivalent(data_descriptor1, data_descriptor2);
@@ -1303,31 +1376,27 @@
   return uniform_constant_facts_->GetConstantUniformFactsAndTypes();
 }
 
-std::vector<uint32_t> FactManager::GetIdsForWhichSynonymsAreKnown(
-    opt::IRContext* context) const {
-  return data_synonym_and_id_equation_facts_->GetIdsForWhichSynonymsAreKnown(
-      context);
+std::vector<uint32_t> FactManager::GetIdsForWhichSynonymsAreKnown() const {
+  return data_synonym_and_id_equation_facts_->GetIdsForWhichSynonymsAreKnown();
 }
 
 std::vector<const protobufs::DataDescriptor*>
 FactManager::GetSynonymsForDataDescriptor(
-    const protobufs::DataDescriptor& data_descriptor,
-    opt::IRContext* context) const {
+    const protobufs::DataDescriptor& data_descriptor) const {
   return data_synonym_and_id_equation_facts_->GetSynonymsForDataDescriptor(
-      data_descriptor, context);
+      data_descriptor);
 }
 
 std::vector<const protobufs::DataDescriptor*> FactManager::GetSynonymsForId(
-    uint32_t id, opt::IRContext* context) const {
-  return GetSynonymsForDataDescriptor(MakeDataDescriptor(id, {}), context);
+    uint32_t id) const {
+  return GetSynonymsForDataDescriptor(MakeDataDescriptor(id, {}));
 }
 
 bool FactManager::IsSynonymous(
     const protobufs::DataDescriptor& data_descriptor1,
-    const protobufs::DataDescriptor& data_descriptor2,
-    opt::IRContext* context) const {
-  return data_synonym_and_id_equation_facts_->IsSynonymous(
-      data_descriptor1, data_descriptor2, context);
+    const protobufs::DataDescriptor& data_descriptor2) const {
+  return data_synonym_and_id_equation_facts_->IsSynonymous(data_descriptor1,
+                                                           data_descriptor2);
 }
 
 bool FactManager::BlockIsDead(uint32_t block_id) const {
@@ -1372,5 +1441,11 @@
   data_synonym_and_id_equation_facts_->AddFact(fact, context);
 }
 
+void FactManager::ComputeClosureOfFacts(
+    opt::IRContext* ir_context, uint32_t maximum_equivalence_class_size) {
+  data_synonym_and_id_equation_facts_->ComputeClosureOfFacts(
+      ir_context, maximum_equivalence_class_size);
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fact_manager.h b/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
index f80d677..f520e42 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
@@ -76,6 +76,21 @@
                          const std::vector<uint32_t>& rhs_id,
                          opt::IRContext* context);
 
+  // Inspects all known facts and adds corollary facts; e.g. if we know that
+  // a.x == b.x and a.y == b.y, where a and b have vec2 type, we can record
+  // that a == b holds.
+  //
+  // This method is expensive, and should only be called (by applying a
+  // transformation) at the start of a fuzzer pass that depends on data
+  // synonym facts, rather than calling it every time a new data synonym fact
+  // is added.
+  //
+  // The parameter |maximum_equivalence_class_size| specifies the size beyond
+  // which equivalence classes should not be mined for new facts, to avoid
+  // excessively-long closure computations.
+  void ComputeClosureOfFacts(opt::IRContext* ir_context,
+                             uint32_t maximum_equivalence_class_size);
+
   // The fact manager is responsible for managing a few distinct categories of
   // facts. In principle there could be different fact managers for each kind
   // of fact, but in practice providing one 'go to' place for facts is
@@ -125,25 +140,22 @@
 
   // Returns every id for which a fact of the form "this id is synonymous with
   // this piece of data" is known.
-  std::vector<uint32_t> GetIdsForWhichSynonymsAreKnown(
-      opt::IRContext* context) const;
+  std::vector<uint32_t> GetIdsForWhichSynonymsAreKnown() const;
 
   // Returns the equivalence class of all known synonyms of |id|, or an empty
   // set if no synonyms are known.
   std::vector<const protobufs::DataDescriptor*> GetSynonymsForId(
-      uint32_t id, opt::IRContext* context) const;
+      uint32_t id) const;
 
   // Returns the equivalence class of all known synonyms of |data_descriptor|,
   // or empty if no synonyms are known.
   std::vector<const protobufs::DataDescriptor*> GetSynonymsForDataDescriptor(
-      const protobufs::DataDescriptor& data_descriptor,
-      opt::IRContext* context) const;
+      const protobufs::DataDescriptor& data_descriptor) const;
 
   // Returns true if and ony if |data_descriptor1| and |data_descriptor2| are
   // known to be synonymous.
   bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1,
-                    const protobufs::DataDescriptor& data_descriptor2,
-                    opt::IRContext* context) const;
+                    const protobufs::DataDescriptor& data_descriptor2) const;
 
   // End of id synonym facts
   //==============================
diff --git a/third_party/SPIRV-Tools/source/fuzz/force_render_red.cpp b/third_party/SPIRV-Tools/source/fuzz/force_render_red.cpp
index 46e23e8..5bf2879 100644
--- a/third_party/SPIRV-Tools/source/fuzz/force_render_red.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/force_render_red.cpp
@@ -17,6 +17,7 @@
 #include "source/fuzz/fact_manager.h"
 #include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
 #include "source/fuzz/uniform_buffer_element_descriptor.h"
 #include "source/opt/build_module.h"
@@ -159,7 +160,8 @@
 }  // namespace
 
 bool ForceRenderRed(
-    const spv_target_env& target_env, const std::vector<uint32_t>& binary_in,
+    const spv_target_env& target_env, spv_validator_options validator_options,
+    const std::vector<uint32_t>& binary_in,
     const spvtools::fuzz::protobufs::FactSequence& initial_facts,
     std::vector<uint32_t>* binary_out) {
   auto message_consumer = spvtools::utils::CLIMessageConsumer;
@@ -171,7 +173,7 @@
   }
 
   // Initial binary should be valid.
-  if (!tools.Validate(&binary_in[0], binary_in.size())) {
+  if (!tools.Validate(&binary_in[0], binary_in.size(), validator_options)) {
     message_consumer(SPV_MSG_ERROR, nullptr, {},
                      "Initial binary is invalid; stopping.");
     return false;
@@ -187,6 +189,8 @@
   for (auto& fact : initial_facts.fact()) {
     fact_manager.AddFact(fact, ir_context.get());
   }
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto entry_point_function =
       FindFragmentShaderEntryPoint(ir_context.get(), message_consumer);
@@ -355,8 +359,9 @@
     for (auto& replacement : {first_greater_then_operand_replacement.get(),
                               second_greater_then_operand_replacement.get()}) {
       if (replacement) {
-        assert(replacement->IsApplicable(ir_context.get(), fact_manager));
-        replacement->Apply(ir_context.get(), &fact_manager);
+        assert(replacement->IsApplicable(ir_context.get(),
+                                         transformation_context));
+        replacement->Apply(ir_context.get(), &transformation_context);
       }
     }
   }
diff --git a/third_party/SPIRV-Tools/source/fuzz/force_render_red.h b/third_party/SPIRV-Tools/source/fuzz/force_render_red.h
index 2484d27..b51c72b 100644
--- a/third_party/SPIRV-Tools/source/fuzz/force_render_red.h
+++ b/third_party/SPIRV-Tools/source/fuzz/force_render_red.h
@@ -38,7 +38,8 @@
 // instead become: 'u > v', where 'u' and 'v' are pieces of uniform data for
 // which it is known that 'u < v' holds.
 bool ForceRenderRed(
-    const spv_target_env& target_env, const std::vector<uint32_t>& binary_in,
+    const spv_target_env& target_env, spv_validator_options validator_options,
+    const std::vector<uint32_t>& binary_in,
     const spvtools::fuzz::protobufs::FactSequence& initial_facts,
     std::vector<uint32_t>* binary_out);
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
index 119bd3c..3343abc 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
@@ -33,7 +33,7 @@
 #include "source/fuzz/fuzzer_pass_add_local_variables.h"
 #include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
 #include "source/fuzz/fuzzer_pass_add_stores.h"
-#include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
+#include "source/fuzz/fuzzer_pass_adjust_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_selection_controls.h"
@@ -51,6 +51,7 @@
 #include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/pseudo_random_generator.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/build_module.h"
 #include "source/spirv_fuzzer_options.h"
 #include "source/util/make_unique.h"
@@ -66,19 +67,19 @@
 const uint32_t kChanceOfApplyingAnotherPass = 85;
 
 // A convenience method to add a fuzzer pass to |passes| with probability 0.5.
-// All fuzzer passes take |ir_context|, |fact_manager|, |fuzzer_context| and
-// |transformation_sequence_out| as parameters.  Extra arguments can be provided
-// via |extra_args|.
+// All fuzzer passes take |ir_context|, |transformation_context|,
+// |fuzzer_context| and |transformation_sequence_out| as parameters.  Extra
+// arguments can be provided via |extra_args|.
 template <typename T, typename... Args>
 void MaybeAddPass(
     std::vector<std::unique_ptr<FuzzerPass>>* passes,
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformation_sequence_out,
     Args&&... extra_args) {
   if (fuzzer_context->ChooseEven()) {
-    passes->push_back(MakeUnique<T>(ir_context, fact_manager, fuzzer_context,
-                                    transformation_sequence_out,
+    passes->push_back(MakeUnique<T>(ir_context, transformation_context,
+                                    fuzzer_context, transformation_sequence_out,
                                     std::forward<Args>(extra_args)...));
   }
 }
@@ -86,26 +87,31 @@
 }  // namespace
 
 struct Fuzzer::Impl {
-  explicit Impl(spv_target_env env, uint32_t random_seed,
-                bool validate_after_each_pass)
+  Impl(spv_target_env env, uint32_t random_seed, bool validate_after_each_pass,
+       spv_validator_options options)
       : target_env(env),
         seed(random_seed),
-        validate_after_each_fuzzer_pass(validate_after_each_pass) {}
+        validate_after_each_fuzzer_pass(validate_after_each_pass),
+        validator_options(options) {}
 
   bool ApplyPassAndCheckValidity(FuzzerPass* pass,
                                  const opt::IRContext& ir_context,
                                  const spvtools::SpirvTools& tools) const;
 
   const spv_target_env target_env;       // Target environment.
+  MessageConsumer consumer;              // Message consumer.
   const uint32_t seed;                   // Seed for random number generator.
   bool validate_after_each_fuzzer_pass;  // Determines whether the validator
-  // should be invoked after every fuzzer pass.
-  MessageConsumer consumer;  // Message consumer.
+                                         // should be invoked after every fuzzer
+                                         // pass.
+  spv_validator_options validator_options;  // Options to control validation.
 };
 
 Fuzzer::Fuzzer(spv_target_env env, uint32_t seed,
-               bool validate_after_each_fuzzer_pass)
-    : impl_(MakeUnique<Impl>(env, seed, validate_after_each_fuzzer_pass)) {}
+               bool validate_after_each_fuzzer_pass,
+               spv_validator_options validator_options)
+    : impl_(MakeUnique<Impl>(env, seed, validate_after_each_fuzzer_pass,
+                             validator_options)) {}
 
 Fuzzer::~Fuzzer() = default;
 
@@ -120,7 +126,8 @@
   if (validate_after_each_fuzzer_pass) {
     std::vector<uint32_t> binary_to_validate;
     ir_context.module()->ToBinary(&binary_to_validate, false);
-    if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size())) {
+    if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size(),
+                        validator_options)) {
       consumer(SPV_MSG_INFO, nullptr, {},
                "Binary became invalid during fuzzing (set a breakpoint to "
                "inspect); stopping.");
@@ -149,7 +156,8 @@
   }
 
   // Initial binary should be valid.
-  if (!tools.Validate(&binary_in[0], binary_in.size())) {
+  if (!tools.Validate(&binary_in[0], binary_in.size(),
+                      impl_->validator_options)) {
     impl_->consumer(SPV_MSG_ERROR, nullptr, {},
                     "Initial binary is invalid; stopping.");
     return Fuzzer::FuzzerResultStatus::kInitialBinaryInvalid;
@@ -175,83 +183,75 @@
 
   FactManager fact_manager;
   fact_manager.AddFacts(impl_->consumer, initial_facts, ir_context.get());
-
-  // Add some essential ingredients to the module if they are not already
-  // present, such as boolean constants.
-  FuzzerPassAddUsefulConstructs add_useful_constructs(
-      ir_context.get(), &fact_manager, &fuzzer_context,
-      transformation_sequence_out);
-  if (!impl_->ApplyPassAndCheckValidity(&add_useful_constructs, *ir_context,
-                                        tools)) {
-    return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
-  }
+  TransformationContext transformation_context(&fact_manager,
+                                               impl_->validator_options);
 
   // Apply some semantics-preserving passes.
   std::vector<std::unique_ptr<FuzzerPass>> passes;
   while (passes.empty()) {
-    MaybeAddPass<FuzzerPassAddAccessChains>(&passes, ir_context.get(),
-                                            &fact_manager, &fuzzer_context,
-                                            transformation_sequence_out);
-    MaybeAddPass<FuzzerPassAddCompositeTypes>(&passes, ir_context.get(),
-                                              &fact_manager, &fuzzer_context,
-                                              transformation_sequence_out);
-    MaybeAddPass<FuzzerPassAddDeadBlocks>(&passes, ir_context.get(),
-                                          &fact_manager, &fuzzer_context,
-                                          transformation_sequence_out);
-    MaybeAddPass<FuzzerPassAddDeadBreaks>(&passes, ir_context.get(),
-                                          &fact_manager, &fuzzer_context,
-                                          transformation_sequence_out);
-    MaybeAddPass<FuzzerPassAddDeadContinues>(&passes, ir_context.get(),
-                                             &fact_manager, &fuzzer_context,
-                                             transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddAccessChains>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddCompositeTypes>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddDeadBlocks>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddDeadBreaks>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddDeadContinues>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassAddEquationInstructions>(
-        &passes, ir_context.get(), &fact_manager, &fuzzer_context,
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
-    MaybeAddPass<FuzzerPassAddFunctionCalls>(&passes, ir_context.get(),
-                                             &fact_manager, &fuzzer_context,
-                                             transformation_sequence_out);
-    MaybeAddPass<FuzzerPassAddGlobalVariables>(&passes, ir_context.get(),
-                                               &fact_manager, &fuzzer_context,
-                                               transformation_sequence_out);
-    MaybeAddPass<FuzzerPassAddLoads>(&passes, ir_context.get(), &fact_manager,
-                                     &fuzzer_context,
+    MaybeAddPass<FuzzerPassAddFunctionCalls>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddGlobalVariables>(
+        &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(),
-                                              &fact_manager, &fuzzer_context,
-                                              transformation_sequence_out);
-    MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(), &fact_manager,
-                                      &fuzzer_context,
-                                      transformation_sequence_out);
-    MaybeAddPass<FuzzerPassApplyIdSynonyms>(&passes, ir_context.get(),
-                                            &fact_manager, &fuzzer_context,
-                                            transformation_sequence_out);
-    MaybeAddPass<FuzzerPassConstructComposites>(&passes, ir_context.get(),
-                                                &fact_manager, &fuzzer_context,
-                                                transformation_sequence_out);
-    MaybeAddPass<FuzzerPassCopyObjects>(&passes, ir_context.get(),
-                                        &fact_manager, &fuzzer_context,
-                                        transformation_sequence_out);
-    MaybeAddPass<FuzzerPassDonateModules>(
-        &passes, ir_context.get(), &fact_manager, &fuzzer_context,
-        transformation_sequence_out, donor_suppliers);
-    MaybeAddPass<FuzzerPassMergeBlocks>(&passes, ir_context.get(),
-                                        &fact_manager, &fuzzer_context,
-                                        transformation_sequence_out);
-    MaybeAddPass<FuzzerPassObfuscateConstants>(&passes, ir_context.get(),
-                                               &fact_manager, &fuzzer_context,
-                                               transformation_sequence_out);
-    MaybeAddPass<FuzzerPassOutlineFunctions>(&passes, ir_context.get(),
-                                             &fact_manager, &fuzzer_context,
-                                             transformation_sequence_out);
-    MaybeAddPass<FuzzerPassPermuteBlocks>(&passes, ir_context.get(),
-                                          &fact_manager, &fuzzer_context,
-                                          transformation_sequence_out);
-    MaybeAddPass<FuzzerPassPermuteFunctionParameters>(
-        &passes, ir_context.get(), &fact_manager, &fuzzer_context,
+    MaybeAddPass<FuzzerPassAddLocalVariables>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
-    MaybeAddPass<FuzzerPassSplitBlocks>(&passes, ir_context.get(),
-                                        &fact_manager, &fuzzer_context,
-                                        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      transformation_sequence_out);
+    MaybeAddPass<FuzzerPassApplyIdSynonyms>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassConstructComposites>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassCopyObjects>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassDonateModules>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out, donor_suppliers);
+    MaybeAddPass<FuzzerPassMergeBlocks>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassObfuscateConstants>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassOutlineFunctions>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassPermuteBlocks>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassPermuteFunctionParameters>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassSplitBlocks>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
   }
 
   bool is_first = true;
@@ -271,26 +271,29 @@
   // Now apply some passes that it does not make sense to apply repeatedly,
   // as they do not unlock other passes.
   std::vector<std::unique_ptr<FuzzerPass>> final_passes;
-  MaybeAddPass<FuzzerPassAdjustFunctionControls>(
-      &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+  MaybeAddPass<FuzzerPassAdjustBranchWeights>(
+      &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
       transformation_sequence_out);
-  MaybeAddPass<FuzzerPassAdjustLoopControls>(&final_passes, ir_context.get(),
-                                             &fact_manager, &fuzzer_context,
-                                             transformation_sequence_out);
+  MaybeAddPass<FuzzerPassAdjustFunctionControls>(
+      &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
+      transformation_sequence_out);
+  MaybeAddPass<FuzzerPassAdjustLoopControls>(
+      &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
+      transformation_sequence_out);
   MaybeAddPass<FuzzerPassAdjustMemoryOperandsMasks>(
-      &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+      &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
       transformation_sequence_out);
   MaybeAddPass<FuzzerPassAdjustSelectionControls>(
-      &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+      &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
       transformation_sequence_out);
   MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
-      &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+      &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
       transformation_sequence_out);
   MaybeAddPass<FuzzerPassSwapCommutableOperands>(
-      &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+      &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
       transformation_sequence_out);
   MaybeAddPass<FuzzerPassToggleAccessChainInstruction>(
-      &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+      &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
       transformation_sequence_out);
   for (auto& pass : final_passes) {
     if (!impl_->ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) {
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer.h
index 3ac73a1..6c3ef71 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer.h
@@ -41,8 +41,9 @@
   // seed for pseudo-random number generation.
   // |validate_after_each_fuzzer_pass| controls whether the validator will be
   // invoked after every fuzzer pass is applied.
-  explicit Fuzzer(spv_target_env env, uint32_t seed,
-                  bool validate_after_each_fuzzer_pass);
+  Fuzzer(spv_target_env env, uint32_t seed,
+         bool validate_after_each_fuzzer_pass,
+         spv_validator_options validator_options);
 
   // Disables copy/move constructor/assignment operations.
   Fuzzer(const Fuzzer&) = delete;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
index 2f9fc5a..1779709 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
@@ -40,6 +40,7 @@
     5, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingStore = {5, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorType = {20, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfAdjustingBranchWeights = {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingFunctionControl = {20,
                                                                          70};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingLoopControl = {20, 90};
@@ -68,6 +69,7 @@
 
 // Default limits for various quantities that are chosen during fuzzing.
 // Keep them in alphabetical order.
+const uint32_t kDefaultMaxEquivalenceClassSizeForDataSynonymFactClosure = 1000;
 const uint32_t kDefaultMaxLoopControlPartialCount = 100;
 const uint32_t kDefaultMaxLoopControlPeelCount = 100;
 const uint32_t kDefaultMaxLoopLimit = 20;
@@ -89,6 +91,8 @@
                              uint32_t min_fresh_id)
     : random_generator_(random_generator),
       next_fresh_id_(min_fresh_id),
+      max_equivalence_class_size_for_data_synonym_fact_closure_(
+          kDefaultMaxEquivalenceClassSizeForDataSynonymFactClosure),
       max_loop_control_partial_count_(kDefaultMaxLoopControlPartialCount),
       max_loop_control_peel_count_(kDefaultMaxLoopControlPeelCount),
       max_loop_limit_(kDefaultMaxLoopLimit),
@@ -121,6 +125,8 @@
   chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore);
   chance_of_adding_vector_type_ =
       ChooseBetweenMinAndMax(kChanceOfAddingVectorType);
+  chance_of_adjusting_branch_weights_ =
+      ChooseBetweenMinAndMax(kChanceOfAdjustingBranchWeights);
   chance_of_adjusting_function_control_ =
       ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl);
   chance_of_adjusting_loop_control_ =
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
index 1529705..dd19d9a 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
@@ -136,6 +136,9 @@
   uint32_t GetChanceOfAddingVectorType() {
     return chance_of_adding_vector_type_;
   }
+  uint32_t GetChanceOfAdjustingBranchWeights() {
+    return chance_of_adjusting_branch_weights_;
+  }
   uint32_t GetChanceOfAdjustingFunctionControl() {
     return chance_of_adjusting_function_control_;
   }
@@ -183,25 +186,40 @@
   uint32_t GetChanceOfTogglingAccessChainInstruction() {
     return chance_of_toggling_access_chain_instruction_;
   }
-  uint32_t GetRandomLoopControlPeelCount() {
-    return random_generator_->RandomUint32(max_loop_control_peel_count_);
+
+  // Other functions to control transformations. Keep them in alphabetical
+  // order.
+  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 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_);
   }
+  std::pair<uint32_t, uint32_t> GetRandomBranchWeights() {
+    std::pair<uint32_t, uint32_t> branch_weights = {0, 0};
+
+    while (branch_weights.first == 0 && branch_weights.second == 0) {
+      // Using INT32_MAX to do not overflow UINT32_MAX when the branch weights
+      // are added together.
+      branch_weights.first = random_generator_->RandomUint32(INT32_MAX);
+      branch_weights.second = random_generator_->RandomUint32(INT32_MAX);
+    }
+
+    return branch_weights;
+  }
   uint32_t GetRandomSizeForNewArray() {
     // Ensure that the array size is non-zero.
     return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
   }
-
-  // Other functions to control transformations. Keep them in alphabetical
-  // order.
-  uint32_t GetRandomIndexForAccessChain(uint32_t composite_size_bound) {
-    return random_generator_->RandomUint32(composite_size_bound);
-  }
   bool GoDeeperInConstantObfuscation(uint32_t depth) {
     return go_deeper_in_constant_obfuscation_(depth, random_generator_);
   }
@@ -228,6 +246,7 @@
   uint32_t chance_of_adding_no_contraction_decoration_;
   uint32_t chance_of_adding_store_;
   uint32_t chance_of_adding_vector_type_;
+  uint32_t chance_of_adjusting_branch_weights_;
   uint32_t chance_of_adjusting_function_control_;
   uint32_t chance_of_adjusting_loop_control_;
   uint32_t chance_of_adjusting_memory_operands_mask_;
@@ -251,6 +270,7 @@
   // Limits associated with various quantities for which random values are
   // chosen during fuzzing.
   // Keep them in alphabetical order.
+  uint32_t max_equivalence_class_size_for_data_synonym_fact_closure_;
   uint32_t max_loop_control_partial_count_;
   uint32_t max_loop_control_peel_count_;
   uint32_t max_loop_limit_;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
index a76f10d..cd94e4e 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
@@ -14,6 +14,8 @@
 
 #include "source/fuzz/fuzzer_pass.h"
 
+#include <set>
+
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/transformation_add_constant_boolean.h"
@@ -31,11 +33,12 @@
 namespace spvtools {
 namespace fuzz {
 
-FuzzerPass::FuzzerPass(opt::IRContext* ir_context, FactManager* fact_manager,
+FuzzerPass::FuzzerPass(opt::IRContext* ir_context,
+                       TransformationContext* transformation_context,
                        FuzzerContext* fuzzer_context,
                        protobufs::TransformationSequence* transformations)
     : ir_context_(ir_context),
-      fact_manager_(fact_manager),
+      transformation_context_(transformation_context),
       fuzzer_context_(fuzzer_context),
       transformations_(transformations) {}
 
@@ -316,6 +319,35 @@
   return result;
 }
 
+uint32_t FuzzerPass::FindOrCreateConstant(const std::vector<uint32_t>& words,
+                                          uint32_t type_id) {
+  assert(type_id && "Constant's type id can't be 0.");
+
+  const auto* type = GetIRContext()->get_type_mgr()->GetType(type_id);
+  assert(type && "Type does not exist.");
+
+  if (type->AsBool()) {
+    assert(words.size() == 1);
+    return FindOrCreateBoolConstant(words[0]);
+  } 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());
+  } 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]);
+  }
+
+  // This assertion will fail in debug build but not in release build
+  // so we return 0 to make compiler happy.
+  assert(false && "Constant type is not supported");
+  return 0;
+}
+
 uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
   for (auto& inst : GetIRContext()->types_values()) {
     if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) {
@@ -328,43 +360,72 @@
 }
 
 std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
-FuzzerPass::GetAvailableBaseTypesAndPointers(
+FuzzerPass::GetAvailableBasicTypesAndPointers(
     SpvStorageClass storage_class) const {
-  // Records all of the base types available in the module.
-  std::vector<uint32_t> base_types;
+  // Records all of the basic types available in the module.
+  std::set<uint32_t> basic_types;
 
-  // For each base type, records all the associated pointer types that target
-  // that base type and that have |storage_class| as their storage class.
-  std::map<uint32_t, std::vector<uint32_t>> base_type_to_pointers;
+  // For each basic type, records all the associated pointer types that target
+  // the basic type and that have |storage_class| as their storage class.
+  std::map<uint32_t, std::vector<uint32_t>> basic_type_to_pointers;
 
   for (auto& inst : GetIRContext()->types_values()) {
+    // For each basic type that we come across, record type, and the fact that
+    // we cannot yet have seen any pointers that use the basic type as its
+    // pointee type.
+    //
+    // For pointer types with basic pointee types, associate the pointer type
+    // with the basic type.
     switch (inst.opcode()) {
-      case SpvOpTypeArray:
       case SpvOpTypeBool:
       case SpvOpTypeFloat:
       case SpvOpTypeInt:
       case SpvOpTypeMatrix:
-      case SpvOpTypeStruct:
       case SpvOpTypeVector:
-        // These types are suitable as pointer base types.  Record the type,
-        // and the fact that we cannot yet have seen any pointers that use this
-        // as its base type.
-        base_types.push_back(inst.result_id());
-        base_type_to_pointers.insert({inst.result_id(), {}});
+        // These are all basic types.
+        basic_types.insert(inst.result_id());
+        basic_type_to_pointers.insert({inst.result_id(), {}});
         break;
-      case SpvOpTypePointer:
-        if (inst.GetSingleWordInOperand(0) == storage_class) {
-          // The pointer has the desired storage class, so we are interested in
-          // it.  Associate it with its base type.
-          base_type_to_pointers.at(inst.GetSingleWordInOperand(1))
-              .push_back(inst.result_id());
+      case SpvOpTypeArray:
+        // An array type is basic if its base type is basic.
+        if (basic_types.count(inst.GetSingleWordInOperand(0))) {
+          basic_types.insert(inst.result_id());
+          basic_type_to_pointers.insert({inst.result_id(), {}});
         }
         break;
+      case SpvOpTypeStruct: {
+        // A struct type is basic if all of its members are basic.
+        bool all_members_are_basic_types = true;
+        for (uint32_t i = 0; i < inst.NumInOperands(); i++) {
+          if (!basic_types.count(inst.GetSingleWordInOperand(i))) {
+            all_members_are_basic_types = false;
+            break;
+          }
+        }
+        if (all_members_are_basic_types) {
+          basic_types.insert(inst.result_id());
+          basic_type_to_pointers.insert({inst.result_id(), {}});
+        }
+        break;
+      }
+      case SpvOpTypePointer: {
+        // We are interested in the pointer if its pointee type is basic and it
+        // has the right storage class.
+        auto pointee_type = inst.GetSingleWordInOperand(1);
+        if (inst.GetSingleWordInOperand(0) == storage_class &&
+            basic_types.count(pointee_type)) {
+          // The pointer has the desired storage class, and its pointee type is
+          // a basic type, so we are interested in it.  Associate it with its
+          // basic type.
+          basic_type_to_pointers.at(pointee_type).push_back(inst.result_id());
+        }
+        break;
+      }
       default:
         break;
     }
   }
-  return {base_types, base_type_to_pointers};
+  return {{basic_types.begin(), basic_types.end()}, basic_type_to_pointers};
 }
 
 uint32_t FuzzerPass::FindOrCreateZeroConstant(
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
index 46ee408..800b888 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
@@ -18,9 +18,10 @@
 #include <functional>
 #include <vector>
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/fuzzer_context.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -29,22 +30,25 @@
 // Interface for applying a pass of transformations to a module.
 class FuzzerPass {
  public:
-  FuzzerPass(opt::IRContext* ir_context, FactManager* fact_manager,
+  FuzzerPass(opt::IRContext* ir_context,
+             TransformationContext* transformation_context,
              FuzzerContext* fuzzer_context,
              protobufs::TransformationSequence* transformations);
 
   virtual ~FuzzerPass();
 
   // Applies the pass to the module |ir_context_|, assuming and updating
-  // facts from |fact_manager_|, and using |fuzzer_context_| to guide the
-  // process.  Appends to |transformations_| all transformations that were
-  // applied during the pass.
+  // information from |transformation_context_|, and using |fuzzer_context_| to
+  // guide the process.  Appends to |transformations_| all transformations that
+  // were applied during the pass.
   virtual void Apply() = 0;
 
  protected:
   opt::IRContext* GetIRContext() const { return ir_context_; }
 
-  FactManager* GetFactManager() const { return fact_manager_; }
+  TransformationContext* GetTransformationContext() const {
+    return transformation_context_;
+  }
 
   FuzzerContext* GetFuzzerContext() const { return fuzzer_context_; }
 
@@ -91,11 +95,11 @@
 
   // A generic helper for applying a transformation that should be applicable
   // by construction, and adding it to the sequence of applied transformations.
-  template <typename TransformationType>
-  void ApplyTransformation(const TransformationType& transformation) {
-    assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+  void ApplyTransformation(const Transformation& transformation) {
+    assert(transformation.IsApplicable(GetIRContext(),
+                                       *GetTransformationContext()) &&
            "Transformation should be applicable by construction.");
-    transformation.Apply(GetIRContext(), GetFactManager());
+    transformation.Apply(GetIRContext(), GetTransformationContext());
     *GetTransformations()->add_transformation() = transformation.ToMessage();
   }
 
@@ -160,23 +164,34 @@
   // type do not exist, transformations are applied to add them.
   uint32_t FindOrCreateBoolConstant(bool value);
 
+  // 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.
+  uint32_t FindOrCreateConstant(const std::vector<uint32_t>& words,
+                                uint32_t type_id);
+
   // Returns the result id of an instruction of the form:
   //   %id = OpUndef %|type_id|
   // If no such instruction exists, a transformation is applied to add it.
   uint32_t FindOrCreateGlobalUndef(uint32_t type_id);
 
-  // Yields a pair, (base_type_ids, base_type_ids_to_pointers), such that:
-  // - base_type_ids captures every scalar or composite type declared in the
-  //   module (i.e., all int, bool, float, vector, matrix, struct and array
-  //   types
-  // - base_type_ids_to_pointers maps every such base type to the sequence
+  // 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),
+  // or a runtime-sized array.
+  //
+  // Yields a pair, (basic_type_ids, basic_type_ids_to_pointers), such that:
+  // - basic_type_ids captures every basic type declared in the module.
+  // - basic_type_ids_to_pointers maps every such basic type to the sequence
   //   of all pointer types that have storage class |storage_class| and the
-  //   given base type as their pointee type.  The sequence may be empty for
-  //   some base types if no pointers to those types are defined for the given
+  //   given basic type as their pointee type.  The sequence may be empty for
+  //   some basic types if no pointers to those types are defined for the given
   //   storage class, and the sequence will have multiple elements if there are
-  //   repeated pointer declarations for the same base type and storage class.
+  //   repeated pointer declarations for the same basic type and storage class.
   std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
-  GetAvailableBaseTypesAndPointers(SpvStorageClass storage_class) const;
+  GetAvailableBasicTypesAndPointers(SpvStorageClass storage_class) const;
 
   // Given a type id, |scalar_or_composite_type_id|, which must correspond to
   // some scalar or composite type, returns the result id of an instruction
@@ -230,7 +245,7 @@
       const std::vector<uint32_t>& constant_ids);
 
   opt::IRContext* ir_context_;
-  FactManager* fact_manager_;
+  TransformationContext* transformation_context_;
   FuzzerContext* fuzzer_context_;
   protobufs::TransformationSequence* transformations_;
 };
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 cfc2812..b9c1eed 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
@@ -21,10 +21,11 @@
 namespace fuzz {
 
 FuzzerPassAddAccessChains::FuzzerPassAddAccessChains(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassAddAccessChains::~FuzzerPassAddAccessChains() = default;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.h
index 7e8ed61..8649296 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.h
@@ -26,7 +26,7 @@
 class FuzzerPassAddAccessChains : public FuzzerPass {
  public:
   FuzzerPassAddAccessChains(opt::IRContext* ir_context,
-                            FactManager* fact_manager,
+                            TransformationContext* transformation_context,
                             FuzzerContext* fuzzer_context,
                             protobufs::TransformationSequence* transformations);
 
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 32c720e..9b0dda8 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
@@ -22,10 +22,11 @@
 namespace fuzz {
 
 FuzzerPassAddCompositeTypes::FuzzerPassAddCompositeTypes(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassAddCompositeTypes::~FuzzerPassAddCompositeTypes() = default;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_types.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_types.h
index 29d4bb8..87bc0ff 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_types.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_types.h
@@ -25,7 +25,7 @@
 class FuzzerPassAddCompositeTypes : public FuzzerPass {
  public:
   FuzzerPassAddCompositeTypes(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
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 c9bc9c4..30a4145 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
@@ -21,10 +21,11 @@
 namespace fuzz {
 
 FuzzerPassAddDeadBlocks::FuzzerPassAddDeadBlocks(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassAddDeadBlocks::~FuzzerPassAddDeadBlocks() = default;
 
@@ -40,6 +41,12 @@
               GetFuzzerContext()->GetChanceOfAddingDeadBlock())) {
         continue;
       }
+
+      // Make sure the module contains a boolean constant equal to
+      // |condition_value|.
+      bool condition_value = GetFuzzerContext()->ChooseEven();
+      FindOrCreateBoolConstant(condition_value);
+
       // We speculatively create a transformation, and then apply it (below) if
       // it turns out to be applicable.  This avoids duplicating the logic for
       // applicability checking.
@@ -47,14 +54,14 @@
       // It means that fresh ids for transformations that turn out not to be
       // applicable end up being unused.
       candidate_transformations.emplace_back(TransformationAddDeadBlock(
-          GetFuzzerContext()->GetFreshId(), block.id(),
-          GetFuzzerContext()->ChooseEven()));
+          GetFuzzerContext()->GetFreshId(), block.id(), condition_value));
     }
   }
   // Apply all those transformations that are in fact applicable.
   for (auto& transformation : candidate_transformations) {
-    if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
-      transformation.Apply(GetIRContext(), GetFactManager());
+    if (transformation.IsApplicable(GetIRContext(),
+                                    *GetTransformationContext())) {
+      transformation.Apply(GetIRContext(), GetTransformationContext());
       *GetTransformations()->add_transformation() = transformation.ToMessage();
     }
   }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_blocks.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_blocks.h
index 01e3843..d78f088 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_blocks.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_blocks.h
@@ -24,7 +24,8 @@
 // passes can then manipulate such blocks.
 class FuzzerPassAddDeadBlocks : public FuzzerPass {
  public:
-  FuzzerPassAddDeadBlocks(opt::IRContext* ir_context, FactManager* fact_manager,
+  FuzzerPassAddDeadBlocks(opt::IRContext* ir_context,
+                          TransformationContext* transformation_context,
                           FuzzerContext* fuzzer_context,
                           protobufs::TransformationSequence* transformations);
 
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 aefc2fc..f3900aa 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
@@ -21,10 +21,11 @@
 namespace fuzz {
 
 FuzzerPassAddDeadBreaks::FuzzerPassAddDeadBreaks(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassAddDeadBreaks::~FuzzerPassAddDeadBreaks() = default;
 
@@ -76,11 +77,15 @@
           });
         }
 
+        // Make sure the module has a required boolean constant to be used in
+        // OpBranchConditional instruction.
+        auto break_condition = GetFuzzerContext()->ChooseEven();
+        FindOrCreateBoolConstant(break_condition);
+
         auto candidate_transformation = TransformationAddDeadBreak(
-            block.id(), merge_block->id(), GetFuzzerContext()->ChooseEven(),
-            std::move(phi_ids));
-        if (candidate_transformation.IsApplicable(GetIRContext(),
-                                                  *GetFactManager())) {
+            block.id(), merge_block->id(), break_condition, std::move(phi_ids));
+        if (candidate_transformation.IsApplicable(
+                GetIRContext(), *GetTransformationContext())) {
           // Only consider a transformation as a candidate if it is applicable.
           candidate_transformations.push_back(
               std::move(candidate_transformation));
@@ -109,10 +114,11 @@
     candidate_transformations.erase(candidate_transformations.begin() + index);
     // Probabilistically decide whether to try to apply it vs. ignore it, in the
     // case that it is applicable.
-    if (transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+    if (transformation.IsApplicable(GetIRContext(),
+                                    *GetTransformationContext()) &&
         GetFuzzerContext()->ChoosePercentage(
             GetFuzzerContext()->GetChanceOfAddingDeadBreak())) {
-      transformation.Apply(GetIRContext(), GetFactManager());
+      transformation.Apply(GetIRContext(), GetTransformationContext());
       *GetTransformations()->add_transformation() = transformation.ToMessage();
     }
   }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.h
index 12a5095..c379eed 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.h
@@ -23,7 +23,8 @@
 // A fuzzer pass for adding dead break edges to the module.
 class FuzzerPassAddDeadBreaks : public FuzzerPass {
  public:
-  FuzzerPassAddDeadBreaks(opt::IRContext* ir_context, FactManager* fact_manager,
+  FuzzerPassAddDeadBreaks(opt::IRContext* ir_context,
+                          TransformationContext* transformation_context,
                           FuzzerContext* fuzzer_context,
                           protobufs::TransformationSequence* transformations);
 
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 852df3d..56a7fd1 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
@@ -21,10 +21,11 @@
 namespace fuzz {
 
 FuzzerPassAddDeadContinues::FuzzerPassAddDeadContinues(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassAddDeadContinues::~FuzzerPassAddDeadContinues() = default;
 
@@ -67,18 +68,24 @@
         });
       }
 
+      // Make sure the module contains a boolean constant equal to
+      // |condition_value|.
+      bool condition_value = GetFuzzerContext()->ChooseEven();
+      FindOrCreateBoolConstant(condition_value);
+
       // 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
       // precondition for the transformation will fail and it will be ignored.
       auto candidate_transformation = TransformationAddDeadContinue(
-          block.id(), GetFuzzerContext()->ChooseEven(), std::move(phi_ids));
+          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(),
-                                                *GetFactManager()) &&
+                                                *GetTransformationContext()) &&
           GetFuzzerContext()->ChoosePercentage(
               GetFuzzerContext()->GetChanceOfAddingDeadContinue())) {
-        candidate_transformation.Apply(GetIRContext(), GetFactManager());
+        candidate_transformation.Apply(GetIRContext(),
+                                       GetTransformationContext());
         *GetTransformations()->add_transformation() =
             candidate_transformation.ToMessage();
       }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.h
index d067f1c..b2acb93 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.h
@@ -24,7 +24,7 @@
 class FuzzerPassAddDeadContinues : public FuzzerPass {
  public:
   FuzzerPassAddDeadContinues(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
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 7f34344..49c4a8a 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
@@ -23,10 +23,11 @@
 namespace fuzz {
 
 FuzzerPassAddEquationInstructions::FuzzerPassAddEquationInstructions(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassAddEquationInstructions::~FuzzerPassAddEquationInstructions() =
     default;
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 84229c0..6e64977 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
@@ -27,7 +27,7 @@
 class FuzzerPassAddEquationInstructions : public FuzzerPass {
  public:
   FuzzerPassAddEquationInstructions(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
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 545aa16..569df10 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
@@ -24,10 +24,11 @@
 namespace fuzz {
 
 FuzzerPassAddFunctionCalls::FuzzerPassAddFunctionCalls(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassAddFunctionCalls::~FuzzerPassAddFunctionCalls() = default;
 
@@ -74,8 +75,9 @@
         while (!candidate_functions.empty()) {
           opt::Function* candidate_function =
               GetFuzzerContext()->RemoveAtRandomIndex(&candidate_functions);
-          if (!GetFactManager()->BlockIsDead(block->id()) &&
-              !GetFactManager()->FunctionIsLivesafe(
+          if (!GetTransformationContext()->GetFactManager()->BlockIsDead(
+                  block->id()) &&
+              !GetTransformationContext()->GetFactManager()->FunctionIsLivesafe(
                   candidate_function->result_id())) {
             // Unless in a dead block, only livesafe functions can be invoked
             continue;
@@ -132,9 +134,11 @@
                 default:
                   return false;
               }
-              if (!GetFactManager()->BlockIsDead(block->id()) &&
-                  !GetFactManager()->PointeeValueIsIrrelevant(
-                      inst->result_id())) {
+              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
@@ -210,8 +214,9 @@
         result.push_back(fresh_variable_id);
 
         // Now bring the variable into existence.
-        if (type_instruction->GetSingleWordInOperand(0) ==
-            SpvStorageClassFunction) {
+        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(
@@ -220,16 +225,19 @@
                   type_instruction->GetSingleWordInOperand(1)),
               true));
         } else {
-          assert(type_instruction->GetSingleWordInOperand(0) ==
-                     SpvStorageClassPrivate &&
-                 "Only Function and Private storage classes are "
+          assert((storage_class == SpvStorageClassPrivate ||
+                  storage_class == SpvStorageClassWorkgroup) &&
+                 "Only Function, Private and Workgroup storage classes are "
                  "supported at present.");
-          // Add a new zero-initialized global variable to the module,
-          // noting that its pointee value is irrelevant.
+          // 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,
-              FindOrCreateZeroConstant(
-                  type_instruction->GetSingleWordInOperand(1)),
+              fresh_variable_id, arg_type_id, storage_class,
+              storage_class == SpvStorageClassPrivate
+                  ? FindOrCreateZeroConstant(
+                        type_instruction->GetSingleWordInOperand(1))
+                  : 0,
               true));
         }
       } else {
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 5d184fd..8f75e8c 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
@@ -25,7 +25,7 @@
 class FuzzerPassAddFunctionCalls : public FuzzerPass {
  public:
   FuzzerPassAddFunctionCalls(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
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 1371f46..4023b22 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
@@ -21,53 +21,56 @@
 namespace fuzz {
 
 FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default;
 
 void FuzzerPassAddGlobalVariables::Apply() {
-  auto base_type_ids_and_pointers =
-      GetAvailableBaseTypesAndPointers(SpvStorageClassPrivate);
+  auto basic_type_ids_and_pointers =
+      GetAvailableBasicTypesAndPointers(SpvStorageClassPrivate);
 
-  // These are the base types that are available to this fuzzer pass.
-  auto& base_types = base_type_ids_and_pointers.first;
+  // These are the basic types that are available to this fuzzer pass.
+  auto& basic_types = basic_type_ids_and_pointers.first;
 
-  // These are the pointers to those base types that are *initially* available
+  // These are the pointers to those basic types that are *initially* available
   // to the fuzzer pass.  The fuzzer pass might add pointer types in cases where
-  // none are available for a given base type.
-  auto& base_type_to_pointers = base_type_ids_and_pointers.second;
+  // none are available for a given basic type.
+  auto& basic_type_to_pointers = basic_type_ids_and_pointers.second;
 
   // Probabilistically keep adding global variables.
   while (GetFuzzerContext()->ChoosePercentage(
       GetFuzzerContext()->GetChanceOfAddingGlobalVariable())) {
-    // Choose a random base type; the new variable's type will be a pointer to
-    // this base type.
-    uint32_t base_type =
-        base_types[GetFuzzerContext()->RandomIndex(base_types)];
+    // Choose a random basic type; the new variable's type will be a pointer to
+    // this basic type.
+    uint32_t basic_type =
+        basic_types[GetFuzzerContext()->RandomIndex(basic_types)];
     uint32_t pointer_type_id;
-    std::vector<uint32_t>& available_pointers_to_base_type =
-        base_type_to_pointers.at(base_type);
-    // Determine whether there is at least one pointer to this base type.
-    if (available_pointers_to_base_type.empty()) {
+    std::vector<uint32_t>& available_pointers_to_basic_type =
+        basic_type_to_pointers.at(basic_type);
+    // Determine whether there is at least one pointer to this basic type.
+    if (available_pointers_to_basic_type.empty()) {
       // There is not.  Make one, to use here, and add it to the available
-      // pointers for the base type so that future variables can potentially
+      // pointers for the basic type so that future variables can potentially
       // use it.
       pointer_type_id = GetFuzzerContext()->GetFreshId();
-      available_pointers_to_base_type.push_back(pointer_type_id);
+      available_pointers_to_basic_type.push_back(pointer_type_id);
       ApplyTransformation(TransformationAddTypePointer(
-          pointer_type_id, SpvStorageClassPrivate, base_type));
+          pointer_type_id, SpvStorageClassPrivate, basic_type));
     } else {
       // There is - grab one.
       pointer_type_id =
-          available_pointers_to_base_type[GetFuzzerContext()->RandomIndex(
-              available_pointers_to_base_type)];
+          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,
-        FindOrCreateZeroConstant(base_type), true));
+        SpvStorageClassPrivate, FindOrCreateZeroConstant(basic_type), true));
   }
 }
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_global_variables.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_global_variables.h
index c71d147..a907d36 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_global_variables.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_global_variables.h
@@ -25,7 +25,7 @@
 class FuzzerPassAddGlobalVariables : public FuzzerPass {
  public:
   FuzzerPassAddGlobalVariables(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.cpp
index 851787f..256255b 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.cpp
@@ -21,10 +21,11 @@
 namespace fuzz {
 
 FuzzerPassAddLoads::FuzzerPassAddLoads(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassAddLoads::~FuzzerPassAddLoads() = default;
 
@@ -59,7 +60,7 @@
                   if (!instruction->result_id() || !instruction->type_id()) {
                     return false;
                   }
-                  switch (instruction->result_id()) {
+                  switch (instruction->opcode()) {
                     case SpvOpConstantNull:
                     case SpvOpUndef:
                       // Do not allow loading from a null or undefined pointer;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.h
index 125bc5d..c4d5b27 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.h
@@ -23,7 +23,8 @@
 // Fuzzer pass that adds stores, at random, from pointers in the module.
 class FuzzerPassAddLoads : public FuzzerPass {
  public:
-  FuzzerPassAddLoads(opt::IRContext* ir_context, FactManager* fact_manager,
+  FuzzerPassAddLoads(opt::IRContext* ir_context,
+                     TransformationContext* transformation_context,
                      FuzzerContext* fuzzer_context,
                      protobufs::TransformationSequence* transformations);
 
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 8d6d80d..661159e 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
@@ -22,55 +22,56 @@
 namespace fuzz {
 
 FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassAddLocalVariables::~FuzzerPassAddLocalVariables() = default;
 
 void FuzzerPassAddLocalVariables::Apply() {
-  auto base_type_ids_and_pointers =
-      GetAvailableBaseTypesAndPointers(SpvStorageClassFunction);
+  auto basic_type_ids_and_pointers =
+      GetAvailableBasicTypesAndPointers(SpvStorageClassFunction);
 
-  // These are the base types that are available to this fuzzer pass.
-  auto& base_types = base_type_ids_and_pointers.first;
+  // These are the basic types that are available to this fuzzer pass.
+  auto& basic_types = basic_type_ids_and_pointers.first;
 
-  // These are the pointers to those base types that are *initially* available
+  // These are the pointers to those basic types that are *initially* available
   // to the fuzzer pass.  The fuzzer pass might add pointer types in cases where
-  // none are available for a given base type.
-  auto& base_type_to_pointers = base_type_ids_and_pointers.second;
+  // none are available for a given basic type.
+  auto& basic_type_to_pointers = basic_type_ids_and_pointers.second;
 
   // Consider every function in the module.
   for (auto& function : *GetIRContext()->module()) {
     // Probabilistically keep adding random variables to this function.
     while (GetFuzzerContext()->ChoosePercentage(
         GetFuzzerContext()->GetChanceOfAddingLocalVariable())) {
-      // Choose a random base type; the new variable's type will be a pointer to
-      // this base type.
-      uint32_t base_type =
-          base_types[GetFuzzerContext()->RandomIndex(base_types)];
+      // Choose a random basic type; the new variable's type will be a pointer
+      // to this basic type.
+      uint32_t basic_type =
+          basic_types[GetFuzzerContext()->RandomIndex(basic_types)];
       uint32_t pointer_type;
-      std::vector<uint32_t>& available_pointers_to_base_type =
-          base_type_to_pointers.at(base_type);
-      // Determine whether there is at least one pointer to this base type.
-      if (available_pointers_to_base_type.empty()) {
+      std::vector<uint32_t>& available_pointers_to_basic_type =
+          basic_type_to_pointers.at(basic_type);
+      // Determine whether there is at least one pointer to this basic type.
+      if (available_pointers_to_basic_type.empty()) {
         // There is not.  Make one, to use here, and add it to the available
-        // pointers for the base type so that future variables can potentially
+        // pointers for the basic type so that future variables can potentially
         // use it.
         pointer_type = GetFuzzerContext()->GetFreshId();
         ApplyTransformation(TransformationAddTypePointer(
-            pointer_type, SpvStorageClassFunction, base_type));
-        available_pointers_to_base_type.push_back(pointer_type);
+            pointer_type, SpvStorageClassFunction, basic_type));
+        available_pointers_to_basic_type.push_back(pointer_type);
       } else {
         // There is - grab one.
         pointer_type =
-            available_pointers_to_base_type[GetFuzzerContext()->RandomIndex(
-                available_pointers_to_base_type)];
+            available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex(
+                available_pointers_to_basic_type)];
       }
       ApplyTransformation(TransformationAddLocalVariable(
           GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(),
-          FindOrCreateZeroConstant(base_type), true));
+          FindOrCreateZeroConstant(basic_type), true));
     }
   }
 }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_local_variables.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_local_variables.h
index eed3665..08d26d8 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_local_variables.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_local_variables.h
@@ -25,7 +25,7 @@
 class FuzzerPassAddLocalVariables : public FuzzerPass {
  public:
   FuzzerPassAddLocalVariables(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
index 82fb539..09627d0 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
@@ -20,10 +20,11 @@
 namespace fuzz {
 
 FuzzerPassAddNoContractionDecorations::FuzzerPassAddNoContractionDecorations(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassAddNoContractionDecorations::
     ~FuzzerPassAddNoContractionDecorations() = default;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h
index abe5bd7..f32e5bc 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h
@@ -24,7 +24,7 @@
 class FuzzerPassAddNoContractionDecorations : public FuzzerPass {
  public:
   FuzzerPassAddNoContractionDecorations(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.cpp
index 794ddc3..46efc64 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.cpp
@@ -21,10 +21,11 @@
 namespace fuzz {
 
 FuzzerPassAddStores::FuzzerPassAddStores(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassAddStores::~FuzzerPassAddStores() = default;
 
@@ -67,12 +68,11 @@
                     // Not a pointer.
                     return false;
                   }
-                  if (type_inst->GetSingleWordInOperand(0) ==
-                      SpvStorageClassInput) {
-                    // Read-only: cannot store to it.
+                  if (instruction->IsReadOnlyPointer()) {
+                    // Read only: cannot store to it.
                     return false;
                   }
-                  switch (instruction->result_id()) {
+                  switch (instruction->opcode()) {
                     case SpvOpConstantNull:
                     case SpvOpUndef:
                       // Do not allow storing to a null or undefined pointer;
@@ -82,9 +82,13 @@
                     default:
                       break;
                   }
-                  return GetFactManager()->BlockIsDead(block->id()) ||
-                         GetFactManager()->PointeeValueIsIrrelevant(
-                             instruction->result_id());
+                  return GetTransformationContext()
+                             ->GetFactManager()
+                             ->BlockIsDead(block->id()) ||
+                         GetTransformationContext()
+                             ->GetFactManager()
+                             ->PointeeValueIsIrrelevant(
+                                 instruction->result_id());
                 });
 
         // At this point, |relevant_pointers| contains all the pointers we might
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.h
index 9daa9e0..55ec67f 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.h
@@ -25,7 +25,8 @@
 // are known not to affect the module's overall behaviour.
 class FuzzerPassAddStores : public FuzzerPass {
  public:
-  FuzzerPassAddStores(opt::IRContext* ir_context, FactManager* fact_manager,
+  FuzzerPassAddStores(opt::IRContext* ir_context,
+                      TransformationContext* transformation_context,
                       FuzzerContext* fuzzer_context,
                       protobufs::TransformationSequence* transformations);
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_useful_constructs.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_useful_constructs.cpp
deleted file mode 100644
index 8552dfd..0000000
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_useful_constructs.cpp
+++ /dev/null
@@ -1,214 +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/fuzzer_pass_add_useful_constructs.h"
-
-#include "source/fuzz/transformation_add_constant_boolean.h"
-#include "source/fuzz/transformation_add_constant_scalar.h"
-#include "source/fuzz/transformation_add_type_boolean.h"
-#include "source/fuzz/transformation_add_type_float.h"
-#include "source/fuzz/transformation_add_type_int.h"
-#include "source/fuzz/transformation_add_type_pointer.h"
-
-namespace spvtools {
-namespace fuzz {
-
-FuzzerPassAddUsefulConstructs::FuzzerPassAddUsefulConstructs(
-    opt::IRContext* ir_context, FactManager* fact_manager,
-    FuzzerContext* fuzzer_context,
-    protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
-
-FuzzerPassAddUsefulConstructs::~FuzzerPassAddUsefulConstructs() = default;
-
-void FuzzerPassAddUsefulConstructs::MaybeAddIntConstant(
-    uint32_t width, bool is_signed, std::vector<uint32_t> data) const {
-  opt::analysis::Integer temp_int_type(width, is_signed);
-  assert(GetIRContext()->get_type_mgr()->GetId(&temp_int_type) &&
-         "int type should already be registered.");
-  auto registered_int_type = GetIRContext()
-                                 ->get_type_mgr()
-                                 ->GetRegisteredType(&temp_int_type)
-                                 ->AsInteger();
-  auto int_type_id = GetIRContext()->get_type_mgr()->GetId(registered_int_type);
-  assert(int_type_id &&
-         "The relevant int type should have been added to the module already.");
-  opt::analysis::IntConstant int_constant(registered_int_type, data);
-  if (!GetIRContext()->get_constant_mgr()->FindConstant(&int_constant)) {
-    TransformationAddConstantScalar add_constant_int =
-        TransformationAddConstantScalar(GetFuzzerContext()->GetFreshId(),
-                                        int_type_id, data);
-    assert(add_constant_int.IsApplicable(GetIRContext(), *GetFactManager()) &&
-           "Should be applicable by construction.");
-    add_constant_int.Apply(GetIRContext(), GetFactManager());
-    *GetTransformations()->add_transformation() = add_constant_int.ToMessage();
-  }
-}
-
-void FuzzerPassAddUsefulConstructs::MaybeAddFloatConstant(
-    uint32_t width, std::vector<uint32_t> data) const {
-  opt::analysis::Float temp_float_type(width);
-  assert(GetIRContext()->get_type_mgr()->GetId(&temp_float_type) &&
-         "float type should already be registered.");
-  auto registered_float_type = GetIRContext()
-                                   ->get_type_mgr()
-                                   ->GetRegisteredType(&temp_float_type)
-                                   ->AsFloat();
-  auto float_type_id =
-      GetIRContext()->get_type_mgr()->GetId(registered_float_type);
-  assert(
-      float_type_id &&
-      "The relevant float type should have been added to the module already.");
-  opt::analysis::FloatConstant float_constant(registered_float_type, data);
-  if (!GetIRContext()->get_constant_mgr()->FindConstant(&float_constant)) {
-    TransformationAddConstantScalar add_constant_float =
-        TransformationAddConstantScalar(GetFuzzerContext()->GetFreshId(),
-                                        float_type_id, data);
-    assert(add_constant_float.IsApplicable(GetIRContext(), *GetFactManager()) &&
-           "Should be applicable by construction.");
-    add_constant_float.Apply(GetIRContext(), GetFactManager());
-    *GetTransformations()->add_transformation() =
-        add_constant_float.ToMessage();
-  }
-}
-
-void FuzzerPassAddUsefulConstructs::Apply() {
-  {
-    // Add boolean type if not present.
-    opt::analysis::Bool temp_bool_type;
-    if (!GetIRContext()->get_type_mgr()->GetId(&temp_bool_type)) {
-      auto add_type_boolean =
-          TransformationAddTypeBoolean(GetFuzzerContext()->GetFreshId());
-      assert(add_type_boolean.IsApplicable(GetIRContext(), *GetFactManager()) &&
-             "Should be applicable by construction.");
-      add_type_boolean.Apply(GetIRContext(), GetFactManager());
-      *GetTransformations()->add_transformation() =
-          add_type_boolean.ToMessage();
-    }
-  }
-
-  {
-    // Add signed and unsigned 32-bit integer types if not present.
-    for (auto is_signed : {true, false}) {
-      opt::analysis::Integer temp_int_type(32, is_signed);
-      if (!GetIRContext()->get_type_mgr()->GetId(&temp_int_type)) {
-        TransformationAddTypeInt add_type_int = TransformationAddTypeInt(
-            GetFuzzerContext()->GetFreshId(), 32, is_signed);
-        assert(add_type_int.IsApplicable(GetIRContext(), *GetFactManager()) &&
-               "Should be applicable by construction.");
-        add_type_int.Apply(GetIRContext(), GetFactManager());
-        *GetTransformations()->add_transformation() = add_type_int.ToMessage();
-      }
-    }
-  }
-
-  {
-    // Add 32-bit float type if not present.
-    opt::analysis::Float temp_float_type(32);
-    if (!GetIRContext()->get_type_mgr()->GetId(&temp_float_type)) {
-      TransformationAddTypeFloat add_type_float =
-          TransformationAddTypeFloat(GetFuzzerContext()->GetFreshId(), 32);
-      assert(add_type_float.IsApplicable(GetIRContext(), *GetFactManager()) &&
-             "Should be applicable by construction.");
-      add_type_float.Apply(GetIRContext(), GetFactManager());
-      *GetTransformations()->add_transformation() = add_type_float.ToMessage();
-    }
-  }
-
-  // Add boolean constants true and false if not present.
-  opt::analysis::Bool temp_bool_type;
-  auto bool_type = GetIRContext()
-                       ->get_type_mgr()
-                       ->GetRegisteredType(&temp_bool_type)
-                       ->AsBool();
-  for (auto boolean_value : {true, false}) {
-    // Add OpConstantTrue/False if not already there.
-    opt::analysis::BoolConstant bool_constant(bool_type, boolean_value);
-    if (!GetIRContext()->get_constant_mgr()->FindConstant(&bool_constant)) {
-      TransformationAddConstantBoolean add_constant_boolean(
-          GetFuzzerContext()->GetFreshId(), boolean_value);
-      assert(add_constant_boolean.IsApplicable(GetIRContext(),
-                                               *GetFactManager()) &&
-             "Should be applicable by construction.");
-      add_constant_boolean.Apply(GetIRContext(), GetFactManager());
-      *GetTransformations()->add_transformation() =
-          add_constant_boolean.ToMessage();
-    }
-  }
-
-  // Add signed and unsigned 32-bit integer constants 0 and 1 if not present.
-  for (auto is_signed : {true, false}) {
-    for (auto value : {0u, 1u}) {
-      MaybeAddIntConstant(32, is_signed, {value});
-    }
-  }
-
-  // Add 32-bit float constants 0.0 and 1.0 if not present.
-  uint32_t uint_data[2];
-  float float_data[2] = {0.0, 1.0};
-  memcpy(uint_data, float_data, sizeof(float_data));
-  for (unsigned int& datum : uint_data) {
-    MaybeAddFloatConstant(32, {datum});
-  }
-
-  // For every known-to-be-constant uniform, make sure we have instructions
-  // declaring:
-  // - a pointer type with uniform storage class, whose pointee type is the type
-  //   of the element
-  // - a signed integer constant for each index required to access the element
-  // - a constant for the constant value itself
-  for (auto& fact_and_type_id :
-       GetFactManager()->GetConstantUniformFactsAndTypes()) {
-    uint32_t element_type_id = fact_and_type_id.second;
-    assert(element_type_id);
-    auto element_type =
-        GetIRContext()->get_type_mgr()->GetType(element_type_id);
-    assert(element_type &&
-           "If the constant uniform fact is well-formed, the module must "
-           "already have a declaration of the type for the uniform element.");
-    opt::analysis::Pointer uniform_pointer(element_type,
-                                           SpvStorageClassUniform);
-    if (!GetIRContext()->get_type_mgr()->GetId(&uniform_pointer)) {
-      auto add_pointer =
-          TransformationAddTypePointer(GetFuzzerContext()->GetFreshId(),
-                                       SpvStorageClassUniform, element_type_id);
-      assert(add_pointer.IsApplicable(GetIRContext(), *GetFactManager()) &&
-             "Should be applicable by construction.");
-      add_pointer.Apply(GetIRContext(), GetFactManager());
-      *GetTransformations()->add_transformation() = add_pointer.ToMessage();
-    }
-    std::vector<uint32_t> words;
-    for (auto word : fact_and_type_id.first.constant_word()) {
-      words.push_back(word);
-    }
-    // We get the element type again as the type manager may have been
-    // invalidated since we last retrieved it.
-    element_type = GetIRContext()->get_type_mgr()->GetType(element_type_id);
-    if (element_type->AsInteger()) {
-      MaybeAddIntConstant(element_type->AsInteger()->width(),
-                          element_type->AsInteger()->IsSigned(), words);
-    } else {
-      assert(element_type->AsFloat() &&
-             "Known uniform values must be integer or floating-point.");
-      MaybeAddFloatConstant(element_type->AsFloat()->width(), words);
-    }
-    for (auto index :
-         fact_and_type_id.first.uniform_buffer_element_descriptor().index()) {
-      MaybeAddIntConstant(32, true, {index});
-    }
-  }
-}
-
-}  // namespace fuzz
-}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_useful_constructs.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_useful_constructs.h
deleted file mode 100644
index 7dc00f1..0000000
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_useful_constructs.h
+++ /dev/null
@@ -1,46 +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_FUZZER_PASS_ADD_USEFUL_CONSTRUCTS_
-#define SOURCE_FUZZ_FUZZER_PASS_ADD_USEFUL_CONSTRUCTS_
-
-#include "source/fuzz/fuzzer_pass.h"
-
-namespace spvtools {
-namespace fuzz {
-
-// An initial pass for adding useful ingredients to the module, such as boolean
-// constants, if they are not present.
-class FuzzerPassAddUsefulConstructs : public FuzzerPass {
- public:
-  FuzzerPassAddUsefulConstructs(
-      opt::IRContext* ir_context, FactManager* fact_manager,
-      FuzzerContext* fuzzer_context,
-      protobufs::TransformationSequence* transformations);
-
-  ~FuzzerPassAddUsefulConstructs() override;
-
-  void Apply() override;
-
- private:
-  void MaybeAddIntConstant(uint32_t width, bool is_signed,
-                           std::vector<uint32_t> data) const;
-
-  void MaybeAddFloatConstant(uint32_t width, std::vector<uint32_t> data) const;
-};
-
-}  // namespace fuzz
-}  // namespace spvtools
-
-#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_USEFUL_CONSTRUCTS_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp
new file mode 100644
index 0000000..1d6d434
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp
@@ -0,0 +1,48 @@
+// 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_adjust_branch_weights.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_adjust_branch_weights.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAdjustBranchWeights::FuzzerPassAdjustBranchWeights(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassAdjustBranchWeights::~FuzzerPassAdjustBranchWeights() = default;
+
+void FuzzerPassAdjustBranchWeights::Apply() {
+  // For all OpBranchConditional instructions,
+  // randomly applies the transformation.
+  GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
+    if (instruction->opcode() == SpvOpBranchConditional &&
+        GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfAdjustingBranchWeights())) {
+      ApplyTransformation(TransformationAdjustBranchWeights(
+          MakeInstructionDescriptor(GetIRContext(), instruction),
+          GetFuzzerContext()->GetRandomBranchWeights()));
+    }
+  });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_branch_weights.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_branch_weights.h
new file mode 100644
index 0000000..5b2b33f
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_branch_weights.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_ADJUST_BRANCH_WEIGHTS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_BRANCH_WEIGHTS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// This fuzzer pass searches for branch conditional instructions
+// and randomly chooses which of these instructions will have their weights
+// adjusted.
+class FuzzerPassAdjustBranchWeights : public FuzzerPass {
+ public:
+  FuzzerPassAdjustBranchWeights(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAdjustBranchWeights();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADJUST_BRANCH_WEIGHTS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
index fe229bc..aa62d2f 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
@@ -20,10 +20,11 @@
 namespace fuzz {
 
 FuzzerPassAdjustFunctionControls::FuzzerPassAdjustFunctionControls(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassAdjustFunctionControls::~FuzzerPassAdjustFunctionControls() = default;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_function_controls.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_function_controls.h
index 02d3600..e20541b 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_function_controls.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_function_controls.h
@@ -24,7 +24,7 @@
 class FuzzerPassAdjustFunctionControls : public FuzzerPass {
  public:
   FuzzerPassAdjustFunctionControls(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
index c9843d0..f7addff 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
@@ -20,10 +20,11 @@
 namespace fuzz {
 
 FuzzerPassAdjustLoopControls::FuzzerPassAdjustLoopControls(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassAdjustLoopControls::~FuzzerPassAdjustLoopControls() = default;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h
index e945606..ee5cd48 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h
@@ -24,7 +24,7 @@
 class FuzzerPassAdjustLoopControls : public FuzzerPass {
  public:
   FuzzerPassAdjustLoopControls(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
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 2d3d676..32f5ea5 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
@@ -21,10 +21,11 @@
 namespace fuzz {
 
 FuzzerPassAdjustMemoryOperandsMasks::FuzzerPassAdjustMemoryOperandsMasks(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassAdjustMemoryOperandsMasks::~FuzzerPassAdjustMemoryOperandsMasks() =
     default;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h
index c3d7118..699dcb5 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h
@@ -25,7 +25,7 @@
 class FuzzerPassAdjustMemoryOperandsMasks : public FuzzerPass {
  public:
   FuzzerPassAdjustMemoryOperandsMasks(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
index 397dfed..83b1854 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
@@ -20,10 +20,11 @@
 namespace fuzz {
 
 FuzzerPassAdjustSelectionControls::FuzzerPassAdjustSelectionControls(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassAdjustSelectionControls::~FuzzerPassAdjustSelectionControls() =
     default;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h
index b5b255c..820b30d 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h
@@ -24,7 +24,7 @@
 class FuzzerPassAdjustSelectionControls : public FuzzerPass {
  public:
   FuzzerPassAdjustSelectionControls(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
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 5711f35..0ec93e1 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
@@ -19,32 +19,45 @@
 #include "source/fuzz/id_use_descriptor.h"
 #include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/transformation_composite_extract.h"
+#include "source/fuzz/transformation_compute_data_synonym_fact_closure.h"
 #include "source/fuzz/transformation_replace_id_with_synonym.h"
 
 namespace spvtools {
 namespace fuzz {
 
 FuzzerPassApplyIdSynonyms::FuzzerPassApplyIdSynonyms(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassApplyIdSynonyms::~FuzzerPassApplyIdSynonyms() = default;
 
 void FuzzerPassApplyIdSynonyms::Apply() {
-  for (auto id_with_known_synonyms :
-       GetFactManager()->GetIdsForWhichSynonymsAreKnown(GetIRContext())) {
-    // Gather up all uses of |id_with_known_synonym|, and then subsequently
-    // iterate over these uses.  We use this separation because, when
-    // considering a given use, we might apply a transformation that will
+  // Compute a closure of data synonym facts, to enrich the pool of synonyms
+  // that are available.
+  ApplyTransformation(TransformationComputeDataSynonymFactClosure(
+      GetFuzzerContext()
+          ->GetMaximumEquivalenceClassSizeForDataSynonymFactClosure()));
+
+  for (auto id_with_known_synonyms : GetTransformationContext()
+                                         ->GetFactManager()
+                                         ->GetIdsForWhichSynonymsAreKnown()) {
+    // Gather up all uses of |id_with_known_synonym| as a regular id, and
+    // subsequently iterate over these uses.  We use this separation because,
+    // when considering a given use, we might apply a transformation that will
     // invalidate the def-use manager.
     std::vector<std::pair<opt::Instruction*, uint32_t>> uses;
     GetIRContext()->get_def_use_mgr()->ForEachUse(
         id_with_known_synonyms,
         [&uses](opt::Instruction* use_inst, uint32_t use_index) -> void {
-          uses.emplace_back(
-              std::pair<opt::Instruction*, uint32_t>(use_inst, use_index));
+          // We only gather up regular id uses; e.g. we do not include a use of
+          // the id as the scope for an atomic operation.
+          if (use_inst->GetOperand(use_index).type == SPV_OPERAND_TYPE_ID) {
+            uses.emplace_back(
+                std::pair<opt::Instruction*, uint32_t>(use_inst, use_index));
+          }
         });
 
     for (auto& use : uses) {
@@ -70,8 +83,9 @@
       }
 
       std::vector<const protobufs::DataDescriptor*> synonyms_to_try;
-      for (auto& data_descriptor : GetFactManager()->GetSynonymsForId(
-               id_with_known_synonyms, GetIRContext())) {
+      for (auto& data_descriptor :
+           GetTransformationContext()->GetFactManager()->GetSynonymsForId(
+               id_with_known_synonyms)) {
         protobufs::DataDescriptor descriptor_for_this_id =
             MakeDataDescriptor(id_with_known_synonyms, {});
         if (DataDescriptorEquals()(data_descriptor, &descriptor_for_this_id)) {
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h
index 1a0748e..1a9213d 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h
@@ -27,7 +27,7 @@
 class FuzzerPassApplyIdSynonyms : public FuzzerPass {
  public:
   FuzzerPassApplyIdSynonyms(opt::IRContext* ir_context,
-                            FactManager* fact_manager,
+                            TransformationContext* transformation_context,
                             FuzzerContext* fuzzer_context,
                             protobufs::TransformationSequence* transformations);
 
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 330b9cf..e78f8ec 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
@@ -25,10 +25,11 @@
 namespace fuzz {
 
 FuzzerPassConstructComposites::FuzzerPassConstructComposites(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassConstructComposites::~FuzzerPassConstructComposites() = default;
 
@@ -87,7 +88,7 @@
         // for constructing a composite of that type.  Otherwise these variables
         // will remain 0 and null respectively.
         uint32_t chosen_composite_type = 0;
-        std::unique_ptr<std::vector<uint32_t>> constructor_arguments = nullptr;
+        std::vector<uint32_t> constructor_arguments;
 
         // Initially, all composite type ids are available for us to try.  Keep
         // trying until we run out of options.
@@ -95,35 +96,38 @@
         while (!composites_to_try_constructing.empty()) {
           // Remove a composite type from the composite types left for us to
           // try.
-          auto index =
-              GetFuzzerContext()->RandomIndex(composites_to_try_constructing);
           auto next_composite_to_try_constructing =
-              composites_to_try_constructing[index];
-          composites_to_try_constructing.erase(
-              composites_to_try_constructing.begin() + index);
+              GetFuzzerContext()->RemoveAtRandomIndex(
+                  &composites_to_try_constructing);
 
           // Now try to construct a composite of this type, using an appropriate
           // helper method depending on the kind of composite type.
-          auto composite_type = GetIRContext()->get_type_mgr()->GetType(
+          auto composite_type_inst = GetIRContext()->get_def_use_mgr()->GetDef(
               next_composite_to_try_constructing);
-          if (auto array_type = composite_type->AsArray()) {
-            constructor_arguments = TryConstructingArrayComposite(
-                *array_type, type_id_to_available_instructions);
-          } else if (auto matrix_type = composite_type->AsMatrix()) {
-            constructor_arguments = TryConstructingMatrixComposite(
-                *matrix_type, type_id_to_available_instructions);
-          } else if (auto struct_type = composite_type->AsStruct()) {
-            constructor_arguments = TryConstructingStructComposite(
-                *struct_type, type_id_to_available_instructions);
-          } else {
-            auto vector_type = composite_type->AsVector();
-            assert(vector_type &&
-                   "The space of possible composite types should be covered by "
-                   "the above cases.");
-            constructor_arguments = TryConstructingVectorComposite(
-                *vector_type, type_id_to_available_instructions);
+          switch (composite_type_inst->opcode()) {
+            case SpvOpTypeArray:
+              constructor_arguments = FindComponentsToConstructArray(
+                  *composite_type_inst, type_id_to_available_instructions);
+              break;
+            case SpvOpTypeMatrix:
+              constructor_arguments = FindComponentsToConstructMatrix(
+                  *composite_type_inst, type_id_to_available_instructions);
+              break;
+            case SpvOpTypeStruct:
+              constructor_arguments = FindComponentsToConstructStruct(
+                  *composite_type_inst, type_id_to_available_instructions);
+              break;
+            case SpvOpTypeVector:
+              constructor_arguments = FindComponentsToConstructVector(
+                  *composite_type_inst, type_id_to_available_instructions);
+              break;
+            default:
+              assert(false &&
+                     "The space of possible composite types should be covered "
+                     "by the above cases.");
+              break;
           }
-          if (constructor_arguments != nullptr) {
+          if (!constructor_arguments.empty()) {
             // We succeeded!  Note the composite type we finally settled on, and
             // exit from the loop.
             chosen_composite_type = next_composite_to_try_constructing;
@@ -134,20 +138,15 @@
         if (!chosen_composite_type) {
           // We did not manage to make a composite; return 0 to indicate that no
           // instructions were added.
-          assert(constructor_arguments == nullptr);
+          assert(constructor_arguments.empty());
           return;
         }
-        assert(constructor_arguments != nullptr);
+        assert(!constructor_arguments.empty());
 
         // Make and apply a transformation.
-        TransformationCompositeConstruct transformation(
-            chosen_composite_type, *constructor_arguments,
-            instruction_descriptor, GetFuzzerContext()->GetFreshId());
-        assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
-               "This transformation should be applicable by construction.");
-        transformation.Apply(GetIRContext(), GetFactManager());
-        *GetTransformations()->add_transformation() =
-            transformation.ToMessage();
+        ApplyTransformation(TransformationCompositeConstruct(
+            chosen_composite_type, constructor_arguments,
+            instruction_descriptor, GetFuzzerContext()->GetFreshId()));
       });
 }
 
@@ -160,20 +159,15 @@
   type_id_to_available_instructions->at(inst->type_id()).push_back(inst);
 }
 
-std::unique_ptr<std::vector<uint32_t>>
-FuzzerPassConstructComposites::TryConstructingArrayComposite(
-    const opt::analysis::Array& array_type,
+std::vector<uint32_t>
+FuzzerPassConstructComposites::FindComponentsToConstructArray(
+    const opt::Instruction& array_type_instruction,
     const TypeIdToInstructions& type_id_to_available_instructions) {
-  // At present we assume arrays have a constant size.
-  assert(array_type.length_info().words.size() == 2);
-  assert(array_type.length_info().words[0] ==
-         opt::analysis::Array::LengthInfo::kConstant);
-
-  auto result = MakeUnique<std::vector<uint32_t>>();
+  assert(array_type_instruction.opcode() == SpvOpTypeArray &&
+         "Precondition: instruction must be an array type.");
 
   // Get the element type for the array.
-  auto element_type_id =
-      GetIRContext()->get_type_mgr()->GetId(array_type.element_type());
+  auto element_type_id = array_type_instruction.GetSingleWordInOperand(0);
 
   // Get all instructions at our disposal that compute something of this element
   // type.
@@ -184,26 +178,34 @@
     // If there are not any instructions available that compute the element type
     // of the array then we are not in a position to construct a composite with
     // this array type.
-    return nullptr;
+    return {};
   }
-  for (uint32_t index = 0; index < array_type.length_info().words[1]; index++) {
-    result->push_back(available_instructions
-                          ->second[GetFuzzerContext()->RandomIndex(
-                              available_instructions->second)]
-                          ->result_id());
+
+  uint32_t array_length =
+      GetIRContext()
+          ->get_def_use_mgr()
+          ->GetDef(array_type_instruction.GetSingleWordInOperand(1))
+          ->GetSingleWordInOperand(0);
+
+  std::vector<uint32_t> result;
+  for (uint32_t index = 0; index < array_length; index++) {
+    result.push_back(available_instructions
+                         ->second[GetFuzzerContext()->RandomIndex(
+                             available_instructions->second)]
+                         ->result_id());
   }
   return result;
 }
 
-std::unique_ptr<std::vector<uint32_t>>
-FuzzerPassConstructComposites::TryConstructingMatrixComposite(
-    const opt::analysis::Matrix& matrix_type,
+std::vector<uint32_t>
+FuzzerPassConstructComposites::FindComponentsToConstructMatrix(
+    const opt::Instruction& matrix_type_instruction,
     const TypeIdToInstructions& type_id_to_available_instructions) {
-  auto result = MakeUnique<std::vector<uint32_t>>();
+  assert(matrix_type_instruction.opcode() == SpvOpTypeMatrix &&
+         "Precondition: instruction must be a matrix type.");
 
   // Get the element type for the matrix.
-  auto element_type_id =
-      GetIRContext()->get_type_mgr()->GetId(matrix_type.element_type());
+  auto element_type_id = matrix_type_instruction.GetSingleWordInOperand(0);
 
   // Get all instructions at our disposal that compute something of this element
   // type.
@@ -214,25 +216,32 @@
     // If there are not any instructions available that compute the element type
     // of the matrix then we are not in a position to construct a composite with
     // this matrix type.
-    return nullptr;
+    return {};
   }
-  for (uint32_t index = 0; index < matrix_type.element_count(); index++) {
-    result->push_back(available_instructions
-                          ->second[GetFuzzerContext()->RandomIndex(
-                              available_instructions->second)]
-                          ->result_id());
+  std::vector<uint32_t> result;
+  for (uint32_t index = 0;
+       index < matrix_type_instruction.GetSingleWordInOperand(1); index++) {
+    result.push_back(available_instructions
+                         ->second[GetFuzzerContext()->RandomIndex(
+                             available_instructions->second)]
+                         ->result_id());
   }
   return result;
 }
 
-std::unique_ptr<std::vector<uint32_t>>
-FuzzerPassConstructComposites::TryConstructingStructComposite(
-    const opt::analysis::Struct& struct_type,
+std::vector<uint32_t>
+FuzzerPassConstructComposites::FindComponentsToConstructStruct(
+    const opt::Instruction& struct_type_instruction,
     const TypeIdToInstructions& type_id_to_available_instructions) {
-  auto result = MakeUnique<std::vector<uint32_t>>();
+  assert(struct_type_instruction.opcode() == SpvOpTypeStruct &&
+         "Precondition: instruction must be a struct type.");
+  std::vector<uint32_t> result;
   // Consider the type of each field of the struct.
-  for (auto element_type : struct_type.element_types()) {
-    auto element_type_id = GetIRContext()->get_type_mgr()->GetId(element_type);
+  for (uint32_t in_operand_index = 0;
+       in_operand_index < struct_type_instruction.NumInOperands();
+       in_operand_index++) {
+    auto element_type_id =
+        struct_type_instruction.GetSingleWordInOperand(in_operand_index);
     // Find the instructions at our disposal that compute something of the field
     // type.
     auto available_instructions =
@@ -240,24 +249,28 @@
     if (available_instructions == type_id_to_available_instructions.cend()) {
       // If there are no such instructions, we cannot construct a composite of
       // this struct type.
-      return nullptr;
+      return {};
     }
-    result->push_back(available_instructions
-                          ->second[GetFuzzerContext()->RandomIndex(
-                              available_instructions->second)]
-                          ->result_id());
+    result.push_back(available_instructions
+                         ->second[GetFuzzerContext()->RandomIndex(
+                             available_instructions->second)]
+                         ->result_id());
   }
   return result;
 }
 
-std::unique_ptr<std::vector<uint32_t>>
-FuzzerPassConstructComposites::TryConstructingVectorComposite(
-    const opt::analysis::Vector& vector_type,
+std::vector<uint32_t>
+FuzzerPassConstructComposites::FindComponentsToConstructVector(
+    const opt::Instruction& vector_type_instruction,
     const TypeIdToInstructions& type_id_to_available_instructions) {
+  assert(vector_type_instruction.opcode() == SpvOpTypeVector &&
+         "Precondition: instruction must be a vector type.");
+
   // Get details of the type underlying the vector, and the width of the vector,
   // for convenience.
-  auto element_type = vector_type.element_type();
-  auto element_count = vector_type.element_count();
+  auto element_type_id = vector_type_instruction.GetSingleWordInOperand(0);
+  auto element_type = GetIRContext()->get_type_mgr()->GetType(element_type_id);
+  auto element_count = vector_type_instruction.GetSingleWordInOperand(1);
 
   // Collect a mapping, from type id to width, for scalar/vector types that are
   // smaller in width than |vector_type|, but that have the same underlying
@@ -268,14 +281,12 @@
   std::map<uint32_t, uint32_t> smaller_vector_type_id_to_width;
   // Add the underlying type.  This id must exist, in order for |vector_type| to
   // exist.
-  auto scalar_type_id = GetIRContext()->get_type_mgr()->GetId(element_type);
-  smaller_vector_type_id_to_width[scalar_type_id] = 1;
+  smaller_vector_type_id_to_width[element_type_id] = 1;
 
   // Now add every vector type with width at least 2, and less than the width of
   // |vector_type|.
   for (uint32_t width = 2; width < element_count; width++) {
-    opt::analysis::Vector smaller_vector_type(vector_type.element_type(),
-                                              width);
+    opt::analysis::Vector smaller_vector_type(element_type, width);
     auto smaller_vector_type_id =
         GetIRContext()->get_type_mgr()->GetId(&smaller_vector_type);
     // We might find that there is no declared type of this smaller width.
@@ -302,12 +313,11 @@
   // order at this stage.
   std::vector<opt::Instruction*> instructions_to_use;
 
-  while (vector_slots_used < vector_type.element_count()) {
+  while (vector_slots_used < element_count) {
     std::vector<opt::Instruction*> instructions_to_choose_from;
     for (auto& entry : smaller_vector_type_id_to_width) {
       if (entry.second >
-          std::min(vector_type.element_count() - 1,
-                   vector_type.element_count() - vector_slots_used)) {
+          std::min(element_count - 1, element_count - vector_slots_used)) {
         continue;
       }
       auto available_instructions =
@@ -326,7 +336,7 @@
       // another manner, so we could opt to retry a few times here, but it is
       // simpler to just give up on the basis that this will not happen
       // frequently.
-      return nullptr;
+      return {};
     }
     auto instruction_to_use =
         instructions_to_choose_from[GetFuzzerContext()->RandomIndex(
@@ -345,16 +355,16 @@
       vector_slots_used += 1;
     }
   }
-  assert(vector_slots_used == vector_type.element_count());
+  assert(vector_slots_used == element_count);
 
-  auto result = MakeUnique<std::vector<uint32_t>>();
+  std::vector<uint32_t> result;
   std::vector<uint32_t> operands;
   while (!instructions_to_use.empty()) {
     auto index = GetFuzzerContext()->RandomIndex(instructions_to_use);
-    result->push_back(instructions_to_use[index]->result_id());
+    result.push_back(instructions_to_use[index]->result_id());
     instructions_to_use.erase(instructions_to_use.begin() + index);
   }
-  assert(result->size() > 1);
+  assert(result.size() > 1);
   return result;
 }
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.h
index 99ef31f..9853fad 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.h
@@ -27,7 +27,7 @@
 class FuzzerPassConstructComposites : public FuzzerPass {
  public:
   FuzzerPassConstructComposites(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
@@ -49,27 +49,28 @@
       opt::Instruction* inst,
       TypeIdToInstructions* type_id_to_available_instructions);
 
+  // Requires that |array_type_instruction| has opcode OpTypeArray.
   // Attempts to find suitable instruction result ids from the values of
   // |type_id_to_available_instructions| that would allow a composite of type
-  // |array_type| to be constructed.  Returns said ids if they can be found.
-  // Returns |nullptr| otherwise.
-  std::unique_ptr<std::vector<uint32_t>> TryConstructingArrayComposite(
-      const opt::analysis::Array& array_type,
+  // |array_type_instruction| to be constructed.  Returns said ids if they can
+  // be found and an empty vector otherwise.
+  std::vector<uint32_t> FindComponentsToConstructArray(
+      const opt::Instruction& array_type_instruction,
       const TypeIdToInstructions& type_id_to_available_instructions);
 
-  // Similar to TryConstructingArrayComposite, but for matrices.
-  std::unique_ptr<std::vector<uint32_t>> TryConstructingMatrixComposite(
-      const opt::analysis::Matrix& matrix_type,
+  // Similar to FindComponentsToConstructArray, but for matrices.
+  std::vector<uint32_t> FindComponentsToConstructMatrix(
+      const opt::Instruction& matrix_type_instruction,
       const TypeIdToInstructions& type_id_to_available_instructions);
 
-  // Similar to TryConstructingArrayComposite, but for structs.
-  std::unique_ptr<std::vector<uint32_t>> TryConstructingStructComposite(
-      const opt::analysis::Struct& struct_type,
+  // Similar to FindComponentsToConstructArray, but for structs.
+  std::vector<uint32_t> FindComponentsToConstructStruct(
+      const opt::Instruction& struct_type_instruction,
       const TypeIdToInstructions& type_id_to_available_instructions);
 
-  // Similar to TryConstructingArrayComposite, but for vectors.
-  std::unique_ptr<std::vector<uint32_t>> TryConstructingVectorComposite(
-      const opt::analysis::Vector& vector_type,
+  // Similar to FindComponentsToConstructArray, but for vectors.
+  std::vector<uint32_t> FindComponentsToConstructVector(
+      const opt::Instruction& vector_type_instruction,
       const TypeIdToInstructions& 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 588cfb6..f055b59 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
@@ -21,10 +21,11 @@
 namespace fuzz {
 
 FuzzerPassCopyObjects::FuzzerPassCopyObjects(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.h
index 5419459..8de382e 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.h
@@ -23,7 +23,8 @@
 // A fuzzer pass for adding adding copies of objects to the module.
 class FuzzerPassCopyObjects : public FuzzerPass {
  public:
-  FuzzerPassCopyObjects(opt::IRContext* ir_context, FactManager* fact_manager,
+  FuzzerPassCopyObjects(opt::IRContext* ir_context,
+                        TransformationContext* transformation_context,
                         FuzzerContext* fuzzer_context,
                         protobufs::TransformationSequence* transformations);
 
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 27d8a6e..b907376 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
@@ -22,6 +22,7 @@
 #include "source/fuzz/instruction_message.h"
 #include "source/fuzz/transformation_add_constant_boolean.h"
 #include "source/fuzz/transformation_add_constant_composite.h"
+#include "source/fuzz/transformation_add_constant_null.h"
 #include "source/fuzz/transformation_add_constant_scalar.h"
 #include "source/fuzz/transformation_add_function.h"
 #include "source/fuzz/transformation_add_global_undef.h"
@@ -40,11 +41,12 @@
 namespace fuzz {
 
 FuzzerPassDonateModules::FuzzerPassDonateModules(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations,
     const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations),
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations),
       donor_suppliers_(donor_suppliers) {}
 
 FuzzerPassDonateModules::~FuzzerPassDonateModules() = default;
@@ -62,7 +64,9 @@
     std::unique_ptr<opt::IRContext> donor_ir_context = donor_suppliers_.at(
         GetFuzzerContext()->RandomIndex(donor_suppliers_))();
     assert(donor_ir_context != nullptr && "Supplying of donor failed");
-    assert(fuzzerutil::IsValid(donor_ir_context.get()) &&
+    assert(fuzzerutil::IsValid(
+               donor_ir_context.get(),
+               GetTransformationContext()->GetValidatorOptions()) &&
            "The donor module must be valid");
     // Donate the supplied module.
     //
@@ -112,6 +116,7 @@
   switch (donor_storage_class) {
     case SpvStorageClassFunction:
     case SpvStorageClassPrivate:
+    case SpvStorageClassWorkgroup:
       // We leave these alone
       return donor_storage_class;
     case SpvStorageClassInput:
@@ -119,6 +124,8 @@
     case SpvStorageClassUniform:
     case SpvStorageClassUniformConstant:
     case SpvStorageClassPushConstant:
+    case SpvStorageClassImage:
+    case SpvStorageClassStorageBuffer:
       // We change these to Private
       return SpvStorageClassPrivate;
     default:
@@ -162,284 +169,385 @@
     std::map<uint32_t, uint32_t>* original_id_to_donated_id) {
   // Consider every type/global/constant/undef in the module.
   for (auto& type_or_value : donor_ir_context->module()->types_values()) {
-    // Each such instruction generates a result id, and as part of donation we
-    // need to associate the donor's result id with a new result id.  That new
-    // result id will either be the id of some existing instruction, or a fresh
-    // id.  This variable captures it.
-    uint32_t new_result_id;
+    HandleTypeOrValue(type_or_value, original_id_to_donated_id);
+  }
+}
 
-    // Decide how to handle each kind of instruction on a case-by-case basis.
-    //
-    // Because the donor module is required to be valid, when we encounter a
-    // type comprised of component types (e.g. an aggregate or pointer), we know
-    // that its component types will have been considered previously, and that
-    // |original_id_to_donated_id| will already contain an entry for them.
-    switch (type_or_value.opcode()) {
-      case SpvOpTypeVoid: {
-        // Void has to exist already in order for us to have an entry point.
-        // Get the existing id of void.
-        opt::analysis::Void void_type;
-        new_result_id = GetIRContext()->get_type_mgr()->GetId(&void_type);
-        assert(new_result_id &&
-               "The module being transformed will always have 'void' type "
-               "declared.");
-      } break;
-      case SpvOpTypeBool: {
-        // Bool cannot be declared multiple times, so use its existing id if
-        // present, or add a declaration of Bool with a fresh id if not.
-        opt::analysis::Bool bool_type;
-        auto bool_type_id = GetIRContext()->get_type_mgr()->GetId(&bool_type);
-        if (bool_type_id) {
-          new_result_id = bool_type_id;
-        } else {
-          new_result_id = GetFuzzerContext()->GetFreshId();
-          ApplyTransformation(TransformationAddTypeBoolean(new_result_id));
-        }
-      } break;
-      case SpvOpTypeInt: {
-        // Int cannot be declared multiple times with the same width and
-        // signedness, so check whether an existing identical Int type is
-        // present and use its id if so.  Otherwise add a declaration of the
-        // Int type used by the donor, with a fresh id.
-        const uint32_t width = type_or_value.GetSingleWordInOperand(0);
-        const bool is_signed =
-            static_cast<bool>(type_or_value.GetSingleWordInOperand(1));
-        opt::analysis::Integer int_type(width, is_signed);
-        auto int_type_id = GetIRContext()->get_type_mgr()->GetId(&int_type);
-        if (int_type_id) {
-          new_result_id = int_type_id;
-        } else {
-          new_result_id = GetFuzzerContext()->GetFreshId();
-          ApplyTransformation(
-              TransformationAddTypeInt(new_result_id, width, is_signed));
-        }
-      } break;
-      case SpvOpTypeFloat: {
-        // Similar to SpvOpTypeInt.
-        const uint32_t width = type_or_value.GetSingleWordInOperand(0);
-        opt::analysis::Float float_type(width);
-        auto float_type_id = GetIRContext()->get_type_mgr()->GetId(&float_type);
-        if (float_type_id) {
-          new_result_id = float_type_id;
-        } else {
-          new_result_id = GetFuzzerContext()->GetFreshId();
-          ApplyTransformation(TransformationAddTypeFloat(new_result_id, width));
-        }
-      } break;
-      case SpvOpTypeVector: {
-        // It is not legal to have two Vector type declarations with identical
-        // element types and element counts, so check whether an existing
-        // identical Vector type is present and use its id if so.  Otherwise add
-        // a declaration of the Vector type used by the donor, with a fresh id.
+void FuzzerPassDonateModules::HandleTypeOrValue(
+    const opt::Instruction& type_or_value,
+    std::map<uint32_t, uint32_t>* original_id_to_donated_id) {
+  // The type/value instruction generates a result id, and we need to associate
+  // the donor's result id with a new result id.  That new result id will either
+  // be the id of some existing instruction, or a fresh id.  This variable
+  // captures it.
+  uint32_t new_result_id;
 
-        // When considering the vector's component type id, we look up the id
-        // use in the donor to find the id to which this has been remapped.
-        uint32_t component_type_id = original_id_to_donated_id->at(
-            type_or_value.GetSingleWordInOperand(0));
-        auto component_type =
-            GetIRContext()->get_type_mgr()->GetType(component_type_id);
-        assert(component_type && "The base type should be registered.");
-        auto component_count = type_or_value.GetSingleWordInOperand(1);
-        opt::analysis::Vector vector_type(component_type, component_count);
-        auto vector_type_id =
-            GetIRContext()->get_type_mgr()->GetId(&vector_type);
-        if (vector_type_id) {
-          new_result_id = vector_type_id;
-        } else {
-          new_result_id = GetFuzzerContext()->GetFreshId();
-          ApplyTransformation(TransformationAddTypeVector(
-              new_result_id, component_type_id, component_count));
-        }
-      } break;
-      case SpvOpTypeMatrix: {
-        // Similar to SpvOpTypeVector.
-        uint32_t column_type_id = original_id_to_donated_id->at(
-            type_or_value.GetSingleWordInOperand(0));
-        auto column_type =
-            GetIRContext()->get_type_mgr()->GetType(column_type_id);
-        assert(column_type && column_type->AsVector() &&
-               "The column type should be a registered vector type.");
-        auto column_count = type_or_value.GetSingleWordInOperand(1);
-        opt::analysis::Matrix matrix_type(column_type, column_count);
-        auto matrix_type_id =
-            GetIRContext()->get_type_mgr()->GetId(&matrix_type);
-        if (matrix_type_id) {
-          new_result_id = matrix_type_id;
-        } else {
-          new_result_id = GetFuzzerContext()->GetFreshId();
-          ApplyTransformation(TransformationAddTypeMatrix(
-              new_result_id, column_type_id, column_count));
-        }
-
-      } break;
-      case SpvOpTypeArray: {
-        // It is OK to have multiple structurally identical array types, so
-        // we go ahead and add a remapped version of the type declared by the
-        // donor.
+  // Decide how to handle each kind of instruction on a case-by-case basis.
+  //
+  // Because the donor module is required to be valid, when we encounter a
+  // type comprised of component types (e.g. an aggregate or pointer), we know
+  // that its component types will have been considered previously, and that
+  // |original_id_to_donated_id| will already contain an entry for them.
+  switch (type_or_value.opcode()) {
+    case SpvOpTypeImage:
+    case SpvOpTypeSampledImage:
+    case SpvOpTypeSampler:
+      // We do not donate types and variables that relate to images and
+      // samplers, so we skip these types and subsequently skip anything that
+      // depends on them.
+      return;
+    case SpvOpTypeVoid: {
+      // Void has to exist already in order for us to have an entry point.
+      // Get the existing id of void.
+      opt::analysis::Void void_type;
+      new_result_id = GetIRContext()->get_type_mgr()->GetId(&void_type);
+      assert(new_result_id &&
+             "The module being transformed will always have 'void' type "
+             "declared.");
+    } break;
+    case SpvOpTypeBool: {
+      // Bool cannot be declared multiple times, so use its existing id if
+      // present, or add a declaration of Bool with a fresh id if not.
+      opt::analysis::Bool bool_type;
+      auto bool_type_id = GetIRContext()->get_type_mgr()->GetId(&bool_type);
+      if (bool_type_id) {
+        new_result_id = bool_type_id;
+      } else {
         new_result_id = GetFuzzerContext()->GetFreshId();
-        ApplyTransformation(TransformationAddTypeArray(
+        ApplyTransformation(TransformationAddTypeBoolean(new_result_id));
+      }
+    } break;
+    case SpvOpTypeInt: {
+      // Int cannot be declared multiple times with the same width and
+      // signedness, so check whether an existing identical Int type is
+      // present and use its id if so.  Otherwise add a declaration of the
+      // Int type used by the donor, with a fresh id.
+      const uint32_t width = type_or_value.GetSingleWordInOperand(0);
+      const bool is_signed =
+          static_cast<bool>(type_or_value.GetSingleWordInOperand(1));
+      opt::analysis::Integer int_type(width, is_signed);
+      auto int_type_id = GetIRContext()->get_type_mgr()->GetId(&int_type);
+      if (int_type_id) {
+        new_result_id = int_type_id;
+      } else {
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(
+            TransformationAddTypeInt(new_result_id, width, is_signed));
+      }
+    } break;
+    case SpvOpTypeFloat: {
+      // Similar to SpvOpTypeInt.
+      const uint32_t width = type_or_value.GetSingleWordInOperand(0);
+      opt::analysis::Float float_type(width);
+      auto float_type_id = GetIRContext()->get_type_mgr()->GetId(&float_type);
+      if (float_type_id) {
+        new_result_id = float_type_id;
+      } else {
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(TransformationAddTypeFloat(new_result_id, width));
+      }
+    } break;
+    case SpvOpTypeVector: {
+      // It is not legal to have two Vector type declarations with identical
+      // element types and element counts, so check whether an existing
+      // identical Vector type is present and use its id if so.  Otherwise add
+      // a declaration of the Vector type used by the donor, with a fresh id.
+
+      // When considering the vector's component type id, we look up the id
+      // use in the donor to find the id to which this has been remapped.
+      uint32_t component_type_id = original_id_to_donated_id->at(
+          type_or_value.GetSingleWordInOperand(0));
+      auto component_type =
+          GetIRContext()->get_type_mgr()->GetType(component_type_id);
+      assert(component_type && "The base type should be registered.");
+      auto component_count = type_or_value.GetSingleWordInOperand(1);
+      opt::analysis::Vector vector_type(component_type, component_count);
+      auto vector_type_id = GetIRContext()->get_type_mgr()->GetId(&vector_type);
+      if (vector_type_id) {
+        new_result_id = vector_type_id;
+      } else {
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(TransformationAddTypeVector(
+            new_result_id, component_type_id, component_count));
+      }
+    } break;
+    case SpvOpTypeMatrix: {
+      // Similar to SpvOpTypeVector.
+      uint32_t column_type_id = original_id_to_donated_id->at(
+          type_or_value.GetSingleWordInOperand(0));
+      auto column_type =
+          GetIRContext()->get_type_mgr()->GetType(column_type_id);
+      assert(column_type && column_type->AsVector() &&
+             "The column type should be a registered vector type.");
+      auto column_count = type_or_value.GetSingleWordInOperand(1);
+      opt::analysis::Matrix matrix_type(column_type, column_count);
+      auto matrix_type_id = GetIRContext()->get_type_mgr()->GetId(&matrix_type);
+      if (matrix_type_id) {
+        new_result_id = matrix_type_id;
+      } else {
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(TransformationAddTypeMatrix(
+            new_result_id, column_type_id, column_count));
+      }
+
+    } break;
+    case SpvOpTypeArray: {
+      // It is OK to have multiple structurally identical array types, so
+      // we go ahead and add a remapped version of the type declared by the
+      // donor.
+      uint32_t component_type_id = type_or_value.GetSingleWordInOperand(0);
+      if (!original_id_to_donated_id->count(component_type_id)) {
+        // We did not donate the component type of this array type, so we
+        // cannot donate the array type.
+        return;
+      }
+      new_result_id = GetFuzzerContext()->GetFreshId();
+      ApplyTransformation(TransformationAddTypeArray(
+          new_result_id, original_id_to_donated_id->at(component_type_id),
+          original_id_to_donated_id->at(
+              type_or_value.GetSingleWordInOperand(1))));
+    } break;
+    case SpvOpTypeRuntimeArray: {
+      // A runtime array is allowed as the final member of an SSBO.  During
+      // donation we turn runtime arrays into fixed-size arrays.  For dead
+      // code donations this is OK because the array is never indexed into at
+      // runtime, so it does not matter what its size is.  For live-safe code,
+      // all accesses are made in-bounds, so this is also OK.
+      //
+      // The special OpArrayLength instruction, which works on runtime arrays,
+      // is rewritten to yield the fixed length that is used for the array.
+
+      uint32_t component_type_id = type_or_value.GetSingleWordInOperand(0);
+      if (!original_id_to_donated_id->count(component_type_id)) {
+        // We did not donate the component type of this runtime array type, so
+        // we cannot donate it as a fixed-size array.
+        return;
+      }
+      new_result_id = GetFuzzerContext()->GetFreshId();
+      ApplyTransformation(TransformationAddTypeArray(
+          new_result_id, original_id_to_donated_id->at(component_type_id),
+          FindOrCreate32BitIntegerConstant(
+              GetFuzzerContext()->GetRandomSizeForNewArray(), false)));
+    } break;
+    case SpvOpTypeStruct: {
+      // Similar to SpvOpTypeArray.
+      std::vector<uint32_t> member_type_ids;
+      for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) {
+        auto component_type_id = type_or_value.GetSingleWordInOperand(i);
+        if (!original_id_to_donated_id->count(component_type_id)) {
+          // We did not donate every member type for this struct type, so we
+          // cannot donate the struct type.
+          return;
+        }
+        member_type_ids.push_back(
+            original_id_to_donated_id->at(component_type_id));
+      }
+      new_result_id = GetFuzzerContext()->GetFreshId();
+      ApplyTransformation(
+          TransformationAddTypeStruct(new_result_id, member_type_ids));
+    } break;
+    case SpvOpTypePointer: {
+      // Similar to SpvOpTypeArray.
+      uint32_t pointee_type_id = type_or_value.GetSingleWordInOperand(1);
+      if (!original_id_to_donated_id->count(pointee_type_id)) {
+        // We did not donate the pointee type for this pointer type, so we
+        // cannot donate the pointer type.
+        return;
+      }
+      new_result_id = GetFuzzerContext()->GetFreshId();
+      ApplyTransformation(TransformationAddTypePointer(
+          new_result_id,
+          AdaptStorageClass(static_cast<SpvStorageClass>(
+              type_or_value.GetSingleWordInOperand(0))),
+          original_id_to_donated_id->at(pointee_type_id)));
+    } break;
+    case SpvOpTypeFunction: {
+      // It is not OK to have multiple function types that use identical ids
+      // for their return and parameter types.  We thus go through all
+      // existing function types to look for a match.  We do not use the
+      // type manager here because we want to regard two function types that
+      // are structurally identical but that differ with respect to the
+      // actual ids used for pointer types as different.
+      //
+      // Example:
+      //
+      // %1 = OpTypeVoid
+      // %2 = OpTypeInt 32 0
+      // %3 = OpTypePointer Function %2
+      // %4 = OpTypePointer Function %2
+      // %5 = OpTypeFunction %1 %3
+      // %6 = OpTypeFunction %1 %4
+      //
+      // We regard %5 and %6 as distinct function types here, even though
+      // they both have the form "uint32* -> void"
+
+      std::vector<uint32_t> return_and_parameter_types;
+      for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) {
+        uint32_t return_or_parameter_type =
+            type_or_value.GetSingleWordInOperand(i);
+        if (!original_id_to_donated_id->count(return_or_parameter_type)) {
+          // We did not donate every return/parameter type for this function
+          // type, so we cannot donate the function type.
+          return;
+        }
+        return_and_parameter_types.push_back(
+            original_id_to_donated_id->at(return_or_parameter_type));
+      }
+      uint32_t existing_function_id = fuzzerutil::FindFunctionType(
+          GetIRContext(), return_and_parameter_types);
+      if (existing_function_id) {
+        new_result_id = existing_function_id;
+      } else {
+        // No match was found, so add a remapped version of the function type
+        // to the module, with a fresh id.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        std::vector<uint32_t> argument_type_ids;
+        for (uint32_t i = 1; i < type_or_value.NumInOperands(); i++) {
+          argument_type_ids.push_back(original_id_to_donated_id->at(
+              type_or_value.GetSingleWordInOperand(i)));
+        }
+        ApplyTransformation(TransformationAddTypeFunction(
             new_result_id,
             original_id_to_donated_id->at(
                 type_or_value.GetSingleWordInOperand(0)),
-            original_id_to_donated_id->at(
-                type_or_value.GetSingleWordInOperand(1))));
-      } break;
-      case SpvOpTypeStruct: {
-        // Similar to SpvOpTypeArray.
-        new_result_id = GetFuzzerContext()->GetFreshId();
-        std::vector<uint32_t> member_type_ids;
-        type_or_value.ForEachInId(
-            [&member_type_ids,
-             &original_id_to_donated_id](const uint32_t* component_type_id) {
-              member_type_ids.push_back(
-                  original_id_to_donated_id->at(*component_type_id));
-            });
-        ApplyTransformation(
-            TransformationAddTypeStruct(new_result_id, member_type_ids));
-      } break;
-      case SpvOpTypePointer: {
-        // Similar to SpvOpTypeArray.
-        new_result_id = GetFuzzerContext()->GetFreshId();
-        ApplyTransformation(TransformationAddTypePointer(
-            new_result_id,
-            AdaptStorageClass(static_cast<SpvStorageClass>(
-                type_or_value.GetSingleWordInOperand(0))),
-            original_id_to_donated_id->at(
-                type_or_value.GetSingleWordInOperand(1))));
-      } break;
-      case SpvOpTypeFunction: {
-        // It is not OK to have multiple function types that use identical ids
-        // for their return and parameter types.  We thus go through all
-        // existing function types to look for a match.  We do not use the
-        // type manager here because we want to regard two function types that
-        // are structurally identical but that differ with respect to the
-        // actual ids used for pointer types as different.
-        //
-        // Example:
-        //
-        // %1 = OpTypeVoid
-        // %2 = OpTypeInt 32 0
-        // %3 = OpTypePointer Function %2
-        // %4 = OpTypePointer Function %2
-        // %5 = OpTypeFunction %1 %3
-        // %6 = OpTypeFunction %1 %4
-        //
-        // We regard %5 and %6 as distinct function types here, even though
-        // they both have the form "uint32* -> void"
+            argument_type_ids));
+      }
+    } break;
+    case SpvOpConstantTrue:
+    case SpvOpConstantFalse: {
+      // It is OK to have duplicate definitions of True and False, so add
+      // these to the module, using a remapped Bool type.
+      new_result_id = GetFuzzerContext()->GetFreshId();
+      ApplyTransformation(TransformationAddConstantBoolean(
+          new_result_id, type_or_value.opcode() == SpvOpConstantTrue));
+    } break;
+    case SpvOpConstant: {
+      // It is OK to have duplicate constant definitions, so add this to the
+      // module using a remapped result type.
+      new_result_id = GetFuzzerContext()->GetFreshId();
+      std::vector<uint32_t> data_words;
+      type_or_value.ForEachInOperand([&data_words](const uint32_t* in_operand) {
+        data_words.push_back(*in_operand);
+      });
+      ApplyTransformation(TransformationAddConstantScalar(
+          new_result_id, original_id_to_donated_id->at(type_or_value.type_id()),
+          data_words));
+    } break;
+    case SpvOpConstantComposite: {
+      assert(original_id_to_donated_id->count(type_or_value.type_id()) &&
+             "Composite types for which it is possible to create a constant "
+             "should have been donated.");
 
-        std::vector<uint32_t> return_and_parameter_types;
-        for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) {
-          return_and_parameter_types.push_back(original_id_to_donated_id->at(
-              type_or_value.GetSingleWordInOperand(i)));
-        }
-        uint32_t existing_function_id = fuzzerutil::FindFunctionType(
-            GetIRContext(), return_and_parameter_types);
-        if (existing_function_id) {
-          new_result_id = existing_function_id;
-        } else {
-          // No match was found, so add a remapped version of the function type
-          // to the module, with a fresh id.
-          new_result_id = GetFuzzerContext()->GetFreshId();
-          std::vector<uint32_t> argument_type_ids;
-          for (uint32_t i = 1; i < type_or_value.NumInOperands(); i++) {
-            argument_type_ids.push_back(original_id_to_donated_id->at(
-                type_or_value.GetSingleWordInOperand(i)));
-          }
-          ApplyTransformation(TransformationAddTypeFunction(
-              new_result_id,
-              original_id_to_donated_id->at(
-                  type_or_value.GetSingleWordInOperand(0)),
-              argument_type_ids));
-        }
-      } break;
-      case SpvOpConstantTrue:
-      case SpvOpConstantFalse: {
-        // It is OK to have duplicate definitions of True and False, so add
-        // these to the module, using a remapped Bool type.
-        new_result_id = GetFuzzerContext()->GetFreshId();
-        ApplyTransformation(TransformationAddConstantBoolean(
-            new_result_id, type_or_value.opcode() == SpvOpConstantTrue));
-      } break;
-      case SpvOpConstant: {
-        // It is OK to have duplicate constant definitions, so add this to the
-        // module using a remapped result type.
-        new_result_id = GetFuzzerContext()->GetFreshId();
-        std::vector<uint32_t> data_words;
-        type_or_value.ForEachInOperand(
-            [&data_words](const uint32_t* in_operand) {
-              data_words.push_back(*in_operand);
-            });
-        ApplyTransformation(TransformationAddConstantScalar(
-            new_result_id,
-            original_id_to_donated_id->at(type_or_value.type_id()),
-            data_words));
-      } break;
-      case SpvOpConstantComposite: {
-        // It is OK to have duplicate constant composite definitions, so add
-        // this to the module using remapped versions of all consituent ids and
-        // the result type.
-        new_result_id = GetFuzzerContext()->GetFreshId();
-        std::vector<uint32_t> constituent_ids;
-        type_or_value.ForEachInId(
-            [&constituent_ids,
-             &original_id_to_donated_id](const uint32_t* constituent_id) {
-              constituent_ids.push_back(
-                  original_id_to_donated_id->at(*constituent_id));
-            });
-        ApplyTransformation(TransformationAddConstantComposite(
-            new_result_id,
-            original_id_to_donated_id->at(type_or_value.type_id()),
-            constituent_ids));
-      } break;
-      case SpvOpVariable: {
-        // This is a global variable that could have one of various storage
-        // classes.  However, we change all global variable pointer storage
-        // classes (such as Uniform, Input and Output) to private when donating
-        // pointer types.  Thus this variable's pointer type is guaranteed to
-        // have storage class private.  As a result, we simply add a Private
-        // storage class global variable, using remapped versions of the result
-        // type and initializer ids for the global variable in the donor.
-        //
-        // We regard the added variable as having an irrelevant value.  This
-        // means that future passes can add stores to the variable in any
-        // way they wish, and pass them as pointer parameters to functions
-        // without worrying about whether their data might get modified.
-        new_result_id = GetFuzzerContext()->GetFreshId();
-        uint32_t remapped_pointer_type =
-            original_id_to_donated_id->at(type_or_value.type_id());
-        uint32_t initializer_id;
-        if (type_or_value.NumInOperands() == 1) {
-          // The variable did not have an initializer; initialize it to zero.
-          // This is to limit problems associated with uninitialized data.
-          initializer_id = FindOrCreateZeroConstant(
-              fuzzerutil::GetPointeeTypeIdFromPointerType(
-                  GetIRContext(), remapped_pointer_type));
-        } else {
-          // The variable already had an initializer; use its remapped id.
-          initializer_id = original_id_to_donated_id->at(
-              type_or_value.GetSingleWordInOperand(1));
-        }
-        ApplyTransformation(TransformationAddGlobalVariable(
-            new_result_id, remapped_pointer_type, initializer_id, true));
-      } break;
-      case SpvOpUndef: {
-        // It is fine to have multiple Undef instructions of the same type, so
-        // we just add this to the recipient module.
-        new_result_id = GetFuzzerContext()->GetFreshId();
-        ApplyTransformation(TransformationAddGlobalUndef(
-            new_result_id,
-            original_id_to_donated_id->at(type_or_value.type_id())));
-      } break;
-      default: {
-        assert(0 && "Unknown type/value.");
-        new_result_id = 0;
-      } break;
-    }
-    // Update the id mapping to associate the instruction's result id with its
-    // corresponding id in the recipient.
-    original_id_to_donated_id->insert(
-        {type_or_value.result_id(), new_result_id});
+      // It is OK to have duplicate constant composite definitions, so add
+      // this to the module using remapped versions of all consituent ids and
+      // the result type.
+      new_result_id = GetFuzzerContext()->GetFreshId();
+      std::vector<uint32_t> constituent_ids;
+      type_or_value.ForEachInId([&constituent_ids, &original_id_to_donated_id](
+                                    const uint32_t* constituent_id) {
+        assert(original_id_to_donated_id->count(*constituent_id) &&
+               "The constants used to construct this composite should "
+               "have been donated.");
+        constituent_ids.push_back(
+            original_id_to_donated_id->at(*constituent_id));
+      });
+      ApplyTransformation(TransformationAddConstantComposite(
+          new_result_id, original_id_to_donated_id->at(type_or_value.type_id()),
+          constituent_ids));
+    } break;
+    case SpvOpConstantNull: {
+      if (!original_id_to_donated_id->count(type_or_value.type_id())) {
+        // We did not donate the type associated with this null constant, so
+        // we cannot donate the null constant.
+        return;
+      }
+
+      // It is fine to have multiple OpConstantNull instructions of the same
+      // type, so we just add this to the recipient module.
+      new_result_id = GetFuzzerContext()->GetFreshId();
+      ApplyTransformation(TransformationAddConstantNull(
+          new_result_id,
+          original_id_to_donated_id->at(type_or_value.type_id())));
+    } break;
+    case SpvOpVariable: {
+      if (!original_id_to_donated_id->count(type_or_value.type_id())) {
+        // We did not donate the pointer type associated with this variable,
+        // so we cannot donate the variable.
+        return;
+      }
+
+      // This is a global variable that could have one of various storage
+      // classes.  However, we change all global variable pointer storage
+      // classes (such as Uniform, Input and Output) to private when donating
+      // pointer types, with the exception of the Workgroup storage class.
+      //
+      // Thus this variable's pointer type is guaranteed to have storage class
+      // Private or Workgroup.
+      //
+      // We add a global variable with either Private or Workgroup storage
+      // class, using remapped versions of the result type and initializer ids
+      // for the global variable in the donor.
+      //
+      // We regard the added variable as having an irrelevant value.  This
+      // means that future passes can add stores to the variable in any
+      // way they wish, and pass them as pointer parameters to functions
+      // without worrying about whether their data might get modified.
+      new_result_id = GetFuzzerContext()->GetFreshId();
+      uint32_t remapped_pointer_type =
+          original_id_to_donated_id->at(type_or_value.type_id());
+      uint32_t initializer_id;
+      SpvStorageClass storage_class =
+          static_cast<SpvStorageClass>(type_or_value.GetSingleWordInOperand(
+              0)) == SpvStorageClassWorkgroup
+              ? SpvStorageClassWorkgroup
+              : SpvStorageClassPrivate;
+      if (type_or_value.NumInOperands() == 1) {
+        // The variable did not have an initializer.  Initialize it to zero
+        // if it has Private storage class (to limit problems associated with
+        // uninitialized data), and leave it uninitialized if it has Workgroup
+        // storage class (as Workgroup variables cannot have initializers).
+
+        // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3275): we
+        //  could initialize Workgroup variables at the start of an entry
+        //  point, and should do so if their uninitialized nature proves
+        //  problematic.
+        initializer_id = storage_class == SpvStorageClassWorkgroup
+                             ? 0
+                             : FindOrCreateZeroConstant(
+                                   fuzzerutil::GetPointeeTypeIdFromPointerType(
+                                       GetIRContext(), remapped_pointer_type));
+      } else {
+        // The variable already had an initializer; use its remapped id.
+        initializer_id = original_id_to_donated_id->at(
+            type_or_value.GetSingleWordInOperand(1));
+      }
+      ApplyTransformation(
+          TransformationAddGlobalVariable(new_result_id, remapped_pointer_type,
+                                          storage_class, initializer_id, true));
+    } break;
+    case SpvOpUndef: {
+      if (!original_id_to_donated_id->count(type_or_value.type_id())) {
+        // We did not donate the type associated with this undef, so we cannot
+        // donate the undef.
+        return;
+      }
+
+      // It is fine to have multiple Undef instructions of the same type, so
+      // we just add this to the recipient module.
+      new_result_id = GetFuzzerContext()->GetFreshId();
+      ApplyTransformation(TransformationAddGlobalUndef(
+          new_result_id,
+          original_id_to_donated_id->at(type_or_value.type_id())));
+    } break;
+    default: {
+      assert(0 && "Unknown type/value.");
+      new_result_id = 0;
+    } break;
   }
+
+  // Update the id mapping to associate the instruction's result id with its
+  // corresponding id in the recipient.
+  original_id_to_donated_id->insert({type_or_value.result_id(), new_result_id});
 }
 
 void FuzzerPassDonateModules::HandleFunctions(
@@ -468,220 +576,49 @@
     }
     assert(function_to_donate && "Function to be donated was not found.");
 
+    if (!original_id_to_donated_id->count(
+            function_to_donate->DefInst().GetSingleWordInOperand(1))) {
+      // We were not able to donate this function's type, so we cannot donate
+      // the function.
+      continue;
+    }
+
     // We will collect up protobuf messages representing the donor function's
     // instructions here, and use them to create an AddFunction transformation.
     std::vector<protobufs::Instruction> donated_instructions;
 
-    // Scan through the function, remapping each result id that it generates to
-    // a fresh id.  This is necessary because functions include forward
-    // references, e.g. to labels.
-    function_to_donate->ForEachInst([this, &original_id_to_donated_id](
-                                        const opt::Instruction* instruction) {
-      if (instruction->result_id()) {
-        original_id_to_donated_id->insert(
-            {instruction->result_id(), GetFuzzerContext()->GetFreshId()});
-      }
-    });
+    // This set tracks the ids of those instructions for which donation was
+    // completely skipped: neither the instruction nor a substitute for it was
+    // donated.
+    std::set<uint32_t> skipped_instructions;
 
     // Consider every instruction of the donor function.
-    function_to_donate->ForEachInst([this, &donated_instructions,
-                                     &original_id_to_donated_id](
-                                        const opt::Instruction* instruction) {
-      // Get the instruction's input operands into donation-ready form,
-      // remapping any id uses in the process.
-      opt::Instruction::OperandList input_operands;
-
-      // Consider each input operand in turn.
-      for (uint32_t in_operand_index = 0;
-           in_operand_index < instruction->NumInOperands();
-           in_operand_index++) {
-        std::vector<uint32_t> operand_data;
-        const opt::Operand& in_operand =
-            instruction->GetInOperand(in_operand_index);
-        switch (in_operand.type) {
-          case SPV_OPERAND_TYPE_ID:
-          case SPV_OPERAND_TYPE_TYPE_ID:
-          case SPV_OPERAND_TYPE_RESULT_ID:
-          case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
-          case SPV_OPERAND_TYPE_SCOPE_ID:
-            // This is an id operand - it consists of a single word of data,
-            // which needs to be remapped so that it is replaced with the
-            // donated form of the id.
-            operand_data.push_back(
-                original_id_to_donated_id->at(in_operand.words[0]));
-            break;
-          default:
-            // For non-id operands, we just add each of the data words.
-            for (auto word : in_operand.words) {
-              operand_data.push_back(word);
-            }
-            break;
-        }
-        input_operands.push_back({in_operand.type, operand_data});
-      }
-
-      if (instruction->opcode() == SpvOpVariable &&
-          instruction->NumInOperands() == 1) {
-        // This is an uninitialized local variable.  Initialize it to zero.
-        input_operands.push_back(
-            {SPV_OPERAND_TYPE_ID,
-             {FindOrCreateZeroConstant(
-                 fuzzerutil::GetPointeeTypeIdFromPointerType(
-                     GetIRContext(),
-                     original_id_to_donated_id->at(instruction->type_id())))}});
-      }
-
-      // Remap the result type and result id (if present) of the
-      // instruction, and turn it into a protobuf message.
-      donated_instructions.push_back(MakeInstructionMessage(
-          instruction->opcode(),
-          instruction->type_id()
-              ? original_id_to_donated_id->at(instruction->type_id())
-              : 0,
-          instruction->result_id()
-              ? original_id_to_donated_id->at(instruction->result_id())
-              : 0,
-          input_operands));
-    });
+    function_to_donate->ForEachInst(
+        [this, &donated_instructions, donor_ir_context,
+         &original_id_to_donated_id,
+         &skipped_instructions](const opt::Instruction* instruction) {
+          if (instruction->opcode() == SpvOpArrayLength) {
+            // We treat OpArrayLength specially.
+            HandleOpArrayLength(*instruction, original_id_to_donated_id,
+                                &donated_instructions);
+          } else if (!CanDonateInstruction(donor_ir_context, *instruction,
+                                           *original_id_to_donated_id,
+                                           skipped_instructions)) {
+            // This is an instruction that we cannot directly donate.
+            HandleDifficultInstruction(*instruction, original_id_to_donated_id,
+                                       &donated_instructions,
+                                       &skipped_instructions);
+          } else {
+            PrepareInstructionForDonation(*instruction, donor_ir_context,
+                                          original_id_to_donated_id,
+                                          &donated_instructions);
+          }
+        });
 
     if (make_livesafe) {
-      // Various types and constants must be in place for a function to be made
-      // live-safe.  Add them if not already present.
-      FindOrCreateBoolType();  // Needed for comparisons
-      FindOrCreatePointerTo32BitIntegerType(
-          false, SpvStorageClassFunction);  // Needed for adding loop limiters
-      FindOrCreate32BitIntegerConstant(
-          0, false);  // Needed for initializing loop limiters
-      FindOrCreate32BitIntegerConstant(
-          1, false);  // Needed for incrementing loop limiters
-
-      // Get a fresh id for the variable that will be used as a loop limiter.
-      const uint32_t loop_limiter_variable_id =
-          GetFuzzerContext()->GetFreshId();
-      // Choose a random loop limit, and add the required constant to the
-      // module if not already there.
-      const uint32_t loop_limit = FindOrCreate32BitIntegerConstant(
-          GetFuzzerContext()->GetRandomLoopLimit(), false);
-
-      // Consider every loop header in the function to donate, and create a
-      // structure capturing the ids to be used for manipulating the loop
-      // limiter each time the loop is iterated.
-      std::vector<protobufs::LoopLimiterInfo> loop_limiters;
-      for (auto& block : *function_to_donate) {
-        if (block.IsLoopHeader()) {
-          protobufs::LoopLimiterInfo loop_limiter;
-          // Grab the loop header's id, mapped to its donated value.
-          loop_limiter.set_loop_header_id(
-              original_id_to_donated_id->at(block.id()));
-          // Get fresh ids that will be used to load the loop limiter, increment
-          // it, compare it with the loop limit, and an id for a new block that
-          // will contain the loop's original terminator.
-          loop_limiter.set_load_id(GetFuzzerContext()->GetFreshId());
-          loop_limiter.set_increment_id(GetFuzzerContext()->GetFreshId());
-          loop_limiter.set_compare_id(GetFuzzerContext()->GetFreshId());
-          loop_limiter.set_logical_op_id(GetFuzzerContext()->GetFreshId());
-          loop_limiters.emplace_back(loop_limiter);
-        }
-      }
-
-      // Consider every access chain in the function to donate, and create a
-      // structure containing the ids necessary to clamp the access chain
-      // indices to be in-bounds.
-      std::vector<protobufs::AccessChainClampingInfo>
-          access_chain_clamping_info;
-      for (auto& block : *function_to_donate) {
-        for (auto& inst : block) {
-          switch (inst.opcode()) {
-            case SpvOpAccessChain:
-            case SpvOpInBoundsAccessChain: {
-              protobufs::AccessChainClampingInfo clamping_info;
-              clamping_info.set_access_chain_id(
-                  original_id_to_donated_id->at(inst.result_id()));
-
-              auto base_object = donor_ir_context->get_def_use_mgr()->GetDef(
-                  inst.GetSingleWordInOperand(0));
-              assert(base_object && "The base object must exist.");
-              auto pointer_type = donor_ir_context->get_def_use_mgr()->GetDef(
-                  base_object->type_id());
-              assert(pointer_type &&
-                     pointer_type->opcode() == SpvOpTypePointer &&
-                     "The base object must have pointer type.");
-
-              auto should_be_composite_type =
-                  donor_ir_context->get_def_use_mgr()->GetDef(
-                      pointer_type->GetSingleWordInOperand(1));
-
-              // Walk the access chain, creating fresh ids to facilitate
-              // clamping each index.  For simplicity we do this for every
-              // index, even though constant indices will not end up being
-              // clamped.
-              for (uint32_t index = 1; index < inst.NumInOperands(); index++) {
-                auto compare_and_select_ids =
-                    clamping_info.add_compare_and_select_ids();
-                compare_and_select_ids->set_first(
-                    GetFuzzerContext()->GetFreshId());
-                compare_and_select_ids->set_second(
-                    GetFuzzerContext()->GetFreshId());
-
-                // Get the bound for the component being indexed into.
-                uint32_t bound =
-                    TransformationAddFunction::GetBoundForCompositeIndex(
-                        donor_ir_context, *should_be_composite_type);
-                const uint32_t index_id = inst.GetSingleWordInOperand(index);
-                auto index_inst =
-                    donor_ir_context->get_def_use_mgr()->GetDef(index_id);
-                auto index_type_inst =
-                    donor_ir_context->get_def_use_mgr()->GetDef(
-                        index_inst->type_id());
-                assert(index_type_inst->opcode() == SpvOpTypeInt);
-                assert(index_type_inst->GetSingleWordInOperand(0) == 32);
-                opt::analysis::Integer* index_int_type =
-                    donor_ir_context->get_type_mgr()
-                        ->GetType(index_type_inst->result_id())
-                        ->AsInteger();
-                if (index_inst->opcode() != SpvOpConstant) {
-                  // We will have to clamp this index, so we need a constant
-                  // whose value is one less than the bound, to compare
-                  // against and to use as the clamped value.
-                  FindOrCreate32BitIntegerConstant(bound - 1,
-                                                   index_int_type->IsSigned());
-                }
-                should_be_composite_type =
-                    TransformationAddFunction::FollowCompositeIndex(
-                        donor_ir_context, *should_be_composite_type, index_id);
-              }
-              access_chain_clamping_info.push_back(clamping_info);
-              break;
-            }
-            default:
-              break;
-          }
-        }
-      }
-
-      // If the function contains OpKill or OpUnreachable instructions, and has
-      // non-void return type, then we need a value %v to use in order to turn
-      // these into instructions of the form OpReturn %v.
-      uint32_t kill_unreachable_return_value_id;
-      auto function_return_type_inst =
-          donor_ir_context->get_def_use_mgr()->GetDef(
-              function_to_donate->type_id());
-      if (function_return_type_inst->opcode() == SpvOpTypeVoid) {
-        // The return type is void, so we don't need a return value.
-        kill_unreachable_return_value_id = 0;
-      } else {
-        // We do need a return value; we use zero.
-        assert(function_return_type_inst->opcode() != SpvOpTypePointer &&
-               "Function return type must not be a pointer.");
-        kill_unreachable_return_value_id =
-            FindOrCreateZeroConstant(original_id_to_donated_id->at(
-                function_return_type_inst->result_id()));
-      }
-      // Add the function in a livesafe manner.
-      ApplyTransformation(TransformationAddFunction(
-          donated_instructions, loop_limiter_variable_id, loop_limit,
-          loop_limiters, kill_unreachable_return_value_id,
-          access_chain_clamping_info));
+      // Make the function livesafe and then add it.
+      AddLivesafeFunction(*function_to_donate, donor_ir_context,
+                          *original_id_to_donated_id, donated_instructions);
     } else {
       // Add the function in a non-livesafe manner.
       ApplyTransformation(TransformationAddFunction(donated_instructions));
@@ -689,6 +626,133 @@
   }
 }
 
+bool FuzzerPassDonateModules::CanDonateInstruction(
+    opt::IRContext* donor_ir_context, const opt::Instruction& instruction,
+    const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
+    const std::set<uint32_t>& skipped_instructions) const {
+  if (instruction.type_id() &&
+      !original_id_to_donated_id.count(instruction.type_id())) {
+    // We could not donate the result type of this instruction, so we cannot
+    // donate the instruction.
+    return false;
+  }
+
+  // Now consider instructions we specifically want to skip because we do not
+  // yet support them.
+  switch (instruction.opcode()) {
+    case SpvOpAtomicLoad:
+    case SpvOpAtomicStore:
+    case SpvOpAtomicExchange:
+    case SpvOpAtomicCompareExchange:
+    case SpvOpAtomicCompareExchangeWeak:
+    case SpvOpAtomicIIncrement:
+    case SpvOpAtomicIDecrement:
+    case SpvOpAtomicIAdd:
+    case SpvOpAtomicISub:
+    case SpvOpAtomicSMin:
+    case SpvOpAtomicUMin:
+    case SpvOpAtomicSMax:
+    case SpvOpAtomicUMax:
+    case SpvOpAtomicAnd:
+    case SpvOpAtomicOr:
+    case SpvOpAtomicXor:
+      // We conservatively ignore all atomic instructions at present.
+      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3276): Consider
+      //  being less conservative here.
+    case SpvOpImageSampleImplicitLod:
+    case SpvOpImageSampleExplicitLod:
+    case SpvOpImageSampleDrefImplicitLod:
+    case SpvOpImageSampleDrefExplicitLod:
+    case SpvOpImageSampleProjImplicitLod:
+    case SpvOpImageSampleProjExplicitLod:
+    case SpvOpImageSampleProjDrefImplicitLod:
+    case SpvOpImageSampleProjDrefExplicitLod:
+    case SpvOpImageFetch:
+    case SpvOpImageGather:
+    case SpvOpImageDrefGather:
+    case SpvOpImageRead:
+    case SpvOpImageWrite:
+    case SpvOpImageSparseSampleImplicitLod:
+    case SpvOpImageSparseSampleExplicitLod:
+    case SpvOpImageSparseSampleDrefImplicitLod:
+    case SpvOpImageSparseSampleDrefExplicitLod:
+    case SpvOpImageSparseSampleProjImplicitLod:
+    case SpvOpImageSparseSampleProjExplicitLod:
+    case SpvOpImageSparseSampleProjDrefImplicitLod:
+    case SpvOpImageSparseSampleProjDrefExplicitLod:
+    case SpvOpImageSparseFetch:
+    case SpvOpImageSparseGather:
+    case SpvOpImageSparseDrefGather:
+    case SpvOpImageSparseRead:
+    case SpvOpImageSampleFootprintNV:
+    case SpvOpImage:
+    case SpvOpImageQueryFormat:
+    case SpvOpImageQueryLevels:
+    case SpvOpImageQueryLod:
+    case SpvOpImageQueryOrder:
+    case SpvOpImageQuerySamples:
+    case SpvOpImageQuerySize:
+    case SpvOpImageQuerySizeLod:
+    case SpvOpSampledImage:
+      // We ignore all instructions related to accessing images, since we do not
+      // donate images.
+      return false;
+    case SpvOpLoad:
+      switch (donor_ir_context->get_def_use_mgr()
+                  ->GetDef(instruction.type_id())
+                  ->opcode()) {
+        case SpvOpTypeImage:
+        case SpvOpTypeSampledImage:
+        case SpvOpTypeSampler:
+          // Again, we ignore instructions that relate to accessing images.
+          return false;
+        default:
+          break;
+      }
+    default:
+      break;
+  }
+
+  // Examine each id input operand to the instruction.  If it turns out that we
+  // have skipped any of these operands then we cannot donate the instruction.
+  bool result = true;
+  instruction.WhileEachInId(
+      [donor_ir_context, &original_id_to_donated_id, &result,
+       &skipped_instructions](const uint32_t* in_id) -> bool {
+        if (!original_id_to_donated_id.count(*in_id)) {
+          // We do not have a mapped result id for this id operand.  That either
+          // means that it is a forward reference (which is OK), that we skipped
+          // the instruction that generated it (which is not OK), or that it is
+          // the id of a function or global value that we did not donate (which
+          // is not OK).  We check for the latter two cases.
+          if (skipped_instructions.count(*in_id) ||
+              // A function or global value does not have an associated basic
+              // block.
+              !donor_ir_context->get_instr_block(*in_id)) {
+            result = false;
+            return false;
+          }
+        }
+        return true;
+      });
+  return result;
+}
+
+bool FuzzerPassDonateModules::IsBasicType(
+    const opt::Instruction& instruction) const {
+  switch (instruction.opcode()) {
+    case SpvOpTypeArray:
+    case SpvOpTypeFloat:
+    case SpvOpTypeInt:
+    case SpvOpTypeMatrix:
+    case SpvOpTypeStruct:
+    case SpvOpTypeVector:
+      return true;
+    default:
+      return false;
+  }
+}
+
 std::vector<uint32_t>
 FuzzerPassDonateModules::GetFunctionsInCallGraphTopologicalOrder(
     opt::IRContext* context) {
@@ -735,5 +799,333 @@
   return result;
 }
 
+void FuzzerPassDonateModules::HandleOpArrayLength(
+    const opt::Instruction& instruction,
+    std::map<uint32_t, uint32_t>* original_id_to_donated_id,
+    std::vector<protobufs::Instruction>* donated_instructions) const {
+  assert(instruction.opcode() == SpvOpArrayLength &&
+         "Precondition: instruction must be OpArrayLength.");
+  uint32_t donated_variable_id =
+      original_id_to_donated_id->at(instruction.GetSingleWordInOperand(0));
+  auto donated_variable_instruction =
+      GetIRContext()->get_def_use_mgr()->GetDef(donated_variable_id);
+  auto pointer_to_struct_instruction =
+      GetIRContext()->get_def_use_mgr()->GetDef(
+          donated_variable_instruction->type_id());
+  assert(pointer_to_struct_instruction->opcode() == SpvOpTypePointer &&
+         "Type of variable must be pointer.");
+  auto donated_struct_type_instruction =
+      GetIRContext()->get_def_use_mgr()->GetDef(
+          pointer_to_struct_instruction->GetSingleWordInOperand(1));
+  assert(donated_struct_type_instruction->opcode() == SpvOpTypeStruct &&
+         "Pointee type of pointer used by OpArrayLength must be struct.");
+  assert(donated_struct_type_instruction->NumInOperands() ==
+             instruction.GetSingleWordInOperand(1) + 1 &&
+         "OpArrayLength must refer to the final member of the given "
+         "struct.");
+  uint32_t fixed_size_array_type_id =
+      donated_struct_type_instruction->GetSingleWordInOperand(
+          donated_struct_type_instruction->NumInOperands() - 1);
+  auto fixed_size_array_type_instruction =
+      GetIRContext()->get_def_use_mgr()->GetDef(fixed_size_array_type_id);
+  assert(fixed_size_array_type_instruction->opcode() == SpvOpTypeArray &&
+         "The donated array type must be fixed-size.");
+  auto array_size_id =
+      fixed_size_array_type_instruction->GetSingleWordInOperand(1);
+
+  if (instruction.result_id() &&
+      !original_id_to_donated_id->count(instruction.result_id())) {
+    original_id_to_donated_id->insert(
+        {instruction.result_id(), GetFuzzerContext()->GetFreshId()});
+  }
+
+  donated_instructions->push_back(MakeInstructionMessage(
+      SpvOpCopyObject, original_id_to_donated_id->at(instruction.type_id()),
+      original_id_to_donated_id->at(instruction.result_id()),
+      opt::Instruction::OperandList({{SPV_OPERAND_TYPE_ID, {array_size_id}}})));
+}
+
+void FuzzerPassDonateModules::HandleDifficultInstruction(
+    const opt::Instruction& instruction,
+    std::map<uint32_t, uint32_t>* original_id_to_donated_id,
+    std::vector<protobufs::Instruction>* donated_instructions,
+    std::set<uint32_t>* skipped_instructions) {
+  if (!instruction.result_id()) {
+    // It does not generate a result id, so it can be ignored.
+    return;
+  }
+  if (!original_id_to_donated_id->count(instruction.type_id())) {
+    // We cannot handle this instruction's result type, so we need to skip it
+    // all together.
+    skipped_instructions->insert(instruction.result_id());
+    return;
+  }
+
+  // We now attempt to replace the instruction with an OpCopyObject.
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3278): We could do
+  //  something more refined here - we could check which operands to the
+  //  instruction could not be donated and replace those operands with
+  //  references to other ids (such as constants), so that we still get an
+  //  instruction with the opcode and easy-to-handle operands of the donor
+  //  instruction.
+  auto remapped_type_id = original_id_to_donated_id->at(instruction.type_id());
+  if (!IsBasicType(
+          *GetIRContext()->get_def_use_mgr()->GetDef(remapped_type_id))) {
+    // The instruction has a non-basic result type, so we cannot replace it with
+    // an object copy of a constant.  We thus skip it completely.
+    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3279): We could
+    //  instead look for an available id of the right type and generate an
+    //  OpCopyObject of that id.
+    skipped_instructions->insert(instruction.result_id());
+    return;
+  }
+
+  // We are going to add an OpCopyObject instruction.  Add a mapping for the
+  // result id of the original instruction if does not already exist (it may
+  // exist in the case that it has been forward-referenced).
+  if (!original_id_to_donated_id->count(instruction.result_id())) {
+    original_id_to_donated_id->insert(
+        {instruction.result_id(), GetFuzzerContext()->GetFreshId()});
+  }
+
+  // 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);
+  donated_instructions->push_back(MakeInstructionMessage(
+      SpvOpCopyObject, remapped_type_id,
+      original_id_to_donated_id->at(instruction.result_id()),
+      opt::Instruction::OperandList({{SPV_OPERAND_TYPE_ID, {zero_constant}}})));
+}
+
+void FuzzerPassDonateModules::PrepareInstructionForDonation(
+    const opt::Instruction& instruction, opt::IRContext* donor_ir_context,
+    std::map<uint32_t, uint32_t>* original_id_to_donated_id,
+    std::vector<protobufs::Instruction>* donated_instructions) {
+  // Get the instruction's input operands into donation-ready form,
+  // remapping any id uses in the process.
+  opt::Instruction::OperandList input_operands;
+
+  // Consider each input operand in turn.
+  for (uint32_t in_operand_index = 0;
+       in_operand_index < instruction.NumInOperands(); in_operand_index++) {
+    std::vector<uint32_t> operand_data;
+    const opt::Operand& in_operand = instruction.GetInOperand(in_operand_index);
+    // Check whether this operand is an id.
+    if (spvIsIdType(in_operand.type)) {
+      // This is an id operand - it consists of a single word of data,
+      // which needs to be remapped so that it is replaced with the
+      // donated form of the id.
+      auto operand_id = in_operand.words[0];
+      if (!original_id_to_donated_id->count(operand_id)) {
+        // This is a forward reference.  We will choose a corresponding
+        // donor id for the referenced id and update the mapping to
+        // reflect it.
+
+        // Keep release compilers happy because |donor_ir_context| is only used
+        // in this assertion.
+        (void)(donor_ir_context);
+        assert((donor_ir_context->get_def_use_mgr()
+                        ->GetDef(operand_id)
+                        ->opcode() == SpvOpLabel ||
+                instruction.opcode() == SpvOpPhi) &&
+               "Unsupported forward reference.");
+        original_id_to_donated_id->insert(
+            {operand_id, GetFuzzerContext()->GetFreshId()});
+      }
+      operand_data.push_back(original_id_to_donated_id->at(operand_id));
+    } else {
+      // For non-id operands, we just add each of the data words.
+      for (auto word : in_operand.words) {
+        operand_data.push_back(word);
+      }
+    }
+    input_operands.push_back({in_operand.type, operand_data});
+  }
+
+  if (instruction.opcode() == SpvOpVariable &&
+      instruction.NumInOperands() == 1) {
+    // This is an uninitialized local variable.  Initialize it to zero.
+    input_operands.push_back(
+        {SPV_OPERAND_TYPE_ID,
+         {FindOrCreateZeroConstant(fuzzerutil::GetPointeeTypeIdFromPointerType(
+             GetIRContext(),
+             original_id_to_donated_id->at(instruction.type_id())))}});
+  }
+
+  if (instruction.result_id() &&
+      !original_id_to_donated_id->count(instruction.result_id())) {
+    original_id_to_donated_id->insert(
+        {instruction.result_id(), GetFuzzerContext()->GetFreshId()});
+  }
+
+  // Remap the result type and result id (if present) of the
+  // instruction, and turn it into a protobuf message.
+  donated_instructions->push_back(MakeInstructionMessage(
+      instruction.opcode(),
+      instruction.type_id()
+          ? original_id_to_donated_id->at(instruction.type_id())
+          : 0,
+      instruction.result_id()
+          ? original_id_to_donated_id->at(instruction.result_id())
+          : 0,
+      input_operands));
+}
+
+void FuzzerPassDonateModules::AddLivesafeFunction(
+    const opt::Function& function_to_donate, opt::IRContext* donor_ir_context,
+    const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
+    const std::vector<protobufs::Instruction>& donated_instructions) {
+  // Various types and constants must be in place for a function to be made
+  // live-safe.  Add them if not already present.
+  FindOrCreateBoolType();  // Needed for comparisons
+  FindOrCreatePointerTo32BitIntegerType(
+      false, SpvStorageClassFunction);  // Needed for adding loop limiters
+  FindOrCreate32BitIntegerConstant(
+      0, false);  // Needed for initializing loop limiters
+  FindOrCreate32BitIntegerConstant(
+      1, false);  // Needed for incrementing loop limiters
+
+  // Get a fresh id for the variable that will be used as a loop limiter.
+  const uint32_t loop_limiter_variable_id = GetFuzzerContext()->GetFreshId();
+  // Choose a random loop limit, and add the required constant to the
+  // module if not already there.
+  const uint32_t loop_limit = FindOrCreate32BitIntegerConstant(
+      GetFuzzerContext()->GetRandomLoopLimit(), false);
+
+  // Consider every loop header in the function to donate, and create a
+  // structure capturing the ids to be used for manipulating the loop
+  // limiter each time the loop is iterated.
+  std::vector<protobufs::LoopLimiterInfo> loop_limiters;
+  for (auto& block : function_to_donate) {
+    if (block.IsLoopHeader()) {
+      protobufs::LoopLimiterInfo loop_limiter;
+      // Grab the loop header's id, mapped to its donated value.
+      loop_limiter.set_loop_header_id(original_id_to_donated_id.at(block.id()));
+      // Get fresh ids that will be used to load the loop limiter, increment
+      // it, compare it with the loop limit, and an id for a new block that
+      // will contain the loop's original terminator.
+      loop_limiter.set_load_id(GetFuzzerContext()->GetFreshId());
+      loop_limiter.set_increment_id(GetFuzzerContext()->GetFreshId());
+      loop_limiter.set_compare_id(GetFuzzerContext()->GetFreshId());
+      loop_limiter.set_logical_op_id(GetFuzzerContext()->GetFreshId());
+      loop_limiters.emplace_back(loop_limiter);
+    }
+  }
+
+  // Consider every access chain in the function to donate, and create a
+  // structure containing the ids necessary to clamp the access chain
+  // indices to be in-bounds.
+  std::vector<protobufs::AccessChainClampingInfo> access_chain_clamping_info;
+  for (auto& block : function_to_donate) {
+    for (auto& inst : block) {
+      switch (inst.opcode()) {
+        case SpvOpAccessChain:
+        case SpvOpInBoundsAccessChain: {
+          protobufs::AccessChainClampingInfo clamping_info;
+          clamping_info.set_access_chain_id(
+              original_id_to_donated_id.at(inst.result_id()));
+
+          auto base_object = donor_ir_context->get_def_use_mgr()->GetDef(
+              inst.GetSingleWordInOperand(0));
+          assert(base_object && "The base object must exist.");
+          auto pointer_type = donor_ir_context->get_def_use_mgr()->GetDef(
+              base_object->type_id());
+          assert(pointer_type && pointer_type->opcode() == SpvOpTypePointer &&
+                 "The base object must have pointer type.");
+
+          auto should_be_composite_type =
+              donor_ir_context->get_def_use_mgr()->GetDef(
+                  pointer_type->GetSingleWordInOperand(1));
+
+          // Walk the access chain, creating fresh ids to facilitate
+          // clamping each index.  For simplicity we do this for every
+          // index, even though constant indices will not end up being
+          // clamped.
+          for (uint32_t index = 1; index < inst.NumInOperands(); index++) {
+            auto compare_and_select_ids =
+                clamping_info.add_compare_and_select_ids();
+            compare_and_select_ids->set_first(GetFuzzerContext()->GetFreshId());
+            compare_and_select_ids->set_second(
+                GetFuzzerContext()->GetFreshId());
+
+            // Get the bound for the component being indexed into.
+            uint32_t bound;
+            if (should_be_composite_type->opcode() == SpvOpTypeRuntimeArray) {
+              // The donor is indexing into a runtime array.  We do not
+              // donate runtime arrays.  Instead, we donate a corresponding
+              // fixed-size array for every runtime array.  We should thus
+              // find that donor composite type's result id maps to a fixed-
+              // size array.
+              auto fixed_size_array_type =
+                  GetIRContext()->get_def_use_mgr()->GetDef(
+                      original_id_to_donated_id.at(
+                          should_be_composite_type->result_id()));
+              assert(fixed_size_array_type->opcode() == SpvOpTypeArray &&
+                     "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);
+            } else {
+              bound = TransformationAddFunction::GetBoundForCompositeIndex(
+                  donor_ir_context, *should_be_composite_type);
+            }
+            const uint32_t index_id = inst.GetSingleWordInOperand(index);
+            auto index_inst =
+                donor_ir_context->get_def_use_mgr()->GetDef(index_id);
+            auto index_type_inst = donor_ir_context->get_def_use_mgr()->GetDef(
+                index_inst->type_id());
+            assert(index_type_inst->opcode() == SpvOpTypeInt);
+            assert(index_type_inst->GetSingleWordInOperand(0) == 32);
+            opt::analysis::Integer* index_int_type =
+                donor_ir_context->get_type_mgr()
+                    ->GetType(index_type_inst->result_id())
+                    ->AsInteger();
+            if (index_inst->opcode() != SpvOpConstant) {
+              // We will have to clamp this index, so we need a constant
+              // whose value is one less than the bound, to compare
+              // against and to use as the clamped value.
+              FindOrCreate32BitIntegerConstant(bound - 1,
+                                               index_int_type->IsSigned());
+            }
+            should_be_composite_type =
+                TransformationAddFunction::FollowCompositeIndex(
+                    donor_ir_context, *should_be_composite_type, index_id);
+          }
+          access_chain_clamping_info.push_back(clamping_info);
+          break;
+        }
+        default:
+          break;
+      }
+    }
+  }
+
+  // If the function contains OpKill or OpUnreachable instructions, and has
+  // non-void return type, then we need a value %v to use in order to turn
+  // these into instructions of the form OpReturn %v.
+  uint32_t kill_unreachable_return_value_id;
+  auto function_return_type_inst =
+      donor_ir_context->get_def_use_mgr()->GetDef(function_to_donate.type_id());
+  if (function_return_type_inst->opcode() == SpvOpTypeVoid) {
+    // The return type is void, so we don't need a return value.
+    kill_unreachable_return_value_id = 0;
+  } else {
+    // We do need a return value; we use zero.
+    assert(function_return_type_inst->opcode() != SpvOpTypePointer &&
+           "Function return type must not be a pointer.");
+    kill_unreachable_return_value_id = FindOrCreateZeroConstant(
+        original_id_to_donated_id.at(function_return_type_inst->result_id()));
+  }
+  // Add the function in a livesafe manner.
+  ApplyTransformation(TransformationAddFunction(
+      donated_instructions, loop_limiter_variable_id, loop_limit, loop_limiters,
+      kill_unreachable_return_value_id, access_chain_clamping_info));
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_donate_modules.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_donate_modules.h
index ef529db..c59ad71 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_donate_modules.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_donate_modules.h
@@ -28,7 +28,7 @@
 class FuzzerPassDonateModules : public FuzzerPass {
  public:
   FuzzerPassDonateModules(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations,
       const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers);
@@ -66,6 +66,11 @@
       opt::IRContext* donor_ir_context,
       std::map<uint32_t, uint32_t>* original_id_to_donated_id);
 
+  // Helper method for HandleTypesAndValues, to handle a single type/value.
+  void HandleTypeOrValue(
+      const opt::Instruction& type_or_value,
+      std::map<uint32_t, uint32_t>* original_id_to_donated_id);
+
   // Assumes that |donor_ir_context| does not exhibit recursion.  Considers the
   // functions in |donor_ir_context|'s call graph in a reverse-topologically-
   // sorted order (leaves-to-root), adding each function to the recipient
@@ -77,6 +82,68 @@
                        std::map<uint32_t, uint32_t>* original_id_to_donated_id,
                        bool make_livesafe);
 
+  // During donation we will have to ignore some instructions, e.g. because they
+  // use opcodes that we cannot support or because they reference the ids of
+  // instructions that have not been donated.  This function encapsulates the
+  // logic for deciding which whether instruction |instruction| from
+  // |donor_ir_context| can be donated.
+  bool CanDonateInstruction(
+      opt::IRContext* donor_ir_context, const opt::Instruction& instruction,
+      const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
+      const std::set<uint32_t>& skipped_instructions) const;
+
+  // We treat the OpArrayLength instruction specially.  In the donor shader this
+  // instruction yields the length of a runtime array that is the final member
+  // of a struct.  During donation, we will have converted the runtime array
+  // type, and the associated struct field, into a fixed-size array.
+  //
+  // Instead of donating this instruction, we turn it into an OpCopyObject
+  // instruction that copies the size of the fixed-size array.
+  void HandleOpArrayLength(
+      const opt::Instruction& instruction,
+      std::map<uint32_t, uint32_t>* original_id_to_donated_id,
+      std::vector<protobufs::Instruction>* donated_instructions) const;
+
+  // The instruction |instruction| is required to be an instruction that cannot
+  // be easily donated, either because it uses an unsupported opcode, has an
+  // unsupported result type, or uses id operands that could not be donated.
+  //
+  // If |instruction| generates a result id, the function attempts to add a
+  // substitute for |instruction| to |donated_instructions| that has the correct
+  // result type.  If this cannot be done, the instruction's result id is added
+  // to |skipped_instructions|.  The mapping from donor ids to recipient ids is
+  // managed by |original_id_to_donated_id|.
+  void HandleDifficultInstruction(
+      const opt::Instruction& instruction,
+      std::map<uint32_t, uint32_t>* original_id_to_donated_id,
+      std::vector<protobufs::Instruction>* donated_instructions,
+      std::set<uint32_t>* skipped_instructions);
+
+  // Adds an instruction based in |instruction| to |donated_instructions| in a
+  // form ready for donation.  The original instruction comes from
+  // |donor_ir_context|, and |original_id_to_donated_id| maps ids from
+  // |donor_ir_context| to corresponding ids in the recipient module.
+  void PrepareInstructionForDonation(
+      const opt::Instruction& instruction, opt::IRContext* donor_ir_context,
+      std::map<uint32_t, uint32_t>* original_id_to_donated_id,
+      std::vector<protobufs::Instruction>* donated_instructions);
+
+  // Requires that |donated_instructions| represents a prepared version of the
+  // instructions of |function_to_donate| (which comes from |donor_ir_context|)
+  // ready for donation, and |original_id_to_donated_id| maps ids from
+  // |donor_ir_context| to their corresponding ids in the recipient module.
+  //
+  // Adds a livesafe version of the function, based on |donated_instructions|,
+  // to the recipient module.
+  void AddLivesafeFunction(
+      const opt::Function& function_to_donate, opt::IRContext* donor_ir_context,
+      const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
+      const std::vector<protobufs::Instruction>& donated_instructions);
+
+  // Returns true if and only if |instruction| is a scalar, vector, matrix,
+  // array or struct; i.e. it is not an opaque type.
+  bool IsBasicType(const opt::Instruction& instruction) const;
+
   // Returns the ids of all functions in |context| in a topological order in
   // relation to the call graph of |context|, which is assumed to be recursion-
   // free.
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 ca1bfb3..49778ae 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
@@ -22,10 +22,11 @@
 namespace fuzz {
 
 FuzzerPassMergeBlocks::FuzzerPassMergeBlocks(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassMergeBlocks::~FuzzerPassMergeBlocks() = default;
 
@@ -44,7 +45,8 @@
       // For other blocks, we add a transformation to merge the block into its
       // predecessor if that transformation would be applicable.
       TransformationMergeBlocks transformation(block.id());
-      if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
+      if (transformation.IsApplicable(GetIRContext(),
+                                      *GetTransformationContext())) {
         potential_transformations.push_back(transformation);
       }
     }
@@ -54,8 +56,9 @@
     uint32_t index = GetFuzzerContext()->RandomIndex(potential_transformations);
     auto transformation = potential_transformations.at(index);
     potential_transformations.erase(potential_transformations.begin() + index);
-    if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
-      transformation.Apply(GetIRContext(), GetFactManager());
+    if (transformation.IsApplicable(GetIRContext(),
+                                    *GetTransformationContext())) {
+      transformation.Apply(GetIRContext(), GetTransformationContext());
       *GetTransformations()->add_transformation() = transformation.ToMessage();
     }
   }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.h
index 457e591..1a6c2c2 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.h
@@ -23,7 +23,8 @@
 // A fuzzer pass for merging blocks in the module.
 class FuzzerPassMergeBlocks : public FuzzerPass {
  public:
-  FuzzerPassMergeBlocks(opt::IRContext* ir_context, FactManager* fact_manager,
+  FuzzerPassMergeBlocks(opt::IRContext* ir_context,
+                        TransformationContext* transformation_context,
                         FuzzerContext* fuzzer_context,
                         protobufs::TransformationSequence* transformations);
 
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 2caf0c6..543c0d7 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
@@ -14,21 +14,25 @@
 
 #include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
 
+#include <algorithm>
 #include <cmath>
 
+#include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
+#include "source/fuzz/uniform_buffer_element_descriptor.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
 namespace fuzz {
 
 FuzzerPassObfuscateConstants::FuzzerPassObfuscateConstants(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassObfuscateConstants::~FuzzerPassObfuscateConstants() = default;
 
@@ -83,12 +87,13 @@
       bool_constant_use, lhs_id, rhs_id, comparison_opcode,
       GetFuzzerContext()->GetFreshId());
   // The transformation should be applicable by construction.
-  assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()));
+  assert(
+      transformation.IsApplicable(GetIRContext(), *GetTransformationContext()));
 
   // Applying this transformation yields a pointer to the new instruction that
   // computes the result of the binary expression.
-  auto binary_operator_instruction =
-      transformation.ApplyWithResult(GetIRContext(), GetFactManager());
+  auto binary_operator_instruction = transformation.ApplyWithResult(
+      GetIRContext(), GetTransformationContext());
 
   // Add this transformation to the sequence of transformations that have been
   // applied.
@@ -238,6 +243,29 @@
       first_constant_is_larger);
 }
 
+std::vector<std::vector<uint32_t>>
+FuzzerPassObfuscateConstants::GetConstantWordsFromUniformsForType(
+    uint32_t type_id) {
+  assert(type_id && "Type id can't be 0");
+  std::vector<std::vector<uint32_t>> result;
+
+  for (const auto& facts_and_types : GetTransformationContext()
+                                         ->GetFactManager()
+                                         ->GetConstantUniformFactsAndTypes()) {
+    if (facts_and_types.second != type_id) {
+      continue;
+    }
+
+    std::vector<uint32_t> words(facts_and_types.first.constant_word().begin(),
+                                facts_and_types.first.constant_word().end());
+    if (std::find(result.begin(), result.end(), words) == result.end()) {
+      result.push_back(std::move(words));
+    }
+  }
+
+  return result;
+}
+
 void FuzzerPassObfuscateConstants::ObfuscateBoolConstant(
     uint32_t depth, const protobufs::IdUseDescriptor& constant_use) {
   // We want to replace the boolean constant use with a binary expression over
@@ -245,7 +273,9 @@
   // with uniforms of the same value.
 
   auto available_types_with_uniforms =
-      GetFactManager()->GetTypesForWhichUniformValuesAreKnown();
+      GetTransformationContext()
+          ->GetFactManager()
+          ->GetTypesForWhichUniformValuesAreKnown();
   if (available_types_with_uniforms.empty()) {
     // Do not try to obfuscate if we do not have access to any uniform
     // elements with known values.
@@ -254,10 +284,9 @@
   auto chosen_type_id =
       available_types_with_uniforms[GetFuzzerContext()->RandomIndex(
           available_types_with_uniforms)];
-  auto available_constants =
-      GetFactManager()->GetConstantsAvailableFromUniformsForType(
-          GetIRContext(), chosen_type_id);
-  if (available_constants.size() == 1) {
+  auto available_constant_words =
+      GetConstantWordsFromUniformsForType(chosen_type_id);
+  if (available_constant_words.size() == 1) {
     // TODO(afd): for now we only obfuscate a boolean if there are at least
     //  two constants available from uniforms, so that we can do a
     //  comparison between them. It would be good to be able to do the
@@ -266,18 +295,25 @@
     return;
   }
 
+  assert(!available_constant_words.empty() &&
+         "There exists a fact but no constants - impossible");
+
   // We know we have at least two known-to-be-constant uniforms of the chosen
   // type.  Pick one of them at random.
-  auto constant_index_1 = GetFuzzerContext()->RandomIndex(available_constants);
+  auto constant_index_1 =
+      GetFuzzerContext()->RandomIndex(available_constant_words);
   uint32_t constant_index_2;
 
   // Now choose another one distinct from the first one.
   do {
-    constant_index_2 = GetFuzzerContext()->RandomIndex(available_constants);
+    constant_index_2 =
+        GetFuzzerContext()->RandomIndex(available_constant_words);
   } while (constant_index_1 == constant_index_2);
 
-  auto constant_id_1 = available_constants[constant_index_1];
-  auto constant_id_2 = available_constants[constant_index_2];
+  auto constant_id_1 = FindOrCreateConstant(
+      available_constant_words[constant_index_1], chosen_type_id);
+  auto constant_id_2 = FindOrCreateConstant(
+      available_constant_words[constant_index_2], chosen_type_id);
 
   assert(constant_id_1 != 0 && constant_id_2 != 0 &&
          "We should not find an available constant with an id of 0.");
@@ -308,25 +344,50 @@
 
   // Check whether we know that any uniforms are guaranteed to be equal to the
   // scalar constant associated with |constant_use|.
-  auto uniform_descriptors = GetFactManager()->GetUniformDescriptorsForConstant(
-      GetIRContext(), constant_use.id_of_interest());
+  auto uniform_descriptors =
+      GetTransformationContext()
+          ->GetFactManager()
+          ->GetUniformDescriptorsForConstant(GetIRContext(),
+                                             constant_use.id_of_interest());
   if (uniform_descriptors.empty()) {
     // No relevant uniforms, so do not obfuscate.
     return;
   }
 
   // Choose a random available uniform known to be equal to the constant.
-  protobufs::UniformBufferElementDescriptor uniform_descriptor =
+  const auto& uniform_descriptor =
       uniform_descriptors[GetFuzzerContext()->RandomIndex(uniform_descriptors)];
+
+  // Make sure the module has OpConstant instructions for each index used to
+  // access a uniform.
+  for (auto index : uniform_descriptor.index()) {
+    FindOrCreate32BitIntegerConstant(index, true);
+  }
+
+  // Make sure the module has OpTypePointer that points to the element type of
+  // the uniform.
+  const auto* uniform_variable_instr =
+      FindUniformVariable(uniform_descriptor, GetIRContext(), true);
+  assert(uniform_variable_instr &&
+         "Uniform variable does not exist or not unique.");
+
+  const auto* uniform_variable_type_intr =
+      GetIRContext()->get_def_use_mgr()->GetDef(
+          uniform_variable_instr->type_id());
+  assert(uniform_variable_type_intr && "Uniform variable has invalid type");
+
+  auto element_type_id = fuzzerutil::WalkCompositeTypeIndices(
+      GetIRContext(), uniform_variable_type_intr->GetSingleWordInOperand(1),
+      uniform_descriptor.index());
+  assert(element_type_id && "Type of uniform variable is invalid");
+
+  FindOrCreatePointerType(element_type_id, SpvStorageClassUniform);
+
   // Create, apply and record a transformation to replace the constant use with
   // the result of a load from the chosen uniform.
-  auto transformation = TransformationReplaceConstantWithUniform(
+  ApplyTransformation(TransformationReplaceConstantWithUniform(
       constant_use, uniform_descriptor, GetFuzzerContext()->GetFreshId(),
-      GetFuzzerContext()->GetFreshId());
-  // Transformation should be applicable by construction.
-  assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()));
-  transformation.Apply(GetIRContext(), GetFactManager());
-  *GetTransformations()->add_transformation() = transformation.ToMessage();
+      GetFuzzerContext()->GetFreshId()));
 }
 
 void FuzzerPassObfuscateConstants::ObfuscateConstant(
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_obfuscate_constants.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_obfuscate_constants.h
index f34717b..52d8efe 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_obfuscate_constants.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_obfuscate_constants.h
@@ -28,7 +28,7 @@
 class FuzzerPassObfuscateConstants : public FuzzerPass {
  public:
   FuzzerPassObfuscateConstants(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
@@ -99,6 +99,11 @@
       uint32_t base_instruction_result_id,
       const std::map<SpvOp, uint32_t>& skipped_opcode_count,
       std::vector<protobufs::IdUseDescriptor>* constant_uses);
+
+  // Returns a vector of unique words that denote constants. Every such constant
+  // is used in |FactConstantUniform| and has type with id equal to |type_id|.
+  std::vector<std::vector<uint32_t>> GetConstantWordsFromUniformsForType(
+      uint32_t type_id);
 };
 
 }  // namespace fuzz
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 d59c195..1665d05 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
@@ -23,10 +23,11 @@
 namespace fuzz {
 
 FuzzerPassOutlineFunctions::FuzzerPassOutlineFunctions(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassOutlineFunctions::~FuzzerPassOutlineFunctions() = default;
 
@@ -88,8 +89,9 @@
         /*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(), *GetFactManager())) {
-      transformation.Apply(GetIRContext(), GetFactManager());
+    if (transformation.IsApplicable(GetIRContext(),
+                                    *GetTransformationContext())) {
+      transformation.Apply(GetIRContext(), GetTransformationContext());
       *GetTransformations()->add_transformation() = transformation.ToMessage();
     }
   }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.h
index 5448e7d..6532ed9 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.h
@@ -25,7 +25,7 @@
 class FuzzerPassOutlineFunctions : public FuzzerPass {
  public:
   FuzzerPassOutlineFunctions(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
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 af6d2a5..27a2d67 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
@@ -20,10 +20,11 @@
 namespace fuzz {
 
 FuzzerPassPermuteBlocks::FuzzerPassPermuteBlocks(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassPermuteBlocks::~FuzzerPassPermuteBlocks() = default;
 
@@ -66,8 +67,9 @@
       // down indefinitely.
       while (true) {
         TransformationMoveBlockDown transformation(*id);
-        if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
-          transformation.Apply(GetIRContext(), GetFactManager());
+        if (transformation.IsApplicable(GetIRContext(),
+                                        *GetTransformationContext())) {
+          transformation.Apply(GetIRContext(), GetTransformationContext());
           *GetTransformations()->add_transformation() =
               transformation.ToMessage();
         } else {
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_blocks.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_blocks.h
index 6735e95..f2d3b39 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_blocks.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_blocks.h
@@ -24,7 +24,8 @@
 // manner.
 class FuzzerPassPermuteBlocks : public FuzzerPass {
  public:
-  FuzzerPassPermuteBlocks(opt::IRContext* ir_context, FactManager* fact_manager,
+  FuzzerPassPermuteBlocks(opt::IRContext* ir_context,
+                          TransformationContext* transformation_context,
                           FuzzerContext* fuzzer_context,
                           protobufs::TransformationSequence* transformations);
 
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 2c49860..57d9cab 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
@@ -25,10 +25,11 @@
 namespace fuzz {
 
 FuzzerPassPermuteFunctionParameters::FuzzerPassPermuteFunctionParameters(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassPermuteFunctionParameters::~FuzzerPassPermuteFunctionParameters() =
     default;
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 bc79804..3f32864 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
@@ -30,7 +30,7 @@
 class FuzzerPassPermuteFunctionParameters : public FuzzerPass {
  public:
   FuzzerPassPermuteFunctionParameters(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
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 6a2ea4d..15c6790 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
@@ -23,10 +23,11 @@
 namespace fuzz {
 
 FuzzerPassSplitBlocks::FuzzerPassSplitBlocks(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassSplitBlocks::~FuzzerPassSplitBlocks() = default;
 
@@ -95,8 +96,9 @@
     // 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(), *GetFactManager())) {
-      transformation.Apply(GetIRContext(), GetFactManager());
+    if (transformation.IsApplicable(GetIRContext(),
+                                    *GetTransformationContext())) {
+      transformation.Apply(GetIRContext(), GetTransformationContext());
       *GetTransformations()->add_transformation() = transformation.ToMessage();
     }
   }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_split_blocks.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_split_blocks.h
index 6e56dde..278ec6d 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_split_blocks.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_split_blocks.h
@@ -24,7 +24,8 @@
 // can be very useful for giving other passes a chance to apply.
 class FuzzerPassSplitBlocks : public FuzzerPass {
  public:
-  FuzzerPassSplitBlocks(opt::IRContext* ir_context, FactManager* fact_manager,
+  FuzzerPassSplitBlocks(opt::IRContext* ir_context,
+                        TransformationContext* transformation_context,
                         FuzzerContext* fuzzer_context,
                         protobufs::TransformationSequence* transformations);
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp
index 4df97c9..321e8ef 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp
@@ -22,10 +22,11 @@
 namespace fuzz {
 
 FuzzerPassSwapCommutableOperands::FuzzerPassSwapCommutableOperands(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassSwapCommutableOperands::~FuzzerPassSwapCommutableOperands() = default;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h
index b0206de..74d937d 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h
@@ -26,7 +26,7 @@
 class FuzzerPassSwapCommutableOperands : public FuzzerPass {
  public:
   FuzzerPassSwapCommutableOperands(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
index 9fb175b..4f26cba 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
@@ -22,10 +22,11 @@
 namespace fuzz {
 
 FuzzerPassToggleAccessChainInstruction::FuzzerPassToggleAccessChainInstruction(
-    opt::IRContext* ir_context, FactManager* fact_manager,
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
 
 FuzzerPassToggleAccessChainInstruction::
     ~FuzzerPassToggleAccessChainInstruction() = default;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
index ec8c3f7..d77c7cb 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
@@ -25,7 +25,7 @@
 class FuzzerPassToggleAccessChainInstruction : public FuzzerPass {
  public:
   FuzzerPassToggleAccessChainInstruction(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
index 4bfa195..f09943f 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
@@ -218,6 +218,12 @@
 }
 
 bool CanMakeSynonymOf(opt::IRContext* ir_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
+    // instructions that are used for making synonyms.
+    return false;
+  }
   if (!inst->HasResultId()) {
     // We can only make a synonym of an instruction that generates an id.
     return false;
@@ -329,11 +335,11 @@
   return array_length_constant->GetU32();
 }
 
-bool IsValid(opt::IRContext* context) {
+bool IsValid(opt::IRContext* context, spv_validator_options validator_options) {
   std::vector<uint32_t> binary;
   context->module()->ToBinary(&binary, false);
   SpirvTools tools(context->grammar().target_env());
-  return tools.Validate(binary);
+  return tools.Validate(binary.data(), binary.size(), validator_options);
 }
 
 std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context) {
@@ -537,6 +543,13 @@
   return 0;
 }
 
+bool IsNullConstantSupported(const opt::analysis::Type& type) {
+  return type.AsBool() || type.AsInteger() || type.AsFloat() ||
+         type.AsMatrix() || type.AsVector() || type.AsArray() ||
+         type.AsStruct() || type.AsPointer() || type.AsEvent() ||
+         type.AsDeviceEvent() || type.AsReserveId() || type.AsQueue();
+}
+
 }  // 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 7be0d59..bccd1d0 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
@@ -18,6 +18,7 @@
 #include <vector>
 
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/basic_block.h"
 #include "source/opt/instruction.h"
 #include "source/opt/ir_context.h"
@@ -132,8 +133,9 @@
 uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
                       opt::IRContext* context);
 
-// Returns true if and only if |context| is valid, according to the validator.
-bool IsValid(opt::IRContext* 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);
 
 // Returns a clone of |context|, by writing |context| to a binary and then
 // parsing it again.
@@ -209,6 +211,10 @@
 uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
                              SpvStorageClass storage_class);
 
+// 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);
+
 }  // namespace fuzzerutil
 
 }  // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
index b816e3b..775b2ad 100644
--- a/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -372,6 +372,9 @@
     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;
     // Add additional option using the next available number.
   }
 }
@@ -422,6 +425,18 @@
 
 }
 
+message TransformationAddConstantNull {
+
+  // Adds a null constant.
+
+  // Id for the constant
+  uint32 fresh_id = 1;
+
+  // Type of the constant
+  uint32 type_id = 2;
+
+}
+
 message TransformationAddConstantScalar {
 
   // Adds a constant of the given scalar type.
@@ -547,8 +562,9 @@
 
 message TransformationAddGlobalVariable {
 
-  // Adds a global variable of the given type to the module, with Private
-  // storage class and optionally with an initializer.
+  // Adds a global variable of the given type to the module, with Private or
+  // Workgroup storage class, and optionally (for the Private case) with an
+  // initializer.
 
   // Fresh id for the global variable
   uint32 fresh_id = 1;
@@ -556,13 +572,15 @@
   // The type of the global variable
   uint32 type_id = 2;
 
+  uint32 storage_class = 3;
+
   // Initial value of the variable
-  uint32 initializer_id = 3;
+  uint32 initializer_id = 4;
 
   // True if and only if the behaviour of the module should not depend on the
   // value of the variable, in which case stores to the variable can be
   // performed in an arbitrary fashion.
-  bool value_is_irrelevant = 4;
+  bool value_is_irrelevant = 5;
 
 }
 
@@ -725,6 +743,19 @@
 
 }
 
+message TransformationAdjustBranchWeights {
+
+  // A transformation that adjusts the branch weights
+  // of a branch conditional instruction.
+
+  // A descriptor for a branch conditional instruction.
+  InstructionDescriptor instruction_descriptor = 1;
+
+  // Branch weights of a branch conditional instruction.
+  UInt32Pair branch_weights = 2;
+
+}
+
 message TransformationCompositeConstruct {
 
   // A transformation that introduces an OpCompositeConstruct instruction to
@@ -765,6 +796,19 @@
 
 }
 
+message TransformationComputeDataSynonymFactClosure {
+
+  // A transformation that impacts the fact manager only, forcing a computation
+  // of the closure of data synonym facts, so that e.g. if the components of
+  // vectors v and w are known to be pairwise synonymous, it is deduced that v
+  // and w are themselves synonymous.
+
+  // When searching equivalence classes for implied facts, equivalence classes
+  // larger than this size will be skipped.
+  uint32 maximum_equivalence_class_size = 1;
+
+}
+
 message TransformationCopyObject {
 
   // A transformation that introduces an OpCopyObject instruction to make a
diff --git a/third_party/SPIRV-Tools/source/fuzz/replayer.cpp b/third_party/SPIRV-Tools/source/fuzz/replayer.cpp
index 398ce59..6312cba 100644
--- a/third_party/SPIRV-Tools/source/fuzz/replayer.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/replayer.cpp
@@ -26,6 +26,7 @@
 #include "source/fuzz/transformation_add_type_float.h"
 #include "source/fuzz/transformation_add_type_int.h"
 #include "source/fuzz/transformation_add_type_pointer.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/fuzz/transformation_move_block_down.h"
 #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
@@ -37,18 +38,22 @@
 namespace fuzz {
 
 struct Replayer::Impl {
-  explicit Impl(spv_target_env env, bool validate)
-      : target_env(env), validate_during_replay(validate) {}
+  Impl(spv_target_env env, bool validate, spv_validator_options options)
+      : target_env(env),
+        validate_during_replay(validate),
+        validator_options(options) {}
 
-  const spv_target_env target_env;  // Target environment.
-  MessageConsumer consumer;         // Message consumer.
-
+  const spv_target_env target_env;    // Target environment.
+  MessageConsumer consumer;           // Message consumer.
   const bool validate_during_replay;  // Controls whether the validator should
                                       // be run after every replay step.
+  spv_validator_options validator_options;  // Options to control
+                                            // validation
 };
 
-Replayer::Replayer(spv_target_env env, bool validate_during_replay)
-    : impl_(MakeUnique<Impl>(env, validate_during_replay)) {}
+Replayer::Replayer(spv_target_env env, bool validate_during_replay,
+                   spv_validator_options validator_options)
+    : impl_(MakeUnique<Impl>(env, validate_during_replay, validator_options)) {}
 
 Replayer::~Replayer() = default;
 
@@ -74,7 +79,8 @@
   }
 
   // Initial binary should be valid.
-  if (!tools.Validate(&binary_in[0], binary_in.size())) {
+  if (!tools.Validate(&binary_in[0], binary_in.size(),
+                      impl_->validator_options)) {
     impl_->consumer(SPV_MSG_INFO, nullptr, {},
                     "Initial binary is invalid; stopping.");
     return Replayer::ReplayerResultStatus::kInitialBinaryInvalid;
@@ -94,16 +100,19 @@
 
   FactManager fact_manager;
   fact_manager.AddFacts(impl_->consumer, initial_facts, ir_context.get());
+  TransformationContext transformation_context(&fact_manager,
+                                               impl_->validator_options);
 
   // Consider the transformation proto messages in turn.
   for (auto& message : transformation_sequence_in.transformation()) {
     auto transformation = Transformation::FromMessage(message);
 
     // Check whether the transformation can be applied.
-    if (transformation->IsApplicable(ir_context.get(), fact_manager)) {
+    if (transformation->IsApplicable(ir_context.get(),
+                                     transformation_context)) {
       // The transformation is applicable, so apply it, and copy it to the
       // sequence of transformations that were applied.
-      transformation->Apply(ir_context.get(), &fact_manager);
+      transformation->Apply(ir_context.get(), &transformation_context);
       *transformation_sequence_out->add_transformation() = message;
 
       if (impl_->validate_during_replay) {
@@ -111,8 +120,8 @@
         ir_context->module()->ToBinary(&binary_to_validate, false);
 
         // Check whether the latest transformation led to a valid binary.
-        if (!tools.Validate(&binary_to_validate[0],
-                            binary_to_validate.size())) {
+        if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size(),
+                            impl_->validator_options)) {
           impl_->consumer(SPV_MSG_INFO, nullptr, {},
                           "Binary became invalid during replay (set a "
                           "breakpoint to inspect); stopping.");
diff --git a/third_party/SPIRV-Tools/source/fuzz/replayer.h b/third_party/SPIRV-Tools/source/fuzz/replayer.h
index 1d58bae..e77d840 100644
--- a/third_party/SPIRV-Tools/source/fuzz/replayer.h
+++ b/third_party/SPIRV-Tools/source/fuzz/replayer.h
@@ -37,7 +37,8 @@
   };
 
   // Constructs a replayer from the given target environment.
-  explicit Replayer(spv_target_env env, bool validate_during_replay);
+  Replayer(spv_target_env env, bool validate_during_replay,
+           spv_validator_options validator_options);
 
   // Disables copy/move constructor/assignment operations.
   Replayer(const Replayer&) = delete;
diff --git a/third_party/SPIRV-Tools/source/fuzz/shrinker.cpp b/third_party/SPIRV-Tools/source/fuzz/shrinker.cpp
index 1bb92f1..002e8a7 100644
--- a/third_party/SPIRV-Tools/source/fuzz/shrinker.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/shrinker.cpp
@@ -60,20 +60,27 @@
 }  // namespace
 
 struct Shrinker::Impl {
-  explicit Impl(spv_target_env env, uint32_t limit, bool validate)
-      : target_env(env), step_limit(limit), validate_during_replay(validate) {}
+  Impl(spv_target_env env, uint32_t limit, bool validate,
+       spv_validator_options options)
+      : target_env(env),
+        step_limit(limit),
+        validate_during_replay(validate),
+        validator_options(options) {}
 
-  const spv_target_env target_env;    // Target environment.
-  MessageConsumer consumer;           // Message consumer.
-  const uint32_t step_limit;          // Step limit for reductions.
-  const bool validate_during_replay;  // Determines whether to check for
-                                      // validity during the replaying of
-                                      // transformations.
+  const spv_target_env target_env;          // Target environment.
+  MessageConsumer consumer;                 // Message consumer.
+  const uint32_t step_limit;                // Step limit for reductions.
+  const bool validate_during_replay;        // Determines whether to check for
+                                            // validity during the replaying of
+                                            // transformations.
+  spv_validator_options validator_options;  // Options to control validation.
 };
 
 Shrinker::Shrinker(spv_target_env env, uint32_t step_limit,
-                   bool validate_during_replay)
-    : impl_(MakeUnique<Impl>(env, step_limit, validate_during_replay)) {}
+                   bool validate_during_replay,
+                   spv_validator_options validator_options)
+    : impl_(MakeUnique<Impl>(env, step_limit, validate_during_replay,
+                             validator_options)) {}
 
 Shrinker::~Shrinker() = default;
 
@@ -100,7 +107,8 @@
   }
 
   // Initial binary should be valid.
-  if (!tools.Validate(&binary_in[0], binary_in.size())) {
+  if (!tools.Validate(&binary_in[0], binary_in.size(),
+                      impl_->validator_options)) {
     impl_->consumer(SPV_MSG_INFO, nullptr, {},
                     "Initial binary is invalid; stopping.");
     return Shrinker::ShrinkerResultStatus::kInitialBinaryInvalid;
@@ -113,7 +121,8 @@
   // 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)
+  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::ReplayerResultStatus::kComplete) {
@@ -184,7 +193,8 @@
       // transformations inapplicable.
       std::vector<uint32_t> next_binary;
       protobufs::TransformationSequence next_transformation_sequence;
-      if (Replayer(impl_->target_env, false)
+      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) !=
           Replayer::ReplayerResultStatus::kComplete) {
diff --git a/third_party/SPIRV-Tools/source/fuzz/shrinker.h b/third_party/SPIRV-Tools/source/fuzz/shrinker.h
index 0163a53..17b15bf 100644
--- a/third_party/SPIRV-Tools/source/fuzz/shrinker.h
+++ b/third_party/SPIRV-Tools/source/fuzz/shrinker.h
@@ -50,8 +50,8 @@
       const std::vector<uint32_t>& binary, uint32_t counter)>;
 
   // Constructs a shrinker from the given target environment.
-  Shrinker(spv_target_env env, uint32_t step_limit,
-           bool validate_during_replay);
+  Shrinker(spv_target_env env, uint32_t step_limit, bool validate_during_replay,
+           spv_validator_options validator_options);
 
   // Disables copy/move constructor/assignment operations.
   Shrinker(const Shrinker&) = delete;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
index f18c86b..8b84169 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
@@ -20,6 +20,7 @@
 #include "source/fuzz/transformation_access_chain.h"
 #include "source/fuzz/transformation_add_constant_boolean.h"
 #include "source/fuzz/transformation_add_constant_composite.h"
+#include "source/fuzz/transformation_add_constant_null.h"
 #include "source/fuzz/transformation_add_constant_scalar.h"
 #include "source/fuzz/transformation_add_dead_block.h"
 #include "source/fuzz/transformation_add_dead_break.h"
@@ -38,8 +39,10 @@
 #include "source/fuzz/transformation_add_type_pointer.h"
 #include "source/fuzz/transformation_add_type_struct.h"
 #include "source/fuzz/transformation_add_type_vector.h"
+#include "source/fuzz/transformation_adjust_branch_weights.h"
 #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"
@@ -78,6 +81,9 @@
     case protobufs::Transformation::TransformationCase::kAddConstantComposite:
       return MakeUnique<TransformationAddConstantComposite>(
           message.add_constant_composite());
+    case protobufs::Transformation::TransformationCase::kAddConstantNull:
+      return MakeUnique<TransformationAddConstantNull>(
+          message.add_constant_null());
     case protobufs::Transformation::TransformationCase::kAddConstantScalar:
       return MakeUnique<TransformationAddConstantScalar>(
           message.add_constant_scalar());
@@ -124,12 +130,19 @@
       return MakeUnique<TransformationAddTypeStruct>(message.add_type_struct());
     case protobufs::Transformation::TransformationCase::kAddTypeVector:
       return MakeUnique<TransformationAddTypeVector>(message.add_type_vector());
+    case protobufs::Transformation::TransformationCase::kAdjustBranchWeights:
+      return MakeUnique<TransformationAdjustBranchWeights>(
+          message.adjust_branch_weights());
     case protobufs::Transformation::TransformationCase::kCompositeConstruct:
       return MakeUnique<TransformationCompositeConstruct>(
           message.composite_construct());
     case protobufs::Transformation::TransformationCase::kCompositeExtract:
       return MakeUnique<TransformationCompositeExtract>(
           message.composite_extract());
+    case protobufs::Transformation::TransformationCase::
+        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:
@@ -195,9 +208,9 @@
 }
 
 bool Transformation::CheckIdIsFreshAndNotUsedByThisTransformation(
-    uint32_t id, opt::IRContext* context,
+    uint32_t id, opt::IRContext* ir_context,
     std::set<uint32_t>* ids_used_by_this_transformation) {
-  if (!fuzzerutil::IsFreshId(context, id)) {
+  if (!fuzzerutil::IsFreshId(ir_context, id)) {
     return false;
   }
   if (ids_used_by_this_transformation->count(id) != 0) {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation.h b/third_party/SPIRV-Tools/source/fuzz/transformation.h
index dbe803f..dbd0fe2 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation.h
@@ -17,8 +17,8 @@
 
 #include <memory>
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -60,19 +60,22 @@
  public:
   // A precondition that determines whether the transformation can be cleanly
   // applied in a semantics-preserving manner to the SPIR-V module given by
-  // |context|, in the presence of facts captured by |fact_manager|.
+  // |ir_context|, in the presence of facts and other contextual information
+  // captured by |transformation_context|.
+  //
   // Preconditions for individual transformations must be documented in the
-  // associated header file using precise English. The fact manager is used to
-  // provide access to facts about the module that are known to be true, on
+  // associated header file using precise English. The transformation context
+  // provides access to facts about the module that are known to be true, on
   // which the precondition may depend.
-  virtual bool IsApplicable(opt::IRContext* context,
-                            const FactManager& fact_manager) const = 0;
+  virtual bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const = 0;
 
-  // Requires that IsApplicable(context, fact_manager) holds.  Applies the
-  // transformation, mutating |context| and possibly updating |fact_manager|
-  // with new facts established by the transformation.
-  virtual void Apply(opt::IRContext* context,
-                     FactManager* fact_manager) const = 0;
+  // Requires that IsApplicable(ir_context, *transformation_context) holds.
+  // Applies the transformation, mutating |ir_context| and possibly updating
+  // |transformation_context| with new facts established by the transformation.
+  virtual void Apply(opt::IRContext* ir_context,
+                     TransformationContext* transformation_context) const = 0;
 
   // Turns the transformation into a protobuf message for serialization.
   virtual protobufs::Transformation ToMessage() const = 0;
@@ -90,7 +93,7 @@
   // checking id freshness for a transformation that uses many ids, all of which
   // must be distinct.
   static bool CheckIdIsFreshAndNotUsedByThisTransformation(
-      uint32_t id, opt::IRContext* context,
+      uint32_t id, opt::IRContext* ir_context,
       std::set<uint32_t>* ids_used_by_this_transformation);
 };
 
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 8c31006..ff17c36 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.cpp
@@ -40,19 +40,18 @@
 }
 
 bool TransformationAccessChain::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // The result id must be fresh
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
   // The pointer id must exist and have a type.
-  auto pointer = context->get_def_use_mgr()->GetDef(message_.pointer_id());
+  auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id());
   if (!pointer || !pointer->type_id()) {
     return false;
   }
   // The type must indeed be a pointer
-  auto pointer_type = context->get_def_use_mgr()->GetDef(pointer->type_id());
+  auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
   if (pointer_type->opcode() != SpvOpTypePointer) {
     return false;
   }
@@ -60,7 +59,7 @@
   // The described instruction to insert before must exist and be a suitable
   // point where an OpAccessChain instruction could be inserted.
   auto instruction_to_insert_before =
-      FindInstruction(message_.instruction_to_insert_before(), context);
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
   if (!instruction_to_insert_before) {
     return false;
   }
@@ -86,7 +85,7 @@
   // The pointer on which the access chain is to be based needs to be available
   // (according to dominance rules) at the insertion point.
   if (!fuzzerutil::IdIsAvailableBeforeInstruction(
-          context, instruction_to_insert_before, message_.pointer_id())) {
+          ir_context, instruction_to_insert_before, message_.pointer_id())) {
     return false;
   }
 
@@ -104,7 +103,7 @@
     // integer.  Otherwise, the integer with which the id is associated is the
     // second component.
     std::pair<bool, uint32_t> maybe_index_value =
-        GetIndexValue(context, index_id);
+        GetIndexValue(ir_context, index_id);
     if (!maybe_index_value.first) {
       // There was no integer: this index is no good.
       return false;
@@ -113,7 +112,7 @@
     // type is not a composite or the index is out of bounds, and the id of
     // the next type otherwise.
     subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
-        context, subobject_type_id, maybe_index_value.second);
+        ir_context, subobject_type_id, maybe_index_value.second);
     if (!subobject_type_id) {
       // Either the type was not a composite (so that too many indices were
       // provided), or the index was out of bounds.
@@ -128,13 +127,14 @@
   // We do not use the type manager to look up this type, due to problems
   // associated with pointers to isomorphic structs being regarded as the same.
   return fuzzerutil::MaybeGetPointerType(
-             context, subobject_type_id,
+             ir_context, subobject_type_id,
              static_cast<SpvStorageClass>(
                  pointer_type->GetSingleWordInOperand(0))) != 0;
 }
 
 void TransformationAccessChain::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   // The operands to the access chain are the pointer followed by the indices.
   // The result type of the access chain is determined by where the indices
   // lead.  We thus push the pointer to a sequence of operands, and then follow
@@ -148,8 +148,8 @@
   operands.push_back({SPV_OPERAND_TYPE_ID, {message_.pointer_id()}});
 
   // Start walking the indices, starting with the pointer's base type.
-  auto pointer_type = context->get_def_use_mgr()->GetDef(
-      context->get_def_use_mgr()->GetDef(message_.pointer_id())->type_id());
+  auto pointer_type = ir_context->get_def_use_mgr()->GetDef(
+      ir_context->get_def_use_mgr()->GetDef(message_.pointer_id())->type_id());
   uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
 
   // Go through the index ids in turn.
@@ -157,33 +157,35 @@
     // Add the index id to the operands.
     operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}});
     // Get the integer value associated with the index id.
-    uint32_t index_value = GetIndexValue(context, index_id).second;
+    uint32_t index_value = GetIndexValue(ir_context, index_id).second;
     // Walk to the next type in the composite object using this index.
     subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
-        context, subobject_type_id, index_value);
+        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.
   uint32_t result_type = fuzzerutil::MaybeGetPointerType(
-      context, subobject_type_id,
+      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.
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
-  FindInstruction(message_.instruction_to_insert_before(), context)
-      ->InsertBefore(
-          MakeUnique<opt::Instruction>(context, SpvOpAccessChain, result_type,
-                                       message_.fresh_id(), operands));
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+  FindInstruction(message_.instruction_to_insert_before(), ir_context)
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpAccessChain, result_type, message_.fresh_id(),
+          operands));
 
   // Conservatively invalidate all analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  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 (fact_manager->PointeeValueIsIrrelevant(message_.pointer_id())) {
-    fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id());
+  if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
+          message_.pointer_id())) {
+    transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+        message_.fresh_id());
   }
 }
 
@@ -194,8 +196,8 @@
 }
 
 std::pair<bool, uint32_t> TransformationAccessChain::GetIndexValue(
-    opt::IRContext* context, uint32_t index_id) const {
-  auto index_instruction = context->get_def_use_mgr()->GetDef(index_id);
+    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
@@ -203,7 +205,7 @@
     return {false, 0};
   }
   auto index_type =
-      context->get_def_use_mgr()->GetDef(index_instruction->type_id());
+      ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id());
   if (index_type->opcode() != SpvOpTypeInt ||
       index_type->GetSingleWordInOperand(0) != 32) {
     return {false, 0};
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 92d9e6a..9306a59 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.h
@@ -17,9 +17,9 @@
 
 #include <utility>
 
-#include "source/fuzz/fact_manager.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 {
@@ -47,8 +47,9 @@
   // - If type t is the final type reached by walking these indices, the module
   //   must include an instruction "OpTypePointer SC %t" where SC is the storage
   //   class associated with |message_.pointer_id|
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds an instruction of the form:
   //   |message_.fresh_id| = OpAccessChain %ptr |message_.index_id|
@@ -57,10 +58,12 @@
   // the indices in |message_.index_id|, and with the same storage class as
   // |message_.pointer_id|.
   //
-  // If |fact_manager| reports that |message_.pointer_id| has an irrelevant
-  // pointee value, then the fact that |message_.fresh_id| (the result of the
-  // access chain) also has an irrelevant pointee value is also recorded.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  // 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
+  // pointee value is also recorded.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
@@ -68,7 +71,7 @@
   // Returns {false, 0} if |index_id| does not correspond to a 32-bit integer
   // constant.  Otherwise, returns {true, value}, where value is the value of
   // the 32-bit integer constant to which |index_id| corresponds.
-  std::pair<bool, uint32_t> GetIndexValue(opt::IRContext* context,
+  std::pair<bool, uint32_t> GetIndexValue(opt::IRContext* ir_context,
                                           uint32_t index_id) const;
 
   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 21c8ed3..1930f7e 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
@@ -31,27 +31,28 @@
 }
 
 bool TransformationAddConstantBoolean::IsApplicable(
-    opt::IRContext* context, const FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   opt::analysis::Bool bool_type;
-  if (!context->get_type_mgr()->GetId(&bool_type)) {
+  if (!ir_context->get_type_mgr()->GetId(&bool_type)) {
     // No OpTypeBool is present.
     return false;
   }
-  return fuzzerutil::IsFreshId(context, message_.fresh_id());
+  return fuzzerutil::IsFreshId(ir_context, message_.fresh_id());
 }
 
-void TransformationAddConstantBoolean::Apply(opt::IRContext* context,
-                                             FactManager* /*unused*/) const {
+void TransformationAddConstantBoolean::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   opt::analysis::Bool bool_type;
   // Add the boolean constant to the module, ensuring the module's id bound is
   // high enough.
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
-  context->module()->AddGlobalValue(
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+  ir_context->module()->AddGlobalValue(
       message_.is_true() ? SpvOpConstantTrue : SpvOpConstantFalse,
-      message_.fresh_id(), context->get_type_mgr()->GetId(&bool_type));
+      message_.fresh_id(), ir_context->get_type_mgr()->GetId(&bool_type));
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 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 79df1cd..5d876cf 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
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_BOOLEAN_CONSTANT_H_
 #define SOURCE_FUZZ_TRANSFORMATION_ADD_BOOLEAN_CONSTANT_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -32,12 +32,14 @@
 
   // - |message_.fresh_id| must not be used by the module.
   // - The module must already contain OpTypeBool.
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // - Adds OpConstantTrue (OpConstantFalse) to the module with id
   //   |message_.fresh_id| if |message_.is_true| holds (does not hold).
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() 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 7ba1ea4..ae34b26 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
@@ -37,15 +37,14 @@
 }
 
 bool TransformationAddConstantComposite::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // Check that the given id is fresh.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
   // Check that the composite type id is an instruction id.
   auto composite_type_instruction =
-      context->get_def_use_mgr()->GetDef(message_.type_id());
+      ir_context->get_def_use_mgr()->GetDef(message_.type_id());
   if (!composite_type_instruction) {
     return false;
   }
@@ -56,7 +55,7 @@
     case SpvOpTypeArray:
       for (uint32_t index = 0;
            index <
-           fuzzerutil::GetArraySize(*composite_type_instruction, context);
+           fuzzerutil::GetArraySize(*composite_type_instruction, ir_context);
            index++) {
         constituent_type_ids.push_back(
             composite_type_instruction->GetSingleWordInOperand(0));
@@ -93,7 +92,7 @@
   // corresponding constituent type.
   for (uint32_t index = 0; index < constituent_type_ids.size(); index++) {
     auto constituent_instruction =
-        context->get_def_use_mgr()->GetDef(message_.constituent_id(index));
+        ir_context->get_def_use_mgr()->GetDef(message_.constituent_id(index));
     if (!constituent_instruction) {
       return false;
     }
@@ -105,18 +104,19 @@
 }
 
 void TransformationAddConstantComposite::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   opt::Instruction::OperandList in_operands;
   for (auto constituent_id : message_.constituent_id()) {
     in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}});
   }
-  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-      context, SpvOpConstantComposite, message_.type_id(), message_.fresh_id(),
-      in_operands));
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpConstantComposite, message_.type_id(),
+      message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 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 9a824a0..4fec561 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
@@ -17,9 +17,9 @@
 
 #include <vector>
 
-#include "source/fuzz/fact_manager.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 {
@@ -38,13 +38,15 @@
   // - |message_.type_id| must be the id of a composite type
   // - |message_.constituent_id| must refer to ids that match the constituent
   //   types of this composite type
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      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|.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_null.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_null.cpp
new file mode 100644
index 0000000..dedbc21
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_null.cpp
@@ -0,0 +1,66 @@
+// 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_constant_null.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddConstantNull::TransformationAddConstantNull(
+    const spvtools::fuzz::protobufs::TransformationAddConstantNull& message)
+    : message_(message) {}
+
+TransformationAddConstantNull::TransformationAddConstantNull(uint32_t fresh_id,
+                                                             uint32_t type_id) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_type_id(type_id);
+}
+
+bool TransformationAddConstantNull::IsApplicable(
+    opt::IRContext* context, const TransformationContext& /*unused*/) const {
+  // A fresh id is required.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  auto type = context->get_type_mgr()->GetType(message_.type_id());
+  // The type must exist.
+  if (!type) {
+    return false;
+  }
+  // The type must be one of the types for which null constants are allowed,
+  // according to the SPIR-V spec.
+  return fuzzerutil::IsNullConstantSupported(*type);
+}
+
+void TransformationAddConstantNull::Apply(
+    opt::IRContext* context, TransformationContext* /*unused*/) const {
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context, SpvOpConstantNull, message_.type_id(), message_.fresh_id(),
+      opt::Instruction::OperandList()));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddConstantNull::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_constant_null() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_null.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_null.h
new file mode 100644
index 0000000..590fc0d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_null.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_NULL_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_NULL_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 TransformationAddConstantNull : public Transformation {
+ public:
+  explicit TransformationAddConstantNull(
+      const protobufs::TransformationAddConstantNull& message);
+
+  TransformationAddConstantNull(uint32_t fresh_id, uint32_t type_id);
+
+  // - |message_.fresh_id| must be fresh
+  // - |message_.type_id| must be the id of a type for which it is acceptable
+  //   to create a null constant
+  bool IsApplicable(
+      opt::IRContext* context,
+      const TransformationContext& transformation_context) const override;
+
+  // Adds an OpConstantNull instruction to the module, with |message_.type_id|
+  // as its type.  The instruction has result id |message_.fresh_id|.
+  void Apply(opt::IRContext* context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddConstantNull message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_NULL_H_
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 36af5e0..e13d08f 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
@@ -33,14 +33,13 @@
 }
 
 bool TransformationAddConstantScalar::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // The id needs to be fresh.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
   // The type id for the scalar must exist and be a type.
-  auto type = context->get_type_mgr()->GetType(message_.type_id());
+  auto type = ir_context->get_type_mgr()->GetType(message_.type_id());
   if (!type) {
     return false;
   }
@@ -61,20 +60,21 @@
 }
 
 void TransformationAddConstantScalar::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   opt::Instruction::OperandList operand_list;
   for (auto word : message_.word()) {
     operand_list.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {word}});
   }
-  context->module()->AddGlobalValue(
-      MakeUnique<opt::Instruction>(context, SpvOpConstant, message_.type_id(),
-                                   message_.fresh_id(), operand_list));
+  ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpConstant, message_.type_id(), message_.fresh_id(),
+      operand_list));
 
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
 
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 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 914cfe6..e0ed39f 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
@@ -17,9 +17,9 @@
 
 #include <vector>
 
-#include "source/fuzz/fact_manager.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 {
@@ -37,11 +37,13 @@
   // - |message_.type_id| must be the id of a floating-point or integer type
   // - The size of |message_.word| must be compatible with the width of this
   //   type
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds a new OpConstant instruction with the given type and words.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
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 b58f75e..b246c3f 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,16 +32,15 @@
 }
 
 bool TransformationAddDeadBlock::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // The new block's id must be fresh.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
 
   // First, we check that a constant with the same value as
   // |message_.condition_value| is present.
-  if (!fuzzerutil::MaybeGetBoolConstantId(context,
+  if (!fuzzerutil::MaybeGetBoolConstantId(ir_context,
                                           message_.condition_value())) {
     // The required constant is not present, so the transformation cannot be
     // applied.
@@ -50,7 +49,7 @@
 
   // The existing block must indeed exist.
   auto existing_block =
-      fuzzerutil::MaybeFindBlock(context, message_.existing_block());
+      fuzzerutil::MaybeFindBlock(ir_context, message_.existing_block());
   if (!existing_block) {
     return false;
   }
@@ -68,13 +67,13 @@
   // Its successor must not be a merge block nor continue target.
   auto successor_block_id =
       existing_block->terminator()->GetSingleWordInOperand(0);
-  if (fuzzerutil::IsMergeOrContinue(context, successor_block_id)) {
+  if (fuzzerutil::IsMergeOrContinue(ir_context, successor_block_id)) {
     return false;
   }
 
   // The successor must not be a loop header (i.e., |message_.existing_block|
   // must not be a back-edge block.
-  if (context->cfg()->block(successor_block_id)->IsLoopHeader()) {
+  if (ir_context->cfg()->block(successor_block_id)->IsLoopHeader()) {
     return false;
   }
 
@@ -82,34 +81,36 @@
 }
 
 void TransformationAddDeadBlock::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   // Update the module id bound so that it is at least the id of the new block.
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
 
   // Get the existing block and its successor.
-  auto existing_block = context->cfg()->block(message_.existing_block());
+  auto existing_block = ir_context->cfg()->block(message_.existing_block());
   auto successor_block_id =
       existing_block->terminator()->GetSingleWordInOperand(0);
 
   // Get the id of the boolean value that will be used as the branch condition.
-  auto bool_id =
-      fuzzerutil::MaybeGetBoolConstantId(context, message_.condition_value());
+  auto bool_id = fuzzerutil::MaybeGetBoolConstantId(ir_context,
+                                                    message_.condition_value());
 
   // Make a new block that unconditionally branches to the original successor
   // block.
   auto enclosing_function = existing_block->GetParent();
-  std::unique_ptr<opt::BasicBlock> new_block = MakeUnique<opt::BasicBlock>(
-      MakeUnique<opt::Instruction>(context, SpvOpLabel, 0, message_.fresh_id(),
-                                   opt::Instruction::OperandList()));
+  std::unique_ptr<opt::BasicBlock> new_block =
+      MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpLabel, 0, message_.fresh_id(),
+          opt::Instruction::OperandList()));
   new_block->AddInstruction(MakeUnique<opt::Instruction>(
-      context, SpvOpBranch, 0, 0,
+      ir_context, SpvOpBranch, 0, 0,
       opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_ID, {successor_block_id}}})));
 
   // Turn the original block into a selection merge, with its original successor
   // as the merge block.
   existing_block->terminator()->InsertBefore(MakeUnique<opt::Instruction>(
-      context, SpvOpSelectionMerge, 0, 0,
+      ir_context, SpvOpSelectionMerge, 0, 0,
       opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_ID, {successor_block_id}},
            {SPV_OPERAND_TYPE_SELECTION_CONTROL,
@@ -135,7 +136,8 @@
                                             existing_block);
 
   // Record the fact that the new block is dead.
-  fact_manager->AddFactBlockIsDead(message_.fresh_id());
+  transformation_context->GetFactManager()->AddFactBlockIsDead(
+      message_.fresh_id());
 
   // Fix up OpPhi instructions in the successor block, so that the values they
   // yield when control has transferred from the new block are the same as if
@@ -143,7 +145,7 @@
   // to be valid since |message_.existing_block| dominates the new block by
   // construction.  Other transformations can change these phi operands to more
   // interesting values.
-  context->cfg()
+  ir_context->cfg()
       ->block(successor_block_id)
       ->ForEachPhiInst([this](opt::Instruction* phi_inst) {
         // Copy the operand that provides the phi value for the first of any
@@ -156,7 +158,7 @@
 
   // Do not rely on any existing analysis results since the control flow graph
   // of the module has changed.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationAddDeadBlock::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_block.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_block.h
index 059daca9..7d07616 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_block.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_block.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_
 #define SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -41,15 +41,17 @@
   // - |message_.existing_block| must not be a back-edge block, since in this
   //   case the newly-added block would lead to another back-edge to the
   //   associated loop header
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Changes the OpBranch from |message_.existing_block| to its successor 's'
   // to an OpBranchConditional to either 's' or a new block,
   // |message_.fresh_id|, which itself unconditionally branches to 's'.  The
   // conditional branch uses |message.condition_value| as its condition, and is
   // arranged so that control will pass to 's' at runtime.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
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 43847fa..db9de7d 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
@@ -14,8 +14,8 @@
 
 #include "source/fuzz/transformation_add_dead_break.h"
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/basic_block.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/struct_cfg_analysis.h"
@@ -39,7 +39,7 @@
 }
 
 bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow(
-    opt::IRContext* context, opt::BasicBlock* bb_from) const {
+    opt::IRContext* ir_context, opt::BasicBlock* bb_from) const {
   // Look at the structured control flow associated with |from_block| and
   // check whether it is contained in an appropriate construct with merge id
   // |to_block| such that a break from |from_block| to |to_block| is legal.
@@ -70,7 +70,7 @@
   // structured control flow construct.
 
   auto containing_construct =
-      context->GetStructuredCFGAnalysis()->ContainingConstruct(
+      ir_context->GetStructuredCFGAnalysis()->ContainingConstruct(
           message_.from_block());
   if (!containing_construct) {
     // |from_block| is not in a construct from which we can break.
@@ -79,7 +79,7 @@
 
   // Consider case (2)
   if (message_.to_block() ==
-      context->cfg()->block(containing_construct)->MergeBlockId()) {
+      ir_context->cfg()->block(containing_construct)->MergeBlockId()) {
     // This looks like an instance of case (2).
     // However, the structured CFG analysis regards the continue construct of a
     // loop as part of the loop, but it is not legal to jump from a loop's
@@ -90,28 +90,29 @@
     //  currently allow a dead break from a back edge block, but we could and
     //  ultimately should.
     return !fuzzerutil::BlockIsInLoopContinueConstruct(
-        context, message_.from_block(), containing_construct);
+        ir_context, message_.from_block(), containing_construct);
   }
 
   // Case (3) holds if and only if |to_block| is the merge block for this
   // innermost loop that contains |from_block|
   auto containing_loop_header =
-      context->GetStructuredCFGAnalysis()->ContainingLoop(
+      ir_context->GetStructuredCFGAnalysis()->ContainingLoop(
           message_.from_block());
   if (containing_loop_header &&
       message_.to_block() ==
-          context->cfg()->block(containing_loop_header)->MergeBlockId()) {
+          ir_context->cfg()->block(containing_loop_header)->MergeBlockId()) {
     return !fuzzerutil::BlockIsInLoopContinueConstruct(
-        context, message_.from_block(), containing_loop_header);
+        ir_context, message_.from_block(), containing_loop_header);
   }
   return false;
 }
 
 bool TransformationAddDeadBreak::IsApplicable(
-    opt::IRContext* context, const FactManager& /*unused*/) const {
+    opt::IRContext* ir_context,
+    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(context,
+  if (!fuzzerutil::MaybeGetBoolConstantId(ir_context,
                                           message_.break_condition_value())) {
     // The required constant is not present, so the transformation cannot be
     // applied.
@@ -121,17 +122,17 @@
   // Check that |message_.from_block| and |message_.to_block| really are block
   // ids
   opt::BasicBlock* bb_from =
-      fuzzerutil::MaybeFindBlock(context, message_.from_block());
+      fuzzerutil::MaybeFindBlock(ir_context, message_.from_block());
   if (bb_from == nullptr) {
     return false;
   }
   opt::BasicBlock* bb_to =
-      fuzzerutil::MaybeFindBlock(context, message_.to_block());
+      fuzzerutil::MaybeFindBlock(ir_context, message_.to_block());
   if (bb_to == nullptr) {
     return false;
   }
 
-  if (!fuzzerutil::BlockIsReachableInItsFunction(context, bb_to)) {
+  if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, bb_to)) {
     // If the target of the break is unreachable, we conservatively do not
     // allow adding a dead break, to avoid the compilations that arise due to
     // the lack of sensible dominance information for unreachable blocks.
@@ -157,14 +158,14 @@
       "The id of the block we found should match the target id for the break.");
 
   // Check whether the data passed to extend OpPhi instructions is appropriate.
-  if (!fuzzerutil::PhiIdsOkForNewEdge(context, bb_from, bb_to,
+  if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, bb_from, bb_to,
                                       message_.phi_id())) {
     return false;
   }
 
   // Check that adding the break would respect the rules of structured
   // control flow.
-  if (!AddingBreakRespectsStructuredControlFlow(context, bb_from)) {
+  if (!AddingBreakRespectsStructuredControlFlow(ir_context, bb_from)) {
     return false;
   }
 
@@ -177,16 +178,18 @@
   // being places on the validator.  This should be revisited if we are sure
   // the validator is complete with respect to checking structured control flow
   // rules.
-  auto cloned_context = fuzzerutil::CloneIRContext(context);
+  auto cloned_context = fuzzerutil::CloneIRContext(ir_context);
   ApplyImpl(cloned_context.get());
-  return fuzzerutil::IsValid(cloned_context.get());
+  return fuzzerutil::IsValid(cloned_context.get(),
+                             transformation_context.GetValidatorOptions());
 }
 
-void TransformationAddDeadBreak::Apply(opt::IRContext* context,
-                                       FactManager* /*unused*/) const {
-  ApplyImpl(context);
+void TransformationAddDeadBreak::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  ApplyImpl(ir_context);
   // Invalidate all analyses
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationAddDeadBreak::ToMessage() const {
@@ -196,10 +199,10 @@
 }
 
 void TransformationAddDeadBreak::ApplyImpl(
-    spvtools::opt::IRContext* context) const {
+    spvtools::opt::IRContext* ir_context) const {
   fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
-      context, context->cfg()->block(message_.from_block()),
-      context->cfg()->block(message_.to_block()),
+      ir_context, ir_context->cfg()->block(message_.from_block()),
+      ir_context->cfg()->block(message_.to_block()),
       message_.break_condition_value(), message_.phi_id());
 }
 
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 81a2c99..0ea9210 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
@@ -17,9 +17,9 @@
 
 #include <vector>
 
-#include "source/fuzz/fact_manager.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 {
@@ -50,21 +50,23 @@
   //   maintain validity of the module.
   //   In particular, the new branch must not lead to violations of the rule
   //   that a use must be dominated by its definition.
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Replaces the terminator of a with a conditional branch to b or c.
   // The boolean constant associated with |message_.break_condition_value| is
   // used as the condition, and the order of b and c is arranged such that
   // control is guaranteed to jump to c.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
  private:
   // Returns true if and only if adding an edge from |bb_from| to
   // |message_.to_block| respects structured control flow.
-  bool AddingBreakRespectsStructuredControlFlow(opt::IRContext* context,
+  bool AddingBreakRespectsStructuredControlFlow(opt::IRContext* ir_context,
                                                 opt::BasicBlock* bb_from) const;
 
   // Used by 'Apply' to actually apply the transformation to the module of
@@ -73,7 +75,7 @@
   // 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* context) const;
+  void ApplyImpl(opt::IRContext* ir_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 3a4875e..1fc6d67 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
@@ -34,11 +34,12 @@
 }
 
 bool TransformationAddDeadContinue::IsApplicable(
-    opt::IRContext* context, const FactManager& /*unused*/) const {
+    opt::IRContext* ir_context,
+    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(
-          context, message_.continue_condition_value())) {
+          ir_context, message_.continue_condition_value())) {
     // The required constant is not present, so the transformation cannot be
     // applied.
     return false;
@@ -46,7 +47,7 @@
 
   // Check that |message_.from_block| really is a block id.
   opt::BasicBlock* bb_from =
-      fuzzerutil::MaybeFindBlock(context, message_.from_block());
+      fuzzerutil::MaybeFindBlock(ir_context, message_.from_block());
   if (bb_from == nullptr) {
     return false;
   }
@@ -68,31 +69,33 @@
   // Because the structured CFG analysis does not regard a loop header as part
   // of the loop it heads, we check first whether bb_from is a loop header
   // before using the structured CFG analysis.
-  auto loop_header = bb_from->IsLoopHeader()
-                         ? message_.from_block()
-                         : context->GetStructuredCFGAnalysis()->ContainingLoop(
-                               message_.from_block());
+  auto loop_header =
+      bb_from->IsLoopHeader()
+          ? message_.from_block()
+          : ir_context->GetStructuredCFGAnalysis()->ContainingLoop(
+                message_.from_block());
   if (!loop_header) {
     return false;
   }
 
-  auto continue_block = context->cfg()->block(loop_header)->ContinueBlockId();
+  auto continue_block =
+      ir_context->cfg()->block(loop_header)->ContinueBlockId();
 
   if (!fuzzerutil::BlockIsReachableInItsFunction(
-          context, context->cfg()->block(continue_block))) {
+          ir_context, ir_context->cfg()->block(continue_block))) {
     // If the loop's continue block is unreachable, we conservatively do not
     // allow adding a dead continue, to avoid the compilations that arise due to
     // the lack of sensible dominance information for unreachable blocks.
     return false;
   }
 
-  if (fuzzerutil::BlockIsInLoopContinueConstruct(context, message_.from_block(),
-                                                 loop_header)) {
+  if (fuzzerutil::BlockIsInLoopContinueConstruct(
+          ir_context, message_.from_block(), loop_header)) {
     // We cannot jump to the continue target from the continue construct.
     return false;
   }
 
-  if (context->GetStructuredCFGAnalysis()->IsMergeBlock(continue_block)) {
+  if (ir_context->GetStructuredCFGAnalysis()->IsMergeBlock(continue_block)) {
     // A branch straight to the continue target that is also a merge block might
     // break the property that a construct header must dominate its merge block
     // (if the merge block is reachable).
@@ -100,8 +103,8 @@
   }
 
   // Check whether the data passed to extend OpPhi instructions is appropriate.
-  if (!fuzzerutil::PhiIdsOkForNewEdge(context, bb_from,
-                                      context->cfg()->block(continue_block),
+  if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, bb_from,
+                                      ir_context->cfg()->block(continue_block),
                                       message_.phi_id())) {
     return false;
   }
@@ -115,16 +118,18 @@
   // being placed on the validator.  This should be revisited if we are sure
   // the validator is complete with respect to checking structured control flow
   // rules.
-  auto cloned_context = fuzzerutil::CloneIRContext(context);
+  auto cloned_context = fuzzerutil::CloneIRContext(ir_context);
   ApplyImpl(cloned_context.get());
-  return fuzzerutil::IsValid(cloned_context.get());
+  return fuzzerutil::IsValid(cloned_context.get(),
+                             transformation_context.GetValidatorOptions());
 }
 
-void TransformationAddDeadContinue::Apply(opt::IRContext* context,
-                                          FactManager* /*unused*/) const {
-  ApplyImpl(context);
+void TransformationAddDeadContinue::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  ApplyImpl(ir_context);
   // Invalidate all analyses
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationAddDeadContinue::ToMessage() const {
@@ -134,16 +139,16 @@
 }
 
 void TransformationAddDeadContinue::ApplyImpl(
-    spvtools::opt::IRContext* context) const {
-  auto bb_from = context->cfg()->block(message_.from_block());
+    spvtools::opt::IRContext* ir_context) const {
+  auto bb_from = ir_context->cfg()->block(message_.from_block());
   auto continue_block =
       bb_from->IsLoopHeader()
           ? bb_from->ContinueBlockId()
-          : context->GetStructuredCFGAnalysis()->LoopContinueBlock(
+          : ir_context->GetStructuredCFGAnalysis()->LoopContinueBlock(
                 message_.from_block());
   assert(continue_block && "message_.from_block must be in a loop.");
   fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
-      context, bb_from, context->cfg()->block(continue_block),
+      ir_context, bb_from, ir_context->cfg()->block(continue_block),
       message_.continue_condition_value(), message_.phi_id());
 }
 
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 86b4c93..1053c16 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
@@ -17,9 +17,9 @@
 
 #include <vector>
 
-#include "source/fuzz/fact_manager.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 {
@@ -52,14 +52,16 @@
   //   In particular, adding an edge from somewhere in the loop to the continue
   //   target must not prevent uses of ids in the continue target from being
   //   dominated by the definitions of those ids.
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Replaces the terminator of a with a conditional branch to b or c.
   // The boolean constant associated with |message_.continue_condition_value| is
   // used as the condition, and the order of b and c is arranged such that
   // control is guaranteed to jump to c.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
@@ -70,7 +72,7 @@
   // 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* context) const;
+  void ApplyImpl(opt::IRContext* ir_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 8f0d3c9..90276ed 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.cpp
@@ -56,8 +56,8 @@
 }
 
 bool TransformationAddFunction::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& fact_manager) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   // This transformation may use a lot of ids, all of which need to be fresh
   // and distinct.  This set tracks them.
   std::set<uint32_t> ids_used_by_this_transformation;
@@ -66,7 +66,7 @@
   for (auto& instruction : message_.instruction()) {
     if (instruction.result_id()) {
       if (!CheckIdIsFreshAndNotUsedByThisTransformation(
-              instruction.result_id(), context,
+              instruction.result_id(), ir_context,
               &ids_used_by_this_transformation)) {
         return false;
       }
@@ -77,28 +77,28 @@
     // Ensure that all ids provided for making the function livesafe are fresh
     // and distinct.
     if (!CheckIdIsFreshAndNotUsedByThisTransformation(
-            message_.loop_limiter_variable_id(), context,
+            message_.loop_limiter_variable_id(), ir_context,
             &ids_used_by_this_transformation)) {
       return false;
     }
     for (auto& loop_limiter_info : message_.loop_limiter_info()) {
       if (!CheckIdIsFreshAndNotUsedByThisTransformation(
-              loop_limiter_info.load_id(), context,
+              loop_limiter_info.load_id(), ir_context,
               &ids_used_by_this_transformation)) {
         return false;
       }
       if (!CheckIdIsFreshAndNotUsedByThisTransformation(
-              loop_limiter_info.increment_id(), context,
+              loop_limiter_info.increment_id(), ir_context,
               &ids_used_by_this_transformation)) {
         return false;
       }
       if (!CheckIdIsFreshAndNotUsedByThisTransformation(
-              loop_limiter_info.compare_id(), context,
+              loop_limiter_info.compare_id(), ir_context,
               &ids_used_by_this_transformation)) {
         return false;
       }
       if (!CheckIdIsFreshAndNotUsedByThisTransformation(
-              loop_limiter_info.logical_op_id(), context,
+              loop_limiter_info.logical_op_id(), ir_context,
               &ids_used_by_this_transformation)) {
         return false;
       }
@@ -107,11 +107,11 @@
          message_.access_chain_clamping_info()) {
       for (auto& pair : access_chain_clamping_info.compare_and_select_ids()) {
         if (!CheckIdIsFreshAndNotUsedByThisTransformation(
-                pair.first(), context, &ids_used_by_this_transformation)) {
+                pair.first(), ir_context, &ids_used_by_this_transformation)) {
           return false;
         }
         if (!CheckIdIsFreshAndNotUsedByThisTransformation(
-                pair.second(), context, &ids_used_by_this_transformation)) {
+                pair.second(), ir_context, &ids_used_by_this_transformation)) {
           return false;
         }
       }
@@ -123,8 +123,8 @@
   // is taken here.
 
   // We first clone the current module, so that we can try adding the new
-  // function without risking wrecking |context|.
-  auto cloned_module = fuzzerutil::CloneIRContext(context);
+  // function without risking wrecking |ir_context|.
+  auto cloned_module = fuzzerutil::CloneIRContext(ir_context);
 
   // We try to add a function to the cloned module, which may fail if
   // |message_.instruction| is not sufficiently well-formed.
@@ -134,12 +134,14 @@
 
   // Check whether the cloned module is still valid after adding the function.
   // If it is not, the transformation is not applicable.
-  if (!fuzzerutil::IsValid(cloned_module.get())) {
+  if (!fuzzerutil::IsValid(cloned_module.get(),
+                           transformation_context.GetValidatorOptions())) {
     return false;
   }
 
   if (message_.is_livesafe()) {
-    if (!TryToMakeFunctionLivesafe(cloned_module.get(), fact_manager)) {
+    if (!TryToMakeFunctionLivesafe(cloned_module.get(),
+                                   transformation_context)) {
       return false;
     }
     // After making the function livesafe, we check validity of the module
@@ -148,7 +150,8 @@
     // has the potential to make the module invalid when it was otherwise valid.
     // It is simpler to rely on the validator to guard against this than to
     // consider all scenarios when making a function livesafe.
-    if (!fuzzerutil::IsValid(cloned_module.get())) {
+    if (!fuzzerutil::IsValid(cloned_module.get(),
+                             transformation_context.GetValidatorOptions())) {
       return false;
     }
   }
@@ -156,10 +159,11 @@
 }
 
 void TransformationAddFunction::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   // Add the function to the module.  As the transformation is applicable, this
   // should succeed.
-  bool success = TryToAddFunction(context);
+  bool success = TryToAddFunction(ir_context);
   assert(success && "The function should be successfully added.");
   (void)(success);  // Keep release builds happy (otherwise they may complain
                     // that |success| is not used).
@@ -172,16 +176,16 @@
   for (auto& instruction : message_.instruction()) {
     switch (instruction.opcode()) {
       case SpvOpFunctionParameter:
-        if (context->get_def_use_mgr()
+        if (ir_context->get_def_use_mgr()
                 ->GetDef(instruction.result_type_id())
                 ->opcode() == SpvOpTypePointer) {
-          fact_manager->AddFactValueOfPointeeIsIrrelevant(
-              instruction.result_id());
+          transformation_context->GetFactManager()
+              ->AddFactValueOfPointeeIsIrrelevant(instruction.result_id());
         }
         break;
       case SpvOpVariable:
-        fact_manager->AddFactValueOfPointeeIsIrrelevant(
-            instruction.result_id());
+        transformation_context->GetFactManager()
+            ->AddFactValueOfPointeeIsIrrelevant(instruction.result_id());
         break;
       default:
         break;
@@ -190,7 +194,7 @@
 
   if (message_.is_livesafe()) {
     // Make the function livesafe, which also should succeed.
-    success = TryToMakeFunctionLivesafe(context, *fact_manager);
+    success = TryToMakeFunctionLivesafe(ir_context, *transformation_context);
     assert(success && "It should be possible to make the function livesafe.");
     (void)(success);  // Keep release builds happy.
 
@@ -198,17 +202,18 @@
     assert(message_.instruction(0).opcode() == SpvOpFunction &&
            "The first instruction of an 'add function' transformation must be "
            "OpFunction.");
-    fact_manager->AddFactFunctionIsLivesafe(
+    transformation_context->GetFactManager()->AddFactFunctionIsLivesafe(
         message_.instruction(0).result_id());
   } else {
     // Inform the fact manager that all blocks in the function are dead.
     for (auto& inst : message_.instruction()) {
       if (inst.opcode() == SpvOpLabel) {
-        fact_manager->AddFactBlockIsDead(inst.result_id());
+        transformation_context->GetFactManager()->AddFactBlockIsDead(
+            inst.result_id());
       }
     }
   }
-  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationAddFunction::ToMessage() const {
@@ -218,9 +223,9 @@
 }
 
 bool TransformationAddFunction::TryToAddFunction(
-    opt::IRContext* context) const {
+    opt::IRContext* ir_context) const {
   // This function returns false if |message_.instruction| was not well-formed
-  // enough to actually create a function and add it to |context|.
+  // enough to actually create a function and add it to |ir_context|.
 
   // A function must have at least some instructions.
   if (message_.instruction().empty()) {
@@ -235,7 +240,7 @@
 
   // Make a function, headed by the OpFunction instruction.
   std::unique_ptr<opt::Function> new_function = MakeUnique<opt::Function>(
-      InstructionFromMessage(context, function_begin));
+      InstructionFromMessage(ir_context, function_begin));
 
   // Keeps track of which instruction protobuf message we are currently
   // considering.
@@ -249,7 +254,7 @@
          message_.instruction(instruction_index).opcode() ==
              SpvOpFunctionParameter) {
     new_function->AddParameter(InstructionFromMessage(
-        context, message_.instruction(instruction_index)));
+        ir_context, message_.instruction(instruction_index)));
     instruction_index++;
   }
 
@@ -270,7 +275,7 @@
     // as its parent.
     std::unique_ptr<opt::BasicBlock> block =
         MakeUnique<opt::BasicBlock>(InstructionFromMessage(
-            context, message_.instruction(instruction_index)));
+            ir_context, message_.instruction(instruction_index)));
     block->SetParent(new_function.get());
 
     // Consider successive instructions until we hit another label or the end
@@ -281,7 +286,7 @@
                SpvOpFunctionEnd &&
            message_.instruction(instruction_index).opcode() != SpvOpLabel) {
       block->AddInstruction(InstructionFromMessage(
-          context, message_.instruction(instruction_index)));
+          ir_context, message_.instruction(instruction_index)));
       instruction_index++;
     }
     // Add the block to the new function.
@@ -295,22 +300,23 @@
   }
   // Set the function's final instruction, add the function to the module and
   // report success.
-  new_function->SetFunctionEnd(
-      InstructionFromMessage(context, message_.instruction(instruction_index)));
-  context->AddFunction(std::move(new_function));
+  new_function->SetFunctionEnd(InstructionFromMessage(
+      ir_context, message_.instruction(instruction_index)));
+  ir_context->AddFunction(std::move(new_function));
 
-  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 
   return true;
 }
 
 bool TransformationAddFunction::TryToMakeFunctionLivesafe(
-    opt::IRContext* context, const FactManager& fact_manager) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   assert(message_.is_livesafe() && "Precondition: is_livesafe must hold.");
 
   // Get a pointer to the added function.
   opt::Function* added_function = nullptr;
-  for (auto& function : *context->module()) {
+  for (auto& function : *ir_context->module()) {
     if (function.result_id() == message_.instruction(0).result_id()) {
       added_function = &function;
       break;
@@ -318,7 +324,7 @@
   }
   assert(added_function && "The added function should have been found.");
 
-  if (!TryToAddLoopLimiters(context, added_function)) {
+  if (!TryToAddLoopLimiters(ir_context, added_function)) {
     // Adding loop limiters did not work; bail out.
     return false;
   }
@@ -332,20 +338,20 @@
       switch (inst.opcode()) {
         case SpvOpKill:
         case SpvOpUnreachable:
-          if (!TryToTurnKillOrUnreachableIntoReturn(context, added_function,
+          if (!TryToTurnKillOrUnreachableIntoReturn(ir_context, added_function,
                                                     &inst)) {
             return false;
           }
           break;
         case SpvOpAccessChain:
         case SpvOpInBoundsAccessChain:
-          if (!TryToClampAccessChainIndices(context, &inst)) {
+          if (!TryToClampAccessChainIndices(ir_context, &inst)) {
             return false;
           }
           break;
         case SpvOpFunctionCall:
           // A livesafe function my only call other livesafe functions.
-          if (!fact_manager.FunctionIsLivesafe(
+          if (!transformation_context.GetFactManager()->FunctionIsLivesafe(
                   inst.GetSingleWordInOperand(0))) {
             return false;
           }
@@ -358,7 +364,7 @@
 }
 
 bool TransformationAddFunction::TryToAddLoopLimiters(
-    opt::IRContext* context, opt::Function* added_function) const {
+    opt::IRContext* ir_context, opt::Function* added_function) const {
   // Collect up all the loop headers so that we can subsequently add loop
   // limiting logic.
   std::vector<opt::BasicBlock*> loop_headers;
@@ -377,7 +383,7 @@
   // manipulating a loop limiter.
 
   auto loop_limit_constant_id_instr =
-      context->get_def_use_mgr()->GetDef(message_.loop_limit_constant_id());
+      ir_context->get_def_use_mgr()->GetDef(message_.loop_limit_constant_id());
   if (!loop_limit_constant_id_instr ||
       loop_limit_constant_id_instr->opcode() != SpvOpConstant) {
     // The loop limit constant id instruction must exist and have an
@@ -385,7 +391,7 @@
     return false;
   }
 
-  auto loop_limit_type = context->get_def_use_mgr()->GetDef(
+  auto loop_limit_type = ir_context->get_def_use_mgr()->GetDef(
       loop_limit_constant_id_instr->type_id());
   if (loop_limit_type->opcode() != SpvOpTypeInt ||
       loop_limit_type->GetSingleWordInOperand(0) != 32) {
@@ -397,36 +403,36 @@
   // Find the id of the "unsigned int" type.
   opt::analysis::Integer unsigned_int_type(32, false);
   uint32_t unsigned_int_type_id =
-      context->get_type_mgr()->GetId(&unsigned_int_type);
+      ir_context->get_type_mgr()->GetId(&unsigned_int_type);
   if (!unsigned_int_type_id) {
     // Unsigned int is not available; we need this type in order to add loop
     // limiters.
     return false;
   }
   auto registered_unsigned_int_type =
-      context->get_type_mgr()->GetRegisteredType(&unsigned_int_type);
+      ir_context->get_type_mgr()->GetRegisteredType(&unsigned_int_type);
 
   // Look for 0 of type unsigned int.
   opt::analysis::IntConstant zero(registered_unsigned_int_type->AsInteger(),
                                   {0});
-  auto registered_zero = context->get_constant_mgr()->FindConstant(&zero);
+  auto registered_zero = ir_context->get_constant_mgr()->FindConstant(&zero);
   if (!registered_zero) {
     // We need 0 in order to be able to initialize loop limiters.
     return false;
   }
-  uint32_t zero_id = context->get_constant_mgr()
+  uint32_t zero_id = ir_context->get_constant_mgr()
                          ->GetDefiningInstruction(registered_zero)
                          ->result_id();
 
   // Look for 1 of type unsigned int.
   opt::analysis::IntConstant one(registered_unsigned_int_type->AsInteger(),
                                  {1});
-  auto registered_one = context->get_constant_mgr()->FindConstant(&one);
+  auto registered_one = ir_context->get_constant_mgr()->FindConstant(&one);
   if (!registered_one) {
     // We need 1 in order to be able to increment loop limiters.
     return false;
   }
-  uint32_t one_id = context->get_constant_mgr()
+  uint32_t one_id = ir_context->get_constant_mgr()
                         ->GetDefiningInstruction(registered_one)
                         ->result_id();
 
@@ -434,7 +440,7 @@
   opt::analysis::Pointer pointer_to_unsigned_int_type(
       registered_unsigned_int_type, SpvStorageClassFunction);
   uint32_t pointer_to_unsigned_int_type_id =
-      context->get_type_mgr()->GetId(&pointer_to_unsigned_int_type);
+      ir_context->get_type_mgr()->GetId(&pointer_to_unsigned_int_type);
   if (!pointer_to_unsigned_int_type_id) {
     // We need pointer-to-unsigned int in order to declare the loop limiter
     // variable.
@@ -443,7 +449,7 @@
 
   // Look for bool type.
   opt::analysis::Bool bool_type;
-  uint32_t bool_type_id = context->get_type_mgr()->GetId(&bool_type);
+  uint32_t bool_type_id = ir_context->get_type_mgr()->GetId(&bool_type);
   if (!bool_type_id) {
     // We need bool in order to compare the loop limiter's value with the loop
     // limit constant.
@@ -454,22 +460,23 @@
   // block, via an instruction of the form:
   //   %loop_limiter_var = SpvOpVariable %ptr_to_uint Function %zero
   added_function->begin()->begin()->InsertBefore(MakeUnique<opt::Instruction>(
-      context, SpvOpVariable, pointer_to_unsigned_int_type_id,
+      ir_context, SpvOpVariable, pointer_to_unsigned_int_type_id,
       message_.loop_limiter_variable_id(),
       opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
            {SPV_OPERAND_TYPE_ID, {zero_id}}})));
   // Update the module's id bound since we have added the loop limiter
   // variable id.
-  fuzzerutil::UpdateModuleIdBound(context, message_.loop_limiter_variable_id());
+  fuzzerutil::UpdateModuleIdBound(ir_context,
+                                  message_.loop_limiter_variable_id());
 
   // Consider each loop in turn.
   for (auto loop_header : loop_headers) {
     // Look for the loop's back-edge block.  This is a predecessor of the loop
     // header that is dominated by the loop header.
     uint32_t back_edge_block_id = 0;
-    for (auto pred : context->cfg()->preds(loop_header->id())) {
-      if (context->GetDominatorAnalysis(added_function)
+    for (auto pred : ir_context->cfg()->preds(loop_header->id())) {
+      if (ir_context->GetDominatorAnalysis(added_function)
               ->Dominates(loop_header->id(), pred)) {
         back_edge_block_id = pred;
         break;
@@ -481,7 +488,7 @@
       // move on from this loop.
       continue;
     }
-    auto back_edge_block = context->cfg()->block(back_edge_block_id);
+    auto back_edge_block = ir_context->cfg()->block(back_edge_block_id);
 
     // Go through the sequence of loop limiter infos and find the one
     // corresponding to this loop.
@@ -579,14 +586,15 @@
     // Add a load from the loop limiter variable, of the form:
     //   %t1 = OpLoad %uint32 %loop_limiter
     new_instructions.push_back(MakeUnique<opt::Instruction>(
-        context, SpvOpLoad, unsigned_int_type_id, loop_limiter_info.load_id(),
+        ir_context, SpvOpLoad, unsigned_int_type_id,
+        loop_limiter_info.load_id(),
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}}})));
 
     // Increment the loaded value:
     //   %t2 = OpIAdd %uint32 %t1 %one
     new_instructions.push_back(MakeUnique<opt::Instruction>(
-        context, SpvOpIAdd, unsigned_int_type_id,
+        ir_context, SpvOpIAdd, unsigned_int_type_id,
         loop_limiter_info.increment_id(),
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}},
@@ -595,7 +603,7 @@
     // Store the incremented value back to the loop limiter variable:
     //   OpStore %loop_limiter %t2
     new_instructions.push_back(MakeUnique<opt::Instruction>(
-        context, SpvOpStore, 0, 0,
+        ir_context, SpvOpStore, 0, 0,
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}},
              {SPV_OPERAND_TYPE_ID, {loop_limiter_info.increment_id()}}})));
@@ -605,7 +613,7 @@
     // or
     //   %t3 = OpULessThan %bool %t1 %loop_limit
     new_instructions.push_back(MakeUnique<opt::Instruction>(
-        context,
+        ir_context,
         compare_using_greater_than_equal ? SpvOpUGreaterThanEqual
                                          : SpvOpULessThan,
         bool_type_id, loop_limiter_info.compare_id(),
@@ -615,7 +623,7 @@
 
     if (back_edge_block_terminator->opcode() == SpvOpBranchConditional) {
       new_instructions.push_back(MakeUnique<opt::Instruction>(
-          context,
+          ir_context,
           compare_using_greater_than_equal ? SpvOpLogicalOr : SpvOpLogicalAnd,
           bool_type_id, loop_limiter_info.logical_op_id(),
           opt::Instruction::OperandList(
@@ -644,8 +652,9 @@
       // Check that, if the merge block starts with OpPhi instructions, suitable
       // ids have been provided to give these instructions a value corresponding
       // to the new incoming edge from the back edge block.
-      auto merge_block = context->cfg()->block(loop_header->MergeBlockId());
-      if (!fuzzerutil::PhiIdsOkForNewEdge(context, back_edge_block, merge_block,
+      auto merge_block = ir_context->cfg()->block(loop_header->MergeBlockId());
+      if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, back_edge_block,
+                                          merge_block,
                                           loop_limiter_info.phi_id())) {
         return false;
       }
@@ -681,16 +690,18 @@
 
     // Update the module's id bound with respect to the various ids that
     // have been used for loop limiter manipulation.
-    fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.load_id());
-    fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.increment_id());
-    fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.compare_id());
-    fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.logical_op_id());
+    fuzzerutil::UpdateModuleIdBound(ir_context, loop_limiter_info.load_id());
+    fuzzerutil::UpdateModuleIdBound(ir_context,
+                                    loop_limiter_info.increment_id());
+    fuzzerutil::UpdateModuleIdBound(ir_context, loop_limiter_info.compare_id());
+    fuzzerutil::UpdateModuleIdBound(ir_context,
+                                    loop_limiter_info.logical_op_id());
   }
   return true;
 }
 
 bool TransformationAddFunction::TryToTurnKillOrUnreachableIntoReturn(
-    opt::IRContext* context, opt::Function* added_function,
+    opt::IRContext* ir_context, opt::Function* added_function,
     opt::Instruction* kill_or_unreachable_inst) const {
   assert((kill_or_unreachable_inst->opcode() == SpvOpKill ||
           kill_or_unreachable_inst->opcode() == SpvOpUnreachable) &&
@@ -698,7 +709,7 @@
 
   // Get the function's return type.
   auto function_return_type_inst =
-      context->get_def_use_mgr()->GetDef(added_function->type_id());
+      ir_context->get_def_use_mgr()->GetDef(added_function->type_id());
 
   if (function_return_type_inst->opcode() == SpvOpTypeVoid) {
     // The function has void return type, so change this instruction to
@@ -712,7 +723,7 @@
     // We first check that the id, %id, provided with the transformation
     // specifically to turn OpKill and OpUnreachable instructions into
     // OpReturnValue %id has the same type as the function's return type.
-    if (context->get_def_use_mgr()
+    if (ir_context->get_def_use_mgr()
             ->GetDef(message_.kill_unreachable_return_value_id())
             ->type_id() != function_return_type_inst->result_id()) {
       return false;
@@ -725,7 +736,7 @@
 }
 
 bool TransformationAddFunction::TryToClampAccessChainIndices(
-    opt::IRContext* context, opt::Instruction* access_chain_inst) const {
+    opt::IRContext* ir_context, opt::Instruction* access_chain_inst) const {
   assert((access_chain_inst->opcode() == SpvOpAccessChain ||
           access_chain_inst->opcode() == SpvOpInBoundsAccessChain) &&
          "Precondition: instruction must be OpAccessChain or "
@@ -756,14 +767,14 @@
 
   // Walk the access chain, clamping each index to be within bounds if it is
   // not a constant.
-  auto base_object = context->get_def_use_mgr()->GetDef(
+  auto base_object = ir_context->get_def_use_mgr()->GetDef(
       access_chain_inst->GetSingleWordInOperand(0));
   assert(base_object && "The base object must exist.");
   auto pointer_type =
-      context->get_def_use_mgr()->GetDef(base_object->type_id());
+      ir_context->get_def_use_mgr()->GetDef(base_object->type_id());
   assert(pointer_type && pointer_type->opcode() == SpvOpTypePointer &&
          "The base object must have pointer type.");
-  auto should_be_composite_type = context->get_def_use_mgr()->GetDef(
+  auto should_be_composite_type = ir_context->get_def_use_mgr()->GetDef(
       pointer_type->GetSingleWordInOperand(1));
 
   // Consider each index input operand in turn (operand 0 is the base object).
@@ -784,41 +795,43 @@
     // Get the bound for the composite being indexed into; e.g. the number of
     // columns of matrix or the size of an array.
     uint32_t bound =
-        GetBoundForCompositeIndex(context, *should_be_composite_type);
+        GetBoundForCompositeIndex(ir_context, *should_be_composite_type);
 
     // Get the instruction associated with the index and figure out its integer
     // type.
     const uint32_t index_id = access_chain_inst->GetSingleWordInOperand(index);
-    auto index_inst = context->get_def_use_mgr()->GetDef(index_id);
+    auto index_inst = ir_context->get_def_use_mgr()->GetDef(index_id);
     auto index_type_inst =
-        context->get_def_use_mgr()->GetDef(index_inst->type_id());
+        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 =
-        context->get_type_mgr()
+        ir_context->get_type_mgr()
             ->GetType(index_type_inst->result_id())
             ->AsInteger();
 
-    if (index_inst->opcode() != SpvOpConstant) {
-      // The index is non-constant so we need to clamp it.
+    if (index_inst->opcode() != SpvOpConstant ||
+        index_inst->GetSingleWordInOperand(0) >= bound) {
+      // The index is either non-constant or an out-of-bounds constant, so we
+      // need to clamp it.
       assert(should_be_composite_type->opcode() != SpvOpTypeStruct &&
              "Access chain indices into structures are required to be "
              "constants.");
       opt::analysis::IntConstant bound_minus_one(index_int_type, {bound - 1});
-      if (!context->get_constant_mgr()->FindConstant(&bound_minus_one)) {
+      if (!ir_context->get_constant_mgr()->FindConstant(&bound_minus_one)) {
         // We do not have an integer constant whose value is |bound| -1.
         return false;
       }
 
       opt::analysis::Bool bool_type;
-      uint32_t bool_type_id = context->get_type_mgr()->GetId(&bool_type);
+      uint32_t bool_type_id = ir_context->get_type_mgr()->GetId(&bool_type);
       if (!bool_type_id) {
         // Bool type is not declared; we cannot do a comparison.
         return false;
       }
 
       uint32_t bound_minus_one_id =
-          context->get_constant_mgr()
+          ir_context->get_constant_mgr()
               ->GetDefiningInstruction(&bound_minus_one)
               ->result_id();
 
@@ -832,7 +845,7 @@
       // Compare the index with the bound via an instruction of the form:
       //   %t1 = OpULessThanEqual %bool %index %bound_minus_one
       new_instructions.push_back(MakeUnique<opt::Instruction>(
-          context, SpvOpULessThanEqual, bool_type_id, compare_id,
+          ir_context, SpvOpULessThanEqual, bool_type_id, compare_id,
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {index_inst->result_id()}},
                {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
@@ -840,7 +853,7 @@
       // Select the index if in-bounds, otherwise one less than the bound:
       //   %t2 = OpSelect %int_type %t1 %index %bound_minus_one
       new_instructions.push_back(MakeUnique<opt::Instruction>(
-          context, SpvOpSelect, index_type_inst->result_id(), select_id,
+          ir_context, SpvOpSelect, index_type_inst->result_id(), select_id,
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {compare_id}},
                {SPV_OPERAND_TYPE_ID, {index_inst->result_id()}},
@@ -851,41 +864,31 @@
 
       // Replace %index with %t2.
       access_chain_inst->SetInOperand(index, {select_id});
-      fuzzerutil::UpdateModuleIdBound(context, compare_id);
-      fuzzerutil::UpdateModuleIdBound(context, select_id);
-    } else {
-      // TODO(afd): At present the SPIR-V spec is not clear on whether
-      //  statically out-of-bounds indices mean that a module is invalid (so
-      //  that it should be rejected by the validator), or that such accesses
-      //  yield undefined results.  Via the following assertion, we assume that
-      //  functions added to the module do not feature statically out-of-bounds
-      //  accesses.
-      // Assert that the index is smaller (unsigned) than this value.
-      // Return false if it is not (to keep compilers happy).
-      if (index_inst->GetSingleWordInOperand(0) >= bound) {
-        assert(false &&
-               "The function has a statically out-of-bounds access; "
-               "this should not occur.");
-        return false;
-      }
+      fuzzerutil::UpdateModuleIdBound(ir_context, compare_id);
+      fuzzerutil::UpdateModuleIdBound(ir_context, select_id);
     }
     should_be_composite_type =
-        FollowCompositeIndex(context, *should_be_composite_type, index_id);
+        FollowCompositeIndex(ir_context, *should_be_composite_type, index_id);
   }
   return true;
 }
 
 uint32_t TransformationAddFunction::GetBoundForCompositeIndex(
-    opt::IRContext* context, const opt::Instruction& composite_type_inst) {
+    opt::IRContext* ir_context, const opt::Instruction& composite_type_inst) {
   switch (composite_type_inst.opcode()) {
     case SpvOpTypeArray:
-      return fuzzerutil::GetArraySize(composite_type_inst, context);
+      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;
@@ -893,11 +896,12 @@
 }
 
 opt::Instruction* TransformationAddFunction::FollowCompositeIndex(
-    opt::IRContext* context, const opt::Instruction& composite_type_inst,
+    opt::IRContext* ir_context, const opt::Instruction& composite_type_inst,
     uint32_t index_id) {
   uint32_t sub_object_type_id;
   switch (composite_type_inst.opcode()) {
     case SpvOpTypeArray:
+    case SpvOpTypeRuntimeArray:
       sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0);
       break;
     case SpvOpTypeMatrix:
@@ -905,12 +909,12 @@
       sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0);
       break;
     case SpvOpTypeStruct: {
-      auto index_inst = context->get_def_use_mgr()->GetDef(index_id);
+      auto index_inst = ir_context->get_def_use_mgr()->GetDef(index_id);
       assert(index_inst->opcode() == SpvOpConstant);
-      assert(
-          context->get_def_use_mgr()->GetDef(index_inst->type_id())->opcode() ==
-          SpvOpTypeInt);
-      assert(context->get_def_use_mgr()
+      assert(ir_context->get_def_use_mgr()
+                 ->GetDef(index_inst->type_id())
+                 ->opcode() == SpvOpTypeInt);
+      assert(ir_context->get_def_use_mgr()
                  ->GetDef(index_inst->type_id())
                  ->GetSingleWordInOperand(0) == 32);
       uint32_t index_value = index_inst->GetSingleWordInOperand(0);
@@ -924,7 +928,7 @@
       break;
   }
   assert(sub_object_type_id && "No sub-object found.");
-  return context->get_def_use_mgr()->GetDef(sub_object_type_id);
+  return ir_context->get_def_use_mgr()->GetDef(sub_object_type_id);
 }
 
 }  // namespace fuzz
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 848b799..5af197b 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_
 #define SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -47,12 +47,14 @@
   //   ingredients to make the function livesafe, and the function must only
   //   invoke other livesafe functions
   // - Adding the created function to the module must lead to a valid module.
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds the function defined by |message_.instruction| to the module, making
   // it livesafe if |message_.is_livesafe| holds.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
@@ -61,26 +63,26 @@
   // an array, the number of components of a vector, or the number of columns of
   // a matrix.
   static uint32_t GetBoundForCompositeIndex(
-      opt::IRContext* context, const opt::Instruction& composite_type_inst);
+      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.
   static opt::Instruction* FollowCompositeIndex(
-      opt::IRContext* context, const opt::Instruction& composite_type_inst,
+      opt::IRContext* ir_context, const opt::Instruction& composite_type_inst,
       uint32_t index_id);
 
  private:
   // Attempts to create a function from the series of instructions in
-  // |message_.instruction| and add it to |context|.
+  // |message_.instruction| and add it to |ir_context|.
   //
   // Returns false if adding the function is not possible due to the messages
   // not respecting the basic structure of a function, e.g. if there is no
-  // OpFunction instruction or no blocks; in this case |context| is left in an
-  // indeterminate state.
+  // OpFunction instruction or no blocks; in this case |ir_context| is left in
+  // an indeterminate state.
   //
-  // Otherwise returns true.  Whether |context| is valid after addition of the
-  // function depends on the contents of |message_.instruction|.
+  // Otherwise returns true.  Whether |ir_context| is valid after addition of
+  // the function depends on the contents of |message_.instruction|.
   //
   // Intended usage:
   // - Perform a dry run of this method on a clone of a module, and use
@@ -89,30 +91,31 @@
   //   added, or leads to an invalid module.
   // - If the dry run succeeds, run the method on the real module of interest,
   //   to add the function.
-  bool TryToAddFunction(opt::IRContext* context) const;
+  bool TryToAddFunction(opt::IRContext* ir_context) const;
 
   // Should only be called if |message_.is_livesafe| holds.  Attempts to make
   // the function livesafe (see FactFunctionIsLivesafe for a definition).
-  // Returns false if this is not possible, due to |message_| or |context| not
-  // containing sufficient ingredients (such as types and fresh ids) to add
+  // Returns false if this is not possible, due to |message_| or |ir_context|
+  // not containing sufficient ingredients (such as types and fresh ids) to add
   // the instrumentation necessary to make the function livesafe.
-  bool TryToMakeFunctionLivesafe(opt::IRContext* context,
-                                 const FactManager& fact_manager) const;
+  bool TryToMakeFunctionLivesafe(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const;
 
   // A helper for TryToMakeFunctionLivesafe that tries to add loop-limiting
   // logic.
-  bool TryToAddLoopLimiters(opt::IRContext* context,
+  bool TryToAddLoopLimiters(opt::IRContext* ir_context,
                             opt::Function* added_function) const;
 
   // A helper for TryToMakeFunctionLivesafe that tries to replace OpKill and
   // OpUnreachable instructions into return instructions.
   bool TryToTurnKillOrUnreachableIntoReturn(
-      opt::IRContext* context, opt::Function* added_function,
+      opt::IRContext* ir_context, opt::Function* added_function,
       opt::Instruction* kill_or_unreachable_inst) const;
 
   // A helper for TryToMakeFunctionLivesafe that tries to clamp access chain
   // indices so that they are guaranteed to be in-bounds.
-  bool TryToClampAccessChainIndices(opt::IRContext* context,
+  bool TryToClampAccessChainIndices(opt::IRContext* ir_context,
                                     opt::Instruction* access_chain_inst) const;
 
   protobufs::TransformationAddFunction message_;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_undef.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_undef.cpp
index f9585b3..ba45f22 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_undef.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_undef.cpp
@@ -30,26 +30,26 @@
 }
 
 bool TransformationAddGlobalUndef::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // A fresh id is required.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
-  auto type = context->get_type_mgr()->GetType(message_.type_id());
+  auto type = ir_context->get_type_mgr()->GetType(message_.type_id());
   // The type must exist, and must not be a function type.
   return type && !type->AsFunction();
 }
 
 void TransformationAddGlobalUndef::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
-  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-      context, SpvOpUndef, message_.type_id(), message_.fresh_id(),
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpUndef, message_.type_id(), message_.fresh_id(),
       opt::Instruction::OperandList()));
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationAddGlobalUndef::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_undef.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_undef.h
index 550d9f6..c89fe9d 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_undef.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_undef.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_
 #define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -32,12 +32,14 @@
 
   // - |message_.fresh_id| must be fresh
   // - |message_.type_id| must be the id of a non-function type
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds an OpUndef instruction to the module, with |message_.type_id| as its
   // type.  The instruction has result id |message_.fresh_id|.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
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 e4f9f7a..6464bfb 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.cpp
@@ -24,23 +24,34 @@
     : message_(message) {}
 
 TransformationAddGlobalVariable::TransformationAddGlobalVariable(
-    uint32_t fresh_id, uint32_t type_id, uint32_t initializer_id,
-    bool value_is_irrelevant) {
+    uint32_t fresh_id, uint32_t type_id, SpvStorageClass storage_class,
+    uint32_t initializer_id, bool value_is_irrelevant) {
   message_.set_fresh_id(fresh_id);
   message_.set_type_id(type_id);
+  message_.set_storage_class(storage_class);
   message_.set_initializer_id(initializer_id);
   message_.set_value_is_irrelevant(value_is_irrelevant);
 }
 
 bool TransformationAddGlobalVariable::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // The result id must be fresh.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
+
+  // The storage class must be Private or Workgroup.
+  auto storage_class = static_cast<SpvStorageClass>(message_.storage_class());
+  switch (storage_class) {
+    case SpvStorageClassPrivate:
+    case SpvStorageClassWorkgroup:
+      break;
+    default:
+      assert(false && "Unsupported storage class.");
+      return false;
+  }
   // The type id must correspond to a type.
-  auto type = context->get_type_mgr()->GetType(message_.type_id());
+  auto type = ir_context->get_type_mgr()->GetType(message_.type_id());
   if (!type) {
     return false;
   }
@@ -49,42 +60,52 @@
   if (!pointer_type) {
     return false;
   }
-  // ... with Private storage class.
-  if (pointer_type->storage_class() != SpvStorageClassPrivate) {
+  // ... with the right storage class.
+  if (pointer_type->storage_class() != storage_class) {
     return false;
   }
-  // The initializer id must be the id of a constant.  Check this with the
-  // constant manager.
-  auto constant_id = context->get_constant_mgr()->GetConstantsFromIds(
-      {message_.initializer_id()});
-  if (constant_id.empty()) {
-    return false;
-  }
-  assert(constant_id.size() == 1 &&
-         "We asked for the constant associated with a single id; we should "
-         "get a single constant.");
-  // The type of the constant must match the pointee type of the pointer.
-  if (pointer_type->pointee_type() != constant_id[0]->type()) {
-    return false;
+  if (message_.initializer_id()) {
+    // An initializer is not allowed if the storage class is Workgroup.
+    if (storage_class == SpvStorageClassWorkgroup) {
+      assert(false &&
+             "By construction this transformation should not have an "
+             "initializer when Workgroup storage class is used.");
+      return false;
+    }
+    // The initializer id must be the id of a constant.  Check this with the
+    // constant manager.
+    auto constant_id = ir_context->get_constant_mgr()->GetConstantsFromIds(
+        {message_.initializer_id()});
+    if (constant_id.empty()) {
+      return false;
+    }
+    assert(constant_id.size() == 1 &&
+           "We asked for the constant associated with a single id; we should "
+           "get a single constant.");
+    // The type of the constant must match the pointee type of the pointer.
+    if (pointer_type->pointee_type() != constant_id[0]->type()) {
+      return false;
+    }
   }
   return true;
 }
 
 void TransformationAddGlobalVariable::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   opt::Instruction::OperandList input_operands;
   input_operands.push_back(
-      {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassPrivate}});
+      {SPV_OPERAND_TYPE_STORAGE_CLASS, {message_.storage_class()}});
   if (message_.initializer_id()) {
     input_operands.push_back(
         {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
   }
-  context->module()->AddGlobalValue(
-      MakeUnique<opt::Instruction>(context, SpvOpVariable, message_.type_id(),
-                                   message_.fresh_id(), input_operands));
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_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 (PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(context)) {
+  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.
@@ -94,18 +115,20 @@
     //
     // 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()) {
+    for (auto& entry_point : ir_context->module()->entry_points()) {
       entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {message_.fresh_id()}});
     }
   }
 
   if (message_.value_is_irrelevant()) {
-    fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id());
+    transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+        message_.fresh_id());
   }
 
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationAddGlobalVariable::ToMessage() const {
@@ -115,12 +138,12 @@
 }
 
 bool TransformationAddGlobalVariable::
-    PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(
-        opt::IRContext* context) {
+    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 (context->grammar().target_env()) {
+  switch (ir_context->grammar().target_env()) {
     case SPV_ENV_UNIVERSAL_1_0:
     case SPV_ENV_UNIVERSAL_1_1:
     case SPV_ENV_UNIVERSAL_1_2:
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 920ac45..289af9e 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
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_
 #define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -29,31 +29,40 @@
       const protobufs::TransformationAddGlobalVariable& message);
 
   TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id,
+                                  SpvStorageClass storage_class,
                                   uint32_t initializer_id,
                                   bool value_is_irrelevant);
 
   // - |message_.fresh_id| must be fresh
-  // - |message_.type_id| must be the id of a pointer type with Private storage
-  //   class
-  // - |message_.initializer_id| must either be 0 or the id of a constant whose
+  // - |message_.type_id| must be the id of a pointer type with the same storage
+  //   class as |message_.storage_class|
+  // - |message_.storage_class| must be Private or Workgroup
+  // - |message_.initializer_id| must be 0 if |message_.storage_class| is
+  //   Workgroup, and otherwise may either be 0 or the id of a constant whose
   //   type is the pointee type of |message_.type_id|
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
-  // Adds a global variable with Private storage class to the module, with type
-  // |message_.type_id| and either no initializer or |message_.initializer_id|
-  // as an initializer, depending on whether |message_.initializer_id| is 0.
-  // The global variable has result id |message_.fresh_id|.
+  // Adds a global variable with storage class |message_.storage_class| to the
+  // module, with type |message_.type_id| and either no initializer or
+  // |message_.initializer_id| as an initializer, depending on whether
+  // |message_.initializer_id| is 0.  The global variable has result id
+  // |message_.fresh_id|.
   //
-  // If |message_.value_is_irrelevant| holds, adds a corresponding fact to
-  // |fact_manager|.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  // If |message_.value_is_irrelevant| holds, adds a corresponding fact to the
+  // fact manager in |transformation_context|.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
  private:
-  static bool PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(
-      opt::IRContext* context);
+  // 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_local_variable.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.cpp
index 69e536d..5136249 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
@@ -34,23 +34,22 @@
 }
 
 bool TransformationAddLocalVariable::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // The provided id must be fresh.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
   // The pointer type id must indeed correspond to a pointer, and it must have
   // function storage class.
   auto type_instruction =
-      context->get_def_use_mgr()->GetDef(message_.type_id());
+      ir_context->get_def_use_mgr()->GetDef(message_.type_id());
   if (!type_instruction || type_instruction->opcode() != SpvOpTypePointer ||
       type_instruction->GetSingleWordInOperand(0) != SpvStorageClassFunction) {
     return false;
   }
   // The initializer must...
   auto initializer_instruction =
-      context->get_def_use_mgr()->GetDef(message_.initializer_id());
+      ir_context->get_def_use_mgr()->GetDef(message_.initializer_id());
   // ... exist, ...
   if (!initializer_instruction) {
     return false;
@@ -65,17 +64,18 @@
     return false;
   }
   // The function to which the local variable is to be added must exist.
-  return fuzzerutil::FindFunction(context, message_.function_id());
+  return fuzzerutil::FindFunction(ir_context, message_.function_id());
 }
 
 void TransformationAddLocalVariable::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
-  fuzzerutil::FindFunction(context, message_.function_id())
+    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>(
-          context, SpvOpVariable, message_.type_id(), message_.fresh_id(),
+          ir_context, SpvOpVariable, message_.type_id(), message_.fresh_id(),
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_STORAGE_CLASS,
                 {
@@ -83,9 +83,10 @@
                     SpvStorageClassFunction}},
                {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}}})));
   if (message_.value_is_irrelevant()) {
-    fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id());
+    transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+        message_.fresh_id());
   }
-  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationAddLocalVariable::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.h
index b8e00dd..6460904 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_
 #define SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -38,15 +38,17 @@
   // - |message_.initializer_id| must be the id of a constant with the same
   //   type as the pointer's pointee type
   // - |message_.function_id| must be the id of a function
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds an instruction to the start of |message_.function_id|, of the form:
   //   |message_.fresh_id| = OpVariable |message_.type_id| Function
   //                         |message_.initializer_id|
-  // If |message_.value_is_irrelevant| holds, adds a corresponding fact to
-  // |fact_manager|.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  // If |message_.value_is_irrelevant| holds, adds a corresponding fact to the
+  // fact manager in |transformation_context|.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_no_contraction_decoration.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_no_contraction_decoration.cpp
index 7f22cc2..4668534 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_no_contraction_decoration.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_no_contraction_decoration.cpp
@@ -31,10 +31,9 @@
 }
 
 bool TransformationAddNoContractionDecoration::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // |message_.result_id| must be the id of an instruction.
-  auto instr = context->get_def_use_mgr()->GetDef(message_.result_id());
+  auto instr = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
   if (!instr) {
     return false;
   }
@@ -43,10 +42,10 @@
 }
 
 void TransformationAddNoContractionDecoration::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   // Add a NoContraction decoration targeting |message_.result_id|.
-  context->get_decoration_mgr()->AddDecoration(message_.result_id(),
-                                               SpvDecorationNoContraction);
+  ir_context->get_decoration_mgr()->AddDecoration(message_.result_id(),
+                                                  SpvDecorationNoContraction);
 }
 
 protobufs::Transformation TransformationAddNoContractionDecoration::ToMessage()
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_no_contraction_decoration.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_no_contraction_decoration.h
index cec1b2c..27c3a80 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_no_contraction_decoration.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_no_contraction_decoration.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_
 #define SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -34,13 +34,15 @@
   //   as defined by the SPIR-V specification.
   // - It does not matter whether this instruction is already annotated with the
   //   NoContraction decoration.
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds a decoration of the form:
   //   'OpDecoration |message_.result_id| NoContraction'
   // to the module.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_array.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_array.cpp
index 2074e98..8f5af07 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_array.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_array.cpp
@@ -32,21 +32,20 @@
 }
 
 bool TransformationAddTypeArray::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // A fresh id is required.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
   auto element_type =
-      context->get_type_mgr()->GetType(message_.element_type_id());
+      ir_context->get_type_mgr()->GetType(message_.element_type_id());
   if (!element_type || element_type->AsFunction()) {
     // The element type id either does not refer to a type, or refers to a
     // function type; both are illegal.
     return false;
   }
   auto constant =
-      context->get_constant_mgr()->GetConstantsFromIds({message_.size_id()});
+      ir_context->get_constant_mgr()->GetConstantsFromIds({message_.size_id()});
   if (constant.empty()) {
     // The size id does not refer to a constant.
     return false;
@@ -66,16 +65,17 @@
 }
 
 void TransformationAddTypeArray::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   opt::Instruction::OperandList in_operands;
   in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.element_type_id()}});
   in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.size_id()}});
-  context->module()->AddType(MakeUnique<opt::Instruction>(
-      context, SpvOpTypeArray, 0, message_.fresh_id(), in_operands));
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeArray, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationAddTypeArray::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_array.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_array.h
index b6e0718..5e9b8aa 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_array.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_array.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_
 #define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -35,13 +35,15 @@
   // - |message_.element_type_id| must be the id of a non-function type
   // - |message_.size_id| must be the id of a 32-bit integer constant that is
   //   positive when interpreted as signed.
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds an OpTypeArray instruction to the module, with element type given by
   // |message_.element_type_id| and size given by |message_.size_id|.  The
   // result id of the instruction is |message_.fresh_id|.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_boolean.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_boolean.cpp
index b55028a..77409a8 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_boolean.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_boolean.cpp
@@ -28,27 +28,27 @@
 }
 
 bool TransformationAddTypeBoolean::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // The id must be fresh.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
 
   // Applicable if there is no bool type already declared in the module.
   opt::analysis::Bool bool_type;
-  return context->get_type_mgr()->GetId(&bool_type) == 0;
+  return ir_context->get_type_mgr()->GetId(&bool_type) == 0;
 }
 
 void TransformationAddTypeBoolean::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   opt::Instruction::OperandList empty_operands;
-  context->module()->AddType(MakeUnique<opt::Instruction>(
-      context, SpvOpTypeBool, 0, message_.fresh_id(), empty_operands));
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeBool, 0, message_.fresh_id(), empty_operands));
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationAddTypeBoolean::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_boolean.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_boolean.h
index 98c1e63..5ce5b9a 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_boolean.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_boolean.h
@@ -15,7 +15,6 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_BOOLEAN_H_
 #define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_BOOLEAN_H_
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation.h"
 #include "source/opt/ir_context.h"
@@ -32,11 +31,13 @@
 
   // - |message_.fresh_id| must not be used by the module.
   // - The module must not yet declare OpTypeBoolean
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds OpTypeBoolean with |message_.fresh_id| as result id.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
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 d2af5f8..80716e1 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
@@ -30,29 +30,29 @@
     : message_(message) {}
 
 bool TransformationAddTypeFloat::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // The id must be fresh.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
 
   // Applicable if there is no float type with this width already declared in
   // the module.
   opt::analysis::Float float_type(message_.width());
-  return context->get_type_mgr()->GetId(&float_type) == 0;
+  return ir_context->get_type_mgr()->GetId(&float_type) == 0;
 }
 
 void TransformationAddTypeFloat::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   opt::Instruction::OperandList width = {
       {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}}};
-  context->module()->AddType(MakeUnique<opt::Instruction>(
-      context, SpvOpTypeFloat, 0, message_.fresh_id(), width));
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeFloat, 0, message_.fresh_id(), width));
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationAddTypeFloat::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_float.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_float.h
index 0fdc831..a8fa0e1 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_float.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_float.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FLOAT_H_
 #define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FLOAT_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -33,11 +33,13 @@
   // - |message_.fresh_id| must not be used by the module
   // - The module must not contain an OpTypeFloat instruction with width
   //   |message_.width|
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds an OpTypeFloat instruction to the module with the given width
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
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 4b6717b..991a28b 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
@@ -36,19 +36,18 @@
 }
 
 bool TransformationAddTypeFunction::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // The result id must be fresh.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
   // The return and argument types must be type ids but not not be function
   // type ids.
-  if (!fuzzerutil::IsNonFunctionTypeId(context, message_.return_type_id())) {
+  if (!fuzzerutil::IsNonFunctionTypeId(ir_context, message_.return_type_id())) {
     return false;
   }
   for (auto argument_type_id : message_.argument_type_id()) {
-    if (!fuzzerutil::IsNonFunctionTypeId(context, argument_type_id)) {
+    if (!fuzzerutil::IsNonFunctionTypeId(ir_context, argument_type_id)) {
       return false;
     }
   }
@@ -56,7 +55,7 @@
   // 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 : context->module()->types_values()) {
+  for (auto& inst : ir_context->module()->types_values()) {
     if (inst.opcode() != SpvOpTypeFunction) {
       // Consider only OpTypeFunction instructions.
       continue;
@@ -89,18 +88,19 @@
 }
 
 void TransformationAddTypeFunction::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+    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}});
   }
-  context->module()->AddType(MakeUnique<opt::Instruction>(
-      context, SpvOpTypeFunction, 0, message_.fresh_id(), in_operands));
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeFunction, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationAddTypeFunction::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.h
index 3880963..f26b250 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.h
@@ -17,9 +17,9 @@
 
 #include <vector>
 
-#include "source/fuzz/fact_manager.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 {
@@ -39,13 +39,15 @@
   // - The module must not contain an OpTypeFunction instruction defining a
   //   function type with the signature provided by the given return and
   //   argument types
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds an OpTypeFunction instruction to the module, with signature given by
   // |message_.return_type_id| and |message_.argument_type_id|.  The result id
   // for the instruction is |message_.fresh_id|.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
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 6f59270..a932a5f 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
@@ -32,30 +32,30 @@
 }
 
 bool TransformationAddTypeInt::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // The id must be fresh.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
 
   // 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 context->get_type_mgr()->GetId(&int_type) == 0;
+  return ir_context->get_type_mgr()->GetId(&int_type) == 0;
 }
 
-void TransformationAddTypeInt::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+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}}};
-  context->module()->AddType(MakeUnique<opt::Instruction>(
-      context, SpvOpTypeInt, 0, message_.fresh_id(), in_operands));
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeInt, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationAddTypeInt::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_int.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_int.h
index 86342d0..5c3c959 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_int.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_int.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_INT_H_
 #define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_INT_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -33,12 +33,14 @@
   // - |message_.fresh_id| must not be used by the module
   // - The module must not contain an OpTypeInt instruction with width
   //   |message_.width| and signedness |message.is_signed|
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds an OpTypeInt instruction to the module with the given width and
   // signedness.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.cpp
index 07ab705..2c24eaa 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.cpp
@@ -31,15 +31,14 @@
 }
 
 bool TransformationAddTypeMatrix::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // The result id must be fresh.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
   // The column type must be a floating-point vector.
   auto column_type =
-      context->get_type_mgr()->GetType(message_.column_type_id());
+      ir_context->get_type_mgr()->GetType(message_.column_type_id());
   if (!column_type) {
     return false;
   }
@@ -48,17 +47,18 @@
 }
 
 void TransformationAddTypeMatrix::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   opt::Instruction::OperandList in_operands;
   in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.column_type_id()}});
   in_operands.push_back(
       {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.column_count()}});
-  context->module()->AddType(MakeUnique<opt::Instruction>(
-      context, SpvOpTypeMatrix, 0, message_.fresh_id(), in_operands));
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeMatrix, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationAddTypeMatrix::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.h
index 69d6389..6d0724e 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_
 #define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -33,13 +33,15 @@
 
   // - |message_.fresh_id| must be a fresh id
   // - |message_.column_type_id| must be the id of a floating-point vector type
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds an OpTypeMatrix instruction to the module, with column type
   // |message_.column_type_id| and |message_.column_count| columns, with result
   // id |message_.fresh_id|.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_pointer.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_pointer.cpp
index 426985a..6cc8171 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_pointer.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_pointer.cpp
@@ -31,28 +31,29 @@
 }
 
 bool TransformationAddTypePointer::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // The id must be fresh.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
   // The base type must be known.
-  return context->get_type_mgr()->GetType(message_.base_type_id()) != nullptr;
+  return ir_context->get_type_mgr()->GetType(message_.base_type_id()) !=
+         nullptr;
 }
 
 void TransformationAddTypePointer::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   // Add the pointer type.
   opt::Instruction::OperandList in_operands = {
       {SPV_OPERAND_TYPE_STORAGE_CLASS, {message_.storage_class()}},
       {SPV_OPERAND_TYPE_ID, {message_.base_type_id()}}};
-  context->module()->AddType(MakeUnique<opt::Instruction>(
-      context, SpvOpTypePointer, 0, message_.fresh_id(), in_operands));
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypePointer, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationAddTypePointer::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_pointer.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_pointer.h
index 2b9ff77..3b50a29 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_pointer.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_pointer.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_
 #define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -34,12 +34,14 @@
   // - |message_.fresh_id| must not be used by the module
   // - |message_.base_type_id| must be the result id of an OpType[...]
   // instruction
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds an OpTypePointer instruction with the given storage class and base
   // type to the module.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
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 1ae8372..6ce5ea1 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
@@ -32,14 +32,13 @@
 }
 
 bool TransformationAddTypeStruct::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // A fresh id is required.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
   for (auto member_type : message_.member_type_id()) {
-    auto type = context->get_type_mgr()->GetType(member_type);
+    auto type = ir_context->get_type_mgr()->GetType(member_type);
     if (!type || type->AsFunction()) {
       // The member type id either does not refer to a type, or refers to a
       // function type; both are illegal.
@@ -50,17 +49,18 @@
 }
 
 void TransformationAddTypeStruct::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+    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}});
   }
-  context->module()->AddType(MakeUnique<opt::Instruction>(
-      context, SpvOpTypeStruct, 0, message_.fresh_id(), in_operands));
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeStruct, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationAddTypeStruct::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.h
index edf3ec6..86a532d 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.h
@@ -17,9 +17,9 @@
 
 #include <vector>
 
-#include "source/fuzz/fact_manager.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 {
@@ -35,12 +35,14 @@
 
   // - |message_.fresh_id| must be a fresh id
   // - |message_.member_type_id| must be a sequence of non-function type ids
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds an OpTypeStruct instruction whose field types are given by
   // |message_.member_type_id|, with result id |message_.fresh_id|.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
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 3fdf50b..f7b2fb5 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
@@ -31,13 +31,12 @@
 }
 
 bool TransformationAddTypeVector::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
   auto component_type =
-      context->get_type_mgr()->GetType(message_.component_type_id());
+      ir_context->get_type_mgr()->GetType(message_.component_type_id());
   if (!component_type) {
     return false;
   }
@@ -46,17 +45,18 @@
 }
 
 void TransformationAddTypeVector::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+    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()}});
-  context->module()->AddType(MakeUnique<opt::Instruction>(
-      context, SpvOpTypeVector, 0, message_.fresh_id(), in_operands));
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeVector, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationAddTypeVector::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.h
index af840f5..240f7cc 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_
 #define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -33,13 +33,15 @@
 
   // - |message_.fresh_id| must be a fresh id
   // - |message_.component_type_id| must be the id of a scalar type
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds an OpTypeVector instruction to the module, with component type
   // |message_.component_type_id| and |message_.component_count| components,
   // with result id |message_.fresh_id|.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_adjust_branch_weights.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_adjust_branch_weights.cpp
new file mode 100644
index 0000000..ed68134
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_adjust_branch_weights.cpp
@@ -0,0 +1,97 @@
+// 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_adjust_branch_weights.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+namespace {
+
+const uint32_t kBranchWeightForTrueLabelIndex = 3;
+const uint32_t kBranchWeightForFalseLabelIndex = 4;
+
+}  // namespace
+
+TransformationAdjustBranchWeights::TransformationAdjustBranchWeights(
+    const spvtools::fuzz::protobufs::TransformationAdjustBranchWeights& message)
+    : message_(message) {}
+
+TransformationAdjustBranchWeights::TransformationAdjustBranchWeights(
+    const protobufs::InstructionDescriptor& instruction_descriptor,
+    const std::pair<uint32_t, uint32_t>& branch_weights) {
+  *message_.mutable_instruction_descriptor() = instruction_descriptor;
+  message_.mutable_branch_weights()->set_first(branch_weights.first);
+  message_.mutable_branch_weights()->set_second(branch_weights.second);
+}
+
+bool TransformationAdjustBranchWeights::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  auto instruction =
+      FindInstruction(message_.instruction_descriptor(), ir_context);
+  if (instruction == nullptr) {
+    return false;
+  }
+
+  SpvOp opcode = static_cast<SpvOp>(
+      message_.instruction_descriptor().target_instruction_opcode());
+
+  assert(instruction->opcode() == opcode &&
+         "The located instruction must have the same opcode as in the "
+         "descriptor.");
+
+  // Must be an OpBranchConditional instruction.
+  if (opcode != SpvOpBranchConditional) {
+    return false;
+  }
+
+  assert((message_.branch_weights().first() != 0 ||
+          message_.branch_weights().second() != 0) &&
+         "At least one weight must be non-zero.");
+
+  assert(message_.branch_weights().first() <=
+             UINT32_MAX - message_.branch_weights().second() &&
+         "The sum of the two weights must not be greater than UINT32_MAX.");
+
+  return true;
+}
+
+void TransformationAdjustBranchWeights::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  auto instruction =
+      FindInstruction(message_.instruction_descriptor(), ir_context);
+  if (instruction->HasBranchWeights()) {
+    instruction->SetOperand(kBranchWeightForTrueLabelIndex,
+                            {message_.branch_weights().first()});
+    instruction->SetOperand(kBranchWeightForFalseLabelIndex,
+                            {message_.branch_weights().second()});
+  } else {
+    instruction->AddOperand({SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER,
+                             {message_.branch_weights().first()}});
+    instruction->AddOperand({SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER,
+                             {message_.branch_weights().second()}});
+  }
+}
+
+protobufs::Transformation TransformationAdjustBranchWeights::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_adjust_branch_weights() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_adjust_branch_weights.h b/third_party/SPIRV-Tools/source/fuzz/transformation_adjust_branch_weights.h
new file mode 100644
index 0000000..638b0a9
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_adjust_branch_weights.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_ADJUST_BRANCH_WEIGHTS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADJUST_BRANCH_WEIGHTS_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 TransformationAdjustBranchWeights : public Transformation {
+ public:
+  explicit TransformationAdjustBranchWeights(
+      const protobufs::TransformationAdjustBranchWeights& message);
+
+  TransformationAdjustBranchWeights(
+      const protobufs::InstructionDescriptor& instruction_descriptor,
+      const std::pair<uint32_t, uint32_t>& branch_weights);
+
+  // - |message_.instruction_descriptor| must identify an existing
+  //   branch conditional instruction
+  // - At least one of |branch_weights| must be non-zero and
+  //   the two weights must not overflow a 32-bit unsigned integer when added
+  //   together
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Adjust the branch weights of a branch conditional instruction.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAdjustBranchWeights message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADJUST_BRANCH_WEIGHTS_H_
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 9c63c1d..cd4f22f 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_construct.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_construct.cpp
@@ -40,14 +40,14 @@
 }
 
 bool TransformationCompositeConstruct::IsApplicable(
-    opt::IRContext* context, const FactManager& /*fact_manager*/) const {
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     // We require the id for the composite constructor to be unused.
     return false;
   }
 
   auto insert_before =
-      FindInstruction(message_.instruction_to_insert_before(), context);
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
   if (!insert_before) {
     // The instruction before which the composite should be inserted was not
     // found.
@@ -55,7 +55,7 @@
   }
 
   auto composite_type =
-      context->get_type_mgr()->GetType(message_.composite_type_id());
+      ir_context->get_type_mgr()->GetType(message_.composite_type_id());
 
   if (!fuzzerutil::IsCompositeType(composite_type)) {
     // The type must actually be a composite.
@@ -64,27 +64,31 @@
 
   // If the type is an array, matrix, struct or vector, the components need to
   // be suitable for constructing something of that type.
-  if (composite_type->AsArray() && !ComponentsForArrayConstructionAreOK(
-                                       context, *composite_type->AsArray())) {
+  if (composite_type->AsArray() &&
+      !ComponentsForArrayConstructionAreOK(ir_context,
+                                           *composite_type->AsArray())) {
     return false;
   }
-  if (composite_type->AsMatrix() && !ComponentsForMatrixConstructionAreOK(
-                                        context, *composite_type->AsMatrix())) {
+  if (composite_type->AsMatrix() &&
+      !ComponentsForMatrixConstructionAreOK(ir_context,
+                                            *composite_type->AsMatrix())) {
     return false;
   }
-  if (composite_type->AsStruct() && !ComponentsForStructConstructionAreOK(
-                                        context, *composite_type->AsStruct())) {
+  if (composite_type->AsStruct() &&
+      !ComponentsForStructConstructionAreOK(ir_context,
+                                            *composite_type->AsStruct())) {
     return false;
   }
-  if (composite_type->AsVector() && !ComponentsForVectorConstructionAreOK(
-                                        context, *composite_type->AsVector())) {
+  if (composite_type->AsVector() &&
+      !ComponentsForVectorConstructionAreOK(ir_context,
+                                            *composite_type->AsVector())) {
     return false;
   }
 
   // Now check whether every component being used to initialize the composite is
   // available at the desired program point.
   for (auto& component : message_.component()) {
-    if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+    if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
                                                     component)) {
       return false;
     }
@@ -93,13 +97,14 @@
   return true;
 }
 
-void TransformationCompositeConstruct::Apply(opt::IRContext* context,
-                                             FactManager* fact_manager) const {
+void TransformationCompositeConstruct::Apply(
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   // Use the base and offset information from the transformation to determine
   // where in the module a new instruction should be inserted.
   auto insert_before_inst =
-      FindInstruction(message_.instruction_to_insert_before(), context);
-  auto destination_block = context->get_instr_block(insert_before_inst);
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
+  auto destination_block = ir_context->get_instr_block(insert_before_inst);
   auto insert_before = fuzzerutil::GetIteratorForInstruction(
       destination_block, insert_before_inst);
 
@@ -111,22 +116,22 @@
 
   // Insert an OpCompositeConstruct instruction.
   insert_before.InsertBefore(MakeUnique<opt::Instruction>(
-      context, SpvOpCompositeConstruct, message_.composite_type_id(),
+      ir_context, SpvOpCompositeConstruct, message_.composite_type_id(),
       message_.fresh_id(), in_operands));
 
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
-  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 
   // Inform the fact manager that we now have new synonyms: every component of
   // the composite is synonymous with the id used to construct that component,
   // except in the case of a vector where a single vector id can span multiple
   // components.
   auto composite_type =
-      context->get_type_mgr()->GetType(message_.composite_type_id());
+      ir_context->get_type_mgr()->GetType(message_.composite_type_id());
   uint32_t index = 0;
   for (auto component : message_.component()) {
-    auto component_type = context->get_type_mgr()->GetType(
-        context->get_def_use_mgr()->GetDef(component)->type_id());
+    auto component_type = ir_context->get_type_mgr()->GetType(
+        ir_context->get_def_use_mgr()->GetDef(component)->type_id());
     if (composite_type->AsVector() && component_type->AsVector()) {
       // The case where the composite being constructed is a vector and the
       // component provided for construction is also a vector is special.  It
@@ -139,24 +144,24 @@
       for (uint32_t subvector_index = 0;
            subvector_index < component_type->AsVector()->element_count();
            subvector_index++) {
-        fact_manager->AddFactDataSynonym(
+        transformation_context->GetFactManager()->AddFactDataSynonym(
             MakeDataDescriptor(component, {subvector_index}),
-            MakeDataDescriptor(message_.fresh_id(), {index}), context);
+            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.
-      fact_manager->AddFactDataSynonym(
+      transformation_context->GetFactManager()->AddFactDataSynonym(
           MakeDataDescriptor(component, {}),
-          MakeDataDescriptor(message_.fresh_id(), {index}), context);
+          MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
       index++;
     }
   }
 }
 
 bool TransformationCompositeConstruct::ComponentsForArrayConstructionAreOK(
-    opt::IRContext* context, const opt::analysis::Array& array_type) const {
+    opt::IRContext* ir_context, const opt::analysis::Array& array_type) const {
   if (array_type.length_info().words[0] !=
       opt::analysis::Array::LengthInfo::kConstant) {
     // We only handle constant-sized arrays.
@@ -176,13 +181,13 @@
   // Check that each component is the result id of an instruction whose type is
   // the array's element type.
   for (auto component_id : message_.component()) {
-    auto inst = context->get_def_use_mgr()->GetDef(component_id);
+    auto inst = ir_context->get_def_use_mgr()->GetDef(component_id);
     if (inst == nullptr || !inst->type_id()) {
       // The component does not correspond to an instruction with a result
       // type.
       return false;
     }
-    auto component_type = context->get_type_mgr()->GetType(inst->type_id());
+    auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id());
     assert(component_type);
     if (component_type != array_type.element_type()) {
       // The component's type does not match the array's element type.
@@ -193,7 +198,8 @@
 }
 
 bool TransformationCompositeConstruct::ComponentsForMatrixConstructionAreOK(
-    opt::IRContext* context, const opt::analysis::Matrix& matrix_type) const {
+    opt::IRContext* ir_context,
+    const opt::analysis::Matrix& matrix_type) const {
   if (static_cast<uint32_t>(message_.component().size()) !=
       matrix_type.element_count()) {
     // The number of components must match the number of columns of the matrix.
@@ -202,13 +208,13 @@
   // Check that each component is the result id of an instruction whose type is
   // the matrix's column type.
   for (auto component_id : message_.component()) {
-    auto inst = context->get_def_use_mgr()->GetDef(component_id);
+    auto inst = ir_context->get_def_use_mgr()->GetDef(component_id);
     if (inst == nullptr || !inst->type_id()) {
       // The component does not correspond to an instruction with a result
       // type.
       return false;
     }
-    auto component_type = context->get_type_mgr()->GetType(inst->type_id());
+    auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id());
     assert(component_type);
     if (component_type != matrix_type.element_type()) {
       // The component's type does not match the matrix's column type.
@@ -219,7 +225,8 @@
 }
 
 bool TransformationCompositeConstruct::ComponentsForStructConstructionAreOK(
-    opt::IRContext* context, const opt::analysis::Struct& struct_type) const {
+    opt::IRContext* ir_context,
+    const opt::analysis::Struct& struct_type) const {
   if (static_cast<uint32_t>(message_.component().size()) !=
       struct_type.element_types().size()) {
     // The number of components must match the number of fields of the struct.
@@ -229,14 +236,14 @@
   // matches the associated field type.
   for (uint32_t field_index = 0;
        field_index < struct_type.element_types().size(); field_index++) {
-    auto inst =
-        context->get_def_use_mgr()->GetDef(message_.component()[field_index]);
+    auto inst = ir_context->get_def_use_mgr()->GetDef(
+        message_.component()[field_index]);
     if (inst == nullptr || !inst->type_id()) {
       // The component does not correspond to an instruction with a result
       // type.
       return false;
     }
-    auto component_type = context->get_type_mgr()->GetType(inst->type_id());
+    auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id());
     assert(component_type);
     if (component_type != struct_type.element_types()[field_index]) {
       // The component's type does not match the corresponding field type.
@@ -247,17 +254,18 @@
 }
 
 bool TransformationCompositeConstruct::ComponentsForVectorConstructionAreOK(
-    opt::IRContext* context, const opt::analysis::Vector& vector_type) const {
+    opt::IRContext* ir_context,
+    const opt::analysis::Vector& vector_type) const {
   uint32_t base_element_count = 0;
   auto element_type = vector_type.element_type();
   for (auto& component_id : message_.component()) {
-    auto inst = context->get_def_use_mgr()->GetDef(component_id);
+    auto inst = ir_context->get_def_use_mgr()->GetDef(component_id);
     if (inst == nullptr || !inst->type_id()) {
       // The component does not correspond to an instruction with a result
       // type.
       return false;
     }
-    auto component_type = context->get_type_mgr()->GetType(inst->type_id());
+    auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id());
     assert(component_type);
     if (component_type == element_type) {
       base_element_count++;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_construct.h b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_construct.h
index 5369c4c..2e55e70 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_construct.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_construct.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_CONSTRUCT_H_
 #define SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_CONSTRUCT_H_
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -49,15 +49,17 @@
   //   before 'inst'.
   // - Each element of |message_.component| must be available directly before
   //   'inst'.
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Inserts a new OpCompositeConstruct instruction, with id
   // |message_.fresh_id|, directly before the instruction identified by
   // |message_.base_instruction_id| and |message_.offset|.  The instruction
   // creates a composite of type |message_.composite_type_id| using the ids of
   // |message_.component|.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
@@ -65,19 +67,22 @@
   // Helper to decide whether the components of the transformation are suitable
   // for constructing an array of the given type.
   bool ComponentsForArrayConstructionAreOK(
-      opt::IRContext* context, const opt::analysis::Array& array_type) const;
+      opt::IRContext* ir_context, const opt::analysis::Array& array_type) const;
 
   // Similar, but for matrices.
   bool ComponentsForMatrixConstructionAreOK(
-      opt::IRContext* context, const opt::analysis::Matrix& matrix_type) const;
+      opt::IRContext* ir_context,
+      const opt::analysis::Matrix& matrix_type) const;
 
   // Similar, but for structs.
   bool ComponentsForStructConstructionAreOK(
-      opt::IRContext* context, const opt::analysis::Struct& struct_type) const;
+      opt::IRContext* ir_context,
+      const opt::analysis::Struct& struct_type) const;
 
   // Similar, but for vectors.
   bool ComponentsForVectorConstructionAreOK(
-      opt::IRContext* context, const opt::analysis::Vector& vector_type) const;
+      opt::IRContext* ir_context,
+      const opt::analysis::Vector& vector_type) const;
 
   protobufs::TransformationCompositeConstruct message_;
 };
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 5d3a386..3dc3953 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.cpp
@@ -40,24 +40,23 @@
 }
 
 bool TransformationCompositeExtract::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
   auto instruction_to_insert_before =
-      FindInstruction(message_.instruction_to_insert_before(), context);
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
   if (!instruction_to_insert_before) {
     return false;
   }
   auto composite_instruction =
-      context->get_def_use_mgr()->GetDef(message_.composite_id());
+      ir_context->get_def_use_mgr()->GetDef(message_.composite_id());
   if (!composite_instruction) {
     return false;
   }
-  if (auto block = context->get_instr_block(composite_instruction)) {
+  if (auto block = ir_context->get_instr_block(composite_instruction)) {
     if (composite_instruction == instruction_to_insert_before ||
-        !context->GetDominatorAnalysis(block->GetParent())
+        !ir_context->GetDominatorAnalysis(block->GetParent())
              ->Dominates(composite_instruction, instruction_to_insert_before)) {
       return false;
     }
@@ -66,7 +65,7 @@
          "An instruction in a block cannot have a result id but no type id.");
 
   auto composite_type =
-      context->get_type_mgr()->GetType(composite_instruction->type_id());
+      ir_context->get_type_mgr()->GetType(composite_instruction->type_id());
   if (!composite_type) {
     return false;
   }
@@ -76,30 +75,33 @@
     return false;
   }
 
-  return fuzzerutil::WalkCompositeTypeIndices(
-             context, composite_instruction->type_id(), message_.index()) != 0;
+  return fuzzerutil::WalkCompositeTypeIndices(ir_context,
+                                              composite_instruction->type_id(),
+                                              message_.index()) != 0;
 }
 
 void TransformationCompositeExtract::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   opt::Instruction::OperandList extract_operands;
   extract_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.composite_id()}});
   for (auto an_index : message_.index()) {
     extract_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {an_index}});
   }
   auto composite_instruction =
-      context->get_def_use_mgr()->GetDef(message_.composite_id());
+      ir_context->get_def_use_mgr()->GetDef(message_.composite_id());
   auto extracted_type = fuzzerutil::WalkCompositeTypeIndices(
-      context, composite_instruction->type_id(), message_.index());
+      ir_context, composite_instruction->type_id(), message_.index());
 
-  FindInstruction(message_.instruction_to_insert_before(), context)
+  FindInstruction(message_.instruction_to_insert_before(), ir_context)
       ->InsertBefore(MakeUnique<opt::Instruction>(
-          context, SpvOpCompositeExtract, extracted_type, message_.fresh_id(),
-          extract_operands));
+          ir_context, SpvOpCompositeExtract, extracted_type,
+          message_.fresh_id(), extract_operands));
 
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
 
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 
   // Add the fact that the id storing the extracted element is synonymous with
   // the index into the structure.
@@ -111,8 +113,9 @@
       MakeDataDescriptor(message_.composite_id(), std::move(indices));
   protobufs::DataDescriptor data_descriptor_for_result_id =
       MakeDataDescriptor(message_.fresh_id(), {});
-  fact_manager->AddFactDataSynonym(data_descriptor_for_extracted_element,
-                                   data_descriptor_for_result_id, context);
+  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 c4c9278..8f52d22 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_EXTRACT_H_
 #define SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_EXTRACT_H_
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -41,15 +41,17 @@
   // - |message_.index| must be a suitable set of indices for
   //   |message_.composite_id|, i.e. it must be possible to follow this chain
   //   of indices to reach a sub-object of |message_.composite_id|
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds an OpCompositeConstruct instruction before the instruction identified
   // by |message_.instruction_to_insert_before|, that extracts from
   // |message_.composite_id| via indices |message_.index| into
   // |message_.fresh_id|.  Generates a data synonym fact relating
   // |message_.fresh_id| to the extracted element.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp
new file mode 100644
index 0000000..ff3ba3c
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp
@@ -0,0 +1,52 @@
+// 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_compute_data_synonym_fact_closure.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationComputeDataSynonymFactClosure::
+    TransformationComputeDataSynonymFactClosure(
+        const spvtools::fuzz::protobufs::
+            TransformationComputeDataSynonymFactClosure& message)
+    : message_(message) {}
+
+TransformationComputeDataSynonymFactClosure::
+    TransformationComputeDataSynonymFactClosure(
+        uint32_t maximum_equivalence_class_size) {
+  message_.set_maximum_equivalence_class_size(maximum_equivalence_class_size);
+}
+
+bool TransformationComputeDataSynonymFactClosure::IsApplicable(
+    opt::IRContext* /*unused*/, const TransformationContext& /*unused*/) const {
+  return true;
+}
+
+void TransformationComputeDataSynonymFactClosure::Apply(
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  transformation_context->GetFactManager()->ComputeClosureOfFacts(
+      ir_context, message_.maximum_equivalence_class_size());
+}
+
+protobufs::Transformation
+TransformationComputeDataSynonymFactClosure::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_compute_data_synonym_fact_closure() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_compute_data_synonym_fact_closure.h b/third_party/SPIRV-Tools/source/fuzz/transformation_compute_data_synonym_fact_closure.h
new file mode 100644
index 0000000..eab43ff
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_compute_data_synonym_fact_closure.h
@@ -0,0 +1,53 @@
+// 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_COMPUTE_DATA_SYNONYM_FACT_CLOSURE_H_
+#define SOURCE_FUZZ_TRANSFORMATION_COMPUTE_DATA_SYNONYM_FACT_CLOSURE_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 TransformationComputeDataSynonymFactClosure : public Transformation {
+ public:
+  explicit TransformationComputeDataSynonymFactClosure(
+      const protobufs::TransformationComputeDataSynonymFactClosure& message);
+
+  explicit TransformationComputeDataSynonymFactClosure(
+      uint32_t maximum_equivalence_class_size);
+
+  // This transformation is trivially applicable.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Forces the fact manager to compute a closure of data synonym facts, so that
+  // facts implied by existing facts are deduced.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationComputeDataSynonymFactClosure message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_COMPUTE_DATA_SYNONYM_FACT_CLOSURE_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_context.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_context.cpp
new file mode 100644
index 0000000..9c8a90f
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_context.cpp
@@ -0,0 +1,29 @@
+// 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_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationContext::TransformationContext(
+    FactManager* transformation_context,
+    spv_validator_options validator_options)
+    : fact_manager_(transformation_context),
+      validator_options_(validator_options) {}
+
+TransformationContext::~TransformationContext() = default;
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_context.h b/third_party/SPIRV-Tools/source/fuzz/transformation_context.h
new file mode 100644
index 0000000..37e15a2
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_context.h
@@ -0,0 +1,56 @@
+// 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_CONTEXT_H_
+#define SOURCE_FUZZ_TRANSFORMATION_CONTEXT_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "spirv-tools/libspirv.hpp"
+
+namespace spvtools {
+namespace fuzz {
+
+// Encapsulates all information that is required to inform how to apply a
+// transformation to a module.
+class TransformationContext {
+ public:
+  // Constructs a transformation context with a given fact manager and validator
+  // options.
+  TransformationContext(FactManager* fact_manager,
+                        spv_validator_options validator_options);
+
+  ~TransformationContext();
+
+  FactManager* GetFactManager() { return fact_manager_; }
+
+  const FactManager* GetFactManager() const { return fact_manager_; }
+
+  spv_validator_options GetValidatorOptions() const {
+    return validator_options_;
+  }
+
+ private:
+  // Manages facts that inform whether transformations can be applied, and that
+  // are produced by applying transformations.
+  FactManager* fact_manager_;
+
+  // Options to control validation when deciding whether transformations can be
+  // applied.
+  spv_validator_options validator_options_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_CONTEXT_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.cpp
index bfdced3..7b5b5c9 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.cpp
@@ -38,22 +38,22 @@
 }
 
 bool TransformationCopyObject::IsApplicable(
-    opt::IRContext* context, const FactManager& /*fact_manager*/) const {
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    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 = context->get_def_use_mgr()->GetDef(message_.object());
+  auto object_inst = ir_context->get_def_use_mgr()->GetDef(message_.object());
   if (!object_inst) {
     return false;
   }
-  if (!fuzzerutil::CanMakeSynonymOf(context, object_inst)) {
+  if (!fuzzerutil::CanMakeSynonymOf(ir_context, object_inst)) {
     return false;
   }
 
   auto insert_before =
-      FindInstruction(message_.instruction_to_insert_before(), context);
+      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;
@@ -66,17 +66,18 @@
 
   // |message_object| must be available directly before the point where we want
   // to add the copy.
-  return fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+  return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
                                                     message_.object());
 }
 
-void TransformationCopyObject::Apply(opt::IRContext* context,
-                                     FactManager* fact_manager) const {
-  auto object_inst = context->get_def_use_mgr()->GetDef(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(), context);
-  auto destination_block = context->get_instr_block(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);
@@ -86,18 +87,21 @@
   opt::Instruction::OperandList operands = {
       {SPV_OPERAND_TYPE_ID, {message_.object()}}};
   insert_before->InsertBefore(MakeUnique<opt::Instruction>(
-      context, SpvOp::SpvOpCopyObject, object_inst->type_id(),
+      ir_context, SpvOp::SpvOpCopyObject, object_inst->type_id(),
       message_.fresh_id(), operands));
 
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 
-  fact_manager->AddFactDataSynonym(MakeDataDescriptor(message_.object(), {}),
-                                   MakeDataDescriptor(message_.fresh_id(), {}),
-                                   context);
+  transformation_context->GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(message_.object(), {}),
+      MakeDataDescriptor(message_.fresh_id(), {}), ir_context);
 
-  if (fact_manager->PointeeValueIsIrrelevant(message_.object())) {
-    fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id());
+  if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
+          message_.object())) {
+    transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+        message_.fresh_id());
   }
 }
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.h b/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.h
index 9e9c26a..80d57ae 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_H_
 #define SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -49,19 +49,21 @@
   // - |message_.object| must be available directly before 'inst'.
   // - |message_.object| must not be a null pointer or undefined pointer (so as
   //   to make it legal to load from copied pointers).
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  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 |fact_manager|.
+  //   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 |fact_manager| about
-  //   |message_.fresh_id|.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  //   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;
 
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 21b67f6..5c31417 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.cpp
@@ -37,40 +37,40 @@
 }
 
 bool TransformationEquationInstruction::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // The result id must be fresh.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  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(), context);
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
   if (!insert_before) {
     return false;
   }
   // The input ids must all exist, not be OpUndef, and be available before this
   // instruction.
   for (auto id : message_.in_operand_id()) {
-    auto inst = context->get_def_use_mgr()->GetDef(id);
+    auto inst = ir_context->get_def_use_mgr()->GetDef(id);
     if (!inst) {
       return false;
     }
     if (inst->opcode() == SpvOpUndef) {
       return false;
     }
-    if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+    if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
                                                     id)) {
       return false;
     }
   }
 
-  return MaybeGetResultType(context) != 0;
+  return MaybeGetResultType(ir_context) != 0;
 }
 
 void TransformationEquationInstruction::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
 
   opt::Instruction::OperandList in_operands;
   std::vector<uint32_t> rhs_id;
@@ -79,16 +79,16 @@
     rhs_id.push_back(id);
   }
 
-  FindInstruction(message_.instruction_to_insert_before(), context)
+  FindInstruction(message_.instruction_to_insert_before(), ir_context)
       ->InsertBefore(MakeUnique<opt::Instruction>(
-          context, static_cast<SpvOp>(message_.opcode()),
-          MaybeGetResultType(context), message_.fresh_id(), in_operands));
+          ir_context, static_cast<SpvOp>(message_.opcode()),
+          MaybeGetResultType(ir_context), message_.fresh_id(), in_operands));
 
-  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 
-  fact_manager->AddFactIdEquation(message_.fresh_id(),
-                                  static_cast<SpvOp>(message_.opcode()), rhs_id,
-                                  context);
+  transformation_context->GetFactManager()->AddFactIdEquation(
+      message_.fresh_id(), static_cast<SpvOp>(message_.opcode()), rhs_id,
+      ir_context);
 }
 
 protobufs::Transformation TransformationEquationInstruction::ToMessage() const {
@@ -98,7 +98,7 @@
 }
 
 uint32_t TransformationEquationInstruction::MaybeGetResultType(
-    opt::IRContext* context) const {
+    opt::IRContext* ir_context) const {
   switch (static_cast<SpvOp>(message_.opcode())) {
     case SpvOpIAdd:
     case SpvOpISub: {
@@ -108,13 +108,13 @@
       uint32_t first_operand_width = 0;
       uint32_t first_operand_type_id = 0;
       for (uint32_t index = 0; index < 2; index++) {
-        auto operand_inst =
-            context->get_def_use_mgr()->GetDef(message_.in_operand_id(index));
+        auto operand_inst = ir_context->get_def_use_mgr()->GetDef(
+            message_.in_operand_id(index));
         if (!operand_inst || !operand_inst->type_id()) {
           return 0;
         }
         auto operand_type =
-            context->get_type_mgr()->GetType(operand_inst->type_id());
+            ir_context->get_type_mgr()->GetType(operand_inst->type_id());
         if (!(operand_type->AsInteger() ||
               (operand_type->AsVector() &&
                operand_type->AsVector()->element_type()->AsInteger()))) {
@@ -144,12 +144,12 @@
         return 0;
       }
       auto operand_inst =
-          context->get_def_use_mgr()->GetDef(message_.in_operand_id(0));
+          ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0));
       if (!operand_inst || !operand_inst->type_id()) {
         return 0;
       }
       auto operand_type =
-          context->get_type_mgr()->GetType(operand_inst->type_id());
+          ir_context->get_type_mgr()->GetType(operand_inst->type_id());
       if (!(operand_type->AsBool() ||
             (operand_type->AsVector() &&
              operand_type->AsVector()->element_type()->AsBool()))) {
@@ -162,12 +162,12 @@
         return 0;
       }
       auto operand_inst =
-          context->get_def_use_mgr()->GetDef(message_.in_operand_id(0));
+          ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0));
       if (!operand_inst || !operand_inst->type_id()) {
         return 0;
       }
       auto operand_type =
-          context->get_type_mgr()->GetType(operand_inst->type_id());
+          ir_context->get_type_mgr()->GetType(operand_inst->type_id());
       if (!(operand_type->AsInteger() ||
             (operand_type->AsVector() &&
              operand_type->AsVector()->element_type()->AsInteger()))) {
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 2456ba5..7eec9c6 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.h
@@ -17,9 +17,9 @@
 
 #include <vector>
 
-#include "source/fuzz/fact_manager.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 {
@@ -44,8 +44,9 @@
   //   equations, the types of the ids in |message_.in_operand_id| must be
   //   suitable for use with this opcode, and the module must contain an
   //   appropriate result type id.
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds an instruction to the module, right before
   // |message_.instruction_to_insert_before|, of the form:
@@ -56,7 +57,8 @@
   // compatible with the opcode and input operands.
   //
   // The fact manager is also updated to inform it of this equation fact.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
@@ -65,7 +67,7 @@
   // 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* context) const;
+  uint32_t MaybeGetResultType(opt::IRContext* ir_context) const;
 
   protobufs::TransformationEquationInstruction message_;
 };
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.cpp
index cea8537..432634d 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.cpp
@@ -39,25 +39,26 @@
 }
 
 bool TransformationFunctionCall::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& fact_manager) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   // The result id must be fresh
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
 
   // The function must exist
-  auto callee_inst = context->get_def_use_mgr()->GetDef(message_.callee_id());
+  auto callee_inst =
+      ir_context->get_def_use_mgr()->GetDef(message_.callee_id());
   if (!callee_inst || callee_inst->opcode() != SpvOpFunction) {
     return false;
   }
 
   // The function must not be an entry point
-  if (fuzzerutil::FunctionIsEntryPoint(context, message_.callee_id())) {
+  if (fuzzerutil::FunctionIsEntryPoint(ir_context, message_.callee_id())) {
     return false;
   }
 
-  auto callee_type_inst = context->get_def_use_mgr()->GetDef(
+  auto callee_type_inst = ir_context->get_def_use_mgr()->GetDef(
       callee_inst->GetSingleWordInOperand(1));
   assert(callee_type_inst->opcode() == SpvOpTypeFunction &&
          "Bad function type.");
@@ -73,7 +74,7 @@
   // The instruction descriptor must refer to a position where it is valid to
   // insert the call
   auto insert_before =
-      FindInstruction(message_.instruction_to_insert_before(), context);
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
   if (!insert_before) {
     return false;
   }
@@ -82,13 +83,15 @@
     return false;
   }
 
-  auto block = context->get_instr_block(insert_before);
+  auto block = ir_context->get_instr_block(insert_before);
   auto enclosing_function = block->GetParent();
 
   // If the block is not dead, the function must be livesafe
-  bool block_is_dead = fact_manager.BlockIsDead(block->id());
+  bool block_is_dead =
+      transformation_context.GetFactManager()->BlockIsDead(block->id());
   if (!block_is_dead &&
-      !fact_manager.FunctionIsLivesafe(message_.callee_id())) {
+      !transformation_context.GetFactManager()->FunctionIsLivesafe(
+          message_.callee_id())) {
     return false;
   }
 
@@ -98,7 +101,7 @@
        arg_index < static_cast<uint32_t>(message_.argument_id().size());
        arg_index++) {
     opt::Instruction* arg_inst =
-        context->get_def_use_mgr()->GetDef(message_.argument_id(arg_index));
+        ir_context->get_def_use_mgr()->GetDef(message_.argument_id(arg_index));
     if (!arg_inst) {
       // The given argument does not correspond to an instruction.
       return false;
@@ -112,7 +115,7 @@
       return false;
     }
     opt::Instruction* arg_type_inst =
-        context->get_def_use_mgr()->GetDef(arg_inst->type_id());
+        ir_context->get_def_use_mgr()->GetDef(arg_inst->type_id());
     if (arg_type_inst->opcode() == SpvOpTypePointer) {
       switch (arg_inst->opcode()) {
         case SpvOpFunctionParameter:
@@ -124,7 +127,8 @@
           return false;
       }
       if (!block_is_dead &&
-          !fact_manager.PointeeValueIsIrrelevant(arg_inst->result_id())) {
+          !transformation_context.GetFactManager()->PointeeValueIsIrrelevant(
+              arg_inst->result_id())) {
         // This is not a dead block, so pointer parameters passed to the called
         // function might really have their contents modified. We thus require
         // such pointers to be to arbitrary-valued variables, which this is not.
@@ -134,7 +138,7 @@
 
     // The argument id needs to be available (according to dominance rules) at
     // the point where the call will occur.
-    if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+    if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
                                                     arg_inst->result_id())) {
       return false;
     }
@@ -146,19 +150,19 @@
     return false;
   }
   // Ensure the call would not lead to indirect recursion.
-  return !CallGraph(context)
+  return !CallGraph(ir_context)
               .GetIndirectCallees(message_.callee_id())
               .count(block->GetParent()->result_id());
 }
 
 void TransformationFunctionCall::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   // Update the module's bound to reflect the fresh id for the result of the
   // function call.
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   // Get the return type of the function being called.
   uint32_t return_type =
-      context->get_def_use_mgr()->GetDef(message_.callee_id())->type_id();
+      ir_context->get_def_use_mgr()->GetDef(message_.callee_id())->type_id();
   // Populate the operands to the call instruction, with the function id and the
   // arguments.
   opt::Instruction::OperandList operands;
@@ -167,12 +171,12 @@
     operands.push_back({SPV_OPERAND_TYPE_ID, {arg}});
   }
   // Insert the function call before the instruction specified in the message.
-  FindInstruction(message_.instruction_to_insert_before(), context)
-      ->InsertBefore(
-          MakeUnique<opt::Instruction>(context, SpvOpFunctionCall, return_type,
-                                       message_.fresh_id(), operands));
+  FindInstruction(message_.instruction_to_insert_before(), ir_context)
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpFunctionCall, return_type, message_.fresh_id(),
+          operands));
   // Invalidate all analyses since we have changed the module.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationFunctionCall::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.h b/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.h
index a9ae5be..4ad7db1 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_
 #define SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -44,14 +44,16 @@
   // - If the insertion point is not in a dead block then |message_function_id|
   //   must refer to a livesafe function, and every pointer argument in
   //   |message_.arg_id| must refer to an arbitrary-valued variable
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds an instruction of the form:
   //   |fresh_id| = OpFunctionCall %type |callee_id| |arg_id...|
   // before |instruction_to_insert_before|, where %type is the return type of
   // |callee_id|.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_load.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_load.cpp
index 4cba37d..a260c33 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_load.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_load.cpp
@@ -34,20 +34,19 @@
 }
 
 bool TransformationLoad::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // The result id must be fresh.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
 
   // The pointer must exist and have a type.
-  auto pointer = context->get_def_use_mgr()->GetDef(message_.pointer_id());
+  auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id());
   if (!pointer || !pointer->type_id()) {
     return false;
   }
   // The type must indeed be a pointer type.
-  auto pointer_type = context->get_def_use_mgr()->GetDef(pointer->type_id());
+  auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
   assert(pointer_type && "Type id must be defined.");
   if (pointer_type->opcode() != SpvOpTypePointer) {
     return false;
@@ -65,7 +64,7 @@
 
   // Determine which instruction we should be inserting before.
   auto insert_before =
-      FindInstruction(message_.instruction_to_insert_before(), context);
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
   // It must exist, ...
   if (!insert_before) {
     return false;
@@ -76,21 +75,21 @@
   }
 
   // The pointer needs to be available at the insertion point.
-  return fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+  return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
                                                     message_.pointer_id());
 }
 
-void TransformationLoad::Apply(opt::IRContext* context,
-                               spvtools::fuzz::FactManager* /*unused*/) const {
+void TransformationLoad::Apply(opt::IRContext* ir_context,
+                               TransformationContext* /*unused*/) const {
   uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
-      context, fuzzerutil::GetTypeId(context, message_.pointer_id()));
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
-  FindInstruction(message_.instruction_to_insert_before(), context)
+      ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id()));
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+  FindInstruction(message_.instruction_to_insert_before(), ir_context)
       ->InsertBefore(MakeUnique<opt::Instruction>(
-          context, SpvOpLoad, result_type, message_.fresh_id(),
+          ir_context, SpvOpLoad, result_type, message_.fresh_id(),
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}})));
-  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationLoad::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_load.h b/third_party/SPIRV-Tools/source/fuzz/transformation_load.h
index ff99016..4c7c00b 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_load.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_load.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_LOAD_H_
 #define SOURCE_FUZZ_TRANSFORMATION_LOAD_H_
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -37,15 +37,17 @@
   // - |message_.instruction_to_insert_before| must identify an instruction
   //   before which it is valid to insert an OpLoad, and where
   //   |message_.pointer_id| is available (according to dominance rules)
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds an instruction of the form:
   //   |message_.fresh_id| = OpLoad %type |message_.pointer_id|
   // before the instruction identified by
   // |message_.instruction_to_insert_before|, where %type is the pointer's
   // pointee type.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_merge_blocks.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_merge_blocks.cpp
index 316e80d..68ac092 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_merge_blocks.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_merge_blocks.cpp
@@ -29,40 +29,41 @@
 }
 
 bool TransformationMergeBlocks::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
-  auto second_block = fuzzerutil::MaybeFindBlock(context, message_.block_id());
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  auto second_block =
+      fuzzerutil::MaybeFindBlock(ir_context, message_.block_id());
   // The given block must exist.
   if (!second_block) {
     return false;
   }
   // The block must have just one predecessor.
-  auto predecessors = context->cfg()->preds(second_block->id());
+  auto predecessors = ir_context->cfg()->preds(second_block->id());
   if (predecessors.size() != 1) {
     return false;
   }
-  auto first_block = context->cfg()->block(predecessors.at(0));
+  auto first_block = ir_context->cfg()->block(predecessors.at(0));
 
-  return opt::blockmergeutil::CanMergeWithSuccessor(context, first_block);
+  return opt::blockmergeutil::CanMergeWithSuccessor(ir_context, first_block);
 }
 
-void TransformationMergeBlocks::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
-  auto second_block = fuzzerutil::MaybeFindBlock(context, message_.block_id());
-  auto first_block =
-      context->cfg()->block(context->cfg()->preds(second_block->id()).at(0));
+void TransformationMergeBlocks::Apply(opt::IRContext* ir_context,
+                                      TransformationContext* /*unused*/) const {
+  auto second_block =
+      fuzzerutil::MaybeFindBlock(ir_context, message_.block_id());
+  auto first_block = ir_context->cfg()->block(
+      ir_context->cfg()->preds(second_block->id()).at(0));
 
   auto function = first_block->GetParent();
   // We need an iterator pointing to the predecessor, hence the loop.
   for (auto bi = function->begin(); bi != function->end(); ++bi) {
     if (bi->id() == first_block->id()) {
-      assert(opt::blockmergeutil::CanMergeWithSuccessor(context, &*bi) &&
+      assert(opt::blockmergeutil::CanMergeWithSuccessor(ir_context, &*bi) &&
              "Because 'Apply' should only be invoked if 'IsApplicable' holds, "
              "it must be possible to merge |bi| with its successor.");
-      opt::blockmergeutil::MergeWithSuccessor(context, function, bi);
+      opt::blockmergeutil::MergeWithSuccessor(ir_context, function, bi);
       // Invalidate all analyses, since we have changed the module
       // significantly.
-      context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+      ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
       return;
     }
   }
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_merge_blocks.h b/third_party/SPIRV-Tools/source/fuzz/transformation_merge_blocks.h
index 86216db..1dc16d2 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_merge_blocks.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_merge_blocks.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_
 #define SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -35,12 +35,14 @@
   // - b must be the sole successor of a
   // - Replacing a with the merge of a and b (and removing b) must lead to a
   //   valid module
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // The contents of b are merged into a, and a's terminator is replaced with
   // the terminator of b.  Block b is removed from the module.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_move_block_down.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_move_block_down.cpp
index f181855..6c71ab7 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_move_block_down.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_move_block_down.cpp
@@ -28,10 +28,10 @@
 }
 
 bool TransformationMoveBlockDown::IsApplicable(
-    opt::IRContext* context, const FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // Go through every block in every function, looking for a block whose id
   // matches that of the block we want to consider moving down.
-  for (auto& function : *context->module()) {
+  for (auto& function : *ir_context->module()) {
     for (auto block_it = function.begin(); block_it != function.end();
          ++block_it) {
       if (block_it->id() == message_.block_id()) {
@@ -43,7 +43,7 @@
         }
         // Record the block we would like to consider moving down.
         opt::BasicBlock* block_matching_id = &*block_it;
-        if (!context->GetDominatorAnalysis(&function)->IsReachable(
+        if (!ir_context->GetDominatorAnalysis(&function)->IsReachable(
                 block_matching_id)) {
           // The block is not reachable.  We are not allowed to move it down.
           return false;
@@ -60,7 +60,7 @@
         opt::BasicBlock* next_block_in_program_order = &*block_it;
         // We can move the block of interest down if and only if it does not
         // dominate the block that comes next.
-        return !context->GetDominatorAnalysis(&function)->Dominates(
+        return !ir_context->GetDominatorAnalysis(&function)->Dominates(
             block_matching_id, next_block_in_program_order);
       }
     }
@@ -71,11 +71,11 @@
   return false;
 }
 
-void TransformationMoveBlockDown::Apply(opt::IRContext* context,
-                                        FactManager* /*unused*/) const {
+void TransformationMoveBlockDown::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   // Go through every block in every function, looking for a block whose id
   // matches that of the block we want to move down.
-  for (auto& function : *context->module()) {
+  for (auto& function : *ir_context->module()) {
     for (auto block_it = function.begin(); block_it != function.end();
          ++block_it) {
       if (block_it->id() == message_.block_id()) {
@@ -87,7 +87,7 @@
         // For performance, it is vital to keep the dominator analysis valid
         // (which due to https://github.com/KhronosGroup/SPIRV-Tools/issues/2889
         // requires keeping the CFG analysis valid).
-        context->InvalidateAnalysesExceptFor(
+        ir_context->InvalidateAnalysesExceptFor(
             opt::IRContext::Analysis::kAnalysisDefUse |
             opt::IRContext::Analysis::kAnalysisCFG |
             opt::IRContext::Analysis::kAnalysisDominatorAnalysis);
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_move_block_down.h b/third_party/SPIRV-Tools/source/fuzz/transformation_move_block_down.h
index fd1584a..7551c38 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_move_block_down.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_move_block_down.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_MOVE_BLOCK_DOWN_H_
 #define SOURCE_FUZZ_TRANSFORMATION_MOVE_BLOCK_DOWN_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -35,12 +35,14 @@
   //   in a function.
   // - b must not dominate the block that follows it in program order.
   // - b must be reachable.
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // The block with id |message_.block_id| is moved down; i.e. the program order
   // between it and the block that follows it is swapped.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.cpp
index 01d1c45..05fd923 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.cpp
@@ -70,72 +70,71 @@
 }
 
 bool TransformationOutlineFunction::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   std::set<uint32_t> ids_used_by_this_transformation;
 
   // The various new ids used by the transformation must be fresh and distinct.
 
   if (!CheckIdIsFreshAndNotUsedByThisTransformation(
-          message_.new_function_struct_return_type_id(), context,
+          message_.new_function_struct_return_type_id(), ir_context,
           &ids_used_by_this_transformation)) {
     return false;
   }
 
   if (!CheckIdIsFreshAndNotUsedByThisTransformation(
-          message_.new_function_type_id(), context,
+          message_.new_function_type_id(), ir_context,
           &ids_used_by_this_transformation)) {
     return false;
   }
 
   if (!CheckIdIsFreshAndNotUsedByThisTransformation(
-          message_.new_function_id(), context,
+          message_.new_function_id(), ir_context,
           &ids_used_by_this_transformation)) {
     return false;
   }
 
   if (!CheckIdIsFreshAndNotUsedByThisTransformation(
-          message_.new_function_region_entry_block(), context,
+          message_.new_function_region_entry_block(), ir_context,
           &ids_used_by_this_transformation)) {
     return false;
   }
 
   if (!CheckIdIsFreshAndNotUsedByThisTransformation(
-          message_.new_caller_result_id(), context,
+          message_.new_caller_result_id(), ir_context,
           &ids_used_by_this_transformation)) {
     return false;
   }
 
   if (!CheckIdIsFreshAndNotUsedByThisTransformation(
-          message_.new_callee_result_id(), context,
+          message_.new_callee_result_id(), ir_context,
           &ids_used_by_this_transformation)) {
     return false;
   }
 
   for (auto& pair : message_.input_id_to_fresh_id()) {
     if (!CheckIdIsFreshAndNotUsedByThisTransformation(
-            pair.second(), context, &ids_used_by_this_transformation)) {
+            pair.second(), ir_context, &ids_used_by_this_transformation)) {
       return false;
     }
   }
 
   for (auto& pair : message_.output_id_to_fresh_id()) {
     if (!CheckIdIsFreshAndNotUsedByThisTransformation(
-            pair.second(), context, &ids_used_by_this_transformation)) {
+            pair.second(), ir_context, &ids_used_by_this_transformation)) {
       return false;
     }
   }
 
   // The entry and exit block ids must indeed refer to blocks.
   for (auto block_id : {message_.entry_block(), message_.exit_block()}) {
-    auto block_label = context->get_def_use_mgr()->GetDef(block_id);
+    auto block_label = ir_context->get_def_use_mgr()->GetDef(block_id);
     if (!block_label || block_label->opcode() != SpvOpLabel) {
       return false;
     }
   }
 
-  auto entry_block = context->cfg()->block(message_.entry_block());
-  auto exit_block = context->cfg()->block(message_.exit_block());
+  auto entry_block = ir_context->cfg()->block(message_.entry_block());
+  auto exit_block = ir_context->cfg()->block(message_.exit_block());
 
   // The entry block cannot start with OpVariable - this would mean that
   // outlining would remove a variable from the function containing the region
@@ -151,7 +150,7 @@
 
   // For simplicity, we do not allow the exit block to be a merge block or
   // continue target.
-  if (fuzzerutil::IsMergeOrContinue(context, exit_block->id())) {
+  if (fuzzerutil::IsMergeOrContinue(ir_context, exit_block->id())) {
     return false;
   }
 
@@ -169,14 +168,14 @@
 
   // The entry block must dominate the exit block.
   auto dominator_analysis =
-      context->GetDominatorAnalysis(entry_block->GetParent());
+      ir_context->GetDominatorAnalysis(entry_block->GetParent());
   if (!dominator_analysis->Dominates(entry_block, exit_block)) {
     return false;
   }
 
   // The exit block must post-dominate the entry block.
   auto postdominator_analysis =
-      context->GetPostDominatorAnalysis(entry_block->GetParent());
+      ir_context->GetPostDominatorAnalysis(entry_block->GetParent());
   if (!postdominator_analysis->Dominates(exit_block, entry_block)) {
     return false;
   }
@@ -184,8 +183,9 @@
   // Find all the blocks dominated by |message_.entry_block| and post-dominated
   // by |message_.exit_block|.
   auto region_set = GetRegionBlocks(
-      context, entry_block = context->cfg()->block(message_.entry_block()),
-      exit_block = context->cfg()->block(message_.exit_block()));
+      ir_context,
+      entry_block = ir_context->cfg()->block(message_.entry_block()),
+      exit_block = ir_context->cfg()->block(message_.exit_block()));
 
   // Check whether |region_set| really is a single-entry single-exit region, and
   // also check whether structured control flow constructs and their merge
@@ -198,10 +198,15 @@
   for (auto& block : *entry_block->GetParent()) {
     if (&block == exit_block) {
       // It is OK (and typically expected) for the exit block of the region to
-      // have successors outside the region.  It is also OK for the exit block
-      // to head a structured control flow construct - the block containing the
-      // call to the outlined function will end up heading this construct if
-      // outlining takes place.
+      // have successors outside the region.
+      //
+      // It is also OK for the exit block to head a selection construct: the
+      // block containing the call to the outlined function will end up heading
+      // this construct if outlining takes place.  However, it is not OK for
+      // the exit block to head a loop construct.
+      if (block.GetLoopMergeInst()) {
+        return false;
+      }
       continue;
     }
 
@@ -210,9 +215,9 @@
       // see whether all of the block's successors are in the region.  If they
       // are not, the region is not single-entry single-exit.
       bool all_successors_in_region = true;
-      block.WhileEachSuccessorLabel([&all_successors_in_region, context,
+      block.WhileEachSuccessorLabel([&all_successors_in_region, ir_context,
                                      &region_set](uint32_t successor) -> bool {
-        if (region_set.count(context->cfg()->block(successor)) == 0) {
+        if (region_set.count(ir_context->cfg()->block(successor)) == 0) {
           all_successors_in_region = false;
           return false;
         }
@@ -227,7 +232,8 @@
       // The block is a loop or selection header -- the header and its
       // associated merge block had better both be in the region or both be
       // outside the region.
-      auto merge_block = context->cfg()->block(merge->GetSingleWordOperand(0));
+      auto merge_block =
+          ir_context->cfg()->block(merge->GetSingleWordOperand(0));
       if (region_set.count(&block) != region_set.count(merge_block)) {
         return false;
       }
@@ -236,7 +242,7 @@
     if (auto loop_merge = block.GetLoopMergeInst()) {
       // Similar to the above, but for the continue target of a loop.
       auto continue_target =
-          context->cfg()->block(loop_merge->GetSingleWordOperand(1));
+          ir_context->cfg()->block(loop_merge->GetSingleWordOperand(1));
       if (continue_target != exit_block &&
           region_set.count(&block) != region_set.count(continue_target)) {
         return false;
@@ -248,7 +254,7 @@
   // used inside the region, ...
   std::map<uint32_t, uint32_t> input_id_to_fresh_id_map =
       PairSequenceToMap(message_.input_id_to_fresh_id());
-  for (auto id : GetRegionInputIds(context, region_set, exit_block)) {
+  for (auto id : GetRegionInputIds(ir_context, region_set, exit_block)) {
     // There needs to be a corresponding fresh id to be used as a function
     // parameter.
     if (input_id_to_fresh_id_map.count(id) == 0) {
@@ -256,8 +262,8 @@
     }
     // Furthermore, if the input id has pointer type it must be an OpVariable
     // or OpFunctionParameter.
-    auto input_id_inst = context->get_def_use_mgr()->GetDef(id);
-    if (context->get_def_use_mgr()
+    auto input_id_inst = ir_context->get_def_use_mgr()->GetDef(id);
+    if (ir_context->get_def_use_mgr()
             ->GetDef(input_id_inst->type_id())
             ->opcode() == SpvOpTypePointer) {
       switch (input_id_inst->opcode()) {
@@ -273,12 +279,20 @@
   }
 
   // For each region output id -- i.e. every id defined inside the region but
-  // used outside the region -- there needs to be a corresponding fresh id that
-  // can hold the value for this id computed in the outlined function.
+  // used outside the region, ...
   std::map<uint32_t, uint32_t> output_id_to_fresh_id_map =
       PairSequenceToMap(message_.output_id_to_fresh_id());
-  for (auto id : GetRegionOutputIds(context, region_set, exit_block)) {
-    if (output_id_to_fresh_id_map.count(id) == 0) {
+  for (auto id : GetRegionOutputIds(ir_context, region_set, exit_block)) {
+    if (
+        // ... there needs to be a corresponding fresh id that can hold the
+        // value for this id computed in the outlined function, and ...
+        output_id_to_fresh_id_map.count(id) == 0
+        // ... the output id must not have pointer type (to avoid creating a
+        // struct with pointer members to pass data out of the outlined
+        // function)
+        || ir_context->get_def_use_mgr()
+                   ->GetDef(fuzzerutil::GetTypeId(ir_context, id))
+                   ->opcode() == SpvOpTypePointer) {
       return false;
     }
   }
@@ -287,25 +301,26 @@
 }
 
 void TransformationOutlineFunction::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   // The entry block for the region before outlining.
   auto original_region_entry_block =
-      context->cfg()->block(message_.entry_block());
+      ir_context->cfg()->block(message_.entry_block());
 
   // The exit block for the region before outlining.
   auto original_region_exit_block =
-      context->cfg()->block(message_.exit_block());
+      ir_context->cfg()->block(message_.exit_block());
 
   // The single-entry single-exit region defined by |message_.entry_block| and
   // |message_.exit_block|.
   std::set<opt::BasicBlock*> region_blocks = GetRegionBlocks(
-      context, original_region_entry_block, original_region_exit_block);
+      ir_context, original_region_entry_block, original_region_exit_block);
 
   // Input and output ids for the region being outlined.
   std::vector<uint32_t> region_input_ids =
-      GetRegionInputIds(context, region_blocks, original_region_exit_block);
+      GetRegionInputIds(ir_context, region_blocks, original_region_exit_block);
   std::vector<uint32_t> region_output_ids =
-      GetRegionOutputIds(context, region_blocks, original_region_exit_block);
+      GetRegionOutputIds(ir_context, region_blocks, original_region_exit_block);
 
   // Maps from input and output ids to fresh ids.
   std::map<uint32_t, uint32_t> input_id_to_fresh_id_map =
@@ -313,14 +328,14 @@
   std::map<uint32_t, uint32_t> output_id_to_fresh_id_map =
       PairSequenceToMap(message_.output_id_to_fresh_id());
 
-  UpdateModuleIdBoundForFreshIds(context, input_id_to_fresh_id_map,
+  UpdateModuleIdBoundForFreshIds(ir_context, input_id_to_fresh_id_map,
                                  output_id_to_fresh_id_map);
 
   // Construct a map that associates each output id with its type id.
   std::map<uint32_t, uint32_t> output_id_to_type_id;
   for (uint32_t output_id : region_output_ids) {
     output_id_to_type_id[output_id] =
-        context->get_def_use_mgr()->GetDef(output_id)->type_id();
+        ir_context->get_def_use_mgr()->GetDef(output_id)->type_id();
   }
 
   // The region will be collapsed to a single block that calls a function
@@ -331,53 +346,55 @@
   // collapsed block later.
   std::unique_ptr<opt::Instruction> cloned_exit_block_terminator =
       std::unique_ptr<opt::Instruction>(
-          original_region_exit_block->terminator()->Clone(context));
+          original_region_exit_block->terminator()->Clone(ir_context));
   std::unique_ptr<opt::Instruction> cloned_exit_block_merge =
       original_region_exit_block->GetMergeInst()
           ? std::unique_ptr<opt::Instruction>(
-                original_region_exit_block->GetMergeInst()->Clone(context))
+                original_region_exit_block->GetMergeInst()->Clone(ir_context))
           : nullptr;
 
   // Make a function prototype for the outlined function, which involves
   // figuring out its required type.
-  std::unique_ptr<opt::Function> outlined_function =
-      PrepareFunctionPrototype(region_input_ids, region_output_ids,
-                               input_id_to_fresh_id_map, context, fact_manager);
+  std::unique_ptr<opt::Function> outlined_function = PrepareFunctionPrototype(
+      region_input_ids, region_output_ids, input_id_to_fresh_id_map, ir_context,
+      transformation_context);
 
   // If the original function was livesafe, the new function should also be
   // livesafe.
-  if (fact_manager->FunctionIsLivesafe(
+  if (transformation_context->GetFactManager()->FunctionIsLivesafe(
           original_region_entry_block->GetParent()->result_id())) {
-    fact_manager->AddFactFunctionIsLivesafe(message_.new_function_id());
+    transformation_context->GetFactManager()->AddFactFunctionIsLivesafe(
+        message_.new_function_id());
   }
 
   // Adapt the region to be outlined so that its input ids are replaced with the
   // ids of the outlined function's input parameters, and so that output ids
   // are similarly remapped.
   RemapInputAndOutputIdsInRegion(
-      context, *original_region_exit_block, region_blocks, region_input_ids,
+      ir_context, *original_region_exit_block, region_blocks, region_input_ids,
       region_output_ids, input_id_to_fresh_id_map, output_id_to_fresh_id_map);
 
   // Fill out the body of the outlined function according to the region that is
   // being outlined.
-  PopulateOutlinedFunction(*original_region_entry_block,
-                           *original_region_exit_block, region_blocks,
-                           region_output_ids, output_id_to_fresh_id_map,
-                           context, outlined_function.get(), fact_manager);
+  PopulateOutlinedFunction(
+      *original_region_entry_block, *original_region_exit_block, region_blocks,
+      region_output_ids, output_id_to_fresh_id_map, ir_context,
+      outlined_function.get(), transformation_context);
 
   // Collapse the region that has been outlined into a function down to a single
   // block that calls said function.
   ShrinkOriginalRegion(
-      context, region_blocks, region_input_ids, region_output_ids,
+      ir_context, region_blocks, region_input_ids, region_output_ids,
       output_id_to_type_id, outlined_function->type_id(),
       std::move(cloned_exit_block_merge),
       std::move(cloned_exit_block_terminator), original_region_entry_block);
 
   // Add the outlined function to the module.
-  context->module()->AddFunction(std::move(outlined_function));
+  ir_context->module()->AddFunction(std::move(outlined_function));
 
   // Major surgery has been conducted on the module, so invalidate all analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationOutlineFunction::ToMessage() const {
@@ -387,30 +404,31 @@
 }
 
 std::vector<uint32_t> TransformationOutlineFunction::GetRegionInputIds(
-    opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
+    opt::IRContext* ir_context, const std::set<opt::BasicBlock*>& region_set,
     opt::BasicBlock* region_exit_block) {
   std::vector<uint32_t> result;
 
   auto enclosing_function = region_exit_block->GetParent();
 
   // Consider each parameter of the function containing the region.
-  enclosing_function->ForEachParam([context, &region_set, &result](
-                                       opt::Instruction* function_parameter) {
-    // Consider every use of the parameter.
-    context->get_def_use_mgr()->WhileEachUse(
-        function_parameter, [context, function_parameter, &region_set, &result](
-                                opt::Instruction* use, uint32_t /*unused*/) {
-          // Get the block, if any, in which the parameter is used.
-          auto use_block = context->get_instr_block(use);
-          // If the use is in a block that lies within the region, the
-          // parameter is an input id for the region.
-          if (use_block && region_set.count(use_block) != 0) {
-            result.push_back(function_parameter->result_id());
-            return false;
-          }
-          return true;
-        });
-  });
+  enclosing_function->ForEachParam(
+      [ir_context, &region_set, &result](opt::Instruction* function_parameter) {
+        // Consider every use of the parameter.
+        ir_context->get_def_use_mgr()->WhileEachUse(
+            function_parameter,
+            [ir_context, function_parameter, &region_set, &result](
+                opt::Instruction* use, uint32_t /*unused*/) {
+              // Get the block, if any, in which the parameter is used.
+              auto use_block = ir_context->get_instr_block(use);
+              // If the use is in a block that lies within the region, the
+              // parameter is an input id for the region.
+              if (use_block && region_set.count(use_block) != 0) {
+                result.push_back(function_parameter->result_id());
+                return false;
+              }
+              return true;
+            });
+      });
 
   // Consider all definitions in the function that might turn out to be input
   // ids.
@@ -430,15 +448,15 @@
     // Consider each candidate input id to check whether it is used in the
     // region.
     for (auto& inst : candidate_input_ids_for_block) {
-      context->get_def_use_mgr()->WhileEachUse(
+      ir_context->get_def_use_mgr()->WhileEachUse(
           inst,
-          [context, &inst, region_exit_block, &region_set, &result](
+          [ir_context, &inst, region_exit_block, &region_set, &result](
               opt::Instruction* use, uint32_t /*unused*/) -> bool {
 
             // Find the block in which this id use occurs, recording the id as
             // an input id if the block is outside the region, with some
             // exceptions detailed below.
-            auto use_block = context->get_instr_block(use);
+            auto use_block = ir_context->get_instr_block(use);
 
             if (!use_block) {
               // There might be no containing block, e.g. if the use is in a
@@ -467,7 +485,7 @@
 }
 
 std::vector<uint32_t> TransformationOutlineFunction::GetRegionOutputIds(
-    opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
+    opt::IRContext* ir_context, const std::set<opt::BasicBlock*>& region_set,
     opt::BasicBlock* region_exit_block) {
   std::vector<uint32_t> result;
 
@@ -479,15 +497,15 @@
     }
     // Consider each use of each instruction defined in the block.
     for (auto& inst : block) {
-      context->get_def_use_mgr()->WhileEachUse(
+      ir_context->get_def_use_mgr()->WhileEachUse(
           &inst,
-          [&region_set, context, &inst, region_exit_block, &result](
+          [&region_set, ir_context, &inst, region_exit_block, &result](
               opt::Instruction* use, uint32_t /*unused*/) -> bool {
 
             // Find the block in which this id use occurs, recording the id as
             // an output id if the block is outside the region, with some
             // exceptions detailed below.
-            auto use_block = context->get_instr_block(use);
+            auto use_block = ir_context->get_instr_block(use);
 
             if (!use_block) {
               // There might be no containing block, e.g. if the use is in a
@@ -513,12 +531,13 @@
 }
 
 std::set<opt::BasicBlock*> TransformationOutlineFunction::GetRegionBlocks(
-    opt::IRContext* context, opt::BasicBlock* entry_block,
+    opt::IRContext* ir_context, opt::BasicBlock* entry_block,
     opt::BasicBlock* exit_block) {
   auto enclosing_function = entry_block->GetParent();
-  auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function);
+  auto dominator_analysis =
+      ir_context->GetDominatorAnalysis(enclosing_function);
   auto postdominator_analysis =
-      context->GetPostDominatorAnalysis(enclosing_function);
+      ir_context->GetPostDominatorAnalysis(enclosing_function);
 
   std::set<opt::BasicBlock*> result;
   for (auto& block : *enclosing_function) {
@@ -535,7 +554,8 @@
     const std::vector<uint32_t>& region_input_ids,
     const std::vector<uint32_t>& region_output_ids,
     const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
-    opt::IRContext* context, FactManager* fact_manager) const {
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   uint32_t return_type_id = 0;
   uint32_t function_type_id = 0;
 
@@ -547,14 +567,14 @@
   if (region_output_ids.empty()) {
     std::vector<uint32_t> return_and_parameter_types;
     opt::analysis::Void void_type;
-    return_type_id = context->get_type_mgr()->GetId(&void_type);
+    return_type_id = ir_context->get_type_mgr()->GetId(&void_type);
     return_and_parameter_types.push_back(return_type_id);
     for (auto id : region_input_ids) {
       return_and_parameter_types.push_back(
-          context->get_def_use_mgr()->GetDef(id)->type_id());
+          ir_context->get_def_use_mgr()->GetDef(id)->type_id());
     }
     function_type_id =
-        fuzzerutil::FindFunctionType(context, return_and_parameter_types);
+        fuzzerutil::FindFunctionType(ir_context, return_and_parameter_types);
   }
 
   // If no existing function type was found, we need to create one.
@@ -568,12 +588,12 @@
       opt::Instruction::OperandList struct_member_types;
       for (uint32_t output_id : region_output_ids) {
         auto output_id_type =
-            context->get_def_use_mgr()->GetDef(output_id)->type_id();
+            ir_context->get_def_use_mgr()->GetDef(output_id)->type_id();
         struct_member_types.push_back({SPV_OPERAND_TYPE_ID, {output_id_type}});
       }
       // Add a new struct type to the module.
-      context->module()->AddType(MakeUnique<opt::Instruction>(
-          context, SpvOpTypeStruct, 0,
+      ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpTypeStruct, 0,
           message_.new_function_struct_return_type_id(),
           std::move(struct_member_types)));
       // The return type for the function is the newly-created struct.
@@ -589,12 +609,12 @@
     for (auto id : region_input_ids) {
       function_type_operands.push_back(
           {SPV_OPERAND_TYPE_ID,
-           {context->get_def_use_mgr()->GetDef(id)->type_id()}});
+           {ir_context->get_def_use_mgr()->GetDef(id)->type_id()}});
     }
     // Add a new function type to the module, and record that this is the type
     // id for the new function.
-    context->module()->AddType(MakeUnique<opt::Instruction>(
-        context, SpvOpTypeFunction, 0, message_.new_function_type_id(),
+    ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpTypeFunction, 0, message_.new_function_type_id(),
         function_type_operands));
     function_type_id = message_.new_function_type_id();
   }
@@ -603,7 +623,7 @@
   // and the return type and function type prepared above.
   std::unique_ptr<opt::Function> outlined_function =
       MakeUnique<opt::Function>(MakeUnique<opt::Instruction>(
-          context, SpvOpFunction, return_type_id, message_.new_function_id(),
+          ir_context, SpvOpFunction, return_type_id, message_.new_function_id(),
           opt::Instruction::OperandList(
               {{spv_operand_type_t ::SPV_OPERAND_TYPE_LITERAL_INTEGER,
                 {SpvFunctionControlMaskNone}},
@@ -614,14 +634,15 @@
   // provided in |input_id_to_fresh_id_map|.
   for (auto id : region_input_ids) {
     outlined_function->AddParameter(MakeUnique<opt::Instruction>(
-        context, SpvOpFunctionParameter,
-        context->get_def_use_mgr()->GetDef(id)->type_id(),
+        ir_context, SpvOpFunctionParameter,
+        ir_context->get_def_use_mgr()->GetDef(id)->type_id(),
         input_id_to_fresh_id_map.at(id), opt::Instruction::OperandList()));
     // If the input id is an irrelevant-valued variable, the same should be true
     // of the corresponding parameter.
-    if (fact_manager->PointeeValueIsIrrelevant(id)) {
-      fact_manager->AddFactValueOfPointeeIsIrrelevant(
-          input_id_to_fresh_id_map.at(id));
+    if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
+            id)) {
+      transformation_context->GetFactManager()
+          ->AddFactValueOfPointeeIsIrrelevant(input_id_to_fresh_id_map.at(id));
     }
   }
 
@@ -629,31 +650,32 @@
 }
 
 void TransformationOutlineFunction::UpdateModuleIdBoundForFreshIds(
-    opt::IRContext* context,
+    opt::IRContext* ir_context,
     const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
     const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const {
   // Enlarge the module's id bound as needed to accommodate the various fresh
   // ids associated with the transformation.
   fuzzerutil::UpdateModuleIdBound(
-      context, message_.new_function_struct_return_type_id());
-  fuzzerutil::UpdateModuleIdBound(context, message_.new_function_type_id());
-  fuzzerutil::UpdateModuleIdBound(context, message_.new_function_id());
-  fuzzerutil::UpdateModuleIdBound(context,
+      ir_context, message_.new_function_struct_return_type_id());
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_function_type_id());
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_function_id());
+  fuzzerutil::UpdateModuleIdBound(ir_context,
                                   message_.new_function_region_entry_block());
-  fuzzerutil::UpdateModuleIdBound(context, message_.new_caller_result_id());
-  fuzzerutil::UpdateModuleIdBound(context, message_.new_callee_result_id());
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_caller_result_id());
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_callee_result_id());
 
   for (auto& entry : input_id_to_fresh_id_map) {
-    fuzzerutil::UpdateModuleIdBound(context, entry.second);
+    fuzzerutil::UpdateModuleIdBound(ir_context, entry.second);
   }
 
   for (auto& entry : output_id_to_fresh_id_map) {
-    fuzzerutil::UpdateModuleIdBound(context, entry.second);
+    fuzzerutil::UpdateModuleIdBound(ir_context, entry.second);
   }
 }
 
 void TransformationOutlineFunction::RemapInputAndOutputIdsInRegion(
-    opt::IRContext* context, const opt::BasicBlock& original_region_exit_block,
+    opt::IRContext* ir_context,
+    const opt::BasicBlock& original_region_exit_block,
     const std::set<opt::BasicBlock*>& region_blocks,
     const std::vector<uint32_t>& region_input_ids,
     const std::vector<uint32_t>& region_output_ids,
@@ -664,11 +686,11 @@
   // This is done by considering each region input id in turn.
   for (uint32_t id : region_input_ids) {
     // We then consider each use of the input id.
-    context->get_def_use_mgr()->ForEachUse(
-        id, [context, id, &input_id_to_fresh_id_map, region_blocks](
+    ir_context->get_def_use_mgr()->ForEachUse(
+        id, [ir_context, id, &input_id_to_fresh_id_map, region_blocks](
                 opt::Instruction* use, uint32_t operand_index) {
           // Find the block in which this use of the input id occurs.
-          opt::BasicBlock* use_block = context->get_instr_block(use);
+          opt::BasicBlock* use_block = ir_context->get_instr_block(use);
           // We want to rewrite the use id if its block occurs in the outlined
           // region.
           if (region_blocks.count(use_block) != 0) {
@@ -684,12 +706,12 @@
   // This is done by considering each region output id in turn.
   for (uint32_t id : region_output_ids) {
     // First consider each use of the output id and update the relevant uses.
-    context->get_def_use_mgr()->ForEachUse(
-        id,
-        [context, &original_region_exit_block, id, &output_id_to_fresh_id_map,
-         region_blocks](opt::Instruction* use, uint32_t operand_index) {
+    ir_context->get_def_use_mgr()->ForEachUse(
+        id, [ir_context, &original_region_exit_block, id,
+             &output_id_to_fresh_id_map,
+             region_blocks](opt::Instruction* use, uint32_t operand_index) {
           // Find the block in which this use of the output id occurs.
-          auto use_block = context->get_instr_block(use);
+          auto use_block = ir_context->get_instr_block(use);
           // We want to rewrite the use id if its block occurs in the outlined
           // region, with one exception: the terminator of the exit block of
           // the region is going to remain in the original function, so if the
@@ -710,7 +732,7 @@
     // defines the corresponding fresh id.  We do this after changing all the
     // uses so that the definition of the original id is still registered when
     // we analyse its uses.
-    context->get_def_use_mgr()->GetDef(id)->SetResultId(
+    ir_context->get_def_use_mgr()->GetDef(id)->SetResultId(
         output_id_to_fresh_id_map.at(id));
   }
 }
@@ -721,8 +743,8 @@
     const std::set<opt::BasicBlock*>& region_blocks,
     const std::vector<uint32_t>& region_output_ids,
     const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map,
-    opt::IRContext* context, opt::Function* outlined_function,
-    FactManager* fact_manager) const {
+    opt::IRContext* ir_context, opt::Function* outlined_function,
+    TransformationContext* transformation_context) const {
   // When we create the exit block for the outlined region, we use this pointer
   // to track of it so that we can manipulate it later.
   opt::BasicBlock* outlined_region_exit_block = nullptr;
@@ -732,14 +754,16 @@
   // |message_.new_function_region_entry_block| as its id.
   std::unique_ptr<opt::BasicBlock> outlined_region_entry_block =
       MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
-          context, SpvOpLabel, 0, message_.new_function_region_entry_block(),
+          ir_context, SpvOpLabel, 0, message_.new_function_region_entry_block(),
           opt::Instruction::OperandList()));
   outlined_region_entry_block->SetParent(outlined_function);
 
   // If the original region's entry block was dead, the outlined region's entry
   // block is also dead.
-  if (fact_manager->BlockIsDead(original_region_entry_block.id())) {
-    fact_manager->AddFactBlockIsDead(outlined_region_entry_block->id());
+  if (transformation_context->GetFactManager()->BlockIsDead(
+          original_region_entry_block.id())) {
+    transformation_context->GetFactManager()->AddFactBlockIsDead(
+        outlined_region_entry_block->id());
   }
 
   if (&original_region_entry_block == &original_region_exit_block) {
@@ -748,7 +772,7 @@
 
   for (auto& inst : original_region_entry_block) {
     outlined_region_entry_block->AddInstruction(
-        std::unique_ptr<opt::Instruction>(inst.Clone(context)));
+        std::unique_ptr<opt::Instruction>(inst.Clone(ir_context)));
   }
   outlined_function->AddBasicBlock(std::move(outlined_region_entry_block));
 
@@ -767,7 +791,7 @@
     }
     // Clone the block so that it can be added to the new function.
     auto cloned_block =
-        std::unique_ptr<opt::BasicBlock>(block_it->Clone(context));
+        std::unique_ptr<opt::BasicBlock>(block_it->Clone(ir_context));
 
     // If this is the region's exit block, then the cloned block is the outlined
     // region's exit block.
@@ -823,7 +847,7 @@
     // The case where there are no region output ids is simple: we just add
     // OpReturn.
     outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
-        context, SpvOpReturn, 0, 0, opt::Instruction::OperandList()));
+        ir_context, SpvOpReturn, 0, 0, opt::Instruction::OperandList()));
   } else {
     // In the case where there are output ids, we add an OpCompositeConstruct
     // instruction to pack all the output values into a struct, and then an
@@ -834,21 +858,21 @@
           {SPV_OPERAND_TYPE_ID, {output_id_to_fresh_id_map.at(id)}});
     }
     outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
-        context, SpvOpCompositeConstruct,
+        ir_context, SpvOpCompositeConstruct,
         message_.new_function_struct_return_type_id(),
         message_.new_callee_result_id(), struct_member_operands));
     outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
-        context, SpvOpReturnValue, 0, 0,
+        ir_context, SpvOpReturnValue, 0, 0,
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {message_.new_callee_result_id()}}})));
   }
 
   outlined_function->SetFunctionEnd(MakeUnique<opt::Instruction>(
-      context, SpvOpFunctionEnd, 0, 0, opt::Instruction::OperandList()));
+      ir_context, SpvOpFunctionEnd, 0, 0, opt::Instruction::OperandList()));
 }
 
 void TransformationOutlineFunction::ShrinkOriginalRegion(
-    opt::IRContext* context, std::set<opt::BasicBlock*>& region_blocks,
+    opt::IRContext* ir_context, std::set<opt::BasicBlock*>& region_blocks,
     const std::vector<uint32_t>& region_input_ids,
     const std::vector<uint32_t>& region_output_ids,
     const std::map<uint32_t, uint32_t>& output_id_to_type_id,
@@ -912,7 +936,7 @@
   }
 
   original_region_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
-      context, SpvOpFunctionCall, return_type_id,
+      ir_context, SpvOpFunctionCall, return_type_id,
       message_.new_caller_result_id(), function_call_operands));
 
   // If there are output ids, the function call will return a struct.  For each
@@ -921,7 +945,7 @@
   for (uint32_t index = 0; index < region_output_ids.size(); ++index) {
     uint32_t output_id = region_output_ids[index];
     original_region_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
-        context, SpvOpCompositeExtract, output_id_to_type_id.at(output_id),
+        ir_context, SpvOpCompositeExtract, output_id_to_type_id.at(output_id),
         output_id,
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {message_.new_caller_result_id()}},
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.h b/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.h
index 5711790..ba439c8 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.h
@@ -19,9 +19,9 @@
 #include <set>
 #include <vector>
 
-#include "source/fuzz/fact_manager.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 {
@@ -58,8 +58,9 @@
   //   defined outside the region but used in the region
   // - |message_.output_id_to_fresh_id| must contain an entry for every id
   //   defined in the region but used outside the region
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // - A new function with id |message_.new_function_id| is added to the module.
   // - If the region generates output ids, the return type of this function is
@@ -95,14 +96,15 @@
   //   |message_.new_function_struct_return_type| comprised of all the fresh
   //   output ids (unless the return type is void, in which case no value is
   //   returned.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
   // Returns the set of blocks dominated by |entry_block| and post-dominated
   // by |exit_block|.
   static std::set<opt::BasicBlock*> GetRegionBlocks(
-      opt::IRContext* context, opt::BasicBlock* entry_block,
+      opt::IRContext* ir_context, opt::BasicBlock* entry_block,
       opt::BasicBlock* exit_block);
 
   // Yields ids that are used in |region_set| and that are either parameters
@@ -114,7 +116,7 @@
   // - id uses in OpPhi instructions in |region_entry_block| are ignored
   // - id uses in the terminator instruction of |region_exit_block| are ignored
   static std::vector<uint32_t> GetRegionInputIds(
-      opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
+      opt::IRContext* ir_context, const std::set<opt::BasicBlock*>& region_set,
       opt::BasicBlock* region_exit_block);
 
   // Yields all ids that are defined in |region_set| and used outside
@@ -124,14 +126,14 @@
   // - ids defined in the region and used in the terminator of
   //   |region_exit_block| count as output ids
   static std::vector<uint32_t> GetRegionOutputIds(
-      opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
+      opt::IRContext* ir_context, const std::set<opt::BasicBlock*>& region_set,
       opt::BasicBlock* region_exit_block);
 
  private:
   // Ensures that the module's id bound is at least the maximum of any fresh id
   // associated with the transformation.
   void UpdateModuleIdBoundForFreshIds(
-      opt::IRContext* context,
+      opt::IRContext* ir_context,
       const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
       const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const;
 
@@ -142,7 +144,7 @@
   // modified, and |original_region_exit_block| allows for some special cases
   // where ids should not be remapped.
   void RemapInputAndOutputIdsInRegion(
-      opt::IRContext* context,
+      opt::IRContext* ir_context,
       const opt::BasicBlock& original_region_exit_block,
       const std::set<opt::BasicBlock*>& region_blocks,
       const std::vector<uint32_t>& region_input_ids,
@@ -160,12 +162,14 @@
   // are already present).
   //
   // Facts about the function containing the outlined region that are relevant
-  // to the new function are propagated via |fact_manager|.
+  // to the new function are propagated via the vact manager in
+  // |transformation_context|.
   std::unique_ptr<opt::Function> PrepareFunctionPrototype(
       const std::vector<uint32_t>& region_input_ids,
       const std::vector<uint32_t>& region_output_ids,
       const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
-      opt::IRContext* context, FactManager* fact_manager) const;
+      opt::IRContext* ir_context,
+      TransformationContext* transformation_context) const;
 
   // Creates the body of the outlined function by cloning blocks from the
   // original region, given by |region_blocks|, adapting the cloned version
@@ -174,17 +178,17 @@
   // clone.  Parameters |region_output_ids| and |output_id_to_fresh_id_map| are
   // used to determine what the function should return.
   //
-  // The |fact_manager| argument allow facts about blocks being outlined, e.g.
-  // whether they are dead blocks, to be asserted about blocks that get created
-  // during outlining.
+  // The |transformation_context| argument allow facts about blocks being
+  // outlined, e.g. whether they are dead blocks, to be asserted about blocks
+  // that get created during outlining.
   void PopulateOutlinedFunction(
       const opt::BasicBlock& original_region_entry_block,
       const opt::BasicBlock& original_region_exit_block,
       const std::set<opt::BasicBlock*>& region_blocks,
       const std::vector<uint32_t>& region_output_ids,
       const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map,
-      opt::IRContext* context, opt::Function* outlined_function,
-      FactManager* fact_manager) const;
+      opt::IRContext* ir_context, opt::Function* outlined_function,
+      TransformationContext* transformation_context) const;
 
   // Shrinks the outlined region, given by |region_blocks|, down to the single
   // block |original_region_entry_block|.  This block is itself shrunk to just
@@ -203,7 +207,7 @@
   // function is called, this information cannot be gotten from the def-use
   // manager.
   void ShrinkOriginalRegion(
-      opt::IRContext* context, std::set<opt::BasicBlock*>& region_blocks,
+      opt::IRContext* ir_context, std::set<opt::BasicBlock*>& region_blocks,
       const std::vector<uint32_t>& region_input_ids,
       const std::vector<uint32_t>& region_output_ids,
       const std::map<uint32_t, uint32_t>& output_id_to_type_id,
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 2141533..0f1220e 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
@@ -40,17 +40,17 @@
 }
 
 bool TransformationPermuteFunctionParameters::IsApplicable(
-    opt::IRContext* context, const FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // Check that function exists
   const auto* function =
-      fuzzerutil::FindFunction(context, message_.function_id());
+      fuzzerutil::FindFunction(ir_context, message_.function_id());
   if (!function || function->DefInst().opcode() != SpvOpFunction ||
-      fuzzerutil::FunctionIsEntryPoint(context, function->result_id())) {
+      fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
     return false;
   }
 
   // Check that permutation has valid indices
-  const auto* function_type = fuzzerutil::GetFunctionType(context, function);
+  const auto* function_type = fuzzerutil::GetFunctionType(ir_context, function);
   assert(function_type && "Function type is null");
 
   const auto& permutation = message_.permutation();
@@ -83,7 +83,7 @@
   //   - 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 = context->get_def_use_mgr()->GetDef(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()) {
@@ -109,14 +109,14 @@
 }
 
 void TransformationPermuteFunctionParameters::Apply(
-    opt::IRContext* context, FactManager* /*unused*/) const {
+    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(context, function_id);
+  auto* function = fuzzerutil::FindFunction(ir_context, function_id);
   assert(function && "Can't find the function");
 
   // Change function's type
@@ -149,7 +149,7 @@
       });
 
   // Fix all OpFunctionCall instructions
-  context->get_def_use_mgr()->ForEachUser(
+  ir_context->get_def_use_mgr()->ForEachUser(
       &function->DefInst(),
       [function_id, &permutation](opt::Instruction* call) {
         if (call->opcode() != SpvOpFunctionCall ||
@@ -170,7 +170,8 @@
       });
 
   // Make sure our changes are analyzed
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationPermuteFunctionParameters::ToMessage()
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 c67a735..994e4c2 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
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_
 #define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -40,14 +40,16 @@
   //     - function's arguments are permuted according to |permutation| vector
   // - |permutation| is a set of [0..(n - 1)], where n is a number of arguments
   //   to the function
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // - 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|
   // - Calls to the function are adjusted accordingly
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
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 72d9b22..d6f17fc 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
@@ -128,15 +128,15 @@
 }
 
 bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable(
-    opt::IRContext* context, const FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // The id for the binary result must be fresh
-  if (!fuzzerutil::IsFreshId(context,
+  if (!fuzzerutil::IsFreshId(ir_context,
                              message_.fresh_id_for_binary_operation())) {
     return false;
   }
 
   // The used id must be for a boolean constant
-  auto boolean_constant = context->get_def_use_mgr()->GetDef(
+  auto boolean_constant = ir_context->get_def_use_mgr()->GetDef(
       message_.id_use_descriptor().id_of_interest());
   if (!boolean_constant) {
     return false;
@@ -148,7 +148,7 @@
 
   // The left-hand-side id must correspond to a constant instruction.
   auto lhs_constant_inst =
-      context->get_def_use_mgr()->GetDef(message_.lhs_id());
+      ir_context->get_def_use_mgr()->GetDef(message_.lhs_id());
   if (!lhs_constant_inst) {
     return false;
   }
@@ -158,7 +158,7 @@
 
   // The right-hand-side id must correspond to a constant instruction.
   auto rhs_constant_inst =
-      context->get_def_use_mgr()->GetDef(message_.rhs_id());
+      ir_context->get_def_use_mgr()->GetDef(message_.rhs_id());
   if (!rhs_constant_inst) {
     return false;
   }
@@ -173,9 +173,9 @@
 
   // The expression 'LHS opcode RHS' must evaluate to the boolean constant.
   auto lhs_constant =
-      context->get_constant_mgr()->FindDeclaredConstant(message_.lhs_id());
+      ir_context->get_constant_mgr()->FindDeclaredConstant(message_.lhs_id());
   auto rhs_constant =
-      context->get_constant_mgr()->FindDeclaredConstant(message_.rhs_id());
+      ir_context->get_constant_mgr()->FindDeclaredConstant(message_.rhs_id());
   bool expected_result = (boolean_constant->opcode() == SpvOpConstantTrue);
 
   const auto binary_opcode = static_cast<SpvOp>(message_.opcode());
@@ -238,7 +238,7 @@
 
   // The id use descriptor must identify some instruction
   auto instruction =
-      FindInstructionContainingUse(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
   if (instruction == nullptr) {
     return false;
   }
@@ -262,24 +262,25 @@
 }
 
 void TransformationReplaceBooleanConstantWithConstantBinary::Apply(
-    opt::IRContext* context, FactManager* fact_manager) const {
-  ApplyWithResult(context, fact_manager);
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  ApplyWithResult(ir_context, transformation_context);
 }
 
 opt::Instruction*
 TransformationReplaceBooleanConstantWithConstantBinary::ApplyWithResult(
-    opt::IRContext* context, FactManager* /*unused*/) const {
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   opt::analysis::Bool bool_type;
   opt::Instruction::OperandList operands = {
       {SPV_OPERAND_TYPE_ID, {message_.lhs_id()}},
       {SPV_OPERAND_TYPE_ID, {message_.rhs_id()}}};
   auto binary_instruction = MakeUnique<opt::Instruction>(
-      context, static_cast<SpvOp>(message_.opcode()),
-      context->get_type_mgr()->GetId(&bool_type),
+      ir_context, static_cast<SpvOp>(message_.opcode()),
+      ir_context->get_type_mgr()->GetId(&bool_type),
       message_.fresh_id_for_binary_operation(), operands);
   opt::Instruction* result = binary_instruction.get();
   auto instruction_containing_constant_use =
-      FindInstructionContainingUse(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
 
   // We want to insert the new instruction before the instruction that contains
   // the use of the boolean, but we need to go backwards one more instruction if
@@ -298,9 +299,10 @@
   instruction_containing_constant_use->SetInOperand(
       message_.id_use_descriptor().in_operand_index(),
       {message_.fresh_id_for_binary_operation()});
-  fuzzerutil::UpdateModuleIdBound(context,
+  fuzzerutil::UpdateModuleIdBound(ir_context,
                                   message_.fresh_id_for_binary_operation());
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
   return result;
 }
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h
index f74cd8d..3abb485 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_BOOLEAN_CONSTANT_WITH_CONSTANT_BINARY_H_
 #define SOURCE_FUZZ_TRANSFORMATION_REPLACE_BOOLEAN_CONSTANT_WITH_CONSTANT_BINARY_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -49,20 +49,23 @@
   //   TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): consider
   //    replacing a boolean in an OpPhi by adding a binary operator instruction
   //    to the parent block for the OpPhi.
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // A new instruction is added before the boolean constant usage that computes
   // the result of applying |message_.opcode| to |message_.lhs_id| and
   // |message_.rhs_id| is added, with result id
   // |message_.fresh_id_for_binary_operation|.  The boolean constant usage is
   // replaced with this result id.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   // The same as Apply, except that the newly-added binary instruction is
   // returned.
-  opt::Instruction* ApplyWithResult(opt::IRContext* context,
-                                    FactManager* fact_manager) const;
+  opt::Instruction* ApplyWithResult(
+      opt::IRContext* ir_context,
+      TransformationContext* transformation_context) const;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_constant_with_uniform.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_constant_with_uniform.cpp
index 8e0e4e5..a8f9495 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_constant_with_uniform.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_constant_with_uniform.cpp
@@ -39,12 +39,12 @@
 
 std::unique_ptr<opt::Instruction>
 TransformationReplaceConstantWithUniform::MakeAccessChainInstruction(
-    spvtools::opt::IRContext* context, uint32_t constant_type_id) const {
+    spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const {
   // The input operands for the access chain.
   opt::Instruction::OperandList operands_for_access_chain;
 
   opt::Instruction* uniform_variable =
-      FindUniformVariable(message_.uniform_descriptor(), context, false);
+      FindUniformVariable(message_.uniform_descriptor(), ir_context, false);
 
   // The first input operand is the id of the uniform variable.
   operands_for_access_chain.push_back(
@@ -56,42 +56,43 @@
   // instruction ids as operands.
   opt::analysis::Integer int_type(32, true);
   auto registered_int_type =
-      context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger();
-  auto int_type_id = context->get_type_mgr()->GetId(&int_type);
+      ir_context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger();
+  auto int_type_id = ir_context->get_type_mgr()->GetId(&int_type);
   for (auto index : message_.uniform_descriptor().index()) {
     opt::analysis::IntConstant int_constant(registered_int_type, {index});
-    auto constant_id = context->get_constant_mgr()->FindDeclaredConstant(
+    auto constant_id = ir_context->get_constant_mgr()->FindDeclaredConstant(
         &int_constant, int_type_id);
     operands_for_access_chain.push_back({SPV_OPERAND_TYPE_ID, {constant_id}});
   }
 
   // The type id for the access chain is a uniform pointer with base type
   // matching the given constant id type.
-  auto type_and_pointer_type = context->get_type_mgr()->GetTypeAndPointerType(
-      constant_type_id, SpvStorageClassUniform);
+  auto type_and_pointer_type =
+      ir_context->get_type_mgr()->GetTypeAndPointerType(constant_type_id,
+                                                        SpvStorageClassUniform);
   assert(type_and_pointer_type.first != nullptr);
   assert(type_and_pointer_type.second != nullptr);
   auto pointer_to_uniform_constant_type_id =
-      context->get_type_mgr()->GetId(type_and_pointer_type.second.get());
+      ir_context->get_type_mgr()->GetId(type_and_pointer_type.second.get());
 
   return MakeUnique<opt::Instruction>(
-      context, SpvOpAccessChain, pointer_to_uniform_constant_type_id,
+      ir_context, SpvOpAccessChain, pointer_to_uniform_constant_type_id,
       message_.fresh_id_for_access_chain(), operands_for_access_chain);
 }
 
 std::unique_ptr<opt::Instruction>
 TransformationReplaceConstantWithUniform::MakeLoadInstruction(
-    spvtools::opt::IRContext* context, uint32_t constant_type_id) const {
+    spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const {
   opt::Instruction::OperandList operands_for_load = {
       {SPV_OPERAND_TYPE_ID, {message_.fresh_id_for_access_chain()}}};
-  return MakeUnique<opt::Instruction>(context, SpvOpLoad, constant_type_id,
+  return MakeUnique<opt::Instruction>(ir_context, SpvOpLoad, constant_type_id,
                                       message_.fresh_id_for_load(),
                                       operands_for_load);
 }
 
 bool TransformationReplaceConstantWithUniform::IsApplicable(
-    spvtools::opt::IRContext* context,
-    const spvtools::fuzz::FactManager& fact_manager) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   // The following is really an invariant of the transformation rather than
   // merely a requirement of the precondition.  We check it here since we cannot
   // check it in the message_ constructor.
@@ -99,16 +100,17 @@
          "Fresh ids for access chain and load result cannot be the same.");
 
   // The ids for the access chain and load instructions must both be fresh.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id_for_access_chain())) {
+  if (!fuzzerutil::IsFreshId(ir_context,
+                             message_.fresh_id_for_access_chain())) {
     return false;
   }
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id_for_load())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id_for_load())) {
     return false;
   }
 
   // The id specified in the id use descriptor must be that of a declared scalar
   // constant.
-  auto declared_constant = context->get_constant_mgr()->FindDeclaredConstant(
+  auto declared_constant = ir_context->get_constant_mgr()->FindDeclaredConstant(
       message_.id_use_descriptor().id_of_interest());
   if (!declared_constant) {
     return false;
@@ -120,13 +122,13 @@
   // The fact manager needs to believe that the uniform data element described
   // by the uniform buffer element descriptor will hold a scalar value.
   auto constant_id_associated_with_uniform =
-      fact_manager.GetConstantFromUniformDescriptor(
-          context, message_.uniform_descriptor());
+      transformation_context.GetFactManager()->GetConstantFromUniformDescriptor(
+          ir_context, message_.uniform_descriptor());
   if (!constant_id_associated_with_uniform) {
     return false;
   }
   auto constant_associated_with_uniform =
-      context->get_constant_mgr()->FindDeclaredConstant(
+      ir_context->get_constant_mgr()->FindDeclaredConstant(
           constant_id_associated_with_uniform);
   assert(constant_associated_with_uniform &&
          "The constant should be present in the module.");
@@ -149,7 +151,7 @@
   // The id use descriptor must identify some instruction with respect to the
   // module.
   auto instruction_using_constant =
-      FindInstructionContainingUse(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
   if (!instruction_using_constant) {
     return false;
   }
@@ -165,23 +167,23 @@
   // replace with a uniform.
   opt::analysis::Pointer pointer_to_type_of_constant(declared_constant->type(),
                                                      SpvStorageClassUniform);
-  if (!context->get_type_mgr()->GetId(&pointer_to_type_of_constant)) {
+  if (!ir_context->get_type_mgr()->GetId(&pointer_to_type_of_constant)) {
     return false;
   }
 
   // In order to index into the uniform, the module has got to contain the int32
   // type, plus an OpConstant for each of the indices of interest.
   opt::analysis::Integer int_type(32, true);
-  if (!context->get_type_mgr()->GetId(&int_type)) {
+  if (!ir_context->get_type_mgr()->GetId(&int_type)) {
     return false;
   }
   auto registered_int_type =
-      context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger();
-  auto int_type_id = context->get_type_mgr()->GetId(&int_type);
+      ir_context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger();
+  auto int_type_id = ir_context->get_type_mgr()->GetId(&int_type);
   for (auto index : message_.uniform_descriptor().index()) {
     opt::analysis::IntConstant int_constant(registered_int_type, {index});
-    if (!context->get_constant_mgr()->FindDeclaredConstant(&int_constant,
-                                                           int_type_id)) {
+    if (!ir_context->get_constant_mgr()->FindDeclaredConstant(&int_constant,
+                                                              int_type_id)) {
       return false;
     }
   }
@@ -190,11 +192,11 @@
 }
 
 void TransformationReplaceConstantWithUniform::Apply(
-    spvtools::opt::IRContext* context,
-    spvtools::fuzz::FactManager* /*unused*/) const {
+    spvtools::opt::IRContext* ir_context,
+    TransformationContext* /*unused*/) const {
   // Get the instruction that contains the id use we wish to replace.
   auto instruction_containing_constant_use =
-      FindInstructionContainingUse(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
   assert(instruction_containing_constant_use &&
          "Precondition requires that the id use can be found.");
   assert(instruction_containing_constant_use->GetSingleWordInOperand(
@@ -204,17 +206,17 @@
 
   // The id of the type for the constant whose use we wish to replace.
   auto constant_type_id =
-      context->get_def_use_mgr()
+      ir_context->get_def_use_mgr()
           ->GetDef(message_.id_use_descriptor().id_of_interest())
           ->type_id();
 
   // Add an access chain instruction to target the uniform element.
   instruction_containing_constant_use->InsertBefore(
-      MakeAccessChainInstruction(context, constant_type_id));
+      MakeAccessChainInstruction(ir_context, constant_type_id));
 
   // Add a load from this access chain.
   instruction_containing_constant_use->InsertBefore(
-      MakeLoadInstruction(context, constant_type_id));
+      MakeLoadInstruction(ir_context, constant_type_id));
 
   // Adjust the instruction containing the usage of the constant so that this
   // usage refers instead to the result of the load.
@@ -223,11 +225,12 @@
       {message_.fresh_id_for_load()});
 
   // Update the module id bound to reflect the new instructions.
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id_for_load());
-  fuzzerutil::UpdateModuleIdBound(context,
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id_for_load());
+  fuzzerutil::UpdateModuleIdBound(ir_context,
                                   message_.fresh_id_for_access_chain());
 
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationReplaceConstantWithUniform::ToMessage()
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_constant_with_uniform.h b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_constant_with_uniform.h
index ed354b1..b72407c 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_constant_with_uniform.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_constant_with_uniform.h
@@ -58,8 +58,9 @@
   //     - According to the fact manager, the uniform data element specified by
   //       |message_.uniform_descriptor| holds a value with the same type and
   //       value as %C
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // - Introduces two new instructions:
   //   - An access chain targeting the uniform data element specified by
@@ -68,7 +69,8 @@
   //   - A load from this access chain, with id |message_.fresh_id_for_load|
   // - Replaces the id use specified by |message_.id_use_descriptor| with
   //   |message_.fresh_id_for_load|
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
@@ -76,11 +78,11 @@
   // Helper method to create an access chain for the uniform element associated
   // with the transformation.
   std::unique_ptr<opt::Instruction> MakeAccessChainInstruction(
-      spvtools::opt::IRContext* context, uint32_t constant_type_id) const;
+      spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const;
 
   // Helper to create a load instruction.
   std::unique_ptr<opt::Instruction> MakeLoadInstruction(
-      spvtools::opt::IRContext* context, uint32_t constant_type_id) const;
+      spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const;
 
   protobufs::TransformationReplaceConstantWithUniform message_;
 };
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 88c977a..e427f3c 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
@@ -37,28 +37,29 @@
 }
 
 bool TransformationReplaceIdWithSynonym::IsApplicable(
-    spvtools::opt::IRContext* context,
-    const spvtools::fuzz::FactManager& fact_manager) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   auto id_of_interest = message_.id_use_descriptor().id_of_interest();
 
   // Does the fact manager know about the synonym?
   auto data_descriptor_for_synonymous_id =
       MakeDataDescriptor(message_.synonymous_id(), {});
-  if (!fact_manager.IsSynonymous(MakeDataDescriptor(id_of_interest, {}),
-                                 data_descriptor_for_synonymous_id, context)) {
+  if (!transformation_context.GetFactManager()->IsSynonymous(
+          MakeDataDescriptor(id_of_interest, {}),
+          data_descriptor_for_synonymous_id)) {
     return false;
   }
 
   // Does the id use descriptor in the transformation identify an instruction?
   auto use_instruction =
-      FindInstructionContainingUse(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
   if (!use_instruction) {
     return false;
   }
 
   // Is the use suitable for being replaced in principle?
   if (!UseCanBeReplacedWithSynonym(
-          context, use_instruction,
+          ir_context, use_instruction,
           message_.id_use_descriptor().in_operand_index())) {
     return false;
   }
@@ -66,19 +67,21 @@
   // The transformation is applicable if the synonymous id is available at the
   // use point.
   return fuzzerutil::IdIsAvailableAtUse(
-      context, use_instruction, message_.id_use_descriptor().in_operand_index(),
+      ir_context, use_instruction,
+      message_.id_use_descriptor().in_operand_index(),
       message_.synonymous_id());
 }
 
 void TransformationReplaceIdWithSynonym::Apply(
-    spvtools::opt::IRContext* context,
-    spvtools::fuzz::FactManager* /*unused*/) const {
+    spvtools::opt::IRContext* ir_context,
+    TransformationContext* /*unused*/) const {
   auto instruction_to_change =
-      FindInstructionContainingUse(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
   instruction_to_change->SetInOperand(
       message_.id_use_descriptor().in_operand_index(),
       {message_.synonymous_id()});
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage()
@@ -89,7 +92,7 @@
 }
 
 bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
-    opt::IRContext* context, opt::Instruction* use_instruction,
+    opt::IRContext* ir_context, opt::Instruction* use_instruction,
     uint32_t use_in_operand_index) {
   if (use_instruction->opcode() == SpvOpAccessChain &&
       use_in_operand_index > 0) {
@@ -98,10 +101,10 @@
     // synonym, as the use needs to be an OpConstant.
 
     // Get the top-level composite type that is being accessed.
-    auto object_being_accessed = context->get_def_use_mgr()->GetDef(
+    auto object_being_accessed = ir_context->get_def_use_mgr()->GetDef(
         use_instruction->GetSingleWordInOperand(0));
     auto pointer_type =
-        context->get_type_mgr()->GetType(object_being_accessed->type_id());
+        ir_context->get_type_mgr()->GetType(object_being_accessed->type_id());
     assert(pointer_type->AsPointer());
     auto composite_type_being_accessed =
         pointer_type->AsPointer()->pointee_type();
@@ -122,9 +125,12 @@
       } else if (composite_type_being_accessed->AsArray()) {
         composite_type_being_accessed =
             composite_type_being_accessed->AsArray()->element_type();
+      } else if (composite_type_being_accessed->AsRuntimeArray()) {
+        composite_type_being_accessed =
+            composite_type_being_accessed->AsRuntimeArray()->element_type();
       } else {
         assert(composite_type_being_accessed->AsStruct());
-        auto constant_index_instruction = context->get_def_use_mgr()->GetDef(
+        auto constant_index_instruction = ir_context->get_def_use_mgr()->GetDef(
             use_instruction->GetSingleWordInOperand(index_in_operand));
         assert(constant_index_instruction->opcode() == SpvOpConstant);
         uint32_t member_index =
@@ -149,21 +155,30 @@
     // type.
 
     // Get the definition of the function being called.
-    auto function = context->get_def_use_mgr()->GetDef(
+    auto function = ir_context->get_def_use_mgr()->GetDef(
         use_instruction->GetSingleWordInOperand(0));
     // From the function definition, get the function type.
-    auto function_type =
-        context->get_def_use_mgr()->GetDef(function->GetSingleWordInOperand(1));
+    auto function_type = ir_context->get_def_use_mgr()->GetDef(
+        function->GetSingleWordInOperand(1));
     // OpTypeFunction's 0-th input operand is the function return type, and the
     // function argument types follow. Because the arguments to OpFunctionCall
     // start from input operand 1, we can use |use_in_operand_index| to get the
     // type associated with this function argument.
-    auto parameter_type = context->get_type_mgr()->GetType(
+    auto parameter_type = ir_context->get_type_mgr()->GetType(
         function_type->GetSingleWordInOperand(use_in_operand_index));
     if (parameter_type->AsPointer()) {
       return false;
     }
   }
+
+  if (use_instruction->opcode() == SpvOpImageTexelPointer &&
+      use_in_operand_index == 2) {
+    // The OpImageTexelPointer instruction has a Sample parameter that in some
+    // situations must be an id for the value 0.  To guard against disrupting
+    // that requirement, we do not replace this argument to that instruction.
+    return false;
+  }
+
   return true;
 }
 
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 48132c1..a5a9dfd 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
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_ID_WITH_SYNONYM_H_
 #define SOURCE_FUZZ_TRANSFORMATION_REPLACE_ID_WITH_SYNONYM_H_
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -42,12 +42,14 @@
   // - The id must not be a pointer argument to a function call (because the
   //   synonym might not be a memory object declaration).
   // - |fresh_id_for_temporary| must be 0.
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Replaces the use identified by |message_.id_use_descriptor| with the
   // synonymous id identified by |message_.synonymous_id|.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
@@ -58,7 +60,7 @@
   //   indices must be constants, so it is dangerous to replace them.
   // - the id use is not a pointer function call argument, on which there are
   //   restrictions that make replacement problematic.
-  static bool UseCanBeReplacedWithSynonym(opt::IRContext* context,
+  static bool UseCanBeReplacedWithSynonym(opt::IRContext* ir_context,
                                           opt::Instruction* use_instruction,
                                           uint32_t use_in_operand_index);
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_set_function_control.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_set_function_control.cpp
index d2b61f1..d01e743 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_set_function_control.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_set_function_control.cpp
@@ -28,9 +28,9 @@
 }
 
 bool TransformationSetFunctionControl::IsApplicable(
-    opt::IRContext* context, const FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   opt::Instruction* function_def_instruction =
-      FindFunctionDefInstruction(context);
+      FindFunctionDefInstruction(ir_context);
   if (!function_def_instruction) {
     // The given function id does not correspond to any function.
     return false;
@@ -69,10 +69,10 @@
   return true;
 }
 
-void TransformationSetFunctionControl::Apply(opt::IRContext* context,
-                                             FactManager* /*unused*/) const {
+void TransformationSetFunctionControl::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   opt::Instruction* function_def_instruction =
-      FindFunctionDefInstruction(context);
+      FindFunctionDefInstruction(ir_context);
   function_def_instruction->SetInOperand(0, {message_.function_control()});
 }
 
@@ -83,11 +83,11 @@
 }
 
 opt::Instruction* TransformationSetFunctionControl ::FindFunctionDefInstruction(
-    opt::IRContext* context) const {
+    opt::IRContext* ir_context) const {
   // Look through all functions for a function whose defining instruction's
   // result id matches |message_.function_id|, returning the defining
   // instruction if found.
-  for (auto& function : *context->module()) {
+  for (auto& function : *ir_context->module()) {
     if (function.DefInst().result_id() == message_.function_id()) {
       return &function.DefInst();
     }
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_set_function_control.h b/third_party/SPIRV-Tools/source/fuzz/transformation_set_function_control.h
index 0526bb9..5109f74 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_set_function_control.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_set_function_control.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_
 #define SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -37,17 +37,20 @@
   //   at most one of 'Inline' or 'DontInline', and that may not contain 'Pure'
   //   (respectively 'Const') unless the existing function control mask contains
   //   'Pure' (respectively 'Const').
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // The function control operand of instruction |message_.function_id| is
   // over-written with |message_.function_control|.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
  private:
-  opt::Instruction* FindFunctionDefInstruction(opt::IRContext* context) const;
+  opt::Instruction* FindFunctionDefInstruction(
+      opt::IRContext* ir_context) const;
 
   protobufs::TransformationSetFunctionControl message_;
 };
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_set_loop_control.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_set_loop_control.cpp
index 9062f17..845ac69 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_set_loop_control.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_set_loop_control.cpp
@@ -31,9 +31,9 @@
 }
 
 bool TransformationSetLoopControl::IsApplicable(
-    opt::IRContext* context, const FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // |message_.block_id| must identify a block that ends with OpLoopMerge.
-  auto block = context->get_instr_block(message_.block_id());
+  auto block = ir_context->get_instr_block(message_.block_id());
   if (!block) {
     return false;
   }
@@ -79,7 +79,8 @@
 
   if ((message_.loop_control() &
        (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)) &&
-      !(PeelCountIsSupported(context) && PartialCountIsSupported(context))) {
+      !(PeelCountIsSupported(ir_context) &&
+        PartialCountIsSupported(ir_context))) {
     // At least one of PeelCount or PartialCount is used, but the SPIR-V version
     // in question does not support these loop controls.
     return false;
@@ -104,11 +105,11 @@
             (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)));
 }
 
-void TransformationSetLoopControl::Apply(opt::IRContext* context,
-                                         FactManager* /*unused*/) const {
+void TransformationSetLoopControl::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   // Grab the loop merge instruction and its associated loop control mask.
   auto merge_inst =
-      context->get_instr_block(message_.block_id())->GetMergeInst();
+      ir_context->get_instr_block(message_.block_id())->GetMergeInst();
   auto existing_loop_control_mask =
       merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex);
 
@@ -181,11 +182,11 @@
 }
 
 bool TransformationSetLoopControl::PartialCountIsSupported(
-    opt::IRContext* context) {
+    opt::IRContext* ir_context) {
   // TODO(afd): We capture the universal environments for which this loop
   //  control is definitely not supported.  The check should be refined on
   //  demand for other target environments.
-  switch (context->grammar().target_env()) {
+  switch (ir_context->grammar().target_env()) {
     case SPV_ENV_UNIVERSAL_1_0:
     case SPV_ENV_UNIVERSAL_1_1:
     case SPV_ENV_UNIVERSAL_1_2:
@@ -197,11 +198,11 @@
 }
 
 bool TransformationSetLoopControl::PeelCountIsSupported(
-    opt::IRContext* context) {
+    opt::IRContext* ir_context) {
   // TODO(afd): We capture the universal environments for which this loop
   //  control is definitely not supported.  The check should be refined on
   //  demand for other target environments.
-  switch (context->grammar().target_env()) {
+  switch (ir_context->grammar().target_env()) {
     case SPV_ENV_UNIVERSAL_1_0:
     case SPV_ENV_UNIVERSAL_1_1:
     case SPV_ENV_UNIVERSAL_1_2:
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_set_loop_control.h b/third_party/SPIRV-Tools/source/fuzz/transformation_set_loop_control.h
index 28b148c..f0c364f 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_set_loop_control.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_set_loop_control.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_
 #define SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -38,13 +38,14 @@
   //   instruction.
   // - |message_.loop_control| must be a legal loop control mask that
   //   only uses controls available in the SPIR-V version associated with
-  //   |context|, and must not add loop controls that are only valid in the
+  //   |ir_context|, and must not add loop controls that are only valid in the
   //   presence of guarantees about what the loop does (e.g. MinIterations).
   // - |message_.peel_count| (respectively |message_.partial_count|) must be
   //   zero PeelCount (respectively PartialCount) is set in
   //   |message_.loop_control|.
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // - The loop control operand of the OpLoopMergeInstruction in
   //   |message_.block_id| is overwritten with |message_.loop_control|.
@@ -52,16 +53,17 @@
   //   controls with associated literals that have been removed (e.g.
   //   MinIterations), and any that have been added (PeelCount and/or
   //   PartialCount).
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
   // Does the version of SPIR-V being used support the PartialCount loop
   // control?
-  static bool PartialCountIsSupported(opt::IRContext* context);
+  static bool PartialCountIsSupported(opt::IRContext* ir_context);
 
   // Does the version of SPIR-V being used support the PeelCount loop control?
-  static bool PeelCountIsSupported(opt::IRContext* context);
+  static bool PeelCountIsSupported(opt::IRContext* ir_context);
 
  private:
   // Returns true if and only if |loop_single_bit_mask| is *not* set in
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 a14e1a6..131a499 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
@@ -42,8 +42,7 @@
 }
 
 bool TransformationSetMemoryOperandsMask::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   if (message_.memory_operands_mask_index() != 0) {
     // The following conditions should never be violated, even if
     // transformations end up being replayed in a different way to the manner in
@@ -54,11 +53,11 @@
                SpvOpCopyMemory ||
            message_.memory_access_instruction().target_instruction_opcode() ==
                SpvOpCopyMemorySized);
-    assert(MultipleMemoryOperandMasksAreSupported(context));
+    assert(MultipleMemoryOperandMasksAreSupported(ir_context));
   }
 
   auto instruction =
-      FindInstruction(message_.memory_access_instruction(), context);
+      FindInstruction(message_.memory_access_instruction(), ir_context);
   if (!instruction) {
     return false;
   }
@@ -94,9 +93,9 @@
 }
 
 void TransformationSetMemoryOperandsMask::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   auto instruction =
-      FindInstruction(message_.memory_access_instruction(), context);
+      FindInstruction(message_.memory_access_instruction(), ir_context);
   auto original_mask_in_operand_index = GetInOperandIndexForMask(
       *instruction, message_.memory_operands_mask_index());
   // Either add a new operand, if no mask operand was already present, or
@@ -182,11 +181,11 @@
 }
 
 bool TransformationSetMemoryOperandsMask::
-    MultipleMemoryOperandMasksAreSupported(opt::IRContext* context) {
+    MultipleMemoryOperandMasksAreSupported(opt::IRContext* ir_context) {
   // TODO(afd): We capture the universal environments for which this loop
   //  control is definitely not supported.  The check should be refined on
   //  demand for other target environments.
-  switch (context->grammar().target_env()) {
+  switch (ir_context->grammar().target_env()) {
     case SPV_ENV_UNIVERSAL_1_0:
     case SPV_ENV_UNIVERSAL_1_1:
     case SPV_ENV_UNIVERSAL_1_2:
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_set_memory_operands_mask.h b/third_party/SPIRV-Tools/source/fuzz/transformation_set_memory_operands_mask.h
index 20ae145..9f5081b 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_set_memory_operands_mask.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_set_memory_operands_mask.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_SET_MEMORY_OPERANDS_MASK_H_
 #define SOURCE_FUZZ_TRANSFORMATION_SET_MEMORY_OPERANDS_MASK_H_
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -40,14 +40,16 @@
   // - |message_.memory_operands_mask| must be identical to the original memory
   //   operands mask, except that Volatile may be added, and Nontemporal may be
   //   toggled.
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Replaces the operands mask identified by
   // |message_.memory_operands_mask_index| in the instruction described by
   // |message_.memory_access_instruction| with |message_.memory_operands_mask|,
   // creating an input operand for the mask if no such operand was present.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
@@ -57,7 +59,8 @@
 
   // Does the version of SPIR-V being used support multiple memory operand
   // masks on relevant memory access instructions?
-  static bool MultipleMemoryOperandMasksAreSupported(opt::IRContext* context);
+  static bool MultipleMemoryOperandMasksAreSupported(
+      opt::IRContext* ir_context);
 
   // Helper function to get the input operand index associated with mask number
   // |mask_index|. This is a bit tricky if there are multiple masks, because the
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_set_selection_control.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_set_selection_control.cpp
index ebabdef..bee1e35 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_set_selection_control.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_set_selection_control.cpp
@@ -28,13 +28,13 @@
 }
 
 bool TransformationSetSelectionControl::IsApplicable(
-    opt::IRContext* context, const FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   assert((message_.selection_control() == SpvSelectionControlMaskNone ||
           message_.selection_control() == SpvSelectionControlFlattenMask ||
           message_.selection_control() == SpvSelectionControlDontFlattenMask) &&
          "Selection control should never be set to something other than "
          "'None', 'Flatten' or 'DontFlatten'");
-  if (auto block = context->get_instr_block(message_.block_id())) {
+  if (auto block = ir_context->get_instr_block(message_.block_id())) {
     if (auto merge_inst = block->GetMergeInst()) {
       return merge_inst->opcode() == SpvOpSelectionMerge;
     }
@@ -43,9 +43,9 @@
   return false;
 }
 
-void TransformationSetSelectionControl::Apply(opt::IRContext* context,
-                                              FactManager* /*unused*/) const {
-  context->get_instr_block(message_.block_id())
+void TransformationSetSelectionControl::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  ir_context->get_instr_block(message_.block_id())
       ->GetMergeInst()
       ->SetInOperand(1, {message_.selection_control()});
 }
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_set_selection_control.h b/third_party/SPIRV-Tools/source/fuzz/transformation_set_selection_control.h
index 19e0c3c..21fbdda 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_set_selection_control.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_set_selection_control.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_
 #define SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -35,12 +35,14 @@
   //   instruction.
   // - |message_.selection_control| must be one of None, Flatten or
   //   DontFlatten.
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // - The selection control operand of the OpSelectionMergeInstruction in
   //   |message_.block_id| is overwritten with |message_.selection_control|.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_split_block.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_split_block.cpp
index fc5229e..b020d98 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_split_block.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_split_block.cpp
@@ -35,18 +35,19 @@
 }
 
 bool TransformationSplitBlock::IsApplicable(
-    opt::IRContext* context, const FactManager& /*unused*/) const {
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     // We require the id for the new block to be unused.
     return false;
   }
   auto instruction_to_split_before =
-      FindInstruction(message_.instruction_to_split_before(), context);
+      FindInstruction(message_.instruction_to_split_before(), ir_context);
   if (!instruction_to_split_before) {
     // The instruction describing the block we should split does not exist.
     return false;
   }
-  auto block_to_split = context->get_instr_block(instruction_to_split_before);
+  auto block_to_split =
+      ir_context->get_instr_block(instruction_to_split_before);
   assert(block_to_split &&
          "We should not have managed to find the "
          "instruction if it was not contained in a block.");
@@ -75,16 +76,43 @@
   }
   // We cannot split before an OpPhi unless the OpPhi has exactly one
   // associated incoming edge.
-  return !(split_before->opcode() == SpvOpPhi &&
-           split_before->NumInOperands() != 2);
+  if (split_before->opcode() == SpvOpPhi &&
+      split_before->NumInOperands() != 2) {
+    return false;
+  }
+
+  // Splitting the block must not separate the definition of an OpSampledImage
+  // from its use: the SPIR-V data rules require them to be in the same block.
+  std::set<uint32_t> sampled_image_result_ids;
+  bool before_split = true;
+  for (auto& instruction : *block_to_split) {
+    if (&instruction == &*split_before) {
+      before_split = false;
+    }
+    if (before_split) {
+      if (instruction.opcode() == SpvOpSampledImage) {
+        sampled_image_result_ids.insert(instruction.result_id());
+      }
+    } else {
+      if (!instruction.WhileEachInId(
+              [&sampled_image_result_ids](uint32_t* id) -> bool {
+                return !sampled_image_result_ids.count(*id);
+              })) {
+        return false;
+      }
+    }
+  }
+
+  return true;
 }
 
-void TransformationSplitBlock::Apply(opt::IRContext* context,
-                                     FactManager* fact_manager) const {
+void TransformationSplitBlock::Apply(
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   opt::Instruction* instruction_to_split_before =
-      FindInstruction(message_.instruction_to_split_before(), context);
+      FindInstruction(message_.instruction_to_split_before(), ir_context);
   opt::BasicBlock* block_to_split =
-      context->get_instr_block(instruction_to_split_before);
+      ir_context->get_instr_block(instruction_to_split_before);
   auto split_before = fuzzerutil::GetIteratorForInstruction(
       block_to_split, instruction_to_split_before);
   assert(split_before != block_to_split->end() &&
@@ -93,14 +121,14 @@
 
   // We need to make sure the module's id bound is large enough to add the
   // fresh id.
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   // Split the block.
-  auto new_bb = block_to_split->SplitBasicBlock(context, message_.fresh_id(),
+  auto new_bb = block_to_split->SplitBasicBlock(ir_context, message_.fresh_id(),
                                                 split_before);
   // The split does not automatically add a branch between the two parts of
   // the original block, so we add one.
   block_to_split->AddInstruction(MakeUnique<opt::Instruction>(
-      context, SpvOpBranch, 0, 0,
+      ir_context, SpvOpBranch, 0, 0,
       std::initializer_list<opt::Operand>{opt::Operand(
           spv_operand_type_t::SPV_OPERAND_TYPE_ID, {message_.fresh_id()})}));
   // If we split before OpPhi instructions, we need to update their
@@ -117,12 +145,15 @@
 
   // If the block being split was dead, the new block arising from the split is
   // also dead.
-  if (fact_manager->BlockIsDead(block_to_split->id())) {
-    fact_manager->AddFactBlockIsDead(message_.fresh_id());
+  if (transformation_context->GetFactManager()->BlockIsDead(
+          block_to_split->id())) {
+    transformation_context->GetFactManager()->AddFactBlockIsDead(
+        message_.fresh_id());
   }
 
   // Invalidate all analyses
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationSplitBlock::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_split_block.h b/third_party/SPIRV-Tools/source/fuzz/transformation_split_block.h
index a193fc7..3bf6dfd 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_split_block.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_split_block.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_SPLIT_BLOCK_H_
 #define SOURCE_FUZZ_TRANSFORMATION_SPLIT_BLOCK_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -40,8 +40,9 @@
   // - Splitting 'blk' at 'inst', so that all instructions from 'inst' onwards
   //   appear in a new block that 'blk' directly jumps to must be valid.
   // - |message_.fresh_id| must not be used by the module.
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // - A new block with label |message_.fresh_id| is inserted right after 'blk'
   //   in program order.
@@ -49,7 +50,8 @@
   //   block.
   // - 'blk' is made to jump unconditionally to the new block.
   // - If 'blk' was dead, the new block is also dead.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_store.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_store.cpp
index 7cb7611..f77afe3 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_store.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_store.cpp
@@ -34,23 +34,23 @@
 }
 
 bool TransformationStore::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& fact_manager) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   // The pointer must exist and have a type.
-  auto pointer = context->get_def_use_mgr()->GetDef(message_.pointer_id());
+  auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id());
   if (!pointer || !pointer->type_id()) {
     return false;
   }
 
   // The pointer type must indeed be a pointer.
-  auto pointer_type = context->get_def_use_mgr()->GetDef(pointer->type_id());
+  auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
   assert(pointer_type && "Type id must be defined.");
   if (pointer_type->opcode() != SpvOpTypePointer) {
     return false;
   }
 
   // The pointer must not be read only.
-  if (pointer_type->GetSingleWordInOperand(0) == SpvStorageClassInput) {
+  if (pointer->IsReadOnlyPointer()) {
     return false;
   }
 
@@ -65,7 +65,7 @@
 
   // Determine which instruction we should be inserting before.
   auto insert_before =
-      FindInstruction(message_.instruction_to_insert_before(), context);
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
   // It must exist, ...
   if (!insert_before) {
     return false;
@@ -79,14 +79,15 @@
   // The block we are inserting into needs to be dead, or else the pointee type
   // of the pointer we are storing to needs to be irrelevant (otherwise the
   // store could impact on the observable behaviour of the module).
-  if (!fact_manager.BlockIsDead(
-          context->get_instr_block(insert_before)->id()) &&
-      !fact_manager.PointeeValueIsIrrelevant(message_.pointer_id())) {
+  if (!transformation_context.GetFactManager()->BlockIsDead(
+          ir_context->get_instr_block(insert_before)->id()) &&
+      !transformation_context.GetFactManager()->PointeeValueIsIrrelevant(
+          message_.pointer_id())) {
     return false;
   }
 
   // The value being stored needs to exist and have a type.
-  auto value = context->get_def_use_mgr()->GetDef(message_.value_id());
+  auto value = ir_context->get_def_use_mgr()->GetDef(message_.value_id());
   if (!value || !value->type_id()) {
     return false;
   }
@@ -97,25 +98,25 @@
   }
 
   // The pointer needs to be available at the insertion point.
-  if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+  if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
                                                   message_.pointer_id())) {
     return false;
   }
 
   // The value needs to be available at the insertion point.
-  return fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+  return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
                                                     message_.value_id());
 }
 
-void TransformationStore::Apply(opt::IRContext* context,
-                                spvtools::fuzz::FactManager* /*unused*/) const {
-  FindInstruction(message_.instruction_to_insert_before(), context)
+void TransformationStore::Apply(opt::IRContext* ir_context,
+                                TransformationContext* /*unused*/) const {
+  FindInstruction(message_.instruction_to_insert_before(), ir_context)
       ->InsertBefore(MakeUnique<opt::Instruction>(
-          context, SpvOpStore, 0, 0,
+          ir_context, SpvOpStore, 0, 0,
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
                {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})));
-  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationStore::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_store.h b/third_party/SPIRV-Tools/source/fuzz/transformation_store.h
index 699afdd..6746aab 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_store.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_store.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_STORE_H_
 #define SOURCE_FUZZ_TRANSFORMATION_STORE_H_
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -42,14 +42,16 @@
   //   to dominance rules)
   // - Either the insertion point must be in a dead block, or it must be known
   //   that the pointee value of |message_.pointer_id| is irrelevant
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Adds an instruction of the form:
   //   OpStore |pointer_id| |value_id|
   // before the instruction identified by
   // |message_.instruction_to_insert_before|.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.cpp
index 49d9de8..b7622a2 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.cpp
@@ -31,10 +31,10 @@
 }
 
 bool TransformationSwapCommutableOperands::IsApplicable(
-    opt::IRContext* context, const spvtools::fuzz::FactManager& /*unused*/
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/
     ) const {
   auto instruction =
-      FindInstruction(message_.instruction_descriptor(), context);
+      FindInstruction(message_.instruction_descriptor(), ir_context);
   if (instruction == nullptr) return false;
 
   SpvOp opcode = static_cast<SpvOp>(
@@ -46,10 +46,10 @@
 }
 
 void TransformationSwapCommutableOperands::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/
+    opt::IRContext* ir_context, TransformationContext* /*unused*/
     ) const {
   auto instruction =
-      FindInstruction(message_.instruction_descriptor(), context);
+      FindInstruction(message_.instruction_descriptor(), ir_context);
   // By design, the instructions defined to be commutative have exactly two
   // input parameters.
   std::swap(instruction->GetInOperand(0), instruction->GetInOperand(1));
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.h b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.h
index 061e92d..7fe5b70 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_H_
 #define SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -33,11 +33,13 @@
 
   // - |message_.instruction_descriptor| must identify an existing
   // commutative instruction
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Swaps the commutable operands.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp
index ace331a..ca24a18 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp
@@ -33,10 +33,10 @@
 }
 
 bool TransformationToggleAccessChainInstruction::IsApplicable(
-    opt::IRContext* context, const spvtools::fuzz::FactManager& /*unused*/
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/
     ) const {
   auto instruction =
-      FindInstruction(message_.instruction_descriptor(), context);
+      FindInstruction(message_.instruction_descriptor(), ir_context);
   if (instruction == nullptr) {
     return false;
   }
@@ -56,10 +56,10 @@
 }
 
 void TransformationToggleAccessChainInstruction::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/
+    opt::IRContext* ir_context, TransformationContext* /*unused*/
     ) const {
   auto instruction =
-      FindInstruction(message_.instruction_descriptor(), context);
+      FindInstruction(message_.instruction_descriptor(), ir_context);
   SpvOp opcode = instruction->opcode();
 
   if (opcode == SpvOpAccessChain) {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.h b/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.h
index 125e1ab..9cd8fd6 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.h
@@ -15,9 +15,9 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
 #define SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
 
-#include "source/fuzz/fact_manager.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 {
@@ -33,11 +33,13 @@
 
   // - |message_.instruction_descriptor| must identify an existing
   //   access chain instruction
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Toggles the access chain instruction.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
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 e2d889d..ee64292 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.cpp
@@ -39,38 +39,37 @@
 }
 
 bool TransformationVectorShuffle::IsApplicable(
-    opt::IRContext* context,
-    const spvtools::fuzz::FactManager& /*unused*/) const {
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // The fresh id must not already be in use.
-  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
   // The instruction before which the shuffle will be inserted must exist.
   auto instruction_to_insert_before =
-      FindInstruction(message_.instruction_to_insert_before(), context);
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
   if (!instruction_to_insert_before) {
     return false;
   }
   // The first vector must be an instruction with a type id
   auto vector1_instruction =
-      context->get_def_use_mgr()->GetDef(message_.vector1());
+      ir_context->get_def_use_mgr()->GetDef(message_.vector1());
   if (!vector1_instruction || !vector1_instruction->type_id()) {
     return false;
   }
   // The second vector must be an instruction with a type id
   auto vector2_instruction =
-      context->get_def_use_mgr()->GetDef(message_.vector2());
+      ir_context->get_def_use_mgr()->GetDef(message_.vector2());
   if (!vector2_instruction || !vector2_instruction->type_id()) {
     return false;
   }
   auto vector1_type =
-      context->get_type_mgr()->GetType(vector1_instruction->type_id());
+      ir_context->get_type_mgr()->GetType(vector1_instruction->type_id());
   // The first vector instruction's type must actually be a vector type.
   if (!vector1_type->AsVector()) {
     return false;
   }
   auto vector2_type =
-      context->get_type_mgr()->GetType(vector2_instruction->type_id());
+      ir_context->get_type_mgr()->GetType(vector2_instruction->type_id());
   // The second vector instruction's type must actually be a vector type.
   if (!vector2_type->AsVector()) {
     return false;
@@ -92,14 +91,14 @@
   }
   // The module must already declare an appropriate type in which to store the
   // result of the shuffle.
-  if (!GetResultTypeId(context, *vector1_type->AsVector()->element_type())) {
+  if (!GetResultTypeId(ir_context, *vector1_type->AsVector()->element_type())) {
     return false;
   }
   // Each of the vectors used in the shuffle must be available at the insertion
   // point.
   for (auto used_instruction : {vector1_instruction, vector2_instruction}) {
-    if (auto block = context->get_instr_block(used_instruction)) {
-      if (!context->GetDominatorAnalysis(block->GetParent())
+    if (auto block = ir_context->get_instr_block(used_instruction)) {
+      if (!ir_context->GetDominatorAnalysis(block->GetParent())
                ->Dominates(used_instruction, instruction_to_insert_before)) {
         return false;
       }
@@ -113,7 +112,8 @@
 }
 
 void TransformationVectorShuffle::Apply(
-    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   // Make input operands for a shuffle instruction - these comprise the two
   // vectors being shuffled, followed by the integer literal components.
   opt::Instruction::OperandList shuffle_operands = {
@@ -125,16 +125,18 @@
   }
 
   uint32_t result_type_id = GetResultTypeId(
-      context, *GetVectorType(context, message_.vector1())->element_type());
+      ir_context,
+      *GetVectorType(ir_context, message_.vector1())->element_type());
 
   // Add a shuffle instruction right before the instruction identified by
   // |message_.instruction_to_insert_before|.
-  FindInstruction(message_.instruction_to_insert_before(), context)
+  FindInstruction(message_.instruction_to_insert_before(), ir_context)
       ->InsertBefore(MakeUnique<opt::Instruction>(
-          context, SpvOpVectorShuffle, result_type_id, message_.fresh_id(),
+          ir_context, SpvOpVectorShuffle, result_type_id, message_.fresh_id(),
           shuffle_operands));
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
 
   // Add synonym facts relating the defined elements of the shuffle result to
   // the vector components that they come from.
@@ -158,24 +160,26 @@
     // Get a data descriptor for the component of the input vector to which
     // |component| refers.
     if (component <
-        GetVectorType(context, message_.vector1())->element_count()) {
+        GetVectorType(ir_context, message_.vector1())->element_count()) {
       descriptor_for_source_component =
           MakeDataDescriptor(message_.vector1(), {component});
     } else {
       auto index_into_vector_2 =
           component -
-          GetVectorType(context, message_.vector1())->element_count();
-      assert(index_into_vector_2 <
-                 GetVectorType(context, message_.vector2())->element_count() &&
-             "Vector shuffle index is out of bounds.");
+          GetVectorType(ir_context, message_.vector1())->element_count();
+      assert(
+          index_into_vector_2 <
+              GetVectorType(ir_context, message_.vector2())->element_count() &&
+          "Vector shuffle index is out of bounds.");
       descriptor_for_source_component =
           MakeDataDescriptor(message_.vector2(), {index_into_vector_2});
     }
 
     // Add a fact relating this input vector component with the associated
     // result component.
-    fact_manager->AddFactDataSynonym(descriptor_for_result_component,
-                                     descriptor_for_source_component, context);
+    transformation_context->GetFactManager()->AddFactDataSynonym(
+        descriptor_for_result_component, descriptor_for_source_component,
+        ir_context);
   }
 }
 
@@ -186,16 +190,16 @@
 }
 
 uint32_t TransformationVectorShuffle::GetResultTypeId(
-    opt::IRContext* context, const opt::analysis::Type& element_type) const {
+    opt::IRContext* ir_context, const opt::analysis::Type& element_type) const {
   opt::analysis::Vector result_type(
       &element_type, static_cast<uint32_t>(message_.component_size()));
-  return context->get_type_mgr()->GetId(&result_type);
+  return ir_context->get_type_mgr()->GetId(&result_type);
 }
 
 opt::analysis::Vector* TransformationVectorShuffle::GetVectorType(
-    opt::IRContext* context, uint32_t id_of_vector) {
-  return context->get_type_mgr()
-      ->GetType(context->get_def_use_mgr()->GetDef(id_of_vector)->type_id())
+    opt::IRContext* ir_context, uint32_t id_of_vector) {
+  return ir_context->get_type_mgr()
+      ->GetType(ir_context->get_def_use_mgr()->GetDef(id_of_vector)->type_id())
       ->AsVector();
 }
 
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 81ed227..f73fc31 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.h
@@ -15,10 +15,11 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_VECTOR_SHUFFLE_H_
 #define SOURCE_FUZZ_TRANSFORMATION_VECTOR_SHUFFLE_H_
 
-#include "source/fuzz/fact_manager.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
+
 #include "source/opt/types.h"
 
 namespace spvtools {
@@ -45,8 +46,9 @@
   // - The module must already contain a vector type with the same element type
   //   as |message_.vector1| and |message_.vector2|, and with the size of
   //   |message_component| as its element count
-  bool IsApplicable(opt::IRContext* context,
-                    const FactManager& fact_manager) const override;
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
 
   // Inserts an OpVectorShuffle instruction before
   // |message_.instruction_to_insert_before|, shuffles vectors
@@ -58,19 +60,20 @@
   // result vector is a contiguous sub-range of one of the input vectors, a
   // fact is added to record that |message_.fresh_id| is synonymous with this
   // sub-range.
-  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
 
   protobufs::Transformation ToMessage() const override;
 
  private:
-  // Returns a type id that already exists in |context| suitable for
+  // Returns a type id that already exists in |ir_context| suitable for
   // representing the result of the shuffle, where |element_type| is known to
   // be the common element type of the vectors to which the shuffle is being
   // applied.  Returns 0 if no such id exists.
-  uint32_t GetResultTypeId(opt::IRContext* context,
+  uint32_t GetResultTypeId(opt::IRContext* ir_context,
                            const opt::analysis::Type& element_type) const;
 
-  static opt::analysis::Vector* GetVectorType(opt::IRContext* context,
+  static opt::analysis::Vector* GetVectorType(opt::IRContext* ir_context,
                                               uint32_t id_of_vector);
 
   protobufs::TransformationVectorShuffle message_;
diff --git a/third_party/SPIRV-Tools/source/opt/CMakeLists.txt b/third_party/SPIRV-Tools/source/opt/CMakeLists.txt
index 1428c74..0047c34 100644
--- a/third_party/SPIRV-Tools/source/opt/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/source/opt/CMakeLists.txt
@@ -34,6 +34,7 @@
   dead_variable_elimination.h
   decompose_initialized_variables_pass.h
   decoration_manager.h
+  debug_info_manager.h
   def_use_manager.h
   desc_sroa.h
   dominator_analysis.h
@@ -141,6 +142,7 @@
   dead_variable_elimination.cpp
   decompose_initialized_variables_pass.cpp
   decoration_manager.cpp
+  debug_info_manager.cpp
   def_use_manager.cpp
   desc_sroa.cpp
   dominator_analysis.cpp
diff --git a/third_party/SPIRV-Tools/source/opt/code_sink.cpp b/third_party/SPIRV-Tools/source/opt/code_sink.cpp
index 9d54ee5..4c88cd4 100644
--- a/third_party/SPIRV-Tools/source/opt/code_sink.cpp
+++ b/third_party/SPIRV-Tools/source/opt/code_sink.cpp
@@ -177,7 +177,7 @@
     return true;
   }
 
-  if (base_ptr->IsReadOnlyVariable()) {
+  if (base_ptr->IsReadOnlyPointer()) {
     return false;
   }
 
diff --git a/third_party/SPIRV-Tools/source/opt/debug_info_manager.cpp b/third_party/SPIRV-Tools/source/opt/debug_info_manager.cpp
new file mode 100644
index 0000000..9d98584
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/opt/debug_info_manager.cpp
@@ -0,0 +1,297 @@
+// 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/opt/debug_info_manager.h"
+
+#include <cassert>
+
+#include "source/opt/ir_context.h"
+
+// Constants for OpenCL.DebugInfo.100 extension instructions.
+
+static const uint32_t kOpLineOperandLineIndex = 1;
+static const uint32_t kLineOperandIndexDebugFunction = 7;
+static const uint32_t kLineOperandIndexDebugLexicalBlock = 5;
+static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
+static const uint32_t kDebugInlinedAtOperandInlinedIndex = 6;
+
+namespace spvtools {
+namespace opt {
+namespace analysis {
+namespace {
+
+void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) {
+  assert(dbg_inlined_at);
+  assert(dbg_inlined_at->GetOpenCL100DebugOpcode() ==
+         OpenCLDebugInfo100DebugInlinedAt);
+  if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) {
+    dbg_inlined_at->AddOperand({SPV_OPERAND_TYPE_RESULT_ID, {inlined_operand}});
+  } else {
+    dbg_inlined_at->SetOperand(kDebugInlinedAtOperandInlinedIndex,
+                               {inlined_operand});
+  }
+}
+
+uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) {
+  assert(dbg_inlined_at);
+  assert(dbg_inlined_at->GetOpenCL100DebugOpcode() ==
+         OpenCLDebugInfo100DebugInlinedAt);
+  if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex)
+    return kNoInlinedAt;
+  return dbg_inlined_at->GetSingleWordOperand(
+      kDebugInlinedAtOperandInlinedIndex);
+}
+
+}  // namespace
+
+DebugInfoManager::DebugInfoManager(IRContext* c) : context_(c) {
+  AnalyzeDebugInsts(*c->module());
+}
+
+Instruction* DebugInfoManager::GetDbgInst(uint32_t id) {
+  auto dbg_inst_it = id_to_dbg_inst_.find(id);
+  return dbg_inst_it == id_to_dbg_inst_.end() ? nullptr : dbg_inst_it->second;
+}
+
+void DebugInfoManager::RegisterDbgInst(Instruction* inst) {
+  assert(
+      inst->NumInOperands() != 0 &&
+      context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() ==
+          inst->GetInOperand(0).words[0] &&
+      "Given instruction is not a debug instruction");
+  id_to_dbg_inst_[inst->result_id()] = inst;
+}
+
+void DebugInfoManager::RegisterDbgFunction(Instruction* inst) {
+  assert(inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction &&
+         "inst is not a DebugFunction");
+  auto fn_id = inst->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex);
+  assert(
+      fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() &&
+      "Register DebugFunction for a function that already has DebugFunction");
+  fn_id_to_dbg_fn_[fn_id] = inst;
+}
+
+uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
+                                                const DebugScope& scope) {
+  if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() ==
+      0)
+    return kNoInlinedAt;
+
+  uint32_t line_number = 0;
+  if (line == nullptr) {
+    auto* lexical_scope_inst = GetDbgInst(scope.GetLexicalScope());
+    if (lexical_scope_inst == nullptr) return kNoInlinedAt;
+    OpenCLDebugInfo100Instructions debug_opcode =
+        lexical_scope_inst->GetOpenCL100DebugOpcode();
+    switch (debug_opcode) {
+      case OpenCLDebugInfo100DebugFunction:
+        line_number = lexical_scope_inst->GetSingleWordOperand(
+            kLineOperandIndexDebugFunction);
+        break;
+      case OpenCLDebugInfo100DebugLexicalBlock:
+        line_number = lexical_scope_inst->GetSingleWordOperand(
+            kLineOperandIndexDebugLexicalBlock);
+        break;
+      case OpenCLDebugInfo100DebugTypeComposite:
+      case OpenCLDebugInfo100DebugCompilationUnit:
+        assert(false &&
+               "DebugTypeComposite and DebugCompilationUnit are lexical "
+               "scopes, but we inline functions into a function or a block "
+               "of a function, not into a struct/class or a global scope.");
+        break;
+      default:
+        assert(false &&
+               "Unreachable. a debug extension instruction for a "
+               "lexical scope must be DebugFunction, DebugTypeComposite, "
+               "DebugLexicalBlock, or DebugCompilationUnit.");
+        break;
+    }
+  } else {
+    line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex);
+  }
+
+  uint32_t result_id = context()->TakeNextId();
+  std::unique_ptr<Instruction> inlined_at(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>(OpenCLDebugInfo100DebugInlinedAt)}},
+          {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}},
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetLexicalScope()}},
+      }));
+  // |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()}});
+  }
+  RegisterDbgInst(inlined_at.get());
+  context()->module()->AddExtInstDebugInfo(std::move(inlined_at));
+  return result_id;
+}
+
+DebugScope DebugInfoManager::BuildDebugScope(
+    const DebugScope& callee_instr_scope,
+    DebugInlinedAtContext* inlined_at_ctx) {
+  return DebugScope(callee_instr_scope.GetLexicalScope(),
+                    BuildDebugInlinedAtChain(callee_instr_scope.GetInlinedAt(),
+                                             inlined_at_ctx));
+}
+
+uint32_t DebugInfoManager::BuildDebugInlinedAtChain(
+    uint32_t callee_inlined_at, DebugInlinedAtContext* inlined_at_ctx) {
+  if (inlined_at_ctx->GetScopeOfCallInstruction().GetLexicalScope() ==
+      kNoDebugScope)
+    return kNoInlinedAt;
+
+  // Reuse the already generated DebugInlinedAt chain if exists.
+  uint32_t already_generated_chain_head_id =
+      inlined_at_ctx->GetDebugInlinedAtChain(callee_inlined_at);
+  if (already_generated_chain_head_id != kNoInlinedAt) {
+    return already_generated_chain_head_id;
+  }
+
+  const uint32_t new_dbg_inlined_at_id =
+      CreateDebugInlinedAt(inlined_at_ctx->GetLineOfCallInstruction(),
+                           inlined_at_ctx->GetScopeOfCallInstruction());
+  if (new_dbg_inlined_at_id == kNoInlinedAt) return kNoInlinedAt;
+
+  if (callee_inlined_at == kNoInlinedAt) {
+    inlined_at_ctx->SetDebugInlinedAtChain(kNoInlinedAt, new_dbg_inlined_at_id);
+    return new_dbg_inlined_at_id;
+  }
+
+  uint32_t chain_head_id = kNoInlinedAt;
+  uint32_t chain_iter_id = callee_inlined_at;
+  Instruction* last_inlined_at_in_chain = nullptr;
+  do {
+    Instruction* new_inlined_at_in_chain = CloneDebugInlinedAt(
+        chain_iter_id, /* insert_before */ last_inlined_at_in_chain);
+    assert(new_inlined_at_in_chain != nullptr);
+
+    // Set DebugInlinedAt of the new scope as the head of the chain.
+    if (chain_head_id == kNoInlinedAt)
+      chain_head_id = new_inlined_at_in_chain->result_id();
+
+    // Previous DebugInlinedAt of the chain must point to the new
+    // DebugInlinedAt as its Inlined operand to build a recursive
+    // chain.
+    if (last_inlined_at_in_chain != nullptr) {
+      SetInlinedOperand(last_inlined_at_in_chain,
+                        new_inlined_at_in_chain->result_id());
+    }
+    last_inlined_at_in_chain = new_inlined_at_in_chain;
+
+    chain_iter_id = GetInlinedOperand(new_inlined_at_in_chain);
+  } while (chain_iter_id != kNoInlinedAt);
+
+  // Put |new_dbg_inlined_at_id| into the end of the chain.
+  SetInlinedOperand(last_inlined_at_in_chain, new_dbg_inlined_at_id);
+
+  // Keep the new chain information that will be reused it.
+  inlined_at_ctx->SetDebugInlinedAtChain(callee_inlined_at, chain_head_id);
+  return chain_head_id;
+}
+
+Instruction* DebugInfoManager::GetDebugInfoNone() {
+  if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_;
+
+  uint32_t result_id = context()->TakeNextId();
+  std::unique_ptr<Instruction> dbg_info_none_inst(new Instruction(
+      context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+      result_id,
+      {
+          {SPV_OPERAND_TYPE_RESULT_ID,
+           {context()
+                ->get_feature_mgr()
+                ->GetExtInstImportId_OpenCL100DebugInfo()}},
+          {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+           {static_cast<uint32_t>(OpenCLDebugInfo100DebugInfoNone)}},
+      }));
+
+  // Add to the front of |ext_inst_debuginfo_|.
+  debug_info_none_inst_ =
+      context()->module()->ext_inst_debuginfo_begin()->InsertBefore(
+          std::move(dbg_info_none_inst));
+
+  RegisterDbgInst(debug_info_none_inst_);
+  return debug_info_none_inst_;
+}
+
+Instruction* DebugInfoManager::GetDebugInlinedAt(uint32_t dbg_inlined_at_id) {
+  auto* inlined_at = GetDbgInst(dbg_inlined_at_id);
+  if (inlined_at == nullptr) return nullptr;
+  if (inlined_at->GetOpenCL100DebugOpcode() !=
+      OpenCLDebugInfo100DebugInlinedAt) {
+    return nullptr;
+  }
+  return inlined_at;
+}
+
+Instruction* DebugInfoManager::CloneDebugInlinedAt(uint32_t clone_inlined_at_id,
+                                                   Instruction* insert_before) {
+  auto* inlined_at = GetDebugInlinedAt(clone_inlined_at_id);
+  if (inlined_at == nullptr) return nullptr;
+  std::unique_ptr<Instruction> new_inlined_at(inlined_at->Clone(context()));
+  new_inlined_at->SetResultId(context()->TakeNextId());
+  RegisterDbgInst(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));
+}
+
+void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
+  if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax)
+    return;
+
+  RegisterDbgInst(dbg_inst);
+
+  if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) {
+    assert(GetDebugFunction(dbg_inst->GetSingleWordOperand(
+               kDebugFunctionOperandFunctionIndex)) == nullptr &&
+           "Two DebugFunction instruction exists for a single OpFunction.");
+    RegisterDbgFunction(dbg_inst);
+  }
+
+  if (debug_info_none_inst_ == nullptr &&
+      dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
+    debug_info_none_inst_ = dbg_inst;
+  }
+}
+
+void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
+  debug_info_none_inst_ = nullptr;
+  module.ForEachInst([this](Instruction* cpi) { AnalyzeDebugInst(cpi); });
+
+  // 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_->InsertBefore(
+        &*context()->module()->ext_inst_debuginfo_begin());
+  }
+}
+
+}  // 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
new file mode 100644
index 0000000..0c7186e
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/opt/debug_info_manager.h
@@ -0,0 +1,169 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_OPT_DEBUG_INFO_MANAGER_H_
+#define SOURCE_OPT_DEBUG_INFO_MANAGER_H_
+
+#include <unordered_map>
+
+#include "source/opt/instruction.h"
+#include "source/opt/module.h"
+
+namespace spvtools {
+namespace opt {
+namespace analysis {
+
+// When an instruction of a callee function is inlined to its caller function,
+// we need the line and the scope information of the function call instruction
+// to generate DebugInlinedAt. This class keeps the data. For multiple inlining
+// of a single instruction, we have to create multiple DebugInlinedAt
+// instructions as a chain. This class keeps the information of the generated
+// DebugInlinedAt chains to reduce the number of chains.
+class DebugInlinedAtContext {
+ public:
+  explicit DebugInlinedAtContext(Instruction* call_inst)
+      : call_inst_line_(call_inst->dbg_line_inst()),
+        call_inst_scope_(call_inst->GetDebugScope()) {}
+
+  const Instruction* GetLineOfCallInstruction() { return call_inst_line_; }
+  const DebugScope& GetScopeOfCallInstruction() { return call_inst_scope_; }
+  // Puts the DebugInlinedAt chain that is generated for the callee instruction
+  // whose DebugInlinedAt of DebugScope is |callee_instr_inlined_at| into
+  // |callee_inlined_at2chain_|.
+  void SetDebugInlinedAtChain(uint32_t callee_instr_inlined_at,
+                              uint32_t chain_head_id) {
+    callee_inlined_at2chain_[callee_instr_inlined_at] = chain_head_id;
+  }
+  // Gets the DebugInlinedAt chain from |callee_inlined_at2chain_|.
+  uint32_t GetDebugInlinedAtChain(uint32_t callee_instr_inlined_at) {
+    auto chain_itr = callee_inlined_at2chain_.find(callee_instr_inlined_at);
+    if (chain_itr != callee_inlined_at2chain_.end()) return chain_itr->second;
+    return kNoInlinedAt;
+  }
+
+ private:
+  // The line information of the function call instruction that will be
+  // replaced by the callee function.
+  const Instruction* call_inst_line_;
+
+  // The scope information of the function call instruction that will be
+  // replaced by the callee function.
+  const DebugScope call_inst_scope_;
+
+  // Map from DebugInlinedAt ids of callee to head ids of new generated
+  // DebugInlinedAt chain.
+  std::unordered_map<uint32_t, uint32_t> callee_inlined_at2chain_;
+};
+
+// A class for analyzing, managing, and creating OpenCL.DebugInfo.100 extension
+// instructions.
+class DebugInfoManager {
+ public:
+  // Constructs a debug information manager from the given |context|.
+  DebugInfoManager(IRContext* context);
+
+  DebugInfoManager(const DebugInfoManager&) = delete;
+  DebugInfoManager(DebugInfoManager&&) = delete;
+  DebugInfoManager& operator=(const DebugInfoManager&) = delete;
+  DebugInfoManager& operator=(DebugInfoManager&&) = delete;
+
+  friend bool operator==(const DebugInfoManager&, const DebugInfoManager&);
+  friend bool operator!=(const DebugInfoManager& lhs,
+                         const DebugInfoManager& rhs) {
+    return !(lhs == rhs);
+  }
+
+  // Analyzes OpenCL.DebugInfo.100 instruction |dbg_inst|.
+  void AnalyzeDebugInst(Instruction* dbg_inst);
+
+  // Creates new DebugInlinedAt and returns its id. Its line operand is the
+  // line number of |line| if |line| is not nullptr. Otherwise, its line operand
+  // is the line number of lexical scope of |scope|. Its Scope and Inlined
+  // operands are Scope and Inlined of |scope|.
+  uint32_t CreateDebugInlinedAt(const Instruction* line,
+                                const DebugScope& scope);
+
+  // Returns a DebugInfoNone instruction.
+  Instruction* GetDebugInfoNone();
+
+  // Returns DebugInlinedAt whose id is |dbg_inlined_at_id|. If it does not
+  // exist or it is not a DebugInlinedAt instruction, return nullptr.
+  Instruction* GetDebugInlinedAt(uint32_t dbg_inlined_at_id);
+
+  // Returns DebugFunction whose Function operand is |fn_id|. If it does not
+  // exist, return nullptr.
+  Instruction* GetDebugFunction(uint32_t fn_id) {
+    auto dbg_fn_it = fn_id_to_dbg_fn_.find(fn_id);
+    return dbg_fn_it == fn_id_to_dbg_fn_.end() ? nullptr : dbg_fn_it->second;
+  }
+
+  // Clones DebugInlinedAt whose id is |clone_inlined_at_id|. If
+  // |clone_inlined_at_id| is not an id of DebugInlinedAt, returns nullptr.
+  // If |insert_before| is given, inserts the new DebugInlinedAt before it.
+  // Otherwise, inserts the new DebugInlinedAt into the debug instruction
+  // section of the module.
+  Instruction* CloneDebugInlinedAt(uint32_t clone_inlined_at_id,
+                                   Instruction* insert_before = nullptr);
+
+  // Returns the debug scope corresponding to an inlining instruction in the
+  // scope |callee_instr_scope| into |inlined_at_ctx|. Generates all new
+  // debug instructions needed to represent the scope.
+  DebugScope BuildDebugScope(const DebugScope& callee_instr_scope,
+                             DebugInlinedAtContext* inlined_at_ctx);
+
+  // Returns DebugInlinedAt corresponding to inlining an instruction, which
+  // was inlined at |callee_inlined_at|, into |inlined_at_ctx|. Generates all
+  // new debug instructions needed to represent the DebugInlinedAt.
+  uint32_t BuildDebugInlinedAtChain(uint32_t callee_inlined_at,
+                                    DebugInlinedAtContext* inlined_at_ctx);
+
+ private:
+  IRContext* context() { return context_; }
+
+  // Analyzes OpenCL.DebugInfo.100 instructions in the given |module| and
+  // populates data structures in this class.
+  void AnalyzeDebugInsts(Module& module);
+
+  // Returns the debug instruction whose id is |id|. Returns |nullptr| if one
+  // does not exists.
+  Instruction* GetDbgInst(uint32_t id);
+
+  // Registers the debug instruction |inst| into |id_to_dbg_inst_| using id of
+  // |inst| as a key.
+  void RegisterDbgInst(Instruction* inst);
+
+  // Register the DebugFunction instruction |inst|. The function referenced
+  // in |inst| must not already be registered.
+  void RegisterDbgFunction(Instruction* inst);
+
+  IRContext* context_;
+
+  // Mapping from ids of OpenCL.DebugInfo.100 extension instructions
+  // to their Instruction instances.
+  std::unordered_map<uint32_t, Instruction*> id_to_dbg_inst_;
+
+  // Mapping from function's ids to DebugFunction instructions whose
+  // operand is the function.
+  std::unordered_map<uint32_t, Instruction*> fn_id_to_dbg_fn_;
+
+  // 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_;
+};
+
+}  // namespace analysis
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // SOURCE_OPT_DEBUG_INFO_MANAGER_H_
diff --git a/third_party/SPIRV-Tools/source/opt/dominator_tree.cpp b/third_party/SPIRV-Tools/source/opt/dominator_tree.cpp
index c9346e1..da5073a 100644
--- a/third_party/SPIRV-Tools/source/opt/dominator_tree.cpp
+++ b/third_party/SPIRV-Tools/source/opt/dominator_tree.cpp
@@ -241,6 +241,7 @@
 
 bool DominatorTree::Dominates(const DominatorTreeNode* a,
                               const DominatorTreeNode* b) const {
+  if (!a || !b) return false;
   // Node A dominates node B if they are the same.
   if (a == b) return true;
 
diff --git a/third_party/SPIRV-Tools/source/opt/eliminate_dead_members_pass.cpp b/third_party/SPIRV-Tools/source/opt/eliminate_dead_members_pass.cpp
index 0b73b2d..5b8f4ec 100644
--- a/third_party/SPIRV-Tools/source/opt/eliminate_dead_members_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/eliminate_dead_members_pass.cpp
@@ -19,6 +19,7 @@
 
 namespace {
 const uint32_t kRemovedMember = 0xFFFFFFFF;
+const uint32_t kSpecConstOpOpcodeIdx = 0;
 }
 
 namespace spvtools {
@@ -40,7 +41,22 @@
   // we have to mark them as fully used just to be safe.
   for (auto& inst : get_module()->types_values()) {
     if (inst.opcode() == SpvOpSpecConstantOp) {
-      MarkTypeAsFullyUsed(inst.type_id());
+      switch (inst.GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) {
+        case SpvOpCompositeExtract:
+          MarkMembersAsLiveForExtract(&inst);
+          break;
+        case SpvOpCompositeInsert:
+          // Nothing specific to do.
+          break;
+        case SpvOpAccessChain:
+        case SpvOpInBoundsAccessChain:
+        case SpvOpPtrAccessChain:
+        case SpvOpInBoundsPtrAccessChain:
+          assert(false && "Not implemented yet.");
+          break;
+        default:
+          break;
+      }
     } else if (inst.opcode() == SpvOpVariable) {
       switch (inst.GetSingleWordInOperand(0)) {
         case SpvStorageClassInput:
@@ -153,13 +169,17 @@
 
 void EliminateDeadMembersPass::MarkMembersAsLiveForExtract(
     const Instruction* inst) {
-  assert(inst->opcode() == SpvOpCompositeExtract);
+  assert(inst->opcode() == SpvOpCompositeExtract ||
+         (inst->opcode() == SpvOpSpecConstantOp &&
+          inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) ==
+              SpvOpCompositeExtract));
 
-  uint32_t composite_id = inst->GetSingleWordInOperand(0);
+  uint32_t first_operand = (inst->opcode() == SpvOpSpecConstantOp ? 1 : 0);
+  uint32_t composite_id = inst->GetSingleWordInOperand(first_operand);
   Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id);
   uint32_t type_id = composite_inst->type_id();
 
-  for (uint32_t i = 1; i < inst->NumInOperands(); ++i) {
+  for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) {
     Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
     uint32_t member_idx = inst->GetSingleWordInOperand(i);
     switch (type_inst->opcode()) {
@@ -295,10 +315,22 @@
         modified |= UpdateOpArrayLength(inst);
         break;
       case SpvOpSpecConstantOp:
-        assert(false && "Not yet implemented.");
-        // with OpCompositeExtract, OpCompositeInsert
-        // For kernels: OpAccessChain, OpInBoundsAccessChain, OpPtrAccessChain,
-        // OpInBoundsPtrAccessChain
+        switch (inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) {
+          case SpvOpCompositeExtract:
+            modified |= UpdateCompsiteExtract(inst);
+            break;
+          case SpvOpCompositeInsert:
+            modified |= UpdateCompositeInsert(inst);
+            break;
+          case SpvOpAccessChain:
+          case SpvOpInBoundsAccessChain:
+          case SpvOpPtrAccessChain:
+          case SpvOpInBoundsPtrAccessChain:
+            assert(false && "Not implemented yet.");
+            break;
+          default:
+            break;
+        }
         break;
       default:
         break;
@@ -393,7 +425,8 @@
 }
 
 bool EliminateDeadMembersPass::UpdateConstantComposite(Instruction* inst) {
-  assert(inst->opcode() == SpvOpConstantComposite ||
+  assert(inst->opcode() == SpvOpSpecConstantComposite ||
+         inst->opcode() == SpvOpConstantComposite ||
          inst->opcode() == SpvOpCompositeConstruct);
   uint32_t type_id = inst->type_id();
 
@@ -506,14 +539,25 @@
 }
 
 bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) {
-  uint32_t object_id = inst->GetSingleWordInOperand(0);
+  assert(inst->opcode() == SpvOpCompositeExtract ||
+         (inst->opcode() == SpvOpSpecConstantOp &&
+          inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) ==
+              SpvOpCompositeExtract));
+
+  uint32_t first_operand = 0;
+  if (inst->opcode() == SpvOpSpecConstantOp) {
+    first_operand = 1;
+  }
+  uint32_t object_id = inst->GetSingleWordInOperand(first_operand);
   Instruction* object_inst = get_def_use_mgr()->GetDef(object_id);
   uint32_t type_id = object_inst->type_id();
 
   Instruction::OperandList new_operands;
   bool modified = false;
-  new_operands.emplace_back(inst->GetInOperand(0));
-  for (uint32_t i = 1; i < inst->NumInOperands(); ++i) {
+  for (uint32_t i = 0; i < first_operand + 1; i++) {
+    new_operands.emplace_back(inst->GetInOperand(i));
+  }
+  for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) {
     uint32_t member_idx = inst->GetSingleWordInOperand(i);
     uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
     assert(new_member_idx != kRemovedMember);
@@ -526,8 +570,6 @@
     Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
     switch (type_inst->opcode()) {
       case SpvOpTypeStruct:
-        assert(i != 1 || (inst->opcode() != SpvOpPtrAccessChain &&
-                          inst->opcode() != SpvOpInBoundsPtrAccessChain));
         // The type will have already been rewriten, so use the new member
         // index.
         type_id = type_inst->GetSingleWordInOperand(new_member_idx);
@@ -552,15 +594,27 @@
 }
 
 bool EliminateDeadMembersPass::UpdateCompositeInsert(Instruction* inst) {
-  uint32_t composite_id = inst->GetSingleWordInOperand(1);
+  assert(inst->opcode() == SpvOpCompositeInsert ||
+         (inst->opcode() == SpvOpSpecConstantOp &&
+          inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) ==
+              SpvOpCompositeInsert));
+
+  uint32_t first_operand = 0;
+  if (inst->opcode() == SpvOpSpecConstantOp) {
+    first_operand = 1;
+  }
+
+  uint32_t composite_id = inst->GetSingleWordInOperand(first_operand + 1);
   Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id);
   uint32_t type_id = composite_inst->type_id();
 
   Instruction::OperandList new_operands;
   bool modified = false;
-  new_operands.emplace_back(inst->GetInOperand(0));
-  new_operands.emplace_back(inst->GetInOperand(1));
-  for (uint32_t i = 2; i < inst->NumInOperands(); ++i) {
+
+  for (uint32_t i = 0; i < first_operand + 2; ++i) {
+    new_operands.emplace_back(inst->GetInOperand(i));
+  }
+  for (uint32_t i = first_operand + 2; i < inst->NumInOperands(); ++i) {
     uint32_t member_idx = inst->GetSingleWordInOperand(i);
     uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
     if (new_member_idx == kRemovedMember) {
diff --git a/third_party/SPIRV-Tools/source/opt/feature_manager.cpp b/third_party/SPIRV-Tools/source/opt/feature_manager.cpp
index b4d6f1b..ad70c1e 100644
--- a/third_party/SPIRV-Tools/source/opt/feature_manager.cpp
+++ b/third_party/SPIRV-Tools/source/opt/feature_manager.cpp
@@ -78,6 +78,8 @@
 
 void FeatureManager::AddExtInstImportIds(Module* module) {
   extinst_importid_GLSLstd450_ = module->GetExtInstImportId("GLSL.std.450");
+  extinst_importid_OpenCL100DebugInfo_ =
+      module->GetExtInstImportId("OpenCL.DebugInfo.100");
 }
 
 bool operator==(const FeatureManager& a, const FeatureManager& b) {
@@ -100,6 +102,11 @@
     return false;
   }
 
+  if (a.extinst_importid_OpenCL100DebugInfo_ !=
+      b.extinst_importid_OpenCL100DebugInfo_) {
+    return false;
+  }
+
   return true;
 }
 }  // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/feature_manager.h b/third_party/SPIRV-Tools/source/opt/feature_manager.h
index 881d5e6..66d1cba 100644
--- a/third_party/SPIRV-Tools/source/opt/feature_manager.h
+++ b/third_party/SPIRV-Tools/source/opt/feature_manager.h
@@ -51,6 +51,10 @@
     return extinst_importid_GLSLstd450_;
   }
 
+  uint32_t GetExtInstImportId_OpenCL100DebugInfo() const {
+    return extinst_importid_OpenCL100DebugInfo_;
+  }
+
   friend bool operator==(const FeatureManager& a, const FeatureManager& b);
   friend bool operator!=(const FeatureManager& a, const FeatureManager& b) {
     return !(a == b);
@@ -84,6 +88,10 @@
 
   // Common external instruction import ids, cached for performance.
   uint32_t extinst_importid_GLSLstd450_ = 0;
+
+  // Common OpenCL100DebugInfo external instruction import ids, cached
+  // for performance.
+  uint32_t extinst_importid_OpenCL100DebugInfo_ = 0;
 };
 
 }  // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/function.cpp b/third_party/SPIRV-Tools/source/opt/function.cpp
index 5d50f37..320f8ca 100644
--- a/third_party/SPIRV-Tools/source/opt/function.cpp
+++ b/third_party/SPIRV-Tools/source/opt/function.cpp
@@ -84,9 +84,12 @@
     }
   }
 
-  for (auto& di : debug_insts_in_header_) {
-    if (!di.WhileEachInst(f, run_on_debug_line_insts)) {
-      return false;
+  if (!debug_insts_in_header_.empty()) {
+    Instruction* di = &debug_insts_in_header_.front();
+    while (di != nullptr) {
+      Instruction* next_instruction = di->NextNode();
+      if (!di->WhileEachInst(f, run_on_debug_line_insts)) return false;
+      di = next_instruction;
     }
   }
 
@@ -118,9 +121,9 @@
   }
 
   for (const auto& di : debug_insts_in_header_) {
-    if (!di.WhileEachInst(f, run_on_debug_line_insts)) {
+    if (!static_cast<const Instruction*>(&di)->WhileEachInst(
+            f, run_on_debug_line_insts))
       return false;
-    }
   }
 
   for (const auto& bb : blocks_) {
@@ -151,6 +154,18 @@
         ->ForEachInst(f, run_on_debug_line_insts);
 }
 
+void Function::ForEachDebugInstructionsInHeader(
+    const std::function<void(Instruction*)>& f) {
+  if (debug_insts_in_header_.empty()) return;
+
+  Instruction* di = &debug_insts_in_header_.front();
+  while (di != nullptr) {
+    Instruction* next_instruction = di->NextNode();
+    di->ForEachInst(f);
+    di = next_instruction;
+  }
+}
+
 BasicBlock* Function::InsertBasicBlockAfter(
     std::unique_ptr<BasicBlock>&& new_block, BasicBlock* position) {
   for (auto bb_iter = begin(); bb_iter != end(); ++bb_iter) {
diff --git a/third_party/SPIRV-Tools/source/opt/function.h b/third_party/SPIRV-Tools/source/opt/function.h
index f208d8e..d569bf9 100644
--- a/third_party/SPIRV-Tools/source/opt/function.h
+++ b/third_party/SPIRV-Tools/source/opt/function.h
@@ -88,6 +88,10 @@
   // Returns the entry basic block for this function.
   const std::unique_ptr<BasicBlock>& entry() const { return blocks_.front(); }
 
+  // Returns the last basic block in this function.
+  BasicBlock* tail() { return blocks_.back().get(); }
+  const BasicBlock* tail() const { return blocks_.back().get(); }
+
   iterator begin() { return iterator(&blocks_, blocks_.begin()); }
   iterator end() { return iterator(&blocks_, blocks_.end()); }
   const_iterator begin() const { return cbegin(); }
@@ -129,6 +133,11 @@
   void ForEachParam(const std::function<void(Instruction*)>& f,
                     bool run_on_debug_line_insts = false);
 
+  // Runs the given function |f| on each debug instruction in this function's
+  // header in order.
+  void ForEachDebugInstructionsInHeader(
+      const std::function<void(Instruction*)>& f);
+
   BasicBlock* InsertBasicBlockAfter(std::unique_ptr<BasicBlock>&& new_block,
                                     BasicBlock* position);
 
@@ -192,13 +201,13 @@
 }
 
 inline void Function::MoveBasicBlockToAfter(uint32_t id, BasicBlock* ip) {
-  auto block_to_move = std::move(*FindBlock(id).Get());
+  std::unique_ptr<BasicBlock> block_to_move = std::move(*FindBlock(id).Get());
+  blocks_.erase(std::find(std::begin(blocks_), std::end(blocks_), nullptr));
 
   assert(block_to_move->GetParent() == ip->GetParent() &&
          "Both blocks have to be in the same function.");
 
   InsertBasicBlockAfter(std::move(block_to_move), ip);
-  blocks_.erase(std::find(std::begin(blocks_), std::end(blocks_), nullptr));
 }
 
 inline void Function::RemoveEmptyBlocks() {
diff --git a/third_party/SPIRV-Tools/source/opt/graphics_robust_access_pass.cpp b/third_party/SPIRV-Tools/source/opt/graphics_robust_access_pass.cpp
index 22c979c..db14020 100644
--- a/third_party/SPIRV-Tools/source/opt/graphics_robust_access_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/graphics_robust_access_pass.cpp
@@ -802,8 +802,11 @@
     opt::Instruction* image_texel_pointer) {
   // TODO(dneto): Write tests for this code.
   // TODO(dneto): Use signed-clamp
+  (void)(image_texel_pointer);
   return SPV_SUCCESS;
 
+  // Do not compile this code until it is ready to be used.
+#if 0
   // Example:
   //   %texel_ptr = OpImageTexelPointer %texel_ptr_type %image_ptr %coord
   //   %sample
@@ -1035,6 +1038,7 @@
   def_use_mgr->AnalyzeInstUse(image_texel_pointer);
 
   return SPV_SUCCESS;
+#endif
 }
 
 opt::Instruction* GraphicsRobustAccessPass::InsertInst(
diff --git a/third_party/SPIRV-Tools/source/opt/inline_pass.cpp b/third_party/SPIRV-Tools/source/opt/inline_pass.cpp
index 3c874a7..cb5a126 100644
--- a/third_party/SPIRV-Tools/source/opt/inline_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/inline_pass.cpp
@@ -20,6 +20,7 @@
 #include <utility>
 
 #include "source/cfa.h"
+#include "source/opt/reflect.h"
 #include "source/util/make_unique.h"
 
 // Indices of operands in SPIR-V instructions
@@ -83,19 +84,31 @@
 }
 
 void InlinePass::AddStore(uint32_t ptr_id, uint32_t val_id,
-                          std::unique_ptr<BasicBlock>* block_ptr) {
+                          std::unique_ptr<BasicBlock>* block_ptr,
+                          const Instruction* line_inst,
+                          const DebugScope& dbg_scope) {
   std::unique_ptr<Instruction> newStore(
       new Instruction(context(), SpvOpStore, 0, 0,
                       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}},
                        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {val_id}}}));
+  if (line_inst != nullptr) {
+    newStore->dbg_line_insts().push_back(*line_inst);
+  }
+  newStore->SetDebugScope(dbg_scope);
   (*block_ptr)->AddInstruction(std::move(newStore));
 }
 
 void InlinePass::AddLoad(uint32_t type_id, uint32_t resultId, uint32_t ptr_id,
-                         std::unique_ptr<BasicBlock>* block_ptr) {
+                         std::unique_ptr<BasicBlock>* block_ptr,
+                         const Instruction* line_inst,
+                         const DebugScope& dbg_scope) {
   std::unique_ptr<Instruction> newLoad(
       new Instruction(context(), SpvOpLoad, type_id, resultId,
                       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}}));
+  if (line_inst != nullptr) {
+    newLoad->dbg_line_insts().push_back(*line_inst);
+  }
+  newLoad->SetDebugScope(dbg_scope);
   (*block_ptr)->AddInstruction(std::move(newLoad));
 }
 
@@ -140,10 +153,18 @@
 
 bool InlinePass::CloneAndMapLocals(
     Function* calleeFn, std::vector<std::unique_ptr<Instruction>>* new_vars,
-    std::unordered_map<uint32_t, uint32_t>* callee2caller) {
+    std::unordered_map<uint32_t, uint32_t>* callee2caller,
+    analysis::DebugInlinedAtContext* inlined_at_ctx) {
   auto callee_block_itr = calleeFn->begin();
   auto callee_var_itr = callee_block_itr->begin();
-  while (callee_var_itr->opcode() == SpvOp::SpvOpVariable) {
+  while (callee_var_itr->opcode() == SpvOp::SpvOpVariable ||
+         callee_var_itr->GetOpenCL100DebugOpcode() ==
+             OpenCLDebugInfo100DebugDeclare) {
+    if (callee_var_itr->opcode() != SpvOp::SpvOpVariable) {
+      ++callee_var_itr;
+      continue;
+    }
+
     std::unique_ptr<Instruction> var_inst(callee_var_itr->Clone(context()));
     uint32_t newId = context()->TakeNextId();
     if (newId == 0) {
@@ -151,6 +172,9 @@
     }
     get_decoration_mgr()->CloneDecorations(callee_var_itr->result_id(), newId);
     var_inst->SetResultId(newId);
+    var_inst->UpdateDebugInlinedAt(
+        context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
+            callee_var_itr->GetDebugInlinedAt(), inlined_at_ctx));
     (*callee2caller)[callee_var_itr->result_id()] = newId;
     new_vars->push_back(std::move(var_inst));
     ++callee_var_itr;
@@ -232,6 +256,248 @@
   });
 }
 
+void InlinePass::MoveInstsBeforeEntryBlock(
+    std::unordered_map<uint32_t, Instruction*>* preCallSB,
+    BasicBlock* new_blk_ptr, BasicBlock::iterator call_inst_itr,
+    UptrVectorIterator<BasicBlock> call_block_itr) {
+  for (auto cii = call_block_itr->begin(); cii != call_inst_itr;
+       cii = call_block_itr->begin()) {
+    Instruction* inst = &*cii;
+    inst->RemoveFromList();
+    std::unique_ptr<Instruction> cp_inst(inst);
+    // Remember same-block ops for possible regeneration.
+    if (IsSameBlockOp(&*cp_inst)) {
+      auto* sb_inst_ptr = cp_inst.get();
+      (*preCallSB)[cp_inst->result_id()] = sb_inst_ptr;
+    }
+    new_blk_ptr->AddInstruction(std::move(cp_inst));
+  }
+}
+
+std::unique_ptr<BasicBlock> InlinePass::AddGuardBlock(
+    std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
+    std::unordered_map<uint32_t, uint32_t>* callee2caller,
+    std::unique_ptr<BasicBlock> new_blk_ptr, uint32_t entry_blk_label_id) {
+  const auto guard_block_id = context()->TakeNextId();
+  if (guard_block_id == 0) {
+    return nullptr;
+  }
+  AddBranch(guard_block_id, &new_blk_ptr);
+  new_blocks->push_back(std::move(new_blk_ptr));
+  // Start the next block.
+  new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(guard_block_id));
+  // Reset the mapping of the callee's entry block to point to
+  // the guard block.  Do this so we can fix up phis later on to
+  // satisfy dominance.
+  (*callee2caller)[entry_blk_label_id] = guard_block_id;
+  return new_blk_ptr;
+}
+
+InstructionList::iterator InlinePass::AddStoresForVariableInitializers(
+    const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+    analysis::DebugInlinedAtContext* inlined_at_ctx,
+    std::unique_ptr<BasicBlock>* new_blk_ptr,
+    UptrVectorIterator<BasicBlock> callee_first_block_itr) {
+  auto callee_itr = callee_first_block_itr->begin();
+  while (callee_itr->opcode() == SpvOp::SpvOpVariable ||
+         callee_itr->GetOpenCL100DebugOpcode() ==
+             OpenCLDebugInfo100DebugDeclare) {
+    if (callee_itr->opcode() == SpvOp::SpvOpVariable &&
+        callee_itr->NumInOperands() == 2) {
+      assert(callee2caller.count(callee_itr->result_id()) &&
+             "Expected the variable to have already been mapped.");
+      uint32_t new_var_id = callee2caller.at(callee_itr->result_id());
+
+      // The initializer must be a constant or global value.  No mapped
+      // should be used.
+      uint32_t val_id = callee_itr->GetSingleWordInOperand(1);
+      AddStore(new_var_id, val_id, new_blk_ptr, callee_itr->dbg_line_inst(),
+               context()->get_debug_info_mgr()->BuildDebugScope(
+                   callee_itr->GetDebugScope(), inlined_at_ctx));
+    }
+    if (callee_itr->GetOpenCL100DebugOpcode() ==
+        OpenCLDebugInfo100DebugDeclare) {
+      InlineSingleInstruction(
+          callee2caller, new_blk_ptr->get(), &*callee_itr,
+          context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
+              callee_itr->GetDebugScope().GetInlinedAt(), inlined_at_ctx));
+    }
+    ++callee_itr;
+  }
+  return callee_itr;
+}
+
+bool InlinePass::InlineSingleInstruction(
+    const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+    BasicBlock* new_blk_ptr, const Instruction* inst, uint32_t dbg_inlined_at) {
+  // If we have return, it must be at the end of the callee. We will handle
+  // it at the end.
+  if (inst->opcode() == SpvOpReturnValue || inst->opcode() == SpvOpReturn)
+    return true;
+
+  // Copy callee instruction and remap all input Ids.
+  std::unique_ptr<Instruction> cp_inst(inst->Clone(context()));
+  cp_inst->ForEachInId([&callee2caller](uint32_t* iid) {
+    const auto mapItr = callee2caller.find(*iid);
+    if (mapItr != callee2caller.end()) {
+      *iid = mapItr->second;
+    }
+  });
+
+  // If result id is non-zero, remap it.
+  const uint32_t rid = cp_inst->result_id();
+  if (rid != 0) {
+    const auto mapItr = callee2caller.find(rid);
+    if (mapItr == callee2caller.end()) {
+      return false;
+    }
+    uint32_t nid = mapItr->second;
+    cp_inst->SetResultId(nid);
+    get_decoration_mgr()->CloneDecorations(rid, nid);
+  }
+
+  cp_inst->UpdateDebugInlinedAt(dbg_inlined_at);
+  new_blk_ptr->AddInstruction(std::move(cp_inst));
+  return true;
+}
+
+std::unique_ptr<BasicBlock> InlinePass::InlineReturn(
+    const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+    std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
+    std::unique_ptr<BasicBlock> new_blk_ptr,
+    analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn,
+    const Instruction* inst, uint32_t returnVarId) {
+  // Store return value to return variable.
+  if (inst->opcode() == SpvOpReturnValue) {
+    assert(returnVarId != 0);
+    uint32_t valId = inst->GetInOperand(kSpvReturnValueId).words[0];
+    const auto mapItr = callee2caller.find(valId);
+    if (mapItr != callee2caller.end()) {
+      valId = mapItr->second;
+    }
+    AddStore(returnVarId, valId, &new_blk_ptr, inst->dbg_line_inst(),
+             context()->get_debug_info_mgr()->BuildDebugScope(
+                 inst->GetDebugScope(), inlined_at_ctx));
+  }
+
+  uint32_t returnLabelId = 0;
+  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) {
+      returnLabelId = context()->TakeNextId();
+      break;
+    }
+  }
+  if (returnLabelId == 0) return new_blk_ptr;
+
+  if (inst->opcode() == SpvOpReturn || inst->opcode() == SpvOpReturnValue)
+    AddBranch(returnLabelId, &new_blk_ptr);
+  new_blocks->push_back(std::move(new_blk_ptr));
+  return MakeUnique<BasicBlock>(NewLabel(returnLabelId));
+}
+
+bool InlinePass::InlineEntryBlock(
+    const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+    std::unique_ptr<BasicBlock>* new_blk_ptr,
+    UptrVectorIterator<BasicBlock> callee_first_block,
+    analysis::DebugInlinedAtContext* inlined_at_ctx) {
+  auto callee_inst_itr = AddStoresForVariableInitializers(
+      callee2caller, inlined_at_ctx, new_blk_ptr, callee_first_block);
+
+  while (callee_inst_itr != callee_first_block->end()) {
+    if (!InlineSingleInstruction(
+            callee2caller, new_blk_ptr->get(), &*callee_inst_itr,
+            context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
+                callee_inst_itr->GetDebugScope().GetInlinedAt(),
+                inlined_at_ctx))) {
+      return false;
+    }
+    ++callee_inst_itr;
+  }
+  return true;
+}
+
+std::unique_ptr<BasicBlock> InlinePass::InlineBasicBlocks(
+    std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
+    const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+    std::unique_ptr<BasicBlock> new_blk_ptr,
+    analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn) {
+  auto callee_block_itr = calleeFn->begin();
+  ++callee_block_itr;
+
+  while (callee_block_itr != calleeFn->end()) {
+    new_blocks->push_back(std::move(new_blk_ptr));
+    const auto mapItr =
+        callee2caller.find(callee_block_itr->GetLabelInst()->result_id());
+    if (mapItr == callee2caller.end()) return nullptr;
+    new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(mapItr->second));
+
+    auto tail_inst_itr = callee_block_itr->end();
+    for (auto inst_itr = callee_block_itr->begin(); inst_itr != tail_inst_itr;
+         ++inst_itr) {
+      if (!InlineSingleInstruction(
+              callee2caller, new_blk_ptr.get(), &*inst_itr,
+              context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
+                  inst_itr->GetDebugScope().GetInlinedAt(), inlined_at_ctx))) {
+        return nullptr;
+      }
+    }
+
+    ++callee_block_itr;
+  }
+  return new_blk_ptr;
+}
+
+bool InlinePass::MoveCallerInstsAfterFunctionCall(
+    std::unordered_map<uint32_t, Instruction*>* preCallSB,
+    std::unordered_map<uint32_t, uint32_t>* postCallSB,
+    std::unique_ptr<BasicBlock>* new_blk_ptr,
+    BasicBlock::iterator call_inst_itr, bool multiBlocks) {
+  // Copy remaining instructions from caller block.
+  for (Instruction* inst = call_inst_itr->NextNode(); inst;
+       inst = call_inst_itr->NextNode()) {
+    inst->RemoveFromList();
+    std::unique_ptr<Instruction> cp_inst(inst);
+    // If multiple blocks generated, regenerate any same-block
+    // instruction that has not been seen in this last block.
+    if (multiBlocks) {
+      if (!CloneSameBlockOps(&cp_inst, postCallSB, preCallSB, new_blk_ptr)) {
+        return false;
+      }
+
+      // Remember same-block ops in this block.
+      if (IsSameBlockOp(&*cp_inst)) {
+        const uint32_t rid = cp_inst->result_id();
+        (*postCallSB)[rid] = rid;
+      }
+    }
+    new_blk_ptr->get()->AddInstruction(std::move(cp_inst));
+  }
+
+  return true;
+}
+
+void InlinePass::MoveLoopMergeInstToFirstBlock(
+    std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+  // Move the OpLoopMerge from the last block back to the first, where
+  // it belongs.
+  auto& first = new_blocks->front();
+  auto& last = new_blocks->back();
+  assert(first != last);
+
+  // Insert a modified copy of the loop merge into the first block.
+  auto loop_merge_itr = last->tail();
+  --loop_merge_itr;
+  assert(loop_merge_itr->opcode() == SpvOpLoopMerge);
+  std::unique_ptr<Instruction> cp_inst(loop_merge_itr->Clone(context()));
+  first->tail().InsertBefore(std::move(cp_inst));
+
+  // Remove the loop merge from the last block.
+  loop_merge_itr->RemoveFromList();
+  delete &*loop_merge_itr;
+}
+
 bool InlinePass::GenInlineCode(
     std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
     std::vector<std::unique_ptr<Instruction>>* new_vars,
@@ -245,27 +511,60 @@
   // Post-call same-block op ids
   std::unordered_map<uint32_t, uint32_t> postCallSB;
 
+  analysis::DebugInlinedAtContext inlined_at_ctx(&*call_inst_itr);
+
   // Invalidate the def-use chains.  They are not kept up to date while
   // inlining.  However, certain calls try to keep them up-to-date if they are
   // valid.  These operations can fail.
   context()->InvalidateAnalyses(IRContext::kAnalysisDefUse);
 
+  // If the caller is a loop header and the callee has multiple blocks, then the
+  // normal inlining logic will place the OpLoopMerge in the last of several
+  // blocks in the loop.  Instead, it should be placed at the end of the first
+  // block.  We'll wait to move the OpLoopMerge until the end of the regular
+  // inlining logic, and only if necessary.
+  bool caller_is_loop_header = call_block_itr->GetLoopMergeInst() != nullptr;
+
+  // Single-trip loop continue block
+  std::unique_ptr<BasicBlock> single_trip_loop_cont_blk;
+
   Function* calleeFn = id2function_[call_inst_itr->GetSingleWordOperand(
       kSpvFunctionCallFunctionId)];
 
-  // Check for multiple returns in the callee.
-  auto fi = early_return_funcs_.find(calleeFn->result_id());
-  const bool earlyReturn = fi != early_return_funcs_.end();
-
   // Map parameters to actual arguments.
   MapParams(calleeFn, call_inst_itr, &callee2caller);
 
   // Define caller local variables for all callee variables and create map to
   // them.
-  if (!CloneAndMapLocals(calleeFn, new_vars, &callee2caller)) {
+  if (!CloneAndMapLocals(calleeFn, new_vars, &callee2caller, &inlined_at_ctx)) {
     return false;
   }
 
+  // First block needs to use label of original block
+  // but map callee label in case of phi reference.
+  uint32_t entry_blk_label_id = calleeFn->begin()->GetLabelInst()->result_id();
+  callee2caller[entry_blk_label_id] = call_block_itr->id();
+  std::unique_ptr<BasicBlock> new_blk_ptr =
+      MakeUnique<BasicBlock>(NewLabel(call_block_itr->id()));
+
+  // Move instructions of original caller block up to call instruction.
+  MoveInstsBeforeEntryBlock(&preCallSB, new_blk_ptr.get(), call_inst_itr,
+                            call_block_itr);
+
+  if (caller_is_loop_header &&
+      (*(calleeFn->begin())).GetMergeInst() != nullptr) {
+    // We can't place both the caller's merge instruction and
+    // another merge instruction in the same block.  So split the
+    // calling block. Insert an unconditional branch to a new guard
+    // block.  Later, once we know the ID of the last block,  we
+    // will move the caller's OpLoopMerge from the last generated
+    // block into the first block. We also wait to avoid
+    // invalidating various iterators.
+    new_blk_ptr = AddGuardBlock(new_blocks, &callee2caller,
+                                std::move(new_blk_ptr), entry_blk_label_id);
+    if (new_blk_ptr == nullptr) return false;
+  }
+
   // Create return var if needed.
   const uint32_t calleeTypeId = calleeFn->type_id();
   uint32_t returnVarId = 0;
@@ -277,341 +576,62 @@
     }
   }
 
-  // Create set of callee result ids. Used to detect forward references
-  std::unordered_set<uint32_t> callee_result_ids;
-  calleeFn->ForEachInst([&callee_result_ids](const Instruction* cpi) {
+  calleeFn->WhileEachInst([&callee2caller, this](const Instruction* cpi) {
+    // Create set of callee result ids. Used to detect forward references
     const uint32_t rid = cpi->result_id();
-    if (rid != 0) callee_result_ids.insert(rid);
+    if (rid != 0 && callee2caller.find(rid) == callee2caller.end()) {
+      const uint32_t nid = context()->TakeNextId();
+      if (nid == 0) return false;
+      callee2caller[rid] = nid;
+    }
+    return true;
   });
 
-  // If the caller is a loop header and the callee has multiple blocks, then the
-  // normal inlining logic will place the OpLoopMerge in the last of several
-  // blocks in the loop.  Instead, it should be placed at the end of the first
-  // block.  We'll wait to move the OpLoopMerge until the end of the regular
-  // inlining logic, and only if necessary.
-  bool caller_is_loop_header = false;
-  if (call_block_itr->GetLoopMergeInst()) {
-    caller_is_loop_header = true;
-  }
-
-  bool callee_begins_with_structured_header =
-      (*(calleeFn->begin())).GetMergeInst() != nullptr;
-
-  // Clone and map callee code. Copy caller block code to beginning of
-  // first block and end of last block.
-  bool prevInstWasReturn = false;
-  uint32_t singleTripLoopHeaderId = 0;
-  uint32_t singleTripLoopContinueId = 0;
-  uint32_t returnLabelId = 0;
-  bool multiBlocks = false;
-  // new_blk_ptr is a new basic block in the caller.  New instructions are
-  // written to it.  It is created when we encounter the OpLabel
-  // of the first callee block.  It is appended to new_blocks only when
-  // it is complete.
-  std::unique_ptr<BasicBlock> new_blk_ptr;
-  bool successful = calleeFn->WhileEachInst(
-      [&new_blocks, &callee2caller, &call_block_itr, &call_inst_itr,
-       &new_blk_ptr, &prevInstWasReturn, &returnLabelId, &returnVarId,
-       caller_is_loop_header, callee_begins_with_structured_header,
-       &calleeTypeId, &multiBlocks, &postCallSB, &preCallSB, earlyReturn,
-       &singleTripLoopHeaderId, &singleTripLoopContinueId, &callee_result_ids,
-       this](const Instruction* cpi) {
-        switch (cpi->opcode()) {
-          case SpvOpFunction:
-          case SpvOpFunctionParameter:
-            // Already processed
-            break;
-          case SpvOpVariable:
-            if (cpi->NumInOperands() == 2) {
-              assert(callee2caller.count(cpi->result_id()) &&
-                     "Expected the variable to have already been mapped.");
-              uint32_t new_var_id = callee2caller.at(cpi->result_id());
-
-              // The initializer must be a constant or global value.  No mapped
-              // should be used.
-              uint32_t val_id = cpi->GetSingleWordInOperand(1);
-              AddStore(new_var_id, val_id, &new_blk_ptr);
-            }
-            break;
-          case SpvOpUnreachable:
-          case SpvOpKill: {
-            // Generate a return label so that we split the block with the
-            // function call. Copy the terminator into the new block.
-            if (returnLabelId == 0) {
-              returnLabelId = context()->TakeNextId();
-              if (returnLabelId == 0) {
-                return false;
-              }
-            }
-            std::unique_ptr<Instruction> terminator(
-                new Instruction(context(), cpi->opcode(), 0, 0, {}));
-            new_blk_ptr->AddInstruction(std::move(terminator));
-            break;
-          }
-          case SpvOpLabel: {
-            // If previous instruction was early return, insert branch
-            // instruction to return block.
-            if (prevInstWasReturn) {
-              if (returnLabelId == 0) {
-                returnLabelId = context()->TakeNextId();
-                if (returnLabelId == 0) {
-                  return false;
-                }
-              }
-              AddBranch(returnLabelId, &new_blk_ptr);
-              prevInstWasReturn = false;
-            }
-            // Finish current block (if it exists) and get label for next block.
-            uint32_t labelId;
-            bool firstBlock = false;
-            if (new_blk_ptr != nullptr) {
-              new_blocks->push_back(std::move(new_blk_ptr));
-              // If result id is already mapped, use it, otherwise get a new
-              // one.
-              const uint32_t rid = cpi->result_id();
-              const auto mapItr = callee2caller.find(rid);
-              labelId = (mapItr != callee2caller.end())
-                            ? mapItr->second
-                            : context()->TakeNextId();
-              if (labelId == 0) {
-                return false;
-              }
-            } else {
-              // First block needs to use label of original block
-              // but map callee label in case of phi reference.
-              labelId = call_block_itr->id();
-              callee2caller[cpi->result_id()] = labelId;
-              firstBlock = true;
-            }
-            // Create first/next block.
-            new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(labelId));
-            if (firstBlock) {
-              // Copy contents of original caller block up to call instruction.
-              for (auto cii = call_block_itr->begin(); cii != call_inst_itr;
-                   cii = call_block_itr->begin()) {
-                Instruction* inst = &*cii;
-                inst->RemoveFromList();
-                std::unique_ptr<Instruction> cp_inst(inst);
-                // Remember same-block ops for possible regeneration.
-                if (IsSameBlockOp(&*cp_inst)) {
-                  auto* sb_inst_ptr = cp_inst.get();
-                  preCallSB[cp_inst->result_id()] = sb_inst_ptr;
-                }
-                new_blk_ptr->AddInstruction(std::move(cp_inst));
-              }
-              if (caller_is_loop_header &&
-                  callee_begins_with_structured_header) {
-                // We can't place both the caller's merge instruction and
-                // another merge instruction in the same block.  So split the
-                // calling block. Insert an unconditional branch to a new guard
-                // block.  Later, once we know the ID of the last block,  we
-                // will move the caller's OpLoopMerge from the last generated
-                // block into the first block. We also wait to avoid
-                // invalidating various iterators.
-                const auto guard_block_id = context()->TakeNextId();
-                if (guard_block_id == 0) {
-                  return false;
-                }
-                AddBranch(guard_block_id, &new_blk_ptr);
-                new_blocks->push_back(std::move(new_blk_ptr));
-                // Start the next block.
-                new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(guard_block_id));
-                // Reset the mapping of the callee's entry block to point to
-                // the guard block.  Do this so we can fix up phis later on to
-                // satisfy dominance.
-                callee2caller[cpi->result_id()] = guard_block_id;
-              }
-              // If callee has early return, insert a header block for
-              // single-trip loop that will encompass callee code.  Start
-              // postheader block.
-              //
-              // Note: Consider the following combination:
-              //  - the caller is a single block loop
-              //  - the callee does not begin with a structure header
-              //  - the callee has multiple returns.
-              // We still need to split the caller block and insert a guard
-              // block. But we only need to do it once. We haven't done it yet,
-              // but the single-trip loop header will serve the same purpose.
-              if (earlyReturn) {
-                singleTripLoopHeaderId = context()->TakeNextId();
-                if (singleTripLoopHeaderId == 0) {
-                  return false;
-                }
-                AddBranch(singleTripLoopHeaderId, &new_blk_ptr);
-                new_blocks->push_back(std::move(new_blk_ptr));
-                new_blk_ptr =
-                    MakeUnique<BasicBlock>(NewLabel(singleTripLoopHeaderId));
-                returnLabelId = context()->TakeNextId();
-                singleTripLoopContinueId = context()->TakeNextId();
-                if (returnLabelId == 0 || singleTripLoopContinueId == 0) {
-                  return false;
-                }
-                AddLoopMerge(returnLabelId, singleTripLoopContinueId,
-                             &new_blk_ptr);
-                uint32_t postHeaderId = context()->TakeNextId();
-                if (postHeaderId == 0) {
-                  return false;
-                }
-                AddBranch(postHeaderId, &new_blk_ptr);
-                new_blocks->push_back(std::move(new_blk_ptr));
-                new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(postHeaderId));
-                multiBlocks = true;
-                // Reset the mapping of the callee's entry block to point to
-                // the post-header block.  Do this so we can fix up phis later
-                // on to satisfy dominance.
-                callee2caller[cpi->result_id()] = postHeaderId;
-              }
-            } else {
-              multiBlocks = true;
-            }
-          } break;
-          case SpvOpReturnValue: {
-            // Store return value to return variable.
-            assert(returnVarId != 0);
-            uint32_t valId = cpi->GetInOperand(kSpvReturnValueId).words[0];
-            const auto mapItr = callee2caller.find(valId);
-            if (mapItr != callee2caller.end()) {
-              valId = mapItr->second;
-            }
-            AddStore(returnVarId, valId, &new_blk_ptr);
-
-            // Remember we saw a return; if followed by a label, will need to
-            // insert branch.
-            prevInstWasReturn = true;
-          } break;
-          case SpvOpReturn: {
-            // Remember we saw a return; if followed by a label, will need to
-            // insert branch.
-            prevInstWasReturn = true;
-          } break;
-          case SpvOpFunctionEnd: {
-            // If there was an early return, we generated a return label id
-            // for it.  Now we have to generate the return block with that Id.
-            if (returnLabelId != 0) {
-              // If previous instruction was return, insert branch instruction
-              // to return block.
-              if (prevInstWasReturn) AddBranch(returnLabelId, &new_blk_ptr);
-              if (earlyReturn) {
-                // If we generated a loop header for the single-trip loop
-                // to accommodate early returns, insert the continue
-                // target block now, with a false branch back to the loop
-                // header.
-                new_blocks->push_back(std::move(new_blk_ptr));
-                new_blk_ptr =
-                    MakeUnique<BasicBlock>(NewLabel(singleTripLoopContinueId));
-                uint32_t false_id = GetFalseId();
-                if (false_id == 0) {
-                  return false;
-                }
-                AddBranchCond(false_id, singleTripLoopHeaderId, returnLabelId,
-                              &new_blk_ptr);
-              }
-              // Generate the return block.
-              new_blocks->push_back(std::move(new_blk_ptr));
-              new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(returnLabelId));
-              multiBlocks = true;
-            }
-            // Load return value into result id of call, if it exists.
-            if (returnVarId != 0) {
-              const uint32_t resId = call_inst_itr->result_id();
-              assert(resId != 0);
-              AddLoad(calleeTypeId, resId, returnVarId, &new_blk_ptr);
-            }
-            // Copy remaining instructions from caller block.
-            for (Instruction* inst = call_inst_itr->NextNode(); inst;
-                 inst = call_inst_itr->NextNode()) {
-              inst->RemoveFromList();
-              std::unique_ptr<Instruction> cp_inst(inst);
-              // If multiple blocks generated, regenerate any same-block
-              // instruction that has not been seen in this last block.
-              if (multiBlocks) {
-                if (!CloneSameBlockOps(&cp_inst, &postCallSB, &preCallSB,
-                                       &new_blk_ptr)) {
-                  return false;
-                }
-
-                // Remember same-block ops in this block.
-                if (IsSameBlockOp(&*cp_inst)) {
-                  const uint32_t rid = cp_inst->result_id();
-                  postCallSB[rid] = rid;
-                }
-              }
-              new_blk_ptr->AddInstruction(std::move(cp_inst));
-            }
-            // Finalize inline code.
-            new_blocks->push_back(std::move(new_blk_ptr));
-          } break;
-          default: {
-            // Copy callee instruction and remap all input Ids.
-            std::unique_ptr<Instruction> cp_inst(cpi->Clone(context()));
-            bool succeeded = cp_inst->WhileEachInId(
-                [&callee2caller, &callee_result_ids, this](uint32_t* iid) {
-                  const auto mapItr = callee2caller.find(*iid);
-                  if (mapItr != callee2caller.end()) {
-                    *iid = mapItr->second;
-                  } else if (callee_result_ids.find(*iid) !=
-                             callee_result_ids.end()) {
-                    // Forward reference. Allocate a new id, map it,
-                    // use it and check for it when remapping result ids
-                    const uint32_t nid = context()->TakeNextId();
-                    if (nid == 0) {
-                      return false;
-                    }
-                    callee2caller[*iid] = nid;
-                    *iid = nid;
-                  }
-                  return true;
-                });
-            if (!succeeded) {
-              return false;
-            }
-            // If result id is non-zero, remap it. If already mapped, use mapped
-            // value, else use next id.
-            const uint32_t rid = cp_inst->result_id();
-            if (rid != 0) {
-              const auto mapItr = callee2caller.find(rid);
-              uint32_t nid;
-              if (mapItr != callee2caller.end()) {
-                nid = mapItr->second;
-              } else {
-                nid = context()->TakeNextId();
-                if (nid == 0) {
-                  return false;
-                }
-                callee2caller[rid] = nid;
-              }
-              cp_inst->SetResultId(nid);
-              get_decoration_mgr()->CloneDecorations(rid, nid);
-            }
-            new_blk_ptr->AddInstruction(std::move(cp_inst));
-          } break;
-        }
-        return true;
+  // Inline DebugClare instructions in the callee's header.
+  calleeFn->ForEachDebugInstructionsInHeader(
+      [&new_blk_ptr, &callee2caller, &inlined_at_ctx, this](Instruction* inst) {
+        InlineSingleInstruction(
+            callee2caller, new_blk_ptr.get(), inst,
+            context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
+                inst->GetDebugScope().GetInlinedAt(), &inlined_at_ctx));
       });
 
-  if (!successful) {
+  // Inline the entry block of the callee function.
+  if (!InlineEntryBlock(callee2caller, &new_blk_ptr, calleeFn->begin(),
+                        &inlined_at_ctx)) {
     return false;
   }
 
-  if (caller_is_loop_header && (new_blocks->size() > 1)) {
-    // Move the OpLoopMerge from the last block back to the first, where
-    // it belongs.
-    auto& first = new_blocks->front();
-    auto& last = new_blocks->back();
-    assert(first != last);
+  // Inline blocks of the callee function other than the entry block.
+  new_blk_ptr =
+      InlineBasicBlocks(new_blocks, callee2caller, std::move(new_blk_ptr),
+                        &inlined_at_ctx, calleeFn);
+  if (new_blk_ptr == nullptr) return false;
 
-    // Insert a modified copy of the loop merge into the first block.
-    auto loop_merge_itr = last->tail();
-    --loop_merge_itr;
-    assert(loop_merge_itr->opcode() == SpvOpLoopMerge);
-    std::unique_ptr<Instruction> cp_inst(loop_merge_itr->Clone(context()));
-    first->tail().InsertBefore(std::move(cp_inst));
+  new_blk_ptr = InlineReturn(callee2caller, new_blocks, std::move(new_blk_ptr),
+                             &inlined_at_ctx, calleeFn,
+                             &*(calleeFn->tail()->tail()), returnVarId);
 
-    // Remove the loop merge from the last block.
-    loop_merge_itr->RemoveFromList();
-    delete &*loop_merge_itr;
+  // Load return value into result id of call, if it exists.
+  if (returnVarId != 0) {
+    const uint32_t resId = call_inst_itr->result_id();
+    assert(resId != 0);
+    AddLoad(calleeTypeId, resId, returnVarId, &new_blk_ptr,
+            call_inst_itr->dbg_line_inst(), call_inst_itr->GetDebugScope());
   }
 
+  // Move instructions of original caller block after call instruction.
+  if (!MoveCallerInstsAfterFunctionCall(&preCallSB, &postCallSB, &new_blk_ptr,
+                                        call_inst_itr,
+                                        calleeFn->begin() != calleeFn->end()))
+    return false;
+
+  // Finalize inline code.
+  new_blocks->push_back(std::move(new_blk_ptr));
+
+  if (caller_is_loop_header && (new_blocks->size() > 1))
+    MoveLoopMergeInstToFirstBlock(new_blocks);
+
   // Update block map given replacement blocks.
   for (auto& blk : *new_blocks) {
     id2block_[blk->id()] = &*blk;
@@ -624,7 +644,21 @@
   const uint32_t calleeFnId =
       inst->GetSingleWordOperand(kSpvFunctionCallFunctionId);
   const auto ci = inlinable_.find(calleeFnId);
-  return ci != inlinable_.cend();
+  if (ci == inlinable_.cend()) return false;
+
+  if (early_return_funcs_.find(calleeFnId) != early_return_funcs_.end()) {
+    // We rely on the merge-return pass to handle the early return case
+    // in advance.
+    std::string message =
+        "The function '" + id2function_[calleeFnId]->DefInst().PrettyPrint() +
+        "' could not be inlined because the return instruction "
+        "is not at the end of the function. This could be fixed by "
+        "running merge-return before inlining.";
+    consumer()(SPV_MSG_WARNING, "", {0, 0, 0}, message.c_str());
+    return false;
+  }
+
+  return true;
 }
 
 void InlinePass::UpdateSucceedingPhis(
@@ -645,26 +679,6 @@
       });
 }
 
-bool InlinePass::HasNoReturnInStructuredConstruct(Function* func) {
-  // If control not structured, do not do loop/return analysis
-  // TODO: Analyze returns in non-structured control flow
-  if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
-    return false;
-  const auto structured_analysis = context()->GetStructuredCFGAnalysis();
-  // Search for returns in structured construct.
-  bool return_in_construct = false;
-  for (auto& blk : *func) {
-    auto terminal_ii = blk.cend();
-    --terminal_ii;
-    if (spvOpcodeIsReturn(terminal_ii->opcode()) &&
-        structured_analysis->ContainingConstruct(blk.id()) != 0) {
-      return_in_construct = true;
-      break;
-    }
-  }
-  return !return_in_construct;
-}
-
 bool InlinePass::HasNoReturnInLoop(Function* func) {
   // If control not structured, do not do loop/return analysis
   // TODO: Analyze returns in non-structured control flow
@@ -686,10 +700,18 @@
 }
 
 void InlinePass::AnalyzeReturns(Function* func) {
+  // Analyze functions without a return in loop.
   if (HasNoReturnInLoop(func)) {
     no_return_in_loop_.insert(func->result_id());
-    if (!HasNoReturnInStructuredConstruct(func))
+  }
+  // Analyze functions with a return before its tail basic block.
+  for (auto& blk : *func) {
+    auto terminal_ii = blk.cend();
+    --terminal_ii;
+    if (spvOpcodeIsReturn(terminal_ii->opcode()) && &blk != func->tail()) {
       early_return_funcs_.insert(func->result_id());
+      break;
+    }
   }
 }
 
diff --git a/third_party/SPIRV-Tools/source/opt/inline_pass.h b/third_party/SPIRV-Tools/source/opt/inline_pass.h
index bc5f781..202bc97 100644
--- a/third_party/SPIRV-Tools/source/opt/inline_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/inline_pass.h
@@ -24,6 +24,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include "source/opt/debug_info_manager.h"
 #include "source/opt/decoration_manager.h"
 #include "source/opt/module.h"
 #include "source/opt/pass.h"
@@ -58,11 +59,13 @@
 
   // Add store of valId to ptrId to end of block block_ptr.
   void AddStore(uint32_t ptrId, uint32_t valId,
-                std::unique_ptr<BasicBlock>* block_ptr);
+                std::unique_ptr<BasicBlock>* block_ptr,
+                const Instruction* line_inst, const DebugScope& dbg_scope);
 
   // Add load of ptrId into resultId to end of block block_ptr.
   void AddLoad(uint32_t typeId, uint32_t resultId, uint32_t ptrId,
-               std::unique_ptr<BasicBlock>* block_ptr);
+               std::unique_ptr<BasicBlock>* block_ptr,
+               const Instruction* line_inst, const DebugScope& dbg_scope);
 
   // Return new label.
   std::unique_ptr<Instruction> NewLabel(uint32_t label_id);
@@ -79,7 +82,8 @@
   // Clone and map callee locals.  Return true if successful.
   bool CloneAndMapLocals(Function* calleeFn,
                          std::vector<std::unique_ptr<Instruction>>* new_vars,
-                         std::unordered_map<uint32_t, uint32_t>* callee2caller);
+                         std::unordered_map<uint32_t, uint32_t>* callee2caller,
+                         analysis::DebugInlinedAtContext* inlined_at_ctx);
 
   // Create return variable for callee clone code.  The return type of
   // |calleeFn| must not be void.  Returns  the id of the return variable if
@@ -124,10 +128,6 @@
   // Return true if |inst| is a function call that can be inlined.
   bool IsInlinableFunctionCall(const Instruction* inst);
 
-  // Return true if |func| does not have a return that is
-  // nested in a structured if, switch or loop.
-  bool HasNoReturnInStructuredConstruct(Function* func);
-
   // Return true if |func| has no return in a loop. The current analysis
   // requires structured control flow, so return false if control flow not
   // structured ie. module is not a shader.
@@ -171,6 +171,69 @@
   // Set of functions that are originally called directly or indirectly from a
   // continue construct.
   std::unordered_set<uint32_t> funcs_called_from_continue_;
+
+ private:
+  // Moves instructions of the caller function up to the call instruction
+  // to |new_blk_ptr|.
+  void MoveInstsBeforeEntryBlock(
+      std::unordered_map<uint32_t, Instruction*>* preCallSB,
+      BasicBlock* new_blk_ptr, BasicBlock::iterator call_inst_itr,
+      UptrVectorIterator<BasicBlock> call_block_itr);
+
+  // Returns a new guard block after adding a branch to the end of
+  // |new_blocks|.
+  std::unique_ptr<BasicBlock> AddGuardBlock(
+      std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
+      std::unordered_map<uint32_t, uint32_t>* callee2caller,
+      std::unique_ptr<BasicBlock> new_blk_ptr, uint32_t entry_blk_label_id);
+
+  // Add store instructions for initializers of variables.
+  InstructionList::iterator AddStoresForVariableInitializers(
+      const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+      analysis::DebugInlinedAtContext* inlined_at_ctx,
+      std::unique_ptr<BasicBlock>* new_blk_ptr,
+      UptrVectorIterator<BasicBlock> callee_block_itr);
+
+  // Inlines a single instruction of the callee function.
+  bool InlineSingleInstruction(
+      const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+      BasicBlock* new_blk_ptr, const Instruction* inst,
+      uint32_t dbg_inlined_at);
+
+  // Inlines the return instruction of the callee function.
+  std::unique_ptr<BasicBlock> InlineReturn(
+      const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+      std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
+      std::unique_ptr<BasicBlock> new_blk_ptr,
+      analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn,
+      const Instruction* inst, uint32_t returnVarId);
+
+  // Inlines the entry block of the callee function.
+  bool InlineEntryBlock(
+      const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+      std::unique_ptr<BasicBlock>* new_blk_ptr,
+      UptrVectorIterator<BasicBlock> callee_first_block,
+      analysis::DebugInlinedAtContext* inlined_at_ctx);
+
+  // Inlines basic blocks of the callee function other than the entry basic
+  // block.
+  std::unique_ptr<BasicBlock> InlineBasicBlocks(
+      std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
+      const std::unordered_map<uint32_t, uint32_t>& callee2caller,
+      std::unique_ptr<BasicBlock> new_blk_ptr,
+      analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn);
+
+  // Moves instructions of the caller function after the call instruction
+  // to |new_blk_ptr|.
+  bool MoveCallerInstsAfterFunctionCall(
+      std::unordered_map<uint32_t, Instruction*>* preCallSB,
+      std::unordered_map<uint32_t, uint32_t>* postCallSB,
+      std::unique_ptr<BasicBlock>* new_blk_ptr,
+      BasicBlock::iterator call_inst_itr, bool multiBlocks);
+
+  // Move the OpLoopMerge from the last block back to the first.
+  void MoveLoopMergeInstToFirstBlock(
+      std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
 };
 
 }  // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/inst_bindless_check_pass.h b/third_party/SPIRV-Tools/source/opt/inst_bindless_check_pass.h
index 447871b..9335fa5 100644
--- a/third_party/SPIRV-Tools/source/opt/inst_bindless_check_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/inst_bindless_check_pass.h
@@ -28,13 +28,6 @@
 // external design may change as the layer evolves.
 class InstBindlessCheckPass : public InstrumentPass {
  public:
-  // Deprecated interface
-  InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id,
-                        bool input_length_enable, bool input_init_enable,
-                        uint32_t version)
-      : InstrumentPass(desc_set, shader_id, kInstValidationIdBindless, version),
-        input_length_enabled_(input_length_enable),
-        input_init_enabled_(input_init_enable) {}
   // Preferred Interface
   InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id,
                         bool input_length_enable, bool input_init_enable)
diff --git a/third_party/SPIRV-Tools/source/opt/inst_buff_addr_check_pass.h b/third_party/SPIRV-Tools/source/opt/inst_buff_addr_check_pass.h
index 67ffcc3..ec7bb68 100644
--- a/third_party/SPIRV-Tools/source/opt/inst_buff_addr_check_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/inst_buff_addr_check_pass.h
@@ -28,10 +28,6 @@
 // external design of this class may change as the layer evolves.
 class InstBuffAddrCheckPass : public InstrumentPass {
  public:
-  // Deprecated interface
-  InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id, uint32_t version)
-      : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr,
-                       version) {}
   // Preferred interface
   InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id)
       : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr) {}
diff --git a/third_party/SPIRV-Tools/source/opt/inst_debug_printf_pass.h b/third_party/SPIRV-Tools/source/opt/inst_debug_printf_pass.h
index 2968a20..70b0a72 100644
--- a/third_party/SPIRV-Tools/source/opt/inst_debug_printf_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/inst_debug_printf_pass.h
@@ -28,11 +28,10 @@
 class InstDebugPrintfPass : public InstrumentPass {
  public:
   // For test harness only
-  InstDebugPrintfPass()
-      : InstrumentPass(7, 23, kInstValidationIdDebugPrintf, 2) {}
+  InstDebugPrintfPass() : InstrumentPass(7, 23, kInstValidationIdDebugPrintf) {}
   // For all other interfaces
   InstDebugPrintfPass(uint32_t desc_set, uint32_t shader_id)
-      : InstrumentPass(desc_set, shader_id, kInstValidationIdDebugPrintf, 2) {}
+      : InstrumentPass(desc_set, shader_id, kInstValidationIdDebugPrintf) {}
 
   ~InstDebugPrintfPass() override = default;
 
diff --git a/third_party/SPIRV-Tools/source/opt/instruction.cpp b/third_party/SPIRV-Tools/source/opt/instruction.cpp
index 3ce38a9..126848e 100644
--- a/third_party/SPIRV-Tools/source/opt/instruction.cpp
+++ b/third_party/SPIRV-Tools/source/opt/instruction.cpp
@@ -29,13 +29,19 @@
 // Indices used to get particular operands out of instructions using InOperand.
 const uint32_t kTypeImageDimIndex = 1;
 const uint32_t kLoadBaseIndex = 0;
-const uint32_t kVariableStorageClassIndex = 0;
+const uint32_t kPointerTypeStorageClassIndex = 0;
 const uint32_t kTypeImageSampledIndex = 5;
 
 // Constants for OpenCL.DebugInfo.100 extension instructions.
+const uint32_t kExtInstSetIdInIdx = 0;
+const uint32_t kExtInstInstructionInIdx = 1;
 const uint32_t kDebugScopeNumWords = 7;
 const uint32_t kDebugScopeNumWordsWithoutInlinedAt = 6;
 const uint32_t kDebugNoScopeNumWords = 5;
+
+// Number of operands of an OpBranchConditional instruction
+// with weights.
+const uint32_t kOpBranchConditionalWithWeightsNumOperands = 5;
 }  // namespace
 
 Instruction::Instruction(IRContext* c)
@@ -164,6 +170,15 @@
   return size;
 }
 
+bool Instruction::HasBranchWeights() const {
+  if (opcode_ == SpvOpBranchConditional &&
+      NumOperands() == kOpBranchConditionalWithWeightsNumOperands) {
+    return true;
+  }
+
+  return false;
+}
+
 void Instruction::ToBinaryWithoutAttachedDebugInsts(
     std::vector<uint32_t>* binary) const {
   const uint32_t num_words = 1 + NumOperandWords();
@@ -180,10 +195,27 @@
 bool Instruction::IsReadOnlyLoad() const {
   if (IsLoad()) {
     Instruction* address_def = GetBaseAddress();
-    if (!address_def || address_def->opcode() != SpvOpVariable) {
+    if (!address_def) {
       return false;
     }
-    return address_def->IsReadOnlyVariable();
+
+    if (address_def->opcode() == SpvOpVariable) {
+      if (address_def->IsReadOnlyPointer()) {
+        return true;
+      }
+    }
+
+    if (address_def->opcode() == SpvOpLoad) {
+      const analysis::Type* address_type =
+          context()->get_type_mgr()->GetType(address_def->type_id());
+      if (address_type->AsSampledImage() != nullptr) {
+        const auto* image_type =
+            address_type->AsSampledImage()->image_type()->AsImage();
+        if (image_type->sampled() == 1) {
+          return true;
+        }
+      }
+    }
   }
   return false;
 }
@@ -213,11 +245,11 @@
   return base_inst;
 }
 
-bool Instruction::IsReadOnlyVariable() const {
+bool Instruction::IsReadOnlyPointer() const {
   if (context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
-    return IsReadOnlyVariableShaders();
+    return IsReadOnlyPointerShaders();
   else
-    return IsReadOnlyVariableKernel();
+    return IsReadOnlyPointerKernel();
 }
 
 bool Instruction::IsVulkanStorageImage() const {
@@ -225,7 +257,8 @@
     return false;
   }
 
-  uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
+  uint32_t storage_class =
+      GetSingleWordInOperand(kPointerTypeStorageClassIndex);
   if (storage_class != SpvStorageClassUniformConstant) {
     return false;
   }
@@ -259,7 +292,8 @@
     return false;
   }
 
-  uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
+  uint32_t storage_class =
+      GetSingleWordInOperand(kPointerTypeStorageClassIndex);
   if (storage_class != SpvStorageClassUniformConstant) {
     return false;
   }
@@ -293,7 +327,8 @@
     return false;
   }
 
-  uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
+  uint32_t storage_class =
+      GetSingleWordInOperand(kPointerTypeStorageClassIndex);
   if (storage_class != SpvStorageClassUniformConstant) {
     return false;
   }
@@ -342,7 +377,8 @@
     return false;
   }
 
-  uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
+  uint32_t storage_class =
+      GetSingleWordInOperand(kPointerTypeStorageClassIndex);
   if (storage_class == SpvStorageClassUniform) {
     bool is_buffer_block = false;
     context()->get_decoration_mgr()->ForEachDecoration(
@@ -364,7 +400,8 @@
     return false;
   }
 
-  uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
+  uint32_t storage_class =
+      GetSingleWordInOperand(kPointerTypeStorageClassIndex);
   if (storage_class != SpvStorageClassUniform) {
     return false;
   }
@@ -390,9 +427,18 @@
   return is_block;
 }
 
-bool Instruction::IsReadOnlyVariableShaders() const {
-  uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
+bool Instruction::IsReadOnlyPointerShaders() const {
+  if (type_id() == 0) {
+    return false;
+  }
+
   Instruction* type_def = context()->get_def_use_mgr()->GetDef(type_id());
+  if (type_def->opcode() != SpvOpTypePointer) {
+    return false;
+  }
+
+  uint32_t storage_class =
+      type_def->GetSingleWordInOperand(kPointerTypeStorageClassIndex);
 
   switch (storage_class) {
     case SpvStorageClassUniformConstant:
@@ -420,8 +466,19 @@
   return is_nonwritable;
 }
 
-bool Instruction::IsReadOnlyVariableKernel() const {
-  uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
+bool Instruction::IsReadOnlyPointerKernel() const {
+  if (type_id() == 0) {
+    return false;
+  }
+
+  Instruction* type_def = context()->get_def_use_mgr()->GetDef(type_id());
+  if (type_def->opcode() != SpvOpTypePointer) {
+    return false;
+  }
+
+  uint32_t storage_class =
+      type_def->GetSingleWordInOperand(kPointerTypeStorageClassIndex);
+
   return storage_class == SpvStorageClassUniformConstant;
 }
 
@@ -510,6 +567,21 @@
   return false;
 }
 
+OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const {
+  if (opcode() != SpvOpExtInst) return OpenCLDebugInfo100InstructionsMax;
+
+  if (!context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo())
+    return OpenCLDebugInfo100InstructionsMax;
+
+  if (GetSingleWordInOperand(kExtInstSetIdInIdx) !=
+      context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) {
+    return OpenCLDebugInfo100InstructionsMax;
+  }
+
+  return OpenCLDebugInfo100Instructions(
+      GetSingleWordInOperand(kExtInstInstructionInIdx));
+}
+
 bool Instruction::IsValidBaseImage() const {
   uint32_t tid = type_id();
   if (tid == 0) {
@@ -551,7 +623,19 @@
     return false;
   }
   Instruction* type = context()->get_def_use_mgr()->GetDef(type_id());
-  return folder.IsFoldableType(type);
+  if (!folder.IsFoldableType(type)) {
+    return false;
+  }
+
+  // Even if the type of the instruction is foldable, its operands may not be
+  // foldable (e.g., comparisons of 64bit types).  Check that all operand types
+  // are foldable before accepting the instruction.
+  return WhileEachInOperand([&folder, this](const uint32_t* op_id) {
+    Instruction* def_inst = context()->get_def_use_mgr()->GetDef(*op_id);
+    Instruction* def_inst_type =
+        context()->get_def_use_mgr()->GetDef(def_inst->type_id());
+    return folder.IsFoldableType(def_inst_type);
+  });
 }
 
 bool Instruction::IsFloatingPointFoldingAllowed() const {
@@ -714,9 +798,6 @@
     return true;
   }
 
-  const uint32_t kExtInstSetIdInIdx = 0;
-  const uint32_t kExtInstInstructionInIdx = 1;
-
   if (opcode() == SpvOpExtInst) {
     uint32_t instSetId =
         context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
diff --git a/third_party/SPIRV-Tools/source/opt/instruction.h b/third_party/SPIRV-Tools/source/opt/instruction.h
index a3342c6..7d8fed8 100644
--- a/third_party/SPIRV-Tools/source/opt/instruction.h
+++ b/third_party/SPIRV-Tools/source/opt/instruction.h
@@ -22,14 +22,14 @@
 #include <utility>
 #include <vector>
 
-#include "source/opcode.h"
-#include "source/operand.h"
-#include "source/util/ilist_node.h"
-#include "source/util/small_vector.h"
-
+#include "OpenCLDebugInfo100.h"
 #include "source/latest_version_glsl_std_450_header.h"
 #include "source/latest_version_spirv_header.h"
+#include "source/opcode.h"
+#include "source/operand.h"
 #include "source/opt/reflect.h"
+#include "source/util/ilist_node.h"
+#include "source/util/small_vector.h"
 #include "spirv-tools/libspirv.h"
 
 const uint32_t kNoDebugScope = 0;
@@ -92,6 +92,19 @@
   // Returns a string operand as a std::string.
   std::string AsString() const { return AsCString(); }
 
+  // Returns a literal integer operand as a uint64_t
+  uint64_t AsLiteralUint64() const {
+    assert(type == SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER);
+    assert(1 <= words.size());
+    assert(words.size() <= 2);
+    // Load the low word.
+    uint64_t result = uint64_t(words[0]);
+    if (words.size() > 1) {
+      result = result | (uint64_t(words[1]) << 32);
+    }
+    return result;
+  }
+
   friend bool operator==(const Operand& o1, const Operand& o2) {
     return o1.type == o2.type && o1.words == o2.words;
   }
@@ -226,6 +239,10 @@
     return dbg_line_insts_;
   }
 
+  const Instruction* dbg_line_inst() const {
+    return dbg_line_insts_.empty() ? nullptr : &dbg_line_insts_[0];
+  }
+
   // Clear line-related debug instructions attached to this instruction.
   void clear_dbg_line_insts() { dbg_line_insts_.clear(); }
 
@@ -278,6 +295,13 @@
   // Sets DebugScope.
   inline void SetDebugScope(const DebugScope& scope);
   inline const DebugScope& GetDebugScope() const { return dbg_scope_; }
+  // Updates DebugInlinedAt of DebugScope and OpLine.
+  inline void UpdateDebugInlinedAt(uint32_t new_inlined_at);
+  inline uint32_t GetDebugInlinedAt() const {
+    return dbg_scope_.GetInlinedAt();
+  }
+  // Updates OpLine and DebugScope based on the information of |from|.
+  inline void UpdateDebugInfo(const Instruction* from);
   // Remove the |index|-th operand
   void RemoveOperand(uint32_t index) {
     operands_.erase(operands_.begin() + index);
@@ -351,6 +375,10 @@
   inline bool WhileEachInOperand(
       const std::function<bool(const uint32_t*)>& f) const;
 
+  // Returns true if it's an OpBranchConditional instruction
+  // with branch weights.
+  bool HasBranchWeights() const;
+
   // Returns true if any operands can be labels
   inline bool HasLabels() const;
 
@@ -383,8 +411,14 @@
   // Memory-to-memory instructions are not considered loads.
   inline bool IsLoad() const;
 
-  // Returns true if the instruction declares a variable that is read-only.
-  bool IsReadOnlyVariable() const;
+  // Returns true if the instruction generates a pointer that is definitely
+  // read-only.  This is determined by analysing the pointer type's storage
+  // class and decorations that target the pointer's id.  It does not analyse
+  // other instructions that the pointer may be derived from.  Thus if 'true' is
+  // returned, the pointer is definitely read-only, while if 'false' is returned
+  // it is possible that the pointer may actually be read-only if it is derived
+  // from another pointer that is decorated as read-only.
+  bool IsReadOnlyPointer() const;
 
   // The following functions check for the various descriptor types defined in
   // the Vulkan specification section 13.1.
@@ -496,6 +530,11 @@
   // rules for physical addressing.
   bool IsValidBasePointer() const;
 
+  // Returns debug opcode of an OpenCL.100.DebugInfo instruction. If
+  // it is not an OpenCL.100.DebugInfo instruction, just returns
+  // OpenCLDebugInfo100InstructionsMax.
+  OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const;
+
   // Dump this instruction on stderr.  Useful when running interactive
   // debuggers.
   void Dump() const;
@@ -508,11 +547,12 @@
     return 0;
   }
 
-  // Returns true if the instruction declares a variable that is read-only.  The
-  // first version assumes the module is a shader module.  The second assumes a
+  // Returns true if the instruction generates a read-only pointer, with the
+  // same caveats documented in the comment for IsReadOnlyPointer.  The first
+  // version assumes the module is a shader module.  The second assumes a
   // kernel.
-  bool IsReadOnlyVariableShaders() const;
-  bool IsReadOnlyVariableKernel() const;
+  bool IsReadOnlyPointerShaders() const;
+  bool IsReadOnlyPointerKernel() const;
 
   // Returns true if the result of |inst| can be used as the base image for an
   // instruction that samples a image, reads an image, or writes to an image.
@@ -611,6 +651,21 @@
   }
 }
 
+inline void Instruction::UpdateDebugInlinedAt(uint32_t new_inlined_at) {
+  dbg_scope_.SetInlinedAt(new_inlined_at);
+  for (auto& i : dbg_line_insts_) {
+    i.dbg_scope_.SetInlinedAt(new_inlined_at);
+  }
+}
+
+inline void Instruction::UpdateDebugInfo(const Instruction* from) {
+  if (from == nullptr) return;
+  clear_dbg_line_insts();
+  if (!from->dbg_line_insts().empty())
+    dbg_line_insts().push_back(from->dbg_line_insts()[0]);
+  SetDebugScope(from->GetDebugScope());
+}
+
 inline void Instruction::SetResultType(uint32_t ty_id) {
   // TODO(dsinclair): Allow setting a type id if there wasn't one
   // previously. Need to make room in the operands_ array to place the result,
diff --git a/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp b/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
index c8c6c21..4210ad5 100644
--- a/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
@@ -885,14 +885,6 @@
 }
 
 bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
-  // Check that format version 2 requested
-  if (version_ != 2u) {
-    if (consumer()) {
-      std::string message = "Unsupported instrumentation format requested";
-      consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str());
-    }
-    return false;
-  }
   // Make sure all entry points have the same execution model. Do not
   // instrument if they do not.
   // TODO(greg-lunarg): Handle mixed stages. Technically, a shader module
diff --git a/third_party/SPIRV-Tools/source/opt/instrument_pass.h b/third_party/SPIRV-Tools/source/opt/instrument_pass.h
index 11afdce..f6884d2 100644
--- a/third_party/SPIRV-Tools/source/opt/instrument_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/instrument_pass.h
@@ -87,18 +87,7 @@
       : Pass(),
         desc_set_(desc_set),
         shader_id_(shader_id),
-        validation_id_(validation_id),
-        version_(2u) {}
-  // Create instrumentation pass for |validation_id| which utilizes descriptor
-  // set |desc_set| for debug input and output buffers and writes |shader_id|
-  // into debug output records with format |version|. Deprecated.
-  InstrumentPass(uint32_t desc_set, uint32_t shader_id, uint32_t validation_id,
-                 uint32_t version)
-      : Pass(),
-        desc_set_(desc_set),
-        shader_id_(shader_id),
-        validation_id_(validation_id),
-        version_(version) {}
+        validation_id_(validation_id) {}
 
   // Initialize state for instrumentation of module.
   void InitializeInstrument();
@@ -425,9 +414,6 @@
   // id for void type
   uint32_t void_id_;
 
-  // Record format version
-  uint32_t version_;
-
   // boolean to remember storage buffer extension
   bool storage_buffer_ext_defined_;
 
diff --git a/third_party/SPIRV-Tools/source/opt/ir_context.cpp b/third_party/SPIRV-Tools/source/opt/ir_context.cpp
index 72993fd..df04066 100644
--- a/third_party/SPIRV-Tools/source/opt/ir_context.cpp
+++ b/third_party/SPIRV-Tools/source/opt/ir_context.cpp
@@ -16,6 +16,7 @@
 
 #include <cstring>
 
+#include "OpenCLDebugInfo100.h"
 #include "source/latest_version_glsl_std_450_header.h"
 #include "source/opt/log.h"
 #include "source/opt/mem_pass.h"
@@ -29,6 +30,10 @@
 static const int kEntryPointInterfaceInIdx = 3;
 static const int kEntryPointFunctionIdInIdx = 1;
 
+// Constants for OpenCL.DebugInfo.100 extension instructions.
+static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
+static const uint32_t kDebugGlobalVariableOperandVariableIndex = 11;
+
 }  // anonymous namespace
 
 namespace spvtools {
@@ -80,6 +85,9 @@
   if (set & kAnalysisTypes) {
     BuildTypeManager();
   }
+  if (set & kAnalysisDebugInfo) {
+    BuildDebugInfoManager();
+  }
 }
 
 void IRContext::InvalidateAnalysesExceptFor(
@@ -93,6 +101,7 @@
   // away, the ConstantManager has to go away.
   if (analyses_to_invalidate & kAnalysisTypes) {
     analyses_to_invalidate |= kAnalysisConstants;
+    analyses_to_invalidate |= kAnalysisDebugInfo;
   }
 
   // The dominator analysis hold the psuedo entry and exit nodes from the CFG.
@@ -143,6 +152,10 @@
     type_mgr_.reset(nullptr);
   }
 
+  if (analyses_to_invalidate & kAnalysisDebugInfo) {
+    debug_info_mgr_.reset(nullptr);
+  }
+
   valid_analyses_ = Analysis(valid_analyses_ & ~analyses_to_invalidate);
 }
 
@@ -153,6 +166,8 @@
 
   KillNamesAndDecorates(inst);
 
+  KillOperandFromDebugInstructions(inst);
+
   if (AreAnalysesValid(kAnalysisDefUse)) {
     get_def_use_mgr()->ClearInst(inst);
   }
@@ -265,7 +280,7 @@
 bool IRContext::IsConsistent() {
 #ifndef SPIRV_CHECK_CONTEXT
   return true;
-#endif
+#else
   if (AreAnalysesValid(kAnalysisDefUse)) {
     analysis::DefUseManager new_def_use(module());
     if (*get_def_use_mgr() != new_def_use) {
@@ -317,6 +332,7 @@
     }
   }
   return true;
+#endif
 }
 
 void IRContext::ForgetUses(Instruction* inst) {
@@ -365,6 +381,42 @@
   KillNamesAndDecorates(rId);
 }
 
+void IRContext::KillOperandFromDebugInstructions(Instruction* inst) {
+  const auto opcode = inst->opcode();
+  const uint32_t id = inst->result_id();
+  // Kill id of OpFunction from DebugFunction.
+  if (opcode == SpvOpFunction) {
+    for (auto it = module()->ext_inst_debuginfo_begin();
+         it != module()->ext_inst_debuginfo_end(); ++it) {
+      if (it->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugFunction)
+        continue;
+      auto& operand = it->GetOperand(kDebugFunctionOperandFunctionIndex);
+      if (operand.words[0] == id) {
+        operand.words[0] =
+            get_debug_info_mgr()->GetDebugInfoNone()->result_id();
+      }
+    }
+  }
+  // Kill id of OpVariable for global variable from DebugGlobalVariable.
+  if (opcode == SpvOpVariable || IsConstantInst(opcode)) {
+    for (auto it = module()->ext_inst_debuginfo_begin();
+         it != module()->ext_inst_debuginfo_end(); ++it) {
+      if (it->GetOpenCL100DebugOpcode() !=
+          OpenCLDebugInfo100DebugGlobalVariable)
+        continue;
+      auto& operand = it->GetOperand(kDebugGlobalVariableOperandVariableIndex);
+      if (operand.words[0] == id) {
+        operand.words[0] =
+            get_debug_info_mgr()->GetDebugInfoNone()->result_id();
+      }
+    }
+  }
+  // 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) {
   if (capability == SpvCapabilityShader) {
     combinator_ops_[0].insert({SpvOpNop,
diff --git a/third_party/SPIRV-Tools/source/opt/ir_context.h b/third_party/SPIRV-Tools/source/opt/ir_context.h
index 723a2bb..a1b63ff 100644
--- a/third_party/SPIRV-Tools/source/opt/ir_context.h
+++ b/third_party/SPIRV-Tools/source/opt/ir_context.h
@@ -29,6 +29,7 @@
 #include "source/assembly_grammar.h"
 #include "source/opt/cfg.h"
 #include "source/opt/constants.h"
+#include "source/opt/debug_info_manager.h"
 #include "source/opt/decoration_manager.h"
 #include "source/opt/def_use_manager.h"
 #include "source/opt/dominator_analysis.h"
@@ -78,7 +79,8 @@
     kAnalysisIdToFuncMapping = 1 << 13,
     kAnalysisConstants = 1 << 14,
     kAnalysisTypes = 1 << 15,
-    kAnalysisEnd = 1 << 16
+    kAnalysisDebugInfo = 1 << 16,
+    kAnalysisEnd = 1 << 17
   };
 
   using ProcessFunction = std::function<bool(Function*)>;
@@ -326,6 +328,17 @@
     return type_mgr_.get();
   }
 
+  // Returns a pointer to the debug information manager.  If no debug
+  // information manager has been created yet, it creates one.
+  // NOTE: Once created, the debug information manager remains active
+  // it is never re-built.
+  analysis::DebugInfoManager* get_debug_info_mgr() {
+    if (!AreAnalysesValid(kAnalysisDebugInfo)) {
+      BuildDebugInfoManager();
+    }
+    return debug_info_mgr_.get();
+  }
+
   // Returns a pointer to the scalar evolution analysis. If it is invalid it
   // will be rebuilt first.
   ScalarEvolutionAnalysis* GetScalarEvolutionAnalysis() {
@@ -426,6 +439,9 @@
   // Kill all name and decorate ops targeting the result id of |inst|.
   void KillNamesAndDecorates(Instruction* inst);
 
+  // Change operands of debug instruction to DebugInfoNone.
+  void KillOperandFromDebugInstructions(Instruction* inst);
+
   // Returns the next unique id for use by an instruction.
   inline uint32_t TakeNextUniqueId() {
     assert(unique_id_ != std::numeric_limits<uint32_t>::max());
@@ -652,6 +668,13 @@
     valid_analyses_ = valid_analyses_ | kAnalysisTypes;
   }
 
+  // Builds the debug information manager from scratch, even if it was
+  // already valid.
+  void BuildDebugInfoManager() {
+    debug_info_mgr_ = MakeUnique<analysis::DebugInfoManager>(this);
+    valid_analyses_ = valid_analyses_ | kAnalysisDebugInfo;
+  }
+
   // Removes all computed dominator and post-dominator trees. This will force
   // the context to rebuild the trees on demand.
   void ResetDominatorAnalysis() {
@@ -774,6 +797,9 @@
   // Type manager for |module_|.
   std::unique_ptr<analysis::TypeManager> type_mgr_;
 
+  // Debug information manager for |module_|.
+  std::unique_ptr<analysis::DebugInfoManager> debug_info_mgr_;
+
   // A map from an id to its corresponding OpName and OpMemberName instructions.
   std::unique_ptr<std::multimap<uint32_t, Instruction*>> id_to_name_;
 
diff --git a/third_party/SPIRV-Tools/source/opt/ir_loader.cpp b/third_party/SPIRV-Tools/source/opt/ir_loader.cpp
index fcde079..acd41cd 100644
--- a/third_party/SPIRV-Tools/source/opt/ir_loader.cpp
+++ b/third_party/SPIRV-Tools/source/opt/ir_loader.cpp
@@ -135,6 +135,8 @@
       Error(consumer_, src, loc, "terminator instruction outside basic block");
       return false;
     }
+    if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope)
+      spv_inst->SetDebugScope(last_dbg_scope_);
     block_->AddInstruction(std::move(spv_inst));
     function_->AddBasicBlock(std::move(block_));
     block_ = nullptr;
diff --git a/third_party/SPIRV-Tools/source/opt/mem_pass.cpp b/third_party/SPIRV-Tools/source/opt/mem_pass.cpp
index 04e2e8a..d23d679 100644
--- a/third_party/SPIRV-Tools/source/opt/mem_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/mem_pass.cpp
@@ -97,6 +97,11 @@
   Instruction* ptrInst = get_def_use_mgr()->GetDef(*varId);
   Instruction* varInst;
 
+  if (ptrInst->opcode() == SpvOpConstantNull) {
+    *varId = 0;
+    return ptrInst;
+  }
+
   if (ptrInst->opcode() != SpvOpVariable &&
       ptrInst->opcode() != SpvOpFunctionParameter) {
     varInst = ptrInst->GetBaseAddress();
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 bbac4bb..8cb4299 100644
--- a/third_party/SPIRV-Tools/source/opt/merge_return_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/merge_return_pass.cpp
@@ -39,8 +39,11 @@
       if (!is_shader || return_blocks.size() == 0) {
         return false;
       }
-      if (context()->GetStructuredCFGAnalysis()->ContainingConstruct(
-              return_blocks[0]->id()) == 0) {
+      bool isInConstruct =
+          context()->GetStructuredCFGAnalysis()->ContainingConstruct(
+              return_blocks[0]->id()) != 0;
+      bool endsWithReturn = return_blocks[0] == function->tail();
+      if (!isInConstruct && endsWithReturn) {
         return false;
       }
     }
@@ -421,7 +424,6 @@
   auto old_body_id = TakeNextId();
   BasicBlock* old_body = block->SplitBasicBlock(context(), old_body_id, iter);
   predicated->insert(old_body);
-  cfg()->AddEdges(old_body);
 
   // If a return block is being split, mark the new body block also as a return
   // block.
diff --git a/third_party/SPIRV-Tools/source/opt/optimizer.cpp b/third_party/SPIRV-Tools/source/opt/optimizer.cpp
index 0a937e8..25adee9 100644
--- a/third_party/SPIRV-Tools/source/opt/optimizer.cpp
+++ b/third_party/SPIRV-Tools/source/opt/optimizer.cpp
@@ -175,9 +175,18 @@
       .RegisterPass(CreateAggressiveDCEPass())
       .RegisterPass(CreateCCPPass())
       .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateLoopUnrollPass(true))
+      .RegisterPass(CreateDeadBranchElimPass())
       .RegisterPass(CreateRedundancyEliminationPass())
       .RegisterPass(CreateCombineAccessChainsPass())
       .RegisterPass(CreateSimplificationPass())
+      .RegisterPass(CreateScalarReplacementPass())
+      .RegisterPass(CreateLocalAccessChainConvertPass())
+      .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
+      .RegisterPass(CreateLocalSingleStoreElimPass())
+      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateSSARewritePass())
+      .RegisterPass(CreateAggressiveDCEPass())
       .RegisterPass(CreateVectorDCEPass())
       .RegisterPass(CreateDeadInsertElimPass())
       .RegisterPass(CreateDeadBranchElimPass())
@@ -407,19 +416,19 @@
   } else if (pass_name == "replace-invalid-opcode") {
     RegisterPass(CreateReplaceInvalidOpcodePass());
   } else if (pass_name == "inst-bindless-check") {
-    RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, 2));
+    RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false));
     RegisterPass(CreateSimplificationPass());
     RegisterPass(CreateDeadBranchElimPass());
     RegisterPass(CreateBlockMergePass());
     RegisterPass(CreateAggressiveDCEPass());
   } else if (pass_name == "inst-desc-idx-check") {
-    RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true, 2));
+    RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true));
     RegisterPass(CreateSimplificationPass());
     RegisterPass(CreateDeadBranchElimPass());
     RegisterPass(CreateBlockMergePass());
     RegisterPass(CreateAggressiveDCEPass());
   } else if (pass_name == "inst-buff-addr-check") {
-    RegisterPass(CreateInstBuffAddrCheckPass(7, 23, 2));
+    RegisterPass(CreateInstBuffAddrCheckPass(7, 23));
     RegisterPass(CreateAggressiveDCEPass());
   } else if (pass_name == "convert-relaxed-to-half") {
     RegisterPass(CreateConvertRelaxedToHalfPass());
@@ -498,7 +507,7 @@
   } else if (pass_name == "legalize-vector-shuffle") {
     RegisterPass(CreateLegalizeVectorShufflePass());
   } else if (pass_name == "split-invalid-unreachable") {
-    RegisterPass(CreateLegalizeVectorShufflePass());
+    RegisterPass(CreateSplitInvalidUnreachablePass());
   } else if (pass_name == "decompose-initialized-variables") {
     RegisterPass(CreateDecomposeInitializedVariablesPass());
   } else if (pass_name == "graphics-robust-access") {
@@ -885,12 +894,10 @@
 Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
                                                  uint32_t shader_id,
                                                  bool input_length_enable,
-                                                 bool input_init_enable,
-                                                 uint32_t version) {
+                                                 bool input_init_enable) {
   return MakeUnique<Optimizer::PassToken::Impl>(
-      MakeUnique<opt::InstBindlessCheckPass>(desc_set, shader_id,
-                                             input_length_enable,
-                                             input_init_enable, version));
+      MakeUnique<opt::InstBindlessCheckPass>(
+          desc_set, shader_id, input_length_enable, input_init_enable));
 }
 
 Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,
@@ -900,10 +907,9 @@
 }
 
 Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
-                                                 uint32_t shader_id,
-                                                 uint32_t version) {
+                                                 uint32_t shader_id) {
   return MakeUnique<Optimizer::PassToken::Impl>(
-      MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id, version));
+      MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id));
 }
 
 Optimizer::PassToken CreateConvertRelaxedToHalfPass() {
diff --git a/third_party/SPIRV-Tools/source/opt/struct_cfg_analysis.cpp b/third_party/SPIRV-Tools/source/opt/struct_cfg_analysis.cpp
index b16322c..57fc49c 100644
--- a/third_party/SPIRV-Tools/source/opt/struct_cfg_analysis.cpp
+++ b/third_party/SPIRV-Tools/source/opt/struct_cfg_analysis.cpp
@@ -85,9 +85,14 @@
       if (merge_inst->opcode() == SpvOpLoopMerge) {
         new_state.cinfo.containing_loop = block->id();
         new_state.cinfo.containing_switch = 0;
-        new_state.cinfo.in_continue = false;
         new_state.continue_node =
             merge_inst->GetSingleWordInOperand(kContinueNodeIndex);
+        if (block->id() == new_state.continue_node) {
+          new_state.cinfo.in_continue = true;
+          bb_to_construct_[block->id()].in_continue = true;
+        } else {
+          new_state.cinfo.in_continue = false;
+        }
       } else {
         new_state.cinfo.containing_loop = state.back().cinfo.containing_loop;
         new_state.cinfo.in_continue = state.back().cinfo.in_continue;
diff --git a/third_party/SPIRV-Tools/source/opt/type_manager.h b/third_party/SPIRV-Tools/source/opt/type_manager.h
index 8fcf8aa..ce9d83d 100644
--- a/third_party/SPIRV-Tools/source/opt/type_manager.h
+++ b/third_party/SPIRV-Tools/source/opt/type_manager.h
@@ -194,6 +194,13 @@
 
   uint32_t GetBoolTypeId() { return GetTypeInstruction(GetBoolType()); }
 
+  Type* GetVoidType() {
+    Void void_type;
+    return GetRegisteredType(&void_type);
+  }
+
+  uint32_t GetVoidTypeId() { return GetTypeInstruction(GetVoidType()); }
+
  private:
   using TypeToIdMap = std::unordered_map<const Type*, uint32_t, HashTypePointer,
                                          CompareTypePointers>;
diff --git a/third_party/SPIRV-Tools/source/opt/wrap_opkill.cpp b/third_party/SPIRV-Tools/source/opt/wrap_opkill.cpp
index ffd7a10..3c8bae6 100644
--- a/third_party/SPIRV-Tools/source/opt/wrap_opkill.cpp
+++ b/third_party/SPIRV-Tools/source/opt/wrap_opkill.cpp
@@ -59,9 +59,12 @@
   if (func_id == 0) {
     return false;
   }
-  if (ir_builder.AddFunctionCall(GetVoidTypeId(), func_id, {}) == nullptr) {
+  Instruction* call_inst =
+      ir_builder.AddFunctionCall(GetVoidTypeId(), func_id, {});
+  if (call_inst == nullptr) {
     return false;
   }
+  call_inst->UpdateDebugInfo(inst);
 
   Instruction* return_inst = nullptr;
   uint32_t return_type_id = GetOwningFunctionsReturnType(inst);
@@ -147,6 +150,7 @@
   bb->AddInstruction(std::move(kill_inst));
 
   // Add the bb to the function
+  bb->SetParent(opkill_function_.get());
   opkill_function_->AddBasicBlock(std::move(bb));
 
   // Add the function to the module.
diff --git a/third_party/SPIRV-Tools/source/reduce/CMakeLists.txt b/third_party/SPIRV-Tools/source/reduce/CMakeLists.txt
index 51e9b1d..d945bd2 100644
--- a/third_party/SPIRV-Tools/source/reduce/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/source/reduce/CMakeLists.txt
@@ -26,12 +26,14 @@
         reduction_util.h
         remove_block_reduction_opportunity.h
         remove_block_reduction_opportunity_finder.h
-        remove_instruction_reduction_opportunity.h
         remove_function_reduction_opportunity.h
         remove_function_reduction_opportunity_finder.h
+        remove_instruction_reduction_opportunity.h
         remove_selection_reduction_opportunity.h
         remove_selection_reduction_opportunity_finder.h
-        remove_unreferenced_instruction_reduction_opportunity_finder.h
+        remove_struct_member_reduction_opportunity.h
+        remove_unused_instruction_reduction_opportunity_finder.h
+        remove_unused_struct_member_reduction_opportunity_finder.h
         structured_loop_to_selection_reduction_opportunity.h
         structured_loop_to_selection_reduction_opportunity_finder.h
         conditional_branch_to_simple_conditional_branch_opportunity_finder.h
@@ -57,7 +59,9 @@
         remove_instruction_reduction_opportunity.cpp
         remove_selection_reduction_opportunity.cpp
         remove_selection_reduction_opportunity_finder.cpp
-        remove_unreferenced_instruction_reduction_opportunity_finder.cpp
+        remove_struct_member_reduction_opportunity.cpp
+        remove_unused_instruction_reduction_opportunity_finder.cpp
+        remove_unused_struct_member_reduction_opportunity_finder.cpp
         structured_loop_to_selection_reduction_opportunity.cpp
         structured_loop_to_selection_reduction_opportunity_finder.cpp
         conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp
diff --git a/third_party/SPIRV-Tools/source/reduce/pch_source_reduce.h b/third_party/SPIRV-Tools/source/reduce/pch_source_reduce.h
index 6c0da0c..81bed20 100644
--- a/third_party/SPIRV-Tools/source/reduce/pch_source_reduce.h
+++ b/third_party/SPIRV-Tools/source/reduce/pch_source_reduce.h
@@ -20,4 +20,4 @@
 #include "source/reduce/reduction_opportunity.h"
 #include "source/reduce/reduction_pass.h"
 #include "source/reduce/remove_instruction_reduction_opportunity.h"
-#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
+#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h"
diff --git a/third_party/SPIRV-Tools/source/reduce/reducer.cpp b/third_party/SPIRV-Tools/source/reduce/reducer.cpp
index bda41ce..092d409 100644
--- a/third_party/SPIRV-Tools/source/reduce/reducer.cpp
+++ b/third_party/SPIRV-Tools/source/reduce/reducer.cpp
@@ -25,7 +25,8 @@
 #include "source/reduce/remove_block_reduction_opportunity_finder.h"
 #include "source/reduce/remove_function_reduction_opportunity_finder.h"
 #include "source/reduce/remove_selection_reduction_opportunity_finder.h"
-#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
+#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h"
+#include "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h"
 #include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
 #include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h"
 #include "source/spirv_reducer_options.h"
@@ -103,8 +104,8 @@
 
 void Reducer::AddDefaultReductionPasses() {
   AddReductionPass(
-      spvtools::MakeUnique<
-          RemoveUnreferencedInstructionReductionOpportunityFinder>(false));
+      spvtools::MakeUnique<RemoveUnusedInstructionReductionOpportunityFinder>(
+          false));
   AddReductionPass(
       spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
   AddReductionPass(
@@ -126,12 +127,14 @@
           ConditionalBranchToSimpleConditionalBranchOpportunityFinder>());
   AddReductionPass(
       spvtools::MakeUnique<SimpleConditionalBranchToBranchOpportunityFinder>());
+  AddReductionPass(spvtools::MakeUnique<
+                   RemoveUnusedStructMemberReductionOpportunityFinder>());
 
   // Cleanup passes.
 
   AddCleanupReductionPass(
-      spvtools::MakeUnique<
-          RemoveUnreferencedInstructionReductionOpportunityFinder>(true));
+      spvtools::MakeUnique<RemoveUnusedInstructionReductionOpportunityFinder>(
+          true));
 }
 
 void Reducer::AddReductionPass(
diff --git a/third_party/SPIRV-Tools/source/reduce/remove_instruction_reduction_opportunity.cpp b/third_party/SPIRV-Tools/source/reduce/remove_instruction_reduction_opportunity.cpp
index 9ca093b..8026204 100644
--- a/third_party/SPIRV-Tools/source/reduce/remove_instruction_reduction_opportunity.cpp
+++ b/third_party/SPIRV-Tools/source/reduce/remove_instruction_reduction_opportunity.cpp
@@ -22,6 +22,18 @@
 bool RemoveInstructionReductionOpportunity::PreconditionHolds() { return true; }
 
 void RemoveInstructionReductionOpportunity::Apply() {
+  const uint32_t kNumEntryPointInOperandsBeforeInterfaceIds = 3;
+  for (auto& entry_point : inst_->context()->module()->entry_points()) {
+    opt::Instruction::OperandList new_entry_point_in_operands;
+    for (uint32_t index = 0; index < entry_point.NumInOperands(); index++) {
+      if (index >= kNumEntryPointInOperandsBeforeInterfaceIds &&
+          entry_point.GetSingleWordInOperand(index) == inst_->result_id()) {
+        continue;
+      }
+      new_entry_point_in_operands.push_back(entry_point.GetInOperand(index));
+    }
+    entry_point.SetInOperands(std::move(new_entry_point_in_operands));
+  }
   inst_->context()->KillInst(inst_);
 }
 
diff --git a/third_party/SPIRV-Tools/source/reduce/remove_struct_member_reduction_opportunity.cpp b/third_party/SPIRV-Tools/source/reduce/remove_struct_member_reduction_opportunity.cpp
new file mode 100644
index 0000000..787c629
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/reduce/remove_struct_member_reduction_opportunity.cpp
@@ -0,0 +1,208 @@
+// 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/reduce/remove_struct_member_reduction_opportunity.h"
+
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace reduce {
+
+bool RemoveStructMemberReductionOpportunity::PreconditionHolds() {
+  return struct_type_->NumInOperands() == original_number_of_members_;
+}
+
+void RemoveStructMemberReductionOpportunity::Apply() {
+  std::set<opt::Instruction*> decorations_to_kill;
+
+  // We need to remove decorations that target the removed struct member, and
+  // adapt decorations that target later struct members by decrementing the
+  // member identifier.  We also need to adapt composite construction
+  // instructions so that no id is provided for the member being removed.
+  //
+  // To do this, we consider every use of the struct type.
+  struct_type_->context()->get_def_use_mgr()->ForEachUse(
+      struct_type_, [this, &decorations_to_kill](opt::Instruction* user,
+                                                 uint32_t /*operand_index*/) {
+        switch (user->opcode()) {
+          case SpvOpCompositeConstruct:
+          case SpvOpConstantComposite:
+            // This use is constructing a composite of the struct type, so we
+            // must remove the id that was provided for the member we are
+            // removing.
+            user->RemoveInOperand(member_index_);
+            break;
+          case SpvOpMemberDecorate:
+            // This use is decorating a member of the struct.
+            if (user->GetSingleWordInOperand(1) == member_index_) {
+              // The member we are removing is being decorated, so we record
+              // that we need to get rid of the decoration.
+              decorations_to_kill.insert(user);
+            } else if (user->GetSingleWordInOperand(1) > member_index_) {
+              // A member beyond the one we are removing is being decorated, so
+              // we adjust the index that identifies the member.
+              user->SetInOperand(1, {user->GetSingleWordInOperand(1) - 1});
+            }
+            break;
+          default:
+            break;
+        }
+      });
+
+  // Get rid of all the decorations that were found to target the member being
+  // removed.
+  for (auto decoration_to_kill : decorations_to_kill) {
+    decoration_to_kill->context()->KillInst(decoration_to_kill);
+  }
+
+  // We now look through all instructions that access composites via sequences
+  // of indices. Every time we find an index into the struct whose member is
+  // being removed, and if the member being accessed comes after the member
+  // being removed, we need to adjust the index accordingly.
+  //
+  // We go through every relevant instruction in every block of every function,
+  // and invoke a helper to adjust it.
+  auto context = struct_type_->context();
+  for (auto& function : *context->module()) {
+    for (auto& block : function) {
+      for (auto& inst : block) {
+        switch (inst.opcode()) {
+          case SpvOpAccessChain:
+          case SpvOpInBoundsAccessChain: {
+            // These access chain instructions take sequences of ids for
+            // indexing, starting from input operand 1.
+            auto composite_type_id =
+                context->get_def_use_mgr()
+                    ->GetDef(context->get_def_use_mgr()
+                                 ->GetDef(inst.GetSingleWordInOperand(0))
+                                 ->type_id())
+                    ->GetSingleWordInOperand(1);
+            AdjustAccessedIndices(composite_type_id, 1, false, context, &inst);
+          } break;
+          case SpvOpPtrAccessChain:
+          case SpvOpInBoundsPtrAccessChain: {
+            // These access chain instructions take sequences of ids for
+            // indexing, starting from input operand 2.
+            auto composite_type_id =
+                context->get_def_use_mgr()
+                    ->GetDef(context->get_def_use_mgr()
+                                 ->GetDef(inst.GetSingleWordInOperand(1))
+                                 ->type_id())
+                    ->GetSingleWordInOperand(1);
+            AdjustAccessedIndices(composite_type_id, 2, false, context, &inst);
+          } break;
+          case SpvOpCompositeExtract: {
+            // OpCompositeExtract uses literals for indexing, starting at input
+            // operand 1.
+            auto composite_type_id =
+                context->get_def_use_mgr()
+                    ->GetDef(inst.GetSingleWordInOperand(0))
+                    ->type_id();
+            AdjustAccessedIndices(composite_type_id, 1, true, context, &inst);
+          } break;
+          case SpvOpCompositeInsert: {
+            // OpCompositeInsert uses literals for indexing, starting at input
+            // operand 2.
+            auto composite_type_id =
+                context->get_def_use_mgr()
+                    ->GetDef(inst.GetSingleWordInOperand(1))
+                    ->type_id();
+            AdjustAccessedIndices(composite_type_id, 2, true, context, &inst);
+          } break;
+          default:
+            break;
+        }
+      }
+    }
+  }
+
+  // Remove the member from the struct type.
+  struct_type_->RemoveInOperand(member_index_);
+}
+
+void RemoveStructMemberReductionOpportunity::AdjustAccessedIndices(
+    uint32_t composite_type_id, uint32_t first_index_input_operand,
+    bool literal_indices, opt::IRContext* context,
+    opt::Instruction* composite_access_instruction) const {
+  // Walk the series of types that are encountered by following the
+  // instruction's sequence of indices. For all types except structs, this is
+  // routine: the type of the composite dictates what the next type will be
+  // regardless of the specific index value.
+  uint32_t next_type = composite_type_id;
+  for (uint32_t i = first_index_input_operand;
+       i < composite_access_instruction->NumInOperands(); i++) {
+    auto type_inst = context->get_def_use_mgr()->GetDef(next_type);
+    switch (type_inst->opcode()) {
+      case SpvOpTypeArray:
+      case SpvOpTypeMatrix:
+      case SpvOpTypeRuntimeArray:
+      case SpvOpTypeVector:
+        next_type = type_inst->GetSingleWordInOperand(0);
+        break;
+      case SpvOpTypeStruct: {
+        // Struct types are special becuase (a) we may need to adjust the index
+        // being used, if the struct type is the one from which we are removing
+        // a member, and (b) the type encountered by following the current index
+        // is dependent on the value of the index.
+
+        // Work out the member being accessed.  If literal indexing is used this
+        // is simple; otherwise we need to look up the id of the constant
+        // instruction being used as an index and get the value of the constant.
+        uint32_t index_operand =
+            composite_access_instruction->GetSingleWordInOperand(i);
+        uint32_t member = literal_indices ? index_operand
+                                          : context->get_def_use_mgr()
+                                                ->GetDef(index_operand)
+                                                ->GetSingleWordInOperand(0);
+
+        // The next type we will consider is obtained by looking up the struct
+        // type at |member|.
+        next_type = type_inst->GetSingleWordInOperand(member);
+
+        if (type_inst == struct_type_ && member > member_index_) {
+          // The struct type is the struct from which we are removing a member,
+          // and the member being accessed is beyond the member we are removing.
+          // We thus need to decrement the index by 1.
+          uint32_t new_in_operand;
+          if (literal_indices) {
+            // With literal indexing this is straightforward.
+            new_in_operand = member - 1;
+          } else {
+            // With id-based indexing this is more tricky: we need to find or
+            // create a constant instruction whose value is one less than
+            // |member|, and use the id of this constant as the replacement
+            // input operand.
+            auto constant_inst =
+                context->get_def_use_mgr()->GetDef(index_operand);
+            auto int_type = context->get_type_mgr()
+                                ->GetType(constant_inst->type_id())
+                                ->AsInteger();
+            auto new_index_constant =
+                opt::analysis::IntConstant(int_type, {member - 1});
+            new_in_operand = context->get_constant_mgr()
+                                 ->GetDefiningInstruction(&new_index_constant)
+                                 ->result_id();
+          }
+          composite_access_instruction->SetInOperand(i, {new_in_operand});
+        }
+      } break;
+      default:
+        assert(0 && "Unknown composite type.");
+        break;
+    }
+  }
+}
+
+}  // namespace reduce
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/reduce/remove_struct_member_reduction_opportunity.h b/third_party/SPIRV-Tools/source/reduce/remove_struct_member_reduction_opportunity.h
new file mode 100644
index 0000000..899e5ea
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/reduce/remove_struct_member_reduction_opportunity.h
@@ -0,0 +1,84 @@
+// 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_REDUCE_REMOVE_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_H_
+#define SOURCE_REDUCE_REMOVE_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_H_
+
+#include "source/reduce/reduction_opportunity.h"
+
+#include "source/opt/instruction.h"
+
+namespace spvtools {
+namespace reduce {
+
+// An opportunity for removing a member from a struct type, adjusting all uses
+// of the struct accordingly.
+class RemoveStructMemberReductionOpportunity : public ReductionOpportunity {
+ public:
+  // Constructs a reduction opportunity from the struct type |struct_type|, for
+  // removal of member |member_index|.
+  RemoveStructMemberReductionOpportunity(opt::Instruction* struct_type,
+                                         uint32_t member_index)
+      : struct_type_(struct_type),
+        member_index_(member_index),
+        original_number_of_members_(struct_type->NumInOperands()) {}
+
+  // Opportunities to remove fields from a common struct type mutually
+  // invalidate each other.  We guard against this by requiring that the struct
+  // still has the number of members it had when the opportunity was created.
+  bool PreconditionHolds() override;
+
+ protected:
+  void Apply() override;
+
+ private:
+  // |composite_access_instruction| is an instruction that accesses a composite
+  // id using either a series of literal indices (e.g. in the case of
+  // OpCompositeInsert) or a series of index ids (e.g. in the case of
+  // OpAccessChain).
+  //
+  // This function adjusts the indices that are used by
+  // |composite_access_instruction| to that whenever an index is accessing a
+  // member of |struct_type_|, it is decremented if the member is beyond
+  // |member_index_|, to account for the removal of the |member_index_|-th
+  // member.
+  //
+  // |composite_type_id| is the id of the composite type that the series of
+  // indices is to be applied to.
+  //
+  // |first_index_input_operand| specifies the first input operand that is an
+  // index.
+  //
+  // |literal_indices| specifies whether indices are given as literals (true),
+  // or as ids (false).
+  //
+  // If id-based indexing is used, this function will add a constant for
+  // |member_index_| - 1 to the module if needed.
+  void AdjustAccessedIndices(
+      uint32_t composite_type_id, uint32_t first_index_input_operand,
+      bool literal_indices, opt::IRContext* context,
+      opt::Instruction* composite_access_instruction) const;
+
+  // The struct type from which a member is to be removed.
+  opt::Instruction* struct_type_;
+
+  uint32_t member_index_;
+
+  uint32_t original_number_of_members_;
+};
+
+}  // namespace reduce
+}  // namespace spvtools
+
+#endif  //   SOURCE_REDUCE_REMOVE_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_H_
diff --git a/third_party/SPIRV-Tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h b/third_party/SPIRV-Tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h
deleted file mode 100644
index bc4f137..0000000
--- a/third_party/SPIRV-Tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (c) 2018 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
-#define SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
-
-#include "source/reduce/reduction_opportunity_finder.h"
-
-namespace spvtools {
-namespace reduce {
-
-// A finder for opportunities to remove non-control-flow instructions in blocks
-// in cases where the instruction's id is not referenced.  As well as making the
-// module smaller, removing an instruction that references particular ids may
-// create opportunities for subsequently removing the instructions that
-// generated those ids.
-class RemoveUnreferencedInstructionReductionOpportunityFinder
-    : public ReductionOpportunityFinder {
- public:
-  explicit RemoveUnreferencedInstructionReductionOpportunityFinder(
-      bool remove_constants_and_undefs);
-
-  ~RemoveUnreferencedInstructionReductionOpportunityFinder() override = default;
-
-  std::string GetName() const final;
-
-  std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
-      opt::IRContext* context) const final;
-
- private:
-  bool remove_constants_and_undefs_;
-};
-
-}  // namespace reduce
-}  // namespace spvtools
-
-#endif  // SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
diff --git a/third_party/SPIRV-Tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp b/third_party/SPIRV-Tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp
similarity index 60%
rename from third_party/SPIRV-Tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
rename to third_party/SPIRV-Tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp
index ce66691..91ec542 100644
--- a/third_party/SPIRV-Tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
+++ b/third_party/SPIRV-Tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
+#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h"
 
 #include "source/opcode.h"
 #include "source/opt/instruction.h"
@@ -21,14 +21,14 @@
 namespace spvtools {
 namespace reduce {
 
-RemoveUnreferencedInstructionReductionOpportunityFinder::
-    RemoveUnreferencedInstructionReductionOpportunityFinder(
+RemoveUnusedInstructionReductionOpportunityFinder::
+    RemoveUnusedInstructionReductionOpportunityFinder(
         bool remove_constants_and_undefs)
     : remove_constants_and_undefs_(remove_constants_and_undefs) {}
 
 std::vector<std::unique_ptr<ReductionOpportunity>>
-RemoveUnreferencedInstructionReductionOpportunityFinder::
-    GetAvailableOpportunities(opt::IRContext* context) const {
+RemoveUnusedInstructionReductionOpportunityFinder::GetAvailableOpportunities(
+    opt::IRContext* context) const {
   std::vector<std::unique_ptr<ReductionOpportunity>> result;
 
   for (auto& inst : context->module()->debugs1()) {
@@ -60,13 +60,14 @@
   }
 
   for (auto& inst : context->module()->types_values()) {
-    if (context->get_def_use_mgr()->NumUsers(&inst) > 0) {
-      continue;
-    }
     if (!remove_constants_and_undefs_ &&
         spvOpcodeIsConstantOrUndef(inst.opcode())) {
       continue;
     }
+    if (!OnlyReferencedByIntimateDecorationOrEntryPointInterface(context,
+                                                                 inst)) {
+      continue;
+    }
     result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
   }
 
@@ -74,38 +75,9 @@
     if (context->get_def_use_mgr()->NumUsers(&inst) > 0) {
       continue;
     }
-
-    uint32_t decoration = SpvDecorationMax;
-    switch (inst.opcode()) {
-      case SpvOpDecorate:
-      case SpvOpDecorateId:
-      case SpvOpDecorateString:
-        decoration = inst.GetSingleWordInOperand(1u);
-        break;
-      case SpvOpMemberDecorate:
-      case SpvOpMemberDecorateString:
-        decoration = inst.GetSingleWordInOperand(2u);
-        break;
-      default:
-        break;
+    if (!IsIndependentlyRemovableDecoration(inst)) {
+      continue;
     }
-
-    // We conservatively only remove specific decorations that we believe will
-    // not change the shader interface, will not make the shader invalid, will
-    // actually be found in practice, etc.
-
-    switch (decoration) {
-      case SpvDecorationRelaxedPrecision:
-      case SpvDecorationNoSignedWrap:
-      case SpvDecorationNoContraction:
-      case SpvDecorationNoUnsignedWrap:
-      case SpvDecorationUserSemantic:
-        break;
-      default:
-        // Give up.
-        continue;
-    }
-
     result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
   }
 
@@ -139,9 +111,54 @@
   return result;
 }
 
-std::string RemoveUnreferencedInstructionReductionOpportunityFinder::GetName()
-    const {
-  return "RemoveUnreferencedInstructionReductionOpportunityFinder";
+std::string RemoveUnusedInstructionReductionOpportunityFinder::GetName() const {
+  return "RemoveUnusedInstructionReductionOpportunityFinder";
+}
+
+bool RemoveUnusedInstructionReductionOpportunityFinder::
+    OnlyReferencedByIntimateDecorationOrEntryPointInterface(
+        opt::IRContext* context, const opt::Instruction& inst) const {
+  return context->get_def_use_mgr()->WhileEachUse(
+      &inst, [this](opt::Instruction* user, uint32_t use_index) -> bool {
+        return (user->IsDecoration() &&
+                !IsIndependentlyRemovableDecoration(*user)) ||
+               (user->opcode() == SpvOpEntryPoint && use_index > 2);
+      });
+}
+
+bool RemoveUnusedInstructionReductionOpportunityFinder::
+    IsIndependentlyRemovableDecoration(const opt::Instruction& inst) const {
+  uint32_t decoration;
+  switch (inst.opcode()) {
+    case SpvOpDecorate:
+    case SpvOpDecorateId:
+    case SpvOpDecorateString:
+      decoration = inst.GetSingleWordInOperand(1u);
+      break;
+    case SpvOpMemberDecorate:
+    case SpvOpMemberDecorateString:
+      decoration = inst.GetSingleWordInOperand(2u);
+      break;
+    default:
+      // The instruction is not a decoration.  It is legitimate for this to be
+      // reached: it allows the method to be invoked on arbitrary instructions.
+      return false;
+  }
+
+  // We conservatively only remove specific decorations that we believe will
+  // not change the shader interface, will not make the shader invalid, will
+  // actually be found in practice, etc.
+
+  switch (decoration) {
+    case SpvDecorationRelaxedPrecision:
+    case SpvDecorationNoSignedWrap:
+    case SpvDecorationNoContraction:
+    case SpvDecorationNoUnsignedWrap:
+    case SpvDecorationUserSemantic:
+      return true;
+    default:
+      return false;
+  }
 }
 
 }  // namespace reduce
diff --git a/third_party/SPIRV-Tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.h b/third_party/SPIRV-Tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.h
new file mode 100644
index 0000000..cbf6a5b
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
+#define SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
+
+#include "source/reduce/reduction_opportunity_finder.h"
+
+namespace spvtools {
+namespace reduce {
+
+// A finder for opportunities to remove non-control-flow instructions in blocks
+// in cases where the instruction's id is either not referenced at all, or
+// referenced only in a trivial manner (for example, we regard a struct type as
+// unused if it is referenced only by struct layout decorations).  As well as
+// making the module smaller, removing an instruction that references particular
+// ids may create opportunities for subsequently removing the instructions that
+// generated those ids.
+class RemoveUnusedInstructionReductionOpportunityFinder
+    : public ReductionOpportunityFinder {
+ public:
+  explicit RemoveUnusedInstructionReductionOpportunityFinder(
+      bool remove_constants_and_undefs);
+
+  ~RemoveUnusedInstructionReductionOpportunityFinder() override = default;
+
+  std::string GetName() const final;
+
+  std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
+      opt::IRContext* context) const final;
+
+ private:
+  // Returns true if and only if the only uses of |inst| are by decorations that
+  // relate intimately to the instruction (as opposed to decorations that could
+  // be removed independently), or by interface ids in OpEntryPoint.
+  bool OnlyReferencedByIntimateDecorationOrEntryPointInterface(
+      opt::IRContext* context, const opt::Instruction& inst) const;
+
+  // Returns true if and only if |inst| is a decoration instruction that can
+  // legitimately be removed on its own (rather than one that has to be removed
+  // simultaneously with other instructions).
+  bool IsIndependentlyRemovableDecoration(const opt::Instruction& inst) const;
+
+  bool remove_constants_and_undefs_;
+};
+
+}  // namespace reduce
+}  // namespace spvtools
+
+#endif  // SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
diff --git a/third_party/SPIRV-Tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp b/third_party/SPIRV-Tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp
new file mode 100644
index 0000000..39ce47f
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp
@@ -0,0 +1,193 @@
+// 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/reduce/remove_unused_struct_member_reduction_opportunity_finder.h"
+
+#include <map>
+#include <set>
+
+#include "source/reduce/remove_struct_member_reduction_opportunity.h"
+
+namespace spvtools {
+namespace reduce {
+
+std::vector<std::unique_ptr<ReductionOpportunity>>
+RemoveUnusedStructMemberReductionOpportunityFinder::GetAvailableOpportunities(
+    opt::IRContext* context) const {
+  std::vector<std::unique_ptr<ReductionOpportunity>> result;
+
+  // We track those struct members that are never accessed.  We do this by
+  // associating a member index to all the structs that have this member index
+  // but do not use it.  This representation is designed to allow reduction
+  // opportunities to be provided in a useful manner, so that opportunities
+  // associated with the same struct are unlikely to be adjacent.
+  std::map<uint32_t, std::set<opt::Instruction*>> unused_member_to_structs;
+
+  // Consider every struct type in the module.
+  for (auto& type_or_value : context->types_values()) {
+    if (type_or_value.opcode() != SpvOpTypeStruct) {
+      continue;
+    }
+
+    // Initially, we assume that *every* member of the struct is unused.  We
+    // then refine this based on observed uses.
+    std::set<uint32_t> unused_members;
+    for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) {
+      unused_members.insert(i);
+    }
+
+    // A separate reduction pass deals with removal of names.  If a struct
+    // member is still named, we treat it as being used.
+    context->get_def_use_mgr()->ForEachUse(
+        &type_or_value,
+        [&unused_members](opt::Instruction* user, uint32_t /*operand_index*/) {
+          switch (user->opcode()) {
+            case SpvOpMemberName:
+              unused_members.erase(user->GetSingleWordInOperand(1));
+              break;
+            default:
+              break;
+          }
+        });
+
+    for (uint32_t member : unused_members) {
+      if (!unused_member_to_structs.count(member)) {
+        unused_member_to_structs.insert(
+            {member, std::set<opt::Instruction*>()});
+      }
+      unused_member_to_structs.at(member).insert(&type_or_value);
+    }
+  }
+
+  // We now go through every instruction that might index into a struct, and
+  // refine our tracking of which struct members are used based on the struct
+  // indexing we observe.  We cannot just go through all uses of a struct type
+  // because the type is not necessarily even referenced, e.g. when walking
+  // arrays of structs.
+  for (auto& function : *context->module()) {
+    for (auto& block : function) {
+      for (auto& inst : block) {
+        switch (inst.opcode()) {
+          // For each indexing operation we observe, we invoke a helper to
+          // remove from our map those struct indices that are found to be used.
+          // The way the helper is invoked depends on whether the instruction
+          // uses literal or id indices, and the offset into the instruction's
+          // input operands from which index operands are provided.
+          case SpvOpAccessChain:
+          case SpvOpInBoundsAccessChain: {
+            auto composite_type_id =
+                context->get_def_use_mgr()
+                    ->GetDef(context->get_def_use_mgr()
+                                 ->GetDef(inst.GetSingleWordInOperand(0))
+                                 ->type_id())
+                    ->GetSingleWordInOperand(1);
+            MarkAccessedMembersAsUsed(context, composite_type_id, 1, false,
+                                      inst, &unused_member_to_structs);
+          } break;
+          case SpvOpPtrAccessChain:
+          case SpvOpInBoundsPtrAccessChain: {
+            auto composite_type_id =
+                context->get_def_use_mgr()
+                    ->GetDef(context->get_def_use_mgr()
+                                 ->GetDef(inst.GetSingleWordInOperand(1))
+                                 ->type_id())
+                    ->GetSingleWordInOperand(1);
+            MarkAccessedMembersAsUsed(context, composite_type_id, 2, false,
+                                      inst, &unused_member_to_structs);
+          } break;
+          case SpvOpCompositeExtract: {
+            auto composite_type_id =
+                context->get_def_use_mgr()
+                    ->GetDef(inst.GetSingleWordInOperand(0))
+                    ->type_id();
+            MarkAccessedMembersAsUsed(context, composite_type_id, 1, true, inst,
+                                      &unused_member_to_structs);
+          } break;
+          case SpvOpCompositeInsert: {
+            auto composite_type_id =
+                context->get_def_use_mgr()
+                    ->GetDef(inst.GetSingleWordInOperand(1))
+                    ->type_id();
+            MarkAccessedMembersAsUsed(context, composite_type_id, 2, true, inst,
+                                      &unused_member_to_structs);
+          } break;
+          default:
+            break;
+        }
+      }
+    }
+  }
+
+  // We now know those struct indices that are unsed, and we make a reduction
+  // opportunity for each of them. By mapping each relevant member index to the
+  // structs in which it is unsed, we will group all opportunities to remove
+  // member k of a struct (for some k) together.  This reduces the likelihood
+  // that opportunities to remove members from the same struct will be adjacent,
+  // which is good because such opportunities mutually disable one another.
+  for (auto& entry : unused_member_to_structs) {
+    for (auto struct_type : entry.second) {
+      result.push_back(MakeUnique<RemoveStructMemberReductionOpportunity>(
+          struct_type, entry.first));
+    }
+  }
+  return result;
+}
+
+void RemoveUnusedStructMemberReductionOpportunityFinder::
+    MarkAccessedMembersAsUsed(
+        opt::IRContext* context, uint32_t composite_type_id,
+        uint32_t first_index_in_operand, bool literal_indices,
+        const opt::Instruction& composite_access_instruction,
+        std::map<uint32_t, std::set<opt::Instruction*>>*
+            unused_member_to_structs) const {
+  uint32_t next_type = composite_type_id;
+  for (uint32_t i = first_index_in_operand;
+       i < composite_access_instruction.NumInOperands(); i++) {
+    auto type_inst = context->get_def_use_mgr()->GetDef(next_type);
+    switch (type_inst->opcode()) {
+      case SpvOpTypeArray:
+      case SpvOpTypeMatrix:
+      case SpvOpTypeRuntimeArray:
+      case SpvOpTypeVector:
+        next_type = type_inst->GetSingleWordInOperand(0);
+        break;
+      case SpvOpTypeStruct: {
+        uint32_t index_operand =
+            composite_access_instruction.GetSingleWordInOperand(i);
+        uint32_t member = literal_indices ? index_operand
+                                          : context->get_def_use_mgr()
+                                                ->GetDef(index_operand)
+                                                ->GetSingleWordInOperand(0);
+        // Remove the struct type from the struct types associated with this
+        // member index, but only if a set of struct types is known to be
+        // associated with this member index.
+        if (unused_member_to_structs->count(member)) {
+          unused_member_to_structs->at(member).erase(type_inst);
+        }
+        next_type = type_inst->GetSingleWordInOperand(member);
+      } break;
+      default:
+        assert(0 && "Unknown composite type.");
+        break;
+    }
+  }
+}
+
+std::string RemoveUnusedStructMemberReductionOpportunityFinder::GetName()
+    const {
+  return "RemoveUnusedStructMemberReductionOpportunityFinder";
+}
+
+}  // namespace reduce
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h b/third_party/SPIRV-Tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h
new file mode 100644
index 0000000..13f4017
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_REDUCE_REMOVE_UNUSED_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_FINDER_H_
+#define SOURCE_REDUCE_REMOVE_UNUSED_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_FINDER_H_
+
+#include "source/reduce/reduction_opportunity_finder.h"
+
+namespace spvtools {
+namespace reduce {
+
+// A finder for opportunities to remove struct members that are not explicitly
+// used by extract, insert or access chain instructions.
+class RemoveUnusedStructMemberReductionOpportunityFinder
+    : public ReductionOpportunityFinder {
+ public:
+  RemoveUnusedStructMemberReductionOpportunityFinder() = default;
+
+  ~RemoveUnusedStructMemberReductionOpportunityFinder() override = default;
+
+  std::string GetName() const final;
+
+  std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
+      opt::IRContext* context) const final;
+
+ private:
+  // A helper method to update |unused_members_to_structs| by removing from it
+  // all struct member accesses that take place in
+  // |composite_access_instruction|.
+  //
+  // |composite_type_id| is the type of the root object indexed into by the
+  // instruction.
+  //
+  // |first_index_in_operand| provides indicates where in the input operands the
+  // sequence of indices begins.
+  //
+  // |literal_indices| indicates whether indices are literals (true) or ids
+  // (false).
+  void MarkAccessedMembersAsUsed(
+      opt::IRContext* context, uint32_t composite_type_id,
+      uint32_t first_index_in_operand, bool literal_indices,
+      const opt::Instruction& composite_access_instruction,
+      std::map<uint32_t, std::set<opt::Instruction*>>* unused_member_to_structs)
+      const;
+};
+
+}  // namespace reduce
+}  // namespace spvtools
+
+#endif  // SOURCE_REDUCE_REMOVE_UNUSED_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_FINDER_H_
diff --git a/third_party/SPIRV-Tools/source/spirv_reducer_options.cpp b/third_party/SPIRV-Tools/source/spirv_reducer_options.cpp
index 5801d0a..e807875 100644
--- a/third_party/SPIRV-Tools/source/spirv_reducer_options.cpp
+++ b/third_party/SPIRV-Tools/source/spirv_reducer_options.cpp
@@ -19,7 +19,7 @@
 
 namespace {
 // The default maximum number of steps the reducer will take before giving up.
-const uint32_t kDefaultStepLimit = 250;
+const uint32_t kDefaultStepLimit = 2500;
 }  // namespace
 
 spv_reducer_options_t::spv_reducer_options_t()
diff --git a/third_party/SPIRV-Tools/source/val/validate_cfg.cpp b/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
index 1c279f6..1e33e51 100644
--- a/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
@@ -1090,8 +1090,9 @@
         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);
+      break;
     }
-    // Fallthrough.
     case SpvOpKill:
     case SpvOpReturnValue:
     case SpvOpUnreachable:
diff --git a/third_party/SPIRV-Tools/source/val/validate_decorations.cpp b/third_party/SPIRV-Tools/source/val/validate_decorations.cpp
index 3b44833..ce09e18 100644
--- a/third_party/SPIRV-Tools/source/val/validate_decorations.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_decorations.cpp
@@ -1524,6 +1524,22 @@
   return SPV_SUCCESS;
 }
 
+// Returns SPV_SUCCESS if validation rules are satisfied for the Block
+// decoration.  Otherwise emits a diagnostic and returns something other than
+// SPV_SUCCESS.
+spv_result_t CheckBlockDecoration(ValidationState_t& vstate,
+                                  const Instruction& inst,
+                                  const Decoration& decoration) {
+  assert(inst.id() && "Parser ensures the target of the decoration has an ID");
+  if (inst.opcode() != SpvOpTypeStruct) {
+    const char* const dec_name =
+        decoration.dec_type() == SpvDecorationBlock ? "Block" : "BufferBlock";
+    return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+           << dec_name << " decoration on a non-struct type.";
+  }
+  return SPV_SUCCESS;
+}
+
 #define PASS_OR_BAIL_AT_LINE(X, LINE)           \
   {                                             \
     spv_result_t e##LINE = (X);                 \
@@ -1570,6 +1586,10 @@
         case SpvDecorationNoUnsignedWrap:
           PASS_OR_BAIL(CheckIntegerWrapDecoration(vstate, *inst, decoration));
           break;
+        case SpvDecorationBlock:
+        case SpvDecorationBufferBlock:
+          PASS_OR_BAIL(CheckBlockDecoration(vstate, *inst, decoration));
+          break;
         default:
           break;
       }
diff --git a/third_party/SPIRV-Tools/source/val/validate_extensions.cpp b/third_party/SPIRV-Tools/source/val/validate_extensions.cpp
index 1e311c1..7ce681c 100644
--- a/third_party/SPIRV-Tools/source/val/validate_extensions.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_extensions.cpp
@@ -2300,7 +2300,14 @@
             ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
         if (validate_parent != SPV_SUCCESS) return validate_parent;
         CHECK_OPERAND("Linkage Name", SpvOpString, 11);
-        CHECK_OPERAND("Size", SpvOpConstant, 12);
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](OpenCLDebugInfo100Instructions dbg_inst) {
+                  return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
+                },
+                inst, 12)) {
+          CHECK_OPERAND("Size", SpvOpConstant, 12);
+        }
         for (uint32_t word_index = 14; word_index < num_words; ++word_index) {
           if (!DoesDebugInfoOperandMatchExpectation(
                   _,
diff --git a/third_party/SPIRV-Tools/source/val/validate_function.cpp b/third_party/SPIRV-Tools/source/val/validate_function.cpp
index f130eac..596186b 100644
--- a/third_party/SPIRV-Tools/source/val/validate_function.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_function.cpp
@@ -71,6 +71,7 @@
   }
 
   const std::vector<SpvOp> acceptable = {
+      SpvOpGroupDecorate,
       SpvOpDecorate,
       SpvOpEnqueueKernel,
       SpvOpEntryPoint,
diff --git a/third_party/SPIRV-Tools/source/val/validate_image.cpp b/third_party/SPIRV-Tools/source/val/validate_image.cpp
index 5b77058..9ce74a3 100644
--- a/third_party/SPIRV-Tools/source/val/validate_image.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_image.cpp
@@ -160,6 +160,17 @@
   }
 }
 
+bool IsValidGatherLodBiasAMD(const ValidationState_t& _, SpvOp opcode) {
+  switch (opcode) {
+    case SpvOpImageGather:
+    case SpvOpImageSparseGather:
+      return _.HasCapability(SpvCapabilityImageGatherBiasLodAMD);
+    default:
+      break;
+  }
+  return false;
+}
+
 // Returns true if the opcode is a Image instruction which applies
 // homogenous projection to the coordinates.
 bool IsProj(SpvOp opcode) {
@@ -260,11 +271,12 @@
   const bool is_implicit_lod = IsImplicitLod(opcode);
   const bool is_explicit_lod = IsExplicitLod(opcode);
   const bool is_valid_lod_operand = IsValidLodOperand(_, opcode);
+  const bool is_valid_gather_lod_bias_amd = IsValidGatherLodBiasAMD(_, opcode);
 
   // The checks should be done in the order of definition of OperandImage.
 
   if (mask & SpvImageOperandsBiasMask) {
-    if (!is_implicit_lod) {
+    if (!is_implicit_lod && !is_valid_gather_lod_bias_amd) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand Bias can only be used with ImplicitLod opcodes";
     }
@@ -290,7 +302,7 @@
 
   if (mask & SpvImageOperandsLodMask) {
     if (!is_valid_lod_operand && opcode != SpvOpImageFetch &&
-        opcode != SpvOpImageSparseFetch) {
+        opcode != SpvOpImageSparseFetch && !is_valid_gather_lod_bias_amd) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand Lod can only be used with ExplicitLod opcodes "
              << "and OpImageFetch";
@@ -303,7 +315,7 @@
     }
 
     const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
-    if (is_explicit_lod) {
+    if (is_explicit_lod || is_valid_gather_lod_bias_amd) {
       if (!_.IsFloatScalarType(type_id)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected Image Operand Lod to be float scalar when used "
diff --git a/third_party/SPIRV-Tools/source/val/validate_scopes.cpp b/third_party/SPIRV-Tools/source/val/validate_scopes.cpp
index ea3ebcb..a6fb26d 100644
--- a/third_party/SPIRV-Tools/source/val/validate_scopes.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_scopes.cpp
@@ -230,11 +230,33 @@
     if ((_.context()->target_env == SPV_ENV_VULKAN_1_1 ||
          _.context()->target_env == SPV_ENV_VULKAN_1_2) &&
         value != SpvScopeDevice && value != SpvScopeWorkgroup &&
-        value != SpvScopeSubgroup && value != SpvScopeInvocation) {
+        value != SpvScopeSubgroup && value != SpvScopeInvocation &&
+        value != SpvScopeShaderCallKHR) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << spvOpcodeString(opcode)
              << ": in Vulkan 1.1 and 1.2 environment Memory Scope is limited "
-             << "to Device, Workgroup and Invocation";
+             << "to Device, Workgroup, Invocation, and ShaderCall";
+    }
+
+    if (value == SpvScopeShaderCallKHR) {
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [](SpvExecutionModel model, std::string* message) {
+                if (model != SpvExecutionModelRayGenerationKHR &&
+                    model != SpvExecutionModelIntersectionKHR &&
+                    model != SpvExecutionModelAnyHitKHR &&
+                    model != SpvExecutionModelClosestHitKHR &&
+                    model != SpvExecutionModelMissKHR &&
+                    model != SpvExecutionModelCallableKHR) {
+                  if (message) {
+                    *message =
+                        "ShaderCallKHR Memory Scope requires a ray tracing "
+                        "execution model";
+                  }
+                  return false;
+                }
+                return true;
+              });
     }
   }
 
diff --git a/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt b/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
index 99a78fd..dca142a 100644
--- a/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
@@ -21,12 +21,13 @@
           equivalence_relation_test.cpp
           fact_manager_test.cpp
           fuzz_test_util.cpp
-          fuzzer_pass_add_useful_constructs_test.cpp
+          fuzzer_pass_construct_composites_test.cpp
           fuzzer_pass_donate_modules_test.cpp
           instruction_descriptor_test.cpp
           transformation_access_chain_test.cpp
           transformation_add_constant_boolean_test.cpp
           transformation_add_constant_composite_test.cpp
+          transformation_add_constant_null_test.cpp
           transformation_add_constant_scalar_test.cpp
           transformation_add_dead_block_test.cpp
           transformation_add_dead_break_test.cpp
@@ -45,8 +46,10 @@
           transformation_add_type_pointer_test.cpp
           transformation_add_type_struct_test.cpp
           transformation_add_type_vector_test.cpp
+          transformation_adjust_branch_weights_test.cpp
           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
diff --git a/third_party/SPIRV-Tools/test/fuzz/data_synonym_transformation_test.cpp b/third_party/SPIRV-Tools/test/fuzz/data_synonym_transformation_test.cpp
index 21ea068..66ce769 100644
--- a/third_party/SPIRV-Tools/test/fuzz/data_synonym_transformation_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/data_synonym_transformation_test.cpp
@@ -123,13 +123,24 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
-  fact_manager.AddFact(MakeSynonymFact(12, {}, 100, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(13, {}, 100, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(22, {}, 100, {2}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(28, {}, 101, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(23, {}, 101, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(32, {}, 101, {2}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(23, {}, 101, {3}), context.get());
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(12, {}, 100, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(13, {}, 100, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(22, {}, 100, {2}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(28, {}, 101, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(23, {}, 101, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(32, {}, 101, {2}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(23, {}, 101, {3}), context.get());
 
   // Replace %12 with %100[0] in '%25 = OpAccessChain %24 %20 %12'
   auto instruction_descriptor_1 =
@@ -139,13 +150,16 @@
   // Bad: id already in use
   auto bad_extract_1 = TransformationCompositeExtract(
       MakeInstructionDescriptor(25, SpvOpAccessChain, 0), 25, 100, {0});
-  ASSERT_TRUE(good_extract_1.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(bad_extract_1.IsApplicable(context.get(), fact_manager));
-  good_extract_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      good_extract_1.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      bad_extract_1.IsApplicable(context.get(), transformation_context));
+  good_extract_1.Apply(context.get(), &transformation_context);
   auto replacement_1 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(12, instruction_descriptor_1, 1), 102);
-  ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
-  replacement_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_1.IsApplicable(context.get(), transformation_context));
+  replacement_1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %13 with %100[1] in 'OpStore %15 %13'
@@ -153,12 +167,14 @@
   auto good_extract_2 =
       TransformationCompositeExtract(instruction_descriptor_2, 103, 100, {1});
   // No bad example provided here.
-  ASSERT_TRUE(good_extract_2.IsApplicable(context.get(), fact_manager));
-  good_extract_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      good_extract_2.IsApplicable(context.get(), transformation_context));
+  good_extract_2.Apply(context.get(), &transformation_context);
   auto replacement_2 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(13, instruction_descriptor_2, 1), 103);
-  ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
-  replacement_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_2.IsApplicable(context.get(), transformation_context));
+  replacement_2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %22 with %100[2] in '%23 = OpConvertSToF %16 %22'
@@ -166,16 +182,19 @@
       MakeInstructionDescriptor(23, SpvOpConvertSToF, 0);
   auto good_extract_3 =
       TransformationCompositeExtract(instruction_descriptor_3, 104, 100, {2});
-  ASSERT_TRUE(good_extract_3.IsApplicable(context.get(), fact_manager));
-  good_extract_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      good_extract_3.IsApplicable(context.get(), transformation_context));
+  good_extract_3.Apply(context.get(), &transformation_context);
   auto replacement_3 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(22, instruction_descriptor_3, 0), 104);
   // Bad: wrong input operand index
   auto bad_replacement_3 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(22, instruction_descriptor_3, 1), 104);
-  ASSERT_TRUE(replacement_3.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(bad_replacement_3.IsApplicable(context.get(), fact_manager));
-  replacement_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_3.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      bad_replacement_3.IsApplicable(context.get(), transformation_context));
+  replacement_3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %28 with %101[0] in 'OpStore %33 %28'
@@ -185,13 +204,16 @@
   // Bad: instruction descriptor does not identify an appropriate instruction
   auto bad_extract_4 = TransformationCompositeExtract(
       MakeInstructionDescriptor(33, SpvOpCopyObject, 0), 105, 101, {0});
-  ASSERT_TRUE(good_extract_4.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(bad_extract_4.IsApplicable(context.get(), fact_manager));
-  good_extract_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      good_extract_4.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      bad_extract_4.IsApplicable(context.get(), transformation_context));
+  good_extract_4.Apply(context.get(), &transformation_context);
   auto replacement_4 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(28, instruction_descriptor_4, 1), 105);
-  ASSERT_TRUE(replacement_4.IsApplicable(context.get(), fact_manager));
-  replacement_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_4.IsApplicable(context.get(), transformation_context));
+  replacement_4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %23 with %101[1] in '%50 = OpCopyObject %16 %23'
@@ -199,16 +221,19 @@
       MakeInstructionDescriptor(50, SpvOpCopyObject, 0);
   auto good_extract_5 =
       TransformationCompositeExtract(instruction_descriptor_5, 106, 101, {1});
-  ASSERT_TRUE(good_extract_5.IsApplicable(context.get(), fact_manager));
-  good_extract_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      good_extract_5.IsApplicable(context.get(), transformation_context));
+  good_extract_5.Apply(context.get(), &transformation_context);
   auto replacement_5 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(23, instruction_descriptor_5, 0), 106);
   // Bad: wrong synonym fact being used
   auto bad_replacement_5 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(23, instruction_descriptor_5, 0), 105);
-  ASSERT_TRUE(replacement_5.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(bad_replacement_5.IsApplicable(context.get(), fact_manager));
-  replacement_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_5.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      bad_replacement_5.IsApplicable(context.get(), transformation_context));
+  replacement_5.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %32 with %101[2] in 'OpStore %33 %32'
@@ -218,13 +243,16 @@
   // Bad: id 1001 does not exist
   auto bad_extract_6 =
       TransformationCompositeExtract(instruction_descriptor_6, 107, 1001, {2});
-  ASSERT_TRUE(good_extract_6.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(bad_extract_6.IsApplicable(context.get(), fact_manager));
-  good_extract_6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      good_extract_6.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      bad_extract_6.IsApplicable(context.get(), transformation_context));
+  good_extract_6.Apply(context.get(), &transformation_context);
   auto replacement_6 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(32, instruction_descriptor_6, 1), 107);
-  ASSERT_TRUE(replacement_6.IsApplicable(context.get(), fact_manager));
-  replacement_6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_6.IsApplicable(context.get(), transformation_context));
+  replacement_6.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %23 with %101[3] in '%51 = OpCopyObject %16 %23'
@@ -232,16 +260,19 @@
       MakeInstructionDescriptor(51, SpvOpCopyObject, 0);
   auto good_extract_7 =
       TransformationCompositeExtract(instruction_descriptor_7, 108, 101, {3});
-  ASSERT_TRUE(good_extract_7.IsApplicable(context.get(), fact_manager));
-  good_extract_7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      good_extract_7.IsApplicable(context.get(), transformation_context));
+  good_extract_7.Apply(context.get(), &transformation_context);
   auto replacement_7 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(23, instruction_descriptor_7, 0), 108);
   // Bad: use id 0 is invalid
   auto bad_replacement_7 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(0, instruction_descriptor_7, 0), 108);
-  ASSERT_TRUE(replacement_7.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(bad_replacement_7.IsApplicable(context.get(), fact_manager));
-  replacement_7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_7.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      bad_replacement_7.IsApplicable(context.get(), transformation_context));
+  replacement_7.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   const std::string after_transformation = R"(
@@ -380,32 +411,41 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
-  fact_manager.AddFact(MakeSynonymFact(23, {}, 100, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(25, {}, 100, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(50, {}, 100, {2}), context.get());
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(23, {}, 100, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(25, {}, 100, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(50, {}, 100, {2}), context.get());
 
   // Replace %23 with %100[0] in '%26 = OpFAdd %7 %23 %25'
   auto instruction_descriptor_1 = MakeInstructionDescriptor(26, SpvOpFAdd, 0);
   auto extract_1 =
       TransformationCompositeExtract(instruction_descriptor_1, 101, 100, {0});
-  ASSERT_TRUE(extract_1.IsApplicable(context.get(), fact_manager));
-  extract_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_1.IsApplicable(context.get(), transformation_context));
+  extract_1.Apply(context.get(), &transformation_context);
   auto replacement_1 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(23, instruction_descriptor_1, 0), 101);
-  ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
-  replacement_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_1.IsApplicable(context.get(), transformation_context));
+  replacement_1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %25 with %100[1] in '%26 = OpFAdd %7 %23 %25'
   auto instruction_descriptor_2 = MakeInstructionDescriptor(26, SpvOpFAdd, 0);
   auto extract_2 =
       TransformationCompositeExtract(instruction_descriptor_2, 102, 100, {1});
-  ASSERT_TRUE(extract_2.IsApplicable(context.get(), fact_manager));
-  extract_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_2.IsApplicable(context.get(), transformation_context));
+  extract_2.Apply(context.get(), &transformation_context);
   auto replacement_2 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(25, instruction_descriptor_2, 1), 102);
-  ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
-  replacement_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_2.IsApplicable(context.get(), transformation_context));
+  replacement_2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   const std::string after_transformation = R"(
@@ -541,26 +581,37 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  fact_manager.AddFact(MakeSynonymFact(16, {}, 100, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(45, {}, 100, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(27, {}, 101, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(36, {}, 101, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(27, {}, 101, {2}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(22, {}, 102, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(15, {}, 102, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(16, {}, 100, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(45, {}, 100, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(27, {}, 101, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(36, {}, 101, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(27, {}, 101, {2}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(22, {}, 102, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(15, {}, 102, {1}), context.get());
 
   // Replace %45 with %100[1] in '%46 = OpCompositeConstruct %32 %35 %45'
   auto instruction_descriptor_1 =
       MakeInstructionDescriptor(46, SpvOpCompositeConstruct, 0);
   auto extract_1 =
       TransformationCompositeExtract(instruction_descriptor_1, 201, 100, {1});
-  ASSERT_TRUE(extract_1.IsApplicable(context.get(), fact_manager));
-  extract_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_1.IsApplicable(context.get(), transformation_context));
+  extract_1.Apply(context.get(), &transformation_context);
   auto replacement_1 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(45, instruction_descriptor_1, 1), 201);
-  ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
-  replacement_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_1.IsApplicable(context.get(), transformation_context));
+  replacement_1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace second occurrence of %27 with %101[0] in '%28 =
@@ -569,12 +620,13 @@
       MakeInstructionDescriptor(28, SpvOpCompositeConstruct, 0);
   auto extract_2 =
       TransformationCompositeExtract(instruction_descriptor_2, 202, 101, {0});
-  ASSERT_TRUE(extract_2.IsApplicable(context.get(), fact_manager));
-  extract_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_2.IsApplicable(context.get(), transformation_context));
+  extract_2.Apply(context.get(), &transformation_context);
   auto replacement_2 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(27, instruction_descriptor_2, 1), 202);
-  ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
-  replacement_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_2.IsApplicable(context.get(), transformation_context));
+  replacement_2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %36 with %101[1] in '%45 = OpCompositeConstruct %31 %36 %41 %44'
@@ -582,12 +634,13 @@
       MakeInstructionDescriptor(45, SpvOpCompositeConstruct, 0);
   auto extract_3 =
       TransformationCompositeExtract(instruction_descriptor_3, 203, 101, {1});
-  ASSERT_TRUE(extract_3.IsApplicable(context.get(), fact_manager));
-  extract_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_3.IsApplicable(context.get(), transformation_context));
+  extract_3.Apply(context.get(), &transformation_context);
   auto replacement_3 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(36, instruction_descriptor_3, 0), 203);
-  ASSERT_TRUE(replacement_3.IsApplicable(context.get(), fact_manager));
-  replacement_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_3.IsApplicable(context.get(), transformation_context));
+  replacement_3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace first occurrence of %27 with %101[2] in '%28 = OpCompositeConstruct
@@ -596,24 +649,26 @@
       MakeInstructionDescriptor(28, SpvOpCompositeConstruct, 0);
   auto extract_4 =
       TransformationCompositeExtract(instruction_descriptor_4, 204, 101, {2});
-  ASSERT_TRUE(extract_4.IsApplicable(context.get(), fact_manager));
-  extract_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_4.IsApplicable(context.get(), transformation_context));
+  extract_4.Apply(context.get(), &transformation_context);
   auto replacement_4 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(27, instruction_descriptor_4, 0), 204);
-  ASSERT_TRUE(replacement_4.IsApplicable(context.get(), fact_manager));
-  replacement_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_4.IsApplicable(context.get(), transformation_context));
+  replacement_4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %22 with %102[0] in 'OpStore %23 %22'
   auto instruction_descriptor_5 = MakeInstructionDescriptor(23, SpvOpStore, 0);
   auto extract_5 =
       TransformationCompositeExtract(instruction_descriptor_5, 205, 102, {0});
-  ASSERT_TRUE(extract_5.IsApplicable(context.get(), fact_manager));
-  extract_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_5.IsApplicable(context.get(), transformation_context));
+  extract_5.Apply(context.get(), &transformation_context);
   auto replacement_5 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(22, instruction_descriptor_5, 1), 205);
-  ASSERT_TRUE(replacement_5.IsApplicable(context.get(), fact_manager));
-  replacement_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_5.IsApplicable(context.get(), transformation_context));
+  replacement_5.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   const std::string after_transformation = R"(
@@ -816,38 +871,65 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
-  fact_manager.AddFact(MakeSynonymFact(20, {0}, 100, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(20, {1}, 100, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(20, {2}, 100, {2}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(54, {}, 100, {3}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(15, {0}, 101, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(15, {1}, 101, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(19, {0}, 101, {2}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(19, {1}, 101, {3}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(27, {}, 102, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(15, {0}, 102, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(15, {1}, 102, {2}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(33, {}, 103, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(47, {0}, 103, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(47, {1}, 103, {2}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(47, {2}, 103, {3}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(42, {}, 104, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(45, {}, 104, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(38, {0}, 105, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(38, {1}, 105, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(46, {}, 105, {2}), context.get());
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(20, {0}, 100, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(20, {1}, 100, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(20, {2}, 100, {2}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(54, {}, 100, {3}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(15, {0}, 101, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(15, {1}, 101, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(19, {0}, 101, {2}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(19, {1}, 101, {3}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(27, {}, 102, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(15, {0}, 102, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(15, {1}, 102, {2}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(33, {}, 103, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(47, {0}, 103, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(47, {1}, 103, {2}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(47, {2}, 103, {3}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(42, {}, 104, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(45, {}, 104, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(38, {0}, 105, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(38, {1}, 105, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(46, {}, 105, {2}), context.get());
 
   // Replace %20 with %100[0:2] in '%80 = OpCopyObject %16 %20'
   auto instruction_descriptor_1 =
       MakeInstructionDescriptor(80, SpvOpCopyObject, 0);
   auto shuffle_1 = TransformationVectorShuffle(instruction_descriptor_1, 200,
                                                100, 100, {0, 1, 2});
-  ASSERT_TRUE(shuffle_1.IsApplicable(context.get(), fact_manager));
-  shuffle_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(shuffle_1.IsApplicable(context.get(), transformation_context));
+  shuffle_1.Apply(context.get(), &transformation_context);
+  fact_manager.ComputeClosureOfFacts(context.get(), 100);
+
   auto replacement_1 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(20, instruction_descriptor_1, 0), 200);
-  ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
-  replacement_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_1.IsApplicable(context.get(), transformation_context));
+  replacement_1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %54 with %100[3] in '%56 = OpFOrdNotEqual %30 %54 %55'
@@ -856,24 +938,28 @@
   auto extract_2 =
       TransformationCompositeExtract(instruction_descriptor_2, 201, 100, {3});
 
-  ASSERT_TRUE(extract_2.IsApplicable(context.get(), fact_manager));
-  extract_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_2.IsApplicable(context.get(), transformation_context));
+  extract_2.Apply(context.get(), &transformation_context);
   auto replacement_2 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(54, instruction_descriptor_2, 0), 201);
-  ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
-  replacement_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_2.IsApplicable(context.get(), transformation_context));
+  replacement_2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %15 with %101[0:1] in 'OpStore %12 %15'
   auto instruction_descriptor_3 = MakeInstructionDescriptor(64, SpvOpStore, 0);
   auto shuffle_3 = TransformationVectorShuffle(instruction_descriptor_3, 202,
                                                101, 101, {0, 1});
-  ASSERT_TRUE(shuffle_3.IsApplicable(context.get(), fact_manager));
-  shuffle_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(shuffle_3.IsApplicable(context.get(), transformation_context));
+  shuffle_3.Apply(context.get(), &transformation_context);
+  fact_manager.ComputeClosureOfFacts(context.get(), 100);
+
   auto replacement_3 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(15, instruction_descriptor_3, 1), 202);
-  ASSERT_TRUE(replacement_3.IsApplicable(context.get(), fact_manager));
-  replacement_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_3.IsApplicable(context.get(), transformation_context));
+  replacement_3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %19 with %101[2:3] in '%81 = OpVectorShuffle %16 %19 %19 0 0 1'
@@ -881,12 +967,15 @@
       MakeInstructionDescriptor(81, SpvOpVectorShuffle, 0);
   auto shuffle_4 = TransformationVectorShuffle(instruction_descriptor_4, 203,
                                                101, 101, {2, 3});
-  ASSERT_TRUE(shuffle_4.IsApplicable(context.get(), fact_manager));
-  shuffle_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(shuffle_4.IsApplicable(context.get(), transformation_context));
+  shuffle_4.Apply(context.get(), &transformation_context);
+  fact_manager.ComputeClosureOfFacts(context.get(), 100);
+
   auto replacement_4 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(19, instruction_descriptor_4, 0), 203);
-  ASSERT_TRUE(replacement_4.IsApplicable(context.get(), fact_manager));
-  replacement_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_4.IsApplicable(context.get(), transformation_context));
+  replacement_4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %27 with %102[0] in '%82 = OpCompositeConstruct %21 %26 %27 %28
@@ -896,12 +985,13 @@
   auto extract_5 =
       TransformationCompositeExtract(instruction_descriptor_5, 204, 102, {0});
 
-  ASSERT_TRUE(extract_5.IsApplicable(context.get(), fact_manager));
-  extract_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_5.IsApplicable(context.get(), transformation_context));
+  extract_5.Apply(context.get(), &transformation_context);
   auto replacement_5 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(27, instruction_descriptor_5, 1), 204);
-  ASSERT_TRUE(replacement_5.IsApplicable(context.get(), fact_manager));
-  replacement_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_5.IsApplicable(context.get(), transformation_context));
+  replacement_5.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %15 with %102[1:2] in '%83 = OpCopyObject %10 %15'
@@ -909,12 +999,15 @@
       MakeInstructionDescriptor(83, SpvOpCopyObject, 0);
   auto shuffle_6 = TransformationVectorShuffle(instruction_descriptor_6, 205,
                                                102, 102, {1, 2});
-  ASSERT_TRUE(shuffle_6.IsApplicable(context.get(), fact_manager));
-  shuffle_6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(shuffle_6.IsApplicable(context.get(), transformation_context));
+  shuffle_6.Apply(context.get(), &transformation_context);
+  fact_manager.ComputeClosureOfFacts(context.get(), 100);
+
   auto replacement_6 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(15, instruction_descriptor_6, 0), 205);
-  ASSERT_TRUE(replacement_6.IsApplicable(context.get(), fact_manager));
-  replacement_6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_6.IsApplicable(context.get(), transformation_context));
+  replacement_6.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %33 with %103[0] in '%86 = OpCopyObject %30 %33'
@@ -922,12 +1015,13 @@
       MakeInstructionDescriptor(86, SpvOpCopyObject, 0);
   auto extract_7 =
       TransformationCompositeExtract(instruction_descriptor_7, 206, 103, {0});
-  ASSERT_TRUE(extract_7.IsApplicable(context.get(), fact_manager));
-  extract_7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_7.IsApplicable(context.get(), transformation_context));
+  extract_7.Apply(context.get(), &transformation_context);
   auto replacement_7 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(33, instruction_descriptor_7, 0), 206);
-  ASSERT_TRUE(replacement_7.IsApplicable(context.get(), fact_manager));
-  replacement_7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_7.IsApplicable(context.get(), transformation_context));
+  replacement_7.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %47 with %103[1:3] in '%84 = OpCopyObject %39 %47'
@@ -935,12 +1029,15 @@
       MakeInstructionDescriptor(84, SpvOpCopyObject, 0);
   auto shuffle_8 = TransformationVectorShuffle(instruction_descriptor_8, 207,
                                                103, 103, {1, 2, 3});
-  ASSERT_TRUE(shuffle_8.IsApplicable(context.get(), fact_manager));
-  shuffle_8.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(shuffle_8.IsApplicable(context.get(), transformation_context));
+  shuffle_8.Apply(context.get(), &transformation_context);
+  fact_manager.ComputeClosureOfFacts(context.get(), 100);
+
   auto replacement_8 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(47, instruction_descriptor_8, 0), 207);
-  ASSERT_TRUE(replacement_8.IsApplicable(context.get(), fact_manager));
-  replacement_8.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_8.IsApplicable(context.get(), transformation_context));
+  replacement_8.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %42 with %104[0] in '%85 = OpCopyObject %30 %42'
@@ -948,12 +1045,13 @@
       MakeInstructionDescriptor(85, SpvOpCopyObject, 0);
   auto extract_9 =
       TransformationCompositeExtract(instruction_descriptor_9, 208, 104, {0});
-  ASSERT_TRUE(extract_9.IsApplicable(context.get(), fact_manager));
-  extract_9.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_9.IsApplicable(context.get(), transformation_context));
+  extract_9.Apply(context.get(), &transformation_context);
   auto replacement_9 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(42, instruction_descriptor_9, 0), 208);
-  ASSERT_TRUE(replacement_9.IsApplicable(context.get(), fact_manager));
-  replacement_9.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_9.IsApplicable(context.get(), transformation_context));
+  replacement_9.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %45 with %104[1] in '%63 = OpLogicalOr %30 %45 %46'
@@ -961,24 +1059,28 @@
       MakeInstructionDescriptor(63, SpvOpLogicalOr, 0);
   auto extract_10 =
       TransformationCompositeExtract(instruction_descriptor_10, 209, 104, {1});
-  ASSERT_TRUE(extract_10.IsApplicable(context.get(), fact_manager));
-  extract_10.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_10.IsApplicable(context.get(), transformation_context));
+  extract_10.Apply(context.get(), &transformation_context);
   auto replacement_10 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(45, instruction_descriptor_10, 0), 209);
-  ASSERT_TRUE(replacement_10.IsApplicable(context.get(), fact_manager));
-  replacement_10.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_10.IsApplicable(context.get(), transformation_context));
+  replacement_10.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %38 with %105[0:1] in 'OpStore %36 %38'
   auto instruction_descriptor_11 = MakeInstructionDescriptor(85, SpvOpStore, 0);
   auto shuffle_11 = TransformationVectorShuffle(instruction_descriptor_11, 210,
                                                 105, 105, {0, 1});
-  ASSERT_TRUE(shuffle_11.IsApplicable(context.get(), fact_manager));
-  shuffle_11.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(shuffle_11.IsApplicable(context.get(), transformation_context));
+  shuffle_11.Apply(context.get(), &transformation_context);
+  fact_manager.ComputeClosureOfFacts(context.get(), 100);
+
   auto replacement_11 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(38, instruction_descriptor_11, 1), 210);
-  ASSERT_TRUE(replacement_11.IsApplicable(context.get(), fact_manager));
-  replacement_11.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_11.IsApplicable(context.get(), transformation_context));
+  replacement_11.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %46 with %105[2] in '%62 = OpLogicalAnd %30 %45 %46'
@@ -986,12 +1088,13 @@
       MakeInstructionDescriptor(62, SpvOpLogicalAnd, 0);
   auto extract_12 =
       TransformationCompositeExtract(instruction_descriptor_12, 211, 105, {2});
-  ASSERT_TRUE(extract_12.IsApplicable(context.get(), fact_manager));
-  extract_12.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_12.IsApplicable(context.get(), transformation_context));
+  extract_12.Apply(context.get(), &transformation_context);
   auto replacement_12 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(46, instruction_descriptor_12, 1), 211);
-  ASSERT_TRUE(replacement_12.IsApplicable(context.get(), fact_manager));
-  replacement_12.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_12.IsApplicable(context.get(), transformation_context));
+  replacement_12.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   const std::string after_transformation = R"(
diff --git a/third_party/SPIRV-Tools/test/fuzz/equivalence_relation_test.cpp b/third_party/SPIRV-Tools/test/fuzz/equivalence_relation_test.cpp
index 3f2ea58..280aa3a 100644
--- a/third_party/SPIRV-Tools/test/fuzz/equivalence_relation_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/equivalence_relation_test.cpp
@@ -47,6 +47,10 @@
   EquivalenceRelation<uint32_t, UInt32Hash, UInt32Equals> relation;
   ASSERT_TRUE(relation.GetAllKnownValues().empty());
 
+  for (uint32_t element = 0; element < 100; element++) {
+    relation.Register(element);
+  }
+
   for (uint32_t element = 2; element < 80; element += 2) {
     relation.MakeEquivalent(0, element);
     relation.MakeEquivalent(element - 1, element + 1);
@@ -123,6 +127,11 @@
   EquivalenceRelation<uint32_t, UInt32Hash, UInt32Equals> relation2;
 
   for (uint32_t i = 0; i < 1000; ++i) {
+    relation1.Register(i);
+    relation2.Register(i);
+  }
+
+  for (uint32_t i = 0; i < 1000; ++i) {
     if (i >= 10) {
       relation1.MakeEquivalent(i, i - 10);
       relation2.MakeEquivalent(i, i - 10);
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 2c79f12..8b1e0c4 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp
@@ -738,393 +738,6 @@
                              uniform_buffer_element_descriptor));
 }
 
-TEST(FactManagerTest, DataSynonymFacts) {
-  // The SPIR-V types and constants come from the following code.  The body of
-  // the SPIR-V function then constructs a composite that is synonymous with
-  // myT.
-  //
-  // #version 310 es
-  //
-  // precision highp float;
-  //
-  // struct S {
-  //   int a;
-  //   uvec2 b;
-  // };
-  //
-  // struct T {
-  //   bool c[5];
-  //   mat4x2 d;
-  //   S e;
-  // };
-  //
-  // void main() {
-  //   T myT = T(bool[5](true, false, true, false, true),
-  //             mat4x2(vec2(1.0, 2.0), vec2(3.0, 4.0),
-  // 	           vec2(5.0, 6.0), vec2(7.0, 8.0)),
-  //             S(10, uvec2(100u, 200u)));
-  // }
-
-  std::string shader = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-               OpName %4 "main"
-               OpName %15 "S"
-               OpMemberName %15 0 "a"
-               OpMemberName %15 1 "b"
-               OpName %16 "T"
-               OpMemberName %16 0 "c"
-               OpMemberName %16 1 "d"
-               OpMemberName %16 2 "e"
-               OpName %18 "myT"
-               OpMemberDecorate %15 0 RelaxedPrecision
-               OpMemberDecorate %15 1 RelaxedPrecision
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeBool
-          %7 = OpTypeInt 32 0
-          %8 = OpConstant %7 5
-          %9 = OpTypeArray %6 %8
-         %10 = OpTypeFloat 32
-         %11 = OpTypeVector %10 2
-         %12 = OpTypeMatrix %11 4
-         %13 = OpTypeInt 32 1
-         %14 = OpTypeVector %7 2
-         %15 = OpTypeStruct %13 %14
-         %16 = OpTypeStruct %9 %12 %15
-         %17 = OpTypePointer Function %16
-         %19 = OpConstantTrue %6
-         %20 = OpConstantFalse %6
-         %21 = OpConstantComposite %9 %19 %20 %19 %20 %19
-         %22 = OpConstant %10 1
-         %23 = OpConstant %10 2
-         %24 = OpConstantComposite %11 %22 %23
-         %25 = OpConstant %10 3
-         %26 = OpConstant %10 4
-         %27 = OpConstantComposite %11 %25 %26
-         %28 = OpConstant %10 5
-         %29 = OpConstant %10 6
-         %30 = OpConstantComposite %11 %28 %29
-         %31 = OpConstant %10 7
-         %32 = OpConstant %10 8
-         %33 = OpConstantComposite %11 %31 %32
-         %34 = OpConstantComposite %12 %24 %27 %30 %33
-         %35 = OpConstant %13 10
-         %36 = OpConstant %7 100
-         %37 = OpConstant %7 200
-         %38 = OpConstantComposite %14 %36 %37
-         %39 = OpConstantComposite %15 %35 %38
-         %40 = OpConstantComposite %16 %21 %34 %39
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-         %18 = OpVariable %17 Function
-               OpStore %18 %40
-        %100 = OpCompositeConstruct %9 %19 %20 %19 %20 %19
-        %101 = OpCompositeConstruct %11 %22 %23
-        %102 = OpCompositeConstruct %11 %25 %26
-        %103 = OpCompositeConstruct %11 %28 %29
-        %104 = OpCompositeConstruct %11 %31 %32
-        %105 = OpCompositeConstruct %12 %101 %102 %103 %104
-        %106 = OpCompositeConstruct %14 %36 %37
-        %107 = OpCompositeConstruct %15 %35 %106
-        %108 = OpCompositeConstruct %16 %100 %105 %107
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
-  ASSERT_TRUE(IsValid(env, context.get()));
-
-  FactManager fact_manager;
-
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(24, {}), MakeDataDescriptor(101, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
-                                         MakeDataDescriptor(101, {0}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {1}),
-                                         MakeDataDescriptor(101, {1}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
-                                         MakeDataDescriptor(101, {1}),
-                                         context.get()));
-
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(24, {}),
-                                  MakeDataDescriptor(101, {}), context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(24, {}), MakeDataDescriptor(101, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
-                                        MakeDataDescriptor(101, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {1}),
-                                        MakeDataDescriptor(101, {1}),
-                                        context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
-                                         MakeDataDescriptor(101, {1}),
-                                         context.get()));
-
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(27, {}), MakeDataDescriptor(102, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {0}),
-                                         MakeDataDescriptor(102, {0}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {1}),
-                                         MakeDataDescriptor(102, {1}),
-                                         context.get()));
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(27, {0}),
-                                  MakeDataDescriptor(102, {0}), context.get());
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(27, {}), MakeDataDescriptor(102, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {0}),
-                                        MakeDataDescriptor(102, {0}),
-                                        context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {1}),
-                                         MakeDataDescriptor(102, {1}),
-                                         context.get()));
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(27, {1}),
-                                  MakeDataDescriptor(102, {1}), context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(27, {}), MakeDataDescriptor(102, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {0}),
-                                        MakeDataDescriptor(102, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {1}),
-                                        MakeDataDescriptor(102, {1}),
-                                        context.get()));
-
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(30, {}), MakeDataDescriptor(103, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {0}),
-                                         MakeDataDescriptor(103, {0}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1}),
-                                         MakeDataDescriptor(103, {1}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(33, {}), MakeDataDescriptor(104, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {0}),
-                                         MakeDataDescriptor(104, {0}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {1}),
-                                         MakeDataDescriptor(104, {1}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(34, {}), MakeDataDescriptor(105, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {0}),
-                                         MakeDataDescriptor(105, {0}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {1}),
-                                         MakeDataDescriptor(105, {1}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {2}),
-                                         MakeDataDescriptor(105, {2}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {3}),
-                                         MakeDataDescriptor(105, {3}),
-                                         context.get()));
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(30, {}),
-                                  MakeDataDescriptor(103, {}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(33, {}),
-                                  MakeDataDescriptor(104, {}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {0}),
-                                  MakeDataDescriptor(105, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {1}),
-                                  MakeDataDescriptor(105, {1}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {2}),
-                                  MakeDataDescriptor(105, {2}), context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(30, {}), MakeDataDescriptor(103, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {0}),
-                                        MakeDataDescriptor(103, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1}),
-                                        MakeDataDescriptor(103, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(33, {}), MakeDataDescriptor(104, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {0}),
-                                        MakeDataDescriptor(104, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {1}),
-                                        MakeDataDescriptor(104, {1}),
-                                        context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(34, {}), MakeDataDescriptor(105, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {0}),
-                                        MakeDataDescriptor(105, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {1}),
-                                        MakeDataDescriptor(105, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {2}),
-                                        MakeDataDescriptor(105, {2}),
-                                        context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {3}),
-                                         MakeDataDescriptor(105, {3}),
-                                         context.get()));
-
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {3}),
-                                  MakeDataDescriptor(105, {3}), context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {0}),
-                                        MakeDataDescriptor(104, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {3}),
-                                        MakeDataDescriptor(105, {3}),
-                                        context.get()));
-
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(21, {}), MakeDataDescriptor(100, {}), context.get()));
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {0}),
-                                  MakeDataDescriptor(100, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {1}),
-                                  MakeDataDescriptor(100, {1}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {2}),
-                                  MakeDataDescriptor(100, {2}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {3}),
-                                  MakeDataDescriptor(100, {3}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {4}),
-                                  MakeDataDescriptor(100, {4}), context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(21, {}), MakeDataDescriptor(100, {}), context.get()));
-
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(39, {0}),
-                                         MakeDataDescriptor(107, {0}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(35, {}), MakeDataDescriptor(39, {0}), context.get()));
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(39, {0}),
-                                  MakeDataDescriptor(35, {}), context.get());
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(39, {0}),
-                                         MakeDataDescriptor(107, {0}),
-                                         context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(35, {}), MakeDataDescriptor(39, {0}), context.get()));
-
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(38, {0}), MakeDataDescriptor(36, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(38, {1}), MakeDataDescriptor(37, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(106, {0}), MakeDataDescriptor(36, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(106, {1}), MakeDataDescriptor(37, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(38, {}), MakeDataDescriptor(106, {}), context.get()));
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(38, {0}),
-                                  MakeDataDescriptor(36, {}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(106, {0}),
-                                  MakeDataDescriptor(36, {}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(38, {1}),
-                                  MakeDataDescriptor(37, {}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(106, {1}),
-                                  MakeDataDescriptor(37, {}), context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(38, {0}), MakeDataDescriptor(36, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(38, {1}), MakeDataDescriptor(37, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(106, {0}), MakeDataDescriptor(36, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(106, {1}), MakeDataDescriptor(37, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(38, {}), MakeDataDescriptor(106, {}), context.get()));
-
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(40, {}), MakeDataDescriptor(108, {}), context.get()));
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(107, {0}),
-                                  MakeDataDescriptor(35, {}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {0}),
-                                  MakeDataDescriptor(108, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {1}),
-                                  MakeDataDescriptor(108, {1}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {2}),
-                                  MakeDataDescriptor(108, {2}), context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(40, {}), MakeDataDescriptor(108, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0}),
-                                        MakeDataDescriptor(108, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1}),
-                                        MakeDataDescriptor(108, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2}),
-                                        MakeDataDescriptor(108, {2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 0}),
-                                        MakeDataDescriptor(108, {0, 0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 1}),
-                                        MakeDataDescriptor(108, {0, 1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 2}),
-                                        MakeDataDescriptor(108, {0, 2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 3}),
-                                        MakeDataDescriptor(108, {0, 3}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 4}),
-                                        MakeDataDescriptor(108, {0, 4}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 0}),
-                                        MakeDataDescriptor(108, {1, 0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 1}),
-                                        MakeDataDescriptor(108, {1, 1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 2}),
-                                        MakeDataDescriptor(108, {1, 2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 3}),
-                                        MakeDataDescriptor(108, {1, 3}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 0, 0}),
-                                        MakeDataDescriptor(108, {1, 0, 0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 1, 0}),
-                                        MakeDataDescriptor(108, {1, 1, 0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 2, 0}),
-                                        MakeDataDescriptor(108, {1, 2, 0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 3, 0}),
-                                        MakeDataDescriptor(108, {1, 3, 0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 0, 1}),
-                                        MakeDataDescriptor(108, {1, 0, 1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 1, 1}),
-                                        MakeDataDescriptor(108, {1, 1, 1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 2, 1}),
-                                        MakeDataDescriptor(108, {1, 2, 1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 3, 1}),
-                                        MakeDataDescriptor(108, {1, 3, 1}),
-                                        context.get()));
-
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 0}),
-                                        MakeDataDescriptor(108, {2, 0}),
-                                        context.get()));
-
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 1}),
-                                        MakeDataDescriptor(108, {2, 1}),
-                                        context.get()));
-
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 1, 0}),
-                                        MakeDataDescriptor(108, {2, 1, 0}),
-                                        context.get()));
-
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 1, 1}),
-                                        MakeDataDescriptor(108, {2, 1, 1}),
-                                        context.get()));
-}
-
 TEST(FactManagerTest, RecursiveAdditionOfFacts) {
   std::string shader = R"(
                OpCapability Shader
@@ -1157,20 +770,16 @@
   fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
                                   MakeDataDescriptor(11, {2}), context.get());
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(10, {}), MakeDataDescriptor(11, {2}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {}),
+                                        MakeDataDescriptor(11, {2})));
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {0}),
-                                        MakeDataDescriptor(11, {2, 0}),
-                                        context.get()));
+                                        MakeDataDescriptor(11, {2, 0})));
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {1}),
-                                        MakeDataDescriptor(11, {2, 1}),
-                                        context.get()));
+                                        MakeDataDescriptor(11, {2, 1})));
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {2}),
-                                        MakeDataDescriptor(11, {2, 2}),
-                                        context.get()));
+                                        MakeDataDescriptor(11, {2, 2})));
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {3}),
-                                        MakeDataDescriptor(11, {2, 3}),
-                                        context.get()));
+                                        MakeDataDescriptor(11, {2, 3})));
 }
 
 TEST(FactManagerTest, LogicalNotEquationFacts) {
@@ -1209,14 +818,14 @@
   fact_manager.AddFactIdEquation(14, SpvOpLogicalNot, {7}, context.get());
   fact_manager.AddFactIdEquation(17, SpvOpLogicalNot, {16}, context.get());
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(17, {}), MakeDataDescriptor(7, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(15, {}), MakeDataDescriptor(17, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(16, {}), MakeDataDescriptor(14, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(15, {}),
+                                        MakeDataDescriptor(7, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}),
+                                        MakeDataDescriptor(7, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(15, {}),
+                                        MakeDataDescriptor(17, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(16, {}),
+                                        MakeDataDescriptor(14, {})));
 }
 
 TEST(FactManagerTest, SignedNegateEquationFacts) {
@@ -1249,8 +858,8 @@
   fact_manager.AddFactIdEquation(14, SpvOpSNegate, {7}, context.get());
   fact_manager.AddFactIdEquation(15, SpvOpSNegate, {14}, context.get());
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(7, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(7, {}),
+                                        MakeDataDescriptor(15, {})));
 }
 
 TEST(FactManagerTest, AddSubNegateFacts1) {
@@ -1302,12 +911,12 @@
                                   MakeDataDescriptor(22, {}), context.get());
   fact_manager.AddFactIdEquation(24, SpvOpSNegate, {23}, context.get());
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(19, {}), MakeDataDescriptor(15, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(24, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(19, {}),
+                                        MakeDataDescriptor(15, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(20, {}),
+                                        MakeDataDescriptor(16, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {}),
+                                        MakeDataDescriptor(15, {})));
 }
 
 TEST(FactManagerTest, AddSubNegateFacts2) {
@@ -1347,30 +956,158 @@
   fact_manager.AddFactIdEquation(14, SpvOpISub, {15, 16}, context.get());
   fact_manager.AddFactIdEquation(17, SpvOpIAdd, {14, 16}, context.get());
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}),
+                                        MakeDataDescriptor(15, {})));
 
   fact_manager.AddFactIdEquation(18, SpvOpIAdd, {16, 14}, context.get());
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(18, {}),
+                                        MakeDataDescriptor(15, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}),
+                                        MakeDataDescriptor(18, {})));
 
   fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 15}, context.get());
   fact_manager.AddFactIdEquation(20, SpvOpSNegate, {19}, context.get());
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(20, {}),
+                                        MakeDataDescriptor(16, {})));
 
   fact_manager.AddFactIdEquation(21, SpvOpISub, {14, 19}, context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
+                                        MakeDataDescriptor(15, {})));
 
   fact_manager.AddFactIdEquation(22, SpvOpISub, {14, 18}, context.get());
   fact_manager.AddFactIdEquation(23, SpvOpSNegate, {22}, context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(23, {}),
+                                        MakeDataDescriptor(16, {})));
+}
+
+TEST(FactManagerTest, EquationAndEquivalenceFacts) {
+  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
+         %15 = OpConstant %6 24
+         %16 = OpConstant %6 37
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %14 = OpISub %6 %15 %16
+        %114 = OpCopyObject %6 %14
+         %17 = OpIAdd %6 %114 %16 ; ==> synonymous(%17, %15)
+         %18 = OpIAdd %6 %16 %114 ; ==> synonymous(%17, %18, %15)
+         %19 = OpISub %6 %114 %15
+        %119 = OpCopyObject %6 %19
+         %20 = OpSNegate %6 %119 ; ==> synonymous(%20, %16)
+         %21 = OpISub %6 %14 %19 ; ==> synonymous(%21, %15)
+         %22 = OpISub %6 %14 %18
+        %220 = OpCopyObject %6 %22
+         %23 = OpSNegate %6 %220 ; ==> synonymous(%23, %16)
+               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.AddFactIdEquation(14, SpvOpISub, {15, 16}, context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(114, {}),
+                                  MakeDataDescriptor(14, {}), context.get());
+  fact_manager.AddFactIdEquation(17, SpvOpIAdd, {114, 16}, context.get());
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}),
+                                        MakeDataDescriptor(15, {})));
+
+  fact_manager.AddFactIdEquation(18, SpvOpIAdd, {16, 114}, context.get());
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(18, {}),
+                                        MakeDataDescriptor(15, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}),
+                                        MakeDataDescriptor(18, {})));
+
+  fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 15}, context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(119, {}),
+                                  MakeDataDescriptor(19, {}), context.get());
+  fact_manager.AddFactIdEquation(20, SpvOpSNegate, {119}, context.get());
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(20, {}),
+                                        MakeDataDescriptor(16, {})));
+
+  fact_manager.AddFactIdEquation(21, SpvOpISub, {14, 19}, context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
+                                        MakeDataDescriptor(15, {})));
+
+  fact_manager.AddFactIdEquation(22, SpvOpISub, {14, 18}, context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(22, {}),
+                                  MakeDataDescriptor(220, {}), context.get());
+  fact_manager.AddFactIdEquation(23, SpvOpSNegate, {220}, context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(23, {}),
+                                        MakeDataDescriptor(16, {})));
+}
+
+TEST(FactManagerTest, CheckingFactsDoesNotAddConstants) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpMemberDecorate %9 0 Offset 0
+               OpDecorate %9 Block
+               OpDecorate %11 DescriptorSet 0
+               OpDecorate %11 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpTypeStruct %6
+         %10 = OpTypePointer Uniform %9
+         %11 = OpVariable %10 Uniform
+         %12 = OpConstant %6 0
+         %13 = OpTypePointer Uniform %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %14 = OpAccessChain %13 %11 %12
+         %15 = OpLoad %6 %14
+               OpStore %8 %15
+               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;
+
+  // 8[0] == int(1)
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1},
+                            MakeUniformBufferElementDescriptor(0, 0, {0})));
+
+  // Although 8[0] has the value 1, we do not have the constant 1 in the module.
+  // We thus should not find any constants available from uniforms for int type.
+  // Furthermore, the act of looking for appropriate constants should not change
+  // which constants are known to the constant manager.
+  auto int_type = context->get_type_mgr()->GetType(6)->AsInteger();
+  opt::analysis::IntConstant constant_one(int_type, {1});
+  ASSERT_FALSE(context->get_constant_mgr()->FindConstant(&constant_one));
+  auto available_constants =
+      fact_manager.GetConstantsAvailableFromUniformsForType(context.get(), 6);
+  ASSERT_EQ(0, available_constants.size());
+  ASSERT_TRUE(IsEqual(env, shader, context.get()));
+  ASSERT_FALSE(context->get_constant_mgr()->FindConstant(&constant_one));
 }
 
 }  // namespace
diff --git a/third_party/SPIRV-Tools/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp b/third_party/SPIRV-Tools/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp
deleted file mode 100644
index 89f006e..0000000
--- a/third_party/SPIRV-Tools/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp
+++ /dev/null
@@ -1,393 +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/fuzzer_pass_add_useful_constructs.h"
-#include "source/fuzz/pseudo_random_generator.h"
-#include "source/fuzz/uniform_buffer_element_descriptor.h"
-#include "test/fuzz/fuzz_test_util.h"
-
-namespace spvtools {
-namespace fuzz {
-namespace {
-
-bool AddFactHelper(
-    FactManager* fact_manager, opt::IRContext* context, uint32_t word,
-    const protobufs::UniformBufferElementDescriptor& descriptor) {
-  protobufs::FactConstantUniform constant_uniform_fact;
-  constant_uniform_fact.add_constant_word(word);
-  *constant_uniform_fact.mutable_uniform_buffer_element_descriptor() =
-      descriptor;
-  protobufs::Fact fact;
-  *fact.mutable_constant_uniform_fact() = constant_uniform_fact;
-  return fact_manager->AddFact(fact, context);
-}
-
-TEST(FuzzerPassAddUsefulConstructsTest, CheckBasicStuffIsAdded) {
-  // The SPIR-V came from the following empty GLSL shader:
-  //
-  // #version 450
-  //
-  // void main()
-  // {
-  // }
-
-  std::string shader = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource GLSL 450
-               OpName %4 "main"
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
-  ASSERT_TRUE(IsValid(env, context.get()));
-
-  FactManager fact_manager;
-  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
-  protobufs::TransformationSequence transformation_sequence;
-
-  FuzzerPassAddUsefulConstructs pass(context.get(), &fact_manager,
-                                     &fuzzer_context, &transformation_sequence);
-  pass.Apply();
-  ASSERT_TRUE(IsValid(env, context.get()));
-
-  std::string after = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource GLSL 450
-               OpName %4 "main"
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-        %100 = OpTypeBool
-        %101 = OpTypeInt 32 1
-        %102 = OpTypeInt 32 0
-        %103 = OpTypeFloat 32
-        %104 = OpConstantTrue %100
-        %105 = OpConstantFalse %100
-        %106 = OpConstant %101 0
-        %107 = OpConstant %101 1
-        %108 = OpConstant %102 0
-        %109 = OpConstant %102 1
-        %110 = OpConstant %103 0
-        %111 = OpConstant %103 1
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-  ASSERT_TRUE(IsEqual(env, after, context.get()));
-}
-
-TEST(FuzzerPassAddUsefulConstructsTest,
-     CheckTypesIndicesAndConstantsAddedForUniformFacts) {
-  // The SPIR-V came from the following GLSL shader:
-  //
-  // #version 450
-  //
-  // struct S {
-  //   int x;
-  //   float y;
-  //   int z;
-  //   int w;
-  // };
-  //
-  // uniform buf {
-  //   S s;
-  //   uint w[10];
-  // };
-  //
-  // void main() {
-  // }
-
-  std::string shader = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource GLSL 450
-               OpName %4 "main"
-               OpName %8 "S"
-               OpMemberName %8 0 "x"
-               OpMemberName %8 1 "y"
-               OpMemberName %8 2 "z"
-               OpMemberName %8 3 "w"
-               OpName %12 "buf"
-               OpMemberName %12 0 "s"
-               OpMemberName %12 1 "w"
-               OpName %14 ""
-               OpMemberDecorate %8 0 Offset 0
-               OpMemberDecorate %8 1 Offset 4
-               OpMemberDecorate %8 2 Offset 8
-               OpMemberDecorate %8 3 Offset 12
-               OpDecorate %11 ArrayStride 16
-               OpMemberDecorate %12 0 Offset 0
-               OpMemberDecorate %12 1 Offset 16
-               OpDecorate %12 Block
-               OpDecorate %14 DescriptorSet 0
-               OpDecorate %14 Binding 0
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeInt 32 1
-          %7 = OpTypeFloat 32
-          %8 = OpTypeStruct %6 %7 %6 %6
-          %9 = OpTypeInt 32 0
-         %10 = OpConstant %9 10
-         %11 = OpTypeArray %9 %10
-         %12 = OpTypeStruct %8 %11
-         %13 = OpTypePointer Uniform %12
-         %14 = OpVariable %13 Uniform
-          %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;
-  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
-  protobufs::TransformationSequence transformation_sequence;
-
-  // Add some uniform facts.
-
-  // buf.s.x == 200
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 200,
-                            MakeUniformBufferElementDescriptor(0, 0, {0, 0})));
-
-  // buf.s.y == 0.5
-  const float float_value = 0.5;
-  uint32_t float_value_as_uint;
-  memcpy(&float_value_as_uint, &float_value, sizeof(float_value));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_value_as_uint,
-                            MakeUniformBufferElementDescriptor(0, 0, {0, 1})));
-
-  // buf.s.z == 300
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 300,
-                            MakeUniformBufferElementDescriptor(0, 0, {0, 2})));
-
-  // buf.s.w == 400
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 400,
-                            MakeUniformBufferElementDescriptor(0, 0, {0, 3})));
-
-  // buf.w[6] = 22
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 22,
-                            MakeUniformBufferElementDescriptor(0, 0, {1, 6})));
-
-  // buf.w[8] = 23
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 23,
-                            MakeUniformBufferElementDescriptor(0, 0, {1, 8})));
-
-  // Assert some things about the module that are not true prior to adding the
-  // pass
-
-  {
-    // No uniform int pointer
-    opt::analysis::Integer temp_type_signed_int(32, true);
-    opt::analysis::Integer* registered_type_signed_int =
-        context->get_type_mgr()
-            ->GetRegisteredType(&temp_type_signed_int)
-            ->AsInteger();
-    opt::analysis::Pointer type_pointer_uniform_signed_int(
-        registered_type_signed_int, SpvStorageClassUniform);
-    ASSERT_EQ(0,
-              context->get_type_mgr()->GetId(&type_pointer_uniform_signed_int));
-
-    // No uniform uint pointer
-    opt::analysis::Integer temp_type_unsigned_int(32, false);
-    opt::analysis::Integer* registered_type_unsigned_int =
-        context->get_type_mgr()
-            ->GetRegisteredType(&temp_type_unsigned_int)
-            ->AsInteger();
-    opt::analysis::Pointer type_pointer_uniform_unsigned_int(
-        registered_type_unsigned_int, SpvStorageClassUniform);
-    ASSERT_EQ(
-        0, context->get_type_mgr()->GetId(&type_pointer_uniform_unsigned_int));
-
-    // No uniform float pointer
-    opt::analysis::Float temp_type_float(32);
-    opt::analysis::Float* registered_type_float =
-        context->get_type_mgr()->GetRegisteredType(&temp_type_float)->AsFloat();
-    opt::analysis::Pointer type_pointer_uniform_float(registered_type_float,
-                                                      SpvStorageClassUniform);
-    ASSERT_EQ(0, context->get_type_mgr()->GetId(&type_pointer_uniform_float));
-
-    // No int constants 200, 300 nor 400
-    opt::analysis::IntConstant int_constant_200(registered_type_signed_int,
-                                                {200});
-    opt::analysis::IntConstant int_constant_300(registered_type_signed_int,
-                                                {300});
-    opt::analysis::IntConstant int_constant_400(registered_type_signed_int,
-                                                {400});
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_200));
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_300));
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_400));
-
-    // No float constant 0.5
-    opt::analysis::FloatConstant float_constant_zero_point_five(
-        registered_type_float, {float_value_as_uint});
-    ASSERT_EQ(nullptr, context->get_constant_mgr()->FindConstant(
-                           &float_constant_zero_point_five));
-
-    // No uint constant 22
-    opt::analysis::IntConstant uint_constant_22(registered_type_unsigned_int,
-                                                {22});
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&uint_constant_22));
-
-    // No uint constant 23
-    opt::analysis::IntConstant uint_constant_23(registered_type_unsigned_int,
-                                                {23});
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&uint_constant_23));
-
-    // No int constants 0, 1, 2, 3, 6, 8
-    opt::analysis::IntConstant int_constant_0(registered_type_signed_int, {0});
-    opt::analysis::IntConstant int_constant_1(registered_type_signed_int, {1});
-    opt::analysis::IntConstant int_constant_2(registered_type_signed_int, {2});
-    opt::analysis::IntConstant int_constant_3(registered_type_signed_int, {3});
-    opt::analysis::IntConstant int_constant_6(registered_type_signed_int, {6});
-    opt::analysis::IntConstant int_constant_8(registered_type_signed_int, {8});
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_0));
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_1));
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_2));
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_3));
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_6));
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_8));
-  }
-
-  FuzzerPassAddUsefulConstructs pass(context.get(), &fact_manager,
-                                     &fuzzer_context, &transformation_sequence);
-  pass.Apply();
-  ASSERT_TRUE(IsValid(env, context.get()));
-
-  // Now assert some things about the module that should be true following the
-  // pass.
-
-  // We reconstruct all necessary types and constants to guard against the type
-  // and constant managers for the module having been invalidated.
-
-  {
-    // Uniform int pointer now present
-    opt::analysis::Integer temp_type_signed_int(32, true);
-    opt::analysis::Integer* registered_type_signed_int =
-        context->get_type_mgr()
-            ->GetRegisteredType(&temp_type_signed_int)
-            ->AsInteger();
-    opt::analysis::Pointer type_pointer_uniform_signed_int(
-        registered_type_signed_int, SpvStorageClassUniform);
-    ASSERT_NE(0,
-              context->get_type_mgr()->GetId(&type_pointer_uniform_signed_int));
-
-    // Uniform uint pointer now present
-    opt::analysis::Integer temp_type_unsigned_int(32, false);
-    opt::analysis::Integer* registered_type_unsigned_int =
-        context->get_type_mgr()
-            ->GetRegisteredType(&temp_type_unsigned_int)
-            ->AsInteger();
-    opt::analysis::Pointer type_pointer_uniform_unsigned_int(
-        registered_type_unsigned_int, SpvStorageClassUniform);
-    ASSERT_NE(
-        0, context->get_type_mgr()->GetId(&type_pointer_uniform_unsigned_int));
-
-    // Uniform float pointer now present
-    opt::analysis::Float temp_type_float(32);
-    opt::analysis::Float* registered_type_float =
-        context->get_type_mgr()->GetRegisteredType(&temp_type_float)->AsFloat();
-    opt::analysis::Pointer type_pointer_uniform_float(registered_type_float,
-                                                      SpvStorageClassUniform);
-    ASSERT_NE(0, context->get_type_mgr()->GetId(&type_pointer_uniform_float));
-
-    // int constants 200, 300, 400 now present
-    opt::analysis::IntConstant int_constant_200(registered_type_signed_int,
-                                                {200});
-    opt::analysis::IntConstant int_constant_300(registered_type_signed_int,
-                                                {300});
-    opt::analysis::IntConstant int_constant_400(registered_type_signed_int,
-                                                {400});
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_200));
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_300));
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_400));
-
-    // float constant 0.5 now present
-    opt::analysis::FloatConstant float_constant_zero_point_five(
-        registered_type_float, {float_value_as_uint});
-    ASSERT_NE(nullptr, context->get_constant_mgr()->FindConstant(
-                           &float_constant_zero_point_five));
-
-    // uint constant 22 now present
-    opt::analysis::IntConstant uint_constant_22(registered_type_unsigned_int,
-                                                {22});
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&uint_constant_22));
-
-    // uint constant 23 now present
-    opt::analysis::IntConstant uint_constant_23(registered_type_unsigned_int,
-                                                {23});
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&uint_constant_23));
-
-    // int constants 0, 1, 2, 3, 6, 8 now present
-    opt::analysis::IntConstant int_constant_0(registered_type_signed_int, {0});
-    opt::analysis::IntConstant int_constant_1(registered_type_signed_int, {1});
-    opt::analysis::IntConstant int_constant_2(registered_type_signed_int, {2});
-    opt::analysis::IntConstant int_constant_3(registered_type_signed_int, {3});
-    opt::analysis::IntConstant int_constant_6(registered_type_signed_int, {6});
-    opt::analysis::IntConstant int_constant_8(registered_type_signed_int, {8});
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_0));
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_1));
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_2));
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_3));
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_6));
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_8));
-  }
-}
-
-}  // namespace
-}  // namespace fuzz
-}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/fuzzer_pass_construct_composites_test.cpp b/third_party/SPIRV-Tools/test/fuzz/fuzzer_pass_construct_composites_test.cpp
new file mode 100644
index 0000000..cc21f74
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/fuzzer_pass_construct_composites_test.cpp
@@ -0,0 +1,187 @@
+// 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_construct_composites.h"
+#include "source/fuzz/pseudo_random_generator.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(FuzzerPassConstructCompositesTest, IsomorphicStructs) {
+  // This test declares various isomorphic structs, and a struct that is made up
+  // of these isomorphic structs.  The pass to construct composites is then
+  // applied several times to check that no issues arise related to using a
+  // value of one struct type when a value of an isomorphic struct type is
+  // required.
+
+  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 0
+          %8 = OpTypeStruct %6 %6 %6
+          %9 = OpTypeStruct %6 %6 %6
+         %10 = OpTypeStruct %6 %6 %6
+         %11 = OpTypeStruct %6 %6 %6
+         %12 = OpTypeStruct %6 %6 %6
+         %13 = OpTypeStruct %8 %9 %10 %11 %12
+         %14 = OpConstantComposite %8 %7 %7 %7
+         %15 = OpConstantComposite %9 %7 %7 %7
+         %16 = OpConstantComposite %10 %7 %7 %7
+         %17 = OpConstantComposite %11 %7 %7 %7
+         %18 = OpConstantComposite %12 %7 %7 %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+
+  auto prng = MakeUnique<PseudoRandomGenerator>(0);
+
+  for (uint32_t i = 0; i < 10; i++) {
+    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);
+
+    FuzzerContext fuzzer_context(prng.get(), 100);
+    protobufs::TransformationSequence transformation_sequence;
+
+    FuzzerPassConstructComposites fuzzer_pass(
+        context.get(), &transformation_context, &fuzzer_context,
+        &transformation_sequence);
+
+    fuzzer_pass.Apply();
+
+    // We just check that the result is valid.
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+}
+
+TEST(FuzzerPassConstructCompositesTest, IsomorphicArrays) {
+  // This test declares various isomorphic arrays, and a struct that is made up
+  // of these isomorphic arrays.  The pass to construct composites is then
+  // applied several times to check that no issues arise related to using a
+  // value of one array type when a value of an isomorphic array type is
+  // required.
+
+  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
+         %50 = OpTypeInt 32 0
+         %51 = OpConstant %50 3
+          %7 = OpConstant %6 0
+          %8 = OpTypeArray %6 %51
+          %9 = OpTypeArray %6 %51
+         %10 = OpTypeArray %6 %51
+         %11 = OpTypeArray %6 %51
+         %12 = OpTypeArray %6 %51
+         %13 = OpTypeStruct %8 %9 %10 %11 %12
+         %14 = OpConstantComposite %8 %7 %7 %7
+         %15 = OpConstantComposite %9 %7 %7 %7
+         %16 = OpConstantComposite %10 %7 %7 %7
+         %17 = OpConstantComposite %11 %7 %7 %7
+         %18 = OpConstantComposite %12 %7 %7 %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+
+  auto prng = MakeUnique<PseudoRandomGenerator>(0);
+
+  for (uint32_t i = 0; i < 10; i++) {
+    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);
+
+    FuzzerContext fuzzer_context(prng.get(), 100);
+    protobufs::TransformationSequence transformation_sequence;
+
+    FuzzerPassConstructComposites fuzzer_pass(
+        context.get(), &transformation_context, &fuzzer_context,
+        &transformation_sequence);
+
+    fuzzer_pass.Apply();
+
+    // We just check that the result is valid.
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+}
+
+}  // 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 dc7ba3a..0833c1d 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
@@ -194,14 +194,17 @@
   ASSERT_TRUE(IsValid(env, donor_context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  auto prng = MakeUnique<PseudoRandomGenerator>(0);
-  FuzzerContext fuzzer_context(prng.get(), 100);
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
   protobufs::TransformationSequence transformation_sequence;
 
-  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager,
-                                      &fuzzer_context, &transformation_sequence,
-                                      {});
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
 
   fuzzer_pass.DonateSingleModule(donor_context.get(), false);
 
@@ -269,13 +272,17 @@
   ASSERT_TRUE(IsValid(env, donor_context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
   protobufs::TransformationSequence transformation_sequence;
 
-  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager,
-                                      &fuzzer_context, &transformation_sequence,
-                                      {});
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
 
   fuzzer_pass.DonateSingleModule(donor_context.get(), false);
 
@@ -393,13 +400,17 @@
   ASSERT_TRUE(IsValid(env, donor_context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
   protobufs::TransformationSequence transformation_sequence;
 
-  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager,
-                                      &fuzzer_context, &transformation_sequence,
-                                      {});
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
 
   fuzzer_pass.DonateSingleModule(donor_context.get(), false);
 
@@ -481,13 +492,17 @@
   ASSERT_TRUE(IsValid(env, donor_context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
   protobufs::TransformationSequence transformation_sequence;
 
-  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager,
-                                      &fuzzer_context, &transformation_sequence,
-                                      {});
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
 
   fuzzer_pass.DonateSingleModule(donor_context.get(), false);
 
@@ -496,6 +511,993 @@
   ASSERT_TRUE(IsValid(env, recipient_context.get()));
 }
 
+TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Private %6
+          %8 = OpConstantNull %7
+          %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);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImages) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+               OpName %4 "main"
+               OpName %10 "mySampler"
+               OpName %21 "myTexture"
+               OpName %33 "v"
+               OpDecorate %10 RelaxedPrecision
+               OpDecorate %10 DescriptorSet 0
+               OpDecorate %10 Binding 0
+               OpDecorate %11 RelaxedPrecision
+               OpDecorate %21 RelaxedPrecision
+               OpDecorate %21 DescriptorSet 0
+               OpDecorate %21 Binding 1
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %34 RelaxedPrecision
+               OpDecorate %40 RelaxedPrecision
+               OpDecorate %42 RelaxedPrecision
+               OpDecorate %43 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeImage %6 2D 0 0 0 1 Unknown
+          %8 = OpTypeSampledImage %7
+          %9 = OpTypePointer UniformConstant %8
+         %10 = OpVariable %9 UniformConstant
+         %12 = OpTypeInt 32 1
+         %13 = OpConstant %12 2
+         %15 = OpTypeVector %12 2
+         %17 = OpTypeInt 32 0
+         %18 = OpConstant %17 0
+         %20 = OpTypePointer UniformConstant %7
+         %21 = OpVariable %20 UniformConstant
+         %23 = OpConstant %12 1
+         %25 = OpConstant %17 1
+         %27 = OpTypeBool
+         %31 = OpTypeVector %6 4
+         %32 = OpTypePointer Function %31
+         %35 = OpConstantComposite %15 %23 %23
+         %36 = OpConstant %12 3
+         %37 = OpConstant %12 4
+         %38 = OpConstantComposite %15 %36 %37
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %33 = OpVariable %32 Function
+         %11 = OpLoad %8 %10
+         %14 = OpImage %7 %11
+         %16 = OpImageQuerySizeLod %15 %14 %13
+         %19 = OpCompositeExtract %12 %16 0
+         %22 = OpLoad %7 %21
+         %24 = OpImageQuerySizeLod %15 %22 %23
+         %26 = OpCompositeExtract %12 %24 1
+         %28 = OpSGreaterThan %27 %19 %26
+               OpSelectionMerge %30 None
+               OpBranchConditional %28 %29 %41
+         %29 = OpLabel
+         %34 = OpLoad %8 %10
+         %39 = OpImage %7 %34
+         %40 = OpImageFetch %31 %39 %35 Lod|ConstOffset %13 %38
+               OpStore %33 %40
+               OpBranch %30
+         %41 = OpLabel
+         %42 = OpLoad %7 %21
+         %43 = OpImageFetch %31 %42 %35 Lod|ConstOffset %13 %38
+               OpStore %33 %43
+               OpBranch %30
+         %30 = 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);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesSampler) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpDecorate %16 DescriptorSet 0
+               OpDecorate %16 Binding 0
+               OpDecorate %12 DescriptorSet 0
+               OpDecorate %12 Binding 64
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %23 = OpTypeFloat 32
+          %6 = OpTypeImage %23 2D 2 0 0 1 Unknown
+         %47 = OpTypePointer UniformConstant %6
+         %12 = OpVariable %47 UniformConstant
+         %15 = OpTypeSampler
+         %55 = OpTypePointer UniformConstant %15
+         %17 = OpTypeSampledImage %6
+         %16 = OpVariable %55 UniformConstant
+         %37 = OpTypeVector %23 4
+        %109 = OpConstant %23 0
+         %66 = OpConstantComposite %37 %109 %109 %109 %109
+         %56 = OpTypeBool
+         %54 = OpConstantTrue %56
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %50
+         %50 = OpLabel
+         %51 = OpPhi %37 %66 %5 %111 %53
+               OpLoopMerge %52 %53 None
+               OpBranchConditional %54 %53 %52
+         %53 = OpLabel
+        %106 = OpLoad %6 %12
+        %107 = OpLoad %15 %16
+        %110 = OpSampledImage %17 %106 %107
+        %111 = OpImageSampleImplicitLod %37 %110 %66 Bias %109
+               OpBranch %50
+         %52 = 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);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageStructField) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+               OpName %4 "main"
+               OpName %10 "mySampler"
+               OpName %21 "myTexture"
+               OpName %33 "v"
+               OpDecorate %10 RelaxedPrecision
+               OpDecorate %10 DescriptorSet 0
+               OpDecorate %10 Binding 0
+               OpDecorate %11 RelaxedPrecision
+               OpDecorate %21 RelaxedPrecision
+               OpDecorate %21 DescriptorSet 0
+               OpDecorate %21 Binding 1
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %34 RelaxedPrecision
+               OpDecorate %40 RelaxedPrecision
+               OpDecorate %42 RelaxedPrecision
+               OpDecorate %43 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeImage %6 2D 0 0 0 1 Unknown
+          %8 = OpTypeSampledImage %7
+          %9 = OpTypePointer UniformConstant %8
+         %10 = OpVariable %9 UniformConstant
+         %12 = OpTypeInt 32 1
+         %13 = OpConstant %12 2
+         %15 = OpTypeVector %12 2
+         %17 = OpTypeInt 32 0
+         %18 = OpConstant %17 0
+         %20 = OpTypePointer UniformConstant %7
+         %21 = OpVariable %20 UniformConstant
+         %23 = OpConstant %12 1
+         %25 = OpConstant %17 1
+         %27 = OpTypeBool
+         %31 = OpTypeVector %6 4
+         %32 = OpTypePointer Function %31
+         %35 = OpConstantComposite %15 %23 %23
+         %36 = OpConstant %12 3
+         %37 = OpConstant %12 4
+         %38 = OpConstantComposite %15 %36 %37
+        %201 = OpTypeStruct %7 %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %33 = OpVariable %32 Function
+         %11 = OpLoad %8 %10
+         %14 = OpImage %7 %11
+         %22 = OpLoad %7 %21
+        %200 = OpCompositeConstruct %201 %14 %22
+        %202 = OpCompositeExtract %7 %200 0
+        %203 = OpCompositeExtract %7 %200 1
+         %24 = OpImageQuerySizeLod %15 %203 %23
+         %16 = OpImageQuerySizeLod %15 %202 %13
+         %26 = OpCompositeExtract %12 %24 1
+         %19 = OpCompositeExtract %12 %16 0
+         %28 = OpSGreaterThan %27 %19 %26
+               OpSelectionMerge %30 None
+               OpBranchConditional %28 %29 %41
+         %29 = OpLabel
+         %34 = OpLoad %8 %10
+         %39 = OpImage %7 %34
+         %40 = OpImageFetch %31 %39 %35 Lod|ConstOffset %13 %38
+               OpStore %33 %40
+               OpBranch %30
+         %41 = OpLabel
+         %42 = OpLoad %7 %21
+         %43 = OpImageFetch %31 %42 %35 Lod|ConstOffset %13 %38
+               OpStore %33 %43
+               OpBranch %30
+         %30 = 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);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageFunctionParameter) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+               OpName %4 "main"
+               OpName %10 "mySampler"
+               OpName %21 "myTexture"
+               OpName %33 "v"
+               OpDecorate %10 RelaxedPrecision
+               OpDecorate %10 DescriptorSet 0
+               OpDecorate %10 Binding 0
+               OpDecorate %11 RelaxedPrecision
+               OpDecorate %21 RelaxedPrecision
+               OpDecorate %21 DescriptorSet 0
+               OpDecorate %21 Binding 1
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %34 RelaxedPrecision
+               OpDecorate %40 RelaxedPrecision
+               OpDecorate %42 RelaxedPrecision
+               OpDecorate %43 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeImage %6 2D 0 0 0 1 Unknown
+          %8 = OpTypeSampledImage %7
+          %9 = OpTypePointer UniformConstant %8
+         %10 = OpVariable %9 UniformConstant
+         %12 = OpTypeInt 32 1
+         %13 = OpConstant %12 2
+         %15 = OpTypeVector %12 2
+         %17 = OpTypeInt 32 0
+         %18 = OpConstant %17 0
+         %20 = OpTypePointer UniformConstant %7
+         %21 = OpVariable %20 UniformConstant
+         %23 = OpConstant %12 1
+         %25 = OpConstant %17 1
+         %27 = OpTypeBool
+         %31 = OpTypeVector %6 4
+         %32 = OpTypePointer Function %31
+         %35 = OpConstantComposite %15 %23 %23
+         %36 = OpConstant %12 3
+         %37 = OpConstant %12 4
+         %38 = OpConstantComposite %15 %36 %37
+        %201 = OpTypeFunction %15 %7 %12
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %33 = OpVariable %32 Function
+         %11 = OpLoad %8 %10
+         %14 = OpImage %7 %11
+         %16 = OpFunctionCall %15 %200 %14 %13
+         %19 = OpCompositeExtract %12 %16 0
+         %22 = OpLoad %7 %21
+         %24 = OpImageQuerySizeLod %15 %22 %23
+         %26 = OpCompositeExtract %12 %24 1
+         %28 = OpSGreaterThan %27 %19 %26
+               OpSelectionMerge %30 None
+               OpBranchConditional %28 %29 %41
+         %29 = OpLabel
+         %34 = OpLoad %8 %10
+         %39 = OpImage %7 %34
+         %40 = OpImageFetch %31 %39 %35 Lod|ConstOffset %13 %38
+               OpStore %33 %40
+               OpBranch %30
+         %41 = OpLabel
+         %42 = OpLoad %7 %21
+         %43 = OpImageFetch %31 %42 %35 Lod|ConstOffset %13 %38
+               OpStore %33 %43
+               OpBranch %30
+         %30 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %200 = OpFunction %15 None %201
+        %202 = OpFunctionParameter %7
+        %203 = OpFunctionParameter %12
+        %204 = OpLabel
+        %205 = OpImageQuerySizeLod %15 %202 %203
+               OpReturnValue %205
+               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);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateShaderWithImageStorageClass) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+               OpCapability SampledBuffer
+               OpCapability ImageBuffer
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "MainPSPacked"
+               OpExecutionMode %2 OriginUpperLeft
+               OpDecorate %18 DescriptorSet 0
+               OpDecorate %18 Binding 128
+         %49 = OpTypeInt 32 0
+         %50 = OpTypeFloat 32
+         %58 = OpConstant %50 1
+         %66 = OpConstant %49 0
+         %87 = OpTypeVector %50 2
+         %88 = OpConstantComposite %87 %58 %58
+         %17 = OpTypeImage %49 2D 2 0 0 2 R32ui
+        %118 = OpTypePointer UniformConstant %17
+        %123 = OpTypeVector %49 2
+        %132 = OpTypeVoid
+        %133 = OpTypeFunction %132
+        %142 = OpTypePointer Image %49
+         %18 = OpVariable %118 UniformConstant
+          %2 = OpFunction %132 None %133
+        %153 = OpLabel
+        %495 = OpConvertFToU %123 %88
+        %501 = OpImageTexelPointer %142 %18 %495 %66
+               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(), true);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArray) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+               OpDecorate %9 ArrayStride 4
+               OpMemberDecorate %10 0 Offset 0
+               OpDecorate %10 BufferBlock
+               OpDecorate %12 DescriptorSet 0
+               OpDecorate %12 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpTypeRuntimeArray %6
+         %10 = OpTypeStruct %9
+         %11 = OpTypePointer Uniform %10
+         %12 = OpVariable %11 Uniform
+         %13 = OpTypeInt 32 0
+         %16 = OpConstant %6 0
+         %18 = OpConstant %6 1
+         %20 = OpTypePointer Uniform %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %14 = OpArrayLength %13 %12 0
+         %15 = OpBitcast %6 %14
+               OpStore %8 %15
+         %17 = OpLoad %6 %8
+         %19 = OpISub %6 %17 %18
+         %21 = OpAccessChain %20 %12 %16 %19
+               OpStore %21 %16
+               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);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArrayLivesafe) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+               OpDecorate %16 ArrayStride 4
+               OpMemberDecorate %17 0 Offset 0
+               OpDecorate %17 BufferBlock
+               OpDecorate %19 DescriptorSet 0
+               OpDecorate %19 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %16 = OpTypeRuntimeArray %6
+         %17 = OpTypeStruct %16
+         %18 = OpTypePointer Uniform %17
+         %19 = OpVariable %18 Uniform
+         %20 = OpTypeInt 32 0
+         %23 = OpTypeBool
+         %26 = OpConstant %6 32
+         %27 = OpTypePointer Uniform %6
+         %30 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %10
+         %10 = OpLabel
+               OpLoopMerge %12 %13 None
+               OpBranch %14
+         %14 = OpLabel
+         %15 = OpLoad %6 %8
+         %21 = OpArrayLength %20 %19 0
+         %22 = OpBitcast %6 %21
+         %24 = OpSLessThan %23 %15 %22
+               OpBranchConditional %24 %11 %12
+         %11 = OpLabel
+         %25 = OpLoad %6 %8
+         %28 = OpAccessChain %27 %19 %9 %25
+               OpStore %28 %26
+               OpBranch %13
+         %13 = OpLabel
+         %29 = OpLoad %6 %8
+         %31 = OpIAdd %6 %29 %30
+               OpStore %8 %31
+               OpBranch %10
+         %12 = 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(), true);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithWorkgroupVariables) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Workgroup %6
+          %8 = OpVariable %7 Workgroup
+          %9 = OpConstant %6 2
+         %10 = OpVariable %7 Workgroup
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpStore %8 %9
+         %11 = OpLoad %6 %8
+               OpStore %10 %11
+               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(), true);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithAtomics) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+               OpMemberDecorate %9 0 Offset 0
+               OpDecorate %9 BufferBlock
+               OpDecorate %11 DescriptorSet 0
+               OpDecorate %11 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 0
+          %7 = OpTypePointer Function %6
+          %9 = OpTypeStruct %6
+         %10 = OpTypePointer Uniform %9
+         %11 = OpVariable %10 Uniform
+         %12 = OpTypeInt 32 1
+         %13 = OpConstant %12 0
+         %14 = OpTypePointer Uniform %6
+         %16 = OpConstant %6 1
+         %17 = OpConstant %6 0
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %15 = OpAccessChain %14 %11 %13
+         %18 = OpAtomicIAdd %6 %15 %16 %17 %16
+               OpStore %8 %18
+         %19 = OpAccessChain %14 %11 %13
+         %20 = OpLoad %6 %8
+         %21 = OpAtomicUMin %6 %19 %16 %17 %20
+               OpStore %8 %21
+         %22 = OpAccessChain %14 %11 %13
+         %23 = OpLoad %6 %8
+         %24 = OpAtomicUMax %6 %22 %16 %17 %23
+               OpStore %8 %24
+         %25 = OpAccessChain %14 %11 %13
+         %26 = OpLoad %6 %8
+         %27 = OpAtomicAnd %6 %25 %16 %17 %26
+               OpStore %8 %27
+         %28 = OpAccessChain %14 %11 %13
+         %29 = OpLoad %6 %8
+         %30 = OpAtomicOr %6 %28 %16 %17 %29
+               OpStore %8 %30
+         %31 = OpAccessChain %14 %11 %13
+         %32 = OpLoad %6 %8
+         %33 = OpAtomicXor %6 %31 %16 %17 %32
+               OpStore %8 %33
+         %34 = OpAccessChain %14 %11 %13
+         %35 = OpLoad %6 %8
+         %36 = OpAtomicExchange %6 %34 %16 %17 %35
+               OpStore %8 %36
+         %37 = OpAccessChain %14 %11 %13
+         %38 = OpLoad %6 %8
+         %39 = OpAtomicCompareExchange %6 %37 %16 %17 %17 %16 %38
+               OpStore %8 %39
+               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(), true);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
 TEST(FuzzerPassDonateModulesTest, Miscellaneous1) {
   std::string recipient_shader = R"(
                OpCapability Shader
@@ -658,13 +1660,16 @@
   ASSERT_TRUE(IsValid(env, donor_context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
   protobufs::TransformationSequence transformation_sequence;
 
-  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager,
-                                      &fuzzer_context, &transformation_sequence,
-                                      {});
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
 
   fuzzer_pass.DonateSingleModule(donor_context.get(), false);
 
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 b91393e..1e7c643 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
@@ -1553,6 +1553,47 @@
                OpFunctionEnd
   )";
 
+// Some miscellaneous SPIR-V.
+
+const std::string kTestShader6 = 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
+  )";
+
 void AddConstantUniformFact(protobufs::FactSequence* facts,
                             uint32_t descriptor_set, uint32_t binding,
                             std::vector<uint32_t>&& indices, uint32_t value) {
@@ -1591,7 +1632,7 @@
 
   std::vector<fuzzerutil::ModuleSupplier> donor_suppliers;
   for (auto donor : {&kTestShader1, &kTestShader2, &kTestShader3, &kTestShader4,
-                     &kTestShader5}) {
+                     &kTestShader5, &kTestShader6}) {
     donor_suppliers.emplace_back([donor]() {
       return BuildModule(env, kConsoleMessageConsumer, *donor,
                          kFuzzAssembleOption);
@@ -1602,8 +1643,9 @@
     std::vector<uint32_t> fuzzer_binary_out;
     protobufs::TransformationSequence fuzzer_transformation_sequence_out;
 
-    Fuzzer fuzzer(env, seed, true);
-    fuzzer.SetMessageConsumer(kSilentConsumer);
+    spvtools::ValidatorOptions validator_options;
+    Fuzzer fuzzer(env, seed, true, validator_options);
+    fuzzer.SetMessageConsumer(kConsoleMessageConsumer);
     auto fuzzer_result_status =
         fuzzer.Run(binary_in, initial_facts, donor_suppliers,
                    &fuzzer_binary_out, &fuzzer_transformation_sequence_out);
@@ -1613,8 +1655,8 @@
     std::vector<uint32_t> replayer_binary_out;
     protobufs::TransformationSequence replayer_transformation_sequence_out;
 
-    Replayer replayer(env, false);
-    replayer.SetMessageConsumer(kSilentConsumer);
+    Replayer replayer(env, false, validator_options);
+    replayer.SetMessageConsumer(kConsoleMessageConsumer);
     auto replayer_result_status = replayer.Run(
         binary_in, initial_facts, fuzzer_transformation_sequence_out,
         &replayer_binary_out, &replayer_transformation_sequence_out);
@@ -1681,6 +1723,13 @@
                        kNumFuzzerRuns);
 }
 
+TEST(FuzzerReplayerTest, Miscellaneous6) {
+  // Do some fuzzer runs, starting from an initial seed of 57 (seed value chosen
+  // arbitrarily).
+  RunFuzzerAndReplayer(kTestShader6, protobufs::FactSequence(), 57,
+                       kNumFuzzerRuns);
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
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 c906a1e..24b4460 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fuzzer_shrinker_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fuzzer_shrinker_test.cpp
@@ -979,15 +979,19 @@
 // The |step_limit| parameter restricts the number of steps that the shrinker
 // will try; it can be set to something small for a faster (but less thorough)
 // test.
+//
+// The |validator_options| parameter provides validator options that should be
+// used during shrinking.
 void RunAndCheckShrinker(
     const spv_target_env& target_env, const std::vector<uint32_t>& binary_in,
     const protobufs::FactSequence& initial_facts,
     const protobufs::TransformationSequence& transformation_sequence_in,
     const Shrinker::InterestingnessFunction& interestingness_function,
     const std::vector<uint32_t>& expected_binary_out,
-    uint32_t expected_transformations_out_size, uint32_t step_limit) {
+    uint32_t expected_transformations_out_size, uint32_t step_limit,
+    spv_validator_options validator_options) {
   // Run the shrinker.
-  Shrinker shrinker(target_env, step_limit, false);
+  Shrinker shrinker(target_env, step_limit, false, validator_options);
   shrinker.SetMessageConsumer(kSilentConsumer);
 
   std::vector<uint32_t> binary_out;
@@ -1035,7 +1039,8 @@
   // Run the fuzzer and check that it successfully yields a valid binary.
   std::vector<uint32_t> fuzzer_binary_out;
   protobufs::TransformationSequence fuzzer_transformation_sequence_out;
-  Fuzzer fuzzer(env, seed, true);
+  spvtools::ValidatorOptions validator_options;
+  Fuzzer fuzzer(env, seed, true, validator_options);
   fuzzer.SetMessageConsumer(kSilentConsumer);
   auto fuzzer_result_status =
       fuzzer.Run(binary_in, initial_facts, donor_suppliers, &fuzzer_binary_out,
@@ -1048,9 +1053,10 @@
 
   // With the AlwaysInteresting test, we should quickly shrink to the original
   // binary with no transformations remaining.
-  RunAndCheckShrinker(
-      env, binary_in, initial_facts, fuzzer_transformation_sequence_out,
-      AlwaysInteresting().AsFunction(), binary_in, 0, kReasonableStepLimit);
+  RunAndCheckShrinker(env, binary_in, initial_facts,
+                      fuzzer_transformation_sequence_out,
+                      AlwaysInteresting().AsFunction(), binary_in, 0,
+                      kReasonableStepLimit, validator_options);
 
   // With the OnlyInterestingFirstTime test, no shrinking should be achieved.
   RunAndCheckShrinker(
@@ -1058,14 +1064,14 @@
       OnlyInterestingFirstTime().AsFunction(), fuzzer_binary_out,
       static_cast<uint32_t>(
           fuzzer_transformation_sequence_out.transformation_size()),
-      kReasonableStepLimit);
+      kReasonableStepLimit, validator_options);
 
   // The PingPong test is unpredictable; passing an empty expected binary
   // means that we don't check anything beyond that shrinking completes
   // successfully.
-  RunAndCheckShrinker(env, binary_in, initial_facts,
-                      fuzzer_transformation_sequence_out,
-                      PingPong().AsFunction(), {}, 0, kSmallStepLimit);
+  RunAndCheckShrinker(
+      env, binary_in, initial_facts, fuzzer_transformation_sequence_out,
+      PingPong().AsFunction(), {}, 0, kSmallStepLimit, validator_options);
 
   // The InterestingThenRandom test is unpredictable; passing an empty
   // expected binary means that we do not check anything about shrinking
@@ -1073,7 +1079,7 @@
   RunAndCheckShrinker(
       env, binary_in, initial_facts, fuzzer_transformation_sequence_out,
       InterestingThenRandom(PseudoRandomGenerator(seed)).AsFunction(), {}, 0,
-      kSmallStepLimit);
+      kSmallStepLimit, validator_options);
 }
 
 TEST(FuzzerShrinkerTest, Miscellaneous1) {
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 516d371..443c31c 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
@@ -118,169 +118,194 @@
   // Indices 0-5 are in ids 80-85
 
   FactManager fact_manager;
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(54);
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      54);
 
   // Bad: id is not fresh
   ASSERT_FALSE(TransformationAccessChain(
                    43, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id does not exist
   ASSERT_FALSE(TransformationAccessChain(
                    100, 1000, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id is not a type
   ASSERT_FALSE(TransformationAccessChain(
                    100, 5, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id is not a pointer
   ASSERT_FALSE(TransformationAccessChain(
                    100, 23, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: index id does not exist
   ASSERT_FALSE(TransformationAccessChain(
                    100, 43, {1000}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: index id is not a constant
   ASSERT_FALSE(TransformationAccessChain(
                    100, 43, {24}, MakeInstructionDescriptor(25, SpvOpIAdd, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: too many indices
   ASSERT_FALSE(
       TransformationAccessChain(100, 43, {80, 80, 80},
                                 MakeInstructionDescriptor(24, SpvOpLoad, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: index id is out of bounds
   ASSERT_FALSE(
       TransformationAccessChain(100, 43, {80, 83},
                                 MakeInstructionDescriptor(24, SpvOpLoad, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: attempt to insert before variable
   ASSERT_FALSE(TransformationAccessChain(
                    100, 34, {}, MakeInstructionDescriptor(36, SpvOpVariable, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer not available
   ASSERT_FALSE(
       TransformationAccessChain(
           100, 43, {80}, MakeInstructionDescriptor(21, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: instruction descriptor does not identify anything
   ASSERT_FALSE(TransformationAccessChain(
                    100, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 100))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer is null
   ASSERT_FALSE(TransformationAccessChain(
                    100, 45, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer is undef
   ASSERT_FALSE(TransformationAccessChain(
                    100, 46, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer to result type does not exist
   ASSERT_FALSE(TransformationAccessChain(
                    100, 52, {0}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   {
     TransformationAccessChain transformation(
         100, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(100));
+    ASSERT_FALSE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
   }
 
   {
     TransformationAccessChain transformation(
         101, 28, {81}, MakeInstructionDescriptor(42, SpvOpReturn, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101));
+    ASSERT_FALSE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
   }
 
   {
     TransformationAccessChain transformation(
         102, 36, {80, 81}, MakeInstructionDescriptor(37, SpvOpStore, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(102));
+    ASSERT_FALSE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
   }
 
   {
     TransformationAccessChain transformation(
         103, 44, {}, MakeInstructionDescriptor(44, SpvOpStore, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(103));
+    ASSERT_FALSE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(103));
   }
 
   {
     TransformationAccessChain transformation(
         104, 13, {80}, MakeInstructionDescriptor(21, SpvOpAccessChain, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(104));
+    ASSERT_FALSE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(104));
   }
 
   {
     TransformationAccessChain transformation(
         105, 34, {}, MakeInstructionDescriptor(44, SpvOpStore, 1));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(105));
+    ASSERT_FALSE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(105));
   }
 
   {
     TransformationAccessChain transformation(
         106, 38, {}, MakeInstructionDescriptor(40, SpvOpFunctionCall, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(106));
+    ASSERT_FALSE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(106));
   }
 
   {
     TransformationAccessChain transformation(
         107, 14, {}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(107));
+    ASSERT_FALSE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(107));
   }
 
   {
     TransformationAccessChain transformation(
         108, 54, {85, 81, 81}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(108));
+    ASSERT_TRUE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(108));
   }
 
   {
     TransformationAccessChain transformation(
         109, 48, {80, 80}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(109));
+    ASSERT_FALSE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(109));
   }
 
   std::string after_transformation = R"(
@@ -401,19 +426,24 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   {
     TransformationAccessChain transformation(
         100, 11, {}, MakeInstructionDescriptor(5, SpvOpReturn, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
   {
     TransformationAccessChain transformation(
         101, 12, {}, MakeInstructionDescriptor(5, SpvOpReturn, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
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 f51c46b..c603333 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
@@ -43,42 +43,47 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // True and false can both be added as neither is present.
   ASSERT_TRUE(TransformationAddConstantBoolean(7, true).IsApplicable(
-      context.get(), fact_manager));
+      context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddConstantBoolean(7, false).IsApplicable(
-      context.get(), fact_manager));
+      context.get(), transformation_context));
 
   // Id 5 is already taken.
   ASSERT_FALSE(TransformationAddConstantBoolean(5, true).IsApplicable(
-      context.get(), fact_manager));
+      context.get(), transformation_context));
 
   auto add_true = TransformationAddConstantBoolean(7, true);
   auto add_false = TransformationAddConstantBoolean(8, false);
 
-  ASSERT_TRUE(add_true.IsApplicable(context.get(), fact_manager));
-  add_true.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(add_true.IsApplicable(context.get(), transformation_context));
+  add_true.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Having added true, we cannot add it again with the same id.
-  ASSERT_FALSE(add_true.IsApplicable(context.get(), fact_manager));
+  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);
-  ASSERT_TRUE(add_true_again.IsApplicable(context.get(), fact_manager));
-  add_true_again.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      add_true_again.IsApplicable(context.get(), transformation_context));
+  add_true_again.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(add_false.IsApplicable(context.get(), fact_manager));
-  add_false.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(add_false.IsApplicable(context.get(), transformation_context));
+  add_false.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Having added false, we cannot add it again with the same id.
-  ASSERT_FALSE(add_false.IsApplicable(context.get(), fact_manager));
+  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);
-  ASSERT_TRUE(add_false_again.IsApplicable(context.get(), fact_manager));
-  add_false_again.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      add_false_again.IsApplicable(context.get(), transformation_context));
+  add_false_again.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -128,12 +133,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Neither true nor false can be added as OpTypeBool is not present.
   ASSERT_FALSE(TransformationAddConstantBoolean(6, true).IsApplicable(
-      context.get(), fact_manager));
+      context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddConstantBoolean(6, false).IsApplicable(
-      context.get(), fact_manager));
+      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 5ce171b..021bf58 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
@@ -64,19 +64,22 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Too few ids
   ASSERT_FALSE(TransformationAddConstantComposite(103, 8, {100, 101})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Too many ids
   ASSERT_FALSE(TransformationAddConstantComposite(101, 7, {14, 15, 14})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Id already in use
   ASSERT_FALSE(TransformationAddConstantComposite(40, 7, {11, 12})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // %39 is not a type
   ASSERT_FALSE(TransformationAddConstantComposite(100, 39, {11, 12})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   TransformationAddConstantComposite transformations[] = {
       // %100 = OpConstantComposite %7 %11 %12
@@ -101,8 +104,9 @@
       TransformationAddConstantComposite(106, 35, {38, 39, 40})};
 
   for (auto& transformation : transformations) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
   ASSERT_TRUE(IsValid(env, context.get()));
 
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_null_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_null_test.cpp
new file mode 100644
index 0000000..0bfee34
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_null_test.cpp
@@ -0,0 +1,140 @@
+// 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_constant_null.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddConstantNullTest, 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 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 2
+          %9 = OpTypeVector %6 3
+         %10 = OpTypeVector %6 4
+         %11 = OpTypeVector %7 2
+         %20 = OpTypeSampler
+         %21 = OpTypeImage %6 2D 0 0 0 0 Rgba32f
+         %22 = OpTypeSampledImage %21
+          %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);
+
+  // Id already in use
+  ASSERT_FALSE(TransformationAddConstantNull(4, 11).IsApplicable(
+      context.get(), transformation_context));
+  // %1 is not a type
+  ASSERT_FALSE(TransformationAddConstantNull(100, 1).IsApplicable(
+      context.get(), transformation_context));
+
+  // %3 is a function type
+  ASSERT_FALSE(TransformationAddConstantNull(100, 3).IsApplicable(
+      context.get(), transformation_context));
+
+  // %20 is a sampler type
+  ASSERT_FALSE(TransformationAddConstantNull(100, 20).IsApplicable(
+      context.get(), transformation_context));
+
+  // %21 is an image type
+  ASSERT_FALSE(TransformationAddConstantNull(100, 21).IsApplicable(
+      context.get(), transformation_context));
+
+  // %22 is a sampled image type
+  ASSERT_FALSE(TransformationAddConstantNull(100, 22).IsApplicable(
+      context.get(), transformation_context));
+
+  TransformationAddConstantNull transformations[] = {
+      // %100 = OpConstantNull %6
+      TransformationAddConstantNull(100, 6),
+
+      // %101 = OpConstantNull %7
+      TransformationAddConstantNull(101, 7),
+
+      // %102 = OpConstantNull %8
+      TransformationAddConstantNull(102, 8),
+
+      // %103 = OpConstantNull %9
+      TransformationAddConstantNull(103, 9),
+
+      // %104 = OpConstantNull %10
+      TransformationAddConstantNull(104, 10),
+
+      // %105 = OpConstantNull %11
+      TransformationAddConstantNull(105, 11)};
+
+  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
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 2
+          %9 = OpTypeVector %6 3
+         %10 = OpTypeVector %6 4
+         %11 = OpTypeVector %7 2
+         %20 = OpTypeSampler
+         %21 = OpTypeImage %6 2D 0 0 0 0 Rgba32f
+         %22 = OpTypeSampledImage %21
+        %100 = OpConstantNull %6
+        %101 = OpConstantNull %7
+        %102 = OpConstantNull %8
+        %103 = OpConstantNull %9
+        %104 = OpConstantNull %10
+        %105 = OpConstantNull %11
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_scalar_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_scalar_test.cpp
index b156111..5124b7d 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
@@ -62,6 +62,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const float float_values[2] = {3.0, 30.0};
   uint32_t uint_for_float[2];
@@ -87,55 +90,62 @@
   auto bad_type_id_is_pointer = TransformationAddConstantScalar(111, 11, {0});
 
   // Id is already in use.
-  ASSERT_FALSE(bad_id_already_used.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_id_already_used.IsApplicable(context.get(), transformation_context));
 
   // At least one word of data must be provided.
-  ASSERT_FALSE(bad_no_data.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_no_data.IsApplicable(context.get(), transformation_context));
 
   // Cannot give two data words for a 32-bit type.
-  ASSERT_FALSE(bad_too_much_data.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_too_much_data.IsApplicable(context.get(), transformation_context));
 
   // Type id does not exist
-  ASSERT_FALSE(
-      bad_type_id_does_not_exist.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_type_id_does_not_exist.IsApplicable(context.get(),
+                                                       transformation_context));
 
   // Type id is not a type
-  ASSERT_FALSE(
-      bad_type_id_is_not_a_type.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_type_id_is_not_a_type.IsApplicable(context.get(),
+                                                      transformation_context));
 
   // Type id is void
-  ASSERT_FALSE(bad_type_id_is_void.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_type_id_is_void.IsApplicable(context.get(), transformation_context));
 
   // Type id is pointer
-  ASSERT_FALSE(
-      bad_type_id_is_pointer.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_type_id_is_pointer.IsApplicable(context.get(),
+                                                   transformation_context));
 
-  ASSERT_TRUE(add_signed_int_1.IsApplicable(context.get(), fact_manager));
-  add_signed_int_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      add_signed_int_1.IsApplicable(context.get(), transformation_context));
+  add_signed_int_1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(add_signed_int_10.IsApplicable(context.get(), fact_manager));
-  add_signed_int_10.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      add_signed_int_10.IsApplicable(context.get(), transformation_context));
+  add_signed_int_10.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(add_unsigned_int_2.IsApplicable(context.get(), fact_manager));
-  add_unsigned_int_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      add_unsigned_int_2.IsApplicable(context.get(), transformation_context));
+  add_unsigned_int_2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(add_unsigned_int_20.IsApplicable(context.get(), fact_manager));
-  add_unsigned_int_20.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      add_unsigned_int_20.IsApplicable(context.get(), transformation_context));
+  add_unsigned_int_20.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(add_float_3.IsApplicable(context.get(), fact_manager));
-  add_float_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(add_float_3.IsApplicable(context.get(), transformation_context));
+  add_float_3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(add_float_30.IsApplicable(context.get(), fact_manager));
-  add_float_30.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(add_float_30.IsApplicable(context.get(), transformation_context));
+  add_float_30.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_FALSE(bad_add_float_30_id_already_used.IsApplicable(context.get(),
-                                                             fact_manager));
+  ASSERT_FALSE(bad_add_float_30_id_already_used.IsApplicable(
+      context.get(), transformation_context));
 
   std::string after_transformation = R"(
                OpCapability Shader
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_block_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_block_test.cpp
index f89140f..c9be520 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_block_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_block_test.cpp
@@ -46,21 +46,25 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Id 4 is already in use
   ASSERT_FALSE(TransformationAddDeadBlock(4, 5, true)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Id 7 is not a block
   ASSERT_FALSE(TransformationAddDeadBlock(100, 7, true)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   TransformationAddDeadBlock transformation(100, 5, true);
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(fact_manager.BlockIsDead(100));
+  ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(100));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -119,9 +123,12 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   ASSERT_FALSE(TransformationAddDeadBlock(100, 9, true)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadBlockTest, TargetBlockMustNotBeLoopMergeOrContinue) {
@@ -160,13 +167,16 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Bad because 9's successor is the loop continue target.
   ASSERT_FALSE(TransformationAddDeadBlock(100, 9, true)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Bad because 10's successor is the loop merge.
   ASSERT_FALSE(TransformationAddDeadBlock(100, 10, true)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadBlockTest, SourceBlockMustNotBeLoopHead) {
@@ -203,10 +213,13 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Bad because 8 is a loop head.
   ASSERT_FALSE(TransformationAddDeadBlock(100, 8, true)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadBlockTest, OpPhiInTarget) {
@@ -240,13 +253,17 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationAddDeadBlock transformation(100, 5, true);
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(fact_manager.BlockIsDead(100));
+  ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(100));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -309,11 +326,14 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // 9 is a back edge block, so it would not be OK to add a dead block here,
   // as then both 9 and the dead block would branch to the loop header, 8.
   ASSERT_FALSE(TransformationAddDeadBlock(100, 9, true)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace
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 d60fc1f..8400b0c 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
@@ -100,44 +100,47 @@
   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);
 
   const uint32_t merge_block = 16;
 
   // These are all possibilities.
   ASSERT_TRUE(TransformationAddDeadBreak(15, merge_block, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(15, merge_block, false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(21, merge_block, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(21, merge_block, false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(22, merge_block, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(22, merge_block, false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(19, merge_block, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(19, merge_block, false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(23, merge_block, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(23, merge_block, false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(24, merge_block, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(24, merge_block, false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable: 100 is not a block id.
   ASSERT_FALSE(TransformationAddDeadBreak(100, merge_block, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(15, 100, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable: 24 is not a merge block.
   ASSERT_FALSE(TransformationAddDeadBreak(15, 24, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // These are the transformations we will apply.
   auto transformation1 = TransformationAddDeadBreak(15, merge_block, true, {});
@@ -147,28 +150,34 @@
   auto transformation5 = TransformationAddDeadBreak(23, merge_block, true, {});
   auto transformation6 = TransformationAddDeadBreak(24, merge_block, false, {});
 
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation4.IsApplicable(context.get(), transformation_context));
+  transformation4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
-  transformation5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation5.IsApplicable(context.get(), transformation_context));
+  transformation5.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
-  transformation6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation6.IsApplicable(context.get(), transformation_context));
+  transformation6.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -333,6 +342,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // The header and merge blocks
   const uint32_t header_inner = 34;
@@ -354,53 +366,53 @@
 
   // Fine to break from a construct to its merge
   ASSERT_TRUE(TransformationAddDeadBreak(inner_block_1, merge_inner, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(inner_block_2, merge_inner, false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(outer_block_1, merge_outer, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(outer_block_2, merge_outer, false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(outer_block_3, merge_outer, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(outer_block_4, merge_outer, false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(after_block_1, merge_after, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(after_block_2, merge_after, false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
 
   // Not OK to break to the wrong merge (whether enclosing or not)
   ASSERT_FALSE(TransformationAddDeadBreak(inner_block_1, merge_outer, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(inner_block_2, merge_after, false, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(outer_block_1, merge_inner, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(outer_block_2, merge_after, false, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(after_block_1, merge_inner, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(after_block_2, merge_outer, false, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Not OK to break from header (as it does not branch unconditionally)
   ASSERT_FALSE(TransformationAddDeadBreak(header_inner, merge_inner, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(header_outer, merge_outer, false, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(header_after, merge_after, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Not OK to break to non-merge
   ASSERT_FALSE(
       TransformationAddDeadBreak(inner_block_1, inner_block_2, true, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationAddDeadBreak(outer_block_2, after_block_1, false, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(outer_block_1, header_after, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   auto transformation1 =
       TransformationAddDeadBreak(inner_block_1, merge_inner, true, {});
@@ -419,36 +431,44 @@
   auto transformation8 =
       TransformationAddDeadBreak(after_block_2, merge_after, false, {});
 
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation4.IsApplicable(context.get(), transformation_context));
+  transformation4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
-  transformation5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation5.IsApplicable(context.get(), transformation_context));
+  transformation5.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
-  transformation6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation6.IsApplicable(context.get(), transformation_context));
+  transformation6.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager));
-  transformation7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation7.IsApplicable(context.get(), transformation_context));
+  transformation7.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation8.IsApplicable(context.get(), fact_manager));
-  transformation8.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation8.IsApplicable(context.get(), transformation_context));
+  transformation8.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -685,6 +705,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // The header and merge blocks
   const uint32_t header_outer_if = 5;
@@ -715,63 +738,63 @@
   // Fine to branch straight to direct merge block for a construct
   ASSERT_TRUE(TransformationAddDeadBreak(then_outer_switch_block_1,
                                          merge_then_outer_switch, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(then_inner_switch_block_1,
                                          merge_then_inner_switch, false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(then_inner_switch_block_2,
                                          merge_then_inner_switch, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(then_inner_switch_block_3,
                                          merge_then_inner_switch, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(else_switch_block_1, merge_else_switch,
                                          false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(else_switch_block_2, merge_else_switch,
                                          true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(else_switch_block_3, merge_else_switch,
                                          false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationAddDeadBreak(inner_if_1_block_1, merge_inner_if_1, true, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(inner_if_1_block_2, merge_inner_if_1,
                                          false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationAddDeadBreak(inner_if_2_block_1, merge_inner_if_2, true, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Not OK to break out of a switch from a selection construct inside the
   // switch.
   ASSERT_FALSE(TransformationAddDeadBreak(inner_if_1_block_1,
                                           merge_then_outer_switch, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(inner_if_1_block_2,
                                           merge_then_outer_switch, false, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(inner_if_2_block_1,
                                           merge_then_outer_switch, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Some miscellaneous inapplicable cases.
   ASSERT_FALSE(
       TransformationAddDeadBreak(header_outer_if, merge_outer_if, true, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(header_inner_if_1, inner_if_1_block_2,
                                           false, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(header_then_inner_switch,
                                           header_then_outer_switch, false, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(header_else_switch,
                                           then_inner_switch_block_3, false, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(header_inner_if_2, header_inner_if_2,
                                           false, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   auto transformation1 = TransformationAddDeadBreak(
       then_outer_switch_block_1, merge_then_outer_switch, true, {});
@@ -794,44 +817,54 @@
   auto transformation10 = TransformationAddDeadBreak(
       inner_if_2_block_1, merge_inner_if_2, true, {});
 
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation4.IsApplicable(context.get(), transformation_context));
+  transformation4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
-  transformation5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation5.IsApplicable(context.get(), transformation_context));
+  transformation5.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
-  transformation6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation6.IsApplicable(context.get(), transformation_context));
+  transformation6.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager));
-  transformation7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation7.IsApplicable(context.get(), transformation_context));
+  transformation7.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation8.IsApplicable(context.get(), fact_manager));
-  transformation8.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation8.IsApplicable(context.get(), transformation_context));
+  transformation8.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation9.IsApplicable(context.get(), fact_manager));
-  transformation9.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation9.IsApplicable(context.get(), transformation_context));
+  transformation9.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation10.IsApplicable(context.get(), fact_manager));
-  transformation10.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation10.IsApplicable(context.get(), transformation_context));
+  transformation10.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -1094,6 +1127,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // The header and merge blocks
   const uint32_t header_do_while = 6;
@@ -1123,75 +1159,75 @@
   // Fine to break from any loop header to its merge
   ASSERT_TRUE(
       TransformationAddDeadBreak(header_do_while, merge_do_while, true, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(header_for_i, merge_for_i, false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadBreak(header_for_j, merge_for_j, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
 
   // Fine to break from any of the blocks in constructs in the "for j" loop to
   // that loop's merge
   ASSERT_TRUE(
       TransformationAddDeadBreak(block_in_inner_if, merge_for_j, false, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationAddDeadBreak(block_switch_case, merge_for_j, true, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationAddDeadBreak(block_switch_default, merge_for_j, false, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Fine to break from the body of the "for i" loop to that loop's merge
   ASSERT_TRUE(
       TransformationAddDeadBreak(block_in_for_i_loop, merge_for_i, true, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Not OK to break from multiple loops
   ASSERT_FALSE(
       TransformationAddDeadBreak(block_in_inner_if, merge_do_while, false, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationAddDeadBreak(block_switch_case, merge_do_while, true, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(block_switch_default, merge_do_while,
                                           false, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationAddDeadBreak(header_for_j, merge_do_while, true, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Not OK to break loop from its continue construct
   ASSERT_FALSE(
       TransformationAddDeadBreak(continue_do_while, merge_do_while, true, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationAddDeadBreak(continue_for_j, merge_for_j, false, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(continue_for_i, merge_for_i, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Not OK to break out of multiple non-loop constructs if not breaking to a
   // loop merge
   ASSERT_FALSE(
       TransformationAddDeadBreak(block_in_inner_if, merge_if_x_eq_y, false, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationAddDeadBreak(block_switch_case, merge_if_x_eq_y, true, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(block_switch_default, merge_if_x_eq_y,
                                           false, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Some miscellaneous inapplicable transformations
   ASSERT_FALSE(
       TransformationAddDeadBreak(header_if_x_eq_2, header_if_x_eq_y, false, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationAddDeadBreak(merge_if_x_eq_2, merge_switch, false, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationAddDeadBreak(header_switch, header_switch, false, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   auto transformation1 =
       TransformationAddDeadBreak(header_do_while, merge_do_while, true, {});
@@ -1208,32 +1244,39 @@
   auto transformation7 =
       TransformationAddDeadBreak(block_in_for_i_loop, merge_for_i, true, {});
 
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation4.IsApplicable(context.get(), transformation_context));
+  transformation4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
-  transformation5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation5.IsApplicable(context.get(), transformation_context));
+  transformation5.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
-  transformation6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation6.IsApplicable(context.get(), transformation_context));
+  transformation6.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager));
-  transformation7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation7.IsApplicable(context.get(), transformation_context));
+  transformation7.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -1421,12 +1464,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Not OK to break loop from its continue construct
   ASSERT_FALSE(TransformationAddDeadBreak(13, 12, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(23, 12, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadBreakTest, SelectionInContinueConstruct) {
@@ -1509,6 +1555,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const uint32_t loop_merge = 12;
   const uint32_t selection_merge = 24;
@@ -1520,13 +1569,13 @@
   // Not OK to jump from the selection to the loop merge, as this would break
   // from the loop's continue construct.
   ASSERT_FALSE(TransformationAddDeadBreak(in_selection_1, loop_merge, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(in_selection_2, loop_merge, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(in_selection_3, loop_merge, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddDeadBreak(in_selection_4, loop_merge, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // But fine to jump from the selection to its merge.
 
@@ -1539,20 +1588,24 @@
   auto transformation4 =
       TransformationAddDeadBreak(in_selection_4, selection_merge, true, {});
 
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation4.IsApplicable(context.get(), transformation_context));
+  transformation4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -1720,6 +1773,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const uint32_t outer_loop_merge = 34;
   const uint32_t outer_loop_block = 33;
@@ -1729,22 +1785,24 @@
   // Some inapplicable cases
   ASSERT_FALSE(
       TransformationAddDeadBreak(inner_loop_block, outer_loop_merge, true, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationAddDeadBreak(outer_loop_block, inner_loop_merge, true, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   auto transformation1 =
       TransformationAddDeadBreak(inner_loop_block, inner_loop_merge, true, {});
   auto transformation2 =
       TransformationAddDeadBreak(outer_loop_block, outer_loop_merge, true, {});
 
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -1936,36 +1994,39 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Some inapplicable transformations
   // Not applicable because there is already an edge 19->20, so the OpPhis at 20
   // do not need to be updated
   ASSERT_FALSE(TransformationAddDeadBreak(19, 20, true, {13, 21})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Not applicable because two OpPhis (not zero) need to be updated at 20
   ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Not applicable because two OpPhis (not just one) need to be updated at 20
   ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {13})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Not applicable because the given ids do not have types that match the
   // OpPhis at 20, in order
   ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {21, 13})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Not applicable because id 23 is a label
   ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {21, 23})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Not applicable because 101 is not an id
   ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {21, 101})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Not applicable because ids 51 and 47 are not available at the end of block
   // 23
   ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {51, 47})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Not applicable because OpConstantFalse is not present in the module
   ASSERT_FALSE(TransformationAddDeadBreak(19, 20, false, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   auto transformation1 = TransformationAddDeadBreak(19, 20, true, {});
   auto transformation2 = TransformationAddDeadBreak(23, 20, true, {13, 21});
@@ -1973,24 +2034,29 @@
   auto transformation4 = TransformationAddDeadBreak(30, 31, true, {21, 13});
   auto transformation5 = TransformationAddDeadBreak(75, 31, true, {47, 51});
 
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation4.IsApplicable(context.get(), transformation_context));
+  transformation4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
-  transformation5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation5.IsApplicable(context.get(), transformation_context));
+  transformation5.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -2119,9 +2185,13 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto bad_transformation = TransformationAddDeadBreak(100, 101, false, {});
-  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadBreakTest, RespectDominanceRules2) {
@@ -2172,9 +2242,13 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {});
-  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadBreakTest, RespectDominanceRules3) {
@@ -2219,11 +2293,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto good_transformation = TransformationAddDeadBreak(100, 101, false, {11});
-  ASSERT_TRUE(good_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      good_transformation.IsApplicable(context.get(), transformation_context));
 
-  good_transformation.Apply(context.get(), &fact_manager);
+  good_transformation.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -2307,11 +2385,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto good_transformation = TransformationAddDeadBreak(102, 101, false, {11});
-  ASSERT_TRUE(good_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      good_transformation.IsApplicable(context.get(), transformation_context));
 
-  good_transformation.Apply(context.get(), &fact_manager);
+  good_transformation.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -2389,9 +2471,13 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto bad_transformation = TransformationAddDeadBreak(100, 101, false, {});
-  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadBreakTest, RespectDominanceRules6) {
@@ -2446,9 +2532,13 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {});
-  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadBreakTest, RespectDominanceRules7) {
@@ -2505,9 +2595,13 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {});
-  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadBreakTest, RespectDominanceRules8) {
@@ -2551,9 +2645,13 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {});
-  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadBreakTest,
@@ -2597,12 +2695,16 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Bad because 14 comes before 12 in the module, and 14 has no predecessors.
   // This means that an edge from 12 to 14 will lead to 12 dominating 14, which
   // is illegal if 12 appears after 14.
   auto bad_transformation = TransformationAddDeadBreak(12, 14, true, {});
-  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_transformation.IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_continue_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_continue_test.cpp
index ff93da8..07ee3b1 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_continue_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_continue_test.cpp
@@ -97,57 +97,63 @@
   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);
 
   // These are all possibilities.
   ASSERT_TRUE(TransformationAddDeadContinue(11, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadContinue(11, false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadContinue(12, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadContinue(12, false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadContinue(40, true, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationAddDeadContinue(40, false, {})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable: 100 is not a block id.
   ASSERT_FALSE(TransformationAddDeadContinue(100, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable: 10 is not in a loop.
   ASSERT_FALSE(TransformationAddDeadContinue(10, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable: 15 does not branch unconditionally to a single successor.
   ASSERT_FALSE(TransformationAddDeadContinue(15, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable: 13 is not in a loop and has no successor.
   ASSERT_FALSE(TransformationAddDeadContinue(13, true, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable: 14 is the loop continue target, so it's not OK to jump to
   // the loop continue from there.
   ASSERT_FALSE(TransformationAddDeadContinue(14, false, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // These are the transformations we will apply.
   auto transformation1 = TransformationAddDeadContinue(11, true, {});
   auto transformation2 = TransformationAddDeadContinue(12, false, {});
   auto transformation3 = TransformationAddDeadContinue(40, true, {});
 
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -365,19 +371,24 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   std::vector<uint32_t> good = {6, 7, 18, 20, 34, 40, 45, 46, 47, 56, 57};
   std::vector<uint32_t> bad = {5, 8, 9, 19, 21, 22, 33, 41, 58, 59, 60};
 
   for (uint32_t from_block : bad) {
     ASSERT_FALSE(TransformationAddDeadContinue(from_block, true, {})
-                     .IsApplicable(context.get(), fact_manager));
+                     .IsApplicable(context.get(), transformation_context));
   }
   for (uint32_t from_block : good) {
     const TransformationAddDeadContinue transformation(from_block, true, {});
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
-    ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+    ASSERT_FALSE(
+        transformation.IsApplicable(context.get(), transformation_context));
   }
 
   std::string after_transformation = R"(
@@ -600,19 +611,24 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   std::vector<uint32_t> good = {32, 33, 46, 52, 101};
   std::vector<uint32_t> bad = {5, 34, 36, 35, 47, 49, 48};
 
   for (uint32_t from_block : bad) {
     ASSERT_FALSE(TransformationAddDeadContinue(from_block, false, {})
-                     .IsApplicable(context.get(), fact_manager));
+                     .IsApplicable(context.get(), transformation_context));
   }
   for (uint32_t from_block : good) {
     const TransformationAddDeadContinue transformation(from_block, false, {});
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
-    ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+    ASSERT_FALSE(
+        transformation.IsApplicable(context.get(), transformation_context));
   }
 
   std::string after_transformation = R"(
@@ -806,6 +822,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   std::vector<uint32_t> bad = {5, 19, 20, 23, 31, 32, 33, 70};
 
@@ -813,24 +832,28 @@
 
   for (uint32_t from_block : bad) {
     ASSERT_FALSE(TransformationAddDeadContinue(from_block, true, {})
-                     .IsApplicable(context.get(), fact_manager));
+                     .IsApplicable(context.get(), transformation_context));
   }
   auto transformation1 = TransformationAddDeadContinue(29, true, {13, 21});
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
 
   auto transformation2 = TransformationAddDeadContinue(30, true, {22, 46});
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
 
   // 75 already has the continue block as a successor, so we should not provide
   // phi ids.
   auto transformationBad = TransformationAddDeadContinue(75, true, {27, 46});
-  ASSERT_FALSE(transformationBad.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformationBad.IsApplicable(context.get(), transformation_context));
 
   auto transformation3 = TransformationAddDeadContinue(75, true, {});
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -974,26 +997,33 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // This transformation is not applicable because the dead continue from the
   // loop body prevents the definition of %23 later in the loop body from
   // dominating its use in the loop's continue target.
   auto bad_transformation = TransformationAddDeadContinue(13, false, {});
-  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_transformation.IsApplicable(context.get(), transformation_context));
 
   auto good_transformation_1 = TransformationAddDeadContinue(7, false, {});
-  ASSERT_TRUE(good_transformation_1.IsApplicable(context.get(), fact_manager));
-  good_transformation_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(good_transformation_1.IsApplicable(context.get(),
+                                                 transformation_context));
+  good_transformation_1.Apply(context.get(), &transformation_context);
 
   auto good_transformation_2 = TransformationAddDeadContinue(22, false, {});
-  ASSERT_TRUE(good_transformation_2.IsApplicable(context.get(), fact_manager));
-  good_transformation_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(good_transformation_2.IsApplicable(context.get(),
+                                                 transformation_context));
+  good_transformation_2.Apply(context.get(), &transformation_context);
 
   // This transformation is OK, because the definition of %21 in the loop body
   // is only used in an OpPhi in the loop's continue target.
   auto good_transformation_3 = TransformationAddDeadContinue(6, false, {11});
-  ASSERT_TRUE(good_transformation_3.IsApplicable(context.get(), fact_manager));
-  good_transformation_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(good_transformation_3.IsApplicable(context.get(),
+                                                 transformation_context));
+  good_transformation_3.Apply(context.get(), &transformation_context);
 
   std::string after_transformations = R"(
                OpCapability Shader
@@ -1083,11 +1113,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // This transformation would shortcut the part of the loop body that defines
   // an id used after the loop.
   auto bad_transformation = TransformationAddDeadContinue(100, false, {});
-  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadContinueTest, RespectDominanceRules3) {
@@ -1131,11 +1165,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // This transformation would shortcut the part of the loop body that defines
   // an id used after the loop.
   auto bad_transformation = TransformationAddDeadContinue(100, false, {});
-  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadContinueTest, Miscellaneous1) {
@@ -1270,11 +1308,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // This transformation would shortcut the part of the loop body that defines
   // an id used in the continue target.
   auto bad_transformation = TransformationAddDeadContinue(165, false, {});
-  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadContinueTest, Miscellaneous2) {
@@ -1336,11 +1378,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // This transformation would introduce a branch from a continue target to
   // itself.
   auto bad_transformation = TransformationAddDeadContinue(1554, true, {});
-  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadContinueTest, Miscellaneous3) {
@@ -1394,13 +1440,17 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto bad_transformation = TransformationAddDeadContinue(299, false, {});
 
   // The continue edge would connect %299 to the previously-unreachable %236,
   // making %299 dominate %236, and breaking the rule that block ordering must
   // respect dominance.
-  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadContinueTest, Miscellaneous4) {
@@ -1454,13 +1504,17 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto bad_transformation = TransformationAddDeadContinue(10, false, {});
 
   // The continue edge would connect %10 to the previously-unreachable %13,
   // making %10 dominate %13, and breaking the rule that block ordering must
   // respect dominance.
-  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadContinueTest, Miscellaneous5) {
@@ -1506,12 +1560,16 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto bad_transformation = TransformationAddDeadContinue(110, true, {});
 
   // The continue edge would lead to the use of %200 in block %101 no longer
   // being dominated by its definition in block %111.
-  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddDeadContinueTest, Miscellaneous6) {
@@ -1551,10 +1609,14 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto bad_transformation = TransformationAddDeadContinue(10, true, {});
 
-  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_transformation.IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_function_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_function_test.cpp
index aed12dc..bbd915b 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_function_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_function_test.cpp
@@ -59,13 +59,14 @@
 }
 
 // Returns true if and only if every pointer parameter and variable associated
-// with |function_id| in |context| is known by |fact_manager| to be irrelevant,
-// with the exception of |loop_limiter_id|, which must not be irrelevant.  (It
-// can be 0 if no loop limiter is expected, and 0 should not be deemed
-// irrelevant).
+// with |function_id| in |context| is known by |transformation_context| to be
+// irrelevant, with the exception of |loop_limiter_id|, which must not be
+// irrelevant.  (It can be 0 if no loop limiter is expected, and 0 should not be
+// deemed irrelevant).
 bool AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
-    opt::IRContext* context, const FactManager& fact_manager,
-    uint32_t function_id, uint32_t loop_limiter_id) {
+    opt::IRContext* context,
+    const TransformationContext& transformation_context, uint32_t function_id,
+    uint32_t loop_limiter_id) {
   // Look at all the functions until the function of interest is found.
   for (auto& function : *context->module()) {
     if (function.result_id() != function_id) {
@@ -73,15 +74,16 @@
     }
     // Check that the parameters are all irrelevant.
     bool found_non_irrelevant_parameter = false;
-    function.ForEachParam(
-        [context, &fact_manager,
-         &found_non_irrelevant_parameter](opt::Instruction* inst) {
-          if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() ==
-                  SpvOpTypePointer &&
-              !fact_manager.PointeeValueIsIrrelevant(inst->result_id())) {
-            found_non_irrelevant_parameter = true;
-          }
-        });
+    function.ForEachParam([context, &transformation_context,
+                           &found_non_irrelevant_parameter](
+                              opt::Instruction* inst) {
+      if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() ==
+              SpvOpTypePointer &&
+          !transformation_context.GetFactManager()->PointeeValueIsIrrelevant(
+              inst->result_id())) {
+        found_non_irrelevant_parameter = true;
+      }
+    });
     if (found_non_irrelevant_parameter) {
       // A non-irrelevant parameter was found.
       return false;
@@ -96,7 +98,8 @@
       // The variable should be irrelevant if and only if it is not the loop
       // limiter.
       if ((inst.result_id() == loop_limiter_id) ==
-          fact_manager.PointeeValueIsIrrelevant(inst.result_id())) {
+          transformation_context.GetFactManager()->PointeeValueIsIrrelevant(
+              inst.result_id())) {
         return false;
       }
     }
@@ -142,6 +145,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationAddFunction transformation1(std::vector<protobufs::Instruction>(
       {MakeInstructionMessage(
@@ -212,8 +218,9 @@
                               {{SPV_OPERAND_TYPE_ID, {39}}}),
        MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}));
 
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation1 = R"(
@@ -278,12 +285,12 @@
                OpFunctionEnd
   )";
   ASSERT_TRUE(IsEqual(env, after_transformation1, context.get()));
-  ASSERT_TRUE(fact_manager.BlockIsDead(14));
-  ASSERT_TRUE(fact_manager.BlockIsDead(21));
-  ASSERT_TRUE(fact_manager.BlockIsDead(22));
-  ASSERT_TRUE(fact_manager.BlockIsDead(23));
-  ASSERT_TRUE(fact_manager.BlockIsDead(24));
-  ASSERT_TRUE(fact_manager.BlockIsDead(25));
+  ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(14));
+  ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(21));
+  ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(22));
+  ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(23));
+  ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(24));
+  ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(25));
 
   TransformationAddFunction transformation2(std::vector<protobufs::Instruction>(
       {MakeInstructionMessage(
@@ -332,8 +339,9 @@
        MakeInstructionMessage(SpvOpReturn, 0, 0, {}),
        MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}));
 
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation2 = R"(
@@ -414,7 +422,7 @@
                OpFunctionEnd
   )";
   ASSERT_TRUE(IsEqual(env, after_transformation2, context.get()));
-  ASSERT_TRUE(fact_manager.BlockIsDead(16));
+  ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(16));
 }
 
 TEST(TransformationAddFunctionTest, InapplicableTransformations) {
@@ -486,11 +494,14 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // No instructions
   ASSERT_FALSE(
       TransformationAddFunction(std::vector<protobufs::Instruction>({}))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // No function begin
   ASSERT_FALSE(
@@ -499,7 +510,7 @@
               {MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}),
                MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}),
                MakeInstructionMessage(SpvOpLabel, 0, 14, {})}))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // No OpLabel
   ASSERT_FALSE(
@@ -512,7 +523,7 @@
                MakeInstructionMessage(SpvOpReturnValue, 0, 0,
                                       {{SPV_OPERAND_TYPE_ID, {39}}}),
                MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Abrupt end of instructions
   ASSERT_FALSE(TransformationAddFunction(
@@ -521,7 +532,7 @@
                        {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
                          {SpvFunctionControlMaskNone}},
                         {SPV_OPERAND_TYPE_ID, {10}}})}))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // No function end
   ASSERT_FALSE(
@@ -534,7 +545,7 @@
                MakeInstructionMessage(SpvOpLabel, 0, 14, {}),
                MakeInstructionMessage(SpvOpReturnValue, 0, 0,
                                       {{SPV_OPERAND_TYPE_ID, {39}}})}))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddFunctionTest, LoopLimiters) {
@@ -622,20 +633,27 @@
 
   FactManager fact_manager1;
   FactManager fact_manager2;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context1(&fact_manager1,
+                                                validator_options);
+  TransformationContext transformation_context2(&fact_manager2,
+                                                validator_options);
 
   const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context1.get()));
 
   TransformationAddFunction add_dead_function(instructions);
-  ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1));
-  add_dead_function.Apply(context1.get(), &fact_manager1);
+  ASSERT_TRUE(
+      add_dead_function.IsApplicable(context1.get(), transformation_context1));
+  add_dead_function.Apply(context1.get(), &transformation_context1);
   ASSERT_TRUE(IsValid(env, context1.get()));
   // The added function should not be deemed livesafe.
-  ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(30));
+  ASSERT_FALSE(
+      transformation_context1.GetFactManager()->FunctionIsLivesafe(30));
   // All variables/parameters in the function should be deemed irrelevant.
   ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
-      context1.get(), fact_manager1, 30, 0));
+      context1.get(), transformation_context1, 30, 0));
 
   std::string added_as_dead_code = R"(
                OpCapability Shader
@@ -711,16 +729,16 @@
 
   TransformationAddFunction add_livesafe_function(instructions, 100, 10,
                                                   loop_limiters, 0, {});
-  ASSERT_TRUE(
-      add_livesafe_function.IsApplicable(context2.get(), fact_manager2));
-  add_livesafe_function.Apply(context2.get(), &fact_manager2);
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(),
+                                                 transformation_context2));
+  add_livesafe_function.Apply(context2.get(), &transformation_context2);
   ASSERT_TRUE(IsValid(env, context2.get()));
   // The added function should indeed be deemed livesafe.
-  ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(30));
+  ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(30));
   // All variables/parameters in the function should be deemed irrelevant,
   // except the loop limiter.
   ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
-      context2.get(), fact_manager2, 30, 100));
+      context2.get(), transformation_context2, 30, 100));
   std::string added_as_livesafe_code = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
@@ -837,20 +855,27 @@
 
   FactManager fact_manager1;
   FactManager fact_manager2;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context1(&fact_manager1,
+                                                validator_options);
+  TransformationContext transformation_context2(&fact_manager2,
+                                                validator_options);
 
   const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context1.get()));
 
   TransformationAddFunction add_dead_function(instructions);
-  ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1));
-  add_dead_function.Apply(context1.get(), &fact_manager1);
+  ASSERT_TRUE(
+      add_dead_function.IsApplicable(context1.get(), transformation_context1));
+  add_dead_function.Apply(context1.get(), &transformation_context1);
   ASSERT_TRUE(IsValid(env, context1.get()));
   // The added function should not be deemed livesafe.
-  ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(10));
+  ASSERT_FALSE(
+      transformation_context1.GetFactManager()->FunctionIsLivesafe(10));
   // All variables/parameters in the function should be deemed irrelevant.
   ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
-      context1.get(), fact_manager1, 10, 0));
+      context1.get(), transformation_context1, 10, 0));
 
   std::string added_as_dead_code = R"(
                OpCapability Shader
@@ -887,15 +912,15 @@
 
   TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0,
                                                   {});
-  ASSERT_TRUE(
-      add_livesafe_function.IsApplicable(context2.get(), fact_manager2));
-  add_livesafe_function.Apply(context2.get(), &fact_manager2);
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(),
+                                                 transformation_context2));
+  add_livesafe_function.Apply(context2.get(), &transformation_context2);
   ASSERT_TRUE(IsValid(env, context2.get()));
   // The added function should indeed be deemed livesafe.
-  ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(10));
+  ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(10));
   // All variables/parameters in the function should be deemed irrelevant.
   ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
-      context2.get(), fact_manager2, 10, 0));
+      context2.get(), transformation_context2, 10, 0));
   std::string added_as_livesafe_code = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
@@ -985,20 +1010,27 @@
 
   FactManager fact_manager1;
   FactManager fact_manager2;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context1(&fact_manager1,
+                                                validator_options);
+  TransformationContext transformation_context2(&fact_manager2,
+                                                validator_options);
 
   const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context1.get()));
 
   TransformationAddFunction add_dead_function(instructions);
-  ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1));
-  add_dead_function.Apply(context1.get(), &fact_manager1);
+  ASSERT_TRUE(
+      add_dead_function.IsApplicable(context1.get(), transformation_context1));
+  add_dead_function.Apply(context1.get(), &transformation_context1);
   ASSERT_TRUE(IsValid(env, context1.get()));
   // The added function should not be deemed livesafe.
-  ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(10));
+  ASSERT_FALSE(
+      transformation_context1.GetFactManager()->FunctionIsLivesafe(10));
   // All variables/parameters in the function should be deemed irrelevant.
   ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
-      context1.get(), fact_manager1, 10, 0));
+      context1.get(), transformation_context1, 10, 0));
 
   std::string added_as_dead_code = R"(
                OpCapability Shader
@@ -1036,15 +1068,15 @@
 
   TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13,
                                                   {});
-  ASSERT_TRUE(
-      add_livesafe_function.IsApplicable(context2.get(), fact_manager2));
-  add_livesafe_function.Apply(context2.get(), &fact_manager2);
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(),
+                                                 transformation_context2));
+  add_livesafe_function.Apply(context2.get(), &transformation_context2);
   ASSERT_TRUE(IsValid(env, context2.get()));
   // The added function should indeed be deemed livesafe.
-  ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(10));
+  ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(10));
   // All variables/parameters in the function should be deemed irrelevant.
   ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
-      context2.get(), fact_manager2, 10, 0));
+      context2.get(), transformation_context2, 10, 0));
   std::string added_as_livesafe_code = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
@@ -1265,20 +1297,27 @@
 
   FactManager fact_manager1;
   FactManager fact_manager2;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context1(&fact_manager1,
+                                                validator_options);
+  TransformationContext transformation_context2(&fact_manager2,
+                                                validator_options);
 
   const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context1.get()));
 
   TransformationAddFunction add_dead_function(instructions);
-  ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1));
-  add_dead_function.Apply(context1.get(), &fact_manager1);
+  ASSERT_TRUE(
+      add_dead_function.IsApplicable(context1.get(), transformation_context1));
+  add_dead_function.Apply(context1.get(), &transformation_context1);
   ASSERT_TRUE(IsValid(env, context1.get()));
   // The function should not be deemed livesafe
-  ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(12));
+  ASSERT_FALSE(
+      transformation_context1.GetFactManager()->FunctionIsLivesafe(12));
   // All variables/parameters in the function should be deemed irrelevant.
   ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
-      context1.get(), fact_manager1, 12, 0));
+      context1.get(), transformation_context1, 12, 0));
 
   std::string added_as_dead_code = R"(
                OpCapability Shader
@@ -1409,15 +1448,15 @@
 
   TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13,
                                                   access_chain_clamping_info);
-  ASSERT_TRUE(
-      add_livesafe_function.IsApplicable(context2.get(), fact_manager2));
-  add_livesafe_function.Apply(context2.get(), &fact_manager2);
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(),
+                                                 transformation_context2));
+  add_livesafe_function.Apply(context2.get(), &transformation_context2);
   ASSERT_TRUE(IsValid(env, context2.get()));
   // The function should be deemed livesafe
-  ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(12));
+  ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(12));
   // All variables/parameters in the function should be deemed irrelevant.
   ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
-      context2.get(), fact_manager2, 12, 0));
+      context2.get(), transformation_context2, 12, 0));
   std::string added_as_livesafe_code = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
@@ -1585,23 +1624,29 @@
 
   FactManager fact_manager1;
   FactManager fact_manager2;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context1(&fact_manager1,
+                                                validator_options);
+  TransformationContext transformation_context2(&fact_manager2,
+                                                validator_options);
 
   // Mark function 6 as livesafe.
-  fact_manager2.AddFactFunctionIsLivesafe(6);
+  transformation_context2.GetFactManager()->AddFactFunctionIsLivesafe(6);
 
   const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context1.get()));
 
   TransformationAddFunction add_dead_function(instructions);
-  ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1));
-  add_dead_function.Apply(context1.get(), &fact_manager1);
+  ASSERT_TRUE(
+      add_dead_function.IsApplicable(context1.get(), transformation_context1));
+  add_dead_function.Apply(context1.get(), &transformation_context1);
   ASSERT_TRUE(IsValid(env, context1.get()));
   // The function should not be deemed livesafe
-  ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(8));
+  ASSERT_FALSE(transformation_context1.GetFactManager()->FunctionIsLivesafe(8));
   // All variables/parameters in the function should be deemed irrelevant.
   ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
-      context1.get(), fact_manager1, 8, 0));
+      context1.get(), transformation_context1, 8, 0));
 
   std::string added_as_live_or_dead_code = R"(
                OpCapability Shader
@@ -1630,15 +1675,15 @@
 
   TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0,
                                                   {});
-  ASSERT_TRUE(
-      add_livesafe_function.IsApplicable(context2.get(), fact_manager2));
-  add_livesafe_function.Apply(context2.get(), &fact_manager2);
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(),
+                                                 transformation_context2));
+  add_livesafe_function.Apply(context2.get(), &transformation_context2);
   ASSERT_TRUE(IsValid(env, context2.get()));
   // The function should be deemed livesafe
-  ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(8));
+  ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(8));
   // All variables/parameters in the function should be deemed irrelevant.
   ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
-      context2.get(), fact_manager2, 8, 0));
+      context2.get(), transformation_context2, 8, 0));
   ASSERT_TRUE(IsEqual(env, added_as_live_or_dead_code, context2.get()));
 }
 
@@ -1679,20 +1724,26 @@
 
   FactManager fact_manager1;
   FactManager fact_manager2;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context1(&fact_manager1,
+                                                validator_options);
+  TransformationContext transformation_context2(&fact_manager2,
+                                                validator_options);
 
   const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context1.get()));
 
   TransformationAddFunction add_dead_function(instructions);
-  ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1));
-  add_dead_function.Apply(context1.get(), &fact_manager1);
+  ASSERT_TRUE(
+      add_dead_function.IsApplicable(context1.get(), transformation_context1));
+  add_dead_function.Apply(context1.get(), &transformation_context1);
   ASSERT_TRUE(IsValid(env, context1.get()));
   // The function should not be deemed livesafe
-  ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(8));
+  ASSERT_FALSE(transformation_context1.GetFactManager()->FunctionIsLivesafe(8));
   // All variables/parameters in the function should be deemed irrelevant.
   ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
-      context1.get(), fact_manager1, 8, 0));
+      context1.get(), transformation_context1, 8, 0));
 
   std::string added_as_dead_code = R"(
                OpCapability Shader
@@ -1721,8 +1772,8 @@
 
   TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0,
                                                   {});
-  ASSERT_FALSE(
-      add_livesafe_function.IsApplicable(context2.get(), fact_manager2));
+  ASSERT_FALSE(add_livesafe_function.IsApplicable(context2.get(),
+                                                  transformation_context2));
 }
 
 TEST(TransformationAddFunctionTest,
@@ -1804,11 +1855,14 @@
   const auto consumer = nullptr;
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  // Make a sequence of instruction messages corresponding to function %8 in
+  // Make a sequence of instruction messages corresponding to function %6 in
   // |donor|.
   std::vector<protobufs::Instruction> instructions =
       GetInstructionsForFunction(env, consumer, donor, 6);
@@ -1821,8 +1875,9 @@
   loop_limiter_info.set_logical_op_id(105);
   TransformationAddFunction add_livesafe_function(instructions, 100, 32,
                                                   {loop_limiter_info}, 0, {});
-  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
-  add_livesafe_function.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
+                                                 transformation_context));
+  add_livesafe_function.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string expected = R"(
                OpCapability Shader
@@ -1958,11 +2013,14 @@
   const auto consumer = nullptr;
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  // Make a sequence of instruction messages corresponding to function %8 in
+  // Make a sequence of instruction messages corresponding to function %6 in
   // |donor|.
   std::vector<protobufs::Instruction> instructions =
       GetInstructionsForFunction(env, consumer, donor, 6);
@@ -1975,8 +2033,9 @@
   loop_limiter_info.set_logical_op_id(105);
   TransformationAddFunction add_livesafe_function(instructions, 100, 32,
                                                   {loop_limiter_info}, 0, {});
-  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
-  add_livesafe_function.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
+                                                 transformation_context));
+  add_livesafe_function.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string expected = R"(
                OpCapability Shader
@@ -2110,11 +2169,14 @@
   const auto consumer = nullptr;
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  // Make a sequence of instruction messages corresponding to function %8 in
+  // Make a sequence of instruction messages corresponding to function %6 in
   // |donor|.
   std::vector<protobufs::Instruction> instructions =
       GetInstructionsForFunction(env, consumer, donor, 6);
@@ -2127,8 +2189,9 @@
   loop_limiter_info.set_logical_op_id(105);
   TransformationAddFunction add_livesafe_function(instructions, 100, 32,
                                                   {loop_limiter_info}, 0, {});
-  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
-  add_livesafe_function.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
+                                                 transformation_context));
+  add_livesafe_function.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string expected = R"(
                OpCapability Shader
@@ -2254,11 +2317,14 @@
   const auto consumer = nullptr;
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  // Make a sequence of instruction messages corresponding to function %8 in
+  // Make a sequence of instruction messages corresponding to function %6 in
   // |donor|.
   std::vector<protobufs::Instruction> instructions =
       GetInstructionsForFunction(env, consumer, donor, 6);
@@ -2271,8 +2337,9 @@
   loop_limiter_info.set_logical_op_id(105);
   TransformationAddFunction add_livesafe_function(instructions, 100, 32,
                                                   {loop_limiter_info}, 0, {});
-  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
-  add_livesafe_function.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
+                                                 transformation_context));
+  add_livesafe_function.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string expected = R"(
                OpCapability Shader
@@ -2408,11 +2475,14 @@
   const auto consumer = nullptr;
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  // Make a sequence of instruction messages corresponding to function %8 in
+  // Make a sequence of instruction messages corresponding to function %6 in
   // |donor|.
   std::vector<protobufs::Instruction> instructions =
       GetInstructionsForFunction(env, consumer, donor, 6);
@@ -2425,8 +2495,9 @@
   loop_limiter_info.set_logical_op_id(105);
   TransformationAddFunction add_livesafe_function(instructions, 100, 32,
                                                   {loop_limiter_info}, 0, {});
-  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
-  add_livesafe_function.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
+                                                 transformation_context));
+  add_livesafe_function.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string expected = R"(
                OpCapability Shader
@@ -2570,6 +2641,9 @@
   const auto consumer = nullptr;
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -2590,15 +2664,17 @@
                                            {loop_limiter_info}, 0, {});
   // The loop limiter info is not good enough; it does not include ids to patch
   // up the OpPhi at the loop merge.
-  ASSERT_FALSE(no_op_phi_data.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      no_op_phi_data.IsApplicable(context.get(), transformation_context));
 
   // Add a phi id for the new edge from the loop back edge block to the loop
   // merge.
   loop_limiter_info.add_phi_id(28);
   TransformationAddFunction with_op_phi_data(instructions, 100, 28,
                                              {loop_limiter_info}, 0, {});
-  ASSERT_TRUE(with_op_phi_data.IsApplicable(context.get(), fact_manager));
-  with_op_phi_data.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      with_op_phi_data.IsApplicable(context.get(), transformation_context));
+  with_op_phi_data.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string expected = R"(
                OpCapability Shader
@@ -2758,6 +2834,9 @@
   const auto consumer = nullptr;
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -2776,8 +2855,9 @@
 
   TransformationAddFunction transformation(instructions, 100, 28,
                                            {loop_limiter_info}, 0, {});
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string expected = R"(
                OpCapability Shader
@@ -2845,6 +2925,120 @@
   ASSERT_TRUE(IsEqual(env, expected, context.get()));
 }
 
+TEST(TransformationAddFunctionTest, StaticallyOutOfBoundsArrayAccess) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypeInt 32 0
+         %10 = OpConstant %9 3
+         %11 = OpTypeArray %8 %10
+         %12 = OpTypePointer Private %11
+         %13 = OpVariable %12 Private
+         %14 = OpConstant %8 3
+         %20 = OpConstant %8 2
+         %15 = OpConstant %8 1
+         %21 = OpTypeBool
+         %16 = OpTypePointer Private %8
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypeInt 32 0
+         %10 = OpConstant %9 3
+         %11 = OpTypeArray %8 %10
+         %12 = OpTypePointer Private %11
+         %13 = OpVariable %12 Private
+         %14 = OpConstant %8 3
+         %15 = OpConstant %8 1
+         %16 = OpTypePointer Private %8
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+         %17 = OpAccessChain %16 %13 %14
+               OpStore %17 %15
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Make a sequence of instruction messages corresponding to function %6 in
+  // |donor|.
+  std::vector<protobufs::Instruction> instructions =
+      GetInstructionsForFunction(env, consumer, donor, 6);
+
+  TransformationAddFunction add_livesafe_function(
+      instructions, 0, 0, {}, 0, {MakeAccessClampingInfo(17, {{100, 101}})});
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
+                                                 transformation_context));
+  add_livesafe_function.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  std::string expected = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypeInt 32 0
+         %10 = OpConstant %9 3
+         %11 = OpTypeArray %8 %10
+         %12 = OpTypePointer Private %11
+         %13 = OpVariable %12 Private
+         %14 = OpConstant %8 3
+         %20 = OpConstant %8 2
+         %15 = OpConstant %8 1
+         %21 = OpTypeBool
+         %16 = OpTypePointer Private %8
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+        %100 = OpULessThanEqual %21 %14 %20
+        %101 = OpSelect %8 %100 %14 %20
+         %17 = OpAccessChain %16 %13 %101
+               OpStore %17 %15
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, expected, context.get()));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_undef_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_undef_test.cpp
index c14f7e9..8c06db0 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_undef_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_undef_test.cpp
@@ -47,17 +47,20 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Id already in use
-  ASSERT_FALSE(TransformationAddGlobalUndef(4, 11).IsApplicable(context.get(),
-                                                                fact_manager));
+  ASSERT_FALSE(TransformationAddGlobalUndef(4, 11).IsApplicable(
+      context.get(), transformation_context));
   // %1 is not a type
-  ASSERT_FALSE(TransformationAddGlobalUndef(100, 1).IsApplicable(context.get(),
-                                                                 fact_manager));
+  ASSERT_FALSE(TransformationAddGlobalUndef(100, 1).IsApplicable(
+      context.get(), transformation_context));
 
   // %3 is a function type
-  ASSERT_FALSE(TransformationAddGlobalUndef(100, 3).IsApplicable(context.get(),
-                                                                 fact_manager));
+  ASSERT_FALSE(TransformationAddGlobalUndef(100, 3).IsApplicable(
+      context.get(), transformation_context));
 
   TransformationAddGlobalUndef transformations[] = {
       // %100 = OpUndef %6
@@ -79,8 +82,9 @@
       TransformationAddGlobalUndef(105, 11)};
 
   for (auto& transformation : transformations) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
   ASSERT_TRUE(IsValid(env, context.get()));
 
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_variable_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_variable_test.cpp
index 619f068..5c74ca0 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_variable_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_variable_test.cpp
@@ -60,79 +60,105 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Id already in use
-  ASSERT_FALSE(TransformationAddGlobalVariable(4, 10, 0, true)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationAddGlobalVariable(4, 10, SpvStorageClassPrivate, 0, true)
+          .IsApplicable(context.get(), transformation_context));
   // %1 is not a type
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 1, 0, false)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationAddGlobalVariable(100, 1, SpvStorageClassPrivate, 0, false)
+          .IsApplicable(context.get(), transformation_context));
 
   // %7 is not a pointer type
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 7, 0, true)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationAddGlobalVariable(100, 7, SpvStorageClassPrivate, 0, true)
+          .IsApplicable(context.get(), transformation_context));
 
   // %9 does not have Private storage class
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 9, 0, false)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationAddGlobalVariable(100, 9, SpvStorageClassPrivate, 0, false)
+          .IsApplicable(context.get(), transformation_context));
 
   // %15 does not have Private storage class
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 15, 0, true)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationAddGlobalVariable(100, 15, SpvStorageClassPrivate, 0, true)
+          .IsApplicable(context.get(), transformation_context));
 
   // %10 is a pointer to float, while %16 is an int constant
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 16, false)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, SpvStorageClassPrivate,
+                                               16, false)
+                   .IsApplicable(context.get(), transformation_context));
 
   // %10 is a Private pointer to float, while %15 is a variable with type
   // Uniform float pointer
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 15, true)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationAddGlobalVariable(100, 10, SpvStorageClassPrivate, 15, true)
+          .IsApplicable(context.get(), transformation_context));
 
   // %12 is a Private pointer to int, while %10 is a variable with type
   // Private float pointer
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 12, 10, false)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate,
+                                               10, false)
+                   .IsApplicable(context.get(), transformation_context));
 
   // %10 is pointer-to-float, and %14 has type pointer-to-float; that's not OK
   // since the initializer's type should be the *pointee* type.
-  ASSERT_FALSE(TransformationAddGlobalVariable(104, 10, 14, true)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationAddGlobalVariable(104, 10, SpvStorageClassPrivate, 14, true)
+          .IsApplicable(context.get(), transformation_context));
 
   // This would work in principle, but logical addressing does not allow
   // a pointer to a pointer.
-  ASSERT_FALSE(TransformationAddGlobalVariable(104, 17, 14, false)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationAddGlobalVariable(104, 17, SpvStorageClassPrivate,
+                                               14, false)
+                   .IsApplicable(context.get(), transformation_context));
 
   TransformationAddGlobalVariable transformations[] = {
       // %100 = OpVariable %12 Private
-      TransformationAddGlobalVariable(100, 12, 16, true),
+      TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16,
+                                      true),
 
       // %101 = OpVariable %10 Private
-      TransformationAddGlobalVariable(101, 10, 40, false),
+      TransformationAddGlobalVariable(101, 10, SpvStorageClassPrivate, 40,
+                                      false),
 
       // %102 = OpVariable %13 Private
-      TransformationAddGlobalVariable(102, 13, 41, true),
+      TransformationAddGlobalVariable(102, 13, SpvStorageClassPrivate, 41,
+                                      true),
 
       // %103 = OpVariable %12 Private %16
-      TransformationAddGlobalVariable(103, 12, 16, false),
+      TransformationAddGlobalVariable(103, 12, SpvStorageClassPrivate, 16,
+                                      false),
 
       // %104 = OpVariable %19 Private %21
-      TransformationAddGlobalVariable(104, 19, 21, true),
+      TransformationAddGlobalVariable(104, 19, SpvStorageClassPrivate, 21,
+                                      true),
 
       // %105 = OpVariable %19 Private %22
-      TransformationAddGlobalVariable(105, 19, 22, false)};
+      TransformationAddGlobalVariable(105, 19, SpvStorageClassPrivate, 22,
+                                      false)};
 
   for (auto& transformation : transformations) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(100));
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(102));
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(104));
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101));
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(103));
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(105));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(104));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(103));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(105));
 
   ASSERT_TRUE(IsValid(env, context.get()));
 
@@ -223,24 +249,34 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationAddGlobalVariable transformations[] = {
       // %100 = OpVariable %12 Private
-      TransformationAddGlobalVariable(100, 12, 16, true),
+      TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16,
+                                      true),
 
       // %101 = OpVariable %12 Private %16
-      TransformationAddGlobalVariable(101, 12, 16, false),
+      TransformationAddGlobalVariable(101, 12, SpvStorageClassPrivate, 16,
+                                      false),
 
       // %102 = OpVariable %19 Private %21
-      TransformationAddGlobalVariable(102, 19, 21, true)};
+      TransformationAddGlobalVariable(102, 19, SpvStorageClassPrivate, 21,
+                                      true)};
 
   for (auto& transformation : transformations) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(100));
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(102));
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -284,6 +320,85 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationAddGlobalVariableTest, TestAddingWorkgroupGlobals) {
+  // This checks that workgroup globals can be added to a compute shader.
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Workgroup %6
+         %50 = OpConstant %6 2
+          %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);
+
+#ifndef NDEBUG
+  ASSERT_DEATH(
+      TransformationAddGlobalVariable(8, 7, SpvStorageClassWorkgroup, 50, true)
+          .IsApplicable(context.get(), transformation_context),
+      "By construction this transformation should not have an.*initializer "
+      "when Workgroup storage class is used");
+#endif
+
+  TransformationAddGlobalVariable transformations[] = {
+      // %8 = OpVariable %7 Workgroup
+      TransformationAddGlobalVariable(8, 7, SpvStorageClassWorkgroup, 0, true),
+
+      // %10 = OpVariable %7 Workgroup
+      TransformationAddGlobalVariable(10, 7, SpvStorageClassWorkgroup, 0,
+                                      false)};
+
+  for (auto& transformation : transformations) {
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(8));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(10));
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main" %8 %10
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Workgroup %6
+         %50 = OpConstant %6 2
+          %8 = OpVariable %7 Workgroup
+         %10 = OpVariable %7 Workgroup
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_local_variable_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_local_variable_test.cpp
index fd7047f..e989b33 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_local_variable_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_local_variable_test.cpp
@@ -79,66 +79,81 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // A few cases of inapplicable transformations:
   // Id 4 is already in use
   ASSERT_FALSE(TransformationAddLocalVariable(4, 50, 4, 51, true)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Type mismatch between initializer and pointer
   ASSERT_FALSE(TransformationAddLocalVariable(105, 46, 4, 51, true)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Id 5 is not a function
   ASSERT_FALSE(TransformationAddLocalVariable(105, 50, 5, 51, true)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // %105 = OpVariable %50 Function %51
   {
     TransformationAddLocalVariable transformation(105, 50, 4, 51, true);
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
 
   // %104 = OpVariable %41 Function %46
   {
     TransformationAddLocalVariable transformation(104, 41, 4, 46, false);
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
 
   // %103 = OpVariable %35 Function %38
   {
     TransformationAddLocalVariable transformation(103, 35, 4, 38, true);
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
 
   // %102 = OpVariable %31 Function %33
   {
     TransformationAddLocalVariable transformation(102, 31, 4, 33, false);
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
 
   // %101 = OpVariable %19 Function %29
   {
     TransformationAddLocalVariable transformation(101, 19, 4, 29, true);
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
 
   // %100 = OpVariable %8 Function %12
   {
     TransformationAddLocalVariable transformation(100, 8, 4, 12, false);
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
 
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(100));
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(101));
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(102));
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(103));
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(104));
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(105));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(103));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(104));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(105));
 
   std::string after_transformation = R"(
                OpCapability Shader
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_no_contraction_decoration_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_no_contraction_decoration_test.cpp
index b1a87ea..46841a5 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_no_contraction_decoration_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_no_contraction_decoration_test.cpp
@@ -94,23 +94,27 @@
   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: 200 is not an id
   ASSERT_FALSE(TransformationAddNoContractionDecoration(200).IsApplicable(
-      context.get(), fact_manager));
+      context.get(), transformation_context));
   // Invalid: 17 is a block id
   ASSERT_FALSE(TransformationAddNoContractionDecoration(17).IsApplicable(
-      context.get(), fact_manager));
+      context.get(), transformation_context));
   // Invalid: 24 is not arithmetic
   ASSERT_FALSE(TransformationAddNoContractionDecoration(24).IsApplicable(
-      context.get(), fact_manager));
+      context.get(), transformation_context));
 
   // It is valid to add NoContraction to each of these ids (and it's fine to
   // have duplicates of the decoration, in the case of 32).
   for (uint32_t result_id : {32u, 32u, 27u, 29u, 39u}) {
     TransformationAddNoContractionDecoration transformation(result_id);
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_array_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_array_test.cpp
index 2bcbe73..4392f99 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_array_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_array_test.cpp
@@ -54,37 +54,40 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Id already in use
   ASSERT_FALSE(TransformationAddTypeArray(4, 10, 16).IsApplicable(
-      context.get(), fact_manager));
+      context.get(), transformation_context));
   // %1 is not a type
   ASSERT_FALSE(TransformationAddTypeArray(100, 1, 16)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // %3 is a function type
   ASSERT_FALSE(TransformationAddTypeArray(100, 3, 16)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // %2 is not a constant
   ASSERT_FALSE(TransformationAddTypeArray(100, 11, 2)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // %18 is not an integer
   ASSERT_FALSE(TransformationAddTypeArray(100, 11, 18)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // %13 is signed 0
   ASSERT_FALSE(TransformationAddTypeArray(100, 11, 13)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // %14 is negative
   ASSERT_FALSE(TransformationAddTypeArray(100, 11, 14)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // %17 is unsigned 0
   ASSERT_FALSE(TransformationAddTypeArray(100, 11, 17)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   TransformationAddTypeArray transformations[] = {
       // %100 = OpTypeArray %10 %16
@@ -94,8 +97,9 @@
       TransformationAddTypeArray(101, 7, 12)};
 
   for (auto& transformation : transformations) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
   ASSERT_TRUE(IsValid(env, context.get()));
 
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_boolean_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_boolean_test.cpp
index 9975953..60eabd9 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_boolean_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_boolean_test.cpp
@@ -42,19 +42,23 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Not applicable because id 1 is already in use.
-  ASSERT_FALSE(TransformationAddTypeBoolean(1).IsApplicable(context.get(),
-                                                            fact_manager));
+  ASSERT_FALSE(TransformationAddTypeBoolean(1).IsApplicable(
+      context.get(), transformation_context));
 
   auto add_type_bool = TransformationAddTypeBoolean(100);
-  ASSERT_TRUE(add_type_bool.IsApplicable(context.get(), fact_manager));
-  add_type_bool.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      add_type_bool.IsApplicable(context.get(), transformation_context));
+  add_type_bool.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Not applicable as we already have this type now.
-  ASSERT_FALSE(TransformationAddTypeBoolean(101).IsApplicable(context.get(),
-                                                              fact_manager));
+  ASSERT_FALSE(TransformationAddTypeBoolean(101).IsApplicable(
+      context.get(), transformation_context));
 
   std::string after_transformation = R"(
                OpCapability Shader
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_float_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_float_test.cpp
index 67408da..7d17266 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_float_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_float_test.cpp
@@ -42,19 +42,23 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Not applicable because id 1 is already in use.
-  ASSERT_FALSE(TransformationAddTypeFloat(1, 32).IsApplicable(context.get(),
-                                                              fact_manager));
+  ASSERT_FALSE(TransformationAddTypeFloat(1, 32).IsApplicable(
+      context.get(), transformation_context));
 
   auto add_type_float_32 = TransformationAddTypeFloat(100, 32);
-  ASSERT_TRUE(add_type_float_32.IsApplicable(context.get(), fact_manager));
-  add_type_float_32.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      add_type_float_32.IsApplicable(context.get(), transformation_context));
+  add_type_float_32.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Not applicable as we already have this type now.
-  ASSERT_FALSE(TransformationAddTypeFloat(101, 32).IsApplicable(context.get(),
-                                                                fact_manager));
+  ASSERT_FALSE(TransformationAddTypeFloat(101, 32).IsApplicable(
+      context.get(), transformation_context));
 
   std::string after_transformation = R"(
                OpCapability Shader
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_function_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_function_test.cpp
index 46bd436..1557bb8 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_function_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_function_test.cpp
@@ -59,21 +59,24 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Id already in use
   ASSERT_FALSE(TransformationAddTypeFunction(4, 12, {12, 16, 14})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // %1 is not a type
   ASSERT_FALSE(TransformationAddTypeFunction(100, 1, {12, 16, 14})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // %18 is a function type
   ASSERT_FALSE(TransformationAddTypeFunction(100, 12, {18})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // A function of this signature already exists
   ASSERT_FALSE(TransformationAddTypeFunction(100, 17, {14, 16})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   TransformationAddTypeFunction transformations[] = {
       // %100 = OpTypeFunction %12 %12 %16 %14
@@ -86,8 +89,9 @@
       TransformationAddTypeFunction(102, 17, {200, 16})};
 
   for (auto& transformation : transformations) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
   ASSERT_TRUE(IsValid(env, context.get()));
 
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_int_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_int_test.cpp
index c6f884c..63b17c2 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_int_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_int_test.cpp
@@ -42,10 +42,13 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Not applicable because id 1 is already in use.
   ASSERT_FALSE(TransformationAddTypeInt(1, 32, false)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   auto add_type_signed_int_32 = TransformationAddTypeInt(100, 32, true);
   auto add_type_unsigned_int_32 = TransformationAddTypeInt(101, 32, false);
@@ -53,20 +56,21 @@
   auto add_type_unsigned_int_32_again =
       TransformationAddTypeInt(103, 32, false);
 
-  ASSERT_TRUE(add_type_signed_int_32.IsApplicable(context.get(), fact_manager));
-  add_type_signed_int_32.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(add_type_signed_int_32.IsApplicable(context.get(),
+                                                  transformation_context));
+  add_type_signed_int_32.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(
-      add_type_unsigned_int_32.IsApplicable(context.get(), fact_manager));
-  add_type_unsigned_int_32.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(add_type_unsigned_int_32.IsApplicable(context.get(),
+                                                    transformation_context));
+  add_type_unsigned_int_32.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Not applicable as we already have these types now.
-  ASSERT_FALSE(
-      add_type_signed_int_32_again.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(
-      add_type_unsigned_int_32_again.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(add_type_signed_int_32_again.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_FALSE(add_type_unsigned_int_32_again.IsApplicable(
+      context.get(), transformation_context));
 
   std::string after_transformation = R"(
                OpCapability Shader
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_matrix_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_matrix_test.cpp
index 84f27e9..e925012 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_matrix_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_matrix_test.cpp
@@ -47,17 +47,20 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Id already in use
-  ASSERT_FALSE(TransformationAddTypeMatrix(4, 9, 2).IsApplicable(context.get(),
-                                                                 fact_manager));
+  ASSERT_FALSE(TransformationAddTypeMatrix(4, 9, 2).IsApplicable(
+      context.get(), transformation_context));
   // %1 is not a type
   ASSERT_FALSE(TransformationAddTypeMatrix(100, 1, 2).IsApplicable(
-      context.get(), fact_manager));
+      context.get(), transformation_context));
 
   // %11 is not a floating-point vector
   ASSERT_FALSE(TransformationAddTypeMatrix(100, 11, 2)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   TransformationAddTypeMatrix transformations[] = {
       // %100 = OpTypeMatrix %8 2
@@ -88,8 +91,9 @@
       TransformationAddTypeMatrix(108, 10, 4)};
 
   for (auto& transformation : transformations) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
   ASSERT_TRUE(IsValid(env, context.get()));
 
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_pointer_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_pointer_test.cpp
index e36707f..35303e4 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_pointer_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_pointer_test.cpp
@@ -97,6 +97,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto bad_type_id_does_not_exist =
       TransformationAddTypePointer(100, SpvStorageClassFunction, 101);
@@ -122,12 +125,12 @@
   auto good_new_private_pointer_to_uniform_pointer_to_vec2 =
       TransformationAddTypePointer(108, SpvStorageClassPrivate, 107);
 
-  ASSERT_FALSE(
-      bad_type_id_does_not_exist.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(
-      bad_type_id_is_not_type.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(
-      bad_result_id_is_not_fresh.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_type_id_does_not_exist.IsApplicable(context.get(),
+                                                       transformation_context));
+  ASSERT_FALSE(bad_type_id_is_not_type.IsApplicable(context.get(),
+                                                    transformation_context));
+  ASSERT_FALSE(bad_result_id_is_not_fresh.IsApplicable(context.get(),
+                                                       transformation_context));
 
   for (auto& transformation :
        {good_new_private_pointer_to_t, good_new_uniform_pointer_to_t,
@@ -136,8 +139,9 @@
         good_new_private_pointer_to_private_pointer_to_float,
         good_new_uniform_pointer_to_vec2,
         good_new_private_pointer_to_uniform_pointer_to_vec2}) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_struct_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_struct_test.cpp
index ae68c9a..06f78cd 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_struct_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_struct_test.cpp
@@ -47,17 +47,20 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Id already in use
-  ASSERT_FALSE(TransformationAddTypeStruct(4, {}).IsApplicable(context.get(),
-                                                               fact_manager));
+  ASSERT_FALSE(TransformationAddTypeStruct(4, {}).IsApplicable(
+      context.get(), transformation_context));
   // %1 is not a type
   ASSERT_FALSE(TransformationAddTypeStruct(100, {1}).IsApplicable(
-      context.get(), fact_manager));
+      context.get(), transformation_context));
 
   // %3 is a function type
   ASSERT_FALSE(TransformationAddTypeStruct(100, {3}).IsApplicable(
-      context.get(), fact_manager));
+      context.get(), transformation_context));
 
   TransformationAddTypeStruct transformations[] = {
       // %100 = OpTypeStruct %6 %7 %8 %9 %10 %11
@@ -73,8 +76,9 @@
       TransformationAddTypeStruct(103, {6, 6})};
 
   for (auto& transformation : transformations) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
   ASSERT_TRUE(IsValid(env, context.get()));
 
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_vector_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_vector_test.cpp
index 6ac4498..f1252a3 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_vector_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_vector_test.cpp
@@ -45,13 +45,16 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Id already in use
-  ASSERT_FALSE(TransformationAddTypeVector(4, 6, 2).IsApplicable(context.get(),
-                                                                 fact_manager));
+  ASSERT_FALSE(TransformationAddTypeVector(4, 6, 2).IsApplicable(
+      context.get(), transformation_context));
   // %1 is not a type
   ASSERT_FALSE(TransformationAddTypeVector(100, 1, 2).IsApplicable(
-      context.get(), fact_manager));
+      context.get(), transformation_context));
 
   TransformationAddTypeVector transformations[] = {
       // %100 = OpTypeVector %6 2
@@ -67,8 +70,9 @@
       TransformationAddTypeVector(103, 9, 2)};
 
   for (auto& transformation : transformations) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
   ASSERT_TRUE(IsValid(env, context.get()));
 
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_adjust_branch_weights_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_adjust_branch_weights_test.cpp
new file mode 100644
index 0000000..7f8ba31
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_adjust_branch_weights_test.cpp
@@ -0,0 +1,349 @@
+// 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_adjust_branch_weights.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAdjustBranchWeightsTest, IsApplicableTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %51 %27
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %25 "buf"
+               OpMemberName %25 0 "value"
+               OpName %27 ""
+               OpName %51 "color"
+               OpMemberDecorate %25 0 Offset 0
+               OpDecorate %25 Block
+               OpDecorate %27 DescriptorSet 0
+               OpDecorate %27 Binding 0
+               OpDecorate %51 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+        %150 = OpTypeVector %6 2
+         %10 = OpConstant %6 0.300000012
+         %11 = OpConstant %6 0.400000006
+         %12 = OpConstant %6 0.5
+         %13 = OpConstant %6 1
+         %14 = OpConstantComposite %7 %10 %11 %12 %13
+         %15 = OpTypeInt 32 1
+         %18 = OpConstant %15 0
+         %25 = OpTypeStruct %6
+         %26 = OpTypePointer Uniform %25
+         %27 = OpVariable %26 Uniform
+         %28 = OpTypePointer Uniform %6
+         %32 = OpTypeBool
+        %103 = OpConstantTrue %32
+         %34 = OpConstant %6 0.100000001
+         %48 = OpConstant %15 1
+         %50 = OpTypePointer Output %7
+         %51 = OpVariable %50 Output
+        %100 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %101 = OpVariable %100 Function
+        %102 = OpVariable %100 Function
+               OpBranch %19
+         %19 = OpLabel
+         %60 = OpPhi %7 %14 %5 %58 %20
+         %59 = OpPhi %15 %18 %5 %49 %20
+         %29 = OpAccessChain %28 %27 %18
+         %30 = OpLoad %6 %29
+         %31 = OpConvertFToS %15 %30
+         %33 = OpSLessThan %32 %59 %31
+               OpLoopMerge %21 %20 None
+               OpBranchConditional %33 %20 %21 1 2
+         %20 = OpLabel
+         %39 = OpCompositeExtract %6 %60 0
+         %40 = OpFAdd %6 %39 %34
+         %55 = OpCompositeInsert %7 %40 %60 0
+         %44 = OpCompositeExtract %6 %60 1
+         %45 = OpFSub %6 %44 %34
+         %58 = OpCompositeInsert %7 %45 %55 1
+         %49 = OpIAdd %15 %59 %48
+               OpBranch %19
+         %21 = OpLabel
+               OpStore %51 %60
+               OpSelectionMerge %105 None
+               OpBranchConditional %103 %104 %105
+        %104 = OpLabel
+               OpBranch %105
+        %105 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_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 OpBranchConditional instruction with weigths.
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(33, SpvOpBranchConditional, 0);
+  auto transformation =
+      TransformationAdjustBranchWeights(instruction_descriptor, {0, 1});
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests the two branch weights equal to 0.
+  instruction_descriptor =
+      MakeInstructionDescriptor(33, SpvOpBranchConditional, 0);
+  transformation =
+      TransformationAdjustBranchWeights(instruction_descriptor, {0, 0});
+#ifndef NDEBUG
+  ASSERT_DEATH(
+      transformation.IsApplicable(context.get(), transformation_context),
+      "At least one weight must be non-zero");
+#endif
+
+  // Tests 32-bit unsigned integer overflow.
+  instruction_descriptor =
+      MakeInstructionDescriptor(33, SpvOpBranchConditional, 0);
+  transformation = TransformationAdjustBranchWeights(instruction_descriptor,
+                                                     {UINT32_MAX, 0});
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(33, SpvOpBranchConditional, 0);
+  transformation = TransformationAdjustBranchWeights(instruction_descriptor,
+                                                     {1, UINT32_MAX});
+#ifndef NDEBUG
+  ASSERT_DEATH(
+      transformation.IsApplicable(context.get(), transformation_context),
+      "The sum of the two weights must not be greater than UINT32_MAX");
+#endif
+
+  // Tests OpBranchConditional instruction with no weights.
+  instruction_descriptor =
+      MakeInstructionDescriptor(21, SpvOpBranchConditional, 0);
+  transformation =
+      TransformationAdjustBranchWeights(instruction_descriptor, {0, 1});
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests non-OpBranchConditional instructions.
+  instruction_descriptor = MakeInstructionDescriptor(2, SpvOpTypeVoid, 0);
+  transformation =
+      TransformationAdjustBranchWeights(instruction_descriptor, {5, 6});
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  instruction_descriptor = MakeInstructionDescriptor(20, SpvOpLabel, 0);
+  transformation =
+      TransformationAdjustBranchWeights(instruction_descriptor, {1, 2});
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  instruction_descriptor = MakeInstructionDescriptor(49, SpvOpIAdd, 0);
+  transformation =
+      TransformationAdjustBranchWeights(instruction_descriptor, {1, 2});
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationAdjustBranchWeightsTest, ApplyTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %51 %27
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %25 "buf"
+               OpMemberName %25 0 "value"
+               OpName %27 ""
+               OpName %51 "color"
+               OpMemberDecorate %25 0 Offset 0
+               OpDecorate %25 Block
+               OpDecorate %27 DescriptorSet 0
+               OpDecorate %27 Binding 0
+               OpDecorate %51 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+        %150 = OpTypeVector %6 2
+         %10 = OpConstant %6 0.300000012
+         %11 = OpConstant %6 0.400000006
+         %12 = OpConstant %6 0.5
+         %13 = OpConstant %6 1
+         %14 = OpConstantComposite %7 %10 %11 %12 %13
+         %15 = OpTypeInt 32 1
+         %18 = OpConstant %15 0
+         %25 = OpTypeStruct %6
+         %26 = OpTypePointer Uniform %25
+         %27 = OpVariable %26 Uniform
+         %28 = OpTypePointer Uniform %6
+         %32 = OpTypeBool
+        %103 = OpConstantTrue %32
+         %34 = OpConstant %6 0.100000001
+         %48 = OpConstant %15 1
+         %50 = OpTypePointer Output %7
+         %51 = OpVariable %50 Output
+        %100 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %101 = OpVariable %100 Function
+        %102 = OpVariable %100 Function
+               OpBranch %19
+         %19 = OpLabel
+         %60 = OpPhi %7 %14 %5 %58 %20
+         %59 = OpPhi %15 %18 %5 %49 %20
+         %29 = OpAccessChain %28 %27 %18
+         %30 = OpLoad %6 %29
+         %31 = OpConvertFToS %15 %30
+         %33 = OpSLessThan %32 %59 %31
+               OpLoopMerge %21 %20 None
+               OpBranchConditional %33 %20 %21 1 2
+         %20 = OpLabel
+         %39 = OpCompositeExtract %6 %60 0
+         %40 = OpFAdd %6 %39 %34
+         %55 = OpCompositeInsert %7 %40 %60 0
+         %44 = OpCompositeExtract %6 %60 1
+         %45 = OpFSub %6 %44 %34
+         %58 = OpCompositeInsert %7 %45 %55 1
+         %49 = OpIAdd %15 %59 %48
+               OpBranch %19
+         %21 = OpLabel
+               OpStore %51 %60
+               OpSelectionMerge %105 None
+               OpBranchConditional %103 %104 %105
+        %104 = OpLabel
+               OpBranch %105
+        %105 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_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);
+
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(33, SpvOpBranchConditional, 0);
+  auto transformation =
+      TransformationAdjustBranchWeights(instruction_descriptor, {5, 6});
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(21, SpvOpBranchConditional, 0);
+  transformation =
+      TransformationAdjustBranchWeights(instruction_descriptor, {7, 8});
+  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" %51 %27
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %25 "buf"
+               OpMemberName %25 0 "value"
+               OpName %27 ""
+               OpName %51 "color"
+               OpMemberDecorate %25 0 Offset 0
+               OpDecorate %25 Block
+               OpDecorate %27 DescriptorSet 0
+               OpDecorate %27 Binding 0
+               OpDecorate %51 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+        %150 = OpTypeVector %6 2
+         %10 = OpConstant %6 0.300000012
+         %11 = OpConstant %6 0.400000006
+         %12 = OpConstant %6 0.5
+         %13 = OpConstant %6 1
+         %14 = OpConstantComposite %7 %10 %11 %12 %13
+         %15 = OpTypeInt 32 1
+         %18 = OpConstant %15 0
+         %25 = OpTypeStruct %6
+         %26 = OpTypePointer Uniform %25
+         %27 = OpVariable %26 Uniform
+         %28 = OpTypePointer Uniform %6
+         %32 = OpTypeBool
+        %103 = OpConstantTrue %32
+         %34 = OpConstant %6 0.100000001
+         %48 = OpConstant %15 1
+         %50 = OpTypePointer Output %7
+         %51 = OpVariable %50 Output
+        %100 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %101 = OpVariable %100 Function
+        %102 = OpVariable %100 Function
+               OpBranch %19
+         %19 = OpLabel
+         %60 = OpPhi %7 %14 %5 %58 %20
+         %59 = OpPhi %15 %18 %5 %49 %20
+         %29 = OpAccessChain %28 %27 %18
+         %30 = OpLoad %6 %29
+         %31 = OpConvertFToS %15 %30
+         %33 = OpSLessThan %32 %59 %31
+               OpLoopMerge %21 %20 None
+               OpBranchConditional %33 %20 %21 5 6
+         %20 = OpLabel
+         %39 = OpCompositeExtract %6 %60 0
+         %40 = OpFAdd %6 %39 %34
+         %55 = OpCompositeInsert %7 %40 %60 0
+         %44 = OpCompositeExtract %6 %60 1
+         %45 = OpFSub %6 %44 %34
+         %58 = OpCompositeInsert %7 %45 %55 1
+         %49 = OpIAdd %15 %59 %48
+               OpBranch %19
+         %21 = OpLabel
+               OpStore %51 %60
+               OpSelectionMerge %105 None
+               OpBranchConditional %103 %104 %105 7 8
+        %104 = OpLabel
+               OpBranch %105
+        %105 = OpLabel
+               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_composite_construct_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_composite_construct_test.cpp
index d303368..b663866 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
@@ -129,6 +129,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Make a vec2[3]
   TransformationCompositeConstruct make_vec2_array_length_3(
@@ -138,18 +141,18 @@
   TransformationCompositeConstruct make_vec2_array_length_3_bad(
       37, {41, 45, 27, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0),
       200);
-  ASSERT_TRUE(
-      make_vec2_array_length_3.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(
-      make_vec2_array_length_3_bad.IsApplicable(context.get(), fact_manager));
-  make_vec2_array_length_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_vec2_array_length_3.IsApplicable(context.get(),
+                                                    transformation_context));
+  ASSERT_FALSE(make_vec2_array_length_3_bad.IsApplicable(
+      context.get(), transformation_context));
+  make_vec2_array_length_3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(41, {}), MakeDataDescriptor(200, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(45, {}), MakeDataDescriptor(200, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(27, {}), MakeDataDescriptor(200, {2}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(41, {}), MakeDataDescriptor(200, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(45, {}), MakeDataDescriptor(200, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(27, {}), MakeDataDescriptor(200, {2})));
 
   // Make a float[2]
   TransformationCompositeConstruct make_float_array_length_2(
@@ -157,16 +160,16 @@
   // Bad: %41 does not have type float
   TransformationCompositeConstruct make_float_array_length_2_bad(
       9, {41, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201);
-  ASSERT_TRUE(
-      make_float_array_length_2.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(
-      make_float_array_length_2_bad.IsApplicable(context.get(), fact_manager));
-  make_float_array_length_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_float_array_length_2.IsApplicable(context.get(),
+                                                     transformation_context));
+  ASSERT_FALSE(make_float_array_length_2_bad.IsApplicable(
+      context.get(), transformation_context));
+  make_float_array_length_2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(24, {}), MakeDataDescriptor(201, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(40, {}), MakeDataDescriptor(201, {1}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(24, {}), MakeDataDescriptor(201, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(40, {}), MakeDataDescriptor(201, {1})));
 
   // Make a bool[3]
   TransformationCompositeConstruct make_bool_array_length_3(
@@ -176,18 +179,18 @@
   TransformationCompositeConstruct make_bool_array_length_3_bad(
       47, {33, 54, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 0),
       202);
-  ASSERT_TRUE(
-      make_bool_array_length_3.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(
-      make_bool_array_length_3_bad.IsApplicable(context.get(), fact_manager));
-  make_bool_array_length_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_bool_array_length_3.IsApplicable(context.get(),
+                                                    transformation_context));
+  ASSERT_FALSE(make_bool_array_length_3_bad.IsApplicable(
+      context.get(), transformation_context));
+  make_bool_array_length_3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(33, {}), MakeDataDescriptor(202, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {2}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(33, {}), MakeDataDescriptor(202, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {2})));
 
   // make a uvec3[2][2]
   TransformationCompositeConstruct make_uvec3_array_length_2_2(
@@ -195,17 +198,16 @@
   // Bad: Skip count 100 is too large.
   TransformationCompositeConstruct make_uvec3_array_length_2_2_bad(
       58, {33, 54}, MakeInstructionDescriptor(64, SpvOpStore, 100), 203);
-  ASSERT_TRUE(
-      make_uvec3_array_length_2_2.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_uvec3_array_length_2_2_bad.IsApplicable(context.get(),
-                                                            fact_manager));
-  make_uvec3_array_length_2_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_uvec3_array_length_2_2.IsApplicable(context.get(),
+                                                       transformation_context));
+  ASSERT_FALSE(make_uvec3_array_length_2_2_bad.IsApplicable(
+      context.get(), transformation_context));
+  make_uvec3_array_length_2_2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(69, {}), MakeDataDescriptor(203, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(100, {}),
-                                        MakeDataDescriptor(203, {1}),
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(69, {}), MakeDataDescriptor(203, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(100, {}), MakeDataDescriptor(203, {1})));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -393,6 +395,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // make a mat3x4
   TransformationCompositeConstruct make_mat34(
@@ -400,16 +405,17 @@
   // Bad: %35 is mat4x3, not mat3x4.
   TransformationCompositeConstruct make_mat34_bad(
       35, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
-  ASSERT_TRUE(make_mat34.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_mat34_bad.IsApplicable(context.get(), fact_manager));
-  make_mat34.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_mat34.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_mat34_bad.IsApplicable(context.get(), transformation_context));
+  make_mat34.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(28, {}), MakeDataDescriptor(200, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(31, {}), MakeDataDescriptor(200, {2}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(28, {}), MakeDataDescriptor(200, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(31, {}), MakeDataDescriptor(200, {2})));
 
   // make a mat4x3
   TransformationCompositeConstruct make_mat43(
@@ -417,19 +423,19 @@
   // Bad: %25 does not match the matrix's column type.
   TransformationCompositeConstruct make_mat43_bad(
       35, {25, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201);
-  ASSERT_TRUE(make_mat43.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_mat43_bad.IsApplicable(context.get(), fact_manager));
-  make_mat43.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_mat43.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_mat43_bad.IsApplicable(context.get(), transformation_context));
+  make_mat43.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(11, {}), MakeDataDescriptor(201, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(13, {}), MakeDataDescriptor(201, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(16, {}), MakeDataDescriptor(201, {2}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(100, {}),
-                                        MakeDataDescriptor(201, {3}),
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(11, {}), MakeDataDescriptor(201, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(13, {}), MakeDataDescriptor(201, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(16, {}), MakeDataDescriptor(201, {2})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(100, {}), MakeDataDescriptor(201, {3})));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -602,6 +608,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // make an Inner
   TransformationCompositeConstruct make_inner(
@@ -609,14 +618,15 @@
   // Bad: Too few fields to make the struct.
   TransformationCompositeConstruct make_inner_bad(
       9, {25}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
-  ASSERT_TRUE(make_inner.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_inner_bad.IsApplicable(context.get(), fact_manager));
-  make_inner.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_inner.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_inner_bad.IsApplicable(context.get(), transformation_context));
+  make_inner.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(19, {}), MakeDataDescriptor(200, {1}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(19, {}), MakeDataDescriptor(200, {1})));
 
   // make an Outer
   TransformationCompositeConstruct make_outer(
@@ -626,17 +636,17 @@
   TransformationCompositeConstruct make_outer_bad(
       33, {46, 200, 56},
       MakeInstructionDescriptor(200, SpvOpCompositeConstruct, 0), 201);
-  ASSERT_TRUE(make_outer.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_outer_bad.IsApplicable(context.get(), fact_manager));
-  make_outer.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_outer.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_outer_bad.IsApplicable(context.get(), transformation_context));
+  make_outer.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(46, {}), MakeDataDescriptor(201, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(200, {}),
-                                        MakeDataDescriptor(201, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(56, {}), MakeDataDescriptor(201, {2}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(46, {}), MakeDataDescriptor(201, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(200, {}), MakeDataDescriptor(201, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(56, {}), MakeDataDescriptor(201, {2})));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -922,20 +932,24 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationCompositeConstruct make_vec2(
       7, {17, 11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200);
   // Bad: not enough data for a vec2
   TransformationCompositeConstruct make_vec2_bad(
       7, {11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200);
-  ASSERT_TRUE(make_vec2.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_vec2_bad.IsApplicable(context.get(), fact_manager));
-  make_vec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_vec2.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_vec2_bad.IsApplicable(context.get(), transformation_context));
+  make_vec2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(17, {}), MakeDataDescriptor(200, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(11, {}), MakeDataDescriptor(200, {1}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(17, {}), MakeDataDescriptor(200, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(11, {}), MakeDataDescriptor(200, {1})));
 
   TransformationCompositeConstruct make_vec3(
       25, {12, 32}, MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0),
@@ -944,18 +958,17 @@
   TransformationCompositeConstruct make_vec3_bad(
       25, {12, 32, 32},
       MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0), 201);
-  ASSERT_TRUE(make_vec3.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_vec3_bad.IsApplicable(context.get(), fact_manager));
-  make_vec3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_vec3.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_vec3_bad.IsApplicable(context.get(), transformation_context));
+  make_vec3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(12, {0}),
-                                        MakeDataDescriptor(201, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(12, {1}),
-                                        MakeDataDescriptor(201, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(32, {}), MakeDataDescriptor(201, {2}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(12, {0}), MakeDataDescriptor(201, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(12, {1}), MakeDataDescriptor(201, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(32, {}), MakeDataDescriptor(201, {2})));
 
   TransformationCompositeConstruct make_vec4(
       44, {32, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0),
@@ -964,34 +977,34 @@
   TransformationCompositeConstruct make_vec4_bad(
       44, {48, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0),
       202);
-  ASSERT_TRUE(make_vec4.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_vec4_bad.IsApplicable(context.get(), fact_manager));
-  make_vec4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_vec4.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_vec4_bad.IsApplicable(context.get(), transformation_context));
+  make_vec4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(10, {}), MakeDataDescriptor(202, {2}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(11, {}), MakeDataDescriptor(202, {3}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(10, {}), MakeDataDescriptor(202, {2})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(11, {}), MakeDataDescriptor(202, {3})));
 
   TransformationCompositeConstruct make_ivec2(
       51, {126, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203);
   // Bad: if 128 is not available at the instruction that defines 128
   TransformationCompositeConstruct make_ivec2_bad(
       51, {128, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203);
-  ASSERT_TRUE(make_ivec2.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_ivec2_bad.IsApplicable(context.get(), fact_manager));
-  make_ivec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_ivec2.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_ivec2_bad.IsApplicable(context.get(), transformation_context));
+  make_ivec2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(126, {}),
-                                        MakeDataDescriptor(203, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(120, {}),
-                                        MakeDataDescriptor(203, {1}),
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(126, {}), MakeDataDescriptor(203, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(120, {}), MakeDataDescriptor(203, {1})));
 
   TransformationCompositeConstruct make_ivec3(
       114, {56, 117, 56}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
@@ -1000,17 +1013,17 @@
   TransformationCompositeConstruct make_ivec3_bad(
       114, {56, 117, 1300}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
       204);
-  ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_ivec3_bad.IsApplicable(context.get(), fact_manager));
-  make_ivec3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_ivec3_bad.IsApplicable(context.get(), transformation_context));
+  make_ivec3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(117, {}),
-                                        MakeDataDescriptor(204, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {2}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(117, {}), MakeDataDescriptor(204, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {2})));
 
   TransformationCompositeConstruct make_ivec4(
       122, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
@@ -1019,51 +1032,50 @@
   TransformationCompositeConstruct make_ivec4_bad(
       86, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
       205);
-  ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_ivec4_bad.IsApplicable(context.get(), fact_manager));
-  make_ivec4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_ivec4_bad.IsApplicable(context.get(), transformation_context));
+  make_ivec4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(56, {}), MakeDataDescriptor(205, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(117, {}),
-                                        MakeDataDescriptor(205, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(117, {}),
-                                        MakeDataDescriptor(205, {2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(117, {}),
-                                        MakeDataDescriptor(205, {3}),
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(56, {}), MakeDataDescriptor(205, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {2})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {3})));
 
   TransformationCompositeConstruct make_uvec2(
       86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 0), 206);
   TransformationCompositeConstruct make_uvec2_bad(
       86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 200), 206);
-  ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_uvec2_bad.IsApplicable(context.get(), fact_manager));
-  make_uvec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_uvec2_bad.IsApplicable(context.get(), transformation_context));
+  make_uvec2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(18, {}), MakeDataDescriptor(206, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(38, {}), MakeDataDescriptor(206, {1}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(18, {}), MakeDataDescriptor(206, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(38, {}), MakeDataDescriptor(206, {1})));
 
   TransformationCompositeConstruct make_uvec3(
       59, {14, 18, 136}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
   // Bad because 1300 is not an id
   TransformationCompositeConstruct make_uvec3_bad(
       59, {14, 18, 1300}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
-  ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_uvec3_bad.IsApplicable(context.get(), fact_manager));
-  make_uvec3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_uvec3_bad.IsApplicable(context.get(), transformation_context));
+  make_uvec3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(14, {}), MakeDataDescriptor(207, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(18, {}), MakeDataDescriptor(207, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(136, {}),
-                                        MakeDataDescriptor(207, {2}),
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(14, {}), MakeDataDescriptor(207, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(18, {}), MakeDataDescriptor(207, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(136, {}), MakeDataDescriptor(207, {2})));
 
   TransformationCompositeConstruct make_uvec4(
       131, {14, 18, 136, 136},
@@ -1072,20 +1084,19 @@
   TransformationCompositeConstruct make_uvec4_bad(
       86, {14, 18, 136, 136},
       MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208);
-  ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_uvec4_bad.IsApplicable(context.get(), fact_manager));
-  make_uvec4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_uvec4_bad.IsApplicable(context.get(), transformation_context));
+  make_uvec4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(14, {}), MakeDataDescriptor(208, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(18, {}), MakeDataDescriptor(208, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(136, {}),
-                                        MakeDataDescriptor(208, {2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(136, {}),
-                                        MakeDataDescriptor(208, {3}),
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(14, {}), MakeDataDescriptor(208, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(18, {}), MakeDataDescriptor(208, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(136, {}), MakeDataDescriptor(208, {2})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(136, {}), MakeDataDescriptor(208, {3})));
 
   TransformationCompositeConstruct make_bvec2(
       102,
@@ -1102,55 +1113,51 @@
           41,
       },
       MakeInstructionDescriptor(0, SpvOpExtInstImport, 0), 209);
-  ASSERT_TRUE(make_bvec2.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_bvec2_bad.IsApplicable(context.get(), fact_manager));
-  make_bvec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_bvec2.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_bvec2_bad.IsApplicable(context.get(), transformation_context));
+  make_bvec2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(111, {}),
-                                        MakeDataDescriptor(209, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(41, {}), MakeDataDescriptor(209, {1}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(111, {}), MakeDataDescriptor(209, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(41, {}), MakeDataDescriptor(209, {1})));
 
   TransformationCompositeConstruct make_bvec3(
       93, {108, 73}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210);
   // Bad because there are too many components for a bvec3
   TransformationCompositeConstruct make_bvec3_bad(
       93, {108, 108}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210);
-  ASSERT_TRUE(make_bvec3.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_bvec3_bad.IsApplicable(context.get(), fact_manager));
-  make_bvec3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_bvec3.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_bvec3_bad.IsApplicable(context.get(), transformation_context));
+  make_bvec3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {0}),
-                                        MakeDataDescriptor(210, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {1}),
-                                        MakeDataDescriptor(210, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(73, {}), MakeDataDescriptor(210, {2}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {0}), MakeDataDescriptor(210, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {1}), MakeDataDescriptor(210, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(73, {}), MakeDataDescriptor(210, {2})));
 
   TransformationCompositeConstruct make_bvec4(
       70, {108, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211);
   // Bad because 21 is a type, not a result id
   TransformationCompositeConstruct make_bvec4_bad(
       70, {21, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211);
-  ASSERT_TRUE(make_bvec4.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_bvec4_bad.IsApplicable(context.get(), fact_manager));
-  make_bvec4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_bvec4.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_bvec4_bad.IsApplicable(context.get(), transformation_context));
+  make_bvec4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {0}),
-                                        MakeDataDescriptor(211, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {1}),
-                                        MakeDataDescriptor(211, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {0}),
-                                        MakeDataDescriptor(211, {2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {1}),
-                                        MakeDataDescriptor(211, {3}),
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {0}), MakeDataDescriptor(211, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {1}), MakeDataDescriptor(211, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {0}), MakeDataDescriptor(211, {2})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {1}), MakeDataDescriptor(211, {3})));
 
   std::string after_transformation = R"(
                OpCapability Shader
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 5cc2115..a7674a6 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
@@ -96,100 +96,103 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Instruction does not exist.
   ASSERT_FALSE(TransformationCompositeExtract(
                    MakeInstructionDescriptor(36, SpvOpIAdd, 0), 200, 101, {0})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Id for composite is not a composite.
   ASSERT_FALSE(TransformationCompositeExtract(
                    MakeInstructionDescriptor(36, SpvOpIAdd, 0), 200, 27, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Composite does not dominate instruction being inserted before.
   ASSERT_FALSE(
       TransformationCompositeExtract(
           MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 200, 101, {0})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Too many indices for extraction from struct composite.
   ASSERT_FALSE(
       TransformationCompositeExtract(
           MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 200, 101, {0, 0})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Too many indices for extraction from struct composite.
   ASSERT_FALSE(
       TransformationCompositeExtract(
           MakeInstructionDescriptor(13, SpvOpIEqual, 0), 200, 104, {0, 0, 0})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Out of bounds index for extraction from struct composite.
   ASSERT_FALSE(
       TransformationCompositeExtract(
           MakeInstructionDescriptor(13, SpvOpIEqual, 0), 200, 104, {0, 3})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Result id already used.
   ASSERT_FALSE(TransformationCompositeExtract(
                    MakeInstructionDescriptor(35, SpvOpFAdd, 0), 80, 103, {0})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   TransformationCompositeExtract transformation_1(
       MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2});
-  ASSERT_TRUE(transformation_1.IsApplicable(context.get(), fact_manager));
-  transformation_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation_1.IsApplicable(context.get(), transformation_context));
+  transformation_1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   TransformationCompositeExtract transformation_2(
       MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 202, 104, {0, 2});
-  ASSERT_TRUE(transformation_2.IsApplicable(context.get(), fact_manager));
-  transformation_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation_2.IsApplicable(context.get(), transformation_context));
+  transformation_2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   TransformationCompositeExtract transformation_3(
       MakeInstructionDescriptor(29, SpvOpAccessChain, 0), 203, 104, {0});
-  ASSERT_TRUE(transformation_3.IsApplicable(context.get(), fact_manager));
-  transformation_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation_3.IsApplicable(context.get(), transformation_context));
+  transformation_3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   TransformationCompositeExtract transformation_4(
       MakeInstructionDescriptor(24, SpvOpStore, 0), 204, 101, {0});
-  ASSERT_TRUE(transformation_4.IsApplicable(context.get(), fact_manager));
-  transformation_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation_4.IsApplicable(context.get(), transformation_context));
+  transformation_4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   TransformationCompositeExtract transformation_5(
       MakeInstructionDescriptor(29, SpvOpBranch, 0), 205, 102, {2});
-  ASSERT_TRUE(transformation_5.IsApplicable(context.get(), fact_manager));
-  transformation_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation_5.IsApplicable(context.get(), transformation_context));
+  transformation_5.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   TransformationCompositeExtract transformation_6(
       MakeInstructionDescriptor(37, SpvOpReturn, 0), 206, 103, {1});
-  ASSERT_TRUE(transformation_6.IsApplicable(context.get(), fact_manager));
-  transformation_6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation_6.IsApplicable(context.get(), transformation_context));
+  transformation_6.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(201, {}),
-                                        MakeDataDescriptor(100, {2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(202, {}),
-                                        MakeDataDescriptor(104, {0, 2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(203, {}),
-                                        MakeDataDescriptor(104, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(204, {}),
-                                        MakeDataDescriptor(101, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(205, {}),
-                                        MakeDataDescriptor(102, {2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(206, {}),
-                                        MakeDataDescriptor(103, {1}),
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(201, {}), MakeDataDescriptor(100, {2})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(202, {}), MakeDataDescriptor(104, {0, 2})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(203, {}), MakeDataDescriptor(104, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(204, {}), MakeDataDescriptor(101, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(205, {}), MakeDataDescriptor(102, {2})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(206, {}), MakeDataDescriptor(103, {1})));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -348,49 +351,52 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Cannot insert before the OpVariables of a function.
   ASSERT_FALSE(
       TransformationCompositeExtract(
           MakeInstructionDescriptor(101, SpvOpVariable, 0), 200, 14, {0})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationCompositeExtract(
           MakeInstructionDescriptor(101, SpvOpVariable, 1), 200, 14, {1})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationCompositeExtract(
           MakeInstructionDescriptor(102, SpvOpVariable, 0), 200, 14, {1})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // OK to insert right after the OpVariables.
   ASSERT_FALSE(TransformationCompositeExtract(
                    MakeInstructionDescriptor(102, SpvOpBranch, 1), 200, 14, {1})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert before the OpPhis of a block.
   ASSERT_FALSE(TransformationCompositeExtract(
                    MakeInstructionDescriptor(60, SpvOpPhi, 0), 200, 14, {2})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationCompositeExtract(
                    MakeInstructionDescriptor(59, SpvOpPhi, 0), 200, 14, {3})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // OK to insert after the OpPhis.
   ASSERT_TRUE(
       TransformationCompositeExtract(
           MakeInstructionDescriptor(59, SpvOpAccessChain, 0), 200, 14, {3})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert before OpLoopMerge
   ASSERT_FALSE(TransformationCompositeExtract(
                    MakeInstructionDescriptor(33, SpvOpBranchConditional, 0),
                    200, 14, {3})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert before OpSelectionMerge
   ASSERT_FALSE(TransformationCompositeExtract(
                    MakeInstructionDescriptor(21, SpvOpBranchConditional, 0),
                    200, 14, {2})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_compute_data_synonym_fact_closure_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_compute_data_synonym_fact_closure_test.cpp
new file mode 100644
index 0000000..5fa74b7
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_compute_data_synonym_fact_closure_test.cpp
@@ -0,0 +1,377 @@
+// 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_compute_data_synonym_fact_closure.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationComputeDataSynonymFactClosureTest, DataSynonymFacts) {
+  // The SPIR-V types and constants come from the following code.  The body of
+  // the SPIR-V function then constructs a composite that is synonymous with
+  // myT.
+  //
+  // #version 310 es
+  //
+  // precision highp float;
+  //
+  // struct S {
+  //   int a;
+  //   uvec2 b;
+  // };
+  //
+  // struct T {
+  //   bool c[5];
+  //   mat4x2 d;
+  //   S e;
+  // };
+  //
+  // void main() {
+  //   T myT = T(bool[5](true, false, true, false, true),
+  //             mat4x2(vec2(1.0, 2.0), vec2(3.0, 4.0),
+  // 	           vec2(5.0, 6.0), vec2(7.0, 8.0)),
+  //             S(10, uvec2(100u, 200u)));
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %15 "S"
+               OpMemberName %15 0 "a"
+               OpMemberName %15 1 "b"
+               OpName %16 "T"
+               OpMemberName %16 0 "c"
+               OpMemberName %16 1 "d"
+               OpMemberName %16 2 "e"
+               OpName %18 "myT"
+               OpMemberDecorate %15 0 RelaxedPrecision
+               OpMemberDecorate %15 1 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 5
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypeFloat 32
+         %11 = OpTypeVector %10 2
+         %12 = OpTypeMatrix %11 4
+         %13 = OpTypeInt 32 1
+         %14 = OpTypeVector %7 2
+         %15 = OpTypeStruct %13 %14
+         %16 = OpTypeStruct %9 %12 %15
+         %17 = OpTypePointer Function %16
+         %19 = OpConstantTrue %6
+         %20 = OpConstantFalse %6
+         %21 = OpConstantComposite %9 %19 %20 %19 %20 %19
+         %22 = OpConstant %10 1
+         %23 = OpConstant %10 2
+         %24 = OpConstantComposite %11 %22 %23
+         %25 = OpConstant %10 3
+         %26 = OpConstant %10 4
+         %27 = OpConstantComposite %11 %25 %26
+         %28 = OpConstant %10 5
+         %29 = OpConstant %10 6
+         %30 = OpConstantComposite %11 %28 %29
+         %31 = OpConstant %10 7
+         %32 = OpConstant %10 8
+         %33 = OpConstantComposite %11 %31 %32
+         %34 = OpConstantComposite %12 %24 %27 %30 %33
+         %35 = OpConstant %13 10
+         %36 = OpConstant %7 100
+         %37 = OpConstant %7 200
+         %38 = OpConstantComposite %14 %36 %37
+         %39 = OpConstantComposite %15 %35 %38
+         %40 = OpConstantComposite %16 %21 %34 %39
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %18 = OpVariable %17 Function
+               OpStore %18 %40
+        %100 = OpCompositeConstruct %9 %19 %20 %19 %20 %19
+        %101 = OpCompositeConstruct %11 %22 %23
+        %102 = OpCompositeConstruct %11 %25 %26
+        %103 = OpCompositeConstruct %11 %28 %29
+        %104 = OpCompositeConstruct %11 %31 %32
+        %105 = OpCompositeConstruct %12 %101 %102 %103 %104
+        %106 = OpCompositeConstruct %14 %36 %37
+        %107 = OpCompositeConstruct %15 %35 %106
+        %108 = OpCompositeConstruct %16 %100 %105 %107
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  ASSERT_TRUE(TransformationComputeDataSynonymFactClosure(100).IsApplicable(
+      context.get(), transformation_context));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {}),
+                                         MakeDataDescriptor(101, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
+                                         MakeDataDescriptor(101, {0})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {1}),
+                                         MakeDataDescriptor(101, {1})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
+                                         MakeDataDescriptor(101, {1})));
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(24, {}),
+                                  MakeDataDescriptor(101, {}), context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {}),
+                                        MakeDataDescriptor(101, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
+                                        MakeDataDescriptor(101, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {1}),
+                                        MakeDataDescriptor(101, {1})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
+                                         MakeDataDescriptor(101, {1})));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {}),
+                                         MakeDataDescriptor(102, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {0}),
+                                         MakeDataDescriptor(102, {0})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {1}),
+                                         MakeDataDescriptor(102, {1})));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(27, {0}),
+                                  MakeDataDescriptor(102, {0}), context.get());
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {}),
+                                         MakeDataDescriptor(102, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {0}),
+                                        MakeDataDescriptor(102, {0})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {1}),
+                                         MakeDataDescriptor(102, {1})));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(27, {1}),
+                                  MakeDataDescriptor(102, {1}), context.get());
+
+  TransformationComputeDataSynonymFactClosure(100).Apply(
+      context.get(), &transformation_context);
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {}),
+                                        MakeDataDescriptor(102, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {0}),
+                                        MakeDataDescriptor(102, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {1}),
+                                        MakeDataDescriptor(102, {1})));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {}),
+                                         MakeDataDescriptor(103, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {0}),
+                                         MakeDataDescriptor(103, {0})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1}),
+                                         MakeDataDescriptor(103, {1})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {}),
+                                         MakeDataDescriptor(104, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {0}),
+                                         MakeDataDescriptor(104, {0})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {1}),
+                                         MakeDataDescriptor(104, {1})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {}),
+                                         MakeDataDescriptor(105, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {0}),
+                                         MakeDataDescriptor(105, {0})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {1}),
+                                         MakeDataDescriptor(105, {1})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {2}),
+                                         MakeDataDescriptor(105, {2})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {3}),
+                                         MakeDataDescriptor(105, {3})));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(30, {}),
+                                  MakeDataDescriptor(103, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(33, {}),
+                                  MakeDataDescriptor(104, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {0}),
+                                  MakeDataDescriptor(105, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {1}),
+                                  MakeDataDescriptor(105, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {2}),
+                                  MakeDataDescriptor(105, {2}), context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {}),
+                                        MakeDataDescriptor(103, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {0}),
+                                        MakeDataDescriptor(103, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1}),
+                                        MakeDataDescriptor(103, {1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {}),
+                                        MakeDataDescriptor(104, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {0}),
+                                        MakeDataDescriptor(104, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {1}),
+                                        MakeDataDescriptor(104, {1})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {}),
+                                         MakeDataDescriptor(105, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {0}),
+                                        MakeDataDescriptor(105, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {1}),
+                                        MakeDataDescriptor(105, {1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {2}),
+                                        MakeDataDescriptor(105, {2})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {3}),
+                                         MakeDataDescriptor(105, {3})));
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {3}),
+                                  MakeDataDescriptor(105, {3}), context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {0}),
+                                        MakeDataDescriptor(104, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {3}),
+                                        MakeDataDescriptor(105, {3})));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
+                                         MakeDataDescriptor(100, {})));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {0}),
+                                  MakeDataDescriptor(100, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {1}),
+                                  MakeDataDescriptor(100, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {2}),
+                                  MakeDataDescriptor(100, {2}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {3}),
+                                  MakeDataDescriptor(100, {3}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {4}),
+                                  MakeDataDescriptor(100, {4}), context.get());
+
+  TransformationComputeDataSynonymFactClosure(100).Apply(
+      context.get(), &transformation_context);
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
+                                        MakeDataDescriptor(100, {})));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(39, {0}),
+                                         MakeDataDescriptor(107, {0})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(35, {}),
+                                         MakeDataDescriptor(39, {0})));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(39, {0}),
+                                  MakeDataDescriptor(35, {}), context.get());
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(39, {0}),
+                                         MakeDataDescriptor(107, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(35, {}),
+                                        MakeDataDescriptor(39, {0})));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(38, {0}),
+                                         MakeDataDescriptor(36, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(38, {1}),
+                                         MakeDataDescriptor(37, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(106, {0}),
+                                         MakeDataDescriptor(36, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(106, {1}),
+                                         MakeDataDescriptor(37, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(38, {}),
+                                         MakeDataDescriptor(106, {})));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(38, {0}),
+                                  MakeDataDescriptor(36, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(106, {0}),
+                                  MakeDataDescriptor(36, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(38, {1}),
+                                  MakeDataDescriptor(37, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(106, {1}),
+                                  MakeDataDescriptor(37, {}), context.get());
+
+  TransformationComputeDataSynonymFactClosure(100).Apply(
+      context.get(), &transformation_context);
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(38, {0}),
+                                        MakeDataDescriptor(36, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(38, {1}),
+                                        MakeDataDescriptor(37, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(106, {0}),
+                                        MakeDataDescriptor(36, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(106, {1}),
+                                        MakeDataDescriptor(37, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(38, {}),
+                                        MakeDataDescriptor(106, {})));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}),
+                                         MakeDataDescriptor(108, {})));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(107, {0}),
+                                  MakeDataDescriptor(35, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {0}),
+                                  MakeDataDescriptor(108, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {1}),
+                                  MakeDataDescriptor(108, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {2}),
+                                  MakeDataDescriptor(108, {2}), context.get());
+
+  TransformationComputeDataSynonymFactClosure(100).Apply(
+      context.get(), &transformation_context);
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}),
+                                        MakeDataDescriptor(108, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0}),
+                                        MakeDataDescriptor(108, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1}),
+                                        MakeDataDescriptor(108, {1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2}),
+                                        MakeDataDescriptor(108, {2})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 0}),
+                                        MakeDataDescriptor(108, {0, 0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 1}),
+                                        MakeDataDescriptor(108, {0, 1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 2}),
+                                        MakeDataDescriptor(108, {0, 2})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 3}),
+                                        MakeDataDescriptor(108, {0, 3})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 4}),
+                                        MakeDataDescriptor(108, {0, 4})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 0}),
+                                        MakeDataDescriptor(108, {1, 0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 1}),
+                                        MakeDataDescriptor(108, {1, 1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 2}),
+                                        MakeDataDescriptor(108, {1, 2})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 3}),
+                                        MakeDataDescriptor(108, {1, 3})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 0, 0}),
+                                        MakeDataDescriptor(108, {1, 0, 0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 1, 0}),
+                                        MakeDataDescriptor(108, {1, 1, 0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 2, 0}),
+                                        MakeDataDescriptor(108, {1, 2, 0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 3, 0}),
+                                        MakeDataDescriptor(108, {1, 3, 0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 0, 1}),
+                                        MakeDataDescriptor(108, {1, 0, 1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 1, 1}),
+                                        MakeDataDescriptor(108, {1, 1, 1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 2, 1}),
+                                        MakeDataDescriptor(108, {1, 2, 1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 3, 1}),
+                                        MakeDataDescriptor(108, {1, 3, 1})));
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 0}),
+                                        MakeDataDescriptor(108, {2, 0})));
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 1}),
+                                        MakeDataDescriptor(108, {2, 1})));
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 1, 0}),
+                                        MakeDataDescriptor(108, {2, 1, 0})));
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 1, 1}),
+                                        MakeDataDescriptor(108, {2, 1, 1})));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_copy_object_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_copy_object_test.cpp
index b85f75b..cf9d135 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_copy_object_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_copy_object_test.cpp
@@ -51,77 +51,92 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  ASSERT_EQ(0,
-            fact_manager.GetIdsForWhichSynonymsAreKnown(context.get()).size());
+  ASSERT_EQ(0, transformation_context.GetFactManager()
+                   ->GetIdsForWhichSynonymsAreKnown()
+                   .size());
 
   {
     TransformationCopyObject copy_true(
         7, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100);
-    ASSERT_TRUE(copy_true.IsApplicable(context.get(), fact_manager));
-    copy_true.Apply(context.get(), &fact_manager);
+    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 =
-        fact_manager.GetIdsForWhichSynonymsAreKnown(context.get());
+        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, fact_manager.GetSynonymsForId(7, context.get()).size());
+    ASSERT_EQ(
+        2, transformation_context.GetFactManager()->GetSynonymsForId(7).size());
     protobufs::DataDescriptor descriptor_100 = MakeDataDescriptor(100, {});
-    ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(7, {}),
-                                          descriptor_100, context.get()));
+    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(), fact_manager));
-    copy_false.Apply(context.get(), &fact_manager);
+    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 =
-        fact_manager.GetIdsForWhichSynonymsAreKnown(context.get());
+        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, fact_manager.GetSynonymsForId(8, context.get()).size());
+    ASSERT_EQ(
+        2, transformation_context.GetFactManager()->GetSynonymsForId(8).size());
     protobufs::DataDescriptor descriptor_101 = MakeDataDescriptor(101, {});
-    ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(8, {}),
-                                          descriptor_101, context.get()));
+    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(), fact_manager));
-    copy_false_again.Apply(context.get(), &fact_manager);
+    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 =
-        fact_manager.GetIdsForWhichSynonymsAreKnown(context.get());
+        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, fact_manager.GetSynonymsForId(101, context.get()).size());
+    ASSERT_EQ(
+        3,
+        transformation_context.GetFactManager()->GetSynonymsForId(101).size());
     protobufs::DataDescriptor descriptor_102 = MakeDataDescriptor(102, {});
-    ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(101, {}),
-                                          descriptor_102, context.get()));
+    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(), fact_manager));
-    copy_true_again.Apply(context.get(), &fact_manager);
+    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 =
-        fact_manager.GetIdsForWhichSynonymsAreKnown(context.get());
+        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, fact_manager.GetSynonymsForId(7, context.get()).size());
+    ASSERT_EQ(
+        3, transformation_context.GetFactManager()->GetSynonymsForId(7).size());
     protobufs::DataDescriptor descriptor_103 = MakeDataDescriptor(103, {});
-    ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(7, {}),
-                                          descriptor_103, context.get()));
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(7, {}), descriptor_103));
   }
 
   std::string after_transformation = R"(
@@ -340,116 +355,119 @@
   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(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because %77 is decorated.
   ASSERT_FALSE(TransformationCopyObject(
                    77, MakeInstructionDescriptor(77, SpvOpBranch, 0), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because %80 is decorated.
   ASSERT_FALSE(TransformationCopyObject(
                    80, MakeInstructionDescriptor(77, SpvOpIAdd, 0), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .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(), fact_manager));
+          .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(), fact_manager));
+          .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(), fact_manager));
+          .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(), fact_manager));
+                   .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(), fact_manager));
+                   .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(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationCopyObject(
                    9, MakeInstructionDescriptor(99, SpvOpPhi, 1), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .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(), fact_manager));
+                  .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(), fact_manager));
+          .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(), fact_manager));
+                  .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(), fact_manager));
+                   .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(), fact_manager));
+                  .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(), fact_manager));
+          .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(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because id %300 does not exist
   ASSERT_FALSE(TransformationCopyObject(
                    300, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because the following instruction is OpVariable
   ASSERT_FALSE(TransformationCopyObject(
                    9, MakeInstructionDescriptor(180, SpvOpVariable, 0), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationCopyObject(
                    9, MakeInstructionDescriptor(181, SpvOpVariable, 0), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationCopyObject(
                    9, MakeInstructionDescriptor(182, SpvOpVariable, 0), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .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(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationCopyObjectTest, MiscellaneousCopies) {
@@ -515,6 +533,9 @@
   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),
@@ -533,8 +554,9 @@
           17, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 106)};
 
   for (auto& transformation : transformations) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
 
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -614,16 +636,19 @@
   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(), fact_manager));
+                   .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(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationCopyObjectTest, PropagateIrrelevantPointeeFact) {
@@ -655,7 +680,11 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(8);
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(8);
 
   TransformationCopyObject transformation1(
       8, MakeInstructionDescriptor(9, SpvOpReturn, 0), 100);
@@ -664,18 +693,84 @@
   TransformationCopyObject transformation3(
       100, MakeInstructionDescriptor(9, SpvOpReturn, 0), 102);
 
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      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(fact_manager.PointeeValueIsIrrelevant(8));
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(100));
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(102));
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(9));
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101));
+  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
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 81d849b..1e8aa7e 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
@@ -48,6 +48,9 @@
   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);
@@ -55,59 +58,61 @@
   // Bad: id already in use.
   ASSERT_FALSE(TransformationEquationInstruction(7, SpvOpSNegate, {7},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: identified instruction does not exist.
   ASSERT_FALSE(
       TransformationEquationInstruction(
           14, SpvOpSNegate, {7}, MakeInstructionDescriptor(13, SpvOpLoad, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: id 100 does not exist
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {100},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: id 20 is an OpUndef
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {20},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: id 30 is not available right before its definition
   ASSERT_FALSE(TransformationEquationInstruction(
                    14, SpvOpSNegate, {30},
                    MakeInstructionDescriptor(30, SpvOpCopyObject, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: too many arguments to OpSNegate.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {7, 7},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: 40 is a type id.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {40},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: wrong type of argument to OpSNegate.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {41},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   auto transformation1 = TransformationEquationInstruction(
       14, SpvOpSNegate, {7}, return_instruction);
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto transformation2 = TransformationEquationInstruction(
       15, SpvOpSNegate, {14}, return_instruction);
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {})));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -161,6 +166,9 @@
   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);
@@ -168,32 +176,34 @@
   // Bad: too few arguments to OpLogicalNot.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: 6 is a type id.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {6},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: wrong type of argument to OpLogicalNot.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {21},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   auto transformation1 = TransformationEquationInstruction(
       14, SpvOpLogicalNot, {7}, return_instruction);
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto transformation2 = TransformationEquationInstruction(
       15, SpvOpLogicalNot, {14}, return_instruction);
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {})));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -248,6 +258,9 @@
   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);
@@ -255,59 +268,64 @@
   // Bad: too many arguments to OpIAdd.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 16, 16},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: boolean argument to OpIAdd.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 32},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: type as argument to OpIAdd.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {33, 16},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: arguments of mismatched widths
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 31},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: arguments of mismatched widths
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {31, 15},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   auto transformation1 = TransformationEquationInstruction(
       14, SpvOpIAdd, {15, 16}, return_instruction);
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto transformation2 = TransformationEquationInstruction(
       19, SpvOpISub, {14, 16}, return_instruction);
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(15, {}), MakeDataDescriptor(19, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(15, {}), MakeDataDescriptor(19, {})));
 
   auto transformation3 = TransformationEquationInstruction(
       20, SpvOpISub, {14, 15}, return_instruction);
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {})));
 
   auto transformation4 = TransformationEquationInstruction(
       22, SpvOpISub, {16, 14}, return_instruction);
-  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation4.IsApplicable(context.get(), transformation_context));
+  transformation4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto transformation5 = TransformationEquationInstruction(
       24, SpvOpSNegate, {22}, return_instruction);
-  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
-  transformation5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation5.IsApplicable(context.get(), transformation_context));
+  transformation5.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(24, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(24, {}), MakeDataDescriptor(15, {})));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -364,69 +382,80 @@
   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);
 
   auto transformation1 = TransformationEquationInstruction(
       14, SpvOpISub, {15, 16}, return_instruction);
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto transformation2 = TransformationEquationInstruction(
       17, SpvOpIAdd, {14, 16}, return_instruction);
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {})));
 
   auto transformation3 = TransformationEquationInstruction(
       18, SpvOpIAdd, {16, 14}, return_instruction);
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {})));
 
   auto transformation4 = TransformationEquationInstruction(
       19, SpvOpISub, {14, 15}, return_instruction);
-  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation4.IsApplicable(context.get(), transformation_context));
+  transformation4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto transformation5 = TransformationEquationInstruction(
       20, SpvOpSNegate, {19}, return_instruction);
-  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
-  transformation5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation5.IsApplicable(context.get(), transformation_context));
+  transformation5.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {})));
 
   auto transformation6 = TransformationEquationInstruction(
       21, SpvOpISub, {14, 19}, return_instruction);
-  ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
-  transformation6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation6.IsApplicable(context.get(), transformation_context));
+  transformation6.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {})));
 
   auto transformation7 = TransformationEquationInstruction(
       22, SpvOpISub, {14, 18}, return_instruction);
-  ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager));
-  transformation7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation7.IsApplicable(context.get(), transformation_context));
+  transformation7.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto transformation8 = TransformationEquationInstruction(
       23, SpvOpSNegate, {22}, return_instruction);
-  ASSERT_TRUE(transformation8.IsApplicable(context.get(), fact_manager));
-  transformation8.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation8.IsApplicable(context.get(), transformation_context));
+  transformation8.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {})));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -457,6 +486,146 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationEquationInstructionTest, Miscellaneous1) {
+  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
+        %113 = OpConstant %6 24
+         %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);
+
+  auto transformation1 = TransformationEquationInstruction(
+      522, SpvOpISub, {113, 113}, return_instruction);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto transformation2 = TransformationEquationInstruction(
+      570, SpvOpIAdd, {522, 113}, return_instruction);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.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 %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+        %113 = OpConstant %6 24
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+        %522 = OpISub %6 %113 %113
+        %570 = OpIAdd %6 %522 %113
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(570, {}), MakeDataDescriptor(113, {})));
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, Miscellaneous2) {
+  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
+        %113 = OpConstant %6 24
+         %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);
+
+  auto transformation1 = TransformationEquationInstruction(
+      522, SpvOpISub, {113, 113}, return_instruction);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto transformation2 = TransformationEquationInstruction(
+      570, SpvOpIAdd, {522, 113}, return_instruction);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.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 %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+        %113 = OpConstant %6 24
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+        %522 = OpISub %6 %113 %113
+        %570 = OpIAdd %6 %522 %113
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(570, {}), MakeDataDescriptor(113, {})));
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_function_call_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_function_call_test.cpp
index 9bd971e..d7305f8 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_function_call_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_function_call_test.cpp
@@ -134,24 +134,36 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  fact_manager.AddFactBlockIsDead(59);
-  fact_manager.AddFactBlockIsDead(11);
-  fact_manager.AddFactBlockIsDead(18);
-  fact_manager.AddFactBlockIsDead(25);
-  fact_manager.AddFactBlockIsDead(96);
-  fact_manager.AddFactBlockIsDead(205);
-  fact_manager.AddFactFunctionIsLivesafe(21);
-  fact_manager.AddFactFunctionIsLivesafe(200);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(71);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(72);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(19);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(20);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(23);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(44);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(46);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(51);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(52);
+  transformation_context.GetFactManager()->AddFactBlockIsDead(59);
+  transformation_context.GetFactManager()->AddFactBlockIsDead(11);
+  transformation_context.GetFactManager()->AddFactBlockIsDead(18);
+  transformation_context.GetFactManager()->AddFactBlockIsDead(25);
+  transformation_context.GetFactManager()->AddFactBlockIsDead(96);
+  transformation_context.GetFactManager()->AddFactBlockIsDead(205);
+  transformation_context.GetFactManager()->AddFactFunctionIsLivesafe(21);
+  transformation_context.GetFactManager()->AddFactFunctionIsLivesafe(200);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      71);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      72);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      19);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      20);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      23);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      44);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      46);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      51);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      52);
 
   // Livesafe functions with argument types: 21(7, 13), 200(7, 13)
   // Non-livesafe functions with argument types: 4(), 10(7), 17(7, 13), 24(7)
@@ -164,127 +176,133 @@
   ASSERT_FALSE(
       TransformationFunctionCall(100, 21, {71, 72, 71},
                                  MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // Too few arguments
   ASSERT_FALSE(TransformationFunctionCall(
                    100, 21, {71}, MakeInstructionDescriptor(59, SpvOpBranch, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Arguments are the wrong way around (types do not match)
   ASSERT_FALSE(
       TransformationFunctionCall(100, 21, {72, 71},
                                  MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // 21 is not an appropriate argument
   ASSERT_FALSE(
       TransformationFunctionCall(100, 21, {21, 72},
                                  MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // 300 does not exist
   ASSERT_FALSE(
       TransformationFunctionCall(100, 21, {300, 72},
                                  MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // 71 is not a function
   ASSERT_FALSE(
       TransformationFunctionCall(100, 71, {71, 72},
                                  MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // 500 does not exist
   ASSERT_FALSE(
       TransformationFunctionCall(100, 500, {71, 72},
                                  MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // Id is not fresh
   ASSERT_FALSE(
       TransformationFunctionCall(21, 21, {71, 72},
                                  MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // Access chain as pointer parameter
   ASSERT_FALSE(
       TransformationFunctionCall(100, 21, {98, 72},
                                  MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // Copied object as pointer parameter
   ASSERT_FALSE(
       TransformationFunctionCall(100, 21, {99, 72},
                                  MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // Non-livesafe called from original live block
   ASSERT_FALSE(
       TransformationFunctionCall(
           100, 10, {71}, MakeInstructionDescriptor(99, SpvOpSelectionMerge, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // Non-livesafe called from livesafe function
   ASSERT_FALSE(
       TransformationFunctionCall(
           100, 10, {19}, MakeInstructionDescriptor(38, SpvOpConvertFToS, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // Livesafe function called with pointer to non-arbitrary local variable
   ASSERT_FALSE(
       TransformationFunctionCall(
           100, 21, {61, 72}, MakeInstructionDescriptor(38, SpvOpConvertFToS, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // Direct recursion
   ASSERT_FALSE(TransformationFunctionCall(
                    100, 4, {}, MakeInstructionDescriptor(59, SpvOpBranch, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Indirect recursion
   ASSERT_FALSE(TransformationFunctionCall(
                    100, 24, {9}, MakeInstructionDescriptor(96, SpvOpBranch, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Parameter 23 is not available at the call site
   ASSERT_FALSE(
       TransformationFunctionCall(104, 10, {23},
                                  MakeInstructionDescriptor(205, SpvOpBranch, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Good transformations
   {
     // Livesafe called from dead block: fine
     TransformationFunctionCall transformation(
         100, 21, {71, 72}, MakeInstructionDescriptor(59, SpvOpBranch, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
   {
     // Livesafe called from original live block: fine
     TransformationFunctionCall transformation(
         101, 21, {71, 72}, MakeInstructionDescriptor(98, SpvOpAccessChain, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
   {
     // Livesafe called from livesafe function: fine
     TransformationFunctionCall transformation(
         102, 200, {19, 20}, MakeInstructionDescriptor(36, SpvOpLoad, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
   {
     // Dead called from dead block in injected function: fine
     TransformationFunctionCall transformation(
         103, 10, {23}, MakeInstructionDescriptor(45, SpvOpLoad, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
   {
     // Non-livesafe called from dead block in livesafe function: OK
     TransformationFunctionCall transformation(
         104, 10, {201}, MakeInstructionDescriptor(205, SpvOpBranch, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
   {
     // Livesafe called from dead block with non-arbitrary parameter
     TransformationFunctionCall transformation(
         105, 21, {62, 65}, MakeInstructionDescriptor(59, SpvOpBranch, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
@@ -429,13 +447,16 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  fact_manager.AddFactBlockIsDead(11);
+  transformation_context.GetFactManager()->AddFactBlockIsDead(11);
 
   // 4 is an entry point, so it is not legal for it to be the target of a call.
   ASSERT_FALSE(TransformationFunctionCall(
                    100, 4, {}, MakeInstructionDescriptor(11, SpvOpReturn, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_load_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_load_test.cpp
index 1f728ff..18ca195 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_load_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_load_test.cpp
@@ -85,14 +85,22 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(27);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(11);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(46);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(16);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(52);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      27);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      11);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      46);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      16);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      52);
 
-  fact_manager.AddFactBlockIsDead(36);
+  transformation_context.GetFactManager()->AddFactBlockIsDead(36);
 
   // Variables with pointee types:
   //  52 - ptr_to(7)
@@ -125,86 +133,90 @@
   // Bad: id is not fresh
   ASSERT_FALSE(TransformationLoad(
                    33, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: attempt to load from 11 from outside its function
   ASSERT_FALSE(TransformationLoad(
                    100, 11, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer is not available
   ASSERT_FALSE(TransformationLoad(
                    100, 33, MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: attempt to insert before OpVariable
   ASSERT_FALSE(TransformationLoad(
                    100, 27, MakeInstructionDescriptor(27, SpvOpVariable, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id does not exist
   ASSERT_FALSE(
       TransformationLoad(100, 1000,
                          MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id exists but does not have a type
   ASSERT_FALSE(TransformationLoad(
                    100, 5, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id exists and has a type, but is not a pointer
   ASSERT_FALSE(TransformationLoad(
                    100, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: attempt to load from null pointer
   ASSERT_FALSE(TransformationLoad(
                    100, 60, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: attempt to load from undefined pointer
   ASSERT_FALSE(TransformationLoad(
                    100, 61, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: %40 is not available at the program point
   ASSERT_FALSE(
       TransformationLoad(100, 40, MakeInstructionDescriptor(37, SpvOpReturn, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: The described instruction does not exist
   ASSERT_FALSE(TransformationLoad(
                    100, 33, MakeInstructionDescriptor(1000, SpvOpReturn, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   {
     TransformationLoad transformation(
         100, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
   {
     TransformationLoad transformation(
         101, 46, MakeInstructionDescriptor(16, SpvOpReturnValue, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
   {
     TransformationLoad transformation(
         102, 16, MakeInstructionDescriptor(16, SpvOpReturnValue, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
   {
     TransformationLoad transformation(
         103, 40, MakeInstructionDescriptor(43, SpvOpAccessChain, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_merge_blocks_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_merge_blocks_test.cpp
index e2b4aa6..4500445 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_merge_blocks_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_merge_blocks_test.cpp
@@ -45,11 +45,14 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  ASSERT_FALSE(
-      TransformationMergeBlocks(3).IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(
-      TransformationMergeBlocks(7).IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationMergeBlocks(3).IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_FALSE(TransformationMergeBlocks(7).IsApplicable(
+      context.get(), transformation_context));
 }
 
 TEST(TransformationMergeBlocksTest, DoNotMergeFirstBlockHasMultipleSuccessors) {
@@ -84,9 +87,12 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  ASSERT_FALSE(
-      TransformationMergeBlocks(6).IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationMergeBlocks(6).IsApplicable(
+      context.get(), transformation_context));
 }
 
 TEST(TransformationMergeBlocksTest,
@@ -122,9 +128,12 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  ASSERT_FALSE(
-      TransformationMergeBlocks(10).IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationMergeBlocks(10).IsApplicable(
+      context.get(), transformation_context));
 }
 
 TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsSelectionMerge) {
@@ -161,10 +170,14 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationMergeBlocks transformation(10);
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -231,10 +244,14 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationMergeBlocks transformation(10);
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -306,10 +323,14 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationMergeBlocks transformation(11);
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -377,10 +398,14 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationMergeBlocks transformation(6);
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -454,12 +479,16 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   for (auto& transformation :
        {TransformationMergeBlocks(100), TransformationMergeBlocks(101),
         TransformationMergeBlocks(102), TransformationMergeBlocks(103)}) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
@@ -542,11 +571,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   for (auto& transformation :
        {TransformationMergeBlocks(101), TransformationMergeBlocks(100)}) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
@@ -629,10 +662,14 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationMergeBlocks transformation(101);
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_move_block_down_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_move_block_down_test.cpp
index 02761a2..662e88c 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_move_block_down_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_move_block_down_test.cpp
@@ -53,9 +53,13 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto transformation = TransformationMoveBlockDown(11);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationMoveBlockDownTest, NoMovePossible2) {
@@ -90,9 +94,13 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto transformation = TransformationMoveBlockDown(5);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationMoveBlockDownTest, NoMovePossible3) {
@@ -129,9 +137,13 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto transformation = TransformationMoveBlockDown(100);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationMoveBlockDownTest, NoMovePossible4) {
@@ -172,9 +184,13 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto transformation = TransformationMoveBlockDown(12);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationMoveBlockDownTest, ManyMovesPossible) {
@@ -277,6 +293,9 @@
       BuildModule(env, consumer, before_transformation, kFuzzAssembleOption);
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // The block ids are: 5 14 20 23 21 25 29 32 30 15
   // We make a transformation to move each of them down, plus a transformation
@@ -306,110 +325,130 @@
   // 15 dominates nothing
 
   // Current ordering: 5 14 20 23 21 25 29 32 30 15
-  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_14.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_25.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_15.IsApplicable(context.get(), transformation_context));
 
   // Let's bubble 20 all the way down.
 
-  move_down_20.Apply(context.get(), &fact_manager);
+  move_down_20.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Current ordering: 5 14 23 20 21 25 29 32 30 15
-  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_14.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_25.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_15.IsApplicable(context.get(), transformation_context));
 
-  move_down_20.Apply(context.get(), &fact_manager);
+  move_down_20.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Current ordering: 5 14 23 21 20 25 29 32 30 15
-  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_14.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_25.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_15.IsApplicable(context.get(), transformation_context));
 
-  move_down_20.Apply(context.get(), &fact_manager);
+  move_down_20.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Current ordering: 5 14 23 21 25 20 29 32 30 15
-  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_25.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_14.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_25.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_15.IsApplicable(context.get(), transformation_context));
 
-  move_down_20.Apply(context.get(), &fact_manager);
+  move_down_20.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Current ordering: 5 14 23 21 25 29 20 32 30 15
-  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_14.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_25.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_15.IsApplicable(context.get(), transformation_context));
 
-  move_down_20.Apply(context.get(), &fact_manager);
+  move_down_20.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Current ordering: 5 14 23 21 25 29 32 20 30 15
-  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_14.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_25.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_15.IsApplicable(context.get(), transformation_context));
 
-  move_down_20.Apply(context.get(), &fact_manager);
+  move_down_20.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Current ordering: 5 14 23 21 25 29 32 30 20 15
-  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_14.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_25.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_15.IsApplicable(context.get(), transformation_context));
 
-  move_down_20.Apply(context.get(), &fact_manager);
+  move_down_20.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_bubbling_20_down = R"(
@@ -485,63 +524,72 @@
   ASSERT_TRUE(IsEqual(env, after_bubbling_20_down, context.get()));
 
   // Current ordering: 5 14 23 21 25 29 32 30 15 20
-  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_14.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_25.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_15.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_20.IsApplicable(context.get(), transformation_context));
 
-  move_down_23.Apply(context.get(), &fact_manager);
+  move_down_23.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Current ordering: 5 14 21 23 25 29 32 30 15 20
-  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_14.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_25.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_15.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_20.IsApplicable(context.get(), transformation_context));
 
-  move_down_23.Apply(context.get(), &fact_manager);
+  move_down_23.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Current ordering: 5 14 21 25 23 29 32 30 15 20
-  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_25.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_14.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_25.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_15.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_20.IsApplicable(context.get(), transformation_context));
 
-  move_down_21.Apply(context.get(), &fact_manager);
+  move_down_21.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Current ordering: 5 14 25 21 23 29 32 30 15 20
-  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_14.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_25.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_14.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_25.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_15.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_20.IsApplicable(context.get(), transformation_context));
 
-  move_down_14.Apply(context.get(), &fact_manager);
+  move_down_14.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_more_shuffling = R"(
@@ -617,16 +665,18 @@
   ASSERT_TRUE(IsEqual(env, after_more_shuffling, context.get()));
 
   // Final ordering: 5 25 14 21 23 29 32 30 15 20
-  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_25.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_25.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_14.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(move_down_15.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      move_down_20.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationMoveBlockDownTest, DoNotMoveUnreachable) {
@@ -660,9 +710,13 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto transformation = TransformationMoveBlockDown(6);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_outline_function_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_outline_function_test.cpp
index 40aaebc..ed4fd15 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_outline_function_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_outline_function_test.cpp
@@ -44,12 +44,16 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200,
                                                100, 101, 102, 103,
                                                /* not relevant */ 201, {}, {});
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -105,11 +109,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200,
                                                100, 101, 102, 103,
                                                /* not relevant */ 201, {}, {});
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest, OutlineInterestingControlFlowNoState) {
@@ -158,12 +166,16 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(6, 13, /* not relevant */
                                                200, 100, 101, 102, 103,
                                                /* not relevant */ 201, {}, {});
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -243,12 +255,16 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(6, 6, /* not relevant */ 200,
                                                100, 101, 102, 103,
                                                /* not relevant */ 201, {}, {});
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -317,11 +333,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(6, 6, 99, 100, 101, 102, 103,
                                                105, {}, {{9, 104}});
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -412,12 +432,16 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       6, 80, 100, 101, 102, 103, 104, 105, {},
       {{15, 106}, {9, 107}, {7, 108}, {8, 109}});
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -508,11 +532,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(6, 6, 100, 101, 102, 103, 104,
                                                105, {{7, 106}}, {});
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -582,11 +610,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(6, 6, 100, 101, 102, 103, 104,
                                                105, {{13, 106}}, {});
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -666,11 +698,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(11, 11, 100, 101, 102, 103, 104,
                                                105, {{9, 106}}, {{14, 107}});
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -752,10 +788,14 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(6, 8, 100, 101, 102, 103, 104,
                                                105, {}, {});
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest, DoNotOutlineIfRegionInvolvesReturn) {
@@ -798,11 +838,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200,
                                                100, 101, 102, 103,
                                                /* not relevant */ 201, {}, {});
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest, DoNotOutlineIfRegionInvolvesKill) {
@@ -845,11 +889,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200,
                                                100, 101, 102, 103,
                                                /* not relevant */ 201, {}, {});
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest,
@@ -893,11 +941,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200,
                                                100, 101, 102, 103,
                                                /* not relevant */ 201, {}, {});
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest,
@@ -933,10 +985,14 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(6, 8, 100, 101, 102, 103, 104,
                                                105, {}, {});
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest, DoNotOutlineIfLoopHeadIsOutsideRegion) {
@@ -973,10 +1029,14 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(7, 8, 100, 101, 102, 103, 104,
                                                105, {}, {});
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest,
@@ -1012,10 +1072,14 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(6, 7, 100, 101, 102, 103, 104,
                                                105, {}, {});
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest,
@@ -1053,10 +1117,14 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(6, 7, 100, 101, 102, 103, 104,
                                                105, {}, {});
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest,
@@ -1094,10 +1162,14 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(8, 11, 100, 101, 102, 103, 104,
                                                105, {}, {});
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest, OutlineRegionEndingWithReturnVoid) {
@@ -1132,6 +1204,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 54,
@@ -1145,8 +1220,9 @@
       /*input_id_to_fresh_id*/ {{22, 206}},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -1219,6 +1295,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 9,
@@ -1232,8 +1311,9 @@
       /*input_id_to_fresh_id*/ {{31, 206}},
       /*output_id_to_fresh_id*/ {{32, 207}});
 
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -1310,6 +1390,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 54,
@@ -1323,8 +1406,9 @@
       /*input_id_to_fresh_id*/ {{}},
       /*output_id_to_fresh_id*/ {{6, 206}});
 
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -1396,6 +1480,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 54,
@@ -1409,8 +1496,9 @@
       /*input_id_to_fresh_id*/ {},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -1478,6 +1566,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 21,
@@ -1491,7 +1582,8 @@
       /*input_id_to_fresh_id*/ {{22, 207}},
       /*output_id_to_fresh_id*/ {{23, 208}});
 
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest,
@@ -1531,6 +1623,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 21,
@@ -1544,7 +1639,8 @@
       /*input_id_to_fresh_id*/ {},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest,
@@ -1584,6 +1680,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 5,
@@ -1597,7 +1696,8 @@
       /*input_id_to_fresh_id*/ {},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatUsesAccessChain) {
@@ -1640,6 +1740,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 13,
@@ -1653,7 +1756,8 @@
       /*input_id_to_fresh_id*/ {{12, 207}},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest,
@@ -1698,6 +1802,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 13,
@@ -1711,7 +1818,8 @@
       /*input_id_to_fresh_id*/ {{20, 207}},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest,
@@ -1761,6 +1869,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 11,
@@ -1774,8 +1885,9 @@
       /*input_id_to_fresh_id*/ {{9, 207}},
       /*output_id_to_fresh_id*/ {{14, 208}});
 
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -1913,9 +2025,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
-  fact_manager.AddFactFunctionIsLivesafe(30);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(200);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(201);
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  transformation_context.GetFactManager()->AddFactFunctionIsLivesafe(30);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      200);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      201);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 198,
@@ -1929,24 +2047,31 @@
       /*input_id_to_fresh_id*/ {{100, 407}, {200, 408}, {201, 409}},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // The original function should still be livesafe.
-  ASSERT_TRUE(fact_manager.FunctionIsLivesafe(30));
+  ASSERT_TRUE(transformation_context.GetFactManager()->FunctionIsLivesafe(30));
   // The outlined function should be livesafe.
-  ASSERT_TRUE(fact_manager.FunctionIsLivesafe(402));
+  ASSERT_TRUE(transformation_context.GetFactManager()->FunctionIsLivesafe(402));
   // The variable and parameter that were originally irrelevant should still be.
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(200));
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(201));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(200));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(201));
   // The loop limiter should still be non-irrelevant.
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(100));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
   // The parameters for the original irrelevant variables should be irrelevant.
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(408));
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(409));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(408));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(409));
   // The parameter for the loop limiter should not be irrelevant.
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(407));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(407));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -2129,8 +2254,12 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
   for (uint32_t block_id : {16u, 23u, 24u, 26u, 27u, 34u, 35u, 50u}) {
-    fact_manager.AddFactBlockIsDead(block_id);
+    transformation_context.GetFactManager()->AddFactBlockIsDead(block_id);
   }
 
   TransformationOutlineFunction transformation(
@@ -2145,12 +2274,13 @@
       /*input_id_to_fresh_id*/ {{9, 206}, {12, 207}, {21, 208}},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   // All the original blocks, plus the new function entry block, should be dead.
   for (uint32_t block_id : {16u, 23u, 24u, 26u, 27u, 34u, 35u, 50u, 203u}) {
-    ASSERT_TRUE(fact_manager.BlockIsDead(block_id));
+    ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(block_id));
   }
 }
 
@@ -2208,8 +2338,12 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
   for (uint32_t block_id : {32u, 34u, 35u}) {
-    fact_manager.AddFactBlockIsDead(block_id);
+    transformation_context.GetFactManager()->AddFactBlockIsDead(block_id);
   }
 
   TransformationOutlineFunction transformation(
@@ -2224,15 +2358,17 @@
       /*input_id_to_fresh_id*/ {{11, 206}},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   // The blocks that were originally dead, but not others, should be dead.
   for (uint32_t block_id : {32u, 34u, 35u}) {
-    ASSERT_TRUE(fact_manager.BlockIsDead(block_id));
+    ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(block_id));
   }
   for (uint32_t block_id : {5u, 30u, 31u, 33u, 36u, 37u, 203u}) {
-    ASSERT_FALSE(fact_manager.BlockIsDead(block_id));
+    ASSERT_FALSE(
+        transformation_context.GetFactManager()->BlockIsDead(block_id));
   }
 }
 
@@ -2287,8 +2423,13 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(9);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(14);
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(9);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      14);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 50,
@@ -2302,19 +2443,141 @@
       /*input_id_to_fresh_id*/ {{9, 206}, {10, 207}, {14, 208}, {20, 209}},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   // The variables that were originally irrelevant, plus input parameters
   // corresponding to them, should be irrelevant.  The rest should not be.
   for (uint32_t variable_id : {9u, 14u, 206u, 208u}) {
-    ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(variable_id));
+    ASSERT_TRUE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(
+            variable_id));
   }
   for (uint32_t variable_id : {10u, 20u, 207u, 209u}) {
-    ASSERT_FALSE(fact_manager.BlockIsDead(variable_id));
+    ASSERT_FALSE(
+        transformation_context.GetFactManager()->BlockIsDead(variable_id));
   }
 }
 
+TEST(TransformationOutlineFunctionTest,
+     DoNotOutlineCodeThatProducesUsedPointer) {
+  // This checks that we cannot outline a region of code if it produces a
+  // pointer result id that gets used outside the region.  This avoids creating
+  // a struct with a pointer member.
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %6 "main"
+               OpExecutionMode %6 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %21 = OpTypeBool
+        %100 = OpTypeInt 32 0
+         %99 = OpConstant %100 0
+        %101 = OpTypeVector %100 2
+        %102 = OpTypePointer Function %100
+        %103 = OpTypePointer Function %101
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+        %104 = OpVariable %103 Function
+               OpBranch %80
+         %80 = OpLabel
+        %105 = OpAccessChain %102 %104 %99
+               OpBranch %106
+        %106 = OpLabel
+               OpStore %105 %99
+               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);
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 80,
+      /*exit_block*/ 80,
+      /*new_function_struct_return_type_id*/ 300,
+      /*new_function_type_id*/ 301,
+      /*new_function_id*/ 302,
+      /*new_function_region_entry_block*/ 304,
+      /*new_caller_result_id*/ 305,
+      /*new_callee_result_id*/ 306,
+      /*input_id_to_fresh_id*/ {{104, 307}},
+      /*output_id_to_fresh_id*/ {{105, 308}});
+
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationOutlineFunctionTest, ExitBlockHeadsLoop) {
+  // This checks that it is not possible outline a region that ends in a loop
+  // head.
+  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
+         %15 = OpTypeInt 32 1
+         %35 = OpTypeBool
+         %39 = OpConstant %15 1
+         %40 = OpConstantTrue %35
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %22
+         %22 = OpLabel
+               OpBranch %23
+         %23 = OpLabel
+         %24 = OpPhi %15 %39 %22 %39 %25
+               OpLoopMerge %26 %25 None
+               OpBranchConditional %40 %25 %26
+         %25 = OpLabel
+               OpBranch %23
+         %26 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 22,
+      /*exit_block*/ 23,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 203,
+      /*new_caller_result_id*/ 204,
+      /*new_callee_result_id*/ 205,
+      /*input_id_to_fresh_id*/ {},
+      /*output_id_to_fresh_id*/ {});
+
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
+}
+
 TEST(TransformationOutlineFunctionTest, Miscellaneous1) {
   // This tests outlining of some non-trivial code.
 
@@ -2423,6 +2686,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 150,
@@ -2436,8 +2702,9 @@
       /*input_id_to_fresh_id*/ {{102, 300}, {103, 301}, {40, 302}},
       /*output_id_to_fresh_id*/ {{106, 400}, {107, 401}});
 
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -2588,6 +2855,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 38,
@@ -2601,7 +2871,8 @@
       /*input_id_to_fresh_id*/ {},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest, Miscellaneous3) {
@@ -2643,6 +2914,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 80,
@@ -2656,8 +2930,9 @@
       /*input_id_to_fresh_id*/ {},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  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"(
@@ -2732,6 +3007,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 80,
@@ -2745,8 +3023,9 @@
       /*input_id_to_fresh_id*/ {{104, 307}},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
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 1af4699..a4a7c00 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
@@ -200,52 +200,57 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Can't permute main function
   ASSERT_FALSE(TransformationPermuteFunctionParameters(4, 0, {}).IsApplicable(
-      context.get(), fact_manager));
+      context.get(), transformation_context));
 
   // Can't permute invalid instruction
   ASSERT_FALSE(TransformationPermuteFunctionParameters(101, 0, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Permutation has too many values
   ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {2, 1, 0, 3})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Permutation has too few values
   ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {0, 1})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Permutation has invalid values
   ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {3, 1, 0})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Type id is not an OpTypeFunction instruction
   ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 42, {2, 1, 0})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Type id has incorrect number of operands
   ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 9, {2, 1, 0})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // OpTypeFunction has operands out of order
   ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 18, {2, 1, 0})
-                   .IsApplicable(context.get(), fact_manager));
+                   .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});
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    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});
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
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 527a7b7..b320308 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
@@ -163,6 +163,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   std::vector<protobufs::IdUseDescriptor> uses_of_true = {
       MakeIdUseDescriptor(41, MakeInstructionDescriptor(44, SpvOpStore, 12), 1),
@@ -197,10 +200,10 @@
 #define CHECK_OPERATOR(USE_DESCRIPTOR, LHS_ID, RHS_ID, OPCODE, FRESH_ID) \
   ASSERT_TRUE(TransformationReplaceBooleanConstantWithConstantBinary(    \
                   USE_DESCRIPTOR, LHS_ID, RHS_ID, OPCODE, FRESH_ID)      \
-                  .IsApplicable(context.get(), fact_manager));           \
+                  .IsApplicable(context.get(), transformation_context)); \
   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(   \
                    USE_DESCRIPTOR, RHS_ID, LHS_ID, OPCODE, FRESH_ID)     \
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
 #define CHECK_TRANSFORMATION_APPLICABILITY(GT_OPCODES, LT_OPCODES, SMALL_ID, \
                                            LARGE_ID)                         \
@@ -252,27 +255,27 @@
   // Target id is not fresh
   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
                    uses_of_true[0], 15, 17, SpvOpFOrdLessThan, 15)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // LHS id does not exist
   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
                    uses_of_true[0], 300, 17, SpvOpFOrdLessThan, 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // RHS id does not exist
   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
                    uses_of_true[0], 15, 300, SpvOpFOrdLessThan, 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // LHS and RHS ids do not match type
   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
                    uses_of_true[0], 11, 17, SpvOpFOrdLessThan, 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Opcode not appropriate
   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
                    uses_of_true[0], 15, 17, SpvOpFDiv, 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   auto replace_true_with_double_comparison =
       TransformationReplaceBooleanConstantWithConstantBinary(
@@ -287,21 +290,25 @@
       TransformationReplaceBooleanConstantWithConstantBinary(
           uses_of_false[1], 33, 31, SpvOpSLessThan, 103);
 
-  ASSERT_TRUE(replace_true_with_double_comparison.IsApplicable(context.get(),
-                                                               fact_manager));
-  replace_true_with_double_comparison.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(replace_true_with_double_comparison.IsApplicable(
+      context.get(), transformation_context));
+  replace_true_with_double_comparison.Apply(context.get(),
+                                            &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(replace_true_with_uint32_comparison.IsApplicable(context.get(),
-                                                               fact_manager));
-  replace_true_with_uint32_comparison.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(replace_true_with_uint32_comparison.IsApplicable(
+      context.get(), transformation_context));
+  replace_true_with_uint32_comparison.Apply(context.get(),
+                                            &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(replace_false_with_float_comparison.IsApplicable(context.get(),
-                                                               fact_manager));
-  replace_false_with_float_comparison.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(replace_false_with_float_comparison.IsApplicable(
+      context.get(), transformation_context));
+  replace_false_with_float_comparison.Apply(context.get(),
+                                            &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(replace_false_with_sint64_comparison.IsApplicable(context.get(),
-                                                                fact_manager));
-  replace_false_with_sint64_comparison.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(replace_false_with_sint64_comparison.IsApplicable(
+      context.get(), transformation_context));
+  replace_false_with_sint64_comparison.Apply(context.get(),
+                                             &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after = R"(
@@ -419,7 +426,7 @@
     // The transformation is not applicable because %200 is NaN.
     ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
                      uses_of_true[0], 11, 200, SpvOpFOrdLessThan, 300)
-                     .IsApplicable(context.get(), fact_manager));
+                     .IsApplicable(context.get(), transformation_context));
   }
   if (std::numeric_limits<double>::has_infinity) {
     double positive_infinity_double = std::numeric_limits<double>::infinity();
@@ -436,7 +443,7 @@
     // transformation is restricted to only apply to finite values.
     ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
                      uses_of_true[0], 11, 201, SpvOpFOrdLessThan, 300)
-                     .IsApplicable(context.get(), fact_manager));
+                     .IsApplicable(context.get(), transformation_context));
   }
   if (std::numeric_limits<float>::has_infinity) {
     float positive_infinity_float = std::numeric_limits<float>::infinity();
@@ -461,7 +468,7 @@
     // values.
     ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
                      uses_of_true[0], 203, 202, SpvOpFOrdLessThan, 300)
-                     .IsApplicable(context.get(), fact_manager));
+                     .IsApplicable(context.get(), transformation_context));
   }
 }
 
@@ -531,6 +538,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto use_of_true_in_if = MakeIdUseDescriptor(
       13, MakeInstructionDescriptor(10, SpvOpBranchConditional, 0), 0);
@@ -542,12 +552,14 @@
   auto replacement_2 = TransformationReplaceBooleanConstantWithConstantBinary(
       use_of_false_in_while, 9, 11, SpvOpSGreaterThanEqual, 101);
 
-  ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
-  replacement_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_1.IsApplicable(context.get(), transformation_context));
+  replacement_1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
-  replacement_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_2.IsApplicable(context.get(), transformation_context));
+  replacement_2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after = R"(
@@ -642,12 +654,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto replacement = TransformationReplaceBooleanConstantWithConstantBinary(
       MakeIdUseDescriptor(9, MakeInstructionDescriptor(23, SpvOpPhi, 0), 0), 13,
       15, SpvOpSLessThan, 100);
 
-  ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(replacement.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
@@ -681,12 +696,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
                    MakeIdUseDescriptor(
                        9, MakeInstructionDescriptor(50, SpvOpVariable, 0), 1),
                    13, 15, SpvOpSLessThan, 100)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
index 58d4a89..8cbba46 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
@@ -22,7 +22,8 @@
 namespace {
 
 bool AddFactHelper(
-    FactManager* fact_manager, opt::IRContext* context, uint32_t word,
+    TransformationContext* transformation_context, opt::IRContext* context,
+    uint32_t word,
     const protobufs::UniformBufferElementDescriptor& descriptor) {
   protobufs::FactConstantUniform constant_uniform_fact;
   constant_uniform_fact.add_constant_word(word);
@@ -30,7 +31,7 @@
       descriptor;
   protobufs::Fact fact;
   *fact.mutable_constant_uniform_fact() = constant_uniform_fact;
-  return fact_manager->AddFact(fact, context);
+  return transformation_context->GetFactManager()->AddFact(fact, context);
 }
 
 TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) {
@@ -104,6 +105,10 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
   protobufs::UniformBufferElementDescriptor blockname_a =
       MakeUniformBufferElementDescriptor(0, 0, {0});
   protobufs::UniformBufferElementDescriptor blockname_b =
@@ -111,9 +116,12 @@
   protobufs::UniformBufferElementDescriptor blockname_c =
       MakeUniformBufferElementDescriptor(0, 0, {2});
 
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 1, blockname_a));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 2, blockname_b));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 3, blockname_c));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 1, blockname_a));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 2, blockname_b));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 3, blockname_c));
 
   // The constant ids are 9, 11 and 14, for 1, 2 and 3 respectively.
   protobufs::IdUseDescriptor use_of_9_in_store =
@@ -127,30 +135,30 @@
   auto transformation_use_of_9_in_store =
       TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_a,
                                                100, 101);
-  ASSERT_TRUE(transformation_use_of_9_in_store.IsApplicable(context.get(),
-                                                            fact_manager));
+  ASSERT_TRUE(transformation_use_of_9_in_store.IsApplicable(
+      context.get(), transformation_context));
   auto transformation_use_of_11_in_add =
       TransformationReplaceConstantWithUniform(use_of_11_in_add, blockname_b,
                                                102, 103);
-  ASSERT_TRUE(transformation_use_of_11_in_add.IsApplicable(context.get(),
-                                                           fact_manager));
+  ASSERT_TRUE(transformation_use_of_11_in_add.IsApplicable(
+      context.get(), transformation_context));
   auto transformation_use_of_14_in_add =
       TransformationReplaceConstantWithUniform(use_of_14_in_add, blockname_c,
                                                104, 105);
-  ASSERT_TRUE(transformation_use_of_14_in_add.IsApplicable(context.get(),
-                                                           fact_manager));
+  ASSERT_TRUE(transformation_use_of_14_in_add.IsApplicable(
+      context.get(), transformation_context));
 
   // The transformations are not applicable if we change which uniforms are
   // applied to which constants.
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
                                                         blockname_b, 101, 102)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_add,
                                                         blockname_c, 101, 102)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_14_in_add,
                                                         blockname_a, 101, 102)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // The following transformations do not apply because the uniform descriptors
   // are not sensible.
@@ -160,10 +168,10 @@
       MakeUniformBufferElementDescriptor(0, 0, {5});
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(
                    use_of_9_in_store, nonsense_uniform_descriptor1, 101, 102)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(
                    use_of_9_in_store, nonsense_uniform_descriptor2, 101, 102)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // The following transformation does not apply because the id descriptor is
   // not sensible.
@@ -171,18 +179,19 @@
       MakeIdUseDescriptor(9, MakeInstructionDescriptor(15, SpvOpIAdd, 0), 0);
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(
                    nonsense_id_use_descriptor, blockname_a, 101, 102)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // The following transformations do not apply because the ids are not fresh.
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_add,
                                                         blockname_b, 15, 103)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_add,
                                                         blockname_b, 102, 15)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Apply the use of 9 in a store.
-  transformation_use_of_9_in_store.Apply(context.get(), &fact_manager);
+  transformation_use_of_9_in_store.Apply(context.get(),
+                                         &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string after_replacing_use_of_9_in_store = R"(
                OpCapability Shader
@@ -233,10 +242,10 @@
   )";
   ASSERT_TRUE(IsEqual(env, after_replacing_use_of_9_in_store, context.get()));
 
-  ASSERT_TRUE(transformation_use_of_11_in_add.IsApplicable(context.get(),
-                                                           fact_manager));
+  ASSERT_TRUE(transformation_use_of_11_in_add.IsApplicable(
+      context.get(), transformation_context));
   // Apply the use of 11 in an add.
-  transformation_use_of_11_in_add.Apply(context.get(), &fact_manager);
+  transformation_use_of_11_in_add.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string after_replacing_use_of_11_in_add = R"(
                OpCapability Shader
@@ -289,10 +298,10 @@
   )";
   ASSERT_TRUE(IsEqual(env, after_replacing_use_of_11_in_add, context.get()));
 
-  ASSERT_TRUE(transformation_use_of_14_in_add.IsApplicable(context.get(),
-                                                           fact_manager));
+  ASSERT_TRUE(transformation_use_of_14_in_add.IsApplicable(
+      context.get(), transformation_context));
   // Apply the use of 15 in an add.
-  transformation_use_of_14_in_add.Apply(context.get(), &fact_manager);
+  transformation_use_of_14_in_add.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string after_replacing_use_of_14_in_add = R"(
                OpCapability Shader
@@ -462,6 +471,10 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
   protobufs::UniformBufferElementDescriptor blockname_1 =
       MakeUniformBufferElementDescriptor(0, 0, {0});
   protobufs::UniformBufferElementDescriptor blockname_2 =
@@ -471,10 +484,14 @@
   protobufs::UniformBufferElementDescriptor blockname_4 =
       MakeUniformBufferElementDescriptor(0, 0, {1, 0, 1, 0});
 
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 1, blockname_1));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 2, blockname_2));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 3, blockname_3));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 4, blockname_4));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 1, blockname_1));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 2, blockname_2));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 3, blockname_3));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 4, blockname_4));
 
   // The constant ids are 13, 15, 17 and 20, for 1, 2, 3 and 4 respectively.
   protobufs::IdUseDescriptor use_of_13_in_store =
@@ -490,76 +507,78 @@
   auto transformation_use_of_13_in_store =
       TransformationReplaceConstantWithUniform(use_of_13_in_store, blockname_1,
                                                100, 101);
-  ASSERT_TRUE(transformation_use_of_13_in_store.IsApplicable(context.get(),
-                                                             fact_manager));
+  ASSERT_TRUE(transformation_use_of_13_in_store.IsApplicable(
+      context.get(), transformation_context));
   auto transformation_use_of_15_in_add =
       TransformationReplaceConstantWithUniform(use_of_15_in_add, blockname_2,
                                                102, 103);
-  ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable(context.get(),
-                                                           fact_manager));
+  ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable(
+      context.get(), transformation_context));
   auto transformation_use_of_17_in_add =
       TransformationReplaceConstantWithUniform(use_of_17_in_add, blockname_3,
                                                104, 105);
-  ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable(context.get(),
-                                                           fact_manager));
+  ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable(
+      context.get(), transformation_context));
   auto transformation_use_of_20_in_store =
       TransformationReplaceConstantWithUniform(use_of_20_in_store, blockname_4,
                                                106, 107);
-  ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(context.get(),
-                                                             fact_manager));
+  ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(
+      context.get(), transformation_context));
 
-  ASSERT_TRUE(transformation_use_of_13_in_store.IsApplicable(context.get(),
-                                                             fact_manager));
-  ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable(context.get(),
-                                                           fact_manager));
-  ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable(context.get(),
-                                                           fact_manager));
-  ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(context.get(),
-                                                             fact_manager));
+  ASSERT_TRUE(transformation_use_of_13_in_store.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(
+      context.get(), transformation_context));
 
-  transformation_use_of_13_in_store.Apply(context.get(), &fact_manager);
+  transformation_use_of_13_in_store.Apply(context.get(),
+                                          &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable(context.get(),
-                                                              fact_manager));
-  ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable(context.get(),
-                                                           fact_manager));
-  ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable(context.get(),
-                                                           fact_manager));
-  ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(context.get(),
-                                                             fact_manager));
+  ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(
+      context.get(), transformation_context));
 
-  transformation_use_of_15_in_add.Apply(context.get(), &fact_manager);
+  transformation_use_of_15_in_add.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable(context.get(),
-                                                              fact_manager));
-  ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable(context.get(),
-                                                            fact_manager));
-  ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable(context.get(),
-                                                           fact_manager));
-  ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(context.get(),
-                                                             fact_manager));
+  ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(
+      context.get(), transformation_context));
 
-  transformation_use_of_17_in_add.Apply(context.get(), &fact_manager);
+  transformation_use_of_17_in_add.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable(context.get(),
-                                                              fact_manager));
-  ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable(context.get(),
-                                                            fact_manager));
-  ASSERT_FALSE(transformation_use_of_17_in_add.IsApplicable(context.get(),
-                                                            fact_manager));
-  ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(context.get(),
-                                                             fact_manager));
+  ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_FALSE(transformation_use_of_17_in_add.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(
+      context.get(), transformation_context));
 
-  transformation_use_of_20_in_store.Apply(context.get(), &fact_manager);
+  transformation_use_of_20_in_store.Apply(context.get(),
+                                          &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable(context.get(),
-                                                              fact_manager));
-  ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable(context.get(),
-                                                            fact_manager));
-  ASSERT_FALSE(transformation_use_of_17_in_add.IsApplicable(context.get(),
-                                                            fact_manager));
-  ASSERT_FALSE(transformation_use_of_20_in_store.IsApplicable(context.get(),
-                                                              fact_manager));
+  ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_FALSE(transformation_use_of_17_in_add.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_FALSE(transformation_use_of_20_in_store.IsApplicable(
+      context.get(), transformation_context));
 
   std::string after = R"(
                OpCapability Shader
@@ -697,10 +716,15 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
   protobufs::UniformBufferElementDescriptor blockname_0 =
       MakeUniformBufferElementDescriptor(0, 0, {0});
 
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 0, blockname_0));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 0, blockname_0));
 
   // The constant id is 9 for 0.
   protobufs::IdUseDescriptor use_of_9_in_store =
@@ -710,7 +734,7 @@
   // type is present:
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
                                                         blockname_0, 100, 101)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationReplaceConstantWithUniformTest, NoConstantPresentForIndex) {
@@ -770,12 +794,17 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
   protobufs::UniformBufferElementDescriptor blockname_0 =
       MakeUniformBufferElementDescriptor(0, 0, {0});
   protobufs::UniformBufferElementDescriptor blockname_9 =
       MakeUniformBufferElementDescriptor(0, 0, {1});
 
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 9, blockname_9));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 9, blockname_9));
 
   // The constant id is 9 for 9.
   protobufs::IdUseDescriptor use_of_9_in_store =
@@ -785,7 +814,7 @@
   // index 1 required to index into the uniform buffer:
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
                                                         blockname_9, 100, 101)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationReplaceConstantWithUniformTest,
@@ -842,14 +871,18 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
   protobufs::UniformBufferElementDescriptor blockname_3 =
       MakeUniformBufferElementDescriptor(0, 0, {0});
 
   uint32_t float_data[1];
   float temp = 3.0;
   memcpy(&float_data[0], &temp, sizeof(float));
-  ASSERT_TRUE(
-      AddFactHelper(&fact_manager, context.get(), float_data[0], blockname_3));
+  ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(),
+                            float_data[0], blockname_3));
 
   // The constant id is 9 for 3.0.
   protobufs::IdUseDescriptor use_of_9_in_store =
@@ -859,7 +892,7 @@
   // allow a constant index to be expressed:
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
                                                         blockname_3, 100, 101)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationReplaceConstantWithUniformTest,
@@ -928,13 +961,19 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
   protobufs::UniformBufferElementDescriptor blockname_9 =
       MakeUniformBufferElementDescriptor(0, 0, {0});
   protobufs::UniformBufferElementDescriptor blockname_10 =
       MakeUniformBufferElementDescriptor(0, 0, {1});
 
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 9, blockname_9));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 10, blockname_10));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 9, blockname_9));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 10, blockname_10));
 
   // The constant ids for 9 and 10 are 9 and 11 respectively
   protobufs::IdUseDescriptor use_of_9_in_store =
@@ -945,19 +984,19 @@
   // These are right:
   ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
                                                        blockname_9, 100, 101)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_11_in_store,
                                                        blockname_10, 102, 103)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
 
   // These are wrong because the constants do not match the facts about
   // uniforms.
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_store,
                                                         blockname_9, 100, 101)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
                                                         blockname_10, 102, 103)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationReplaceConstantWithUniformTest, ComplexReplacements) {
@@ -1141,6 +1180,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const float float_array_values[5] = {1.0, 1.5, 1.75, 1.875, 1.9375};
   uint32_t float_array_data[5];
@@ -1188,35 +1230,43 @@
   protobufs::UniformBufferElementDescriptor uniform_h_y =
       MakeUniformBufferElementDescriptor(0, 0, {2, 1});
 
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[0],
-                            uniform_f_a_0));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[1],
-                            uniform_f_a_1));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[2],
-                            uniform_f_a_2));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[3],
-                            uniform_f_a_3));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[4],
-                            uniform_f_a_4));
+  ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(),
+                            float_array_data[0], uniform_f_a_0));
+  ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(),
+                            float_array_data[1], uniform_f_a_1));
+  ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(),
+                            float_array_data[2], uniform_f_a_2));
+  ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(),
+                            float_array_data[3], uniform_f_a_3));
+  ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(),
+                            float_array_data[4], uniform_f_a_4));
 
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 1, uniform_f_b_x));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 2, uniform_f_b_y));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 3, uniform_f_b_z));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 4, uniform_f_b_w));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 1, uniform_f_b_x));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 2, uniform_f_b_y));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 3, uniform_f_b_z));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 4, uniform_f_b_w));
 
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_vector_data[0],
-                            uniform_f_c_x));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_vector_data[1],
-                            uniform_f_c_y));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_vector_data[2],
-                            uniform_f_c_z));
+  ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(),
+                            float_vector_data[0], uniform_f_c_x));
+  ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(),
+                            float_vector_data[1], uniform_f_c_y));
+  ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(),
+                            float_vector_data[2], uniform_f_c_z));
 
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 42, uniform_f_d));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 42, uniform_f_d));
 
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 22, uniform_g));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 22, uniform_g));
 
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 100, uniform_h_x));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 200, uniform_h_y));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 100, uniform_h_x));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 200, uniform_h_y));
 
   std::vector<TransformationReplaceConstantWithUniform> transformations;
 
@@ -1275,8 +1325,9 @@
       uniform_g, 218, 219));
 
   for (auto& transformation : transformations) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
@@ -1480,16 +1531,21 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
   protobufs::UniformBufferElementDescriptor blockname_a =
       MakeUniformBufferElementDescriptor(0, 0, {0});
 
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 0, blockname_a));
+  ASSERT_TRUE(
+      AddFactHelper(&transformation_context, context.get(), 0, blockname_a));
 
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(
                    MakeIdUseDescriptor(
                        50, MakeInstructionDescriptor(8, SpvOpVariable, 0), 1),
                    blockname_a, 100, 101)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace
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 41b6116..37e9510 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
@@ -220,15 +220,19 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
-  SetUpIdSynonyms(&fact_manager, context.get());
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  SetUpIdSynonyms(transformation_context.GetFactManager(), context.get());
 
   // %202 cannot replace %15 as in-operand 0 of %300, since %202 does not
   // dominate %300.
   auto synonym_does_not_dominate_use = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(15, MakeInstructionDescriptor(300, SpvOpIAdd, 0), 0),
       202);
-  ASSERT_FALSE(
-      synonym_does_not_dominate_use.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(synonym_does_not_dominate_use.IsApplicable(
+      context.get(), transformation_context));
 
   // %202 cannot replace %15 as in-operand 2 of %301, since this is the OpPhi's
   // incoming value for block %72, and %202 does not dominate %72.
@@ -237,22 +241,23 @@
           MakeIdUseDescriptor(15, MakeInstructionDescriptor(301, SpvOpPhi, 0),
                               2),
           202);
-  ASSERT_FALSE(synonym_does_not_dominate_use_op_phi.IsApplicable(context.get(),
-                                                                 fact_manager));
+  ASSERT_FALSE(synonym_does_not_dominate_use_op_phi.IsApplicable(
+      context.get(), transformation_context));
 
   // %200 is not a synonym for %84
   auto id_in_use_is_not_synonymous = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
           84, MakeInstructionDescriptor(67, SpvOpSGreaterThan, 0), 0),
       200);
-  ASSERT_FALSE(
-      id_in_use_is_not_synonymous.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(id_in_use_is_not_synonymous.IsApplicable(
+      context.get(), transformation_context));
 
   // %86 is not a synonym for anything (and in particular not for %74)
   auto id_has_no_synonyms = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(86, MakeInstructionDescriptor(84, SpvOpPhi, 0), 2),
       74);
-  ASSERT_FALSE(id_has_no_synonyms.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      id_has_no_synonyms.IsApplicable(context.get(), transformation_context));
 
   // This would lead to %207 = 'OpCopyObject %type %207' if it were allowed
   auto synonym_use_is_in_synonym_definition =
@@ -260,8 +265,8 @@
           MakeIdUseDescriptor(
               84, MakeInstructionDescriptor(207, SpvOpCopyObject, 0), 0),
           207);
-  ASSERT_FALSE(synonym_use_is_in_synonym_definition.IsApplicable(context.get(),
-                                                                 fact_manager));
+  ASSERT_FALSE(synonym_use_is_in_synonym_definition.IsApplicable(
+      context.get(), transformation_context));
 
   // The id use descriptor does not lead to a use (%84 is not used in the
   // definition of %207)
@@ -269,7 +274,8 @@
       MakeIdUseDescriptor(
           84, MakeInstructionDescriptor(200, SpvOpCopyObject, 0), 0),
       207);
-  ASSERT_FALSE(bad_id_use_descriptor.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_id_use_descriptor.IsApplicable(context.get(),
+                                                  transformation_context));
 
   // This replacement would lead to an access chain into a struct using a
   // non-constant index.
@@ -277,7 +283,8 @@
       MakeIdUseDescriptor(
           12, MakeInstructionDescriptor(14, SpvOpAccessChain, 0), 1),
       209);
-  ASSERT_FALSE(bad_access_chain.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      bad_access_chain.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) {
@@ -288,23 +295,28 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
-  SetUpIdSynonyms(&fact_manager, context.get());
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  SetUpIdSynonyms(transformation_context.GetFactManager(), context.get());
 
   auto global_constant_synonym = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(19, MakeInstructionDescriptor(47, SpvOpStore, 0), 1),
       210);
-  ASSERT_TRUE(
-      global_constant_synonym.IsApplicable(context.get(), fact_manager));
-  global_constant_synonym.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(global_constant_synonym.IsApplicable(context.get(),
+                                                   transformation_context));
+  global_constant_synonym.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto replace_vector_access_chain_index = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
           54, MakeInstructionDescriptor(55, SpvOpAccessChain, 0), 1),
       204);
-  ASSERT_TRUE(replace_vector_access_chain_index.IsApplicable(context.get(),
-                                                             fact_manager));
-  replace_vector_access_chain_index.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(replace_vector_access_chain_index.IsApplicable(
+      context.get(), transformation_context));
+  replace_vector_access_chain_index.Apply(context.get(),
+                                          &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // This is an interesting case because it replaces something that is being
@@ -313,22 +325,24 @@
       MakeIdUseDescriptor(
           15, MakeInstructionDescriptor(202, SpvOpCopyObject, 0), 0),
       201);
-  ASSERT_TRUE(regular_replacement.IsApplicable(context.get(), fact_manager));
-  regular_replacement.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      regular_replacement.IsApplicable(context.get(), transformation_context));
+  regular_replacement.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto regular_replacement2 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(55, MakeInstructionDescriptor(203, SpvOpStore, 0), 0),
       203);
-  ASSERT_TRUE(regular_replacement2.IsApplicable(context.get(), fact_manager));
-  regular_replacement2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      regular_replacement2.IsApplicable(context.get(), transformation_context));
+  regular_replacement2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto good_op_phi = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(74, MakeInstructionDescriptor(86, SpvOpPhi, 0), 2),
       205);
-  ASSERT_TRUE(good_op_phi.IsApplicable(context.get(), fact_manager));
-  good_op_phi.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(good_op_phi.IsApplicable(context.get(), transformation_context));
+  good_op_phi.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   const std::string after_transformation = R"(
@@ -504,17 +518,22 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  fact_manager.AddFact(MakeSynonymFact(10, 100), context.get());
-  fact_manager.AddFact(MakeSynonymFact(8, 101), context.get());
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(10, 100),
+                                                   context.get());
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(8, 101),
+                                                   context.get());
 
   // Replace %10 with %100 in:
   // %11 = OpLoad %6 %10
   auto replacement1 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(10, MakeInstructionDescriptor(11, SpvOpLoad, 0), 0),
       100);
-  ASSERT_TRUE(replacement1.IsApplicable(context.get(), fact_manager));
-  replacement1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context));
+  replacement1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %8 with %101 in:
@@ -522,8 +541,8 @@
   auto replacement2 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(8, MakeInstructionDescriptor(11, SpvOpStore, 0), 0),
       101);
-  ASSERT_TRUE(replacement2.IsApplicable(context.get(), fact_manager));
-  replacement2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context));
+  replacement2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %8 with %101 in:
@@ -531,8 +550,8 @@
   auto replacement3 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(8, MakeInstructionDescriptor(12, SpvOpLoad, 0), 0),
       101);
-  ASSERT_TRUE(replacement3.IsApplicable(context.get(), fact_manager));
-  replacement3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(replacement3.IsApplicable(context.get(), transformation_context));
+  replacement3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %10 with %100 in:
@@ -540,8 +559,8 @@
   auto replacement4 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(10, MakeInstructionDescriptor(12, SpvOpStore, 0), 0),
       100);
-  ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager));
-  replacement4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(replacement4.IsApplicable(context.get(), transformation_context));
+  replacement4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   const std::string after_transformation = R"(
@@ -633,8 +652,12 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  fact_manager.AddFact(MakeSynonymFact(14, 100), context.get());
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(14, 100),
+                                                   context.get());
 
   // Replace %14 with %100 in:
   // %16 = OpFunctionCall %2 %10 %14
@@ -642,7 +665,7 @@
       MakeIdUseDescriptor(
           14, MakeInstructionDescriptor(16, SpvOpFunctionCall, 0), 1),
       100);
-  ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(replacement.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
@@ -795,22 +818,38 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Add synonym facts corresponding to the OpCopyObject operations that have
   // been applied to all constants in the module.
-  fact_manager.AddFact(MakeSynonymFact(16, 100), context.get());
-  fact_manager.AddFact(MakeSynonymFact(21, 101), context.get());
-  fact_manager.AddFact(MakeSynonymFact(17, 102), context.get());
-  fact_manager.AddFact(MakeSynonymFact(57, 103), context.get());
-  fact_manager.AddFact(MakeSynonymFact(18, 104), context.get());
-  fact_manager.AddFact(MakeSynonymFact(40, 105), context.get());
-  fact_manager.AddFact(MakeSynonymFact(32, 106), context.get());
-  fact_manager.AddFact(MakeSynonymFact(43, 107), context.get());
-  fact_manager.AddFact(MakeSynonymFact(55, 108), context.get());
-  fact_manager.AddFact(MakeSynonymFact(8, 109), context.get());
-  fact_manager.AddFact(MakeSynonymFact(47, 110), context.get());
-  fact_manager.AddFact(MakeSynonymFact(28, 111), context.get());
-  fact_manager.AddFact(MakeSynonymFact(45, 112), context.get());
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(16, 100),
+                                                   context.get());
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(21, 101),
+                                                   context.get());
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(17, 102),
+                                                   context.get());
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(57, 103),
+                                                   context.get());
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(18, 104),
+                                                   context.get());
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(40, 105),
+                                                   context.get());
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(32, 106),
+                                                   context.get());
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(43, 107),
+                                                   context.get());
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(55, 108),
+                                                   context.get());
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(8, 109),
+                                                   context.get());
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(47, 110),
+                                                   context.get());
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(28, 111),
+                                                   context.get());
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(45, 112),
+                                                   context.get());
 
   // Replacements of the form %16 -> %100
 
@@ -821,7 +860,8 @@
       MakeIdUseDescriptor(
           16, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 1),
       100);
-  ASSERT_FALSE(replacement1.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement1.IsApplicable(context.get(), transformation_context));
 
   // %39 = OpAccessChain %23 %37 *%16*
   // Corresponds to h.*f*
@@ -830,7 +870,8 @@
       MakeIdUseDescriptor(
           16, MakeInstructionDescriptor(39, SpvOpAccessChain, 0), 1),
       100);
-  ASSERT_FALSE(replacement2.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement2.IsApplicable(context.get(), transformation_context));
 
   // %41 = OpAccessChain %19 %37 %21 *%16* %21
   // Corresponds to h.g.*a*[1]
@@ -839,7 +880,8 @@
       MakeIdUseDescriptor(
           16, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 2),
       100);
-  ASSERT_FALSE(replacement3.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement3.IsApplicable(context.get(), transformation_context));
 
   // %52 = OpAccessChain %23 %50 *%16* %16
   // Corresponds to i[*0*].f
@@ -848,8 +890,8 @@
       MakeIdUseDescriptor(
           16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 1),
       100);
-  ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager));
-  replacement4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(replacement4.IsApplicable(context.get(), transformation_context));
+  replacement4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // %52 = OpAccessChain %23 %50 %16 *%16*
@@ -859,7 +901,8 @@
       MakeIdUseDescriptor(
           16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 2),
       100);
-  ASSERT_FALSE(replacement5.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement5.IsApplicable(context.get(), transformation_context));
 
   // %53 = OpAccessChain %19 %50 %21 %21 *%16* %16
   // Corresponds to i[1].g.*a*[0]
@@ -868,7 +911,8 @@
       MakeIdUseDescriptor(
           16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 3),
       100);
-  ASSERT_FALSE(replacement6.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement6.IsApplicable(context.get(), transformation_context));
 
   // %53 = OpAccessChain %19 %50 %21 %21 %16 *%16*
   // Corresponds to i[1].g.a[*0*]
@@ -877,8 +921,8 @@
       MakeIdUseDescriptor(
           16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 4),
       100);
-  ASSERT_TRUE(replacement7.IsApplicable(context.get(), fact_manager));
-  replacement7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(replacement7.IsApplicable(context.get(), transformation_context));
+  replacement7.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replacements of the form %21 -> %101
@@ -890,7 +934,8 @@
       MakeIdUseDescriptor(
           21, MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 1),
       101);
-  ASSERT_FALSE(replacement8.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement8.IsApplicable(context.get(), transformation_context));
 
   // %41 = OpAccessChain %19 %37 *%21* %16 %21
   // Corresponds to h.*g*.a[1]
@@ -899,7 +944,8 @@
       MakeIdUseDescriptor(
           21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 1),
       101);
-  ASSERT_FALSE(replacement9.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement9.IsApplicable(context.get(), transformation_context));
 
   // %41 = OpAccessChain %19 %37 %21 %16 *%21*
   // Corresponds to h.g.a[*1*]
@@ -908,8 +954,9 @@
       MakeIdUseDescriptor(
           21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 3),
       101);
-  ASSERT_TRUE(replacement10.IsApplicable(context.get(), fact_manager));
-  replacement10.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement10.IsApplicable(context.get(), transformation_context));
+  replacement10.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // %44 = OpAccessChain %23 %37 *%21* %21 %43
@@ -919,7 +966,8 @@
       MakeIdUseDescriptor(
           21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 1),
       101);
-  ASSERT_FALSE(replacement11.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement11.IsApplicable(context.get(), transformation_context));
 
   // %44 = OpAccessChain %23 %37 %21 *%21* %43
   // Corresponds to h.g.*b*[0]
@@ -928,7 +976,8 @@
       MakeIdUseDescriptor(
           21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 2),
       101);
-  ASSERT_FALSE(replacement12.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement12.IsApplicable(context.get(), transformation_context));
 
   // %46 = OpAccessChain %26 %37 *%21* %17
   // Corresponds to h.*g*.c
@@ -937,7 +986,8 @@
       MakeIdUseDescriptor(
           21, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 1),
       101);
-  ASSERT_FALSE(replacement13.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement13.IsApplicable(context.get(), transformation_context));
 
   // %53 = OpAccessChain %19 %50 *%21* %21 %16 %16
   // Corresponds to i[*1*].g.a[0]
@@ -946,8 +996,9 @@
       MakeIdUseDescriptor(
           21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 1),
       101);
-  ASSERT_TRUE(replacement14.IsApplicable(context.get(), fact_manager));
-  replacement14.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement14.IsApplicable(context.get(), transformation_context));
+  replacement14.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // %53 = OpAccessChain %19 %50 %21 *%21* %16 %16
@@ -957,7 +1008,8 @@
       MakeIdUseDescriptor(
           21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 2),
       101);
-  ASSERT_FALSE(replacement15.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement15.IsApplicable(context.get(), transformation_context));
 
   // %56 = OpAccessChain %23 %50 %17 *%21* %21 %55
   // Corresponds to i[2].*g*.b[1]
@@ -966,7 +1018,8 @@
       MakeIdUseDescriptor(
           21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 2),
       101);
-  ASSERT_FALSE(replacement16.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement16.IsApplicable(context.get(), transformation_context));
 
   // %56 = OpAccessChain %23 %50 %17 %21 *%21* %55
   // Corresponds to i[2].g.*b*[1]
@@ -975,7 +1028,8 @@
       MakeIdUseDescriptor(
           21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 3),
       101);
-  ASSERT_FALSE(replacement17.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement17.IsApplicable(context.get(), transformation_context));
 
   // %58 = OpAccessChain %26 %50 %57 *%21* %17
   // Corresponds to i[3].*g*.c
@@ -984,7 +1038,8 @@
       MakeIdUseDescriptor(
           21, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 2),
       101);
-  ASSERT_FALSE(replacement18.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement18.IsApplicable(context.get(), transformation_context));
 
   // Replacements of the form %17 -> %102
 
@@ -995,8 +1050,9 @@
       MakeIdUseDescriptor(
           17, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 2),
       102);
-  ASSERT_TRUE(replacement19.IsApplicable(context.get(), fact_manager));
-  replacement19.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement19.IsApplicable(context.get(), transformation_context));
+  replacement19.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // %27 = OpAccessChain %26 %15 %17
@@ -1006,7 +1062,8 @@
       MakeIdUseDescriptor(
           17, MakeInstructionDescriptor(27, SpvOpAccessChain, 0), 1),
       102);
-  ASSERT_FALSE(replacement20.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement20.IsApplicable(context.get(), transformation_context));
 
   // %46 = OpAccessChain %26 %37 %21 %17
   // Corresponds to h.g.*c*
@@ -1015,7 +1072,8 @@
       MakeIdUseDescriptor(
           17, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 2),
       102);
-  ASSERT_FALSE(replacement21.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement21.IsApplicable(context.get(), transformation_context));
 
   // %56 = OpAccessChain %23 %50 %17 %21 %21 %55
   // Corresponds to i[*2*].g.b[1]
@@ -1024,8 +1082,9 @@
       MakeIdUseDescriptor(
           17, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 1),
       102);
-  ASSERT_TRUE(replacement22.IsApplicable(context.get(), fact_manager));
-  replacement22.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement22.IsApplicable(context.get(), transformation_context));
+  replacement22.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // %58 = OpAccessChain %26 %50 %57 %21 %17
@@ -1035,7 +1094,8 @@
       MakeIdUseDescriptor(
           17, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 3),
       102);
-  ASSERT_FALSE(replacement23.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      replacement23.IsApplicable(context.get(), transformation_context));
 
   // Replacements of the form %57 -> %103
 
@@ -1046,8 +1106,9 @@
       MakeIdUseDescriptor(
           57, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 1),
       103);
-  ASSERT_TRUE(replacement24.IsApplicable(context.get(), fact_manager));
-  replacement24.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement24.IsApplicable(context.get(), transformation_context));
+  replacement24.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replacements of the form %32 -> %106
@@ -1059,8 +1120,9 @@
       MakeIdUseDescriptor(
           32, MakeInstructionDescriptor(34, SpvOpAccessChain, 0), 1),
       106);
-  ASSERT_TRUE(replacement25.IsApplicable(context.get(), fact_manager));
-  replacement25.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement25.IsApplicable(context.get(), transformation_context));
+  replacement25.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replacements of the form %43 -> %107
@@ -1072,8 +1134,9 @@
       MakeIdUseDescriptor(
           43, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 3),
       107);
-  ASSERT_TRUE(replacement26.IsApplicable(context.get(), fact_manager));
-  replacement26.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement26.IsApplicable(context.get(), transformation_context));
+  replacement26.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replacements of the form %55 -> %108
@@ -1085,8 +1148,9 @@
       MakeIdUseDescriptor(
           55, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 4),
       108);
-  ASSERT_TRUE(replacement27.IsApplicable(context.get(), fact_manager));
-  replacement27.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement27.IsApplicable(context.get(), transformation_context));
+  replacement27.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replacements of the form %8 -> %109
@@ -1098,8 +1162,9 @@
       MakeIdUseDescriptor(8, MakeInstructionDescriptor(24, SpvOpAccessChain, 0),
                           2),
       109);
-  ASSERT_TRUE(replacement28.IsApplicable(context.get(), fact_manager));
-  replacement28.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement28.IsApplicable(context.get(), transformation_context));
+  replacement28.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   const std::string after_transformation = R"(
@@ -1212,6 +1277,179 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationReplaceIdWithSynonymTest, RuntimeArrayTest) {
+  // This checks that OpRuntimeArray is correctly handled.
+  const std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %8 ArrayStride 8
+               OpMemberDecorate %9 0 Offset 0
+               OpDecorate %9 BufferBlock
+               OpDecorate %11 DescriptorSet 0
+               OpDecorate %11 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeVector %6 2
+          %8 = OpTypeRuntimeArray %7
+          %9 = OpTypeStruct %8
+         %10 = OpTypePointer Uniform %9
+         %11 = OpVariable %10 Uniform
+         %12 = OpConstant %6 0
+         %13 = OpTypeInt 32 0
+         %14 = OpConstant %13 0
+         %15 = OpTypePointer Uniform %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %50 = OpCopyObject %6 %12
+         %51 = OpCopyObject %13 %14
+         %16 = OpAccessChain %15 %11 %12 %12 %14
+               OpStore %16 %12
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Add synonym fact relating %50 and %12.
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(50, 12),
+                                                   context.get());
+  // Add synonym fact relating %51 and %14.
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(51, 14),
+                                                   context.get());
+
+  // Not legal because the index being replaced is a struct index.
+  ASSERT_FALSE(
+      TransformationReplaceIdWithSynonym(
+          MakeIdUseDescriptor(
+              12, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 1),
+          50)
+          .IsApplicable(context.get(), transformation_context));
+
+  // Fine to replace an index into a runtime array.
+  auto replacement1 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          12, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 2),
+      50);
+  ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context));
+  replacement1.Apply(context.get(), &transformation_context);
+
+  // Fine to replace an index into a vector inside the runtime array.
+  auto replacement2 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          14, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 3),
+      51);
+  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 %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %8 ArrayStride 8
+               OpMemberDecorate %9 0 Offset 0
+               OpDecorate %9 BufferBlock
+               OpDecorate %11 DescriptorSet 0
+               OpDecorate %11 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeVector %6 2
+          %8 = OpTypeRuntimeArray %7
+          %9 = OpTypeStruct %8
+         %10 = OpTypePointer Uniform %9
+         %11 = OpVariable %10 Uniform
+         %12 = OpConstant %6 0
+         %13 = OpTypeInt 32 0
+         %14 = OpConstant %13 0
+         %15 = OpTypePointer Uniform %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %50 = OpCopyObject %6 %12
+         %51 = OpCopyObject %13 %14
+         %16 = OpAccessChain %15 %11 %12 %50 %51
+               OpStore %16 %12
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationReplaceIdWithSynonymTest,
+     DoNotReplaceSampleParameterOfOpImageTexelPointer) {
+  const 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 = OpTypeFunction %4
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpConstant %6 2
+          %9 = OpConstant %6 0
+         %10 = OpConstant %6 10
+         %11 = OpTypeBool
+         %12 = OpConstant %6 1
+         %13 = OpTypeFloat 32
+         %14 = OpTypePointer Image %13
+         %15 = OpTypeImage %13 2D 0 0 0 0 Rgba8
+         %16 = OpTypePointer Private %15
+          %3 = OpVariable %16 Private
+         %17 = OpTypeVector %6 2
+         %18 = OpConstantComposite %17 %9 %9
+          %2 = OpFunction %4 None %5
+         %19 = OpLabel
+        %100 = OpCopyObject %6 %9
+         %20 = OpImageTexelPointer %14 %3 %18 %9
+               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);
+
+  // Add synonym fact relating %100 and %9.
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(100, 9),
+                                                   context.get());
+
+  // Not legal the Sample argument of OpImageTexelPointer needs to be a zero
+  // constant.
+  ASSERT_FALSE(
+      TransformationReplaceIdWithSynonym(
+          MakeIdUseDescriptor(
+              9, MakeInstructionDescriptor(20, SpvOpImageTexelPointer, 0), 2),
+          100)
+          .IsApplicable(context.get(), transformation_context));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_set_function_control_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_set_function_control_test.cpp
index 536e965..be7f2be 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_set_function_control_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_set_function_control_test.cpp
@@ -118,41 +118,48 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // %36 is not a function
   ASSERT_FALSE(TransformationSetFunctionControl(36, SpvFunctionControlMaskNone)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Cannot add the Pure function control to %4 as it did not already have it
   ASSERT_FALSE(TransformationSetFunctionControl(4, SpvFunctionControlPureMask)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Cannot add the Const function control to %21 as it did not already
   // have it
   ASSERT_FALSE(TransformationSetFunctionControl(21, SpvFunctionControlConstMask)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Set to None, removing Const
   TransformationSetFunctionControl transformation1(11,
                                                    SpvFunctionControlMaskNone);
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
 
   // Set to Inline; silly to do it on an entry point, but it is allowed
   TransformationSetFunctionControl transformation2(
       4, SpvFunctionControlInlineMask);
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
 
   // Set to Pure, removing DontInline
   TransformationSetFunctionControl transformation3(17,
                                                    SpvFunctionControlPureMask);
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
 
   // Change from Inline to DontInline
   TransformationSetFunctionControl transformation4(
       13, SpvFunctionControlDontInlineMask);
-  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation4.IsApplicable(context.get(), transformation_context));
+  transformation4.Apply(context.get(), &transformation_context);
 
   std::string after_transformation = R"(
                OpCapability Shader
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_set_loop_control_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_set_loop_control_test.cpp
index 83953ec..531aa7a 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_set_loop_control_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_set_loop_control_test.cpp
@@ -256,6 +256,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // These are the loop headers together with the selection controls of their
   // merge instructions:
@@ -275,310 +278,310 @@
   // 2 5 90 4 7 14
 
   ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlMaskNone, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlUnrollMask, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(10, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(
                    10, SpvLoopControlDependencyInfiniteMask, 0, 0)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSetLoopControl(10, SpvLoopControlDependencyLengthMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSetLoopControl(10, SpvLoopControlMinIterationsMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSetLoopControl(10, SpvLoopControlMaxIterationsMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(
                    10, SpvLoopControlIterationMultipleMask, 0, 0)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 3)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 0, 3)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 3, 3)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(
                   10,
                   SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
                   3, 3)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(10,
                                            SpvLoopControlUnrollMask |
                                                SpvLoopControlPeelCountMask |
                                                SpvLoopControlPartialCountMask,
                                            3, 3)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(10,
                                             SpvLoopControlDontUnrollMask |
                                                 SpvLoopControlPeelCountMask |
                                                 SpvLoopControlPartialCountMask,
                                             3, 3)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlMaskNone, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlUnrollMask, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(
                   23,
                   SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
                   3, 3)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSetLoopControl(23, SpvLoopControlMaxIterationsMask, 2, 3)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlMaskNone, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(33, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSetLoopControl(33, SpvLoopControlMinIterationsMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(
           33, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(33,
                                             SpvLoopControlDontUnrollMask |
                                                 SpvLoopControlPartialCountMask,
                                             0, 10)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlMaskNone, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlUnrollMask, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(43, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(
                   43,
                   SpvLoopControlMaskNone | SpvLoopControlDependencyInfiniteMask,
                   0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(
           43, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask,
           0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(
           43,
           SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask,
           0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSetLoopControl(43,
                                    SpvLoopControlDependencyInfiniteMask |
                                        SpvLoopControlDependencyLengthMask,
                                    0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(
           43, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlUnrollMask, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(53, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSetLoopControl(53, SpvLoopControlMaxIterationsMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(
           53, SpvLoopControlMaskNone | SpvLoopControlDependencyLengthMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSetLoopControl(
           53, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask,
           0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(
           53, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyLengthMask,
           0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSetLoopControl(53,
                                    SpvLoopControlDependencyInfiniteMask |
                                        SpvLoopControlDependencyLengthMask,
                                    0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(
           53,
           SpvLoopControlUnrollMask | SpvLoopControlDependencyLengthMask |
               SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
           5, 3)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlMaskNone, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlUnrollMask, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(63, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(63,
                                            SpvLoopControlUnrollMask |
                                                SpvLoopControlMinIterationsMask |
                                                SpvLoopControlPeelCountMask |
                                                SpvLoopControlPartialCountMask,
                                            5, 3)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(63,
                                            SpvLoopControlUnrollMask |
                                                SpvLoopControlMinIterationsMask |
                                                SpvLoopControlPeelCountMask,
                                            23, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(
                    63,
                    SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
                        SpvLoopControlPeelCountMask,
                    2, 23)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlMaskNone, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlUnrollMask, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(73, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(
                    73,
                    SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
                        SpvLoopControlPeelCountMask |
                        SpvLoopControlPartialCountMask,
                    5, 3)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(73,
                                            SpvLoopControlUnrollMask |
                                                SpvLoopControlMaxIterationsMask |
                                                SpvLoopControlPeelCountMask,
                                            23, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(
                    73,
                    SpvLoopControlUnrollMask | SpvLoopControlMaxIterationsMask |
                        SpvLoopControlPeelCountMask,
                    2, 23)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlMaskNone, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlUnrollMask, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(
                    83,
                    SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
                        SpvLoopControlPeelCountMask |
                        SpvLoopControlPartialCountMask,
                    5, 3)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(83,
                                    SpvLoopControlUnrollMask |
                                        SpvLoopControlIterationMultipleMask |
                                        SpvLoopControlPeelCountMask,
                                    23, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSetLoopControl(83,
                                    SpvLoopControlUnrollMask |
                                        SpvLoopControlIterationMultipleMask |
                                        SpvLoopControlPeelCountMask,
                                    2, 23)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlMaskNone, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlUnrollMask, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(93, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 8)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(93, SpvLoopControlPartialCountMask, 0, 8)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(
                   93,
                   SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
                   16, 8)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
 
   ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlMaskNone, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlUnrollMask, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(103, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(103,
                                             SpvLoopControlDontUnrollMask |
                                                 SpvLoopControlPartialCountMask,
                                             0, 60)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlMaskNone, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlUnrollMask, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(113, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSetLoopControl(
           113,
           SpvLoopControlIterationMultipleMask | SpvLoopControlPeelCountMask, 12,
           0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlMaskNone, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlUnrollMask, 0, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(123, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
       TransformationSetLoopControl(
           123,
@@ -586,72 +589,72 @@
               SpvLoopControlIterationMultipleMask |
               SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
           7, 8)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(123,
                                            SpvLoopControlUnrollMask |
                                                SpvLoopControlMinIterationsMask |
                                                SpvLoopControlMaxIterationsMask |
                                                SpvLoopControlPartialCountMask,
                                            0, 9)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(
                    123,
                    SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
                        SpvLoopControlMaxIterationsMask |
                        SpvLoopControlPartialCountMask,
                    7, 9)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSetLoopControl(
           123,
           SpvLoopControlDontUnrollMask | SpvLoopControlMinIterationsMask |
               SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask,
           7, 9)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   TransformationSetLoopControl(10,
                                SpvLoopControlUnrollMask |
                                    SpvLoopControlPeelCountMask |
                                    SpvLoopControlPartialCountMask,
                                3, 3)
-      .Apply(context.get(), &fact_manager);
+      .Apply(context.get(), &transformation_context);
   TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0)
-      .Apply(context.get(), &fact_manager);
+      .Apply(context.get(), &transformation_context);
   TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0)
-      .Apply(context.get(), &fact_manager);
+      .Apply(context.get(), &transformation_context);
   TransformationSetLoopControl(
       43, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask,
       0, 0)
-      .Apply(context.get(), &fact_manager);
+      .Apply(context.get(), &transformation_context);
   TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0)
-      .Apply(context.get(), &fact_manager);
+      .Apply(context.get(), &transformation_context);
   TransformationSetLoopControl(63,
                                SpvLoopControlUnrollMask |
                                    SpvLoopControlMinIterationsMask |
                                    SpvLoopControlPeelCountMask,
                                23, 0)
-      .Apply(context.get(), &fact_manager);
+      .Apply(context.get(), &transformation_context);
   TransformationSetLoopControl(73,
                                SpvLoopControlUnrollMask |
                                    SpvLoopControlMaxIterationsMask |
                                    SpvLoopControlPeelCountMask,
                                23, 0)
-      .Apply(context.get(), &fact_manager);
+      .Apply(context.get(), &transformation_context);
   TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0)
-      .Apply(context.get(), &fact_manager);
+      .Apply(context.get(), &transformation_context);
   TransformationSetLoopControl(
       93, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 16, 8)
-      .Apply(context.get(), &fact_manager);
+      .Apply(context.get(), &transformation_context);
   TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60)
-      .Apply(context.get(), &fact_manager);
+      .Apply(context.get(), &transformation_context);
   TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0)
-      .Apply(context.get(), &fact_manager);
+      .Apply(context.get(), &transformation_context);
   TransformationSetLoopControl(
       123,
       SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
           SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask,
       0, 9)
-      .Apply(context.get(), &fact_manager);
+      .Apply(context.get(), &transformation_context);
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -942,25 +945,28 @@
       BuildModule(SPV_ENV_UNIVERSAL_1_5, consumer, shader, kFuzzAssembleOption);
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationSetLoopControl set_peel_and_partial(
       10, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 4, 4);
 
   // PeelCount and PartialCount were introduced in SPIRV 1.4, so are not valid
   // in the context of older versions.
-  ASSERT_FALSE(
-      set_peel_and_partial.IsApplicable(context_1_0.get(), fact_manager));
-  ASSERT_FALSE(
-      set_peel_and_partial.IsApplicable(context_1_1.get(), fact_manager));
-  ASSERT_FALSE(
-      set_peel_and_partial.IsApplicable(context_1_2.get(), fact_manager));
-  ASSERT_FALSE(
-      set_peel_and_partial.IsApplicable(context_1_3.get(), fact_manager));
+  ASSERT_FALSE(set_peel_and_partial.IsApplicable(context_1_0.get(),
+                                                 transformation_context));
+  ASSERT_FALSE(set_peel_and_partial.IsApplicable(context_1_1.get(),
+                                                 transformation_context));
+  ASSERT_FALSE(set_peel_and_partial.IsApplicable(context_1_2.get(),
+                                                 transformation_context));
+  ASSERT_FALSE(set_peel_and_partial.IsApplicable(context_1_3.get(),
+                                                 transformation_context));
 
-  ASSERT_TRUE(
-      set_peel_and_partial.IsApplicable(context_1_4.get(), fact_manager));
-  ASSERT_TRUE(
-      set_peel_and_partial.IsApplicable(context_1_5.get(), fact_manager));
+  ASSERT_TRUE(set_peel_and_partial.IsApplicable(context_1_4.get(),
+                                                transformation_context));
+  ASSERT_TRUE(set_peel_and_partial.IsApplicable(context_1_5.get(),
+                                                transformation_context));
 }
 
 }  // namespace
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 ad4dc25..c02d8d4 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
@@ -92,37 +92,41 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Not OK: the instruction is not a memory access.
   ASSERT_FALSE(TransformationSetMemoryOperandsMask(
                    MakeInstructionDescriptor(21, SpvOpAccessChain, 0),
                    SpvMemoryAccessMaskNone, 0)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Not OK to remove Aligned
   ASSERT_FALSE(TransformationSetMemoryOperandsMask(
                    MakeInstructionDescriptor(147, SpvOpLoad, 0),
                    SpvMemoryAccessVolatileMask | SpvMemoryAccessNontemporalMask,
                    0)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   TransformationSetMemoryOperandsMask transformation1(
       MakeInstructionDescriptor(147, SpvOpLoad, 0),
       SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0);
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
 
   // Not OK to remove Aligned
   ASSERT_FALSE(TransformationSetMemoryOperandsMask(
                    MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
                    SpvMemoryAccessMaskNone, 0)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // OK: leaves the mask as is
   ASSERT_TRUE(TransformationSetMemoryOperandsMask(
                   MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
                   SpvMemoryAccessAlignedMask, 0)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
 
   // OK: adds Nontemporal and Volatile
   TransformationSetMemoryOperandsMask transformation2(
@@ -130,41 +134,45 @@
       SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask |
           SpvMemoryAccessVolatileMask,
       0);
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
 
   // Not OK to remove Volatile
   ASSERT_FALSE(TransformationSetMemoryOperandsMask(
                    MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
                    SpvMemoryAccessNontemporalMask, 0)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Not OK to add Aligned
   ASSERT_FALSE(TransformationSetMemoryOperandsMask(
                    MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
                    SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // OK: adds Nontemporal
   TransformationSetMemoryOperandsMask transformation3(
       MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
       SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.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(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation4.IsApplicable(context.get(), transformation_context));
+  transformation4.Apply(context.get(), &transformation_context);
 
   // OK: removes Nontemporal, adds Volatile
   TransformationSetMemoryOperandsMask transformation5(
       MakeInstructionDescriptor(148, SpvOpStore, 0),
       SpvMemoryAccessVolatileMask, 0);
-  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
-  transformation5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation5.IsApplicable(context.get(), transformation_context));
+  transformation5.Apply(context.get(), &transformation_context);
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -306,6 +314,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationSetMemoryOperandsMask transformation1(
       MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
@@ -314,9 +325,10 @@
   ASSERT_FALSE(TransformationSetMemoryOperandsMask(
                    MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
                    SpvMemoryAccessVolatileMask, 1)
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
 
   TransformationSetMemoryOperandsMask transformation2(
       MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
@@ -325,9 +337,10 @@
   ASSERT_FALSE(TransformationSetMemoryOperandsMask(
                    MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
                    SpvMemoryAccessNontemporalMask, 0)
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
 
   TransformationSetMemoryOperandsMask transformation3(
       MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
@@ -337,27 +350,31 @@
                    MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
                    SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask,
                    0)
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
 
   TransformationSetMemoryOperandsMask transformation4(
       MakeInstructionDescriptor(138, SpvOpCopyMemory, 1),
       SpvMemoryAccessVolatileMask, 1);
-  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation4.IsApplicable(context.get(), transformation_context));
+  transformation4.Apply(context.get(), &transformation_context);
 
   TransformationSetMemoryOperandsMask transformation5(
       MakeInstructionDescriptor(147, SpvOpLoad, 0),
       SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, 0);
-  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
-  transformation5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation5.IsApplicable(context.get(), transformation_context));
+  transformation5.Apply(context.get(), &transformation_context);
 
   TransformationSetMemoryOperandsMask transformation6(
       MakeInstructionDescriptor(148, SpvOpStore, 0), SpvMemoryAccessMaskNone,
       0);
-  ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
-  transformation6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation6.IsApplicable(context.get(), transformation_context));
+  transformation6.Apply(context.get(), &transformation_context);
 
   std::string after_transformation = R"(
                OpCapability Shader
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_set_selection_control_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_set_selection_control_test.cpp
index 9696417..9afb89d 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_set_selection_control_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_set_selection_control_test.cpp
@@ -103,39 +103,46 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // %44 is not a block
   ASSERT_FALSE(
       TransformationSetSelectionControl(44, SpvSelectionControlFlattenMask)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // %13 does not end with OpSelectionMerge
   ASSERT_FALSE(
       TransformationSetSelectionControl(13, SpvSelectionControlMaskNone)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // %10 ends in OpLoopMerge, not OpSelectionMerge
   ASSERT_FALSE(
       TransformationSetSelectionControl(10, SpvSelectionControlMaskNone)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   TransformationSetSelectionControl transformation1(
       11, SpvSelectionControlDontFlattenMask);
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
 
   TransformationSetSelectionControl transformation2(
       23, SpvSelectionControlFlattenMask);
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
 
   TransformationSetSelectionControl transformation3(
       31, SpvSelectionControlMaskNone);
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
 
   TransformationSetSelectionControl transformation4(
       31, SpvSelectionControlFlattenMask);
-  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation4.IsApplicable(context.get(), transformation_context));
+  transformation4.Apply(context.get(), &transformation_context);
 
   std::string after_transformation = R"(
                OpCapability Shader
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_split_block_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_split_block_test.cpp
index 09007a5..30bac02 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_split_block_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_split_block_test.cpp
@@ -89,57 +89,60 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // No split before OpVariable
   ASSERT_FALSE(TransformationSplitBlock(
                    MakeInstructionDescriptor(8, SpvOpVariable, 0), 100)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSplitBlock(
                    MakeInstructionDescriptor(8, SpvOpVariable, 1), 100)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // No split before OpLabel
   ASSERT_FALSE(TransformationSplitBlock(
                    MakeInstructionDescriptor(14, SpvOpLabel, 0), 100)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // No split if base instruction is outside a function
   ASSERT_FALSE(
       TransformationSplitBlock(MakeInstructionDescriptor(1, SpvOpLabel, 0), 100)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSplitBlock(
                    MakeInstructionDescriptor(1, SpvOpExecutionMode, 0), 100)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // No split if block is loop header
   ASSERT_FALSE(
       TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // No split if base instruction does not exist
   ASSERT_FALSE(
       TransformationSplitBlock(MakeInstructionDescriptor(88, SpvOpIAdd, 0), 100)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSplitBlock(
                    MakeInstructionDescriptor(88, SpvOpIMul, 22), 100)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // No split if too many instructions with the desired opcode are skipped
   ASSERT_FALSE(
       TransformationSplitBlock(
           MakeInstructionDescriptor(18, SpvOpBranchConditional, 1), 100)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // No split if id in use
   ASSERT_FALSE(TransformationSplitBlock(
                    MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 27)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSplitBlock(
                    MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 14)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) {
@@ -199,11 +202,14 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto split_1 = TransformationSplitBlock(
       MakeInstructionDescriptor(5, SpvOpStore, 0), 100);
-  ASSERT_TRUE(split_1.IsApplicable(context.get(), fact_manager));
-  split_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(split_1.IsApplicable(context.get(), transformation_context));
+  split_1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_split_1 = R"(
@@ -250,8 +256,8 @@
 
   auto split_2 = TransformationSplitBlock(
       MakeInstructionDescriptor(11, SpvOpStore, 0), 101);
-  ASSERT_TRUE(split_2.IsApplicable(context.get(), fact_manager));
-  split_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(split_2.IsApplicable(context.get(), transformation_context));
+  split_2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_split_2 = R"(
@@ -300,8 +306,8 @@
 
   auto split_3 = TransformationSplitBlock(
       MakeInstructionDescriptor(14, SpvOpLoad, 0), 102);
-  ASSERT_TRUE(split_3.IsApplicable(context.get(), fact_manager));
-  split_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(split_3.IsApplicable(context.get(), transformation_context));
+  split_3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_split_3 = R"(
@@ -412,21 +418,24 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Illegal to split between the merge and the conditional branch.
   ASSERT_FALSE(
       TransformationSplitBlock(
           MakeInstructionDescriptor(14, SpvOpBranchConditional, 0), 100)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSplitBlock(
           MakeInstructionDescriptor(12, SpvOpBranchConditional, 0), 100)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   auto split = TransformationSplitBlock(
       MakeInstructionDescriptor(14, SpvOpSelectionMerge, 0), 100);
-  ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
-  split.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
+  split.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_split = R"(
@@ -541,19 +550,22 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Illegal to split between the merge and the conditional branch.
   ASSERT_FALSE(TransformationSplitBlock(
                    MakeInstructionDescriptor(9, SpvOpSwitch, 0), 100)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSplitBlock(
                    MakeInstructionDescriptor(15, SpvOpSwitch, 0), 100)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   auto split = TransformationSplitBlock(
       MakeInstructionDescriptor(9, SpvOpSelectionMerge, 0), 100);
-  ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
-  split.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
+  split.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_split = R"(
@@ -674,18 +686,21 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // We cannot split before OpPhi instructions, since the number of incoming
   // blocks may not appropriately match after splitting.
   ASSERT_FALSE(
       TransformationSplitBlock(MakeInstructionDescriptor(26, SpvOpPhi, 0), 100)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) {
@@ -726,16 +741,19 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   ASSERT_TRUE(
       TransformationSplitBlock(MakeInstructionDescriptor(21, SpvOpPhi, 0), 100)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // An equivalent transformation to the above, just described with respect to a
   // different base instruction.
   auto split =
       TransformationSplitBlock(MakeInstructionDescriptor(20, SpvOpPhi, 0), 100);
-  ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
-  split.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
+  split.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_split = R"(
@@ -805,18 +823,21 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Record the fact that block 8 is dead.
-  fact_manager.AddFactBlockIsDead(8);
+  transformation_context.GetFactManager()->AddFactBlockIsDead(8);
 
   auto split = TransformationSplitBlock(
       MakeInstructionDescriptor(8, SpvOpBranch, 0), 100);
-  ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
-  split.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
+  split.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(fact_manager.BlockIsDead(8));
-  ASSERT_TRUE(fact_manager.BlockIsDead(100));
+  ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(8));
+  ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(100));
 
   std::string after_split = R"(
                OpCapability Shader
@@ -845,6 +866,62 @@
   ASSERT_TRUE(IsEqual(env, after_split, context.get()));
 }
 
+TEST(TransformationSplitBlockTest, DoNotSplitUseOfOpSampledImage) {
+  // This checks that we cannot split the definition of an OpSampledImage
+  // from its use.
+  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);
+
+  auto split = TransformationSplitBlock(
+      MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0), 500);
+  ASSERT_FALSE(split.IsApplicable(context.get(), transformation_context));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_store_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_store_test.cpp
index 3fb9b61..07d222f 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_store_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_store_test.cpp
@@ -94,16 +94,26 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(27);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(11);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(46);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(16);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(52);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(81);
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(82);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      27);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      11);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      46);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      16);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      52);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      81);
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+      82);
 
-  fact_manager.AddFactBlockIsDead(36);
+  transformation_context.GetFactManager()->AddFactBlockIsDead(36);
 
   // Variables with pointee types:
   //  52 - ptr_to(7)
@@ -139,90 +149,91 @@
   // Bad: attempt to store to 11 from outside its function
   ASSERT_FALSE(TransformationStore(
                    11, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer is not available
   ASSERT_FALSE(TransformationStore(
                    81, 80, MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: attempt to insert before OpVariable
   ASSERT_FALSE(TransformationStore(
                    52, 24, MakeInstructionDescriptor(27, SpvOpVariable, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id does not exist
   ASSERT_FALSE(TransformationStore(
                    1000, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id exists but does not have a type
   ASSERT_FALSE(TransformationStore(
                    5, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id exists and has a type, but is not a pointer
   ASSERT_FALSE(TransformationStore(
                    24, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: attempt to store to a null pointer
   ASSERT_FALSE(TransformationStore(
                    60, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: attempt to store to an undefined pointer
   ASSERT_FALSE(TransformationStore(
                    61, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: %82 is not available at the program point
   ASSERT_FALSE(
       TransformationStore(82, 80, MakeInstructionDescriptor(37, SpvOpReturn, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: value id does not exist
   ASSERT_FALSE(TransformationStore(
                    27, 1000, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: value id exists but does not have a type
   ASSERT_FALSE(TransformationStore(
                    27, 15, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: value id exists but has the wrong type
   ASSERT_FALSE(TransformationStore(
                    27, 14, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: attempt to store to read-only variable
   ASSERT_FALSE(TransformationStore(
                    92, 93, MakeInstructionDescriptor(40, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: value is not available
   ASSERT_FALSE(TransformationStore(
                    27, 95, MakeInstructionDescriptor(40, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: variable being stored to does not have an irrelevant pointee value,
   // and the store is not in a dead block.
   ASSERT_FALSE(TransformationStore(
                    20, 95, MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // The described instruction does not exist.
   ASSERT_FALSE(TransformationStore(
                    27, 80, MakeInstructionDescriptor(1000, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   {
     // Store to irrelevant variable from dead block.
     TransformationStore transformation(
         27, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
@@ -230,8 +241,9 @@
     // Store to irrelevant variable from live block.
     TransformationStore transformation(
         11, 95, MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
@@ -239,8 +251,9 @@
     // Store to irrelevant variable from live block.
     TransformationStore transformation(
         46, 80, MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
@@ -248,8 +261,9 @@
     // Store to irrelevant variable from live block.
     TransformationStore transformation(
         16, 21, MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
@@ -257,8 +271,9 @@
     // Store to non-irrelevant variable from dead block.
     TransformationStore transformation(
         53, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
   }
 
@@ -336,6 +351,70 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationStoreTest, DoNotAllowStoresToReadOnlyMemory) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpMemberDecorate %10 0 Offset 0
+               OpMemberDecorate %10 1 Offset 4
+               OpDecorate %10 Block
+               OpMemberDecorate %23 0 Offset 0
+               OpDecorate %23 Block
+               OpDecorate %25 DescriptorSet 0
+               OpDecorate %25 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpTypeFloat 32
+         %10 = OpTypeStruct %6 %9
+         %11 = OpTypePointer PushConstant %10
+         %12 = OpVariable %11 PushConstant
+         %13 = OpConstant %6 0
+         %14 = OpTypePointer PushConstant %6
+         %17 = OpConstant %6 1
+         %18 = OpTypePointer PushConstant %9
+         %23 = OpTypeStruct %9
+         %24 = OpTypePointer UniformConstant %23
+         %25 = OpVariable %24 UniformConstant
+         %26 = OpTypePointer UniformConstant %9
+         %50 = OpConstant %9 0
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %15 = OpAccessChain %14 %12 %13
+         %19 = OpAccessChain %18 %12 %17
+         %27 = OpAccessChain %26 %25 %13
+               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.AddFactBlockIsDead(5);
+
+  ASSERT_FALSE(
+      TransformationStore(15, 13, MakeInstructionDescriptor(27, SpvOpReturn, 0))
+          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationStore(19, 50, MakeInstructionDescriptor(27, SpvOpReturn, 0))
+          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationStore(27, 50, MakeInstructionDescriptor(27, SpvOpReturn, 0))
+          .IsApplicable(context.get(), transformation_context));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_swap_commutable_operands_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_swap_commutable_operands_test.cpp
index f0591cf..c213dfe 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_swap_commutable_operands_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_swap_commutable_operands_test.cpp
@@ -111,113 +111,140 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  FactManager factManager;
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Tests existing commutative instructions
   auto instructionDescriptor = MakeInstructionDescriptor(22, SpvOpIAdd, 0);
   auto transformation =
       TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(28, SpvOpIMul, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(42, SpvOpFAdd, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(48, SpvOpFMul, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(66, SpvOpDot, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests existing non-commutative instructions
   instructionDescriptor = MakeInstructionDescriptor(1, SpvOpExtInstImport, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(5, SpvOpLabel, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(8, SpvOpConstant, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(11, SpvOpVariable, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor =
       MakeInstructionDescriptor(14, SpvOpConstantComposite, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests the base instruction id not existing
   instructionDescriptor = MakeInstructionDescriptor(67, SpvOpIAddCarry, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(68, SpvOpIEqual, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(69, SpvOpINotEqual, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(70, SpvOpFOrdEqual, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(71, SpvOpPtrEqual, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests there being no instruction with the desired opcode after the base
   // instruction id
   instructionDescriptor = MakeInstructionDescriptor(24, SpvOpIAdd, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(38, SpvOpIMul, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(45, SpvOpFAdd, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(66, SpvOpFMul, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests there being an instruction with the desired opcode after the base
   // instruction id, but the skip count associated with the instruction
   // descriptor being so high.
   instructionDescriptor = MakeInstructionDescriptor(11, SpvOpIAdd, 100);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(16, SpvOpIMul, 100);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(23, SpvOpFAdd, 100);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(32, SpvOpFMul, 100);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(37, SpvOpDot, 100);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationSwapCommutableOperandsTest, ApplyTest) {
@@ -311,28 +338,31 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  FactManager factManager;
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto instructionDescriptor = MakeInstructionDescriptor(22, SpvOpIAdd, 0);
   auto transformation =
       TransformationSwapCommutableOperands(instructionDescriptor);
-  transformation.Apply(context.get(), &factManager);
+  transformation.Apply(context.get(), &transformation_context);
 
   instructionDescriptor = MakeInstructionDescriptor(28, SpvOpIMul, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  transformation.Apply(context.get(), &factManager);
+  transformation.Apply(context.get(), &transformation_context);
 
   instructionDescriptor = MakeInstructionDescriptor(42, SpvOpFAdd, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  transformation.Apply(context.get(), &factManager);
+  transformation.Apply(context.get(), &transformation_context);
 
   instructionDescriptor = MakeInstructionDescriptor(48, SpvOpFMul, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  transformation.Apply(context.get(), &factManager);
+  transformation.Apply(context.get(), &transformation_context);
 
   instructionDescriptor = MakeInstructionDescriptor(66, SpvOpDot, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
-  transformation.Apply(context.get(), &factManager);
+  transformation.Apply(context.get(), &transformation_context);
 
   std::string variantShader = R"(
                OpCapability Shader
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp
index 98e0a64..b20f59e 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp
@@ -111,78 +111,93 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  FactManager factManager;
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Tests existing access chain instructions
   auto instructionDescriptor =
       MakeInstructionDescriptor(18, SpvOpAccessChain, 0);
   auto transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor =
       MakeInstructionDescriptor(20, SpvOpInBoundsAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(24, SpvOpAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor =
       MakeInstructionDescriptor(26, SpvOpInBoundsAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests existing non-access chain instructions
   instructionDescriptor = MakeInstructionDescriptor(1, SpvOpExtInstImport, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(5, SpvOpLabel, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor =
       MakeInstructionDescriptor(14, SpvOpConstantComposite, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests the base instruction id not existing
   instructionDescriptor = MakeInstructionDescriptor(67, SpvOpAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor = MakeInstructionDescriptor(68, SpvOpAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor =
       MakeInstructionDescriptor(69, SpvOpInBoundsAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests there being no instruction with the desired opcode after the base
   // instruction id
   instructionDescriptor = MakeInstructionDescriptor(65, SpvOpAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor =
       MakeInstructionDescriptor(66, SpvOpInBoundsAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests there being an instruction with the desired opcode after the base
   // instruction id, but the skip count associated with the instruction
@@ -190,13 +205,15 @@
   instructionDescriptor = MakeInstructionDescriptor(11, SpvOpAccessChain, 100);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor =
       MakeInstructionDescriptor(16, SpvOpInBoundsAccessChain, 100);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationToggleAccessChainInstructionTest, ApplyTest) {
@@ -290,35 +307,38 @@
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  FactManager factManager;
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   auto instructionDescriptor =
       MakeInstructionDescriptor(18, SpvOpAccessChain, 0);
   auto transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  transformation.Apply(context.get(), &factManager);
+  transformation.Apply(context.get(), &transformation_context);
 
   instructionDescriptor =
       MakeInstructionDescriptor(20, SpvOpInBoundsAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  transformation.Apply(context.get(), &factManager);
+  transformation.Apply(context.get(), &transformation_context);
 
   instructionDescriptor = MakeInstructionDescriptor(24, SpvOpAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  transformation.Apply(context.get(), &factManager);
+  transformation.Apply(context.get(), &transformation_context);
 
   instructionDescriptor =
       MakeInstructionDescriptor(26, SpvOpInBoundsAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  transformation.Apply(context.get(), &factManager);
+  transformation.Apply(context.get(), &transformation_context);
 
   instructionDescriptor = MakeInstructionDescriptor(38, SpvOpAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
-  transformation.Apply(context.get(), &factManager);
+  transformation.Apply(context.get(), &transformation_context);
 
   std::string variantShader = R"(
                OpCapability Shader
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 385c38b..a29c511 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
@@ -86,249 +86,259 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
-                                  MakeDataDescriptor(12, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}),
-                                  MakeDataDescriptor(12, {1}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(10, {}), MakeDataDescriptor(12, {0}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(11, {}), MakeDataDescriptor(12, {1}), context.get());
 
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
-                                  MakeDataDescriptor(16, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}),
-                                  MakeDataDescriptor(16, {1}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
-                                  MakeDataDescriptor(16, {2}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(10, {}), MakeDataDescriptor(16, {0}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(11, {}), MakeDataDescriptor(16, {1}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(10, {}), MakeDataDescriptor(16, {2}), context.get());
 
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
-                                  MakeDataDescriptor(20, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}),
-                                  MakeDataDescriptor(20, {1}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
-                                  MakeDataDescriptor(20, {2}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}),
-                                  MakeDataDescriptor(20, {3}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(10, {}), MakeDataDescriptor(20, {0}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(11, {}), MakeDataDescriptor(20, {1}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(10, {}), MakeDataDescriptor(20, {2}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(11, {}), MakeDataDescriptor(20, {3}), context.get());
 
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
-                                  MakeDataDescriptor(27, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}),
-                                  MakeDataDescriptor(27, {1}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(25, {}), MakeDataDescriptor(27, {0}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(26, {}), MakeDataDescriptor(27, {1}), context.get());
 
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
-                                  MakeDataDescriptor(31, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}),
-                                  MakeDataDescriptor(31, {1}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
-                                  MakeDataDescriptor(31, {2}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(25, {}), MakeDataDescriptor(31, {0}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(26, {}), MakeDataDescriptor(31, {1}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(25, {}), MakeDataDescriptor(31, {2}), context.get());
 
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
-                                  MakeDataDescriptor(35, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}),
-                                  MakeDataDescriptor(35, {1}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
-                                  MakeDataDescriptor(35, {2}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}),
-                                  MakeDataDescriptor(35, {3}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(25, {}), MakeDataDescriptor(35, {0}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(26, {}), MakeDataDescriptor(35, {1}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(25, {}), MakeDataDescriptor(35, {2}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(26, {}), MakeDataDescriptor(35, {3}), context.get());
 
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
-                                  MakeDataDescriptor(42, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}),
-                                  MakeDataDescriptor(42, {1}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(40, {}), MakeDataDescriptor(42, {0}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(41, {}), MakeDataDescriptor(42, {1}), context.get());
 
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
-                                  MakeDataDescriptor(46, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}),
-                                  MakeDataDescriptor(46, {1}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
-                                  MakeDataDescriptor(46, {2}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(40, {}), MakeDataDescriptor(46, {0}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(41, {}), MakeDataDescriptor(46, {1}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(40, {}), MakeDataDescriptor(46, {2}), context.get());
 
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
-                                  MakeDataDescriptor(50, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}),
-                                  MakeDataDescriptor(50, {1}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
-                                  MakeDataDescriptor(50, {2}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}),
-                                  MakeDataDescriptor(50, {3}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(40, {}), MakeDataDescriptor(50, {0}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(41, {}), MakeDataDescriptor(50, {1}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(40, {}), MakeDataDescriptor(50, {2}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(41, {}), MakeDataDescriptor(50, {3}), context.get());
 
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}),
-                                  MakeDataDescriptor(61, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(56, {}),
-                                  MakeDataDescriptor(61, {1}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}),
-                                  MakeDataDescriptor(61, {2}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(55, {}), MakeDataDescriptor(61, {0}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(56, {}), MakeDataDescriptor(61, {1}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(55, {}), MakeDataDescriptor(61, {2}), context.get());
 
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}),
-                                  MakeDataDescriptor(65, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(56, {}),
-                                  MakeDataDescriptor(65, {1}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}),
-                                  MakeDataDescriptor(65, {2}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(56, {}),
-                                  MakeDataDescriptor(65, {3}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(55, {}), MakeDataDescriptor(65, {0}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(56, {}), MakeDataDescriptor(65, {1}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(55, {}), MakeDataDescriptor(65, {2}), context.get());
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(56, {}), MakeDataDescriptor(65, {3}), context.get());
 
   // %103 does not dominate the return instruction.
   ASSERT_FALSE(TransformationVectorShuffle(
                    MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 103, 65,
                    {3, 5, 7})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Illegal to shuffle a bvec2 and a vec3
   ASSERT_FALSE(TransformationVectorShuffle(
                    MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 112, 61,
                    {0, 2, 4})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Illegal to shuffle an ivec2 and a uvec4
   ASSERT_FALSE(TransformationVectorShuffle(
                    MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 27, 50,
                    {1, 3, 5})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Vector 1 does not exist
   ASSERT_FALSE(TransformationVectorShuffle(
                    MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 300, 50,
                    {1, 3, 5})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Vector 2 does not exist
   ASSERT_FALSE(TransformationVectorShuffle(
                    MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 27, 300,
                    {1, 3, 5})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Index out of range
   ASSERT_FALSE(
       TransformationVectorShuffle(
           MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {0, 20})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Too many indices
   ASSERT_FALSE(TransformationVectorShuffle(
                    MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112,
                    {0, 1, 0, 1, 0, 1, 0, 1})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Too few indices
   ASSERT_FALSE(
       TransformationVectorShuffle(
           MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Too few indices again
   ASSERT_FALSE(
       TransformationVectorShuffle(
           MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {0})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Indices define unknown type: we do not have vec2
   ASSERT_FALSE(
       TransformationVectorShuffle(
           MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 65, 65, {0, 1})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // The instruction to insert before does not exist
   ASSERT_FALSE(TransformationVectorShuffle(
                    MakeInstructionDescriptor(100, SpvOpCompositeConstruct, 1),
                    201, 20, 12, {0xFFFFFFFF, 3, 5})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // The 'fresh' id is already in use
   ASSERT_FALSE(
       TransformationVectorShuffle(
           MakeInstructionDescriptor(100, SpvOpReturn, 0), 12, 12, 112, {})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   protobufs::DataDescriptor temp_dd;
 
   TransformationVectorShuffle transformation1(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {1, 0});
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   temp_dd = MakeDataDescriptor(200, {0});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(11, {}), temp_dd));
   temp_dd = MakeDataDescriptor(200, {1});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(10, {}), temp_dd));
 
   TransformationVectorShuffle transformation2(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 201, 20, 12,
       {0xFFFFFFFF, 3, 5});
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   temp_dd = MakeDataDescriptor(201, {1});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(11, {}), temp_dd));
   temp_dd = MakeDataDescriptor(201, {2});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(11, {}), temp_dd));
 
   TransformationVectorShuffle transformation3(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 202, 27, 35, {5, 4, 1});
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
   temp_dd = MakeDataDescriptor(202, {0});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(26, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(26, {}), temp_dd));
   temp_dd = MakeDataDescriptor(202, {1});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(25, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(25, {}), temp_dd));
   temp_dd = MakeDataDescriptor(202, {2});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(26, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(26, {}), temp_dd));
 
   TransformationVectorShuffle transformation4(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 203, 42, 46, {0, 1});
-  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation4.IsApplicable(context.get(), transformation_context));
+  transformation4.Apply(context.get(), &transformation_context);
   temp_dd = MakeDataDescriptor(203, {0});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(40, {}), temp_dd));
   temp_dd = MakeDataDescriptor(203, {1});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(41, {}), temp_dd));
 
   TransformationVectorShuffle transformation5(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 204, 42, 46, {2, 3, 4});
-  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
-  transformation5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation5.IsApplicable(context.get(), transformation_context));
+  transformation5.Apply(context.get(), &transformation_context);
   temp_dd = MakeDataDescriptor(204, {0});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(40, {}), temp_dd));
   temp_dd = MakeDataDescriptor(204, {1});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(41, {}), temp_dd));
   temp_dd = MakeDataDescriptor(204, {2});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(40, {}), temp_dd));
 
   TransformationVectorShuffle transformation6(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 205, 42, 42,
       {0, 1, 2, 3});
-  ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
-  transformation6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation6.IsApplicable(context.get(), transformation_context));
+  transformation6.Apply(context.get(), &transformation_context);
   temp_dd = MakeDataDescriptor(205, {0});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(40, {}), temp_dd));
   temp_dd = MakeDataDescriptor(205, {1});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(41, {}), temp_dd));
   temp_dd = MakeDataDescriptor(205, {2});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(40, {}), temp_dd));
   temp_dd = MakeDataDescriptor(205, {3});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(41, {}), temp_dd));
 
   // swizzle vec4 from vec4 and vec4 using some undefs
   TransformationVectorShuffle transformation7(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 206, 65, 65,
       {0xFFFFFFFF, 3, 6, 0xFFFFFFFF});
-  ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager));
-  transformation7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation7.IsApplicable(context.get(), transformation_context));
+  transformation7.Apply(context.get(), &transformation_context);
   temp_dd = MakeDataDescriptor(206, {1});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(56, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(56, {}), temp_dd));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -479,52 +489,55 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Cannot insert before the OpVariables of a function.
   ASSERT_FALSE(
       TransformationVectorShuffle(
           MakeInstructionDescriptor(101, SpvOpVariable, 0), 200, 14, 14, {0, 1})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationVectorShuffle(
           MakeInstructionDescriptor(101, SpvOpVariable, 1), 200, 14, 14, {1, 2})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationVectorShuffle(
           MakeInstructionDescriptor(102, SpvOpVariable, 0), 200, 14, 14, {1, 2})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // OK to insert right after the OpVariables.
   ASSERT_FALSE(
       TransformationVectorShuffle(
           MakeInstructionDescriptor(102, SpvOpBranch, 1), 200, 14, 14, {1, 1})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert before the OpPhis of a block.
   ASSERT_FALSE(
       TransformationVectorShuffle(MakeInstructionDescriptor(60, SpvOpPhi, 0),
                                   200, 14, 14, {2, 0})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationVectorShuffle(MakeInstructionDescriptor(59, SpvOpPhi, 0),
                                   200, 14, 14, {3, 0})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // OK to insert after the OpPhis.
   ASSERT_TRUE(TransformationVectorShuffle(
                   MakeInstructionDescriptor(59, SpvOpAccessChain, 0), 200, 14,
                   14, {3, 4})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert before OpLoopMerge
   ASSERT_FALSE(TransformationVectorShuffle(
                    MakeInstructionDescriptor(33, SpvOpBranchConditional, 0),
                    200, 14, 14, {3})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert before OpSelectionMerge
   ASSERT_FALSE(TransformationVectorShuffle(
                    MakeInstructionDescriptor(21, SpvOpBranchConditional, 0),
                    200, 14, 14, {2})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace
diff --git a/third_party/SPIRV-Tools/test/opt/CMakeLists.txt b/third_party/SPIRV-Tools/test/opt/CMakeLists.txt
index 3954338..21a6529 100644
--- a/third_party/SPIRV-Tools/test/opt/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/test/opt/CMakeLists.txt
@@ -33,6 +33,7 @@
        dead_branch_elim_test.cpp
        dead_insert_elim_test.cpp
        dead_variable_elim_test.cpp
+       debug_info_manager_test.cpp
        decompose_initialized_variables_test.cpp
        decoration_manager_test.cpp
        def_use_test.cpp
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
new file mode 100644
index 0000000..f19737f
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/opt/debug_info_manager_test.cpp
@@ -0,0 +1,437 @@
+// 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/opt/debug_info_manager.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "effcee/effcee.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "source/opt/build_module.h"
+#include "source/opt/instruction.h"
+#include "spirv-tools/libspirv.hpp"
+
+// Constants for OpenCL.DebugInfo.100 extension instructions.
+
+static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
+static const uint32_t kDebugInlinedAtOperandLineIndex = 4;
+static const uint32_t kDebugInlinedAtOperandScopeIndex = 5;
+static const uint32_t kDebugInlinedAtOperandInlinedIndex = 6;
+
+namespace spvtools {
+namespace opt {
+namespace analysis {
+namespace {
+
+TEST(DebugInfoManager, GetDebugInlinedAt) {
+  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
+        %100 = OpExtInst %void %1 DebugInlinedAt 7 %22
+       %main = OpFunction %void None %27
+         %28 = OpLabel
+         %31 = OpLoad %float %in_var_COLOR
+               OpStore %100 %31
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  DebugInfoManager manager(context.get());
+
+  EXPECT_EQ(manager.GetDebugInlinedAt(150), nullptr);
+  EXPECT_EQ(manager.GetDebugInlinedAt(31), nullptr);
+  EXPECT_EQ(manager.GetDebugInlinedAt(22), nullptr);
+
+  auto* inst = manager.GetDebugInlinedAt(100);
+  EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex), 7);
+  EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex), 22);
+}
+
+TEST(DebugInfoManager, CreateDebugInlinedAt) {
+  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
+        %100 = OpExtInst %void %1 DebugInlinedAt 7 %22
+       %main = OpFunction %void None %27
+         %28 = OpLabel
+         %31 = OpLoad %float %in_var_COLOR
+               OpStore %100 %31
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  DebugScope scope(22U, 0U);
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  DebugInfoManager manager(context.get());
+
+  uint32_t inlined_at_id = manager.CreateDebugInlinedAt(nullptr, scope);
+  auto* inlined_at = manager.GetDebugInlinedAt(inlined_at_id);
+  EXPECT_NE(inlined_at, nullptr);
+  EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex),
+            1);
+  EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex),
+            22);
+  EXPECT_EQ(inlined_at->NumOperands(), kDebugInlinedAtOperandScopeIndex + 1);
+
+  const uint32_t line_number = 77U;
+  Instruction line(context.get(), SpvOpLine);
+  line.SetInOperands({
+      {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {5U}},
+      {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}},
+      {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {0U}},
+  });
+
+  inlined_at_id = manager.CreateDebugInlinedAt(&line, scope);
+  inlined_at = manager.GetDebugInlinedAt(inlined_at_id);
+  EXPECT_NE(inlined_at, nullptr);
+  EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex),
+            line_number);
+  EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex),
+            22);
+  EXPECT_EQ(inlined_at->NumOperands(), kDebugInlinedAtOperandScopeIndex + 1);
+
+  scope.SetInlinedAt(100U);
+  inlined_at_id = manager.CreateDebugInlinedAt(&line, scope);
+  inlined_at = manager.GetDebugInlinedAt(inlined_at_id);
+  EXPECT_NE(inlined_at, nullptr);
+  EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex),
+            line_number);
+  EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex),
+            22);
+  EXPECT_EQ(inlined_at->NumOperands(), kDebugInlinedAtOperandInlinedIndex + 1);
+  EXPECT_EQ(
+      inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandInlinedIndex),
+      100U);
+}
+
+TEST(DebugInfoManager, GetDebugInfoNone) {
+  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
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  DebugInfoManager manager(context.get());
+
+  Instruction* debug_info_none_inst = manager.GetDebugInfoNone();
+  EXPECT_NE(debug_info_none_inst, nullptr);
+  EXPECT_EQ(debug_info_none_inst->GetOpenCL100DebugOpcode(),
+            OpenCLDebugInfo100DebugInfoNone);
+  EXPECT_EQ(debug_info_none_inst->PreviousNode(), nullptr);
+}
+
+TEST(DebugInfoManager, CreateDebugInfoNone) {
+  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
+         %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
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  DebugInfoManager manager(context.get());
+
+  Instruction* debug_info_none_inst = manager.GetDebugInfoNone();
+  EXPECT_NE(debug_info_none_inst, nullptr);
+  EXPECT_EQ(debug_info_none_inst->GetOpenCL100DebugOpcode(),
+            OpenCLDebugInfo100DebugInfoNone);
+  EXPECT_EQ(debug_info_none_inst->PreviousNode(), nullptr);
+}
+
+TEST(DebugInfoManager, GetDebugFunction) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %200 "200" %in_var_COLOR
+               OpExecutionMode %200 OriginUpperLeft
+          %5 = OpString "ps.hlsl"
+         %14 = OpString "#line 1 \"ps.hlsl\"
+void 200(float in_var_color : COLOR) {
+  float color = in_var_color;
+}
+"
+         %17 = OpString "float"
+         %21 = OpString "200"
+         %24 = OpString "color"
+               OpName %in_var_COLOR "in.var.COLOR"
+               OpName %200 "200"
+               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 %200
+         %25 = OpExtInst %void %1 DebugLocalVariable %24 %18 %15 1 20 %22 FlagIsLocal 0
+       %200 = 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
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  DebugInfoManager manager(context.get());
+
+  EXPECT_EQ(manager.GetDebugFunction(100), nullptr);
+  EXPECT_EQ(manager.GetDebugFunction(150), nullptr);
+
+  Instruction* dbg_fn = manager.GetDebugFunction(200);
+
+  EXPECT_EQ(dbg_fn->GetOpenCL100DebugOpcode(), OpenCLDebugInfo100DebugFunction);
+  EXPECT_EQ(dbg_fn->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex),
+            200);
+}
+
+TEST(DebugInfoManager, CloneDebugInlinedAt) {
+  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
+        %100 = OpExtInst %void %1 DebugInlinedAt 7 %22
+       %main = OpFunction %void None %27
+         %28 = OpLabel
+         %31 = OpLoad %float %in_var_COLOR
+               OpStore %100 %31
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  DebugInfoManager manager(context.get());
+
+  EXPECT_EQ(manager.CloneDebugInlinedAt(150), nullptr);
+  EXPECT_EQ(manager.CloneDebugInlinedAt(22), nullptr);
+
+  auto* inst = manager.CloneDebugInlinedAt(100);
+  EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex), 7);
+  EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex), 22);
+  EXPECT_EQ(inst->NumOperands(), kDebugInlinedAtOperandScopeIndex + 1);
+
+  Instruction* before_100 = nullptr;
+  for (auto it = context->module()->ext_inst_debuginfo_begin();
+       it != context->module()->ext_inst_debuginfo_end(); ++it) {
+    if (it->result_id() == 100) break;
+    before_100 = &*it;
+  }
+  EXPECT_NE(inst, before_100);
+
+  inst = manager.CloneDebugInlinedAt(100, manager.GetDebugInlinedAt(100));
+  EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex), 7);
+  EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex), 22);
+  EXPECT_EQ(inst->NumOperands(), kDebugInlinedAtOperandScopeIndex + 1);
+
+  before_100 = nullptr;
+  for (auto it = context->module()->ext_inst_debuginfo_begin();
+       it != context->module()->ext_inst_debuginfo_end(); ++it) {
+    if (it->result_id() == 100) break;
+    before_100 = &*it;
+  }
+  EXPECT_EQ(inst, before_100);
+}
+
+}  // namespace
+}  // namespace analysis
+}  // namespace opt
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/eliminate_dead_functions_test.cpp b/third_party/SPIRV-Tools/test/opt/eliminate_dead_functions_test.cpp
index 0a3d490..2f8fa9a 100644
--- a/third_party/SPIRV-Tools/test/opt/eliminate_dead_functions_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/eliminate_dead_functions_test.cpp
@@ -204,6 +204,146 @@
                                                     /* skip_nop = */ true);
 }
 
+TEST_F(EliminateDeadFunctionsBasicTest, DebugRemoveFunctionFromDebugFunction) {
+  // We want to remove id of OpFunction from DebugFunction.
+  const std::string text = R"(OpCapability Shader
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main" %3 %4
+OpExecutionMode %2 OriginUpperLeft
+%5 = OpString "ps.hlsl"
+OpSource HLSL 600 %5 "float4 foo() {
+  return 1;
+}
+float4 main(float4 color : COLOR) : SV_TARGET {
+  return foo() + color;
+}
+"
+%6 = OpString "float"
+%7 = OpString "main"
+%8 = OpString "foo"
+; CHECK: [[foo:%\d+]] = OpString "foo"
+OpDecorate %3 Location 0
+OpDecorate %4 Location 0
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%float = OpTypeFloat 32
+%float_1 = OpConstant %float 1
+%v4float = OpTypeVector %float 4
+%14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%18 = OpTypeFunction %void
+%19 = OpTypeFunction %v4float
+%3 = OpVariable %_ptr_Input_v4float Input
+%4 = OpVariable %_ptr_Output_v4float Output
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+; CHECK: [[info_none:%\d+]] = OpExtInst %void %1 DebugInfoNone
+%20 = OpExtInst %void %1 DebugSource %5
+%21 = OpExtInst %void %1 DebugCompilationUnit 1 4 %20 HLSL
+%22 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 Float
+%23 = OpExtInst %void %1 DebugTypeVector %22 4
+%24 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23 %23
+%25 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23
+%26 = OpExtInst %void %1 DebugFunction %7 %24 %20 4 1 %21 %7 FlagIsProtected|FlagIsPrivate 4 %2
+%27 = OpExtInst %void %1 DebugFunction %8 %25 %20 1 1 %21 %8 FlagIsProtected|FlagIsPrivate 1 %28
+; CHECK: {{%\d+}} = OpExtInst %void %1 DebugFunction [[foo]] {{%\d+}} {{%\d+}} 1 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 [[info_none]]
+%29 = OpExtInst %void %1 DebugLexicalBlock %20 1 14 %27
+%40 = OpExtInst %void %1 DebugInlinedAt 4 %26
+%2 = OpFunction %void None %18
+%30 = OpLabel
+%39 = OpVariable %_ptr_Function_v4float Function
+%41 = OpExtInst %void %1 DebugScope %27 %40
+OpStore %39 %14
+%32 = OpLoad %v4float %39
+%42 = OpExtInst %void %1 DebugScope %26
+%33 = OpLoad %v4float %3
+%34 = OpFAdd %v4float %32 %33
+OpStore %4 %34
+%43 = OpExtInst %void %1 DebugNoScope
+OpReturn
+OpFunctionEnd
+%28 = OpFunction %v4float None %19
+%36 = OpLabel
+OpReturnValue %14
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, false);
+}
+
+TEST_F(EliminateDeadFunctionsBasicTest,
+       DebugRemoveFunctionUsingExistingDebugInfoNone) {
+  // We want to remove id of OpFunction from DebugFunction.
+  const std::string text = R"(OpCapability Shader
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main" %3 %4
+OpExecutionMode %2 OriginUpperLeft
+%5 = OpString "ps.hlsl"
+OpSource HLSL 600 %5 "float4 foo() {
+  return 1;
+}
+float4 main(float4 color : COLOR) : SV_TARGET {
+  return foo() + color;
+}
+"
+%6 = OpString "float"
+%7 = OpString "main"
+%8 = OpString "foo"
+; CHECK: [[foo:%\d+]] = OpString "foo"
+OpDecorate %3 Location 0
+OpDecorate %4 Location 0
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%float = OpTypeFloat 32
+%float_1 = OpConstant %float 1
+%v4float = OpTypeVector %float 4
+%14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%18 = OpTypeFunction %void
+%19 = OpTypeFunction %v4float
+%3 = OpVariable %_ptr_Input_v4float Input
+%4 = OpVariable %_ptr_Output_v4float Output
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+; CHECK: [[info_none:%\d+]] = OpExtInst %void %1 DebugInfoNone
+%20 = OpExtInst %void %1 DebugSource %5
+%21 = OpExtInst %void %1 DebugCompilationUnit 1 4 %20 HLSL
+%22 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 Float
+%23 = OpExtInst %void %1 DebugTypeVector %22 4
+%24 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23 %23
+%25 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23
+%26 = OpExtInst %void %1 DebugFunction %7 %24 %20 4 1 %21 %7 FlagIsProtected|FlagIsPrivate 4 %2
+%27 = OpExtInst %void %1 DebugFunction %8 %25 %20 1 1 %21 %8 FlagIsProtected|FlagIsPrivate 1 %28
+; CHECK: {{%\d+}} = OpExtInst %void %1 DebugFunction [[foo]] {{%\d+}} {{%\d+}} 1 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 [[info_none]]
+%29 = OpExtInst %void %1 DebugLexicalBlock %20 1 14 %27
+%35 = OpExtInst %void %1 DebugInfoNone
+%40 = OpExtInst %void %1 DebugInlinedAt 4 %26
+%2 = OpFunction %void None %18
+%30 = OpLabel
+%39 = OpVariable %_ptr_Function_v4float Function
+%41 = OpExtInst %void %1 DebugScope %27 %40
+OpStore %39 %14
+%32 = OpLoad %v4float %39
+%42 = OpExtInst %void %1 DebugScope %26
+%33 = OpLoad %v4float %3
+%34 = OpFAdd %v4float %32 %33
+OpStore %4 %34
+%43 = OpExtInst %void %1 DebugNoScope
+OpReturn
+OpFunctionEnd
+%28 = OpFunction %v4float None %19
+%36 = OpLabel
+OpReturnValue %14
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, false);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/eliminate_dead_member_test.cpp b/third_party/SPIRV-Tools/test/opt/eliminate_dead_member_test.cpp
index b6925d7..a9b0f28 100644
--- a/third_party/SPIRV-Tools/test/opt/eliminate_dead_member_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/eliminate_dead_member_test.cpp
@@ -1085,4 +1085,103 @@
   EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result));
 }
 
+TEST_F(EliminateDeadMemberTest, UpdateSpecConstOpExtract) {
+  // Test that an extract in an OpSpecConstantOp is correctly updated.
+  const std::string text = R"(
+; CHECK: OpName
+; CHECK-NEXT: OpMemberName %type__Globals 0 "y"
+; CHECK-NOT: OpMemberName
+; CHECK: OpDecorate [[spec_const:%\w+]] SpecId 1
+; CHECK: OpMemberDecorate %type__Globals 0 Offset 4
+; CHECK: %type__Globals = OpTypeStruct %uint
+; CHECK: [[struct:%\w+]] = OpSpecConstantComposite %type__Globals [[spec_const]]
+; CHECK: OpSpecConstantOp %uint CompositeExtract [[struct]] 0
+               OpCapability Shader
+               OpCapability Addresses
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource HLSL 600
+               OpName %type__Globals "type.$Globals"
+               OpMemberName %type__Globals 0 "x"
+               OpMemberName %type__Globals 1 "y"
+               OpMemberName %type__Globals 2 "z"
+               OpName %main "main"
+               OpDecorate %c_0 SpecId 0
+               OpDecorate %c_1 SpecId 1
+               OpDecorate %c_2 SpecId 2
+               OpMemberDecorate %type__Globals 0 Offset 0
+               OpMemberDecorate %type__Globals 1 Offset 4
+               OpMemberDecorate %type__Globals 2 Offset 16
+       %uint = OpTypeInt 32 0
+        %c_0 = OpSpecConstant %uint 0
+        %c_1 = OpSpecConstant %uint 1
+        %c_2 = OpSpecConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+%type__Globals = OpTypeStruct %uint %uint %uint
+%spec_const_global = OpSpecConstantComposite %type__Globals %c_0 %c_1 %c_2
+%extract = OpSpecConstantOp %uint CompositeExtract %spec_const_global 1
+       %void = OpTypeVoid
+         %14 = OpTypeFunction %void
+       %main = OpFunction %void None %14
+         %16 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true);
+}
+
+TEST_F(EliminateDeadMemberTest, UpdateSpecConstOpInsert) {
+  // Test that an insert in an OpSpecConstantOp is correctly updated.
+  const std::string text = R"(
+; CHECK: OpName
+; CHECK-NEXT: OpMemberName %type__Globals 0 "y"
+; CHECK-NOT: OpMemberName
+; CHECK: OpDecorate [[spec_const:%\w+]] SpecId 1
+; CHECK: OpMemberDecorate %type__Globals 0 Offset 4
+; CHECK: %type__Globals = OpTypeStruct %uint
+; CHECK: [[struct:%\w+]] = OpSpecConstantComposite %type__Globals [[spec_const]]
+; CHECK: OpSpecConstantOp %type__Globals CompositeInsert %uint_3 [[struct]] 0
+               OpCapability Shader
+               OpCapability Addresses
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource HLSL 600
+               OpName %type__Globals "type.$Globals"
+               OpMemberName %type__Globals 0 "x"
+               OpMemberName %type__Globals 1 "y"
+               OpMemberName %type__Globals 2 "z"
+               OpName %main "main"
+               OpDecorate %c_0 SpecId 0
+               OpDecorate %c_1 SpecId 1
+               OpDecorate %c_2 SpecId 2
+               OpMemberDecorate %type__Globals 0 Offset 0
+               OpMemberDecorate %type__Globals 1 Offset 4
+               OpMemberDecorate %type__Globals 2 Offset 16
+       %uint = OpTypeInt 32 0
+        %c_0 = OpSpecConstant %uint 0
+        %c_1 = OpSpecConstant %uint 1
+        %c_2 = OpSpecConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+%type__Globals = OpTypeStruct %uint %uint %uint
+%spec_const_global = OpSpecConstantComposite %type__Globals %c_0 %c_1 %c_2
+%insert = OpSpecConstantOp %type__Globals CompositeInsert %uint_3 %spec_const_global 1
+%extract = OpSpecConstantOp %uint CompositeExtract %insert 1
+       %void = OpTypeVoid
+         %14 = OpTypeFunction %void
+       %main = OpFunction %void None %14
+         %16 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true);
+}
+
 }  // namespace
diff --git a/third_party/SPIRV-Tools/test/opt/fold_test.cpp b/third_party/SPIRV-Tools/test/opt/fold_test.cpp
index db01924..beb26f2 100644
--- a/third_party/SPIRV-Tools/test/opt/fold_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/fold_test.cpp
@@ -3297,7 +3297,16 @@
             "%2 = OpIMul %int %3 %int_1\n" +
             "OpReturn\n" +
             "OpFunctionEnd",
-        2, 3)
+        2, 3),
+    // Test case 42: Don't fold comparisons of 64-bit types
+    // (https://github.com/KhronosGroup/SPIRV-Tools/issues/3343).
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpSLessThan %bool %long_0 %long_2\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+        2, 0)
 ));
 
 INSTANTIATE_TEST_SUITE_P(CompositeExtractFoldingTest, GeneralInstructionFoldingTest,
diff --git a/third_party/SPIRV-Tools/test/opt/inline_opaque_test.cpp b/third_party/SPIRV-Tools/test/opt/inline_opaque_test.cpp
index d10913a..b8d2dfa 100644
--- a/third_party/SPIRV-Tools/test/opt/inline_opaque_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/inline_opaque_test.cpp
@@ -102,12 +102,12 @@
 OpStore %32 %31
 %33 = OpLoad %S_t %s0
 OpStore %param %33
-%41 = OpAccessChain %_ptr_Function_18 %param %int_2
-%42 = OpLoad %18 %41
-%43 = OpAccessChain %_ptr_Function_v2float %param %int_0
-%44 = OpLoad %v2float %43
-%45 = OpImageSampleImplicitLod %v4float %42 %44
-OpStore %outColor %45
+%42 = OpAccessChain %_ptr_Function_18 %param %int_2
+%43 = OpLoad %18 %42
+%44 = OpAccessChain %_ptr_Function_v2float %param %int_0
+%45 = OpLoad %v2float %44
+%46 = OpImageSampleImplicitLod %v4float %43 %45
+OpStore %outColor %46
 OpReturn
 OpFunctionEnd
 )";
@@ -191,10 +191,10 @@
 %34 = OpVariable %_ptr_Function_20 Function
 %35 = OpVariable %_ptr_Function_20 Function
 %25 = OpVariable %_ptr_Function_20 Function
-%36 = OpLoad %20 %sampler16
-OpStore %34 %36
-%37 = OpLoad %20 %34
-OpStore %35 %37
+%37 = OpLoad %20 %sampler16
+OpStore %34 %37
+%38 = OpLoad %20 %34
+OpStore %35 %38
 %26 = OpLoad %20 %35
 OpStore %25 %26
 %27 = OpLoad %20 %25
@@ -301,12 +301,12 @@
 OpStore %33 %32
 %34 = OpLoad %S_t %s0
 OpStore %param %34
-%44 = OpAccessChain %_ptr_Function_19 %param %int_2
-%45 = OpLoad %19 %44
-%46 = OpAccessChain %_ptr_Function_v2float %param %int_0
-%47 = OpLoad %v2float %46
-%48 = OpImageSampleImplicitLod %v4float %45 %47
-OpStore %outColor %48
+%45 = OpAccessChain %_ptr_Function_19 %param %int_2
+%46 = OpLoad %19 %45
+%47 = OpAccessChain %_ptr_Function_v2float %param %int_0
+%48 = OpLoad %v2float %47
+%49 = OpImageSampleImplicitLod %v4float %46 %48
+OpStore %outColor %49
 OpReturn
 OpFunctionEnd
 )";
diff --git a/third_party/SPIRV-Tools/test/opt/inline_test.cpp b/third_party/SPIRV-Tools/test/opt/inline_test.cpp
index f44c04a..fc2197c 100644
--- a/third_party/SPIRV-Tools/test/opt/inline_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/inline_test.cpp
@@ -115,12 +115,12 @@
       "%param = OpVariable %_ptr_Function_v4float Function",
          "%22 = OpLoad %v4float %BaseColor",
                "OpStore %param %22",
-         "%33 = OpAccessChain %_ptr_Function_float %param %uint_0",
-         "%34 = OpLoad %float %33",
-         "%35 = OpAccessChain %_ptr_Function_float %param %uint_1",
-         "%36 = OpLoad %float %35",
-         "%37 = OpFAdd %float %34 %36",
-               "OpStore %32 %37",
+         "%34 = OpAccessChain %_ptr_Function_float %param %uint_0",
+         "%35 = OpLoad %float %34",
+         "%36 = OpAccessChain %_ptr_Function_float %param %uint_1",
+         "%37 = OpLoad %float %36",
+         "%38 = OpFAdd %float %35 %37",
+               "OpStore %32 %38",
          "%23 = OpLoad %float %32",
          "%24 = OpCompositeConstruct %v4float %23 %23 %23 %23",
                "OpStore %color %24",
@@ -248,7 +248,7 @@
       // clang-format off
        "%main = OpFunction %void None %15",
          "%28 = OpLabel",
-         "%57 = OpVariable %_ptr_Function_float Function",
+         "%58 = OpVariable %_ptr_Function_float Function",
          "%46 = OpVariable %_ptr_Function_float Function",
          "%47 = OpVariable %_ptr_Function_float Function",
          "%48 = OpVariable %_ptr_Function_float Function",
@@ -256,21 +256,21 @@
     "%param_1 = OpVariable %_ptr_Function_v4float Function",
          "%29 = OpLoad %v4float %BaseColor",
                "OpStore %param_1 %29",
-         "%49 = OpAccessChain %_ptr_Function_float %param_1 %uint_0",
-         "%50 = OpLoad %float %49",
-         "%51 = OpAccessChain %_ptr_Function_float %param_1 %uint_1",
-         "%52 = OpLoad %float %51",
-         "%53 = OpFAdd %float %50 %52",
-               "OpStore %46 %53",
-         "%54 = OpAccessChain %_ptr_Function_float %param_1 %uint_2",
-         "%55 = OpLoad %float %54",
-               "OpStore %47 %55",
-         "%58 = OpLoad %float %46",
-         "%59 = OpLoad %float %47",
-         "%60 = OpFMul %float %58 %59",
-               "OpStore %57 %60",
-         "%56 = OpLoad %float %57",
-               "OpStore %48 %56",
+         "%50 = OpAccessChain %_ptr_Function_float %param_1 %uint_0",
+         "%51 = OpLoad %float %50",
+         "%52 = OpAccessChain %_ptr_Function_float %param_1 %uint_1",
+         "%53 = OpLoad %float %52",
+         "%54 = OpFAdd %float %51 %53",
+               "OpStore %46 %54",
+         "%55 = OpAccessChain %_ptr_Function_float %param_1 %uint_2",
+         "%56 = OpLoad %float %55",
+               "OpStore %47 %56",
+         "%60 = OpLoad %float %46",
+         "%61 = OpLoad %float %47",
+         "%62 = OpFMul %float %60 %61",
+               "OpStore %58 %62",
+         "%57 = OpLoad %float %58",
+               "OpStore %48 %57",
          "%30 = OpLoad %float %48",
          "%31 = OpCompositeConstruct %v4float %30 %30 %30 %30",
                "OpStore %color %31",
@@ -390,13 +390,13 @@
                "OpStore %b %24",
          "%25 = OpLoad %v4float %b",
                "OpStore %param %25",
-         "%39 = OpAccessChain %_ptr_Function_float %param %uint_0",
-         "%40 = OpLoad %float %39",
-         "%41 = OpAccessChain %_ptr_Function_float %param %uint_1",
-         "%42 = OpLoad %float %41",
-         "%43 = OpFAdd %float %40 %42",
-         "%44 = OpAccessChain %_ptr_Function_float %param %uint_2",
-               "OpStore %44 %43",
+         "%40 = OpAccessChain %_ptr_Function_float %param %uint_0",
+         "%41 = OpLoad %float %40",
+         "%42 = OpAccessChain %_ptr_Function_float %param %uint_1",
+         "%43 = OpLoad %float %42",
+         "%44 = OpFAdd %float %41 %43",
+         "%45 = OpAccessChain %_ptr_Function_float %param %uint_2",
+               "OpStore %45 %44",
          "%27 = OpLoad %v4float %param",
                "OpStore %b %27",
          "%28 = OpAccessChain %_ptr_Function_float %b %uint_2",
@@ -521,21 +521,21 @@
       "%param = OpVariable %_ptr_Function_v4float Function",
          "%24 = OpLoad %v4float %BaseColor",
                "OpStore %param %24",
-         "%40 = OpAccessChain %_ptr_Function_float %param %uint_0",
-         "%41 = OpLoad %float %40",
-               "OpStore %38 %41",
-         "%42 = OpLoad %float %38",
-         "%43 = OpFOrdLessThan %bool %42 %float_0",
-               "OpSelectionMerge %44 None",
-               "OpBranchConditional %43 %45 %44",
+         "%41 = OpAccessChain %_ptr_Function_float %param %uint_0",
+         "%42 = OpLoad %float %41",
+               "OpStore %38 %42",
+         "%43 = OpLoad %float %38",
+         "%44 = OpFOrdLessThan %bool %43 %float_0",
+               "OpSelectionMerge %48 None",
+               "OpBranchConditional %44 %45 %48",
          "%45 = OpLabel",
          "%46 = OpLoad %float %38",
          "%47 = OpFNegate %float %46",
                "OpStore %38 %47",
-               "OpBranch %44",
-         "%44 = OpLabel",
-         "%48 = OpLoad %float %38",
-               "OpStore %39 %48",
+               "OpBranch %48",
+         "%48 = OpLabel",
+         "%49 = OpLoad %float %38",
+               "OpStore %39 %49",
          "%25 = OpLoad %float %39",
          "%26 = OpCompositeConstruct %v4float %25 %25 %25 %25",
                "OpStore %color %26",
@@ -675,8 +675,8 @@
       // clang-format off
        "%main = OpFunction %void None %12",
          "%27 = OpLabel",
-         "%62 = OpVariable %_ptr_Function_float Function",
          "%63 = OpVariable %_ptr_Function_float Function",
+         "%64 = OpVariable %_ptr_Function_float Function",
          "%52 = OpVariable %_ptr_Function_float Function",
          "%53 = OpVariable %_ptr_Function_float Function",
       "%color = OpVariable %_ptr_Function_v4float Function",
@@ -687,20 +687,20 @@
          "%29 = OpAccessChain %_ptr_Function_float %color %uint_0",
          "%30 = OpLoad %float %29",
                "OpStore %param %30",
-         "%54 = OpLoad %float %param",
-               "OpStore %52 %54",
-         "%55 = OpLoad %float %52",
-         "%56 = OpFOrdLessThan %bool %55 %float_0",
-               "OpSelectionMerge %57 None",
-               "OpBranchConditional %56 %58 %57",
+         "%55 = OpLoad %float %param",
+               "OpStore %52 %55",
+         "%56 = OpLoad %float %52",
+         "%57 = OpFOrdLessThan %bool %56 %float_0",
+               "OpSelectionMerge %61 None",
+               "OpBranchConditional %57 %58 %61",
          "%58 = OpLabel",
          "%59 = OpLoad %float %52",
          "%60 = OpFNegate %float %59",
                "OpStore %52 %60",
-               "OpBranch %57",
-         "%57 = OpLabel",
-         "%61 = OpLoad %float %52",
-               "OpStore %53 %61",
+               "OpBranch %61",
+         "%61 = OpLabel",
+         "%62 = OpLoad %float %52",
+               "OpStore %53 %62",
          "%31 = OpLoad %float %53",
          "%32 = OpFOrdGreaterThan %bool %31 %float_2",
                "OpSelectionMerge %33 None",
@@ -709,25 +709,25 @@
          "%35 = OpAccessChain %_ptr_Function_float %color %uint_1",
          "%36 = OpLoad %float %35",
                "OpStore %param_0 %36",
-         "%64 = OpLoad %float %param_0",
-               "OpStore %62 %64",
-         "%65 = OpLoad %float %62",
-         "%66 = OpFOrdLessThan %bool %65 %float_0",
-               "OpSelectionMerge %67 None",
-               "OpBranchConditional %66 %68 %67",
-         "%68 = OpLabel",
-         "%69 = OpLoad %float %62",
-         "%70 = OpFNegate %float %69",
-               "OpStore %62 %70",
-               "OpBranch %67",
-         "%67 = OpLabel",
-         "%71 = OpLoad %float %62",
+         "%66 = OpLoad %float %param_0",
+               "OpStore %63 %66",
+         "%67 = OpLoad %float %63",
+         "%68 = OpFOrdLessThan %bool %67 %float_0",
+               "OpSelectionMerge %72 None",
+               "OpBranchConditional %68 %69 %72",
+         "%69 = OpLabel",
+         "%70 = OpLoad %float %63",
+         "%71 = OpFNegate %float %70",
                "OpStore %63 %71",
-         "%37 = OpLoad %float %63",
+               "OpBranch %72",
+         "%72 = OpLabel",
+         "%73 = OpLoad %float %63",
+               "OpStore %64 %73",
+         "%37 = OpLoad %float %64",
          "%38 = OpFOrdGreaterThan %bool %37 %float_2",
                "OpBranch %33",
          "%33 = OpLabel",
-         "%39 = OpPhi %bool %32 %57 %38 %67",
+         "%39 = OpPhi %bool %32 %61 %38 %72",
                "OpSelectionMerge %40 None",
                "OpBranchConditional %39 %41 %40",
          "%41 = OpLabel",
@@ -902,28 +902,28 @@
                "OpStore %color1 %42",
          "%43 = OpLoad %v4float %BaseColor",
                "OpStore %param %43",
-         "%68 = OpAccessChain %_ptr_Function_float %param %uint_0",
-         "%69 = OpLoad %float %68",
-               "OpStore %66 %69",
-         "%70 = OpLoad %float %66",
-         "%71 = OpFOrdLessThan %bool %70 %float_0",
-               "OpSelectionMerge %72 None",
-               "OpBranchConditional %71 %73 %72",
+         "%69 = OpAccessChain %_ptr_Function_float %param %uint_0",
+         "%70 = OpLoad %float %69",
+               "OpStore %66 %70",
+         "%71 = OpLoad %float %66",
+         "%72 = OpFOrdLessThan %bool %71 %float_0",
+               "OpSelectionMerge %76 None",
+               "OpBranchConditional %72 %73 %76",
          "%73 = OpLabel",
          "%74 = OpLoad %float %66",
          "%75 = OpFNegate %float %74",
                "OpStore %66 %75",
-               "OpBranch %72",
-         "%72 = OpLabel",
-         "%76 = OpLoad %float %66",
-               "OpStore %67 %76",
+               "OpBranch %76",
+         "%76 = OpLabel",
+         "%77 = OpLoad %float %66",
+               "OpStore %67 %77",
          "%44 = OpLoad %float %67",
          "%45 = OpCompositeConstruct %v4float %44 %44 %44 %44",
                "OpStore %color2 %45",
          "%46 = OpLoad %25 %t2D",
          "%47 = OpLoad %27 %samp",
-         "%77 = OpSampledImage %29 %39 %40",
-         "%48 = OpImageSampleImplicitLod %v4float %77 %35",
+         "%78 = OpSampledImage %29 %39 %40",
+         "%48 = OpImageSampleImplicitLod %v4float %78 %35",
                "OpStore %color3 %48",
          "%49 = OpLoad %v4float %color1",
          "%50 = OpLoad %v4float %color2",
@@ -1108,27 +1108,27 @@
                "OpStore %color1 %43",
          "%46 = OpLoad %v4float %BaseColor",
                "OpStore %param %46",
-         "%70 = OpAccessChain %_ptr_Function_float %param %uint_0",
-         "%71 = OpLoad %float %70",
-               "OpStore %68 %71",
-         "%72 = OpLoad %float %68",
-         "%73 = OpFOrdLessThan %bool %72 %float_0",
-               "OpSelectionMerge %74 None",
-               "OpBranchConditional %73 %75 %74",
+         "%71 = OpAccessChain %_ptr_Function_float %param %uint_0",
+         "%72 = OpLoad %float %71",
+               "OpStore %68 %72",
+         "%73 = OpLoad %float %68",
+         "%74 = OpFOrdLessThan %bool %73 %float_0",
+               "OpSelectionMerge %78 None",
+               "OpBranchConditional %74 %75 %78",
          "%75 = OpLabel",
          "%76 = OpLoad %float %68",
          "%77 = OpFNegate %float %76",
                "OpStore %68 %77",
-               "OpBranch %74",
-         "%74 = OpLabel",
-         "%78 = OpLoad %float %68",
-               "OpStore %69 %78",
+               "OpBranch %78",
+         "%78 = OpLabel",
+         "%79 = OpLoad %float %68",
+               "OpStore %69 %79",
          "%47 = OpLoad %float %69",
          "%48 = OpCompositeConstruct %v4float %47 %47 %47 %47",
                "OpStore %color2 %48",
-         "%79 = OpSampledImage %30 %40 %41",
-         "%80 = OpImage %26 %79",
-         "%49 = OpSampledImage %30 %80 %45",
+         "%80 = OpSampledImage %30 %40 %41",
+         "%81 = OpImage %26 %80",
+         "%49 = OpSampledImage %30 %81 %45",
          "%50 = OpImageSampleImplicitLod %v4float %49 %36",
                "OpStore %color3 %50",
          "%51 = OpLoad %v4float %color1",
@@ -1314,28 +1314,28 @@
                "OpStore %color1 %43",
          "%47 = OpLoad %v4float %BaseColor",
                "OpStore %param %47",
-         "%70 = OpAccessChain %_ptr_Function_float %param %uint_0",
-         "%71 = OpLoad %float %70",
-               "OpStore %68 %71",
-         "%72 = OpLoad %float %68",
-         "%73 = OpFOrdLessThan %bool %72 %float_0",
-               "OpSelectionMerge %74 None",
-               "OpBranchConditional %73 %75 %74",
+         "%71 = OpAccessChain %_ptr_Function_float %param %uint_0",
+         "%72 = OpLoad %float %71",
+               "OpStore %68 %72",
+         "%73 = OpLoad %float %68",
+         "%74 = OpFOrdLessThan %bool %73 %float_0",
+               "OpSelectionMerge %78 None",
+               "OpBranchConditional %74 %75 %78",
          "%75 = OpLabel",
          "%76 = OpLoad %float %68",
          "%77 = OpFNegate %float %76",
                "OpStore %68 %77",
-               "OpBranch %74",
-         "%74 = OpLabel",
-         "%78 = OpLoad %float %68",
-               "OpStore %69 %78",
+               "OpBranch %78",
+         "%78 = OpLabel",
+         "%79 = OpLoad %float %68",
+               "OpStore %69 %79",
          "%48 = OpLoad %float %69",
          "%49 = OpCompositeConstruct %v4float %48 %48 %48 %48",
                "OpStore %color2 %49",
-         "%79 = OpSampledImage %30 %40 %41",
-         "%80 = OpImage %26 %79",
-         "%81 = OpSampledImage %30 %80 %45",
-         "%50 = OpImageSampleImplicitLod %v4float %81 %36",
+         "%80 = OpSampledImage %30 %40 %41",
+         "%81 = OpImage %26 %80",
+         "%82 = OpSampledImage %30 %81 %45",
+         "%50 = OpImageSampleImplicitLod %v4float %82 %36",
                "OpStore %color3 %50",
          "%51 = OpLoad %v4float %color1",
          "%52 = OpLoad %v4float %color2",
@@ -1355,292 +1355,6 @@
       /* skip_nop = */ false, /* do_validate = */ true);
 }
 
-TEST_F(InlineTest, EarlyReturnFunctionInlined) {
-  // #version 140
-  //
-  // in vec4 BaseColor;
-  //
-  // float foo(vec4 bar)
-  // {
-  //     if (bar.x < 0.0)
-  //         return 0.0;
-  //     return bar.x;
-  // }
-  //
-  // void main()
-  // {
-  //     vec4 color = vec4(foo(BaseColor));
-  //     gl_FragColor = color;
-  // }
-
-  const std::string predefs =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 140
-OpName %main "main"
-OpName %foo_vf4_ "foo(vf4;"
-OpName %bar "bar"
-OpName %color "color"
-OpName %BaseColor "BaseColor"
-OpName %param "param"
-OpName %gl_FragColor "gl_FragColor"
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%14 = OpTypeFunction %float %_ptr_Function_v4float
-%uint = OpTypeInt 32 0
-%uint_0 = OpConstant %uint 0
-%_ptr_Function_float = OpTypePointer Function %float
-%float_0 = OpConstant %float 0
-%bool = OpTypeBool
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%gl_FragColor = OpVariable %_ptr_Output_v4float Output
-)";
-
-  const std::string nonEntryFuncs =
-      R"(%foo_vf4_ = OpFunction %float None %14
-%bar = OpFunctionParameter %_ptr_Function_v4float
-%27 = OpLabel
-%28 = OpAccessChain %_ptr_Function_float %bar %uint_0
-%29 = OpLoad %float %28
-%30 = OpFOrdLessThan %bool %29 %float_0
-OpSelectionMerge %31 None
-OpBranchConditional %30 %32 %31
-%32 = OpLabel
-OpReturnValue %float_0
-%31 = OpLabel
-%33 = OpAccessChain %_ptr_Function_float %bar %uint_0
-%34 = OpLoad %float %33
-OpReturnValue %34
-OpFunctionEnd
-)";
-
-  const std::string before =
-      R"(%main = OpFunction %void None %10
-%22 = OpLabel
-%color = OpVariable %_ptr_Function_v4float Function
-%param = OpVariable %_ptr_Function_v4float Function
-%23 = OpLoad %v4float %BaseColor
-OpStore %param %23
-%24 = OpFunctionCall %float %foo_vf4_ %param
-%25 = OpCompositeConstruct %v4float %24 %24 %24 %24
-OpStore %color %25
-%26 = OpLoad %v4float %color
-OpStore %gl_FragColor %26
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string after =
-      R"(%false = OpConstantFalse %bool
-%main = OpFunction %void None %10
-%22 = OpLabel
-%35 = OpVariable %_ptr_Function_float Function
-%color = OpVariable %_ptr_Function_v4float Function
-%param = OpVariable %_ptr_Function_v4float Function
-%23 = OpLoad %v4float %BaseColor
-OpStore %param %23
-OpBranch %36
-%36 = OpLabel
-OpLoopMerge %37 %38 None
-OpBranch %39
-%39 = OpLabel
-%40 = OpAccessChain %_ptr_Function_float %param %uint_0
-%41 = OpLoad %float %40
-%42 = OpFOrdLessThan %bool %41 %float_0
-OpSelectionMerge %43 None
-OpBranchConditional %42 %44 %43
-%44 = OpLabel
-OpStore %35 %float_0
-OpBranch %37
-%43 = OpLabel
-%45 = OpAccessChain %_ptr_Function_float %param %uint_0
-%46 = OpLoad %float %45
-OpStore %35 %46
-OpBranch %37
-%38 = OpLabel
-OpBranchConditional %false %36 %37
-%37 = OpLabel
-%24 = OpLoad %float %35
-%25 = OpCompositeConstruct %v4float %24 %24 %24 %24
-OpStore %color %25
-%26 = OpLoad %v4float %color
-OpStore %gl_FragColor %26
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<InlineExhaustivePass>(predefs + before + nonEntryFuncs,
-                                              predefs + after + nonEntryFuncs,
-                                              false, true);
-}
-
-TEST_F(InlineTest, EarlyReturnNotAppearingLastInFunctionInlined) {
-  // Example from https://github.com/KhronosGroup/SPIRV-Tools/issues/755
-  //
-  // Original example is derived from:
-  //
-  // #version 450
-  //
-  // float foo() {
-  //     if (true) {
-  //     }
-  // }
-  //
-  // void main() { foo(); }
-  //
-  // But the order of basic blocks in foo is changed so that the return
-  // block is listed second-last.  There is only one return in the callee
-  // but it does not appear last.
-
-  const std::string predefs =
-      R"(OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint Vertex %main "main"
-OpSource GLSL 450
-OpName %main "main"
-OpName %foo_ "foo("
-%void = OpTypeVoid
-%4 = OpTypeFunction %void
-%bool = OpTypeBool
-%true = OpConstantTrue %bool
-)";
-
-  const std::string nonEntryFuncs =
-      R"(%foo_ = OpFunction %void None %4
-%7 = OpLabel
-OpSelectionMerge %8 None
-OpBranchConditional %true %9 %8
-%8 = OpLabel
-OpReturn
-%9 = OpLabel
-OpBranch %8
-OpFunctionEnd
-)";
-
-  const std::string before =
-      R"(%main = OpFunction %void None %4
-%10 = OpLabel
-%11 = OpFunctionCall %void %foo_
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string after =
-      R"(%main = OpFunction %void None %4
-%10 = OpLabel
-OpSelectionMerge %12 None
-OpBranchConditional %true %13 %12
-%12 = OpLabel
-OpBranch %14
-%13 = OpLabel
-OpBranch %12
-%14 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<InlineExhaustivePass>(predefs + nonEntryFuncs + before,
-                                              predefs + nonEntryFuncs + after,
-                                              false, true);
-}
-
-TEST_F(InlineTest, ForwardReferencesInPhiInlined) {
-  // The basic structure of the test case is like this:
-  //
-  // int foo() {
-  //   int result = 1;
-  //   if (true) {
-  //      result = 1;
-  //   }
-  //   return result;
-  // }
-  //
-  // void main() {
-  //  int x = foo();
-  // }
-  //
-  // but with modifications: Using Phi instead of load/store, and the
-  // return block in foo appears before the "then" block.
-
-  const std::string predefs =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Vertex %main "main"
-OpSource GLSL 450
-OpName %main "main"
-OpName %foo_ "foo("
-OpName %x "x"
-%void = OpTypeVoid
-%6 = OpTypeFunction %void
-%int = OpTypeInt 32 1
-%8 = OpTypeFunction %int
-%bool = OpTypeBool
-%true = OpConstantTrue %bool
-%int_0 = OpConstant %int 0
-%_ptr_Function_int = OpTypePointer Function %int
-)";
-
-  const std::string nonEntryFuncs =
-      R"(%foo_ = OpFunction %int None %8
-%13 = OpLabel
-%14 = OpCopyObject %int %int_0
-OpSelectionMerge %15 None
-OpBranchConditional %true %16 %15
-%15 = OpLabel
-%17 = OpPhi %int %14 %13 %18 %16
-OpReturnValue %17
-%16 = OpLabel
-%18 = OpCopyObject %int %int_0
-OpBranch %15
-OpFunctionEnd
-)";
-
-  const std::string before =
-      R"(%main = OpFunction %void None %6
-%19 = OpLabel
-%x = OpVariable %_ptr_Function_int Function
-%20 = OpFunctionCall %int %foo_
-OpStore %x %20
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string after =
-      R"(%main = OpFunction %void None %6
-%19 = OpLabel
-%21 = OpVariable %_ptr_Function_int Function
-%x = OpVariable %_ptr_Function_int Function
-%22 = OpCopyObject %int %int_0
-OpSelectionMerge %23 None
-OpBranchConditional %true %24 %23
-%23 = OpLabel
-%26 = OpPhi %int %22 %19 %25 %24
-OpStore %21 %26
-OpBranch %27
-%24 = OpLabel
-%25 = OpCopyObject %int %int_0
-OpBranch %23
-%27 = OpLabel
-%20 = OpLoad %int %21
-OpStore %x %20
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<InlineExhaustivePass>(predefs + nonEntryFuncs + before,
-                                              predefs + nonEntryFuncs + after,
-                                              false, true);
-}
-
 TEST_F(InlineTest, EarlyReturnInLoopIsNotInlined) {
   // #version 140
   //
@@ -1820,8 +1534,8 @@
 OpBranch %10
 %10 = OpLabel
 OpLoopMerge %12 %10 None
-OpBranch %13
-%13 = OpLabel
+OpBranch %14
+%14 = OpLabel
 OpBranchConditional %true %10 %12
 %12 = OpLabel
 OpReturn
@@ -1890,11 +1604,11 @@
 OpBranch %18
 %18 = OpLabel
 %19 = OpCopyObject %int %int_3
-%25 = OpCopyObject %int %int_1
+%26 = OpCopyObject %int %int_1
 OpLoopMerge %22 %23 None
-OpBranch %26
-%26 = OpLabel
-%27 = OpCopyObject %int %int_2
+OpBranch %27
+%27 = OpLabel
+%28 = OpCopyObject %int %int_2
 %21 = OpCopyObject %int %int_4
 OpBranchConditional %true %23 %22
 %23 = OpLabel
@@ -1983,11 +1697,11 @@
 OpLoopMerge %16 %13 None
 OpBranch %17
 %17 = OpLabel
-%18 = OpCopyObject %bool %true
-OpSelectionMerge %19 None
-OpBranchConditional %true %19 %19
-%19 = OpLabel
-%20 = OpPhi %bool %18 %17
+%19 = OpCopyObject %bool %true
+OpSelectionMerge %20 None
+OpBranchConditional %true %20 %20
+%20 = OpLabel
+%21 = OpPhi %bool %19 %17
 OpBranchConditional %true %13 %16
 %16 = OpLabel
 OpReturn
@@ -2060,11 +1774,11 @@
 OpLoopMerge %22 %23 None
 OpBranch %25
 %25 = OpLabel
-%26 = OpCopyObject %int %int_1
-OpSelectionMerge %27 None
-OpBranchConditional %true %27 %27
-%27 = OpLabel
-%28 = OpCopyObject %int %int_2
+%27 = OpCopyObject %int %int_1
+OpSelectionMerge %28 None
+OpBranchConditional %true %28 %28
+%28 = OpLabel
+%29 = OpCopyObject %int %int_2
 %21 = OpCopyObject %int %int_4
 OpBranchConditional %true %23 %22
 %23 = OpLabel
@@ -2080,165 +1794,6 @@
                                               false, true);
 }
 
-TEST_F(
-    InlineTest,
-    SingleBlockLoopCallsMultiBlockCalleeHavingSelectionMergeAndMultiReturns) {
-  // This is similar to SingleBlockLoopCallsMultiBlockCalleeHavingSelectionMerge
-  // except that in addition to starting with a selection header, the
-  // callee also has multi returns.
-  //
-  // So now we have to accommodate:
-  // - The caller's OpLoopMerge (which must move to the first block)
-  // - The single-trip loop to wrap the multi returns, and
-  // - The callee's selection merge in its first block.
-  // Each of these must go into their own blocks.
-
-  const std::string predefs =
-      R"(OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %1 "main"
-OpSource OpenCL_C 120
-%bool = OpTypeBool
-%int = OpTypeInt 32 1
-%true = OpConstantTrue %bool
-%false = OpConstantFalse %bool
-%int_0 = OpConstant %int 0
-%int_1 = OpConstant %int 1
-%int_2 = OpConstant %int 2
-%int_3 = OpConstant %int 3
-%int_4 = OpConstant %int 4
-%void = OpTypeVoid
-%12 = OpTypeFunction %void
-)";
-
-  const std::string nonEntryFuncs =
-      R"(%13 = OpFunction %void None %12
-%14 = OpLabel
-%15 = OpCopyObject %int %int_0
-OpReturn
-%16 = OpLabel
-%17 = OpCopyObject %int %int_1
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string before =
-      R"(%1 = OpFunction %void None %12
-%18 = OpLabel
-OpBranch %19
-%19 = OpLabel
-%20 = OpCopyObject %int %int_2
-%21 = OpFunctionCall %void %13
-%22 = OpCopyObject %int %int_3
-OpLoopMerge %23 %19 None
-OpBranchConditional %true %19 %23
-%23 = OpLabel
-%24 = OpCopyObject %int %int_4
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string after =
-      R"(%1 = OpFunction %void None %12
-%18 = OpLabel
-OpBranch %19
-%19 = OpLabel
-%20 = OpCopyObject %int %int_2
-%25 = OpCopyObject %int %int_0
-OpLoopMerge %23 %19 None
-OpBranch %26
-%27 = OpLabel
-%28 = OpCopyObject %int %int_1
-OpBranch %26
-%26 = OpLabel
-%22 = OpCopyObject %int %int_3
-OpBranchConditional %true %19 %23
-%23 = OpLabel
-%24 = OpCopyObject %int %int_4
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<InlineExhaustivePass>(predefs + nonEntryFuncs + before,
-                                              predefs + nonEntryFuncs + after,
-                                              false, true);
-}
-
-TEST_F(InlineTest, CalleeWithMultiReturnAndPhiRequiresEntryBlockRemapping) {
-  // The case from https://github.com/KhronosGroup/SPIRV-Tools/issues/790
-  //
-  // The callee has multiple returns, and so must be wrapped with a single-trip
-  // loop.  That code must remap the callee entry block ID to the introduced
-  // loop body's ID.  Otherwise you can get a dominance error in a cloned OpPhi.
-
-  const std::string predefs =
-      R"(OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %1 "main"
-OpSource OpenCL_C 120
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%int_1 = OpConstant %int 1
-%int_2 = OpConstant %int 2
-%int_3 = OpConstant %int 3
-%int_4 = OpConstant %int 4
-%void = OpTypeVoid
-%9 = OpTypeFunction %void
-%bool = OpTypeBool
-%false = OpConstantFalse %bool
-)";
-
-  // This callee has multiple returns, and a Phi in the second block referencing
-  // a value generated in the entry block.
-  const std::string nonEntryFuncs =
-      R"(%12 = OpFunction %void None %9
-%13 = OpLabel
-%14 = OpCopyObject %int %int_0
-OpBranch %15
-%15 = OpLabel
-%16 = OpPhi %int %14 %13
-%17 = OpCopyObject %int %int_1
-OpReturn
-%18 = OpLabel
-%19 = OpCopyObject %int %int_2
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string before =
-      R"(%1 = OpFunction %void None %9
-%20 = OpLabel
-%21 = OpCopyObject %int %int_3
-%22 = OpFunctionCall %void %12
-%23 = OpCopyObject %int %int_4
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string after =
-      R"(%1 = OpFunction %void None %9
-%20 = OpLabel
-%21 = OpCopyObject %int %int_3
-%24 = OpCopyObject %int %int_0
-OpBranch %25
-%25 = OpLabel
-%26 = OpPhi %int %24 %20
-%27 = OpCopyObject %int %int_1
-OpBranch %28
-%29 = OpLabel
-%30 = OpCopyObject %int %int_2
-OpBranch %28
-%28 = OpLabel
-%23 = OpCopyObject %int %int_4
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<InlineExhaustivePass>(predefs + nonEntryFuncs + before,
-                                              predefs + nonEntryFuncs + after,
-                                              false, true);
-}
-
 TEST_F(InlineTest, NonInlinableCalleeWithSingleReturn) {
   // The case from https://github.com/KhronosGroup/SPIRV-Tools/issues/2018
   //
@@ -2324,138 +1879,6 @@
       predefs + caller + callee, predefs + caller + callee, false, true);
 }
 
-TEST_F(InlineTest, CalleeWithSingleReturnNeedsSingleTripLoopWrapper) {
-  // The case from https://github.com/KhronosGroup/SPIRV-Tools/issues/2018
-  //
-  // The callee has a single return, but needs single-trip loop wrapper
-  // to be inlined because the return is in a selection structure.
-
-  const std::string predefs =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %_GLF_color
-OpExecutionMode %main OriginUpperLeft
-OpSource ESSL 310
-OpName %main "main"
-OpName %f_ "f("
-OpName %i "i"
-OpName %_GLF_color "_GLF_color"
-OpDecorate %_GLF_color Location 0
-%void = OpTypeVoid
-%7 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%9 = OpTypeFunction %float
-%float_1 = OpConstant %float 1
-%bool = OpTypeBool
-%false = OpConstantFalse %bool
-%true = OpConstantTrue %bool
-%int = OpTypeInt 32 1
-%_ptr_Function_int = OpTypePointer Function %int
-%int_0 = OpConstant %int 0
-%int_1 = OpConstant %int 1
-%v4float = OpTypeVector %float 4
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_GLF_color = OpVariable %_ptr_Output_v4float Output
-%float_0 = OpConstant %float 0
-%21 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
-%22 = OpConstantComposite %v4float %float_0 %float_1 %float_0 %float_1
-)";
-
-  const std::string new_predefs =
-      R"(%_ptr_Function_float = OpTypePointer Function %float
-)";
-
-  const std::string main_before =
-      R"(%main = OpFunction %void None %7
-%23 = OpLabel
-%i = OpVariable %_ptr_Function_int Function
-OpStore %i %int_0
-OpBranch %24
-%24 = OpLabel
-OpLoopMerge %25 %26 None
-OpBranch %27
-%27 = OpLabel
-%28 = OpLoad %int %i
-%29 = OpSLessThan %bool %28 %int_1
-OpBranchConditional %29 %30 %25
-%30 = OpLabel
-OpStore %_GLF_color %21
-%31 = OpFunctionCall %float %f_
-OpBranch %26
-%26 = OpLabel
-%32 = OpLoad %int %i
-%33 = OpIAdd %int %32 %int_1
-OpStore %i %33
-OpBranch %24
-%25 = OpLabel
-OpStore %_GLF_color %22
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string main_after =
-      R"(%main = OpFunction %void None %7
-%23 = OpLabel
-%38 = OpVariable %_ptr_Function_float Function
-%i = OpVariable %_ptr_Function_int Function
-OpStore %i %int_0
-OpBranch %24
-%24 = OpLabel
-OpLoopMerge %25 %26 None
-OpBranch %27
-%27 = OpLabel
-%28 = OpLoad %int %i
-%29 = OpSLessThan %bool %28 %int_1
-OpBranchConditional %29 %30 %25
-%30 = OpLabel
-OpStore %_GLF_color %21
-OpBranch %39
-%39 = OpLabel
-OpLoopMerge %40 %41 None
-OpBranch %42
-%42 = OpLabel
-OpSelectionMerge %43 None
-OpBranchConditional %true %44 %43
-%44 = OpLabel
-OpStore %38 %float_1
-OpBranch %40
-%43 = OpLabel
-OpStore %38 %float_1
-OpBranch %40
-%41 = OpLabel
-OpBranchConditional %false %39 %40
-%40 = OpLabel
-%31 = OpLoad %float %38
-OpBranch %26
-%26 = OpLabel
-%32 = OpLoad %int %i
-%33 = OpIAdd %int %32 %int_1
-OpStore %i %33
-OpBranch %24
-%25 = OpLabel
-OpStore %_GLF_color %22
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string callee =
-      R"(%f_ = OpFunction %float None %9
-%34 = OpLabel
-OpSelectionMerge %35 None
-OpBranchConditional %true %36 %35
-%36 = OpLabel
-OpReturnValue %float_1
-%35 = OpLabel
-OpReturnValue %float_1
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<InlineExhaustivePass>(
-      predefs + main_before + callee,
-      predefs + new_predefs + main_after + callee, false, true);
-}
-
 TEST_F(InlineTest, Decorated1) {
   // Same test as Simple with the difference
   // that OpFAdd in the outlined function is
@@ -2526,7 +1949,7 @@
 )";
 
   const std::string after =
-      R"(OpDecorate %37 RelaxedPrecision
+      R"(OpDecorate %38 RelaxedPrecision
 %void = OpTypeVoid
 %11 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -2548,12 +1971,12 @@
 %param = OpVariable %_ptr_Function_v4float Function
 %23 = OpLoad %v4float %BaseColor
 OpStore %param %23
-%33 = OpAccessChain %_ptr_Function_float %param %uint_0
-%34 = OpLoad %float %33
-%35 = OpAccessChain %_ptr_Function_float %param %uint_1
-%36 = OpLoad %float %35
-%37 = OpFAdd %float %34 %36
-OpStore %32 %37
+%34 = OpAccessChain %_ptr_Function_float %param %uint_0
+%35 = OpLoad %float %34
+%36 = OpAccessChain %_ptr_Function_float %param %uint_1
+%37 = OpLoad %float %36
+%38 = OpFAdd %float %35 %37
+OpStore %32 %38
 %24 = OpLoad %float %32
 %25 = OpCompositeConstruct %v4float %24 %24 %24 %24
 OpStore %color %25
@@ -2672,12 +2095,12 @@
 %param = OpVariable %_ptr_Function_v4float Function
 %22 = OpLoad %v4float %BaseColor
 OpStore %param %22
-%33 = OpAccessChain %_ptr_Function_float %param %uint_0
-%34 = OpLoad %float %33
-%35 = OpAccessChain %_ptr_Function_float %param %uint_1
-%36 = OpLoad %float %35
-%37 = OpFAdd %float %34 %36
-OpStore %32 %37
+%34 = OpAccessChain %_ptr_Function_float %param %uint_0
+%35 = OpLoad %float %34
+%36 = OpAccessChain %_ptr_Function_float %param %uint_1
+%37 = OpLoad %float %36
+%38 = OpFAdd %float %35 %37
+OpStore %32 %38
 %23 = OpLoad %float %32
 %24 = OpCompositeConstruct %v4float %23 %23 %23 %23
 OpStore %color %24
@@ -3017,7 +2440,7 @@
 %main = OpFunction %void None %3
 %5 = OpLabel
 OpKill
-%17 = OpLabel
+%18 = OpLabel
 OpReturn
 OpFunctionEnd
 %kill_ = OpFunction %void None %3
@@ -3030,6 +2453,1305 @@
   SinglePassRunAndCheck<InlineExhaustivePass>(before, after, false, true);
 }
 
+TEST_F(InlineTest, EarlyReturnFunctionInlined) {
+  // #version 140
+  //
+  // in vec4 BaseColor;
+  //
+  // float foo(vec4 bar)
+  // {
+  //     if (bar.x < 0.0)
+  //         return 0.0;
+  //     return bar.x;
+  // }
+  //
+  // void main()
+  // {
+  //     vec4 color = vec4(foo(BaseColor));
+  //     gl_FragColor = color;
+  // }
+
+  const std::string predefs =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %foo_vf4_ "foo(vf4;"
+OpName %bar "bar"
+OpName %color "color"
+OpName %BaseColor "BaseColor"
+OpName %param "param"
+OpName %gl_FragColor "gl_FragColor"
+%void = OpTypeVoid
+%10 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%14 = OpTypeFunction %float %_ptr_Function_v4float
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%bool = OpTypeBool
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+)";
+
+  const std::string foo =
+      R"(%foo_vf4_ = OpFunction %float None %14
+%bar = OpFunctionParameter %_ptr_Function_v4float
+%27 = OpLabel
+%28 = OpAccessChain %_ptr_Function_float %bar %uint_0
+%29 = OpLoad %float %28
+%30 = OpFOrdLessThan %bool %29 %float_0
+OpSelectionMerge %31 None
+OpBranchConditional %30 %32 %31
+%32 = OpLabel
+OpReturnValue %float_0
+%31 = OpLabel
+%33 = OpAccessChain %_ptr_Function_float %bar %uint_0
+%34 = OpLoad %float %33
+OpReturnValue %34
+OpFunctionEnd
+)";
+
+  const std::string fooMergeReturn =
+      R"(%foo_vf4_ = OpFunction %float None %14
+%bar = OpFunctionParameter %_ptr_Function_v4float
+%27 = OpLabel
+%41 = OpVariable %_ptr_Function_bool Function %false
+%36 = OpVariable %_ptr_Function_float Function
+OpSelectionMerge %35 None
+OpSwitch %uint_0 %38
+%38 = OpLabel
+%28 = OpAccessChain %_ptr_Function_float %bar %uint_0
+%29 = OpLoad %float %28
+%30 = OpFOrdLessThan %bool %29 %float_0
+OpSelectionMerge %31 None
+OpBranchConditional %30 %32 %31
+%32 = OpLabel
+OpStore %41 %true
+OpStore %36 %float_0
+OpBranch %35
+%31 = OpLabel
+%33 = OpAccessChain %_ptr_Function_float %bar %uint_0
+%34 = OpLoad %float %33
+OpStore %41 %true
+OpStore %36 %34
+OpBranch %35
+%35 = OpLabel
+%37 = OpLoad %float %36
+OpReturnValue %37
+OpFunctionEnd
+)";
+
+  const std::string before =
+      R"(%main = OpFunction %void None %10
+%22 = OpLabel
+%color = OpVariable %_ptr_Function_v4float Function
+%param = OpVariable %_ptr_Function_v4float Function
+%23 = OpLoad %v4float %BaseColor
+OpStore %param %23
+%24 = OpFunctionCall %float %foo_vf4_ %param
+%25 = OpCompositeConstruct %v4float %24 %24 %24 %24
+OpStore %color %25
+%26 = OpLoad %v4float %color
+OpStore %gl_FragColor %26
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(%false = OpConstantFalse %bool
+%_ptr_Function_bool = OpTypePointer Function %bool
+%true = OpConstantTrue %bool
+%main = OpFunction %void None %10
+%22 = OpLabel
+%43 = OpVariable %_ptr_Function_bool Function %false
+%44 = OpVariable %_ptr_Function_float Function
+%45 = OpVariable %_ptr_Function_float Function
+%color = OpVariable %_ptr_Function_v4float Function
+%param = OpVariable %_ptr_Function_v4float Function
+%23 = OpLoad %v4float %BaseColor
+OpStore %param %23
+OpStore %43 %false
+OpSelectionMerge %55 None
+OpSwitch %uint_0 %47
+%47 = OpLabel
+%48 = OpAccessChain %_ptr_Function_float %param %uint_0
+%49 = OpLoad %float %48
+%50 = OpFOrdLessThan %bool %49 %float_0
+OpSelectionMerge %52 None
+OpBranchConditional %50 %51 %52
+%51 = OpLabel
+OpStore %43 %true
+OpStore %44 %float_0
+OpBranch %55
+%52 = OpLabel
+%53 = OpAccessChain %_ptr_Function_float %param %uint_0
+%54 = OpLoad %float %53
+OpStore %43 %true
+OpStore %44 %54
+OpBranch %55
+%55 = OpLabel
+%56 = OpLoad %float %44
+OpStore %45 %56
+%24 = OpLoad %float %45
+%25 = OpCompositeConstruct %v4float %24 %24 %24 %24
+OpStore %color %25
+%26 = OpLoad %v4float %color
+OpStore %gl_FragColor %26
+OpReturn
+OpFunctionEnd
+)";
+
+  // The early return case must be handled by merge-return first.
+  AddPass<MergeReturnPass>();
+  AddPass<InlineExhaustivePass>();
+  RunAndCheck(predefs + before + foo, predefs + after + fooMergeReturn);
+}
+
+TEST_F(InlineTest, EarlyReturnNotAppearingLastInFunctionInlined) {
+  // Example from https://github.com/KhronosGroup/SPIRV-Tools/issues/755
+  //
+  // Original example is derived from:
+  //
+  // #version 450
+  //
+  // float foo() {
+  //     if (true) {
+  //     }
+  // }
+  //
+  // void main() { foo(); }
+  //
+  // But the order of basic blocks in foo is changed so that the return
+  // block is listed second-last.  There is only one return in the callee
+  // but it does not appear last.
+
+  const std::string predefs =
+      R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main"
+OpSource GLSL 450
+OpName %main "main"
+OpName %foo_ "foo("
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+)";
+
+  const std::string foo =
+      R"(%foo_ = OpFunction %void None %4
+%7 = OpLabel
+OpSelectionMerge %8 None
+OpBranchConditional %true %9 %8
+%8 = OpLabel
+OpReturn
+%9 = OpLabel
+OpBranch %8
+OpFunctionEnd
+)";
+
+  const std::string fooMergeReturn =
+      R"(%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%false = OpConstantFalse %bool
+%_ptr_Function_bool = OpTypePointer Function %bool
+%foo_ = OpFunction %void None %4
+%7 = OpLabel
+%18 = OpVariable %_ptr_Function_bool Function %false
+OpSelectionMerge %12 None
+OpSwitch %uint_0 %13
+%13 = OpLabel
+OpSelectionMerge %8 None
+OpBranchConditional %true %9 %8
+%8 = OpLabel
+OpStore %18 %true
+OpBranch %12
+%9 = OpLabel
+OpBranch %8
+%12 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string before =
+      R"(%main = OpFunction %void None %4
+%10 = OpLabel
+%11 = OpFunctionCall %void %foo_
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(%main = OpFunction %void None %4
+%10 = OpLabel
+%19 = OpVariable %_ptr_Function_bool Function %false
+OpStore %19 %false
+OpSelectionMerge %24 None
+OpSwitch %uint_0 %21
+%21 = OpLabel
+OpSelectionMerge %22 None
+OpBranchConditional %true %23 %22
+%22 = OpLabel
+OpStore %19 %true
+OpBranch %24
+%23 = OpLabel
+OpBranch %22
+%24 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  // The early return case must be handled by merge-return first.
+  AddPass<MergeReturnPass>();
+  AddPass<InlineExhaustivePass>();
+  RunAndCheck(predefs + foo + before, predefs + fooMergeReturn + after);
+}
+
+TEST_F(InlineTest, CalleeWithSingleReturnNeedsSingleTripLoopWrapper) {
+  // The case from https://github.com/KhronosGroup/SPIRV-Tools/issues/2018
+  //
+  // The callee has a single return, but needs single-trip loop wrapper
+  // to be inlined because the return is in a selection structure.
+
+  const std::string predefs =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %_GLF_color
+OpExecutionMode %main OriginUpperLeft
+OpSource ESSL 310
+OpName %main "main"
+OpName %f_ "f("
+OpName %i "i"
+OpName %_GLF_color "_GLF_color"
+OpDecorate %_GLF_color Location 0
+%void = OpTypeVoid
+%7 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%9 = OpTypeFunction %float
+%float_1 = OpConstant %float 1
+%bool = OpTypeBool
+%false = OpConstantFalse %bool
+%true = OpConstantTrue %bool
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_GLF_color = OpVariable %_ptr_Output_v4float Output
+%float_0 = OpConstant %float 0
+%21 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%22 = OpConstantComposite %v4float %float_0 %float_1 %float_0 %float_1
+)";
+
+  const std::string new_predefs =
+      R"(%_ptr_Function_float = OpTypePointer Function %float
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%_ptr_Function_bool = OpTypePointer Function %bool
+)";
+
+  const std::string main_before =
+      R"(%main = OpFunction %void None %7
+%23 = OpLabel
+%i = OpVariable %_ptr_Function_int Function
+OpStore %i %int_0
+OpBranch %24
+%24 = OpLabel
+OpLoopMerge %25 %26 None
+OpBranch %27
+%27 = OpLabel
+%28 = OpLoad %int %i
+%29 = OpSLessThan %bool %28 %int_1
+OpBranchConditional %29 %30 %25
+%30 = OpLabel
+OpStore %_GLF_color %21
+%31 = OpFunctionCall %float %f_
+OpBranch %26
+%26 = OpLabel
+%32 = OpLoad %int %i
+%33 = OpIAdd %int %32 %int_1
+OpStore %i %33
+OpBranch %24
+%25 = OpLabel
+OpStore %_GLF_color %22
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string main_after =
+      R"(%main = OpFunction %void None %7
+%23 = OpLabel
+%46 = OpVariable %_ptr_Function_bool Function %false
+%47 = OpVariable %_ptr_Function_float Function
+%48 = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+OpStore %i %int_0
+OpBranch %24
+%24 = OpLabel
+OpLoopMerge %25 %26 None
+OpBranch %27
+%27 = OpLabel
+%28 = OpLoad %int %i
+%29 = OpSLessThan %bool %28 %int_1
+OpBranchConditional %29 %30 %25
+%30 = OpLabel
+OpStore %_GLF_color %21
+OpStore %46 %false
+OpSelectionMerge %53 None
+OpSwitch %uint_0 %50
+%50 = OpLabel
+OpSelectionMerge %52 None
+OpBranchConditional %true %51 %52
+%51 = OpLabel
+OpStore %46 %true
+OpStore %47 %float_1
+OpBranch %53
+%52 = OpLabel
+OpStore %46 %true
+OpStore %47 %float_1
+OpBranch %53
+%53 = OpLabel
+%54 = OpLoad %float %47
+OpStore %48 %54
+%31 = OpLoad %float %48
+OpBranch %26
+%26 = OpLabel
+%32 = OpLoad %int %i
+%33 = OpIAdd %int %32 %int_1
+OpStore %i %33
+OpBranch %24
+%25 = OpLabel
+OpStore %_GLF_color %22
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string callee =
+      R"(%f_ = OpFunction %float None %9
+%34 = OpLabel
+OpSelectionMerge %35 None
+OpBranchConditional %true %36 %35
+%36 = OpLabel
+OpReturnValue %float_1
+%35 = OpLabel
+OpReturnValue %float_1
+OpFunctionEnd
+)";
+
+  const std::string calleeMergeReturn =
+      R"(%f_ = OpFunction %float None %9
+%34 = OpLabel
+%45 = OpVariable %_ptr_Function_bool Function %false
+%39 = OpVariable %_ptr_Function_float Function
+OpSelectionMerge %37 None
+OpSwitch %uint_0 %41
+%41 = OpLabel
+OpSelectionMerge %35 None
+OpBranchConditional %true %36 %35
+%36 = OpLabel
+OpStore %45 %true
+OpStore %39 %float_1
+OpBranch %37
+%35 = OpLabel
+OpStore %45 %true
+OpStore %39 %float_1
+OpBranch %37
+%37 = OpLabel
+%40 = OpLoad %float %39
+OpReturnValue %40
+OpFunctionEnd
+)";
+
+  // The early return case must be handled by merge-return first.
+  AddPass<MergeReturnPass>();
+  AddPass<InlineExhaustivePass>();
+  RunAndCheck(predefs + main_before + callee,
+              predefs + new_predefs + main_after + calleeMergeReturn);
+}
+
+TEST_F(InlineTest, ForwardReferencesInPhiInlined) {
+  // The basic structure of the test case is like this:
+  //
+  // int foo() {
+  //   int result = 1;
+  //   if (true) {
+  //      result = 1;
+  //   }
+  //   return result;
+  // }
+  //
+  // void main() {
+  //  int x = foo();
+  // }
+  //
+  // but with modifications: Using Phi instead of load/store, and the
+  // return block in foo appears before the "then" block.
+
+  const std::string predefs =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main"
+OpSource GLSL 450
+OpName %main "main"
+OpName %foo_ "foo("
+OpName %x "x"
+%void = OpTypeVoid
+%6 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%8 = OpTypeFunction %int
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%int_0 = OpConstant %int 0
+%_ptr_Function_int = OpTypePointer Function %int
+)";
+
+  const std::string callee =
+      R"(%foo_ = OpFunction %int None %8
+%13 = OpLabel
+%14 = OpCopyObject %int %int_0
+OpSelectionMerge %15 None
+OpBranchConditional %true %16 %15
+%15 = OpLabel
+%17 = OpPhi %int %14 %13 %18 %16
+OpReturnValue %17
+%16 = OpLabel
+%18 = OpCopyObject %int %int_0
+OpBranch %15
+OpFunctionEnd
+)";
+
+  const std::string calleeMergeReturn =
+      R"(%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%false = OpConstantFalse %bool
+%_ptr_Function_bool = OpTypePointer Function %bool
+%foo_ = OpFunction %int None %8
+%13 = OpLabel
+%29 = OpVariable %_ptr_Function_bool Function %false
+%22 = OpVariable %_ptr_Function_int Function
+OpSelectionMerge %21 None
+OpSwitch %uint_0 %24
+%24 = OpLabel
+%14 = OpCopyObject %int %int_0
+OpSelectionMerge %15 None
+OpBranchConditional %true %16 %15
+%15 = OpLabel
+%17 = OpPhi %int %14 %24 %18 %16
+OpStore %29 %true
+OpStore %22 %17
+OpBranch %21
+%16 = OpLabel
+%18 = OpCopyObject %int %int_0
+OpBranch %15
+%21 = OpLabel
+%23 = OpLoad %int %22
+OpReturnValue %23
+OpFunctionEnd
+)";
+
+  const std::string before =
+      R"(%main = OpFunction %void None %6
+%19 = OpLabel
+%x = OpVariable %_ptr_Function_int Function
+%20 = OpFunctionCall %int %foo_
+OpStore %x %20
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(%main = OpFunction %void None %6
+%19 = OpLabel
+%30 = OpVariable %_ptr_Function_bool Function %false
+%31 = OpVariable %_ptr_Function_int Function
+%32 = OpVariable %_ptr_Function_int Function
+%x = OpVariable %_ptr_Function_int Function
+OpStore %30 %false
+OpSelectionMerge %40 None
+OpSwitch %uint_0 %34
+%34 = OpLabel
+%35 = OpCopyObject %int %int_0
+OpSelectionMerge %36 None
+OpBranchConditional %true %38 %36
+%36 = OpLabel
+%37 = OpPhi %int %35 %34 %39 %38
+OpStore %30 %true
+OpStore %31 %37
+OpBranch %40
+%38 = OpLabel
+%39 = OpCopyObject %int %int_0
+OpBranch %36
+%40 = OpLabel
+%41 = OpLoad %int %31
+OpStore %32 %41
+%20 = OpLoad %int %32
+OpStore %x %20
+OpReturn
+OpFunctionEnd
+)";
+
+  AddPass<MergeReturnPass>();
+  AddPass<InlineExhaustivePass>();
+  RunAndCheck(predefs + callee + before, predefs + calleeMergeReturn + after);
+}
+
+TEST_F(InlineTest, DebugSimple) {
+  // Check that it correctly generates DebugInlinedAt and maps it to DebugScope
+  // for the inlined function foo().
+  const std::string text = R"(
+; CHECK: [[main_name:%\d+]] = OpString "main"
+; CHECK: [[foo_name:%\d+]] = OpString "foo"
+; CHECK: [[dbg_main:%\d+]] = OpExtInst %void {{%\d+}} DebugFunction [[main_name]] {{%\d+}} {{%\d+}} 4 1 {{%\d+}} [[main_name]] FlagIsProtected|FlagIsPrivate 4 [[main:%\d+]]
+; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void {{%\d+}} DebugFunction [[foo_name]] {{%\d+}} {{%\d+}} 1 1 {{%\d+}} [[foo_name]] FlagIsProtected|FlagIsPrivate 1 [[foo:%\d+]]
+; CHECK: [[foo_bb:%\d+]] = OpExtInst %void {{%\d+}} DebugLexicalBlock {{%\d+}} 1 14 [[dbg_foo]]
+; CHECK: [[inlined_at:%\d+]] = OpExtInst %void {{%\d+}} DebugInlinedAt 4 [[dbg_main]]
+; CHECK: [[main]] = OpFunction %void None
+; CHECK: {{%\d+}} = OpExtInst %void {{%\d+}} DebugScope [[foo_bb]] [[inlined_at]]
+; CHECK: [[foo]] = OpFunction %v4float None
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %3 %4
+               OpExecutionMode %main OriginUpperLeft
+          %5 = OpString "ps.hlsl"
+               OpSource HLSL 600 %5
+          %6 = OpString "float"
+  %main_name = OpString "main"
+   %foo_name = OpString "foo"
+               OpDecorate %3 Location 0
+               OpDecorate %4 Location 0
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+      %float = OpTypeFloat 32
+    %float_1 = OpConstant %float 1
+    %v4float = OpTypeVector %float 4
+         %14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+         %18 = OpTypeFunction %void
+         %19 = OpTypeFunction %v4float
+          %3 = OpVariable %_ptr_Input_v4float Input
+          %4 = OpVariable %_ptr_Output_v4float Output
+         %20 = OpExtInst %void %1 DebugSource %5
+         %21 = OpExtInst %void %1 DebugCompilationUnit 1 4 %20 HLSL
+         %22 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 Float
+         %23 = OpExtInst %void %1 DebugTypeVector %22 4
+         %24 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23 %23
+         %25 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23
+   %dbg_main = OpExtInst %void %1 DebugFunction %main_name %24 %20 4 1 %21 %main_name FlagIsProtected|FlagIsPrivate 4 %main
+    %dbg_foo = OpExtInst %void %1 DebugFunction %foo_name %25 %20 1 1 %21 %foo_name FlagIsProtected|FlagIsPrivate 1 %foo
+         %29 = OpExtInst %void %1 DebugLexicalBlock %20 1 14 %dbg_foo
+       %main = OpFunction %void None %18
+         %30 = OpLabel
+         %31 = OpExtInst %void %1 DebugScope %dbg_main
+         %32 = OpFunctionCall %v4float %foo
+         %33 = OpLoad %v4float %3
+         %34 = OpFAdd %v4float %32 %33
+               OpStore %4 %34
+               OpReturn
+               OpFunctionEnd
+        %foo = OpFunction %v4float None %19
+         %35 = OpExtInst %void %1 DebugScope %dbg_foo
+         %36 = OpLabel
+         %37 = OpExtInst %void %1 DebugScope %29
+               OpReturnValue %14
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
+}
+
+TEST_F(InlineTest, DebugNested) {
+  // When function main() calls function zoo() and function zoo() calls
+  // function bar() and function bar() calls function foo(), check that
+  // the inline pass correctly generates DebugInlinedAt instructions
+  // for the nested function calls.
+  const std::string text = R"(
+; CHECK: [[v4f1:%\d+]] = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+; CHECK: [[v4f2:%\d+]] = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
+; CHECK: [[v4f3:%\d+]] = OpConstantComposite %v4float %float_3 %float_3 %float_3 %float_3
+; CHECK: [[color:%\d+]] = OpVariable %_ptr_Input_v4float Input
+; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 10 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 10 [[main:%\d+]]
+; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void [[ext]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 1 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 [[foo:%\d+]]
+; CHECK: [[dbg_bar:%\d+]] = OpExtInst %void [[ext]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 4 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 4 [[bar:%\d+]]
+; CHECK: [[dbg_zoo:%\d+]] = OpExtInst %void [[ext]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 7 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 7 [[zoo:%\d+]]
+; CHECK: [[inlined_to_main:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 600 [[dbg_main]]
+; CHECK: [[inlined_to_zoo:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 700 [[dbg_zoo]] [[inlined_to_main]]
+; CHECK: [[inlined_to_bar:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 300 [[dbg_bar]] [[inlined_to_zoo]]
+; CHECK: [[main]] = OpFunction %void None
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_foo]] [[inlined_to_bar]]
+; CHECK-NEXT: OpLine {{%\d+}} 100 0
+; CHECK-NEXT: OpStore {{%\d+}} [[v4f1]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_bar]] [[inlined_to_zoo]]
+; CHECK-NEXT: OpLine {{%\d+}} 300 0
+; CHECK-NEXT: [[foo_ret:%\d+]] = OpLoad %v4float
+; CHECK-NEXT: OpLine {{%\d+}} 400 0
+; CHECK-NEXT: {{%\d+}} = OpFAdd %v4float [[foo_ret]] [[v4f2]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_zoo]] [[inlined_to_main]]
+; CHECK-NEXT: OpLine {{%\d+}} 700 0
+; CHECK-NEXT: [[bar_ret:%\d+]] = OpLoad %v4float
+; CHECK-NEXT: {{%\d+}} = OpFAdd %v4float [[bar_ret]] [[v4f3]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]]
+; CHECK-NEXT: OpLine {{%\d+}} 600 0
+; CHECK-NEXT: [[zoo_ret:%\d+]] = OpLoad %v4float
+; CHECK-NEXT: [[color_val:%\d+]] = OpLoad %v4float [[color]]
+; CHECK-NEXT: {{%\d+}} = OpFAdd %v4float [[zoo_ret]] [[color_val]]
+; CHECK: [[foo]] = OpFunction %v4float None
+; CHECK: [[bar]] = OpFunction %v4float None
+; CHECK: [[zoo]] = OpFunction %v4float None
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %3 %4
+               OpExecutionMode %main OriginUpperLeft
+          %5 = OpString "ps.hlsl"
+               OpSource HLSL 600 %5
+          %6 = OpString "float"
+          %7 = OpString "main"
+          %8 = OpString "foo"
+          %9 = OpString "bar"
+         %10 = OpString "zoo"
+               OpDecorate %3 Location 0
+               OpDecorate %4 Location 0
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+      %float = OpTypeFloat 32
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+    %v4float = OpTypeVector %float 4
+         %18 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+         %19 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
+         %20 = OpConstantComposite %v4float %float_3 %float_3 %float_3 %float_3
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+         %24 = OpTypeFunction %void
+         %25 = OpTypeFunction %v4float
+          %3 = OpVariable %_ptr_Input_v4float Input
+          %4 = OpVariable %_ptr_Output_v4float Output
+         %26 = OpExtInst %void %1 DebugSource %5
+         %27 = OpExtInst %void %1 DebugCompilationUnit 1 4 %26 HLSL
+         %28 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 Float
+         %29 = OpExtInst %void %1 DebugTypeVector %28 4
+         %30 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %29 %29
+         %31 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %29
+         %32 = OpExtInst %void %1 DebugFunction %7 %30 %26 10 1 %27 %7 FlagIsProtected|FlagIsPrivate 10 %main
+         %33 = OpExtInst %void %1 DebugFunction %8 %31 %26 1 1 %27 %8 FlagIsProtected|FlagIsPrivate 1 %foo
+         %35 = OpExtInst %void %1 DebugFunction %9 %31 %26 4 1 %27 %9 FlagIsProtected|FlagIsPrivate 4 %bar
+         %37 = OpExtInst %void %1 DebugFunction %10 %31 %26 7 1 %27 %10 FlagIsProtected|FlagIsPrivate 7 %zoo
+       %main = OpFunction %void None %24
+         %39 = OpLabel
+         %40 = OpExtInst %void %1 DebugScope %32
+               OpLine %5 600 0
+         %41 = OpFunctionCall %v4float %zoo
+         %42 = OpLoad %v4float %3
+         %43 = OpFAdd %v4float %41 %42
+               OpStore %4 %43
+               OpReturn
+               OpFunctionEnd
+        %foo = OpFunction %v4float None %25
+         %44 = OpExtInst %void %1 DebugScope %33
+         %45 = OpLabel
+               OpLine %5 100 0
+               OpReturnValue %18
+               OpFunctionEnd
+               OpLine %5 200 0
+        %bar = OpFunction %v4float None %25
+         %46 = OpExtInst %void %1 DebugScope %35
+         %47 = OpLabel
+               OpLine %5 300 0
+         %48 = OpFunctionCall %v4float %foo
+               OpLine %5 400 0
+         %49 = OpFAdd %v4float %48 %19
+               OpLine %5 500 0
+               OpReturnValue %49
+               OpFunctionEnd
+        %zoo = OpFunction %v4float None %25
+         %50 = OpExtInst %void %1 DebugScope %37
+         %51 = OpLabel
+               OpLine %5 700 0
+         %52 = OpFunctionCall %v4float %bar
+         %53 = OpFAdd %v4float %52 %20
+               OpReturnValue %53
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
+}
+
+TEST_F(InlineTest, DebugSimpleHLSLPixelShader) {
+  const std::string text = R"(
+; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 1 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 %src_main
+; CHECK: [[lex_blk:%\d+]] = OpExtInst %void [[ext]] DebugLexicalBlock {{%\d+}} 1 47 [[dbg_main]]
+; CHECK: %main = OpFunction %void None
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare {{%\d+}} %param_var_color
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[lex_blk]]
+; CHECK: OpLine {{%\d+}} 2 10
+; CHECK: {{%\d+}} = OpLoad %v4float %param_var_color
+; CHECK: OpLine {{%\d+}} 2 3
+; CHECK: OpFunctionEnd
+; CHECK: %src_main = OpFunction %v4float None
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET
+               OpExecutionMode %main OriginUpperLeft
+          %5 = OpString "ps.hlsl"
+               OpSource HLSL 600 %5
+         %14 = OpString "#line 1 \"ps.hlsl\"
+float4 main(float4 color : COLOR) : SV_TARGET {
+  return color;
+}
+"
+         %17 = OpString "float"
+         %21 = OpString "src.main"
+         %24 = OpString "color"
+               OpName %in_var_COLOR "in.var.COLOR"
+               OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+               OpName %main "main"
+               OpName %param_var_color "param.var.color"
+               OpName %src_main "src.main"
+               OpName %color "color"
+               OpName %bb_entry "bb.entry"
+               OpDecorate %in_var_COLOR Location 0
+               OpDecorate %out_var_SV_TARGET Location 0
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+         %27 = OpTypeFunction %void
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+         %33 = OpTypeFunction %v4float %_ptr_Function_v4float
+%in_var_COLOR = OpVariable %_ptr_Input_v4float Input
+%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+         %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
+         %19 = OpExtInst %void %1 DebugTypeVector %18 4
+         %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %19 %19
+         %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %src_main
+         %25 = OpExtInst %void %1 DebugLocalVariable %24 %19 %15 1 20 %22 FlagIsLocal 0
+         %26 = OpExtInst %void %1 DebugLexicalBlock %15 1 47 %22
+       %main = OpFunction %void None %27
+         %28 = OpLabel
+%param_var_color = OpVariable %_ptr_Function_v4float Function
+         %31 = OpLoad %v4float %in_var_COLOR
+               OpStore %param_var_color %31
+         %32 = OpFunctionCall %v4float %src_main %param_var_color
+               OpStore %out_var_SV_TARGET %32
+               OpReturn
+               OpFunctionEnd
+               OpLine %5 1 1
+   %src_main = OpFunction %v4float None %33
+         %34 = OpExtInst %void %1 DebugScope %22
+      %color = OpFunctionParameter %_ptr_Function_v4float
+         %36 = OpExtInst %void %1 DebugDeclare %25 %color %13
+   %bb_entry = OpLabel
+         %38 = OpExtInst %void %1 DebugScope %26
+               OpLine %5 2 10
+         %39 = OpLoad %v4float %color
+               OpLine %5 2 3
+               OpReturnValue %39
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
+}
+
+TEST_F(InlineTest, DebugDeclareForCalleeFunctionParam) {
+  // Check that InlinePass correctly generates DebugDeclare instructions
+  // for callee function's parameters and maps them to corresponding
+  // local variables of caller function.
+  const std::string text = R"(
+; CHECK: [[add:%\d+]] = OpString "add"
+; CHECK: [[a:%\d+]] = OpString "a"
+; CHECK: [[b:%\d+]] = OpString "b"
+; CHECK: [[dbg_add:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[add]]
+; CHECK: [[dbg_a:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[a]]
+; CHECK: [[dbg_b:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[b]]
+; CHECK: [[inlinedat:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 5
+; CHECK: OpStore [[param_a:%\d+]]
+; CHECK: OpStore [[param_b:%\d+]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_add]] [[inlinedat]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare [[dbg_a]] [[param_a]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare [[dbg_b]] [[param_b]]
+
+OpCapability Shader
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "ps.hlsl"
+OpSource HLSL 600 %file_name
+%float_name = OpString "float"
+%main_name = OpString "main"
+%add_name = OpString "add"
+%a_name = OpString "a"
+%b_name = OpString "b"
+OpDecorate %in_var_COLOR Location 0
+OpDecorate %out_var_SV_TARGET Location 0
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%float = OpTypeFloat 32
+%float_1 = OpConstant %float 1
+%float_2 = OpConstant %float 2
+%v4float = OpTypeVector %float 4
+%v4f1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%v4f2 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%add_fn_type = OpTypeFunction %v4float %_ptr_Function_v4float %_ptr_Function_v4float
+%void = OpTypeVoid
+%void_fn_type = OpTypeFunction %void
+%v4f_fn_type = OpTypeFunction %v4float
+%in_var_COLOR = OpVariable %_ptr_Input_v4float 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_f = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_f 4
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
+%add_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f %dbg_v4f
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 5 1 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_add = OpExtInst %void %ext DebugFunction %add_name %add_ty %src 1 1 %cu %add_name FlagIsProtected|FlagIsPrivate 1 %add
+%dbg_a = OpExtInst %void %ext DebugLocalVariable %a_name %dbg_v4f %src 1 13 %dbg_add FlagIsLocal 0
+%dbg_b = OpExtInst %void %ext DebugLocalVariable %b_name %dbg_v4f %src 1 20 %dbg_add FlagIsLocal 1
+%add_lb = OpExtInst %void %ext DebugLexicalBlock %src 1 23 %dbg_add
+%main = OpFunction %void None %void_fn_type
+%main_bb = OpLabel
+%param_a = OpVariable %_ptr_Function_v4float Function
+%param_b = OpVariable %_ptr_Function_v4float Function
+%scope0 = OpExtInst %void %ext DebugScope %dbg_main
+OpStore %param_a %v4f1
+OpStore %param_b %v4f2
+%result = OpFunctionCall %v4float %add %param_a %param_b
+OpStore %out_var_SV_TARGET %result
+OpReturn
+OpFunctionEnd
+%add = OpFunction %v4float None %add_fn_type
+%scope1 = OpExtInst %void %ext DebugScope %dbg_add
+%a = OpFunctionParameter %_ptr_Function_v4float
+%b = OpFunctionParameter %_ptr_Function_v4float
+%decl0 = OpExtInst %void %ext DebugDeclare %dbg_a %a %null_expr
+%decl1 = OpExtInst %void %ext DebugDeclare %dbg_b %b %null_expr
+%add_bb = OpLabel
+%scope2 = OpExtInst %void %ext DebugScope %add_lb
+%a_val = OpLoad %v4float %a
+%b_val = OpLoad %v4float %b
+%res = OpFAdd %v4float %a_val %b_val
+OpReturnValue %res
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
+}
+
+TEST_F(InlineTest, DebugDeclareForCalleeLocalVar) {
+  // Check that InlinePass correctly generates DebugDeclare instructions
+  // for callee function's local variables and maps them to corresponding
+  // local variables of caller function.
+  const std::string text = R"(
+; CHECK: [[add:%\d+]] = OpString "add"
+; CHECK: [[foo:%\d+]] = OpString "foo"
+; CHECK: [[dbg_add:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[add]]
+; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[foo]] {{%\d+}} {{%\d+}} 2 2 [[dbg_add]]
+; CHECK: [[inlinedat:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 5
+
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_add]] [[inlinedat]]
+; CHECK: [[new_foo:%\d+]] = OpVariable %_ptr_Function_v4float Function
+
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_add]] [[inlinedat]]
+; CHECK: [[a_val:%\d+]] = OpLoad %v4float
+; CHECK: [[b_val:%\d+]] = OpLoad %v4float
+; CHECK: [[res:%\d+]] = OpFAdd %v4float [[a_val]] [[b_val]]
+; CHECK: OpStore [[new_foo]] [[res]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare [[dbg_foo]] [[new_foo]]
+
+OpCapability Shader
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "ps.hlsl"
+OpSource HLSL 600 %file_name
+%float_name = OpString "float"
+%main_name = OpString "main"
+%add_name = OpString "add"
+%foo_name = OpString "foo"
+OpDecorate %in_var_COLOR Location 0
+OpDecorate %out_var_SV_TARGET Location 0
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%float = OpTypeFloat 32
+%float_1 = OpConstant %float 1
+%float_2 = OpConstant %float 2
+%v4float = OpTypeVector %float 4
+%v4f1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%v4f2 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%add_fn_type = OpTypeFunction %v4float %_ptr_Function_v4float %_ptr_Function_v4float
+%void = OpTypeVoid
+%void_fn_type = OpTypeFunction %void
+%v4f_fn_type = OpTypeFunction %v4float
+%in_var_COLOR = OpVariable %_ptr_Input_v4float 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_f = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_f 4
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
+%add_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f %dbg_v4f
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 5 1 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_add = OpExtInst %void %ext DebugFunction %add_name %add_ty %src 1 1 %cu %add_name FlagIsProtected|FlagIsPrivate 1 %add
+%dbg_foo = OpExtInst %void %ext DebugLocalVariable %foo_name %dbg_v4f %src 2 2 %dbg_add FlagIsLocal
+%main = OpFunction %void None %void_fn_type
+%main_bb = OpLabel
+%param_a = OpVariable %_ptr_Function_v4float Function
+%param_b = OpVariable %_ptr_Function_v4float Function
+%scope0 = OpExtInst %void %ext DebugScope %dbg_main
+OpStore %param_a %v4f1
+OpStore %param_b %v4f2
+%result = OpFunctionCall %v4float %add %param_a %param_b
+OpStore %out_var_SV_TARGET %result
+OpReturn
+OpFunctionEnd
+%add = OpFunction %v4float None %add_fn_type
+%scope1 = OpExtInst %void %ext DebugScope %dbg_add
+%a = OpFunctionParameter %_ptr_Function_v4float
+%b = OpFunctionParameter %_ptr_Function_v4float
+%add_bb = OpLabel
+%foo = OpVariable %_ptr_Function_v4float Function
+%a_val = OpLoad %v4float %a
+%b_val = OpLoad %v4float %b
+%res = OpFAdd %v4float %a_val %b_val
+OpStore %foo %res
+%decl = OpExtInst %void %ext DebugDeclare %dbg_foo %foo %null_expr
+%foo_val = OpLoad %v4float %foo
+OpReturnValue %foo_val
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
+}
+
+TEST_F(InlineTest, DebugDeclareMultiple) {
+  // Check that InlinePass correctly generates DebugDeclare instructions
+  // for callee function's parameters and maps them to corresponding
+  // local variables of caller function.
+  const std::string text = R"(
+; CHECK: [[add:%\d+]] = OpString "add"
+; CHECK: [[a:%\d+]] = OpString "a"
+; CHECK: [[b:%\d+]] = OpString "b"
+; CHECK: [[dbg_add:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[add]]
+; CHECK: [[dbg_a:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[a]]
+; CHECK: [[dbg_b:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[b]]
+; CHECK: OpFunction
+; CHECK-NOT: OpFunctionEnd
+; CHECK: OpStore [[param_a:%\d+]]
+; CHECK: OpStore [[param_b:%\d+]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_add]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare [[dbg_a]] [[param_a]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare [[dbg_b]] [[param_b]]
+; CHECK: [[a_val:%\d+]] = OpLoad %v4float [[param_a]]
+; CHECK: OpStore [[foo:%\d+]] [[a_val]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugValue [[dbg_a]] [[foo]]
+
+OpCapability Shader
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "ps.hlsl"
+OpSource HLSL 600 %file_name
+%float_name = OpString "float"
+%main_name = OpString "main"
+%add_name = OpString "add"
+%a_name = OpString "a"
+%b_name = OpString "b"
+OpDecorate %in_var_COLOR Location 0
+OpDecorate %out_var_SV_TARGET Location 0
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%float = OpTypeFloat 32
+%float_1 = OpConstant %float 1
+%float_2 = OpConstant %float 2
+%v4float = OpTypeVector %float 4
+%v4f1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%v4f2 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%add_fn_type = OpTypeFunction %v4float %_ptr_Function_v4float %_ptr_Function_v4float
+%void = OpTypeVoid
+%void_fn_type = OpTypeFunction %void
+%v4f_fn_type = OpTypeFunction %v4float
+%in_var_COLOR = OpVariable %_ptr_Input_v4float 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_f = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_f 4
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
+%add_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f %dbg_v4f
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 5 1 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_add = OpExtInst %void %ext DebugFunction %add_name %add_ty %src 1 1 %cu %add_name FlagIsProtected|FlagIsPrivate 1 %add
+%dbg_a = OpExtInst %void %ext DebugLocalVariable %a_name %dbg_v4f %src 1 13 %dbg_add FlagIsLocal 0
+%dbg_b = OpExtInst %void %ext DebugLocalVariable %b_name %dbg_v4f %src 1 20 %dbg_add FlagIsLocal 1
+%main = OpFunction %void None %void_fn_type
+%main_bb = OpLabel
+%param_a = OpVariable %_ptr_Function_v4float Function
+%param_b = OpVariable %_ptr_Function_v4float Function
+%scope0 = OpExtInst %void %ext DebugScope %dbg_main
+OpStore %param_a %v4f1
+OpStore %param_b %v4f2
+%result = OpFunctionCall %v4float %add %param_a %param_b
+OpStore %out_var_SV_TARGET %result
+OpReturn
+OpFunctionEnd
+%add = OpFunction %v4float None %add_fn_type
+%scope1 = OpExtInst %void %ext DebugScope %dbg_add
+%a = OpFunctionParameter %_ptr_Function_v4float
+%b = OpFunctionParameter %_ptr_Function_v4float
+%decl0 = OpExtInst %void %ext DebugDeclare %dbg_a %a %null_expr
+%add_bb = OpLabel
+%decl1 = OpExtInst %void %ext DebugDeclare %dbg_b %b %null_expr
+%foo = OpVariable %_ptr_Function_v4float Function
+%a_val = OpLoad %v4float %a
+OpStore %foo %a_val
+%dbg_val = OpExtInst %void %ext DebugValue %dbg_a %foo %null_expr
+%b_val = OpLoad %v4float %b
+%res = OpFAdd %v4float %a_val %b_val
+OpReturnValue %res
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
+}
+
+TEST_F(InlineTest, DebugValueForFunctionCallReturn) {
+  // Check that InlinePass correctly generates DebugValue instruction
+  // for function call's return value and maps it to a corresponding
+  // value in the caller function.
+  const std::string text = R"(
+; CHECK: [[main:%\d+]] = OpString "main"
+; CHECK: [[add:%\d+]] = OpString "add"
+; CHECK: [[result:%\d+]] = OpString "result"
+; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[main]]
+; CHECK: [[dbg_add:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[add]]
+; CHECK: [[dbg_result:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[result]] {{%\d+}} {{%\d+}} 6 2 [[dbg_main]]
+; CHECK: [[inlinedat:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 5
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_add]] [[inlinedat]]
+; CHECK: [[a_val:%\d+]] = OpLoad %v4float
+; CHECK: [[b_val:%\d+]] = OpLoad %v4float
+; CHECK: [[res:%\d+]] = OpFAdd %v4float [[a_val]] [[b_val]]
+; CHECK: OpStore [[new_result:%\d+]] [[res]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]]
+; CHECK: [[result_val:%\d+]] = OpLoad %v4float [[new_result]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugValue [[dbg_result]] [[result_val]]
+
+OpCapability Shader
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "ps.hlsl"
+OpSource HLSL 600 %file_name
+%float_name = OpString "float"
+%main_name = OpString "main"
+%add_name = OpString "add"
+%result_name = OpString "result"
+OpDecorate %in_var_COLOR Location 0
+OpDecorate %out_var_SV_TARGET Location 0
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%float = OpTypeFloat 32
+%float_1 = OpConstant %float 1
+%float_2 = OpConstant %float 2
+%v4float = OpTypeVector %float 4
+%v4f1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%v4f2 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%add_fn_type = OpTypeFunction %v4float %_ptr_Function_v4float %_ptr_Function_v4float
+%void = OpTypeVoid
+%void_fn_type = OpTypeFunction %void
+%v4f_fn_type = OpTypeFunction %v4float
+%in_var_COLOR = OpVariable %_ptr_Input_v4float 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_f = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_f 4
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
+%add_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f %dbg_v4f
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 5 1 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_add = OpExtInst %void %ext DebugFunction %add_name %add_ty %src 1 1 %cu %add_name FlagIsProtected|FlagIsPrivate 1 %add
+%dbg_result = OpExtInst %void %ext DebugLocalVariable %result_name %dbg_v4f %src 6 2 %dbg_main FlagIsLocal
+%main = OpFunction %void None %void_fn_type
+%main_bb = OpLabel
+%param_a = OpVariable %_ptr_Function_v4float Function
+%param_b = OpVariable %_ptr_Function_v4float Function
+%scope0 = OpExtInst %void %ext DebugScope %dbg_main
+OpStore %param_a %v4f1
+OpStore %param_b %v4f2
+%result = OpFunctionCall %v4float %add %param_a %param_b
+%value = OpExtInst %void %ext DebugValue %dbg_result %result %null_expr
+OpStore %out_var_SV_TARGET %result
+OpReturn
+OpFunctionEnd
+%add = OpFunction %v4float None %add_fn_type
+%scope1 = OpExtInst %void %ext DebugScope %dbg_add
+%a = OpFunctionParameter %_ptr_Function_v4float
+%b = OpFunctionParameter %_ptr_Function_v4float
+%add_bb = OpLabel
+%a_val = OpLoad %v4float %a
+%b_val = OpLoad %v4float %b
+%res = OpFAdd %v4float %a_val %b_val
+OpReturnValue %res
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
+}
+
+TEST_F(InlineTest, NestedWithAnExistingDebugInlinedAt) {
+  // When a DebugScope instruction in a callee function already has a
+  // DebugInlinedAt information, we have to create a recursive
+  // DebugInlinedAt chain. See inlined_to_zoo and inlined_to_bar in
+  // the following code.
+  const std::string text = R"(
+; CHECK: [[main:%\d+]] = OpString "main"
+; CHECK: [[foo:%\d+]] = OpString "foo"
+; CHECK: [[bar:%\d+]] = OpString "bar"
+; CHECK: [[zoo:%\d+]] = OpString "zoo"
+; CHECK: [[v4f1:%\d+]] = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+; CHECK: [[v4f2:%\d+]] = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
+; CHECK: [[v4f3:%\d+]] = OpConstantComposite %v4float %float_3 %float_3 %float_3 %float_3
+; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[main]]
+; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void [[ext]] DebugFunction [[foo]]
+; CHECK: [[dbg_bar:%\d+]] = OpExtInst %void [[ext]] DebugFunction [[bar]]
+; CHECK: [[dbg_zoo:%\d+]] = OpExtInst %void [[ext]] DebugFunction [[zoo]]
+; CHECK: [[inlined_to_main:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 10 [[dbg_main]]
+; CHECK: [[inlined_to_zoo:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 7 [[dbg_zoo]] [[inlined_to_main]]
+; CHECK: [[inlined_to_main:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 10 [[dbg_main]]
+; CHECK: [[inlined_to_bar:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 4 [[dbg_bar]] [[inlined_to_zoo]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_foo]] [[inlined_to_bar]]
+; CHECK: OpStore [[foo_ret:%\d+]] [[v4f1]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_bar]] [[inlined_to_zoo]]
+; CHECK: [[foo_ret_val:%\d+]] = OpLoad %v4float [[foo_ret]]
+; CHECK: [[bar_ret:%\d+]] = OpFAdd %v4float [[foo_ret_val]] [[v4f2]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_zoo]] [[inlined_to_main]]
+; CHECK: [[zoo_result:%\d+]] = OpFAdd %v4float [[bar_ret]] [[v4f3]]
+; CHECK: OpStore [[zoo_ret:%\d+]] [[zoo_result]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]]
+; CHECK: [[zoo_ret_val:%\d+]] = OpLoad %v4float [[zoo_ret]]
+; CHECK: {{%\d+}} = OpFAdd %v4float [[zoo_ret_val]] {{%\d+}}
+
+OpCapability Shader
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "ps.hlsl"
+OpSource HLSL 600 %file_name
+%float_name = OpString "float"
+%main_name = OpString "main"
+%foo_name = OpString "foo"
+%bar_name = OpString "bar"
+%zoo_name = OpString "zoo"
+OpDecorate %in_var_COLOR Location 0
+OpDecorate %out_var_SV_TARGET Location 0
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%float = OpTypeFloat 32
+%float_1 = OpConstant %float 1
+%float_2 = OpConstant %float 2
+%float_3 = OpConstant %float 3
+%v4float = OpTypeVector %float 4
+%v4f1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%v4f2 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
+%v4f3 = OpConstantComposite %v4float %float_3 %float_3 %float_3 %float_3
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%void_fn_type = OpTypeFunction %void
+%v4f_fn_type = OpTypeFunction %v4float
+%in_var_COLOR = OpVariable %_ptr_Input_v4float Input
+%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_f = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_f 4
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
+%foo_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 10 1 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_foo = OpExtInst %void %ext DebugFunction %foo_name %foo_ty %src 1 1 %cu %foo_name FlagIsProtected|FlagIsPrivate 1 %foo
+%dbg_bar = OpExtInst %void %ext DebugFunction %bar_name %foo_ty %src 4 1 %cu %bar_name FlagIsProtected|FlagIsPrivate 4 %bar
+%dbg_zoo = OpExtInst %void %ext DebugFunction %zoo_name %foo_ty %src 7 1 %cu %zoo_name FlagIsProtected|FlagIsPrivate 7 %zoo
+%inlined_to_zoo = OpExtInst %void %ext DebugInlinedAt 7 %dbg_zoo
+%main = OpFunction %void None %void_fn_type
+%main_bb = OpLabel
+%scope0 = OpExtInst %void %ext DebugScope %dbg_main
+%zoo_val = OpFunctionCall %v4float %zoo
+%color = OpLoad %v4float %in_var_COLOR
+%result = OpFAdd %v4float %zoo_val %color
+OpStore %out_var_SV_TARGET %result
+OpReturn
+OpFunctionEnd
+%foo = OpFunction %v4float None %v4f_fn_type
+%scope1 = OpExtInst %void %ext DebugScope %dbg_foo
+%foo_bb = OpLabel
+OpReturnValue %v4f1
+OpFunctionEnd
+%zoo = OpFunction %v4float None %v4f_fn_type
+%scope3 = OpExtInst %void %ext DebugScope %dbg_zoo
+%zoo_bb = OpLabel
+%scope2 = OpExtInst %void %ext DebugScope %dbg_bar %inlined_to_zoo
+%foo_val = OpFunctionCall %v4float %foo
+%bar_val = OpFAdd %v4float %foo_val %v4f2
+%scope4 = OpExtInst %void %ext DebugScope %dbg_zoo
+%zoo_ret = OpFAdd %v4float %bar_val %v4f3
+OpReturnValue %zoo_ret
+OpFunctionEnd
+%bar = OpFunction %v4float None %v4f_fn_type
+%scope5 = OpExtInst %void %ext DebugScope %dbg_bar
+%bar_bb = OpLabel
+%foo_val0 = OpFunctionCall %v4float %foo
+%bar_ret = OpFAdd %v4float %foo_val0 %v4f2
+OpReturnValue %bar_ret
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Empty modules
diff --git a/third_party/SPIRV-Tools/test/opt/instruction_test.cpp b/third_party/SPIRV-Tools/test/opt/instruction_test.cpp
index 1995c5b..c5b92ef 100644
--- a/third_party/SPIRV-Tools/test/opt/instruction_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/instruction_test.cpp
@@ -29,8 +29,8 @@
 namespace opt {
 namespace {
 
-using spvtest::MakeInstruction;
 using ::testing::Eq;
+using spvtest::MakeInstruction;
 using DescriptorTypeTest = PassTest<::testing::Test>;
 using OpaqueTypeTest = PassTest<::testing::Test>;
 using GetBaseTest = PassTest<::testing::Test>;
@@ -74,6 +74,18 @@
   EXPECT_EQ("abcde", operand.AsString());
 }
 
+TEST(InstructionTest, OperandAsLiteralUint64_32bits) {
+  Operand::OperandData words{0x1234};
+  Operand operand(SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, std::move(words));
+  EXPECT_EQ(uint64_t(0x1234), operand.AsLiteralUint64());
+}
+
+TEST(InstructionTest, OperandAsLiteralUint64_64bits) {
+  Operand::OperandData words{0x1234, 0x89ab};
+  Operand operand(SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, std::move(words));
+  EXPECT_EQ((uint64_t(0x89ab) << 32 | 0x1234), operand.AsLiteralUint64());
+}
+
 // The words for an OpTypeInt for 32-bit signed integer resulting in Id 44.
 uint32_t kSampleInstructionWords[] = {(4 << 16) | uint32_t(SpvOpTypeInt), 44,
                                       32, 1};
@@ -327,6 +339,7 @@
           %3 = OpVariable %8 UniformConstant
           %2 = OpFunction %4 None %5
           %9 = OpLabel
+         %10 = OpCopyObject %8 %3
                OpReturn
                OpFunctionEnd
 )";
@@ -341,7 +354,10 @@
   EXPECT_FALSE(type->IsVulkanUniformBuffer());
 
   Instruction* variable = context->get_def_use_mgr()->GetDef(3);
-  EXPECT_FALSE(variable->IsReadOnlyVariable());
+  EXPECT_FALSE(variable->IsReadOnlyPointer());
+
+  Instruction* object_copy = context->get_def_use_mgr()->GetDef(10);
+  EXPECT_FALSE(object_copy->IsReadOnlyPointer());
 }
 
 TEST_F(DescriptorTypeTest, SampledImage) {
@@ -363,6 +379,7 @@
           %3 = OpVariable %8 UniformConstant
           %2 = OpFunction %4 None %5
           %9 = OpLabel
+         %10 = OpCopyObject %8 %3
                OpReturn
                OpFunctionEnd
 )";
@@ -377,7 +394,10 @@
   EXPECT_FALSE(type->IsVulkanUniformBuffer());
 
   Instruction* variable = context->get_def_use_mgr()->GetDef(3);
-  EXPECT_TRUE(variable->IsReadOnlyVariable());
+  EXPECT_TRUE(variable->IsReadOnlyPointer());
+
+  Instruction* object_copy = context->get_def_use_mgr()->GetDef(10);
+  EXPECT_TRUE(object_copy->IsReadOnlyPointer());
 }
 
 TEST_F(DescriptorTypeTest, StorageTexelBuffer) {
@@ -399,6 +419,7 @@
           %3 = OpVariable %8 UniformConstant
           %2 = OpFunction %4 None %5
           %9 = OpLabel
+         %10 = OpCopyObject %8 %3
                OpReturn
                OpFunctionEnd
 )";
@@ -413,7 +434,10 @@
   EXPECT_FALSE(type->IsVulkanUniformBuffer());
 
   Instruction* variable = context->get_def_use_mgr()->GetDef(3);
-  EXPECT_FALSE(variable->IsReadOnlyVariable());
+  EXPECT_FALSE(variable->IsReadOnlyPointer());
+
+  Instruction* object_copy = context->get_def_use_mgr()->GetDef(10);
+  EXPECT_FALSE(object_copy->IsReadOnlyPointer());
 }
 
 TEST_F(DescriptorTypeTest, StorageBuffer) {
@@ -438,6 +462,7 @@
           %3 = OpVariable %10 Uniform
           %2 = OpFunction %4 None %5
          %11 = OpLabel
+         %12 = OpCopyObject %8 %3
                OpReturn
                OpFunctionEnd
 )";
@@ -452,7 +477,10 @@
   EXPECT_FALSE(type->IsVulkanUniformBuffer());
 
   Instruction* variable = context->get_def_use_mgr()->GetDef(3);
-  EXPECT_FALSE(variable->IsReadOnlyVariable());
+  EXPECT_FALSE(variable->IsReadOnlyPointer());
+
+  Instruction* object_copy = context->get_def_use_mgr()->GetDef(12);
+  EXPECT_FALSE(object_copy->IsReadOnlyPointer());
 }
 
 TEST_F(DescriptorTypeTest, UniformBuffer) {
@@ -477,6 +505,7 @@
           %3 = OpVariable %10 Uniform
           %2 = OpFunction %4 None %5
          %11 = OpLabel
+         %12 = OpCopyObject %10 %3
                OpReturn
                OpFunctionEnd
 )";
@@ -491,7 +520,10 @@
   EXPECT_TRUE(type->IsVulkanUniformBuffer());
 
   Instruction* variable = context->get_def_use_mgr()->GetDef(3);
-  EXPECT_TRUE(variable->IsReadOnlyVariable());
+  EXPECT_TRUE(variable->IsReadOnlyPointer());
+
+  Instruction* object_copy = context->get_def_use_mgr()->GetDef(12);
+  EXPECT_TRUE(object_copy->IsReadOnlyPointer());
 }
 
 TEST_F(DescriptorTypeTest, NonWritableIsReadOnly) {
@@ -517,6 +549,7 @@
           %3 = OpVariable %10 Uniform
           %2 = OpFunction %4 None %5
          %11 = OpLabel
+         %12 = OpCopyObject %8 %3
                OpReturn
                OpFunctionEnd
 )";
@@ -524,7 +557,107 @@
   std::unique_ptr<IRContext> context =
       BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
   Instruction* variable = context->get_def_use_mgr()->GetDef(3);
-  EXPECT_TRUE(variable->IsReadOnlyVariable());
+  EXPECT_TRUE(variable->IsReadOnlyPointer());
+
+  // This demonstrates that the check for whether a pointer is read-only is not
+  // precise: copying a NonWritable-decorated variable can yield a pointer that
+  // the check does not regard as read-only.
+  Instruction* object_copy = context->get_def_use_mgr()->GetDef(12);
+  EXPECT_FALSE(object_copy->IsReadOnlyPointer());
+}
+
+TEST_F(DescriptorTypeTest, AccessChainIntoReadOnlyStructIsReadOnly) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 320
+               OpMemberDecorate %3 0 Offset 0
+               OpMemberDecorate %3 1 Offset 4
+               OpDecorate %3 Block
+          %4 = OpTypeVoid
+          %5 = OpTypeFunction %4
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFloat 32
+          %3 = OpTypeStruct %6 %8
+          %9 = OpTypePointer PushConstant %3
+         %10 = OpVariable %9 PushConstant
+         %11 = OpConstant %6 0
+         %12 = OpTypePointer PushConstant %6
+         %13 = OpConstant %6 1
+         %14 = OpTypePointer PushConstant %8
+          %2 = OpFunction %4 None %5
+         %15 = OpLabel
+         %16 = OpVariable %7 Function
+         %17 = OpAccessChain %12 %10 %11
+         %18 = OpAccessChain %14 %10 %13
+               OpReturn
+               OpFunctionEnd
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
+
+  Instruction* push_constant_struct_variable =
+      context->get_def_use_mgr()->GetDef(10);
+  EXPECT_TRUE(push_constant_struct_variable->IsReadOnlyPointer());
+
+  Instruction* push_constant_struct_field_0 =
+      context->get_def_use_mgr()->GetDef(17);
+  EXPECT_TRUE(push_constant_struct_field_0->IsReadOnlyPointer());
+
+  Instruction* push_constant_struct_field_1 =
+      context->get_def_use_mgr()->GetDef(18);
+  EXPECT_TRUE(push_constant_struct_field_1->IsReadOnlyPointer());
+}
+
+TEST_F(DescriptorTypeTest, ReadOnlyPointerParameter) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 320
+               OpMemberDecorate %3 0 Offset 0
+               OpMemberDecorate %3 1 Offset 4
+               OpDecorate %3 Block
+          %4 = OpTypeVoid
+          %5 = OpTypeFunction %4
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFloat 32
+          %3 = OpTypeStruct %6 %8
+          %9 = OpTypePointer PushConstant %3
+         %10 = OpVariable %9 PushConstant
+         %11 = OpConstant %6 0
+         %12 = OpTypePointer PushConstant %6
+         %13 = OpConstant %6 1
+         %14 = OpTypePointer PushConstant %8
+         %15 = OpTypeFunction %4 %9
+          %2 = OpFunction %4 None %5
+         %16 = OpLabel
+         %17 = OpVariable %7 Function
+         %18 = OpAccessChain %12 %10 %11
+         %19 = OpAccessChain %14 %10 %13
+               OpReturn
+               OpFunctionEnd
+         %20 = OpFunction %4 None %15
+         %21 = OpFunctionParameter %9
+         %22 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
+
+  Instruction* push_constant_struct_parameter =
+      context->get_def_use_mgr()->GetDef(21);
+  EXPECT_TRUE(push_constant_struct_parameter->IsReadOnlyPointer());
 }
 
 TEST_F(OpaqueTypeTest, BaseOpaqueTypesShader) {
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 d5710fc..e72561c 100644
--- a/third_party/SPIRV-Tools/test/opt/ir_context_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/ir_context_test.cpp
@@ -19,6 +19,7 @@
 #include <string>
 #include <utility>
 
+#include "OpenCLDebugInfo100.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/pass.h"
@@ -372,6 +373,123 @@
   EXPECT_TRUE(context->annotations().empty());
 }
 
+TEST_F(IRContextTest, KillFunctionFromDebugFunction) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+          %3 = OpString "ps.hlsl"
+          %4 = OpString "foo"
+               OpSource HLSL 600
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+          %7 = OpExtInst %void %1 DebugSource %3
+          %8 = OpExtInst %void %1 DebugCompilationUnit 1 4 %7 HLSL
+          %9 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void
+         %10 = OpExtInst %void %1 DebugFunction %4 %9 %7 1 1 %8 %4 FlagIsProtected|FlagIsPrivate 1 %11
+          %2 = OpFunction %void None %6
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %11 = OpFunction %void None %6
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
+
+  // Delete the second variable.
+  context->KillDef(11);
+
+  // Get DebugInfoNone id.
+  uint32_t debug_info_none_id = 0;
+  for (auto it = context->ext_inst_debuginfo_begin();
+       it != context->ext_inst_debuginfo_end(); ++it) {
+    if (it->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
+      debug_info_none_id = it->result_id();
+    }
+  }
+  EXPECT_NE(0, debug_info_none_id);
+
+  // Check the Function operand of DebugFunction is DebugInfoNone.
+  const uint32_t kDebugFunctionOperandFunctionIndex = 13;
+  bool checked = false;
+  for (auto it = context->ext_inst_debuginfo_begin();
+       it != context->ext_inst_debuginfo_end(); ++it) {
+    if (it->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) {
+      EXPECT_FALSE(checked);
+      EXPECT_EQ(it->GetOperand(kDebugFunctionOperandFunctionIndex).words[0],
+                debug_info_none_id);
+      checked = true;
+    }
+  }
+  EXPECT_TRUE(checked);
+}
+
+TEST_F(IRContextTest, KillVariableFromDebugGlobalVariable) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+          %3 = OpString "ps.hlsl"
+          %4 = OpString "foo"
+          %5 = OpString "int"
+               OpSource HLSL 600
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+%_ptr_Private_uint = OpTypePointer Private %uint
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %11 = OpVariable %_ptr_Private_uint Private
+         %12 = OpExtInst %void %1 DebugSource %3
+         %13 = OpExtInst %void %1 DebugCompilationUnit 1 4 %12 HLSL
+         %14 = OpExtInst %void %1 DebugTypeBasic %5 %uint_32 Signed
+         %15 = OpExtInst %void %1 DebugGlobalVariable %4 %14 %12 1 12 %13 %4 %11 FlagIsDefinition
+          %2 = OpFunction %void None %10
+         %16 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
+
+  // Delete the second variable.
+  context->KillDef(11);
+
+  // Get DebugInfoNone id.
+  uint32_t debug_info_none_id = 0;
+  for (auto it = context->ext_inst_debuginfo_begin();
+       it != context->ext_inst_debuginfo_end(); ++it) {
+    if (it->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
+      debug_info_none_id = it->result_id();
+    }
+  }
+  EXPECT_NE(0, debug_info_none_id);
+
+  // Check the Function operand of DebugFunction is DebugInfoNone.
+  const uint32_t kDebugGlobalVariableOperandVariableIndex = 11;
+  bool checked = false;
+  for (auto it = context->ext_inst_debuginfo_begin();
+       it != context->ext_inst_debuginfo_end(); ++it) {
+    if (it->GetOpenCL100DebugOpcode() ==
+        OpenCLDebugInfo100DebugGlobalVariable) {
+      EXPECT_FALSE(checked);
+      EXPECT_EQ(
+          it->GetOperand(kDebugGlobalVariableOperandVariableIndex).words[0],
+          debug_info_none_id);
+      checked = true;
+    }
+  }
+  EXPECT_TRUE(checked);
+}
+
 TEST_F(IRContextTest, BasicVisitFromEntryPoint) {
   // Make sure we visit the entry point, and the function it calls.
   // Do not visit Dead or Exported.
diff --git a/third_party/SPIRV-Tools/test/opt/ir_loader_test.cpp b/third_party/SPIRV-Tools/test/opt/ir_loader_test.cpp
index 50e3a08..2104492 100644
--- a/third_party/SPIRV-Tools/test/opt/ir_loader_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/ir_loader_test.cpp
@@ -948,6 +948,42 @@
   EXPECT_EQ(text, disassembled_text);
 }
 
+TEST(IrBuilder, DebugInfoForTerminationInsts) {
+  // Check that DebugScope instructions for termination instructions are
+  // preserved.
+  DoRoundTripCheck(R"(OpCapability Shader
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+%3 = OpString "simple_vs.hlsl"
+OpSource HLSL 600 %3
+OpName %main "main"
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%6 = OpExtInst %void %1 DebugSource %3
+%7 = OpExtInst %void %1 DebugCompilationUnit 2 4 %6 HLSL
+%main = OpFunction %void None %5
+%8 = OpLabel
+%20 = OpExtInst %void %1 DebugScope %7
+OpBranch %10
+%21 = OpExtInst %void %1 DebugNoScope
+%10 = OpLabel
+%22 = OpExtInst %void %1 DebugScope %7
+OpKill
+%23 = OpExtInst %void %1 DebugNoScope
+%14 = OpLabel
+%24 = OpExtInst %void %1 DebugScope %7
+OpUnreachable
+%25 = OpExtInst %void %1 DebugNoScope
+%17 = OpLabel
+%26 = OpExtInst %void %1 DebugScope %7
+OpReturn
+%27 = OpExtInst %void %1 DebugNoScope
+OpFunctionEnd
+)");
+}
+
 TEST(IrBuilder, LocalGlobalVariables) {
   // #version 310 es
   //
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 7afbb4c..d29a554 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
@@ -1998,6 +1998,32 @@
   EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
 }
 
+TEST_F(LocalSSAElimTest, OpConstantNull) {
+  const std::string text = R"(
+OpCapability Addresses
+OpCapability Kernel
+OpCapability Int64
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %4 "A"
+OpSource OpenCL_C 200000
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeInt 32 0
+%11 = OpTypePointer CrossWorkgroup %6
+%16 = OpConstantNull %11
+%20 = OpConstant %6 269484031
+%4 = OpFunction %2 None %3
+%17 = OpLabel
+%18 = OpLoad %6 %16 Aligned 536870912
+%19 = OpBitwiseXor %6 %18 %20
+OpStore %16 %19 Aligned 536870912
+OpReturn
+OpFunctionEnd
+  )";
+
+  SinglePassRunToBinary<SSARewritePass>(text, false);
+}
+
 // 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_descriptions.cpp b/third_party/SPIRV-Tools/test/opt/loop_optimizations/loop_descriptions.cpp
index 91dbdc6..4d2f989 100644
--- a/third_party/SPIRV-Tools/test/opt/loop_optimizations/loop_descriptions.cpp
+++ b/third_party/SPIRV-Tools/test/opt/loop_optimizations/loop_descriptions.cpp
@@ -379,6 +379,43 @@
   EXPECT_EQ(loop.GetLatchBlock()->id(), 30u);
 }
 
+TEST_F(PassClassTest, UnreachableMerge) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "main"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+          %1 = OpFunction %void None %3
+          %4 = OpLabel
+               OpBranch %5
+          %5 = OpLabel
+               OpLoopMerge %6 %7 None
+               OpBranch %8
+          %8 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+               OpBranch %7
+          %7 = OpLabel
+               OpBranch %5
+          %6 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+  const Function* f = spvtest::GetFunction(module, 1);
+  LoopDescriptor ld{context.get(), f};
+
+  EXPECT_EQ(ld.NumLoops(), 1u);
+}
+
 }  // 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 d16b65c..e55a48f 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
@@ -1970,6 +1970,113 @@
   EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
 }
 
+TEST_F(MergeReturnPassTest, SingleReturnInMiddle) {
+  const std::string before =
+      R"(
+; CHECK: OpFunction
+; CHECK: OpReturn
+; CHECK-NEXT: OpFunctionEnd
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %foo_ "foo("
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+       %foo_ = OpFunction %void None %4
+          %7 = OpLabel
+               OpSelectionMerge %8 None
+               OpBranchConditional %true %9 %8
+          %8 = OpLabel
+               OpReturn
+          %9 = OpLabel
+               OpBranch %8
+               OpFunctionEnd
+       %main = OpFunction %void None %4
+         %10 = OpLabel
+         %11 = OpFunctionCall %void %foo_
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<MergeReturnPass>(before, false);
+}
+
+TEST_F(MergeReturnPassTest, PhiWithTooManyEntries) {
+  // Check that the OpPhi node has the correct number of entries.  This is
+  // checked by doing validation with the match.
+  const std::string before =
+      R"(
+; CHECK: OpLoopMerge [[merge:%\w+]]
+; CHECK: [[merge]] = OpLabel
+; CHECK-NEXT: {{%\w+}} = OpPhi %int {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %6 = OpTypeFunction %int
+       %bool = OpTypeBool
+      %int_1 = OpConstant %int 1
+      %false = OpConstantFalse %bool
+          %2 = OpFunction %void None %4
+         %10 = OpLabel
+         %11 = OpFunctionCall %int %12
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %int None %6
+         %13 = OpLabel
+               OpBranch %14
+         %14 = OpLabel
+         %15 = OpPhi %int %int_1 %13 %16 %17
+               OpLoopMerge %18 %17 None
+               OpBranch %19
+         %19 = OpLabel
+         %20 = OpUndef %bool
+               OpBranch %21
+         %21 = OpLabel
+               OpLoopMerge %22 %23 None
+               OpBranch %24
+         %24 = OpLabel
+               OpSelectionMerge %25 None
+               OpBranchConditional %20 %22 %25
+         %25 = OpLabel
+               OpReturnValue %int_1
+         %23 = OpLabel
+               OpBranch %21
+         %22 = OpLabel
+               OpSelectionMerge %26 None
+               OpBranchConditional %20 %27 %26
+         %27 = OpLabel
+               OpBranch %28
+         %28 = OpLabel
+               OpLoopMerge %29 %30 None
+               OpBranch %31
+         %31 = OpLabel
+               OpReturnValue %int_1
+         %30 = OpLabel
+               OpBranch %28
+         %29 = OpLabel
+               OpUnreachable
+         %26 = OpLabel
+               OpBranch %17
+         %17 = OpLabel
+         %16 = OpPhi %int %15 %26
+               OpBranchConditional %false %14 %18
+         %18 = OpLabel
+               OpReturnValue %16
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<MergeReturnPass>(before, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/struct_cfg_analysis_test.cpp b/third_party/SPIRV-Tools/test/opt/struct_cfg_analysis_test.cpp
index 0451a8b..2d1deec 100644
--- a/third_party/SPIRV-Tools/test/opt/struct_cfg_analysis_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/struct_cfg_analysis_test.cpp
@@ -1369,6 +1369,35 @@
   auto c = analysis.FindFuncsCalledFromContinue();
   EXPECT_THAT(c, UnorderedElementsAre(14u, 16u, 21u));
 }
+
+TEST_F(StructCFGAnalysisTest, SingleBlockLoop) {
+  const std::string text = R"(
+              OpCapability Shader
+              OpCapability Linkage
+              OpMemoryModel Logical GLSL450
+      %void = OpTypeVoid
+      %bool = OpTypeBool
+     %undef = OpUndef %bool
+   %void_fn = OpTypeFunction %void
+      %main = OpFunction %void None %void_fn
+         %2 = OpLabel
+              OpBranch %3
+         %3 = OpLabel
+              OpLoopMerge %4 %3 None
+              OpBranchConditional %undef %3 %4
+         %4 = OpLabel
+              OpReturn
+              OpFunctionEnd
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  StructuredCFGAnalysis analysis(context.get());
+
+  EXPECT_TRUE(analysis.IsInContinueConstruct(3));
+}
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/value_table_test.cpp b/third_party/SPIRV-Tools/test/opt/value_table_test.cpp
index a0942cc..76e7f73 100644
--- a/third_party/SPIRV-Tools/test/opt/value_table_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/value_table_test.cpp
@@ -684,6 +684,50 @@
   vtable.GetValueNumber(inst);
 }
 
+TEST_F(ValueTableTest, RedundantSampledImageLoad) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %gl_FragColor
+               OpExecutionMode %main OriginLowerLeft
+               OpSource GLSL 330
+               OpName %main "main"
+               OpName %tex0 "tex0"
+               OpName %gl_FragColor "gl_FragColor"
+               OpDecorate %tex0 Location 0
+               OpDecorate %tex0 DescriptorSet 0
+               OpDecorate %tex0 Binding 0
+               OpDecorate %gl_FragColor Location 0
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+          %9 = OpTypeImage %float 2D 0 0 0 1 Unknown
+         %10 = OpTypeSampledImage %9
+%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
+       %tex0 = OpVariable %_ptr_UniformConstant_10 UniformConstant
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+         %13 = OpConstantNull %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+         %14 = OpUndef %v4float
+       %main = OpFunction %void None %6
+         %15 = OpLabel
+         %16 = OpLoad %10 %tex0
+         %17 = OpImageSampleProjImplicitLod %v4float %16 %13
+         %18 = OpImageSampleProjImplicitLod %v4float %16 %13
+         %19 = OpFAdd %v4float %18 %17
+               OpStore %gl_FragColor %19
+               OpReturn
+               OpFunctionEnd
+  )";
+  auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
+  ValueNumberTable vtable(context.get());
+  Instruction* load1 = context->get_def_use_mgr()->GetDef(17);
+  Instruction* load2 = context->get_def_use_mgr()->GetDef(18);
+  EXPECT_EQ(vtable.GetValueNumber(load1), vtable.GetValueNumber(load2));
+}
+
 }  // 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 d50af28..33e52f0 100644
--- a/third_party/SPIRV-Tools/test/opt/wrap_opkill_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/wrap_opkill_test.cpp
@@ -513,6 +513,139 @@
   EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
 }
 
+TEST_F(WrapOpKillTest, SetParentBlock) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+%void = OpTypeVoid
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranch %loop
+%loop = OpLabel
+OpLoopMerge %merge %continue None
+OpBranchConditional %undef %merge %continue
+%continue = OpLabel
+%call = OpFunctionCall %void %kill_func
+OpBranch %loop
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+%kill_func = OpFunction %void None %void_fn
+%kill_entry = OpLabel
+OpKill
+OpFunctionEnd
+)";
+
+  auto result = SinglePassRunToBinary<WrapOpKill>(text, true);
+  EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
+  result = SinglePassRunToBinary<WrapOpKill>(text, true);
+  EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
+}
+
+TEST_F(WrapOpKillTest, KillInSingleBlockLoop) {
+  const std::string text = R"(
+; CHECK: OpFunction %void
+; CHECK: OpFunction %void
+; CHECK-NOT: OpKill
+; CHECK: OpFunctionCall %void [[new_kill:%\w+]]
+; CHECK-NOT: OpKill
+; CHECK: [[new_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpKill
+; CHECK-NEXT: OpFunctionEnd
+              OpCapability Shader
+              OpCapability Linkage
+              OpMemoryModel Logical GLSL450
+      %void = OpTypeVoid
+      %bool = OpTypeBool
+     %undef = OpUndef %bool
+   %void_fn = OpTypeFunction %void
+      %main = OpFunction %void None %void_fn
+%main_entry = OpLabel
+              OpBranch %loop
+      %loop = OpLabel
+      %call = OpFunctionCall %void %sub
+              OpLoopMerge %exit %loop None
+              OpBranchConditional %undef %loop %exit
+      %exit = OpLabel
+              OpReturn
+              OpFunctionEnd
+       %sub = OpFunction %void None %void_fn
+ %sub_entry = OpLabel
+              OpSelectionMerge %ret None
+              OpBranchConditional %undef %kill %ret
+      %kill = OpLabel
+              OpKill
+       %ret = OpLabel
+              OpReturn
+              OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<WrapOpKill>(text, true);
+}
+
+TEST_F(WrapOpKillTest, DebugInfoSimple) {
+  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: {{%\d+}} = OpExtInst %void [[ext:%\d+]] DebugScope
+; CHECK-NEXT: OpLine [[file:%\d+]] 100 200
+; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
+; CHECK-NEXT: {{%\d+}} = OpExtInst %void [[ext]] DebugNoScope
+; CHECK-NEXT: OpReturn
+; CHECK: [[new_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpKill
+; CHECK-NEXT: OpFunctionEnd
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+          %2 = OpString "File name"
+               OpSource GLSL 330
+               OpName %main "main"
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+          %3 = OpExtInst %void %1 DebugSource %2
+          %4 = OpExtInst %void %1 DebugCompilationUnit 0 0 %3 GLSL
+       %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
+         %16 = OpExtInst %void %1 DebugScope %4
+               OpLine %2 100 200
+               OpKill
+               OpFunctionEnd
+  )";
+
+  SinglePassRunAndMatch<WrapOpKill>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/reduce/CMakeLists.txt b/third_party/SPIRV-Tools/test/reduce/CMakeLists.txt
index b19bba4..652f0ab 100644
--- a/third_party/SPIRV-Tools/test/reduce/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/test/reduce/CMakeLists.txt
@@ -24,7 +24,8 @@
         remove_block_test.cpp
         remove_function_test.cpp
         remove_selection_test.cpp
-        remove_unreferenced_instruction_test.cpp
+        remove_unused_instruction_test.cpp
+        remove_unused_struct_member_test.cpp
         structured_loop_to_selection_test.cpp
         validation_during_reduction_test.cpp
         conditional_branch_to_simple_conditional_branch_test.cpp
diff --git a/third_party/SPIRV-Tools/test/reduce/reducer_test.cpp b/third_party/SPIRV-Tools/test/reduce/reducer_test.cpp
index 59f2803..0de5af1 100644
--- a/third_party/SPIRV-Tools/test/reduce/reducer_test.cpp
+++ b/third_party/SPIRV-Tools/test/reduce/reducer_test.cpp
@@ -16,7 +16,7 @@
 
 #include "source/opt/build_module.h"
 #include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
-#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
+#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h"
 #include "test/reduce/reduce_test_util.h"
 
 namespace spvtools {
@@ -157,35 +157,17 @@
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main" %60
+               OpEntryPoint Fragment %4 "main"
                OpExecutionMode %4 OriginUpperLeft
-               OpMemberDecorate %16 0 Offset 0
-               OpDecorate %16 Block
-               OpDecorate %18 DescriptorSet 0
-               OpDecorate %18 Binding 2
-               OpMemberDecorate %25 0 Offset 0
-               OpDecorate %25 Block
-               OpDecorate %27 DescriptorSet 0
-               OpDecorate %27 Binding 1
-               OpDecorate %60 Location 0
           %2 = OpTypeVoid
           %3 = OpTypeFunction %2
           %6 = OpTypeInt 32 1
           %9 = OpConstant %6 0
-         %16 = OpTypeStruct %6
-         %17 = OpTypePointer Uniform %16
-         %18 = OpVariable %17 Uniform
          %22 = OpTypeBool
         %100 = OpConstantTrue %22
          %24 = OpTypeFloat 32
-         %25 = OpTypeStruct %24
-         %26 = OpTypePointer Uniform %25
-         %27 = OpVariable %26 Uniform
          %31 = OpConstant %24 2
          %56 = OpConstant %6 1
-         %58 = OpTypeVector %24 4
-         %59 = OpTypePointer Output %58
-         %60 = OpVariable %59 Output
          %72 = OpUndef %24
          %74 = OpUndef %6
           %4 = OpFunction %2 None %3
@@ -218,8 +200,7 @@
         return ping_pong_interesting.IsInteresting(binary);
       });
   reducer.AddReductionPass(
-      MakeUnique<RemoveUnreferencedInstructionReductionOpportunityFinder>(
-          false));
+      MakeUnique<RemoveUnusedInstructionReductionOpportunityFinder>(false));
   reducer.AddReductionPass(
       MakeUnique<OperandToConstReductionOpportunityFinder>());
 
diff --git a/third_party/SPIRV-Tools/test/reduce/remove_unreferenced_instruction_test.cpp b/third_party/SPIRV-Tools/test/reduce/remove_unused_instruction_test.cpp
similarity index 62%
rename from third_party/SPIRV-Tools/test/reduce/remove_unreferenced_instruction_test.cpp
rename to third_party/SPIRV-Tools/test/reduce/remove_unused_instruction_test.cpp
index 3caf88c..68bc601 100644
--- a/third_party/SPIRV-Tools/test/reduce/remove_unreferenced_instruction_test.cpp
+++ b/third_party/SPIRV-Tools/test/reduce/remove_unused_instruction_test.cpp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
+#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h"
 
 #include "source/opt/build_module.h"
 #include "source/reduce/reduction_opportunity.h"
@@ -25,11 +25,11 @@
 
 const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3;
 
-TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) {
+TEST(RemoveUnusedInstructionReductionPassTest, RemoveStores) {
   // A module with some unused instructions, including some unused OpStore
   // instructions.
 
-  RemoveUnreferencedInstructionReductionOpportunityFinder finder(true);
+  RemoveUnusedInstructionReductionOpportunityFinder finder(true);
 
   const std::string original = R"(
                OpCapability Shader
@@ -223,11 +223,11 @@
   ASSERT_EQ(0, ops.size());
 }
 
-TEST(RemoveUnreferencedInstructionReductionPassTest, Referenced) {
+TEST(RemoveUnusedInstructionReductionPassTest, Referenced) {
   // A module with some unused global variables, constants, and types. Some will
   // not be removed initially because of the OpDecorate instructions.
 
-  RemoveUnreferencedInstructionReductionOpportunityFinder finder(true);
+  RemoveUnusedInstructionReductionOpportunityFinder finder(true);
 
   const std::string shader = R"(
                OpCapability Shader
@@ -375,6 +375,189 @@
   ASSERT_EQ(0, ops.size());
 }
 
+TEST(RemoveUnusedResourceVariableTest, RemoveUnusedResourceVariables) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpMemberDecorate %9 0 Offset 0
+               OpDecorate %9 Block
+               OpDecorate %11 DescriptorSet 0
+               OpDecorate %11 Binding 1
+               OpMemberDecorate %16 0 Offset 0
+               OpMemberDecorate %16 1 Offset 4
+               OpDecorate %16 Block
+               OpDecorate %18 DescriptorSet 0
+               OpDecorate %18 Binding 0
+               OpMemberDecorate %19 0 Offset 0
+               OpDecorate %19 BufferBlock
+               OpDecorate %21 DescriptorSet 1
+               OpDecorate %21 Binding 0
+               OpMemberDecorate %22 0 Offset 0
+               OpDecorate %22 Block
+               OpDecorate %29 DescriptorSet 1
+               OpDecorate %29 Binding 1
+               OpDecorate %32 DescriptorSet 1
+               OpDecorate %32 Binding 2
+               OpDecorate %32 NonReadable
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %9 = OpTypeStruct %6
+         %10 = OpTypePointer Uniform %9
+         %11 = OpVariable %10 Uniform
+         %13 = OpTypePointer Uniform %6
+         %16 = OpTypeStruct %6 %6
+         %17 = OpTypePointer Uniform %16
+         %18 = OpVariable %17 Uniform
+         %19 = OpTypeStruct %6
+         %20 = OpTypePointer Uniform %19
+         %21 = OpVariable %20 Uniform
+         %22 = OpTypeStruct %6
+         %23 = OpTypePointer PushConstant %22
+         %24 = OpVariable %23 PushConstant
+         %25 = OpTypeFloat 32
+         %26 = OpTypeImage %25 2D 0 0 0 1 Unknown
+         %27 = OpTypeSampledImage %26
+         %28 = OpTypePointer UniformConstant %27
+         %29 = OpVariable %28 UniformConstant
+         %30 = OpTypeImage %25 2D 0 0 0 2 Unknown
+         %31 = OpTypePointer UniformConstant %30
+         %32 = OpVariable %31 UniformConstant
+          %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, kReduceAssembleOption);
+
+  auto ops = RemoveUnusedInstructionReductionOpportunityFinder(true)
+                 .GetAvailableOpportunities(context.get());
+  ASSERT_EQ(7, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+  }
+
+  std::string expected_1 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpMemberDecorate %9 0 Offset 0
+               OpDecorate %9 Block
+               OpMemberDecorate %16 0 Offset 0
+               OpMemberDecorate %16 1 Offset 4
+               OpDecorate %16 Block
+               OpMemberDecorate %19 0 Offset 0
+               OpDecorate %19 BufferBlock
+               OpMemberDecorate %22 0 Offset 0
+               OpDecorate %22 Block
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %9 = OpTypeStruct %6
+         %10 = OpTypePointer Uniform %9
+         %16 = OpTypeStruct %6 %6
+         %17 = OpTypePointer Uniform %16
+         %19 = OpTypeStruct %6
+         %20 = OpTypePointer Uniform %19
+         %22 = OpTypeStruct %6
+         %23 = OpTypePointer PushConstant %22
+         %25 = OpTypeFloat 32
+         %26 = OpTypeImage %25 2D 0 0 0 1 Unknown
+         %27 = OpTypeSampledImage %26
+         %28 = OpTypePointer UniformConstant %27
+         %30 = OpTypeImage %25 2D 0 0 0 2 Unknown
+         %31 = OpTypePointer UniformConstant %30
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CheckEqual(env, expected_1, context.get());
+
+  ops = RemoveUnusedInstructionReductionOpportunityFinder(true)
+            .GetAvailableOpportunities(context.get());
+  ASSERT_EQ(6, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+  }
+
+  std::string expected_2 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpMemberDecorate %9 0 Offset 0
+               OpDecorate %9 Block
+               OpMemberDecorate %16 0 Offset 0
+               OpMemberDecorate %16 1 Offset 4
+               OpDecorate %16 Block
+               OpMemberDecorate %19 0 Offset 0
+               OpDecorate %19 BufferBlock
+               OpMemberDecorate %22 0 Offset 0
+               OpDecorate %22 Block
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %9 = OpTypeStruct %6
+         %16 = OpTypeStruct %6 %6
+         %19 = OpTypeStruct %6
+         %22 = OpTypeStruct %6
+         %25 = OpTypeFloat 32
+         %26 = OpTypeImage %25 2D 0 0 0 1 Unknown
+         %27 = OpTypeSampledImage %26
+         %30 = OpTypeImage %25 2D 0 0 0 2 Unknown
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CheckEqual(env, expected_2, context.get());
+
+  ops = RemoveUnusedInstructionReductionOpportunityFinder(true)
+            .GetAvailableOpportunities(context.get());
+  ASSERT_EQ(6, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+  }
+
+  std::string expected_3 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %25 = OpTypeFloat 32
+         %26 = OpTypeImage %25 2D 0 0 0 1 Unknown
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CheckEqual(env, expected_3, context.get());
+}
+
 }  // namespace
 }  // namespace reduce
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/reduce/remove_unused_struct_member_test.cpp b/third_party/SPIRV-Tools/test/reduce/remove_unused_struct_member_test.cpp
new file mode 100644
index 0000000..402ef2d
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/reduce/remove_unused_struct_member_test.cpp
@@ -0,0 +1,238 @@
+// 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/reduce/remove_unused_struct_member_reduction_opportunity_finder.h"
+
+#include "source/opt/build_module.h"
+#include "source/reduce/reduction_opportunity.h"
+#include "test/reduce/reduce_test_util.h"
+
+namespace spvtools {
+namespace reduce {
+namespace {
+
+TEST(RemoveUnusedStructMemberTest, RemoveOneMember) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeStruct %6 %6
+          %8 = OpTypePointer Function %7
+         %50 = OpConstant %6 0
+         %10 = OpConstant %6 1
+         %11 = OpConstant %6 2
+         %12 = OpConstantComposite %7 %10 %11
+         %13 = OpConstant %6 4
+         %14 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %9 = OpVariable %8 Function
+               OpStore %9 %12
+         %15 = OpAccessChain %14 %9 %10
+         %22 = OpInBoundsAccessChain %14 %9 %10
+         %20 = OpLoad %7 %9
+         %21 = OpCompositeExtract %6 %20 1
+         %23 = OpCompositeInsert %7 %10 %20 1
+               OpStore %15 %13
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, shader, kReduceAssembleOption);
+
+  auto ops = RemoveUnusedStructMemberReductionOpportunityFinder()
+                 .GetAvailableOpportunities(context.get());
+  ASSERT_EQ(1, ops.size());
+  ASSERT_TRUE(ops[0]->PreconditionHolds());
+  ops[0]->TryToApply();
+
+  CheckValid(env, context.get());
+
+  std::string expected = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeStruct %6
+          %8 = OpTypePointer Function %7
+         %50 = OpConstant %6 0
+         %10 = OpConstant %6 1
+         %11 = OpConstant %6 2
+         %12 = OpConstantComposite %7 %11
+         %13 = OpConstant %6 4
+         %14 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %9 = OpVariable %8 Function
+               OpStore %9 %12
+         %15 = OpAccessChain %14 %9 %50
+         %22 = OpInBoundsAccessChain %14 %9 %50
+         %20 = OpLoad %7 %9
+         %21 = OpCompositeExtract %6 %20 0
+         %23 = OpCompositeInsert %7 %10 %20 0
+               OpStore %15 %13
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CheckEqual(env, expected, context.get());
+}
+
+TEST(RemoveUnusedStructMemberTest, RemoveUniformBufferMember) {
+  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 %10 0 Offset 0
+               OpMemberDecorate %10 1 Offset 4
+               OpDecorate %10 Block
+               OpDecorate %12 DescriptorSet 0
+               OpDecorate %12 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpTypeInt 32 1
+         %10 = OpTypeStruct %9 %6
+         %11 = OpTypePointer Uniform %10
+         %12 = OpVariable %11 Uniform
+         %13 = OpConstant %9 1
+         %20 = OpConstant %9 0
+         %14 = OpTypePointer Uniform %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %15 = OpAccessChain %14 %12 %13
+         %16 = OpLoad %6 %15
+               OpStore %8 %16
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, shader, kReduceAssembleOption);
+
+  auto ops = RemoveUnusedStructMemberReductionOpportunityFinder()
+                 .GetAvailableOpportunities(context.get());
+  ASSERT_EQ(1, ops.size());
+  ASSERT_TRUE(ops[0]->PreconditionHolds());
+  ops[0]->TryToApply();
+
+  CheckValid(env, context.get());
+
+  std::string expected = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpMemberDecorate %10 0 Offset 4
+               OpDecorate %10 Block
+               OpDecorate %12 DescriptorSet 0
+               OpDecorate %12 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpTypeInt 32 1
+         %10 = OpTypeStruct %6
+         %11 = OpTypePointer Uniform %10
+         %12 = OpVariable %11 Uniform
+         %13 = OpConstant %9 1
+         %20 = OpConstant %9 0
+         %14 = OpTypePointer Uniform %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %15 = OpAccessChain %14 %12 %20
+         %16 = OpLoad %6 %15
+               OpStore %8 %16
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CheckEqual(env, expected, context.get());
+}
+
+TEST(RemoveUnusedStructMemberTest, DoNotRemoveNamedMemberRemoveOneMember) {
+  // This illustrates that naming a member is enough to prevent its removal.
+  // Removal of names is done by a different pass.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpMemberName %7 0 "someName"
+               OpMemberName %7 1 "someOtherName"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeStruct %6 %6
+          %8 = OpTypePointer Function %7
+         %50 = OpConstant %6 0
+         %10 = OpConstant %6 1
+         %11 = OpConstant %6 2
+         %12 = OpConstantComposite %7 %10 %11
+         %13 = OpConstant %6 4
+         %14 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %9 = OpVariable %8 Function
+               OpStore %9 %12
+         %15 = OpAccessChain %14 %9 %10
+         %22 = OpInBoundsAccessChain %14 %9 %10
+         %20 = OpLoad %7 %9
+         %21 = OpCompositeExtract %6 %20 1
+         %23 = OpCompositeInsert %7 %10 %20 1
+               OpStore %15 %13
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, shader, kReduceAssembleOption);
+
+  auto ops = RemoveUnusedStructMemberReductionOpportunityFinder()
+                 .GetAvailableOpportunities(context.get());
+  ASSERT_EQ(0, ops.size());
+}
+
+}  // namespace
+}  // namespace reduce
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/tools/opt/flags.py b/third_party/SPIRV-Tools/test/tools/opt/flags.py
index 2f6c0a7..f7dc64c 100644
--- a/third_party/SPIRV-Tools/test/tools/opt/flags.py
+++ b/third_party/SPIRV-Tools/test/tools/opt/flags.py
@@ -73,7 +73,10 @@
       '--remove-duplicates', '--replace-invalid-opcode', '--ssa-rewrite',
       '--scalar-replacement', '--scalar-replacement=42', '--strength-reduction',
       '--strip-debug', '--strip-reflect', '--vector-dce', '--workaround-1209',
-      '--unify-const'
+      '--unify-const', '--legalize-vector-shuffle',
+      '--split-invalid-unreachable', '--generate-webgpu-initializers',
+      '--decompose-initialized-variables', '--graphics-robust-access',
+      '--wrap-opkill', '--amd-ext-to-khr'
   ]
   expected_passes = [
       'wrap-opkill',
@@ -120,7 +123,14 @@
       'strip-reflect',
       'vector-dce',
       'workaround-1209',
-      'unify-const'
+      'unify-const',
+      'legalize-vector-shuffle',
+      'split-invalid-unreachable',
+      'generate-webgpu-initializers',
+      'decompose-initialized-variables',
+      'graphics-robust-access',
+      'wrap-opkill',
+      'amd-ext-to-khr'
   ]
   shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm')
   output = placeholder.TempFileName('output.spv')
@@ -153,9 +163,18 @@
       'eliminate-dead-code-aggressive',
       'ccp',
       'eliminate-dead-code-aggressive',
+      'loop-unroll',
+      'eliminate-dead-branches',
       'redundancy-elimination',
       'combine-access-chains',
       'simplify-instructions',
+      'scalar-replacement=100',
+      'convert-local-access-chains',
+      'eliminate-local-single-block',
+      'eliminate-local-single-store',
+      'eliminate-dead-code-aggressive',
+      'ssa-rewrite',
+      'eliminate-dead-code-aggressive',
       'vector-dce',
       'eliminate-dead-inserts',
       'eliminate-dead-branches',
diff --git a/third_party/SPIRV-Tools/test/val/val_barriers_test.cpp b/third_party/SPIRV-Tools/test/val/val_barriers_test.cpp
index fa2b153..3643883 100644
--- a/third_party/SPIRV-Tools/test/val/val_barriers_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_barriers_test.cpp
@@ -70,6 +70,7 @@
 %subgroup = OpConstant %u32 3
 %invocation = OpConstant %u32 4
 %queuefamily = OpConstant %u32 5
+%shadercall = OpConstant %u32 6
 
 %none = OpConstant %u32 0
 %acquire = OpConstant %u32 2
@@ -1586,6 +1587,79 @@
                         "CooperativeMatrixNV capability is present"));
 }
 
+TEST_F(ValidateBarriers, OpMemoryBarrierShaderCallRayGenSuccess) {
+  const std::string body =
+      "OpMemoryBarrier %shadercall %release_uniform_workgroup";
+
+  CompileSuccessfully(GenerateShaderCodeImpl(body,
+                                             // capabilities_and_extensions
+                                             R"(
+                                               OpCapability VulkanMemoryModelKHR
+                                               OpCapability RayTracingProvisionalKHR
+                                               OpExtension "SPV_KHR_vulkan_memory_model"
+                                               OpExtension "SPV_KHR_ray_tracing"
+                                             )",
+                                             // definitions
+                                             "",
+                                             // execution_model
+                                             "RayGenerationKHR",
+                                             // memory_model
+                                             "OpMemoryModel Logical VulkanKHR"),
+                      SPV_ENV_VULKAN_1_1);
+
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+}
+
+TEST_F(ValidateBarriers, OpMemoryBarrierShaderCallComputeFailure) {
+  const std::string body =
+      "OpMemoryBarrier %shadercall %release_uniform_workgroup";
+
+  CompileSuccessfully(GenerateShaderCodeImpl(body,
+                                             // capabilities_and_extensions
+                                             R"(
+                                               OpCapability VulkanMemoryModelKHR
+                                               OpExtension "SPV_KHR_vulkan_memory_model"
+                                             )",
+                                             // definitions
+                                             "",
+                                             // execution_model
+                                             "GLCompute",
+                                             // memory_model
+                                             "OpMemoryModel Logical VulkanKHR"),
+                      SPV_ENV_VULKAN_1_1);
+
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "ShaderCallKHR Memory Scope requires a ray tracing execution model"));
+}
+
+TEST_F(ValidateBarriers, OpControlBarrierShaderCallRayGenFailure) {
+  const std::string body = "OpControlBarrier %shadercall %shadercall %none";
+
+  CompileSuccessfully(GenerateShaderCodeImpl(body,
+                                             // capabilities_and_extensions
+                                             R"(
+                                               OpCapability VulkanMemoryModelKHR
+                                               OpCapability RayTracingProvisionalKHR
+                                               OpExtension "SPV_KHR_vulkan_memory_model"
+                                               OpExtension "SPV_KHR_ray_tracing"
+                                             )",
+                                             // definitions
+                                             "",
+                                             // execution_model
+                                             "RayGenerationKHR",
+                                             // memory_model
+                                             "OpMemoryModel Logical VulkanKHR"),
+                      SPV_ENV_VULKAN_1_1);
+
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("in Vulkan environment Execution Scope is limited to "
+                        "Workgroup and Subgroup"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_capability_test.cpp b/third_party/SPIRV-Tools/test/val/val_capability_test.cpp
index 098fa2f..8580818 100644
--- a/third_party/SPIRV-Tools/test/val/val_capability_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_capability_test.cpp
@@ -1229,14 +1229,18 @@
           "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
           ShaderDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
+          // Block applies to struct type.
           "OpEntryPoint Kernel %func \"compute\" \n"
-          "OpDecorate %intt Block\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %block Block\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%block = OpTypeStruct %intt\n" + std::string(kVoidFVoid),
           ShaderDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
+          // BufferBlock applies to struct type.
           "OpEntryPoint Kernel %func \"compute\" \n"
-          "OpDecorate %intt BufferBlock\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %block BufferBlock\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%block = 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_decoration_test.cpp b/third_party/SPIRV-Tools/test/val/val_decoration_test.cpp
index 256e115..204f468 100644
--- a/third_party/SPIRV-Tools/test/val/val_decoration_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_decoration_test.cpp
@@ -700,6 +700,61 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
 }
 
+TEST_F(ValidateDecorations, BlockDecoratingArrayBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpDecorate %Output Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+     %Output = OpTypeArray %float %int_3
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Block decoration on a non-struct type"));
+}
+
+TEST_F(ValidateDecorations, BlockDecoratingIntBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpDecorate %Output Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+     %Output = OpTypeInt 32 1
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Block decoration on a non-struct type"));
+}
+
 TEST_F(ValidateDecorations, BlockMissingOffsetBad) {
   std::string spirv = R"(
                OpCapability Shader
@@ -6995,6 +7050,53 @@
           "contains an array with stride 4, but with an element size of 16"));
 }
 
+TEST_F(ValidateDecorations, FunctionsWithOpGroupDecorate) {
+  std::string spirv = R"(
+                OpCapability Addresses
+                OpCapability Linkage
+                OpCapability Kernel
+                OpCapability Int8
+           %1 = OpExtInstImport "OpenCL.std"
+                OpMemoryModel Physical32 OpenCL
+                OpName %foo "foo"
+                OpName %entry "entry"
+                OpName %bar "bar"
+                OpName %entry_0 "entry"
+                OpName %k "k"
+                OpName %entry_1 "entry"
+                OpName %b "b"
+                OpDecorate %28 FuncParamAttr Zext
+          %28 = OpDecorationGroup
+                OpDecorate %k LinkageAttributes "k" Export
+                OpDecorate %foo LinkageAttributes "foo" Export
+                OpDecorate %bar LinkageAttributes "bar" Export
+                OpDecorate %b Alignment 1
+                OpGroupDecorate %28 %foo %bar
+       %uchar = OpTypeInt 8 0
+        %bool = OpTypeBool
+           %3 = OpTypeFunction %bool
+        %void = OpTypeVoid
+          %10 = OpTypeFunction %void
+ %_ptr_Function_uchar = OpTypePointer Function %uchar
+        %true = OpConstantTrue %bool
+         %foo = OpFunction %bool DontInline %3
+       %entry = OpLabel
+                OpReturnValue %true
+                OpFunctionEnd
+         %bar = OpFunction %bool DontInline %3
+     %entry_0 = OpLabel
+                OpReturnValue %true
+                OpFunctionEnd
+           %k = OpFunction %void DontInline %10
+     %entry_1 = OpLabel
+           %b = OpVariable %_ptr_Function_uchar Function
+                OpReturn
+                OpFunctionEnd
+  )";
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_ext_inst_test.cpp b/third_party/SPIRV-Tools/test/val/val_ext_inst_test.cpp
index aa73989..d8d0010 100644
--- a/third_party/SPIRV-Tools/test/val/val_ext_inst_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_ext_inst_test.cpp
@@ -889,6 +889,31 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeSizeDebugInfoNone) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "OpaqueType foo;
+main() {}
+"
+%ty_name = OpString "struct VS_OUTPUT"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header,
+                                                     "", extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeForwardReference) {
   const std::string src = R"(
 %src = OpString "simple.hlsl"
diff --git a/third_party/SPIRV-Tools/test/val/val_image_test.cpp b/third_party/SPIRV-Tools/test/val/val_image_test.cpp
index 570dd16..1a6e79c 100644
--- a/third_party/SPIRV-Tools/test/val/val_image_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_image_test.cpp
@@ -5004,6 +5004,70 @@
                         "opcodes and OpImageFetch"));
 }
 
+TEST_F(ValidateImage, GatherBiasAMDSuccess) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_1 Bias %f32_1
+)";
+
+  const std::string extra = R"(
+OpCapability ImageGatherBiasLodAMD
+OpExtension "SPV_AMD_texture_gather_bias_lod"
+)";
+  CompileSuccessfully(GenerateShaderCode(body, extra).c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateImage, GatherLodAMDSuccess) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_1 Lod %f32_1
+)";
+
+  const std::string extra = R"(
+OpCapability ImageGatherBiasLodAMD
+OpExtension "SPV_AMD_texture_gather_bias_lod"
+)";
+  CompileSuccessfully(GenerateShaderCode(body, extra).c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateImage, SparseGatherBiasAMDSuccess) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res1 = OpImageSparseGather %struct_u32_f32vec4 %simg %f32vec4_0000 %u32_1 Bias %f32_1
+)";
+
+  const std::string extra = R"(
+OpCapability ImageGatherBiasLodAMD
+OpExtension "SPV_AMD_texture_gather_bias_lod"
+)";
+  CompileSuccessfully(GenerateShaderCode(body, extra).c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateImage, SparseGatherLodAMDSuccess) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res1 = OpImageSparseGather %struct_u32_f32vec4 %simg %f32vec4_0000 %u32_1 Lod %f32_1
+)";
+
+  const std::string extra = R"(
+OpCapability ImageGatherBiasLodAMD
+OpExtension "SPV_AMD_texture_gather_bias_lod"
+)";
+  CompileSuccessfully(GenerateShaderCode(body, extra).c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 // No negative tests for ZeroExtend since we don't truly know the
 // texel format.
 
diff --git a/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp b/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp
index 718d038..2c9807d 100644
--- a/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp
+++ b/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp
@@ -145,6 +145,13 @@
   --version
                Display fuzzer version information.
 
+Supported validator options are as follows. See `spirv-val --help` for details.
+  --before-hlsl-legalization
+  --relax-block-layout
+  --relax-logical-pointer
+  --relax-struct-store
+  --scalar-block-layout
+  --skip-block-layout
 )",
       program, program, program, program);
 }
@@ -166,7 +173,8 @@
                       std::vector<std::string>* interestingness_test,
                       std::string* shrink_transformations_file,
                       std::string* shrink_temp_file_prefix,
-                      spvtools::FuzzerOptions* fuzzer_options) {
+                      spvtools::FuzzerOptions* fuzzer_options,
+                      spvtools::ValidatorOptions* validator_options) {
   uint32_t positional_arg_index = 0;
   bool only_positional_arguments_remain = false;
   bool force_render_red = false;
@@ -227,6 +235,18 @@
                               sizeof("--shrinker-temp-file-prefix=") - 1)) {
         const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
         *shrink_temp_file_prefix = std::string(split_flag.second);
+      } else if (0 == strcmp(cur_arg, "--before-hlsl-legalization")) {
+        validator_options->SetBeforeHlslLegalization(true);
+      } else if (0 == strcmp(cur_arg, "--relax-logical-pointer")) {
+        validator_options->SetRelaxLogicalPointer(true);
+      } else if (0 == strcmp(cur_arg, "--relax-block-layout")) {
+        validator_options->SetRelaxBlockLayout(true);
+      } else if (0 == strcmp(cur_arg, "--scalar-block-layout")) {
+        validator_options->SetScalarBlockLayout(true);
+      } else if (0 == strcmp(cur_arg, "--skip-block-layout")) {
+        validator_options->SetSkipBlockLayout(true);
+      } else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
+        validator_options->SetRelaxStructStore(true);
       } else if (0 == strcmp(cur_arg, "--")) {
         only_positional_arguments_remain = true;
       } else {
@@ -357,6 +377,7 @@
 
 bool Replay(const spv_target_env& target_env,
             spv_const_fuzzer_options fuzzer_options,
+            spv_validator_options validator_options,
             const std::vector<uint32_t>& binary_in,
             const spvtools::fuzz::protobufs::FactSequence& initial_facts,
             const std::string& replay_transformations_file,
@@ -368,8 +389,8 @@
                             &transformation_sequence)) {
     return false;
   }
-  spvtools::fuzz::Replayer replayer(target_env,
-                                    fuzzer_options->replay_validation_enabled);
+  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,
@@ -380,6 +401,7 @@
 
 bool Shrink(const spv_target_env& target_env,
             spv_const_fuzzer_options fuzzer_options,
+            spv_validator_options validator_options,
             const std::vector<uint32_t>& binary_in,
             const spvtools::fuzz::protobufs::FactSequence& initial_facts,
             const std::string& shrink_transformations_file,
@@ -393,9 +415,9 @@
                             &transformation_sequence)) {
     return false;
   }
-  spvtools::fuzz::Shrinker shrinker(target_env,
-                                    fuzzer_options->shrinker_step_limit,
-                                    fuzzer_options->replay_validation_enabled);
+  spvtools::fuzz::Shrinker shrinker(
+      target_env, fuzzer_options->shrinker_step_limit,
+      fuzzer_options->replay_validation_enabled, validator_options);
   shrinker.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
 
   assert(!interestingness_command.empty() &&
@@ -434,6 +456,7 @@
 
 bool Fuzz(const spv_target_env& target_env,
           spv_const_fuzzer_options fuzzer_options,
+          spv_validator_options validator_options,
           const std::vector<uint32_t>& binary_in,
           const spvtools::fuzz::protobufs::FactSequence& initial_facts,
           const std::string& donors, std::vector<uint32_t>* binary_out,
@@ -469,7 +492,7 @@
       fuzzer_options->has_random_seed
           ? fuzzer_options->random_seed
           : static_cast<uint32_t>(std::random_device()()),
-      fuzzer_options->fuzzer_pass_validation_enabled);
+      fuzzer_options->fuzzer_pass_validation_enabled, validator_options);
   fuzzer.SetMessageConsumer(message_consumer);
   auto fuzz_result_status =
       fuzzer.Run(binary_in, initial_facts, donor_suppliers, binary_out,
@@ -513,11 +536,13 @@
   std::string shrink_temp_file_prefix = "temp_";
 
   spvtools::FuzzerOptions fuzzer_options;
+  spvtools::ValidatorOptions validator_options;
 
-  FuzzStatus status = ParseFlags(
-      argc, argv, &in_binary_file, &out_binary_file, &donors_file,
-      &replay_transformations_file, &interestingness_test,
-      &shrink_transformations_file, &shrink_temp_file_prefix, &fuzzer_options);
+  FuzzStatus status =
+      ParseFlags(argc, argv, &in_binary_file, &out_binary_file, &donors_file,
+                 &replay_transformations_file, &interestingness_test,
+                 &shrink_transformations_file, &shrink_temp_file_prefix,
+                 &fuzzer_options, &validator_options);
 
   if (status.action == FuzzActions::STOP) {
     return status.code;
@@ -555,20 +580,22 @@
 
   switch (status.action) {
     case FuzzActions::FORCE_RENDER_RED:
-      if (!spvtools::fuzz::ForceRenderRed(target_env, binary_in, initial_facts,
+      if (!spvtools::fuzz::ForceRenderRed(target_env, validator_options,
+                                          binary_in, initial_facts,
                                           &binary_out)) {
         return 1;
       }
       break;
     case FuzzActions::FUZZ:
-      if (!Fuzz(target_env, fuzzer_options, binary_in, initial_facts,
-                donors_file, &binary_out, &transformations_applied)) {
+      if (!Fuzz(target_env, fuzzer_options, validator_options, binary_in,
+                initial_facts, donors_file, &binary_out,
+                &transformations_applied)) {
         return 1;
       }
       break;
     case FuzzActions::REPLAY:
-      if (!Replay(target_env, fuzzer_options, binary_in, initial_facts,
-                  replay_transformations_file, &binary_out,
+      if (!Replay(target_env, fuzzer_options, validator_options, binary_in,
+                  initial_facts, replay_transformations_file, &binary_out,
                   &transformations_applied)) {
         return 1;
       }
@@ -579,9 +606,9 @@
                   << std::endl;
         return 1;
       }
-      if (!Shrink(target_env, fuzzer_options, binary_in, initial_facts,
-                  shrink_transformations_file, shrink_temp_file_prefix,
-                  interestingness_test, &binary_out,
+      if (!Shrink(target_env, fuzzer_options, validator_options, binary_in,
+                  initial_facts, shrink_transformations_file,
+                  shrink_temp_file_prefix, interestingness_test, &binary_out,
                   &transformations_applied)) {
         return 1;
       }
diff --git a/third_party/SPIRV-Tools/tools/opt/opt.cpp b/third_party/SPIRV-Tools/tools/opt/opt.cpp
index 658bd5b..66f9228 100644
--- a/third_party/SPIRV-Tools/tools/opt/opt.cpp
+++ b/third_party/SPIRV-Tools/tools/opt/opt.cpp
@@ -114,6 +114,10 @@
                and VK_AMD_shader_trinary_minmax with equivalent code using core
                instructions and capabilities.)");
   printf(R"(
+  --before-hlsl-legalization
+               Forwards this option to the validator.  See the validator help
+               for details.)");
+  printf(R"(
   --ccp
                Apply the conditional constant propagation transform.  This will
                propagate constant values throughout the program, and simplify
@@ -403,14 +407,21 @@
                Looks for instructions in the same function that compute the
                same value, and deletes the redundant ones.)");
   printf(R"(
+  --relax-block-layout
+               Forwards this option to the validator.  See the validator help
+               for details.)");
+  printf(R"(
   --relax-float-ops
                Decorate all float operations with RelaxedPrecision if not already
                so decorated. This does not decorate types or variables.)");
   printf(R"(
+  --relax-logical-pointer
+               Forwards this option to the validator.  See the validator help
+               for details.)");
+  printf(R"(
   --relax-struct-store
-               Allow store from one struct type to a different type with
-               compatible layout and members. This option is forwarded to the
-               validator.)");
+               Forwards this option to the validator.  See the validator help
+               for details.)");
   printf(R"(
   --remove-duplicates
                Removes duplicate types, decorations, capabilities and extension
@@ -425,6 +436,10 @@
                Replace loads and stores to function local variables with
                operations on SSA IDs.)");
   printf(R"(
+  --scalar-block-layout
+               Forwards this option to the validator.  See the validator help
+               for details.)");
+  printf(R"(
   --scalar-replacement[=<n>]
                Replace aggregate function scope variables that are only accessed
                via their elements with new function variables representing each
@@ -444,6 +459,10 @@
                Will simplify all instructions in the function as much as
                possible.)");
   printf(R"(
+  --skip-block-layout
+               Forwards this option to the validator.  See the validator help
+               for details.)");
+  printf(R"(
   --split-invalid-unreachable
                Attempts to legalize for WebGPU cases where an unreachable
                merge-block is also a continue-target by splitting it into two
@@ -822,6 +841,18 @@
         optimizer->RegisterWebGPUToVulkanPasses();
       } else if (0 == strcmp(cur_arg, "--validate-after-all")) {
         optimizer->SetValidateAfterAll(true);
+      } else if (0 == strcmp(cur_arg, "--before-hlsl-legalization")) {
+        validator_options->SetBeforeHlslLegalization(true);
+      } else if (0 == strcmp(cur_arg, "--relax-logical-pointer")) {
+        validator_options->SetRelaxLogicalPointer(true);
+      } else if (0 == strcmp(cur_arg, "--relax-block-layout")) {
+        validator_options->SetRelaxBlockLayout(true);
+      } else if (0 == strcmp(cur_arg, "--scalar-block-layout")) {
+        validator_options->SetScalarBlockLayout(true);
+      } else if (0 == strcmp(cur_arg, "--skip-block-layout")) {
+        validator_options->SetSkipBlockLayout(true);
+      } else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
+        validator_options->SetRelaxStructStore(true);
       } else {
         // Some passes used to accept the form '--pass arg', canonicalize them
         // to '--pass=arg'.
diff --git a/third_party/SPIRV-Tools/tools/sva/yarn.lock b/third_party/SPIRV-Tools/tools/sva/yarn.lock
index be19e7c..11ba12f 100644
--- a/third_party/SPIRV-Tools/tools/sva/yarn.lock
+++ b/third_party/SPIRV-Tools/tools/sva/yarn.lock
@@ -47,9 +47,9 @@
   integrity sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==
 
 acorn@^7.0.0:
-  version "7.0.0"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.0.0.tgz#26b8d1cd9a9b700350b71c0905546f64d1284e7a"
-  integrity sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ==
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf"
+  integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==
 
 ajv@6.5.3:
   version "6.5.3"
diff --git a/third_party/SPIRV-Tools/utils/vscode/src/parser/parser.go b/third_party/SPIRV-Tools/utils/vscode/src/parser/parser.go
index 9f5691b..1775b0f 100644
--- a/third_party/SPIRV-Tools/utils/vscode/src/parser/parser.go
+++ b/third_party/SPIRV-Tools/utils/vscode/src/parser/parser.go
@@ -310,7 +310,7 @@
 	tok := &Token{Type: Operator, Range: Range{Start: l.pos, End: l.pos}}
 	for l.e == nil {
 		switch l.next() {
-		case '=':
+		case '=', '|':
 			tok.Range.End = l.pos
 			l.toks = append(l.toks, tok)
 			return
@@ -374,7 +374,7 @@
 		case r == '"':
 			l.restore(s)
 			l.string()
-		case r == '=':
+		case r == '=', r == '|':
 			l.restore(s)
 			l.operator()
 		case r == ';':
@@ -556,21 +556,34 @@
 		s := tok.Text(p.lines)
 		for _, e := range k.Enumerants {
 			if e.Enumerant == s {
-				n := 1
+				count := 1
 				for _, param := range e.Parameters {
-					p, c := p.operand(param.Name, param.Kind, i+n, false)
+					p, c := p.operand(param.Name, param.Kind, i+count, false)
 					if p != nil {
 						op.Tokens = append(op.Tokens, p.Tokens...)
 						op.Parameters = append(op.Parameters, p)
 					}
-					n += c
+					count += c
 				}
-				return op, n
+
+				// Handle bitfield '|' chains
+				if p.tok(i+count).Text(p.lines) == "|" {
+					count++ // '|'
+					p, c := p.operand(n, k, i+count, false)
+					if p != nil {
+						op.Tokens = append(op.Tokens, p.Tokens...)
+						op.Parameters = append(op.Parameters, p)
+					}
+					count += c
+				}
+
+				return op, count
 			}
 		}
 		if !optional {
 			p.err(p.tok(i), "invalid operand value '%s'", s)
 		}
+
 		return nil, 0
 
 	case schema.OperandCategoryID:
diff --git a/third_party/SPIRV-Tools/utils/vscode/src/schema/schema.go b/third_party/SPIRV-Tools/utils/vscode/src/schema/schema.go
index 66bd7dc..0d57cb1 100755
--- a/third_party/SPIRV-Tools/utils/vscode/src/schema/schema.go
+++ b/third_party/SPIRV-Tools/utils/vscode/src/schema/schema.go
@@ -97,6 +97,7 @@
 	OperandCategoryComposite = "Composite"
 )
 
+// OpcodeMap is a map of opcode name to Opcode type.
 type OpcodeMap map[string]*Opcode
 
 var (
@@ -467,11 +468,41 @@
 		"OpGroupNonUniformPartitionNV": OpGroupNonUniformPartitionNV,
 		"OpWritePackedPrimitiveIndices4x8NV": OpWritePackedPrimitiveIndices4x8NV,
 		"OpReportIntersectionNV": OpReportIntersectionNV,
+		"OpReportIntersectionKHR": OpReportIntersectionKHR,
 		"OpIgnoreIntersectionNV": OpIgnoreIntersectionNV,
+		"OpIgnoreIntersectionKHR": OpIgnoreIntersectionKHR,
 		"OpTerminateRayNV": OpTerminateRayNV,
+		"OpTerminateRayKHR": OpTerminateRayKHR,
 		"OpTraceNV": OpTraceNV,
+		"OpTraceRayKHR": OpTraceRayKHR,
 		"OpTypeAccelerationStructureNV": OpTypeAccelerationStructureNV,
+		"OpTypeAccelerationStructureKHR": OpTypeAccelerationStructureKHR,
+		"OpTypeRayQueryProvisionalKHR": OpTypeRayQueryProvisionalKHR,
+		"OpRayQueryInitializeKHR": OpRayQueryInitializeKHR,
+		"OpRayQueryTerminateKHR": OpRayQueryTerminateKHR,
+		"OpRayQueryGenerateIntersectionKHR": OpRayQueryGenerateIntersectionKHR,
+		"OpRayQueryConfirmIntersectionKHR": OpRayQueryConfirmIntersectionKHR,
+		"OpRayQueryProceedKHR": OpRayQueryProceedKHR,
+		"OpRayQueryGetIntersectionTypeKHR": OpRayQueryGetIntersectionTypeKHR,
+		"OpRayQueryGetRayTMinKHR": OpRayQueryGetRayTMinKHR,
+		"OpRayQueryGetRayFlagsKHR": OpRayQueryGetRayFlagsKHR,
+		"OpRayQueryGetIntersectionTKHR": OpRayQueryGetIntersectionTKHR,
+		"OpRayQueryGetIntersectionInstanceCustomIndexKHR": OpRayQueryGetIntersectionInstanceCustomIndexKHR,
+		"OpRayQueryGetIntersectionInstanceIdKHR": OpRayQueryGetIntersectionInstanceIdKHR,
+		"OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR": OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR,
+		"OpRayQueryGetIntersectionGeometryIndexKHR": OpRayQueryGetIntersectionGeometryIndexKHR,
+		"OpRayQueryGetIntersectionPrimitiveIndexKHR": OpRayQueryGetIntersectionPrimitiveIndexKHR,
+		"OpRayQueryGetIntersectionBarycentricsKHR": OpRayQueryGetIntersectionBarycentricsKHR,
+		"OpRayQueryGetIntersectionFrontFaceKHR": OpRayQueryGetIntersectionFrontFaceKHR,
+		"OpRayQueryGetIntersectionCandidateAABBOpaqueKHR": OpRayQueryGetIntersectionCandidateAABBOpaqueKHR,
+		"OpRayQueryGetIntersectionObjectRayDirectionKHR": OpRayQueryGetIntersectionObjectRayDirectionKHR,
+		"OpRayQueryGetIntersectionObjectRayOriginKHR": OpRayQueryGetIntersectionObjectRayOriginKHR,
+		"OpRayQueryGetWorldRayDirectionKHR": OpRayQueryGetWorldRayDirectionKHR,
+		"OpRayQueryGetWorldRayOriginKHR": OpRayQueryGetWorldRayOriginKHR,
+		"OpRayQueryGetIntersectionObjectToWorldKHR": OpRayQueryGetIntersectionObjectToWorldKHR,
+		"OpRayQueryGetIntersectionWorldToObjectKHR": OpRayQueryGetIntersectionWorldToObjectKHR,
 		"OpExecuteCallableNV": OpExecuteCallableNV,
+		"OpExecuteCallableKHR": OpExecuteCallableKHR,
 		"OpTypeCooperativeMatrixNV": OpTypeCooperativeMatrixNV,
 		"OpCooperativeMatrixLoadNV": OpCooperativeMatrixLoadNV,
 		"OpCooperativeMatrixStoreNV": OpCooperativeMatrixStoreNV,
@@ -10573,6 +10604,33 @@
 			}, 
 		},
 	}
+	OpReportIntersectionKHR = &Opcode {
+		Opname:   "OpReportIntersectionKHR",
+		Class:    "Reserved",
+		Opcode:   5334,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'HitKind'",
+				Quantifier: "",
+			}, 
+		},
+	}
 	OpIgnoreIntersectionNV = &Opcode {
 		Opname:   "OpIgnoreIntersectionNV",
 		Class:    "Reserved",
@@ -10580,6 +10638,13 @@
 		Operands: []Operand {
 		},
 	}
+	OpIgnoreIntersectionKHR = &Opcode {
+		Opname:   "OpIgnoreIntersectionKHR",
+		Class:    "Reserved",
+		Opcode:   5335,
+		Operands: []Operand {
+		},
+	}
 	OpTerminateRayNV = &Opcode {
 		Opname:   "OpTerminateRayNV",
 		Class:    "Reserved",
@@ -10587,6 +10652,13 @@
 		Operands: []Operand {
 		},
 	}
+	OpTerminateRayKHR = &Opcode {
+		Opname:   "OpTerminateRayKHR",
+		Class:    "Reserved",
+		Opcode:   5336,
+		Operands: []Operand {
+		},
+	}
 	OpTraceNV = &Opcode {
 		Opname:   "OpTraceNV",
 		Class:    "Reserved",
@@ -10649,6 +10721,68 @@
 			}, 
 		},
 	}
+	OpTraceRayKHR = &Opcode {
+		Opname:   "OpTraceRayKHR",
+		Class:    "Reserved",
+		Opcode:   5337,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Accel'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Flags'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Cull Mask'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Stride'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Miss Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Origin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Tmin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Direction'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Tmax'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'PayloadId'",
+				Quantifier: "",
+			}, 
+		},
+	}
 	OpTypeAccelerationStructureNV = &Opcode {
 		Opname:   "OpTypeAccelerationStructureNV",
 		Class:    "Reserved",
@@ -10661,6 +10795,601 @@
 			}, 
 		},
 	}
+	OpTypeAccelerationStructureKHR = &Opcode {
+		Opname:   "OpTypeAccelerationStructureKHR",
+		Class:    "Reserved",
+		Opcode:   5341,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeRayQueryProvisionalKHR = &Opcode {
+		Opname:   "OpTypeRayQueryProvisionalKHR",
+		Class:    "Reserved",
+		Opcode:   4472,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryInitializeKHR = &Opcode {
+		Opname:   "OpRayQueryInitializeKHR",
+		Class:    "Reserved",
+		Opcode:   4473,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Accel'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayFlags'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'CullMask'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayOrigin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayTMin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayDirection'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayTMax'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryTerminateKHR = &Opcode {
+		Opname:   "OpRayQueryTerminateKHR",
+		Class:    "Reserved",
+		Opcode:   4474,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGenerateIntersectionKHR = &Opcode {
+		Opname:   "OpRayQueryGenerateIntersectionKHR",
+		Class:    "Reserved",
+		Opcode:   4475,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'HitT'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryConfirmIntersectionKHR = &Opcode {
+		Opname:   "OpRayQueryConfirmIntersectionKHR",
+		Class:    "Reserved",
+		Opcode:   4476,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryProceedKHR = &Opcode {
+		Opname:   "OpRayQueryProceedKHR",
+		Class:    "Reserved",
+		Opcode:   4477,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionTypeKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionTypeKHR",
+		Class:    "Reserved",
+		Opcode:   4479,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetRayTMinKHR = &Opcode {
+		Opname:   "OpRayQueryGetRayTMinKHR",
+		Class:    "Reserved",
+		Opcode:   6016,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetRayFlagsKHR = &Opcode {
+		Opname:   "OpRayQueryGetRayFlagsKHR",
+		Class:    "Reserved",
+		Opcode:   6017,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionTKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionTKHR",
+		Class:    "Reserved",
+		Opcode:   6018,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionInstanceCustomIndexKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionInstanceCustomIndexKHR",
+		Class:    "Reserved",
+		Opcode:   6019,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionInstanceIdKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionInstanceIdKHR",
+		Class:    "Reserved",
+		Opcode:   6020,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR",
+		Class:    "Reserved",
+		Opcode:   6021,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionGeometryIndexKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionGeometryIndexKHR",
+		Class:    "Reserved",
+		Opcode:   6022,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionPrimitiveIndexKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionPrimitiveIndexKHR",
+		Class:    "Reserved",
+		Opcode:   6023,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionBarycentricsKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionBarycentricsKHR",
+		Class:    "Reserved",
+		Opcode:   6024,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionFrontFaceKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionFrontFaceKHR",
+		Class:    "Reserved",
+		Opcode:   6025,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR",
+		Class:    "Reserved",
+		Opcode:   6026,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionObjectRayDirectionKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionObjectRayDirectionKHR",
+		Class:    "Reserved",
+		Opcode:   6027,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionObjectRayOriginKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionObjectRayOriginKHR",
+		Class:    "Reserved",
+		Opcode:   6028,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetWorldRayDirectionKHR = &Opcode {
+		Opname:   "OpRayQueryGetWorldRayDirectionKHR",
+		Class:    "Reserved",
+		Opcode:   6029,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetWorldRayOriginKHR = &Opcode {
+		Opname:   "OpRayQueryGetWorldRayOriginKHR",
+		Class:    "Reserved",
+		Opcode:   6030,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionObjectToWorldKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionObjectToWorldKHR",
+		Class:    "Reserved",
+		Opcode:   6031,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionWorldToObjectKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionWorldToObjectKHR",
+		Class:    "Reserved",
+		Opcode:   6032,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
 	OpExecuteCallableNV = &Opcode {
 		Opname:   "OpExecuteCallableNV",
 		Class:    "Reserved",
@@ -10678,6 +11407,23 @@
 			}, 
 		},
 	}
+	OpExecuteCallableKHR = &Opcode {
+		Opname:   "OpExecuteCallableKHR",
+		Class:    "Reserved",
+		Opcode:   5344,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Callable DataId'",
+				Quantifier: "",
+			}, 
+		},
+	}
 	OpTypeCooperativeMatrixNV = &Opcode {
 		Opname:   "OpTypeCooperativeMatrixNV",
 		Class:    "Reserved",
@@ -19430,6 +20176,90 @@
 		},
 		Bases:      []*OperandKind {},
 	}
+	OperandKindRayFlags = &OperandKind {
+		Kind:       "RayFlags",
+		Category:   "BitEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "NoneKHR",
+				Value:        0x0000,
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "OpaqueKHR",
+				Value:        0x0001,
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "NoOpaqueKHR",
+				Value:        0x0002,
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "TerminateOnFirstHitKHR",
+				Value:        0x0004,
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SkipClosestHitShaderKHR",
+				Value:        0x0008,
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "CullBackFacingTrianglesKHR",
+				Value:        0x0010,
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "CullFrontFacingTrianglesKHR",
+				Value:        0x0020,
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "CullOpaqueKHR",
+				Value:        0x0040,
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "CullNoOpaqueKHR",
+				Value:        0x0080,
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SkipTrianglesKHR",
+				Value:        0x0100,
+				Capabilities: []string{"RayTraversalPrimitiveCullingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SkipAABBsKHR",
+				Value:        0x0200,
+				Capabilities: []string{"RayTraversalPrimitiveCullingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
 	OperandKindSourceLanguage = &OperandKind {
 		Kind:       "SourceLanguage",
 		Category:   "ValueEnum",
@@ -19549,42 +20379,84 @@
 			Enumerant{
 				Enumerant:    "RayGenerationNV",
 				Value:        5313,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RayGenerationKHR",
+				Value:        5313,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "IntersectionNV",
 				Value:        5314,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "IntersectionKHR",
+				Value:        5314,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "AnyHitNV",
 				Value:        5315,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "AnyHitKHR",
+				Value:        5315,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ClosestHitNV",
 				Value:        5316,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ClosestHitKHR",
+				Value:        5316,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "MissNV",
 				Value:        5317,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MissKHR",
+				Value:        5317,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "CallableNV",
 				Value:        5318,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CallableKHR",
+				Value:        5318,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
@@ -20172,42 +21044,84 @@
 			Enumerant{
 				Enumerant:    "CallableDataNV",
 				Value:        5328,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CallableDataKHR",
+				Value:        5328,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "IncomingCallableDataNV",
 				Value:        5329,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "IncomingCallableDataKHR",
+				Value:        5329,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayPayloadNV",
 				Value:        5338,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RayPayloadKHR",
+				Value:        5338,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "HitAttributeNV",
 				Value:        5339,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "HitAttributeKHR",
+				Value:        5339,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "IncomingRayPayloadNV",
 				Value:        5342,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "IncomingRayPayloadKHR",
+				Value:        5342,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ShaderRecordBufferNV",
 				Value:        5343,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ShaderRecordBufferKHR",
+				Value:        5343,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
@@ -21593,7 +22507,7 @@
 			Enumerant{
 				Enumerant:    "PrimitiveId",
 				Value:        7,
-				Capabilities: []string{"Geometry","Tessellation","RayTracingNV",},
+				Capabilities: []string{"Geometry","Tessellation","RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
@@ -22139,98 +23053,203 @@
 			Enumerant{
 				Enumerant:    "LaunchIdNV",
 				Value:        5319,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "LaunchIdKHR",
+				Value:        5319,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "LaunchSizeNV",
 				Value:        5320,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "LaunchSizeKHR",
+				Value:        5320,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "WorldRayOriginNV",
 				Value:        5321,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "WorldRayOriginKHR",
+				Value:        5321,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "WorldRayDirectionNV",
 				Value:        5322,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "WorldRayDirectionKHR",
+				Value:        5322,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ObjectRayOriginNV",
 				Value:        5323,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ObjectRayOriginKHR",
+				Value:        5323,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ObjectRayDirectionNV",
 				Value:        5324,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ObjectRayDirectionKHR",
+				Value:        5324,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayTminNV",
 				Value:        5325,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RayTminKHR",
+				Value:        5325,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayTmaxNV",
 				Value:        5326,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RayTmaxKHR",
+				Value:        5326,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "InstanceCustomIndexNV",
 				Value:        5327,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "InstanceCustomIndexKHR",
+				Value:        5327,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ObjectToWorldNV",
 				Value:        5330,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ObjectToWorldKHR",
+				Value:        5330,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "WorldToObjectNV",
 				Value:        5331,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "WorldToObjectKHR",
+				Value:        5331,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "HitTNV",
 				Value:        5332,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "HitTKHR",
+				Value:        5332,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "HitKindNV",
 				Value:        5333,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "HitKindKHR",
+				Value:        5333,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "IncomingRayFlagsNV",
 				Value:        5351,
-				Capabilities: []string{"RayTracingNV",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "IncomingRayFlagsKHR",
+				Value:        5351,
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RayGeometryIndexKHR",
+				Value:        5352,
+				Capabilities: []string{"RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
@@ -22318,6 +23337,13 @@
 				Parameters:   []Parameter{},
 				Version:      "1.5",
 			},
+			Enumerant{
+				Enumerant:    "ShaderCallKHR",
+				Value:        6,
+				Capabilities: []string{"RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
 		},
 		Bases:      []*OperandKind {},
 	}
@@ -23054,6 +24080,20 @@
 				Version:      "1.4",
 			},
 			Enumerant{
+				Enumerant:    "RayQueryProvisionalKHR",
+				Value:        4471,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RayTraversalPrimitiveCullingProvisionalKHR",
+				Value:        4478,
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
 				Enumerant:    "Float16ImageAMD",
 				Value:        5008,
 				Capabilities: []string{"Shader",},
@@ -23425,6 +24465,13 @@
 				Version:      "None",
 			},
 			Enumerant{
+				Enumerant:    "RayTracingProvisionalKHR",
+				Value:        5353,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
 				Enumerant:    "CooperativeMatrixNV",
 				Value:        5357,
 				Capabilities: []string{"Shader",},
@@ -23525,6 +24572,76 @@
 		},
 		Bases:      []*OperandKind {},
 	}
+	OperandKindRayQueryIntersection = &OperandKind {
+		Kind:       "RayQueryIntersection",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "RayQueryCandidateIntersectionKHR",
+				Value:        0,
+				Capabilities: []string{"RayQueryProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "RayQueryCommittedIntersectionKHR",
+				Value:        1,
+				Capabilities: []string{"RayQueryProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindRayQueryCommittedIntersectionType = &OperandKind {
+		Kind:       "RayQueryCommittedIntersectionType",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "RayQueryCommittedIntersectionNoneKHR",
+				Value:        0,
+				Capabilities: []string{"RayQueryProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "RayQueryCommittedIntersectionTriangleKHR",
+				Value:        1,
+				Capabilities: []string{"RayQueryProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "RayQueryCommittedIntersectionGeneratedKHR",
+				Value:        2,
+				Capabilities: []string{"RayQueryProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindRayQueryCandidateIntersectionType = &OperandKind {
+		Kind:       "RayQueryCandidateIntersectionType",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "RayQueryCandidateIntersectionTriangleKHR",
+				Value:        0,
+				Capabilities: []string{"RayQueryProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "RayQueryCandidateIntersectionAABBKHR",
+				Value:        1,
+				Capabilities: []string{"RayQueryProvisionalKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
 	OperandKindIdResultType = &OperandKind {
 		Kind:       "IdResultType",
 		Category:   "Id",