spirv-fuzz: Introduce TransformationContext (#3272)

Some transformations (e.g. TransformationAddFunction) rely on running
the validator to decide whether the transformation is applicable.  A
recent change allowed spirv-fuzz to take validator options, to cater
for the case where a module should be considered valid under
particular conditions.  However, validation during the checking of
transformations had no access to these validator options.

This change introduced TransformationContext, which currently consists
of a fact manager and a set of validator options, but could in the
future have other fields corresponding to other objects that it is
useful to have access to when applying transformations.  Now, instead
of checking and applying transformations in the context of a
FactManager, a TransformationContext is used.  This gives access to
the fact manager as before, and also access to the validator options
when they are needed.
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index 3a9d604..cd53aea 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -99,6 +99,7 @@
         transformation_add_type_vector.h
         transformation_composite_construct.h
         transformation_composite_extract.h
+        transformation_context.h
         transformation_copy_object.h
         transformation_equation_instruction.h
         transformation_function_call.h
@@ -190,6 +191,7 @@
         transformation_add_type_vector.cpp
         transformation_composite_construct.cpp
         transformation_composite_extract.cpp
+        transformation_context.cpp
         transformation_copy_object.cpp
         transformation_equation_instruction.cpp
         transformation_function_call.cpp
diff --git a/source/fuzz/force_render_red.cpp b/source/fuzz/force_render_red.cpp
index 46e23e8..5bf2879 100644
--- a/source/fuzz/force_render_red.cpp
+++ b/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/source/fuzz/force_render_red.h b/source/fuzz/force_render_red.h
index 2484d27..b51c72b 100644
--- a/source/fuzz/force_render_red.h
+++ b/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/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index 3a4fa0e..6524c21 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -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)...));
   }
 }
@@ -182,11 +183,13 @@
 
   FactManager fact_manager;
   fact_manager.AddFacts(impl_->consumer, initial_facts, ir_context.get());
+  TransformationContext transformation_context(&fact_manager,
+                                               impl_->validator_options);
 
   // 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,
+      ir_context.get(), &transformation_context, &fuzzer_context,
       transformation_sequence_out);
   if (!impl_->ApplyPassAndCheckValidity(&add_useful_constructs, *ir_context,
                                         tools)) {
@@ -196,69 +199,69 @@
   // 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;
@@ -279,25 +282,25 @@
   // 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,
+      &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<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/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp
index a76f10d..4ab2946 100644
--- a/source/fuzz/fuzzer_pass.cpp
+++ b/source/fuzz/fuzzer_pass.cpp
@@ -31,11 +31,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) {}
 
diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h
index 46ee408..436fd77 100644
--- a/source/fuzz/fuzzer_pass.h
+++ b/source/fuzz/fuzzer_pass.h
@@ -18,9 +18,9 @@
 #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_context.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -29,22 +29,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_; }
 
@@ -93,9 +96,10 @@
   // by construction, and adding it to the sequence of applied transformations.
   template <typename TransformationType>
   void ApplyTransformation(const TransformationType& transformation) {
-    assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+    assert(transformation.IsApplicable(GetIRContext(),
+                                       *GetTransformationContext()) &&
            "Transformation should be applicable by construction.");
-    transformation.Apply(GetIRContext(), GetFactManager());
+    transformation.Apply(GetIRContext(), GetTransformationContext());
     *GetTransformations()->add_transformation() = transformation.ToMessage();
   }
 
@@ -230,7 +234,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/source/fuzz/fuzzer_pass_add_access_chains.cpp b/source/fuzz/fuzzer_pass_add_access_chains.cpp
index cfc2812..b9c1eed 100644
--- a/source/fuzz/fuzzer_pass_add_access_chains.cpp
+++ b/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/source/fuzz/fuzzer_pass_add_access_chains.h b/source/fuzz/fuzzer_pass_add_access_chains.h
index 7e8ed61..8649296 100644
--- a/source/fuzz/fuzzer_pass_add_access_chains.h
+++ b/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/source/fuzz/fuzzer_pass_add_composite_types.cpp b/source/fuzz/fuzzer_pass_add_composite_types.cpp
index 32c720e..9b0dda8 100644
--- a/source/fuzz/fuzzer_pass_add_composite_types.cpp
+++ b/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/source/fuzz/fuzzer_pass_add_composite_types.h b/source/fuzz/fuzzer_pass_add_composite_types.h
index 29d4bb8..87bc0ff 100644
--- a/source/fuzz/fuzzer_pass_add_composite_types.h
+++ b/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/source/fuzz/fuzzer_pass_add_dead_blocks.cpp b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
index c9bc9c4..4e9db1f 100644
--- a/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
+++ b/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;
 
@@ -53,8 +54,9 @@
   }
   // 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/source/fuzz/fuzzer_pass_add_dead_blocks.h b/source/fuzz/fuzzer_pass_add_dead_blocks.h
index 01e3843..d78f088 100644
--- a/source/fuzz/fuzzer_pass_add_dead_blocks.h
+++ b/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/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
index aefc2fc..6b171dc 100644
--- a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
+++ b/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;
 
@@ -79,8 +80,8 @@
         auto candidate_transformation = TransformationAddDeadBreak(
             block.id(), merge_block->id(), GetFuzzerContext()->ChooseEven(),
             std::move(phi_ids));
-        if (candidate_transformation.IsApplicable(GetIRContext(),
-                                                  *GetFactManager())) {
+        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 +110,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/source/fuzz/fuzzer_pass_add_dead_breaks.h b/source/fuzz/fuzzer_pass_add_dead_breaks.h
index 12a5095..c379eed 100644
--- a/source/fuzz/fuzzer_pass_add_dead_breaks.h
+++ b/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/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/source/fuzz/fuzzer_pass_add_dead_continues.cpp
index 852df3d..b8f07fb 100644
--- a/source/fuzz/fuzzer_pass_add_dead_continues.cpp
+++ b/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;
 
@@ -75,10 +76,11 @@
       // 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/source/fuzz/fuzzer_pass_add_dead_continues.h b/source/fuzz/fuzzer_pass_add_dead_continues.h
index d067f1c..b2acb93 100644
--- a/source/fuzz/fuzzer_pass_add_dead_continues.h
+++ b/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/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
index 7f34344..49c4a8a 100644
--- a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
+++ b/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/source/fuzz/fuzzer_pass_add_equation_instructions.h b/source/fuzz/fuzzer_pass_add_equation_instructions.h
index 84229c0..6e64977 100644
--- a/source/fuzz/fuzzer_pass_add_equation_instructions.h
+++ b/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/source/fuzz/fuzzer_pass_add_function_calls.cpp b/source/fuzz/fuzzer_pass_add_function_calls.cpp
index 545aa16..f666eb2 100644
--- a/source/fuzz/fuzzer_pass_add_function_calls.cpp
+++ b/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
diff --git a/source/fuzz/fuzzer_pass_add_function_calls.h b/source/fuzz/fuzzer_pass_add_function_calls.h
index 5d184fd..8f75e8c 100644
--- a/source/fuzz/fuzzer_pass_add_function_calls.h
+++ b/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/source/fuzz/fuzzer_pass_add_global_variables.cpp b/source/fuzz/fuzzer_pass_add_global_variables.cpp
index 1371f46..ce2b8eb 100644
--- a/source/fuzz/fuzzer_pass_add_global_variables.cpp
+++ b/source/fuzz/fuzzer_pass_add_global_variables.cpp
@@ -21,10 +21,11 @@
 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;
 
diff --git a/source/fuzz/fuzzer_pass_add_global_variables.h b/source/fuzz/fuzzer_pass_add_global_variables.h
index c71d147..a907d36 100644
--- a/source/fuzz/fuzzer_pass_add_global_variables.h
+++ b/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/source/fuzz/fuzzer_pass_add_loads.cpp b/source/fuzz/fuzzer_pass_add_loads.cpp
index 851787f..16d88e3 100644
--- a/source/fuzz/fuzzer_pass_add_loads.cpp
+++ b/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;
 
diff --git a/source/fuzz/fuzzer_pass_add_loads.h b/source/fuzz/fuzzer_pass_add_loads.h
index 125bc5d..c4d5b27 100644
--- a/source/fuzz/fuzzer_pass_add_loads.h
+++ b/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/source/fuzz/fuzzer_pass_add_local_variables.cpp b/source/fuzz/fuzzer_pass_add_local_variables.cpp
index 8d6d80d..ace9be2 100644
--- a/source/fuzz/fuzzer_pass_add_local_variables.cpp
+++ b/source/fuzz/fuzzer_pass_add_local_variables.cpp
@@ -22,10 +22,11 @@
 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;
 
diff --git a/source/fuzz/fuzzer_pass_add_local_variables.h b/source/fuzz/fuzzer_pass_add_local_variables.h
index eed3665..08d26d8 100644
--- a/source/fuzz/fuzzer_pass_add_local_variables.h
+++ b/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/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
index 82fb539..09627d0 100644
--- a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
+++ b/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/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h
index abe5bd7..f32e5bc 100644
--- a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h
+++ b/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/source/fuzz/fuzzer_pass_add_stores.cpp b/source/fuzz/fuzzer_pass_add_stores.cpp
index 794ddc3..e8871be 100644
--- a/source/fuzz/fuzzer_pass_add_stores.cpp
+++ b/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;
 
@@ -82,9 +83,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/source/fuzz/fuzzer_pass_add_stores.h b/source/fuzz/fuzzer_pass_add_stores.h
index 9daa9e0..55ec67f 100644
--- a/source/fuzz/fuzzer_pass_add_stores.h
+++ b/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/source/fuzz/fuzzer_pass_add_useful_constructs.cpp b/source/fuzz/fuzzer_pass_add_useful_constructs.cpp
index 8552dfd..a267612 100644
--- a/source/fuzz/fuzzer_pass_add_useful_constructs.cpp
+++ b/source/fuzz/fuzzer_pass_add_useful_constructs.cpp
@@ -25,10 +25,11 @@
 namespace fuzz {
 
 FuzzerPassAddUsefulConstructs::FuzzerPassAddUsefulConstructs(
-    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) {}
 
 FuzzerPassAddUsefulConstructs::~FuzzerPassAddUsefulConstructs() = default;
 
@@ -49,9 +50,10 @@
     TransformationAddConstantScalar add_constant_int =
         TransformationAddConstantScalar(GetFuzzerContext()->GetFreshId(),
                                         int_type_id, data);
-    assert(add_constant_int.IsApplicable(GetIRContext(), *GetFactManager()) &&
+    assert(add_constant_int.IsApplicable(GetIRContext(),
+                                         *GetTransformationContext()) &&
            "Should be applicable by construction.");
-    add_constant_int.Apply(GetIRContext(), GetFactManager());
+    add_constant_int.Apply(GetIRContext(), GetTransformationContext());
     *GetTransformations()->add_transformation() = add_constant_int.ToMessage();
   }
 }
@@ -75,9 +77,10 @@
     TransformationAddConstantScalar add_constant_float =
         TransformationAddConstantScalar(GetFuzzerContext()->GetFreshId(),
                                         float_type_id, data);
-    assert(add_constant_float.IsApplicable(GetIRContext(), *GetFactManager()) &&
+    assert(add_constant_float.IsApplicable(GetIRContext(),
+                                           *GetTransformationContext()) &&
            "Should be applicable by construction.");
-    add_constant_float.Apply(GetIRContext(), GetFactManager());
+    add_constant_float.Apply(GetIRContext(), GetTransformationContext());
     *GetTransformations()->add_transformation() =
         add_constant_float.ToMessage();
   }
@@ -90,9 +93,10 @@
     if (!GetIRContext()->get_type_mgr()->GetId(&temp_bool_type)) {
       auto add_type_boolean =
           TransformationAddTypeBoolean(GetFuzzerContext()->GetFreshId());
-      assert(add_type_boolean.IsApplicable(GetIRContext(), *GetFactManager()) &&
+      assert(add_type_boolean.IsApplicable(GetIRContext(),
+                                           *GetTransformationContext()) &&
              "Should be applicable by construction.");
-      add_type_boolean.Apply(GetIRContext(), GetFactManager());
+      add_type_boolean.Apply(GetIRContext(), GetTransformationContext());
       *GetTransformations()->add_transformation() =
           add_type_boolean.ToMessage();
     }
@@ -105,9 +109,10 @@
       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()) &&
+        assert(add_type_int.IsApplicable(GetIRContext(),
+                                         *GetTransformationContext()) &&
                "Should be applicable by construction.");
-        add_type_int.Apply(GetIRContext(), GetFactManager());
+        add_type_int.Apply(GetIRContext(), GetTransformationContext());
         *GetTransformations()->add_transformation() = add_type_int.ToMessage();
       }
     }
@@ -119,9 +124,10 @@
     if (!GetIRContext()->get_type_mgr()->GetId(&temp_float_type)) {
       TransformationAddTypeFloat add_type_float =
           TransformationAddTypeFloat(GetFuzzerContext()->GetFreshId(), 32);
-      assert(add_type_float.IsApplicable(GetIRContext(), *GetFactManager()) &&
+      assert(add_type_float.IsApplicable(GetIRContext(),
+                                         *GetTransformationContext()) &&
              "Should be applicable by construction.");
-      add_type_float.Apply(GetIRContext(), GetFactManager());
+      add_type_float.Apply(GetIRContext(), GetTransformationContext());
       *GetTransformations()->add_transformation() = add_type_float.ToMessage();
     }
   }
@@ -139,9 +145,9 @@
       TransformationAddConstantBoolean add_constant_boolean(
           GetFuzzerContext()->GetFreshId(), boolean_value);
       assert(add_constant_boolean.IsApplicable(GetIRContext(),
-                                               *GetFactManager()) &&
+                                               *GetTransformationContext()) &&
              "Should be applicable by construction.");
-      add_constant_boolean.Apply(GetIRContext(), GetFactManager());
+      add_constant_boolean.Apply(GetIRContext(), GetTransformationContext());
       *GetTransformations()->add_transformation() =
           add_constant_boolean.ToMessage();
     }
@@ -168,8 +174,9 @@
   //   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()) {
+  for (auto& fact_and_type_id : GetTransformationContext()
+                                    ->GetFactManager()
+                                    ->GetConstantUniformFactsAndTypes()) {
     uint32_t element_type_id = fact_and_type_id.second;
     assert(element_type_id);
     auto element_type =
@@ -183,9 +190,10 @@
       auto add_pointer =
           TransformationAddTypePointer(GetFuzzerContext()->GetFreshId(),
                                        SpvStorageClassUniform, element_type_id);
-      assert(add_pointer.IsApplicable(GetIRContext(), *GetFactManager()) &&
+      assert(add_pointer.IsApplicable(GetIRContext(),
+                                      *GetTransformationContext()) &&
              "Should be applicable by construction.");
-      add_pointer.Apply(GetIRContext(), GetFactManager());
+      add_pointer.Apply(GetIRContext(), GetTransformationContext());
       *GetTransformations()->add_transformation() = add_pointer.ToMessage();
     }
     std::vector<uint32_t> words;
diff --git a/source/fuzz/fuzzer_pass_add_useful_constructs.h b/source/fuzz/fuzzer_pass_add_useful_constructs.h
index 7dc00f1..17e87a8 100644
--- a/source/fuzz/fuzzer_pass_add_useful_constructs.h
+++ b/source/fuzz/fuzzer_pass_add_useful_constructs.h
@@ -25,7 +25,7 @@
 class FuzzerPassAddUsefulConstructs : public FuzzerPass {
  public:
   FuzzerPassAddUsefulConstructs(
-      opt::IRContext* ir_context, FactManager* fact_manager,
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
index fe229bc..aa62d2f 100644
--- a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
+++ b/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/source/fuzz/fuzzer_pass_adjust_function_controls.h b/source/fuzz/fuzzer_pass_adjust_function_controls.h
index 02d3600..e20541b 100644
--- a/source/fuzz/fuzzer_pass_adjust_function_controls.h
+++ b/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/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
index c9843d0..f7addff 100644
--- a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
+++ b/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/source/fuzz/fuzzer_pass_adjust_loop_controls.h b/source/fuzz/fuzzer_pass_adjust_loop_controls.h
index e945606..ee5cd48 100644
--- a/source/fuzz/fuzzer_pass_adjust_loop_controls.h
+++ b/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/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
index 2d3d676..32f5ea5 100644
--- a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
+++ b/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/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h
index c3d7118..699dcb5 100644
--- a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h
+++ b/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/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
index 397dfed..83b1854 100644
--- a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
+++ b/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/source/fuzz/fuzzer_pass_adjust_selection_controls.h b/source/fuzz/fuzzer_pass_adjust_selection_controls.h
index b5b255c..820b30d 100644
--- a/source/fuzz/fuzzer_pass_adjust_selection_controls.h
+++ b/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/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
index 5711f35..30884a6 100644
--- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
@@ -25,16 +25,19 @@
 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())) {
+       GetTransformationContext()
+           ->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
@@ -70,7 +73,8 @@
       }
 
       std::vector<const protobufs::DataDescriptor*> synonyms_to_try;
-      for (auto& data_descriptor : GetFactManager()->GetSynonymsForId(
+      for (auto& data_descriptor :
+           GetTransformationContext()->GetFactManager()->GetSynonymsForId(
                id_with_known_synonyms, GetIRContext())) {
         protobufs::DataDescriptor descriptor_for_this_id =
             MakeDataDescriptor(id_with_known_synonyms, {});
diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.h b/source/fuzz/fuzzer_pass_apply_id_synonyms.h
index 1a0748e..1a9213d 100644
--- a/source/fuzz/fuzzer_pass_apply_id_synonyms.h
+++ b/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/source/fuzz/fuzzer_pass_construct_composites.cpp b/source/fuzz/fuzzer_pass_construct_composites.cpp
index 330b9cf..b1b0823 100644
--- a/source/fuzz/fuzzer_pass_construct_composites.cpp
+++ b/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;
 
@@ -143,9 +144,10 @@
         TransformationCompositeConstruct transformation(
             chosen_composite_type, *constructor_arguments,
             instruction_descriptor, GetFuzzerContext()->GetFreshId());
-        assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+        assert(transformation.IsApplicable(GetIRContext(),
+                                           *GetTransformationContext()) &&
                "This transformation should be applicable by construction.");
-        transformation.Apply(GetIRContext(), GetFactManager());
+        transformation.Apply(GetIRContext(), GetTransformationContext());
         *GetTransformations()->add_transformation() =
             transformation.ToMessage();
       });
diff --git a/source/fuzz/fuzzer_pass_construct_composites.h b/source/fuzz/fuzzer_pass_construct_composites.h
index 99ef31f..d293514 100644
--- a/source/fuzz/fuzzer_pass_construct_composites.h
+++ b/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);
 
diff --git a/source/fuzz/fuzzer_pass_copy_objects.cpp b/source/fuzz/fuzzer_pass_copy_objects.cpp
index 588cfb6..f055b59 100644
--- a/source/fuzz/fuzzer_pass_copy_objects.cpp
+++ b/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/source/fuzz/fuzzer_pass_copy_objects.h b/source/fuzz/fuzzer_pass_copy_objects.h
index 5419459..8de382e 100644
--- a/source/fuzz/fuzzer_pass_copy_objects.h
+++ b/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/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp
index 27d8a6e..63ce7a6 100644
--- a/source/fuzz/fuzzer_pass_donate_modules.cpp
+++ b/source/fuzz/fuzzer_pass_donate_modules.cpp
@@ -40,11 +40,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 +63,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.
     //
diff --git a/source/fuzz/fuzzer_pass_donate_modules.h b/source/fuzz/fuzzer_pass_donate_modules.h
index ef529db..9087daf 100644
--- a/source/fuzz/fuzzer_pass_donate_modules.h
+++ b/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);
diff --git a/source/fuzz/fuzzer_pass_merge_blocks.cpp b/source/fuzz/fuzzer_pass_merge_blocks.cpp
index ca1bfb3..49778ae 100644
--- a/source/fuzz/fuzzer_pass_merge_blocks.cpp
+++ b/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/source/fuzz/fuzzer_pass_merge_blocks.h b/source/fuzz/fuzzer_pass_merge_blocks.h
index 457e591..1a6c2c2 100644
--- a/source/fuzz/fuzzer_pass_merge_blocks.h
+++ b/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/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
index 2caf0c6..4ced507 100644
--- a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
+++ b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
@@ -25,10 +25,11 @@
 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 +84,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.
@@ -245,7 +247,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,9 +258,10 @@
   auto chosen_type_id =
       available_types_with_uniforms[GetFuzzerContext()->RandomIndex(
           available_types_with_uniforms)];
-  auto available_constants =
-      GetFactManager()->GetConstantsAvailableFromUniformsForType(
-          GetIRContext(), chosen_type_id);
+  auto available_constants = GetTransformationContext()
+                                 ->GetFactManager()
+                                 ->GetConstantsAvailableFromUniformsForType(
+                                     GetIRContext(), chosen_type_id);
   if (available_constants.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
@@ -308,8 +313,11 @@
 
   // 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;
@@ -324,8 +332,9 @@
       constant_use, uniform_descriptor, GetFuzzerContext()->GetFreshId(),
       GetFuzzerContext()->GetFreshId());
   // Transformation should be applicable by construction.
-  assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()));
-  transformation.Apply(GetIRContext(), GetFactManager());
+  assert(
+      transformation.IsApplicable(GetIRContext(), *GetTransformationContext()));
+  transformation.Apply(GetIRContext(), GetTransformationContext());
   *GetTransformations()->add_transformation() = transformation.ToMessage();
 }
 
diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.h b/source/fuzz/fuzzer_pass_obfuscate_constants.h
index f34717b..7755d21 100644
--- a/source/fuzz/fuzzer_pass_obfuscate_constants.h
+++ b/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);
 
diff --git a/source/fuzz/fuzzer_pass_outline_functions.cpp b/source/fuzz/fuzzer_pass_outline_functions.cpp
index d59c195..1665d05 100644
--- a/source/fuzz/fuzzer_pass_outline_functions.cpp
+++ b/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/source/fuzz/fuzzer_pass_outline_functions.h b/source/fuzz/fuzzer_pass_outline_functions.h
index 5448e7d..6532ed9 100644
--- a/source/fuzz/fuzzer_pass_outline_functions.h
+++ b/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/source/fuzz/fuzzer_pass_permute_blocks.cpp b/source/fuzz/fuzzer_pass_permute_blocks.cpp
index af6d2a5..27a2d67 100644
--- a/source/fuzz/fuzzer_pass_permute_blocks.cpp
+++ b/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/source/fuzz/fuzzer_pass_permute_blocks.h b/source/fuzz/fuzzer_pass_permute_blocks.h
index 6735e95..f2d3b39 100644
--- a/source/fuzz/fuzzer_pass_permute_blocks.h
+++ b/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/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
index 2c49860..57d9cab 100644
--- a/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
+++ b/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/source/fuzz/fuzzer_pass_permute_function_parameters.h b/source/fuzz/fuzzer_pass_permute_function_parameters.h
index bc79804..3f32864 100644
--- a/source/fuzz/fuzzer_pass_permute_function_parameters.h
+++ b/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/source/fuzz/fuzzer_pass_split_blocks.cpp b/source/fuzz/fuzzer_pass_split_blocks.cpp
index 6a2ea4d..15c6790 100644
--- a/source/fuzz/fuzzer_pass_split_blocks.cpp
+++ b/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/source/fuzz/fuzzer_pass_split_blocks.h b/source/fuzz/fuzzer_pass_split_blocks.h
index 6e56dde..278ec6d 100644
--- a/source/fuzz/fuzzer_pass_split_blocks.h
+++ b/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/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp b/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp
index 4df97c9..321e8ef 100644
--- a/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp
+++ b/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/source/fuzz/fuzzer_pass_swap_commutable_operands.h b/source/fuzz/fuzzer_pass_swap_commutable_operands.h
index b0206de..74d937d 100644
--- a/source/fuzz/fuzzer_pass_swap_commutable_operands.h
+++ b/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/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
index 9fb175b..4f26cba 100644
--- a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
+++ b/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/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
index ec8c3f7..d77c7cb 100644
--- a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
+++ b/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/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp
index 4bfa195..90cf9fe 100644
--- a/source/fuzz/fuzzer_util.cpp
+++ b/source/fuzz/fuzzer_util.cpp
@@ -329,11 +329,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) {
diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h
index 7be0d59..08edfc5 100644
--- a/source/fuzz/fuzzer_util.h
+++ b/source/fuzz/fuzzer_util.h
@@ -132,8 +132,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.
diff --git a/source/fuzz/replayer.cpp b/source/fuzz/replayer.cpp
index 07dfe72..6312cba 100644
--- a/source/fuzz/replayer.cpp
+++ b/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"
@@ -99,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) {
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index f18c86b..6f008fc 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -195,9 +195,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/source/fuzz/transformation.h b/source/fuzz/transformation.h
index dbe803f..dbd0fe2 100644
--- a/source/fuzz/transformation.h
+++ b/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/source/fuzz/transformation_access_chain.cpp b/source/fuzz/transformation_access_chain.cpp
index 8c31006..ff17c36 100644
--- a/source/fuzz/transformation_access_chain.cpp
+++ b/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/source/fuzz/transformation_access_chain.h b/source/fuzz/transformation_access_chain.h
index 92d9e6a..9306a59 100644
--- a/source/fuzz/transformation_access_chain.h
+++ b/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/source/fuzz/transformation_add_constant_boolean.cpp b/source/fuzz/transformation_add_constant_boolean.cpp
index 21c8ed3..1930f7e 100644
--- a/source/fuzz/transformation_add_constant_boolean.cpp
+++ b/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/source/fuzz/transformation_add_constant_boolean.h b/source/fuzz/transformation_add_constant_boolean.h
index 79df1cd..5d876cf 100644
--- a/source/fuzz/transformation_add_constant_boolean.h
+++ b/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/source/fuzz/transformation_add_constant_composite.cpp b/source/fuzz/transformation_add_constant_composite.cpp
index 7ba1ea4..ae34b26 100644
--- a/source/fuzz/transformation_add_constant_composite.cpp
+++ b/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/source/fuzz/transformation_add_constant_composite.h b/source/fuzz/transformation_add_constant_composite.h
index 9a824a0..4fec561 100644
--- a/source/fuzz/transformation_add_constant_composite.h
+++ b/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/source/fuzz/transformation_add_constant_scalar.cpp b/source/fuzz/transformation_add_constant_scalar.cpp
index 36af5e0..e13d08f 100644
--- a/source/fuzz/transformation_add_constant_scalar.cpp
+++ b/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/source/fuzz/transformation_add_constant_scalar.h b/source/fuzz/transformation_add_constant_scalar.h
index 914cfe6..e0ed39f 100644
--- a/source/fuzz/transformation_add_constant_scalar.h
+++ b/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/source/fuzz/transformation_add_dead_block.cpp b/source/fuzz/transformation_add_dead_block.cpp
index b58f75e..b246c3f 100644
--- a/source/fuzz/transformation_add_dead_block.cpp
+++ b/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/source/fuzz/transformation_add_dead_block.h b/source/fuzz/transformation_add_dead_block.h
index 059daca..7d07616 100644
--- a/source/fuzz/transformation_add_dead_block.h
+++ b/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/source/fuzz/transformation_add_dead_break.cpp b/source/fuzz/transformation_add_dead_break.cpp
index 43847fa..db9de7d 100644
--- a/source/fuzz/transformation_add_dead_break.cpp
+++ b/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/source/fuzz/transformation_add_dead_break.h b/source/fuzz/transformation_add_dead_break.h
index 81a2c99..0ea9210 100644
--- a/source/fuzz/transformation_add_dead_break.h
+++ b/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/source/fuzz/transformation_add_dead_continue.cpp b/source/fuzz/transformation_add_dead_continue.cpp
index 3a4875e..1fc6d67 100644
--- a/source/fuzz/transformation_add_dead_continue.cpp
+++ b/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/source/fuzz/transformation_add_dead_continue.h b/source/fuzz/transformation_add_dead_continue.h
index 86b4c93..1053c16 100644
--- a/source/fuzz/transformation_add_dead_continue.h
+++ b/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/source/fuzz/transformation_add_function.cpp b/source/fuzz/transformation_add_function.cpp
index 8f0d3c9..45fe342 100644
--- a/source/fuzz/transformation_add_function.cpp
+++ b/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,18 +795,18 @@
     // 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();
 
@@ -805,20 +816,20 @@
              "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 +843,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 +851,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,8 +862,8 @@
 
       // Replace %index with %t2.
       access_chain_inst->SetInOperand(index, {select_id});
-      fuzzerutil::UpdateModuleIdBound(context, compare_id);
-      fuzzerutil::UpdateModuleIdBound(context, select_id);
+      fuzzerutil::UpdateModuleIdBound(ir_context, compare_id);
+      fuzzerutil::UpdateModuleIdBound(ir_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
@@ -870,16 +881,16 @@
       }
     }
     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);
@@ -893,7 +904,7 @@
 }
 
 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()) {
@@ -905,12 +916,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 +935,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/source/fuzz/transformation_add_function.h b/source/fuzz/transformation_add_function.h
index 848b799..5af197b 100644
--- a/source/fuzz/transformation_add_function.h
+++ b/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/source/fuzz/transformation_add_global_undef.cpp b/source/fuzz/transformation_add_global_undef.cpp
index f9585b3..ba45f22 100644
--- a/source/fuzz/transformation_add_global_undef.cpp
+++ b/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/source/fuzz/transformation_add_global_undef.h b/source/fuzz/transformation_add_global_undef.h
index 550d9f6..c89fe9d 100644
--- a/source/fuzz/transformation_add_global_undef.h
+++ b/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/source/fuzz/transformation_add_global_variable.cpp b/source/fuzz/transformation_add_global_variable.cpp
index e4f9f7a..c016428 100644
--- a/source/fuzz/transformation_add_global_variable.cpp
+++ b/source/fuzz/transformation_add_global_variable.cpp
@@ -33,14 +33,13 @@
 }
 
 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 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;
   }
@@ -55,7 +54,7 @@
   }
   // The initializer id must be the id of a constant.  Check this with the
   // constant manager.
-  auto constant_id = context->get_constant_mgr()->GetConstantsFromIds(
+  auto constant_id = ir_context->get_constant_mgr()->GetConstantsFromIds(
       {message_.initializer_id()});
   if (constant_id.empty()) {
     return false;
@@ -71,7 +70,8 @@
 }
 
 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}});
@@ -79,12 +79,12 @@
     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 (PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(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 +94,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 {
@@ -116,11 +118,11 @@
 
 bool TransformationAddGlobalVariable::
     PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(
-        opt::IRContext* context) {
+        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/source/fuzz/transformation_add_global_variable.h b/source/fuzz/transformation_add_global_variable.h
index 920ac45..f28af44 100644
--- a/source/fuzz/transformation_add_global_variable.h
+++ b/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 {
@@ -37,23 +37,25 @@
   //   class
   // - |message_.initializer_id| must 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|.
   //
-  // 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);
+      opt::IRContext* ir_context);
 
   protobufs::TransformationAddGlobalVariable message_;
 };
diff --git a/source/fuzz/transformation_add_local_variable.cpp b/source/fuzz/transformation_add_local_variable.cpp
index 69e536d..5136249 100644
--- a/source/fuzz/transformation_add_local_variable.cpp
+++ b/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/source/fuzz/transformation_add_local_variable.h b/source/fuzz/transformation_add_local_variable.h
index b8e00dd..6460904 100644
--- a/source/fuzz/transformation_add_local_variable.h
+++ b/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/source/fuzz/transformation_add_no_contraction_decoration.cpp b/source/fuzz/transformation_add_no_contraction_decoration.cpp
index 7f22cc2..4668534 100644
--- a/source/fuzz/transformation_add_no_contraction_decoration.cpp
+++ b/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/source/fuzz/transformation_add_no_contraction_decoration.h b/source/fuzz/transformation_add_no_contraction_decoration.h
index cec1b2c..27c3a80 100644
--- a/source/fuzz/transformation_add_no_contraction_decoration.h
+++ b/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/source/fuzz/transformation_add_type_array.cpp b/source/fuzz/transformation_add_type_array.cpp
index 2074e98..8f5af07 100644
--- a/source/fuzz/transformation_add_type_array.cpp
+++ b/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/source/fuzz/transformation_add_type_array.h b/source/fuzz/transformation_add_type_array.h
index b6e0718..5e9b8aa 100644
--- a/source/fuzz/transformation_add_type_array.h
+++ b/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/source/fuzz/transformation_add_type_boolean.cpp b/source/fuzz/transformation_add_type_boolean.cpp
index b55028a..77409a8 100644
--- a/source/fuzz/transformation_add_type_boolean.cpp
+++ b/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/source/fuzz/transformation_add_type_boolean.h b/source/fuzz/transformation_add_type_boolean.h
index 98c1e63..5ce5b9a 100644
--- a/source/fuzz/transformation_add_type_boolean.h
+++ b/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/source/fuzz/transformation_add_type_float.cpp b/source/fuzz/transformation_add_type_float.cpp
index d2af5f8..80716e1 100644
--- a/source/fuzz/transformation_add_type_float.cpp
+++ b/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/source/fuzz/transformation_add_type_float.h b/source/fuzz/transformation_add_type_float.h
index 0fdc831..a8fa0e1 100644
--- a/source/fuzz/transformation_add_type_float.h
+++ b/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/source/fuzz/transformation_add_type_function.cpp b/source/fuzz/transformation_add_type_function.cpp
index 4b6717b..991a28b 100644
--- a/source/fuzz/transformation_add_type_function.cpp
+++ b/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/source/fuzz/transformation_add_type_function.h b/source/fuzz/transformation_add_type_function.h
index 3880963..f26b250 100644
--- a/source/fuzz/transformation_add_type_function.h
+++ b/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/source/fuzz/transformation_add_type_int.cpp b/source/fuzz/transformation_add_type_int.cpp
index 6f59270..a932a5f 100644
--- a/source/fuzz/transformation_add_type_int.cpp
+++ b/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/source/fuzz/transformation_add_type_int.h b/source/fuzz/transformation_add_type_int.h
index 86342d0..5c3c959 100644
--- a/source/fuzz/transformation_add_type_int.h
+++ b/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/source/fuzz/transformation_add_type_matrix.cpp b/source/fuzz/transformation_add_type_matrix.cpp
index 07ab705..2c24eaa 100644
--- a/source/fuzz/transformation_add_type_matrix.cpp
+++ b/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/source/fuzz/transformation_add_type_matrix.h b/source/fuzz/transformation_add_type_matrix.h
index 69d6389..6d0724e 100644
--- a/source/fuzz/transformation_add_type_matrix.h
+++ b/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/source/fuzz/transformation_add_type_pointer.cpp b/source/fuzz/transformation_add_type_pointer.cpp
index 426985a..6cc8171 100644
--- a/source/fuzz/transformation_add_type_pointer.cpp
+++ b/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/source/fuzz/transformation_add_type_pointer.h b/source/fuzz/transformation_add_type_pointer.h
index 2b9ff77..3b50a29 100644
--- a/source/fuzz/transformation_add_type_pointer.h
+++ b/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/source/fuzz/transformation_add_type_struct.cpp b/source/fuzz/transformation_add_type_struct.cpp
index 1ae8372..6ce5ea1 100644
--- a/source/fuzz/transformation_add_type_struct.cpp
+++ b/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/source/fuzz/transformation_add_type_struct.h b/source/fuzz/transformation_add_type_struct.h
index edf3ec6..86a532d 100644
--- a/source/fuzz/transformation_add_type_struct.h
+++ b/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/source/fuzz/transformation_add_type_vector.cpp b/source/fuzz/transformation_add_type_vector.cpp
index 3fdf50b..f7b2fb5 100644
--- a/source/fuzz/transformation_add_type_vector.cpp
+++ b/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/source/fuzz/transformation_add_type_vector.h b/source/fuzz/transformation_add_type_vector.h
index af840f5..240f7cc 100644
--- a/source/fuzz/transformation_add_type_vector.h
+++ b/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/source/fuzz/transformation_composite_construct.cpp b/source/fuzz/transformation_composite_construct.cpp
index 9c63c1d..cd4f22f 100644
--- a/source/fuzz/transformation_composite_construct.cpp
+++ b/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/source/fuzz/transformation_composite_construct.h b/source/fuzz/transformation_composite_construct.h
index 5369c4c..2e55e70 100644
--- a/source/fuzz/transformation_composite_construct.h
+++ b/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/source/fuzz/transformation_composite_extract.cpp b/source/fuzz/transformation_composite_extract.cpp
index 5d3a386..3dc3953 100644
--- a/source/fuzz/transformation_composite_extract.cpp
+++ b/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/source/fuzz/transformation_composite_extract.h b/source/fuzz/transformation_composite_extract.h
index c4c9278..8f52d22 100644
--- a/source/fuzz/transformation_composite_extract.h
+++ b/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/source/fuzz/transformation_context.cpp b/source/fuzz/transformation_context.cpp
new file mode 100644
index 0000000..9c8a90f
--- /dev/null
+++ b/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/source/fuzz/transformation_context.h b/source/fuzz/transformation_context.h
new file mode 100644
index 0000000..37e15a2
--- /dev/null
+++ b/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/source/fuzz/transformation_copy_object.cpp b/source/fuzz/transformation_copy_object.cpp
index bfdced3..7b5b5c9 100644
--- a/source/fuzz/transformation_copy_object.cpp
+++ b/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/source/fuzz/transformation_copy_object.h b/source/fuzz/transformation_copy_object.h
index 9e9c26a..80d57ae 100644
--- a/source/fuzz/transformation_copy_object.h
+++ b/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/source/fuzz/transformation_equation_instruction.cpp b/source/fuzz/transformation_equation_instruction.cpp
index 21b67f6..5c31417 100644
--- a/source/fuzz/transformation_equation_instruction.cpp
+++ b/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/source/fuzz/transformation_equation_instruction.h b/source/fuzz/transformation_equation_instruction.h
index 2456ba5..7eec9c6 100644
--- a/source/fuzz/transformation_equation_instruction.h
+++ b/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/source/fuzz/transformation_function_call.cpp b/source/fuzz/transformation_function_call.cpp
index cea8537..432634d 100644
--- a/source/fuzz/transformation_function_call.cpp
+++ b/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/source/fuzz/transformation_function_call.h b/source/fuzz/transformation_function_call.h
index a9ae5be..4ad7db1 100644
--- a/source/fuzz/transformation_function_call.h
+++ b/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/source/fuzz/transformation_load.cpp b/source/fuzz/transformation_load.cpp
index 4cba37d..a260c33 100644
--- a/source/fuzz/transformation_load.cpp
+++ b/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/source/fuzz/transformation_load.h b/source/fuzz/transformation_load.h
index ff99016..4c7c00b 100644
--- a/source/fuzz/transformation_load.h
+++ b/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/source/fuzz/transformation_merge_blocks.cpp b/source/fuzz/transformation_merge_blocks.cpp
index 316e80d..68ac092 100644
--- a/source/fuzz/transformation_merge_blocks.cpp
+++ b/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/source/fuzz/transformation_merge_blocks.h b/source/fuzz/transformation_merge_blocks.h
index 86216db..1dc16d2 100644
--- a/source/fuzz/transformation_merge_blocks.h
+++ b/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/source/fuzz/transformation_move_block_down.cpp b/source/fuzz/transformation_move_block_down.cpp
index f181855..6c71ab7 100644
--- a/source/fuzz/transformation_move_block_down.cpp
+++ b/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/source/fuzz/transformation_move_block_down.h b/source/fuzz/transformation_move_block_down.h
index fd1584a..7551c38 100644
--- a/source/fuzz/transformation_move_block_down.h
+++ b/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/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp
index 01d1c45..117cdc6 100644
--- a/source/fuzz/transformation_outline_function.cpp
+++ b/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
@@ -210,9 +210,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 +227,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 +237,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 +249,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 +257,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()) {
@@ -277,7 +278,7 @@
   // can hold the value for this id computed in the outlined function.
   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)) {
+  for (auto id : GetRegionOutputIds(ir_context, region_set, exit_block)) {
     if (output_id_to_fresh_id_map.count(id) == 0) {
       return false;
     }
@@ -287,25 +288,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 +315,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 +333,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 +391,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 +435,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 +472,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 +484,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 +518,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 +541,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 +554,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 +575,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 +596,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 +610,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 +621,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 +637,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 +673,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 +693,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 +719,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 +730,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 +741,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 +759,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 +778,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 +834,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 +845,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 +923,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 +932,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/source/fuzz/transformation_outline_function.h b/source/fuzz/transformation_outline_function.h
index 5711790..ba439c8 100644
--- a/source/fuzz/transformation_outline_function.h
+++ b/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/source/fuzz/transformation_permute_function_parameters.cpp b/source/fuzz/transformation_permute_function_parameters.cpp
index 2141533..0f1220e 100644
--- a/source/fuzz/transformation_permute_function_parameters.cpp
+++ b/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/source/fuzz/transformation_permute_function_parameters.h b/source/fuzz/transformation_permute_function_parameters.h
index c67a735..994e4c2 100644
--- a/source/fuzz/transformation_permute_function_parameters.h
+++ b/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/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
index 72d9b22..d6f17fc 100644
--- a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
+++ b/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/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h
index f74cd8d..3abb485 100644
--- a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h
+++ b/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/source/fuzz/transformation_replace_constant_with_uniform.cpp b/source/fuzz/transformation_replace_constant_with_uniform.cpp
index 8e0e4e5..a8f9495 100644
--- a/source/fuzz/transformation_replace_constant_with_uniform.cpp
+++ b/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/source/fuzz/transformation_replace_constant_with_uniform.h b/source/fuzz/transformation_replace_constant_with_uniform.h
index ed354b1..b72407c 100644
--- a/source/fuzz/transformation_replace_constant_with_uniform.h
+++ b/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/source/fuzz/transformation_replace_id_with_synonym.cpp b/source/fuzz/transformation_replace_id_with_synonym.cpp
index 88c977a..87194c8 100644
--- a/source/fuzz/transformation_replace_id_with_synonym.cpp
+++ b/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, ir_context)) {
     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();
@@ -124,7 +127,7 @@
             composite_type_being_accessed->AsArray()->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,16 +152,16 @@
     // 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;
diff --git a/source/fuzz/transformation_replace_id_with_synonym.h b/source/fuzz/transformation_replace_id_with_synonym.h
index 48132c1..a5a9dfd 100644
--- a/source/fuzz/transformation_replace_id_with_synonym.h
+++ b/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/source/fuzz/transformation_set_function_control.cpp b/source/fuzz/transformation_set_function_control.cpp
index d2b61f1..d01e743 100644
--- a/source/fuzz/transformation_set_function_control.cpp
+++ b/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/source/fuzz/transformation_set_function_control.h b/source/fuzz/transformation_set_function_control.h
index 0526bb9..5109f74 100644
--- a/source/fuzz/transformation_set_function_control.h
+++ b/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/source/fuzz/transformation_set_loop_control.cpp b/source/fuzz/transformation_set_loop_control.cpp
index 9062f17..845ac69 100644
--- a/source/fuzz/transformation_set_loop_control.cpp
+++ b/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/source/fuzz/transformation_set_loop_control.h b/source/fuzz/transformation_set_loop_control.h
index 28b148c..f0c364f 100644
--- a/source/fuzz/transformation_set_loop_control.h
+++ b/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/source/fuzz/transformation_set_memory_operands_mask.cpp b/source/fuzz/transformation_set_memory_operands_mask.cpp
index a14e1a6..131a499 100644
--- a/source/fuzz/transformation_set_memory_operands_mask.cpp
+++ b/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/source/fuzz/transformation_set_memory_operands_mask.h b/source/fuzz/transformation_set_memory_operands_mask.h
index 20ae145..9f5081b 100644
--- a/source/fuzz/transformation_set_memory_operands_mask.h
+++ b/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/source/fuzz/transformation_set_selection_control.cpp b/source/fuzz/transformation_set_selection_control.cpp
index ebabdef..bee1e35 100644
--- a/source/fuzz/transformation_set_selection_control.cpp
+++ b/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/source/fuzz/transformation_set_selection_control.h b/source/fuzz/transformation_set_selection_control.h
index 19e0c3c..21fbdda 100644
--- a/source/fuzz/transformation_set_selection_control.h
+++ b/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/source/fuzz/transformation_split_block.cpp b/source/fuzz/transformation_split_block.cpp
index fc5229e..3de081e 100644
--- a/source/fuzz/transformation_split_block.cpp
+++ b/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.");
@@ -79,12 +80,13 @@
            split_before->NumInOperands() != 2);
 }
 
-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 +95,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 +119,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/source/fuzz/transformation_split_block.h b/source/fuzz/transformation_split_block.h
index a193fc7..3bf6dfd 100644
--- a/source/fuzz/transformation_split_block.h
+++ b/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/source/fuzz/transformation_store.cpp b/source/fuzz/transformation_store.cpp
index 7cb7611..3df1b7d 100644
--- a/source/fuzz/transformation_store.cpp
+++ b/source/fuzz/transformation_store.cpp
@@ -34,16 +34,16 @@
 }
 
 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;
@@ -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/source/fuzz/transformation_store.h b/source/fuzz/transformation_store.h
index 699afdd..6746aab 100644
--- a/source/fuzz/transformation_store.h
+++ b/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/source/fuzz/transformation_swap_commutable_operands.cpp b/source/fuzz/transformation_swap_commutable_operands.cpp
index 49d9de8..b7622a2 100644
--- a/source/fuzz/transformation_swap_commutable_operands.cpp
+++ b/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/source/fuzz/transformation_swap_commutable_operands.h b/source/fuzz/transformation_swap_commutable_operands.h
index 061e92d..7fe5b70 100644
--- a/source/fuzz/transformation_swap_commutable_operands.h
+++ b/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/source/fuzz/transformation_toggle_access_chain_instruction.cpp b/source/fuzz/transformation_toggle_access_chain_instruction.cpp
index ace331a..ca24a18 100644
--- a/source/fuzz/transformation_toggle_access_chain_instruction.cpp
+++ b/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/source/fuzz/transformation_toggle_access_chain_instruction.h b/source/fuzz/transformation_toggle_access_chain_instruction.h
index 125e1ab..9cd8fd6 100644
--- a/source/fuzz/transformation_toggle_access_chain_instruction.h
+++ b/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/source/fuzz/transformation_vector_shuffle.cpp b/source/fuzz/transformation_vector_shuffle.cpp
index e2d889d..ee64292 100644
--- a/source/fuzz/transformation_vector_shuffle.cpp
+++ b/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/source/fuzz/transformation_vector_shuffle.h b/source/fuzz/transformation_vector_shuffle.h
index 81ed227..f73fc31 100644
--- a/source/fuzz/transformation_vector_shuffle.h
+++ b/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/test/fuzz/data_synonym_transformation_test.cpp b/test/fuzz/data_synonym_transformation_test.cpp
index 21ea068..2d26d17 100644
--- a/test/fuzz/data_synonym_transformation_test.cpp
+++ b/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,63 @@
   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);
   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 +936,26 @@
   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);
   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 +963,13 @@
       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);
   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 +979,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 +993,13 @@
       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);
   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 +1007,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 +1021,13 @@
       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);
   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 +1035,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 +1049,26 @@
       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);
   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 +1076,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/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp b/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp
index 89f006e..90d1c84 100644
--- a/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp
+++ b/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp
@@ -64,10 +64,14 @@
   ASSERT_TRUE(IsValid(env, 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;
 
-  FuzzerPassAddUsefulConstructs pass(context.get(), &fact_manager,
+  FuzzerPassAddUsefulConstructs pass(context.get(), &transformation_context,
                                      &fuzzer_context, &transformation_sequence);
   pass.Apply();
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -173,6 +177,10 @@
   ASSERT_TRUE(IsValid(env, 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;
 
@@ -292,7 +300,7 @@
               context->get_constant_mgr()->FindConstant(&int_constant_8));
   }
 
-  FuzzerPassAddUsefulConstructs pass(context.get(), &fact_manager,
+  FuzzerPassAddUsefulConstructs pass(context.get(), &transformation_context,
                                      &fuzzer_context, &transformation_sequence);
   pass.Apply();
   ASSERT_TRUE(IsValid(env, context.get()));
diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp
index dc7ba3a..549dd13 100644
--- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp
+++ b/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);
   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,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);
 
@@ -393,13 +399,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);
 
@@ -481,13 +490,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);
 
@@ -658,13 +670,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/test/fuzz/transformation_access_chain_test.cpp b/test/fuzz/transformation_access_chain_test.cpp
index 516d371..443c31c 100644
--- a/test/fuzz/transformation_access_chain_test.cpp
+++ b/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/test/fuzz/transformation_add_constant_boolean_test.cpp b/test/fuzz/transformation_add_constant_boolean_test.cpp
index f51c46b..c603333 100644
--- a/test/fuzz/transformation_add_constant_boolean_test.cpp
+++ b/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/test/fuzz/transformation_add_constant_composite_test.cpp b/test/fuzz/transformation_add_constant_composite_test.cpp
index 5ce171b..021bf58 100644
--- a/test/fuzz/transformation_add_constant_composite_test.cpp
+++ b/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/test/fuzz/transformation_add_constant_scalar_test.cpp b/test/fuzz/transformation_add_constant_scalar_test.cpp
index b156111..5124b7d 100644
--- a/test/fuzz/transformation_add_constant_scalar_test.cpp
+++ b/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/test/fuzz/transformation_add_dead_block_test.cpp b/test/fuzz/transformation_add_dead_block_test.cpp
index f89140f..c9be520 100644
--- a/test/fuzz/transformation_add_dead_block_test.cpp
+++ b/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/test/fuzz/transformation_add_dead_break_test.cpp b/test/fuzz/transformation_add_dead_break_test.cpp
index d60fc1f..8400b0c 100644
--- a/test/fuzz/transformation_add_dead_break_test.cpp
+++ b/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/test/fuzz/transformation_add_dead_continue_test.cpp b/test/fuzz/transformation_add_dead_continue_test.cpp
index ff93da8..07ee3b1 100644
--- a/test/fuzz/transformation_add_dead_continue_test.cpp
+++ b/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/test/fuzz/transformation_add_function_test.cpp b/test/fuzz/transformation_add_function_test.cpp
index aed12dc..0d1f2c4 100644
--- a/test/fuzz/transformation_add_function_test.cpp
+++ b/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,6 +1855,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()));
@@ -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,6 +2013,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()));
@@ -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,6 +2169,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()));
@@ -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,6 +2317,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()));
@@ -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,6 +2475,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()));
@@ -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
diff --git a/test/fuzz/transformation_add_global_undef_test.cpp b/test/fuzz/transformation_add_global_undef_test.cpp
index c14f7e9..8c06db0 100644
--- a/test/fuzz/transformation_add_global_undef_test.cpp
+++ b/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/test/fuzz/transformation_add_global_variable_test.cpp b/test/fuzz/transformation_add_global_variable_test.cpp
index 619f068..9b8faa4 100644
--- a/test/fuzz/transformation_add_global_variable_test.cpp
+++ b/test/fuzz/transformation_add_global_variable_test.cpp
@@ -60,49 +60,52 @@
   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));
+                   .IsApplicable(context.get(), transformation_context));
   // %1 is not a type
   ASSERT_FALSE(TransformationAddGlobalVariable(100, 1, 0, false)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // %7 is not a pointer type
   ASSERT_FALSE(TransformationAddGlobalVariable(100, 7, 0, true)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // %9 does not have Private storage class
   ASSERT_FALSE(TransformationAddGlobalVariable(100, 9, 0, false)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // %15 does not have Private storage class
   ASSERT_FALSE(TransformationAddGlobalVariable(100, 15, 0, true)
-                   .IsApplicable(context.get(), fact_manager));
+                   .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));
+                   .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));
+                   .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));
+                   .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));
+                   .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));
+                   .IsApplicable(context.get(), transformation_context));
 
   TransformationAddGlobalVariable transformations[] = {
       // %100 = OpVariable %12 Private
@@ -124,15 +127,22 @@
       TransformationAddGlobalVariable(105, 19, 22, false)};
 
   for (auto& transformation : transformations) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        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,6 +233,9 @@
   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
@@ -235,12 +248,16 @@
       TransformationAddGlobalVariable(102, 19, 21, true)};
 
   for (auto& transformation : transformations) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        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"(
diff --git a/test/fuzz/transformation_add_local_variable_test.cpp b/test/fuzz/transformation_add_local_variable_test.cpp
index fd7047f..e989b33 100644
--- a/test/fuzz/transformation_add_local_variable_test.cpp
+++ b/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/test/fuzz/transformation_add_no_contraction_decoration_test.cpp b/test/fuzz/transformation_add_no_contraction_decoration_test.cpp
index b1a87ea..46841a5 100644
--- a/test/fuzz/transformation_add_no_contraction_decoration_test.cpp
+++ b/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/test/fuzz/transformation_add_type_array_test.cpp b/test/fuzz/transformation_add_type_array_test.cpp
index 2bcbe73..4392f99 100644
--- a/test/fuzz/transformation_add_type_array_test.cpp
+++ b/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/test/fuzz/transformation_add_type_boolean_test.cpp b/test/fuzz/transformation_add_type_boolean_test.cpp
index 9975953..60eabd9 100644
--- a/test/fuzz/transformation_add_type_boolean_test.cpp
+++ b/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/test/fuzz/transformation_add_type_float_test.cpp b/test/fuzz/transformation_add_type_float_test.cpp
index 67408da..7d17266 100644
--- a/test/fuzz/transformation_add_type_float_test.cpp
+++ b/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/test/fuzz/transformation_add_type_function_test.cpp b/test/fuzz/transformation_add_type_function_test.cpp
index 46bd436..1557bb8 100644
--- a/test/fuzz/transformation_add_type_function_test.cpp
+++ b/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/test/fuzz/transformation_add_type_int_test.cpp b/test/fuzz/transformation_add_type_int_test.cpp
index c6f884c..63b17c2 100644
--- a/test/fuzz/transformation_add_type_int_test.cpp
+++ b/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/test/fuzz/transformation_add_type_matrix_test.cpp b/test/fuzz/transformation_add_type_matrix_test.cpp
index 84f27e9..e925012 100644
--- a/test/fuzz/transformation_add_type_matrix_test.cpp
+++ b/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/test/fuzz/transformation_add_type_pointer_test.cpp b/test/fuzz/transformation_add_type_pointer_test.cpp
index e36707f..35303e4 100644
--- a/test/fuzz/transformation_add_type_pointer_test.cpp
+++ b/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/test/fuzz/transformation_add_type_struct_test.cpp b/test/fuzz/transformation_add_type_struct_test.cpp
index ae68c9a..06f78cd 100644
--- a/test/fuzz/transformation_add_type_struct_test.cpp
+++ b/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/test/fuzz/transformation_add_type_vector_test.cpp b/test/fuzz/transformation_add_type_vector_test.cpp
index 6ac4498..f1252a3 100644
--- a/test/fuzz/transformation_add_type_vector_test.cpp
+++ b/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/test/fuzz/transformation_composite_construct_test.cpp b/test/fuzz/transformation_composite_construct_test.cpp
index d303368..e9d7610 100644
--- a/test/fuzz/transformation_composite_construct_test.cpp
+++ b/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,17 +141,17 @@
   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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(41, {}), MakeDataDescriptor(200, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(45, {}), MakeDataDescriptor(200, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(27, {}), MakeDataDescriptor(200, {2}), context.get()));
 
   // Make a float[2]
@@ -157,15 +160,15 @@
   // 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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(24, {}), MakeDataDescriptor(201, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(40, {}), MakeDataDescriptor(201, {1}), context.get()));
 
   // Make a bool[3]
@@ -176,17 +179,17 @@
   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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(33, {}), MakeDataDescriptor(202, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {2}), context.get()));
 
   // make a uvec3[2][2]
@@ -195,17 +198,17 @@
   // 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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->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(100, {}), MakeDataDescriptor(203, {1}),
+      context.get()));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -393,6 +396,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,15 +406,16 @@
   // 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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(28, {}), MakeDataDescriptor(200, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(31, {}), MakeDataDescriptor(200, {2}), context.get()));
 
   // make a mat4x3
@@ -417,19 +424,20 @@
   // 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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(11, {}), MakeDataDescriptor(201, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(13, {}), MakeDataDescriptor(201, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->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(100, {}), MakeDataDescriptor(201, {3}),
+      context.get()));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -602,6 +610,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,13 +620,14 @@
   // 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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(19, {}), MakeDataDescriptor(200, {1}), context.get()));
 
   // make an Outer
@@ -626,16 +638,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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(200, {}), MakeDataDescriptor(201, {1}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(56, {}), MakeDataDescriptor(201, {2}), context.get()));
 
   std::string after_transformation = R"(
@@ -922,19 +935,23 @@
   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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(17, {}), MakeDataDescriptor(200, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(11, {}), MakeDataDescriptor(200, {1}), context.get()));
 
   TransformationCompositeConstruct make_vec3(
@@ -944,17 +961,18 @@
   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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(12, {0}), MakeDataDescriptor(201, {0}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(12, {1}), MakeDataDescriptor(201, {1}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(32, {}), MakeDataDescriptor(201, {2}), context.get()));
 
   TransformationCompositeConstruct make_vec4(
@@ -964,17 +982,18 @@
   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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(10, {}), MakeDataDescriptor(202, {2}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(11, {}), MakeDataDescriptor(202, {3}), context.get()));
 
   TransformationCompositeConstruct make_ivec2(
@@ -982,16 +1001,17 @@
   // 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}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(120, {}), MakeDataDescriptor(203, {1}),
+      context.get()));
 
   TransformationCompositeConstruct make_ivec3(
       114, {56, 117, 56}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
@@ -1000,16 +1020,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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(117, {}), MakeDataDescriptor(204, {1}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {2}), context.get()));
 
   TransformationCompositeConstruct make_ivec4(
@@ -1019,33 +1040,35 @@
   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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->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(117, {}), MakeDataDescriptor(205, {1}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {2}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {3}),
+      context.get()));
 
   TransformationCompositeConstruct make_uvec2(
       86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 0), 206);
   TransformationCompositeConstruct make_uvec2_bad(
       86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 200), 206);
-  ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_uvec2_bad.IsApplicable(context.get(), fact_manager));
-  make_uvec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(18, {}), MakeDataDescriptor(206, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(38, {}), MakeDataDescriptor(206, {1}), context.get()));
 
   TransformationCompositeConstruct make_uvec3(
@@ -1053,17 +1076,18 @@
   // 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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(14, {}), MakeDataDescriptor(207, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->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(136, {}), MakeDataDescriptor(207, {2}),
+      context.get()));
 
   TransformationCompositeConstruct make_uvec4(
       131, {14, 18, 136, 136},
@@ -1072,20 +1096,21 @@
   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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(14, {}), MakeDataDescriptor(208, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->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(136, {}), MakeDataDescriptor(208, {2}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(136, {}), MakeDataDescriptor(208, {3}),
+      context.get()));
 
   TransformationCompositeConstruct make_bvec2(
       102,
@@ -1102,14 +1127,15 @@
           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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(111, {}), MakeDataDescriptor(209, {0}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(41, {}), MakeDataDescriptor(209, {1}), context.get()));
 
   TransformationCompositeConstruct make_bvec3(
@@ -1117,17 +1143,18 @@
   // 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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {0}), MakeDataDescriptor(210, {0}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {1}), MakeDataDescriptor(210, {1}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(73, {}), MakeDataDescriptor(210, {2}), context.get()));
 
   TransformationCompositeConstruct make_bvec4(
@@ -1135,22 +1162,23 @@
   // 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}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {1}), MakeDataDescriptor(211, {1}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {0}), MakeDataDescriptor(211, {2}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {1}), MakeDataDescriptor(211, {3}),
+      context.get()));
 
   std::string after_transformation = R"(
                OpCapability Shader
diff --git a/test/fuzz/transformation_composite_extract_test.cpp b/test/fuzz/transformation_composite_extract_test.cpp
index 5cc2115..d57b62c 100644
--- a/test/fuzz/transformation_composite_extract_test.cpp
+++ b/test/fuzz/transformation_composite_extract_test.cpp
@@ -96,100 +96,109 @@
   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}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(202, {}), MakeDataDescriptor(104, {0, 2}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(203, {}), MakeDataDescriptor(104, {0}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(204, {}), MakeDataDescriptor(101, {0}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(205, {}), MakeDataDescriptor(102, {2}),
+      context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(206, {}), MakeDataDescriptor(103, {1}),
+      context.get()));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -348,49 +357,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/test/fuzz/transformation_copy_object_test.cpp b/test/fuzz/transformation_copy_object_test.cpp
index b85f75b..fa5a5b1 100644
--- a/test/fuzz/transformation_copy_object_test.cpp
+++ b/test/fuzz/transformation_copy_object_test.cpp
@@ -51,77 +51,95 @@
   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(context.get())
+                   .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(
+            context.get());
     ASSERT_EQ(2, ids_for_which_synonyms_are_known.size());
     ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
                           ids_for_which_synonyms_are_known.end(),
                           7) != ids_for_which_synonyms_are_known.end());
-    ASSERT_EQ(2, fact_manager.GetSynonymsForId(7, context.get()).size());
+    ASSERT_EQ(2, transformation_context.GetFactManager()
+                     ->GetSynonymsForId(7, context.get())
+                     .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, context.get()));
   }
 
   {
     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(
+            context.get());
     ASSERT_EQ(4, ids_for_which_synonyms_are_known.size());
     ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
                           ids_for_which_synonyms_are_known.end(),
                           8) != ids_for_which_synonyms_are_known.end());
-    ASSERT_EQ(2, fact_manager.GetSynonymsForId(8, context.get()).size());
+    ASSERT_EQ(2, transformation_context.GetFactManager()
+                     ->GetSynonymsForId(8, context.get())
+                     .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, context.get()));
   }
 
   {
     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(
+            context.get());
     ASSERT_EQ(5, ids_for_which_synonyms_are_known.size());
     ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
                           ids_for_which_synonyms_are_known.end(),
                           101) != ids_for_which_synonyms_are_known.end());
-    ASSERT_EQ(3, fact_manager.GetSynonymsForId(101, context.get()).size());
+    ASSERT_EQ(3, transformation_context.GetFactManager()
+                     ->GetSynonymsForId(101, context.get())
+                     .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, context.get()));
   }
 
   {
     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(
+            context.get());
     ASSERT_EQ(6, ids_for_which_synonyms_are_known.size());
     ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
                           ids_for_which_synonyms_are_known.end(),
                           7) != ids_for_which_synonyms_are_known.end());
-    ASSERT_EQ(3, fact_manager.GetSynonymsForId(7, context.get()).size());
+    ASSERT_EQ(3, transformation_context.GetFactManager()
+                     ->GetSynonymsForId(7, context.get())
+                     .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, context.get()));
   }
 
   std::string after_transformation = R"(
@@ -340,116 +358,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 +536,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 +557,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 +639,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 +683,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 +696,26 @@
   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));
 }
 
 }  // namespace
diff --git a/test/fuzz/transformation_equation_instruction_test.cpp b/test/fuzz/transformation_equation_instruction_test.cpp
index 81d849b..be9024e 100644
--- a/test/fuzz/transformation_equation_instruction_test.cpp
+++ b/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,58 +58,60 @@
   // 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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get()));
 
   std::string after_transformation = R"(
@@ -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,31 +176,33 @@
   // 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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get()));
 
   std::string after_transformation = R"(
@@ -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,58 +268,63 @@
   // 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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(15, {}), MakeDataDescriptor(19, {}), context.get()));
 
   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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
 
   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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(24, {}), MakeDataDescriptor(15, {}), context.get()));
 
   std::string after_transformation = R"(
@@ -364,68 +382,79 @@
   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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {}), context.get()));
 
   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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {}), context.get()));
 
   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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
 
   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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {}), context.get()));
 
   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(
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {}), context.get()));
 
   std::string after_transformation = R"(
diff --git a/test/fuzz/transformation_function_call_test.cpp b/test/fuzz/transformation_function_call_test.cpp
index 9bd971e..d7305f8 100644
--- a/test/fuzz/transformation_function_call_test.cpp
+++ b/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/test/fuzz/transformation_load_test.cpp b/test/fuzz/transformation_load_test.cpp
index 1f728ff..18ca195 100644
--- a/test/fuzz/transformation_load_test.cpp
+++ b/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/test/fuzz/transformation_merge_blocks_test.cpp b/test/fuzz/transformation_merge_blocks_test.cpp
index e2b4aa6..4500445 100644
--- a/test/fuzz/transformation_merge_blocks_test.cpp
+++ b/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/test/fuzz/transformation_move_block_down_test.cpp b/test/fuzz/transformation_move_block_down_test.cpp
index 02761a2..662e88c 100644
--- a/test/fuzz/transformation_move_block_down_test.cpp
+++ b/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/test/fuzz/transformation_outline_function_test.cpp b/test/fuzz/transformation_outline_function_test.cpp
index 40aaebc..7beed85 100644
--- a/test/fuzz/transformation_outline_function_test.cpp
+++ b/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,16 +2443,20 @@
       /*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));
   }
 }
 
@@ -2423,6 +2568,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 +2584,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 +2737,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 +2753,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 +2796,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 +2812,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 +2889,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 +2905,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/test/fuzz/transformation_permute_function_parameters_test.cpp b/test/fuzz/transformation_permute_function_parameters_test.cpp
index 1af4699..a4a7c00 100644
--- a/test/fuzz/transformation_permute_function_parameters_test.cpp
+++ b/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/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp b/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp
index 527a7b7..b320308 100644
--- a/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp
+++ b/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp
@@ -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/test/fuzz/transformation_replace_constant_with_uniform_test.cpp b/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
index 58d4a89..8cbba46 100644
--- a/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
+++ b/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/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
index 41b6116..d563afa 100644
--- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp
+++ b/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"(
diff --git a/test/fuzz/transformation_set_function_control_test.cpp b/test/fuzz/transformation_set_function_control_test.cpp
index 536e965..be7f2be 100644
--- a/test/fuzz/transformation_set_function_control_test.cpp
+++ b/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/test/fuzz/transformation_set_loop_control_test.cpp b/test/fuzz/transformation_set_loop_control_test.cpp
index 83953ec..531aa7a 100644
--- a/test/fuzz/transformation_set_loop_control_test.cpp
+++ b/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/test/fuzz/transformation_set_memory_operands_mask_test.cpp b/test/fuzz/transformation_set_memory_operands_mask_test.cpp
index ad4dc25..c02d8d4 100644
--- a/test/fuzz/transformation_set_memory_operands_mask_test.cpp
+++ b/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/test/fuzz/transformation_set_selection_control_test.cpp b/test/fuzz/transformation_set_selection_control_test.cpp
index 9696417..9afb89d 100644
--- a/test/fuzz/transformation_set_selection_control_test.cpp
+++ b/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/test/fuzz/transformation_split_block_test.cpp b/test/fuzz/transformation_split_block_test.cpp
index 09007a5..8e9e8a4 100644
--- a/test/fuzz/transformation_split_block_test.cpp
+++ b/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
diff --git a/test/fuzz/transformation_store_test.cpp b/test/fuzz/transformation_store_test.cpp
index 3fb9b61..067a5a1 100644
--- a/test/fuzz/transformation_store_test.cpp
+++ b/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()));
   }
 
diff --git a/test/fuzz/transformation_swap_commutable_operands_test.cpp b/test/fuzz/transformation_swap_commutable_operands_test.cpp
index f0591cf..c213dfe 100644
--- a/test/fuzz/transformation_swap_commutable_operands_test.cpp
+++ b/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/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp b/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp
index 98e0a64..b20f59e 100644
--- a/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp
+++ b/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/test/fuzz/transformation_vector_shuffle_test.cpp b/test/fuzz/transformation_vector_shuffle_test.cpp
index 385c38b..e3c7fd4 100644
--- a/test/fuzz/transformation_vector_shuffle_test.cpp
+++ b/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, context.get()));
   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, context.get()));
 
   TransformationVectorShuffle transformation2(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 201, 20, 12,
       {0xFFFFFFFF, 3, 5});
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  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, context.get()));
   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, context.get()));
 
   TransformationVectorShuffle transformation3(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 202, 27, 35, {5, 4, 1});
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  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, context.get()));
   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, context.get()));
   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, context.get()));
 
   TransformationVectorShuffle transformation4(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 203, 42, 46, {0, 1});
-  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  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, context.get()));
   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, context.get()));
 
   TransformationVectorShuffle transformation5(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 204, 42, 46, {2, 3, 4});
-  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
-  transformation5.Apply(context.get(), &fact_manager);
+  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, context.get()));
   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, context.get()));
   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, context.get()));
 
   TransformationVectorShuffle transformation6(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 205, 42, 42,
       {0, 1, 2, 3});
-  ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
-  transformation6.Apply(context.get(), &fact_manager);
+  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, context.get()));
   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, context.get()));
   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, context.get()));
   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, context.get()));
 
   // swizzle vec4 from vec4 and vec4 using some undefs
   TransformationVectorShuffle transformation7(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 206, 65, 65,
       {0xFFFFFFFF, 3, 6, 0xFFFFFFFF});
-  ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager));
-  transformation7.Apply(context.get(), &fact_manager);
+  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, context.get()));
 
   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/tools/fuzz/fuzz.cpp b/tools/fuzz/fuzz.cpp
index 469c81b..2c9807d 100644
--- a/tools/fuzz/fuzz.cpp
+++ b/tools/fuzz/fuzz.cpp
@@ -580,7 +580,8 @@
 
   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;
       }