Squashed 'third_party/SPIRV-Tools/' changes from fd773eb50d6..55af3902fc2

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

git-subtree-dir: third_party/SPIRV-Tools
git-subtree-split: 55af3902fc24db889b0ef8010a83efca04a6352c
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 99a78fd..dca142a 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -21,12 +21,13 @@
           equivalence_relation_test.cpp
           fact_manager_test.cpp
           fuzz_test_util.cpp
-          fuzzer_pass_add_useful_constructs_test.cpp
+          fuzzer_pass_construct_composites_test.cpp
           fuzzer_pass_donate_modules_test.cpp
           instruction_descriptor_test.cpp
           transformation_access_chain_test.cpp
           transformation_add_constant_boolean_test.cpp
           transformation_add_constant_composite_test.cpp
+          transformation_add_constant_null_test.cpp
           transformation_add_constant_scalar_test.cpp
           transformation_add_dead_block_test.cpp
           transformation_add_dead_break_test.cpp
@@ -45,8 +46,10 @@
           transformation_add_type_pointer_test.cpp
           transformation_add_type_struct_test.cpp
           transformation_add_type_vector_test.cpp
+          transformation_adjust_branch_weights_test.cpp
           transformation_composite_construct_test.cpp
           transformation_composite_extract_test.cpp
+          transformation_compute_data_synonym_fact_closure_test.cpp
           transformation_copy_object_test.cpp
           transformation_equation_instruction_test.cpp
           transformation_function_call_test.cpp
diff --git a/test/fuzz/data_synonym_transformation_test.cpp b/test/fuzz/data_synonym_transformation_test.cpp
index 21ea068..66ce769 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,65 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
-  fact_manager.AddFact(MakeSynonymFact(20, {0}, 100, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(20, {1}, 100, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(20, {2}, 100, {2}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(54, {}, 100, {3}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(15, {0}, 101, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(15, {1}, 101, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(19, {0}, 101, {2}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(19, {1}, 101, {3}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(27, {}, 102, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(15, {0}, 102, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(15, {1}, 102, {2}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(33, {}, 103, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(47, {0}, 103, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(47, {1}, 103, {2}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(47, {2}, 103, {3}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(42, {}, 104, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(45, {}, 104, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(38, {0}, 105, {0}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(38, {1}, 105, {1}), context.get());
-  fact_manager.AddFact(MakeSynonymFact(46, {}, 105, {2}), context.get());
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(20, {0}, 100, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(20, {1}, 100, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(20, {2}, 100, {2}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(54, {}, 100, {3}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(15, {0}, 101, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(15, {1}, 101, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(19, {0}, 101, {2}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(19, {1}, 101, {3}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(27, {}, 102, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(15, {0}, 102, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(15, {1}, 102, {2}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(33, {}, 103, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(47, {0}, 103, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(47, {1}, 103, {2}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(47, {2}, 103, {3}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(42, {}, 104, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(45, {}, 104, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(38, {0}, 105, {0}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(38, {1}, 105, {1}), context.get());
+  transformation_context.GetFactManager()->AddFact(
+      MakeSynonymFact(46, {}, 105, {2}), context.get());
 
   // Replace %20 with %100[0:2] in '%80 = OpCopyObject %16 %20'
   auto instruction_descriptor_1 =
       MakeInstructionDescriptor(80, SpvOpCopyObject, 0);
   auto shuffle_1 = TransformationVectorShuffle(instruction_descriptor_1, 200,
                                                100, 100, {0, 1, 2});
-  ASSERT_TRUE(shuffle_1.IsApplicable(context.get(), fact_manager));
-  shuffle_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(shuffle_1.IsApplicable(context.get(), transformation_context));
+  shuffle_1.Apply(context.get(), &transformation_context);
+  fact_manager.ComputeClosureOfFacts(context.get(), 100);
+
   auto replacement_1 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(20, instruction_descriptor_1, 0), 200);
-  ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
-  replacement_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_1.IsApplicable(context.get(), transformation_context));
+  replacement_1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %54 with %100[3] in '%56 = OpFOrdNotEqual %30 %54 %55'
@@ -856,24 +938,28 @@
   auto extract_2 =
       TransformationCompositeExtract(instruction_descriptor_2, 201, 100, {3});
 
-  ASSERT_TRUE(extract_2.IsApplicable(context.get(), fact_manager));
-  extract_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_2.IsApplicable(context.get(), transformation_context));
+  extract_2.Apply(context.get(), &transformation_context);
   auto replacement_2 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(54, instruction_descriptor_2, 0), 201);
-  ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
-  replacement_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_2.IsApplicable(context.get(), transformation_context));
+  replacement_2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %15 with %101[0:1] in 'OpStore %12 %15'
   auto instruction_descriptor_3 = MakeInstructionDescriptor(64, SpvOpStore, 0);
   auto shuffle_3 = TransformationVectorShuffle(instruction_descriptor_3, 202,
                                                101, 101, {0, 1});
-  ASSERT_TRUE(shuffle_3.IsApplicable(context.get(), fact_manager));
-  shuffle_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(shuffle_3.IsApplicable(context.get(), transformation_context));
+  shuffle_3.Apply(context.get(), &transformation_context);
+  fact_manager.ComputeClosureOfFacts(context.get(), 100);
+
   auto replacement_3 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(15, instruction_descriptor_3, 1), 202);
-  ASSERT_TRUE(replacement_3.IsApplicable(context.get(), fact_manager));
-  replacement_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_3.IsApplicable(context.get(), transformation_context));
+  replacement_3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %19 with %101[2:3] in '%81 = OpVectorShuffle %16 %19 %19 0 0 1'
@@ -881,12 +967,15 @@
       MakeInstructionDescriptor(81, SpvOpVectorShuffle, 0);
   auto shuffle_4 = TransformationVectorShuffle(instruction_descriptor_4, 203,
                                                101, 101, {2, 3});
-  ASSERT_TRUE(shuffle_4.IsApplicable(context.get(), fact_manager));
-  shuffle_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(shuffle_4.IsApplicable(context.get(), transformation_context));
+  shuffle_4.Apply(context.get(), &transformation_context);
+  fact_manager.ComputeClosureOfFacts(context.get(), 100);
+
   auto replacement_4 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(19, instruction_descriptor_4, 0), 203);
-  ASSERT_TRUE(replacement_4.IsApplicable(context.get(), fact_manager));
-  replacement_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_4.IsApplicable(context.get(), transformation_context));
+  replacement_4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %27 with %102[0] in '%82 = OpCompositeConstruct %21 %26 %27 %28
@@ -896,12 +985,13 @@
   auto extract_5 =
       TransformationCompositeExtract(instruction_descriptor_5, 204, 102, {0});
 
-  ASSERT_TRUE(extract_5.IsApplicable(context.get(), fact_manager));
-  extract_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_5.IsApplicable(context.get(), transformation_context));
+  extract_5.Apply(context.get(), &transformation_context);
   auto replacement_5 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(27, instruction_descriptor_5, 1), 204);
-  ASSERT_TRUE(replacement_5.IsApplicable(context.get(), fact_manager));
-  replacement_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_5.IsApplicable(context.get(), transformation_context));
+  replacement_5.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %15 with %102[1:2] in '%83 = OpCopyObject %10 %15'
@@ -909,12 +999,15 @@
       MakeInstructionDescriptor(83, SpvOpCopyObject, 0);
   auto shuffle_6 = TransformationVectorShuffle(instruction_descriptor_6, 205,
                                                102, 102, {1, 2});
-  ASSERT_TRUE(shuffle_6.IsApplicable(context.get(), fact_manager));
-  shuffle_6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(shuffle_6.IsApplicable(context.get(), transformation_context));
+  shuffle_6.Apply(context.get(), &transformation_context);
+  fact_manager.ComputeClosureOfFacts(context.get(), 100);
+
   auto replacement_6 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(15, instruction_descriptor_6, 0), 205);
-  ASSERT_TRUE(replacement_6.IsApplicable(context.get(), fact_manager));
-  replacement_6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_6.IsApplicable(context.get(), transformation_context));
+  replacement_6.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %33 with %103[0] in '%86 = OpCopyObject %30 %33'
@@ -922,12 +1015,13 @@
       MakeInstructionDescriptor(86, SpvOpCopyObject, 0);
   auto extract_7 =
       TransformationCompositeExtract(instruction_descriptor_7, 206, 103, {0});
-  ASSERT_TRUE(extract_7.IsApplicable(context.get(), fact_manager));
-  extract_7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_7.IsApplicable(context.get(), transformation_context));
+  extract_7.Apply(context.get(), &transformation_context);
   auto replacement_7 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(33, instruction_descriptor_7, 0), 206);
-  ASSERT_TRUE(replacement_7.IsApplicable(context.get(), fact_manager));
-  replacement_7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_7.IsApplicable(context.get(), transformation_context));
+  replacement_7.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %47 with %103[1:3] in '%84 = OpCopyObject %39 %47'
@@ -935,12 +1029,15 @@
       MakeInstructionDescriptor(84, SpvOpCopyObject, 0);
   auto shuffle_8 = TransformationVectorShuffle(instruction_descriptor_8, 207,
                                                103, 103, {1, 2, 3});
-  ASSERT_TRUE(shuffle_8.IsApplicable(context.get(), fact_manager));
-  shuffle_8.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(shuffle_8.IsApplicable(context.get(), transformation_context));
+  shuffle_8.Apply(context.get(), &transformation_context);
+  fact_manager.ComputeClosureOfFacts(context.get(), 100);
+
   auto replacement_8 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(47, instruction_descriptor_8, 0), 207);
-  ASSERT_TRUE(replacement_8.IsApplicable(context.get(), fact_manager));
-  replacement_8.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_8.IsApplicable(context.get(), transformation_context));
+  replacement_8.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %42 with %104[0] in '%85 = OpCopyObject %30 %42'
@@ -948,12 +1045,13 @@
       MakeInstructionDescriptor(85, SpvOpCopyObject, 0);
   auto extract_9 =
       TransformationCompositeExtract(instruction_descriptor_9, 208, 104, {0});
-  ASSERT_TRUE(extract_9.IsApplicable(context.get(), fact_manager));
-  extract_9.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_9.IsApplicable(context.get(), transformation_context));
+  extract_9.Apply(context.get(), &transformation_context);
   auto replacement_9 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(42, instruction_descriptor_9, 0), 208);
-  ASSERT_TRUE(replacement_9.IsApplicable(context.get(), fact_manager));
-  replacement_9.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_9.IsApplicable(context.get(), transformation_context));
+  replacement_9.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %45 with %104[1] in '%63 = OpLogicalOr %30 %45 %46'
@@ -961,24 +1059,28 @@
       MakeInstructionDescriptor(63, SpvOpLogicalOr, 0);
   auto extract_10 =
       TransformationCompositeExtract(instruction_descriptor_10, 209, 104, {1});
-  ASSERT_TRUE(extract_10.IsApplicable(context.get(), fact_manager));
-  extract_10.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_10.IsApplicable(context.get(), transformation_context));
+  extract_10.Apply(context.get(), &transformation_context);
   auto replacement_10 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(45, instruction_descriptor_10, 0), 209);
-  ASSERT_TRUE(replacement_10.IsApplicable(context.get(), fact_manager));
-  replacement_10.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_10.IsApplicable(context.get(), transformation_context));
+  replacement_10.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %38 with %105[0:1] in 'OpStore %36 %38'
   auto instruction_descriptor_11 = MakeInstructionDescriptor(85, SpvOpStore, 0);
   auto shuffle_11 = TransformationVectorShuffle(instruction_descriptor_11, 210,
                                                 105, 105, {0, 1});
-  ASSERT_TRUE(shuffle_11.IsApplicable(context.get(), fact_manager));
-  shuffle_11.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(shuffle_11.IsApplicable(context.get(), transformation_context));
+  shuffle_11.Apply(context.get(), &transformation_context);
+  fact_manager.ComputeClosureOfFacts(context.get(), 100);
+
   auto replacement_11 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(38, instruction_descriptor_11, 1), 210);
-  ASSERT_TRUE(replacement_11.IsApplicable(context.get(), fact_manager));
-  replacement_11.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_11.IsApplicable(context.get(), transformation_context));
+  replacement_11.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   // Replace %46 with %105[2] in '%62 = OpLogicalAnd %30 %45 %46'
@@ -986,12 +1088,13 @@
       MakeInstructionDescriptor(62, SpvOpLogicalAnd, 0);
   auto extract_12 =
       TransformationCompositeExtract(instruction_descriptor_12, 211, 105, {2});
-  ASSERT_TRUE(extract_12.IsApplicable(context.get(), fact_manager));
-  extract_12.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(extract_12.IsApplicable(context.get(), transformation_context));
+  extract_12.Apply(context.get(), &transformation_context);
   auto replacement_12 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(46, instruction_descriptor_12, 1), 211);
-  ASSERT_TRUE(replacement_12.IsApplicable(context.get(), fact_manager));
-  replacement_12.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      replacement_12.IsApplicable(context.get(), transformation_context));
+  replacement_12.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   const std::string after_transformation = R"(
diff --git a/test/fuzz/equivalence_relation_test.cpp b/test/fuzz/equivalence_relation_test.cpp
index 3f2ea58..280aa3a 100644
--- a/test/fuzz/equivalence_relation_test.cpp
+++ b/test/fuzz/equivalence_relation_test.cpp
@@ -47,6 +47,10 @@
   EquivalenceRelation<uint32_t, UInt32Hash, UInt32Equals> relation;
   ASSERT_TRUE(relation.GetAllKnownValues().empty());
 
+  for (uint32_t element = 0; element < 100; element++) {
+    relation.Register(element);
+  }
+
   for (uint32_t element = 2; element < 80; element += 2) {
     relation.MakeEquivalent(0, element);
     relation.MakeEquivalent(element - 1, element + 1);
@@ -123,6 +127,11 @@
   EquivalenceRelation<uint32_t, UInt32Hash, UInt32Equals> relation2;
 
   for (uint32_t i = 0; i < 1000; ++i) {
+    relation1.Register(i);
+    relation2.Register(i);
+  }
+
+  for (uint32_t i = 0; i < 1000; ++i) {
     if (i >= 10) {
       relation1.MakeEquivalent(i, i - 10);
       relation2.MakeEquivalent(i, i - 10);
diff --git a/test/fuzz/fact_manager_test.cpp b/test/fuzz/fact_manager_test.cpp
index 2c79f12..8b1e0c4 100644
--- a/test/fuzz/fact_manager_test.cpp
+++ b/test/fuzz/fact_manager_test.cpp
@@ -738,393 +738,6 @@
                              uniform_buffer_element_descriptor));
 }
 
-TEST(FactManagerTest, DataSynonymFacts) {
-  // The SPIR-V types and constants come from the following code.  The body of
-  // the SPIR-V function then constructs a composite that is synonymous with
-  // myT.
-  //
-  // #version 310 es
-  //
-  // precision highp float;
-  //
-  // struct S {
-  //   int a;
-  //   uvec2 b;
-  // };
-  //
-  // struct T {
-  //   bool c[5];
-  //   mat4x2 d;
-  //   S e;
-  // };
-  //
-  // void main() {
-  //   T myT = T(bool[5](true, false, true, false, true),
-  //             mat4x2(vec2(1.0, 2.0), vec2(3.0, 4.0),
-  // 	           vec2(5.0, 6.0), vec2(7.0, 8.0)),
-  //             S(10, uvec2(100u, 200u)));
-  // }
-
-  std::string shader = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-               OpName %4 "main"
-               OpName %15 "S"
-               OpMemberName %15 0 "a"
-               OpMemberName %15 1 "b"
-               OpName %16 "T"
-               OpMemberName %16 0 "c"
-               OpMemberName %16 1 "d"
-               OpMemberName %16 2 "e"
-               OpName %18 "myT"
-               OpMemberDecorate %15 0 RelaxedPrecision
-               OpMemberDecorate %15 1 RelaxedPrecision
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeBool
-          %7 = OpTypeInt 32 0
-          %8 = OpConstant %7 5
-          %9 = OpTypeArray %6 %8
-         %10 = OpTypeFloat 32
-         %11 = OpTypeVector %10 2
-         %12 = OpTypeMatrix %11 4
-         %13 = OpTypeInt 32 1
-         %14 = OpTypeVector %7 2
-         %15 = OpTypeStruct %13 %14
-         %16 = OpTypeStruct %9 %12 %15
-         %17 = OpTypePointer Function %16
-         %19 = OpConstantTrue %6
-         %20 = OpConstantFalse %6
-         %21 = OpConstantComposite %9 %19 %20 %19 %20 %19
-         %22 = OpConstant %10 1
-         %23 = OpConstant %10 2
-         %24 = OpConstantComposite %11 %22 %23
-         %25 = OpConstant %10 3
-         %26 = OpConstant %10 4
-         %27 = OpConstantComposite %11 %25 %26
-         %28 = OpConstant %10 5
-         %29 = OpConstant %10 6
-         %30 = OpConstantComposite %11 %28 %29
-         %31 = OpConstant %10 7
-         %32 = OpConstant %10 8
-         %33 = OpConstantComposite %11 %31 %32
-         %34 = OpConstantComposite %12 %24 %27 %30 %33
-         %35 = OpConstant %13 10
-         %36 = OpConstant %7 100
-         %37 = OpConstant %7 200
-         %38 = OpConstantComposite %14 %36 %37
-         %39 = OpConstantComposite %15 %35 %38
-         %40 = OpConstantComposite %16 %21 %34 %39
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-         %18 = OpVariable %17 Function
-               OpStore %18 %40
-        %100 = OpCompositeConstruct %9 %19 %20 %19 %20 %19
-        %101 = OpCompositeConstruct %11 %22 %23
-        %102 = OpCompositeConstruct %11 %25 %26
-        %103 = OpCompositeConstruct %11 %28 %29
-        %104 = OpCompositeConstruct %11 %31 %32
-        %105 = OpCompositeConstruct %12 %101 %102 %103 %104
-        %106 = OpCompositeConstruct %14 %36 %37
-        %107 = OpCompositeConstruct %15 %35 %106
-        %108 = OpCompositeConstruct %16 %100 %105 %107
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
-  ASSERT_TRUE(IsValid(env, context.get()));
-
-  FactManager fact_manager;
-
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(24, {}), MakeDataDescriptor(101, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
-                                         MakeDataDescriptor(101, {0}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {1}),
-                                         MakeDataDescriptor(101, {1}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
-                                         MakeDataDescriptor(101, {1}),
-                                         context.get()));
-
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(24, {}),
-                                  MakeDataDescriptor(101, {}), context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(24, {}), MakeDataDescriptor(101, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
-                                        MakeDataDescriptor(101, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {1}),
-                                        MakeDataDescriptor(101, {1}),
-                                        context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
-                                         MakeDataDescriptor(101, {1}),
-                                         context.get()));
-
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(27, {}), MakeDataDescriptor(102, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {0}),
-                                         MakeDataDescriptor(102, {0}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {1}),
-                                         MakeDataDescriptor(102, {1}),
-                                         context.get()));
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(27, {0}),
-                                  MakeDataDescriptor(102, {0}), context.get());
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(27, {}), MakeDataDescriptor(102, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {0}),
-                                        MakeDataDescriptor(102, {0}),
-                                        context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {1}),
-                                         MakeDataDescriptor(102, {1}),
-                                         context.get()));
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(27, {1}),
-                                  MakeDataDescriptor(102, {1}), context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(27, {}), MakeDataDescriptor(102, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {0}),
-                                        MakeDataDescriptor(102, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {1}),
-                                        MakeDataDescriptor(102, {1}),
-                                        context.get()));
-
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(30, {}), MakeDataDescriptor(103, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {0}),
-                                         MakeDataDescriptor(103, {0}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1}),
-                                         MakeDataDescriptor(103, {1}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(33, {}), MakeDataDescriptor(104, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {0}),
-                                         MakeDataDescriptor(104, {0}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {1}),
-                                         MakeDataDescriptor(104, {1}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(34, {}), MakeDataDescriptor(105, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {0}),
-                                         MakeDataDescriptor(105, {0}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {1}),
-                                         MakeDataDescriptor(105, {1}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {2}),
-                                         MakeDataDescriptor(105, {2}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {3}),
-                                         MakeDataDescriptor(105, {3}),
-                                         context.get()));
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(30, {}),
-                                  MakeDataDescriptor(103, {}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(33, {}),
-                                  MakeDataDescriptor(104, {}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {0}),
-                                  MakeDataDescriptor(105, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {1}),
-                                  MakeDataDescriptor(105, {1}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {2}),
-                                  MakeDataDescriptor(105, {2}), context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(30, {}), MakeDataDescriptor(103, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {0}),
-                                        MakeDataDescriptor(103, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1}),
-                                        MakeDataDescriptor(103, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(33, {}), MakeDataDescriptor(104, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {0}),
-                                        MakeDataDescriptor(104, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {1}),
-                                        MakeDataDescriptor(104, {1}),
-                                        context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(34, {}), MakeDataDescriptor(105, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {0}),
-                                        MakeDataDescriptor(105, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {1}),
-                                        MakeDataDescriptor(105, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {2}),
-                                        MakeDataDescriptor(105, {2}),
-                                        context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {3}),
-                                         MakeDataDescriptor(105, {3}),
-                                         context.get()));
-
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {3}),
-                                  MakeDataDescriptor(105, {3}), context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {0}),
-                                        MakeDataDescriptor(104, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {3}),
-                                        MakeDataDescriptor(105, {3}),
-                                        context.get()));
-
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(21, {}), MakeDataDescriptor(100, {}), context.get()));
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {0}),
-                                  MakeDataDescriptor(100, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {1}),
-                                  MakeDataDescriptor(100, {1}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {2}),
-                                  MakeDataDescriptor(100, {2}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {3}),
-                                  MakeDataDescriptor(100, {3}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {4}),
-                                  MakeDataDescriptor(100, {4}), context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(21, {}), MakeDataDescriptor(100, {}), context.get()));
-
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(39, {0}),
-                                         MakeDataDescriptor(107, {0}),
-                                         context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(35, {}), MakeDataDescriptor(39, {0}), context.get()));
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(39, {0}),
-                                  MakeDataDescriptor(35, {}), context.get());
-  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(39, {0}),
-                                         MakeDataDescriptor(107, {0}),
-                                         context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(35, {}), MakeDataDescriptor(39, {0}), context.get()));
-
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(38, {0}), MakeDataDescriptor(36, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(38, {1}), MakeDataDescriptor(37, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(106, {0}), MakeDataDescriptor(36, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(106, {1}), MakeDataDescriptor(37, {}), context.get()));
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(38, {}), MakeDataDescriptor(106, {}), context.get()));
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(38, {0}),
-                                  MakeDataDescriptor(36, {}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(106, {0}),
-                                  MakeDataDescriptor(36, {}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(38, {1}),
-                                  MakeDataDescriptor(37, {}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(106, {1}),
-                                  MakeDataDescriptor(37, {}), context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(38, {0}), MakeDataDescriptor(36, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(38, {1}), MakeDataDescriptor(37, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(106, {0}), MakeDataDescriptor(36, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(106, {1}), MakeDataDescriptor(37, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(38, {}), MakeDataDescriptor(106, {}), context.get()));
-
-  ASSERT_FALSE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(40, {}), MakeDataDescriptor(108, {}), context.get()));
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(107, {0}),
-                                  MakeDataDescriptor(35, {}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {0}),
-                                  MakeDataDescriptor(108, {0}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {1}),
-                                  MakeDataDescriptor(108, {1}), context.get());
-  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {2}),
-                                  MakeDataDescriptor(108, {2}), context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(40, {}), MakeDataDescriptor(108, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0}),
-                                        MakeDataDescriptor(108, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1}),
-                                        MakeDataDescriptor(108, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2}),
-                                        MakeDataDescriptor(108, {2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 0}),
-                                        MakeDataDescriptor(108, {0, 0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 1}),
-                                        MakeDataDescriptor(108, {0, 1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 2}),
-                                        MakeDataDescriptor(108, {0, 2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 3}),
-                                        MakeDataDescriptor(108, {0, 3}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 4}),
-                                        MakeDataDescriptor(108, {0, 4}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 0}),
-                                        MakeDataDescriptor(108, {1, 0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 1}),
-                                        MakeDataDescriptor(108, {1, 1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 2}),
-                                        MakeDataDescriptor(108, {1, 2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 3}),
-                                        MakeDataDescriptor(108, {1, 3}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 0, 0}),
-                                        MakeDataDescriptor(108, {1, 0, 0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 1, 0}),
-                                        MakeDataDescriptor(108, {1, 1, 0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 2, 0}),
-                                        MakeDataDescriptor(108, {1, 2, 0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 3, 0}),
-                                        MakeDataDescriptor(108, {1, 3, 0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 0, 1}),
-                                        MakeDataDescriptor(108, {1, 0, 1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 1, 1}),
-                                        MakeDataDescriptor(108, {1, 1, 1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 2, 1}),
-                                        MakeDataDescriptor(108, {1, 2, 1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 3, 1}),
-                                        MakeDataDescriptor(108, {1, 3, 1}),
-                                        context.get()));
-
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 0}),
-                                        MakeDataDescriptor(108, {2, 0}),
-                                        context.get()));
-
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 1}),
-                                        MakeDataDescriptor(108, {2, 1}),
-                                        context.get()));
-
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 1, 0}),
-                                        MakeDataDescriptor(108, {2, 1, 0}),
-                                        context.get()));
-
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 1, 1}),
-                                        MakeDataDescriptor(108, {2, 1, 1}),
-                                        context.get()));
-}
-
 TEST(FactManagerTest, RecursiveAdditionOfFacts) {
   std::string shader = R"(
                OpCapability Shader
@@ -1157,20 +770,16 @@
   fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
                                   MakeDataDescriptor(11, {2}), context.get());
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(10, {}), MakeDataDescriptor(11, {2}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {}),
+                                        MakeDataDescriptor(11, {2})));
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {0}),
-                                        MakeDataDescriptor(11, {2, 0}),
-                                        context.get()));
+                                        MakeDataDescriptor(11, {2, 0})));
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {1}),
-                                        MakeDataDescriptor(11, {2, 1}),
-                                        context.get()));
+                                        MakeDataDescriptor(11, {2, 1})));
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {2}),
-                                        MakeDataDescriptor(11, {2, 2}),
-                                        context.get()));
+                                        MakeDataDescriptor(11, {2, 2})));
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {3}),
-                                        MakeDataDescriptor(11, {2, 3}),
-                                        context.get()));
+                                        MakeDataDescriptor(11, {2, 3})));
 }
 
 TEST(FactManagerTest, LogicalNotEquationFacts) {
@@ -1209,14 +818,14 @@
   fact_manager.AddFactIdEquation(14, SpvOpLogicalNot, {7}, context.get());
   fact_manager.AddFactIdEquation(17, SpvOpLogicalNot, {16}, context.get());
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(17, {}), MakeDataDescriptor(7, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(15, {}), MakeDataDescriptor(17, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(16, {}), MakeDataDescriptor(14, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(15, {}),
+                                        MakeDataDescriptor(7, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}),
+                                        MakeDataDescriptor(7, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(15, {}),
+                                        MakeDataDescriptor(17, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(16, {}),
+                                        MakeDataDescriptor(14, {})));
 }
 
 TEST(FactManagerTest, SignedNegateEquationFacts) {
@@ -1249,8 +858,8 @@
   fact_manager.AddFactIdEquation(14, SpvOpSNegate, {7}, context.get());
   fact_manager.AddFactIdEquation(15, SpvOpSNegate, {14}, context.get());
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(7, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(7, {}),
+                                        MakeDataDescriptor(15, {})));
 }
 
 TEST(FactManagerTest, AddSubNegateFacts1) {
@@ -1302,12 +911,12 @@
                                   MakeDataDescriptor(22, {}), context.get());
   fact_manager.AddFactIdEquation(24, SpvOpSNegate, {23}, context.get());
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(19, {}), MakeDataDescriptor(15, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(24, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(19, {}),
+                                        MakeDataDescriptor(15, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(20, {}),
+                                        MakeDataDescriptor(16, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {}),
+                                        MakeDataDescriptor(15, {})));
 }
 
 TEST(FactManagerTest, AddSubNegateFacts2) {
@@ -1347,30 +956,158 @@
   fact_manager.AddFactIdEquation(14, SpvOpISub, {15, 16}, context.get());
   fact_manager.AddFactIdEquation(17, SpvOpIAdd, {14, 16}, context.get());
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}),
+                                        MakeDataDescriptor(15, {})));
 
   fact_manager.AddFactIdEquation(18, SpvOpIAdd, {16, 14}, context.get());
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(18, {}),
+                                        MakeDataDescriptor(15, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}),
+                                        MakeDataDescriptor(18, {})));
 
   fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 15}, context.get());
   fact_manager.AddFactIdEquation(20, SpvOpSNegate, {19}, context.get());
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(20, {}),
+                                        MakeDataDescriptor(16, {})));
 
   fact_manager.AddFactIdEquation(21, SpvOpISub, {14, 19}, context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
+                                        MakeDataDescriptor(15, {})));
 
   fact_manager.AddFactIdEquation(22, SpvOpISub, {14, 18}, context.get());
   fact_manager.AddFactIdEquation(23, SpvOpSNegate, {22}, context.get());
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(23, {}),
+                                        MakeDataDescriptor(16, {})));
+}
+
+TEST(FactManagerTest, EquationAndEquivalenceFacts) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %15 = OpConstant %6 24
+         %16 = OpConstant %6 37
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %14 = OpISub %6 %15 %16
+        %114 = OpCopyObject %6 %14
+         %17 = OpIAdd %6 %114 %16 ; ==> synonymous(%17, %15)
+         %18 = OpIAdd %6 %16 %114 ; ==> synonymous(%17, %18, %15)
+         %19 = OpISub %6 %114 %15
+        %119 = OpCopyObject %6 %19
+         %20 = OpSNegate %6 %119 ; ==> synonymous(%20, %16)
+         %21 = OpISub %6 %14 %19 ; ==> synonymous(%21, %15)
+         %22 = OpISub %6 %14 %18
+        %220 = OpCopyObject %6 %22
+         %23 = OpSNegate %6 %220 ; ==> synonymous(%23, %16)
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  fact_manager.AddFactIdEquation(14, SpvOpISub, {15, 16}, context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(114, {}),
+                                  MakeDataDescriptor(14, {}), context.get());
+  fact_manager.AddFactIdEquation(17, SpvOpIAdd, {114, 16}, context.get());
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}),
+                                        MakeDataDescriptor(15, {})));
+
+  fact_manager.AddFactIdEquation(18, SpvOpIAdd, {16, 114}, context.get());
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(18, {}),
+                                        MakeDataDescriptor(15, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}),
+                                        MakeDataDescriptor(18, {})));
+
+  fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 15}, context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(119, {}),
+                                  MakeDataDescriptor(19, {}), context.get());
+  fact_manager.AddFactIdEquation(20, SpvOpSNegate, {119}, context.get());
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(20, {}),
+                                        MakeDataDescriptor(16, {})));
+
+  fact_manager.AddFactIdEquation(21, SpvOpISub, {14, 19}, context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
+                                        MakeDataDescriptor(15, {})));
+
+  fact_manager.AddFactIdEquation(22, SpvOpISub, {14, 18}, context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(22, {}),
+                                  MakeDataDescriptor(220, {}), context.get());
+  fact_manager.AddFactIdEquation(23, SpvOpSNegate, {220}, context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(23, {}),
+                                        MakeDataDescriptor(16, {})));
+}
+
+TEST(FactManagerTest, CheckingFactsDoesNotAddConstants) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpMemberDecorate %9 0 Offset 0
+               OpDecorate %9 Block
+               OpDecorate %11 DescriptorSet 0
+               OpDecorate %11 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpTypeStruct %6
+         %10 = OpTypePointer Uniform %9
+         %11 = OpVariable %10 Uniform
+         %12 = OpConstant %6 0
+         %13 = OpTypePointer Uniform %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %14 = OpAccessChain %13 %11 %12
+         %15 = OpLoad %6 %14
+               OpStore %8 %15
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // 8[0] == int(1)
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1},
+                            MakeUniformBufferElementDescriptor(0, 0, {0})));
+
+  // Although 8[0] has the value 1, we do not have the constant 1 in the module.
+  // We thus should not find any constants available from uniforms for int type.
+  // Furthermore, the act of looking for appropriate constants should not change
+  // which constants are known to the constant manager.
+  auto int_type = context->get_type_mgr()->GetType(6)->AsInteger();
+  opt::analysis::IntConstant constant_one(int_type, {1});
+  ASSERT_FALSE(context->get_constant_mgr()->FindConstant(&constant_one));
+  auto available_constants =
+      fact_manager.GetConstantsAvailableFromUniformsForType(context.get(), 6);
+  ASSERT_EQ(0, available_constants.size());
+  ASSERT_TRUE(IsEqual(env, shader, context.get()));
+  ASSERT_FALSE(context->get_constant_mgr()->FindConstant(&constant_one));
 }
 
 }  // namespace
diff --git a/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp b/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp
deleted file mode 100644
index 89f006e..0000000
--- a/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp
+++ /dev/null
@@ -1,393 +0,0 @@
-// Copyright (c) 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
-#include "source/fuzz/pseudo_random_generator.h"
-#include "source/fuzz/uniform_buffer_element_descriptor.h"
-#include "test/fuzz/fuzz_test_util.h"
-
-namespace spvtools {
-namespace fuzz {
-namespace {
-
-bool AddFactHelper(
-    FactManager* fact_manager, opt::IRContext* context, uint32_t word,
-    const protobufs::UniformBufferElementDescriptor& descriptor) {
-  protobufs::FactConstantUniform constant_uniform_fact;
-  constant_uniform_fact.add_constant_word(word);
-  *constant_uniform_fact.mutable_uniform_buffer_element_descriptor() =
-      descriptor;
-  protobufs::Fact fact;
-  *fact.mutable_constant_uniform_fact() = constant_uniform_fact;
-  return fact_manager->AddFact(fact, context);
-}
-
-TEST(FuzzerPassAddUsefulConstructsTest, CheckBasicStuffIsAdded) {
-  // The SPIR-V came from the following empty GLSL shader:
-  //
-  // #version 450
-  //
-  // void main()
-  // {
-  // }
-
-  std::string shader = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource GLSL 450
-               OpName %4 "main"
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
-  ASSERT_TRUE(IsValid(env, context.get()));
-
-  FactManager fact_manager;
-  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
-  protobufs::TransformationSequence transformation_sequence;
-
-  FuzzerPassAddUsefulConstructs pass(context.get(), &fact_manager,
-                                     &fuzzer_context, &transformation_sequence);
-  pass.Apply();
-  ASSERT_TRUE(IsValid(env, context.get()));
-
-  std::string after = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource GLSL 450
-               OpName %4 "main"
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-        %100 = OpTypeBool
-        %101 = OpTypeInt 32 1
-        %102 = OpTypeInt 32 0
-        %103 = OpTypeFloat 32
-        %104 = OpConstantTrue %100
-        %105 = OpConstantFalse %100
-        %106 = OpConstant %101 0
-        %107 = OpConstant %101 1
-        %108 = OpConstant %102 0
-        %109 = OpConstant %102 1
-        %110 = OpConstant %103 0
-        %111 = OpConstant %103 1
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-  ASSERT_TRUE(IsEqual(env, after, context.get()));
-}
-
-TEST(FuzzerPassAddUsefulConstructsTest,
-     CheckTypesIndicesAndConstantsAddedForUniformFacts) {
-  // The SPIR-V came from the following GLSL shader:
-  //
-  // #version 450
-  //
-  // struct S {
-  //   int x;
-  //   float y;
-  //   int z;
-  //   int w;
-  // };
-  //
-  // uniform buf {
-  //   S s;
-  //   uint w[10];
-  // };
-  //
-  // void main() {
-  // }
-
-  std::string shader = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource GLSL 450
-               OpName %4 "main"
-               OpName %8 "S"
-               OpMemberName %8 0 "x"
-               OpMemberName %8 1 "y"
-               OpMemberName %8 2 "z"
-               OpMemberName %8 3 "w"
-               OpName %12 "buf"
-               OpMemberName %12 0 "s"
-               OpMemberName %12 1 "w"
-               OpName %14 ""
-               OpMemberDecorate %8 0 Offset 0
-               OpMemberDecorate %8 1 Offset 4
-               OpMemberDecorate %8 2 Offset 8
-               OpMemberDecorate %8 3 Offset 12
-               OpDecorate %11 ArrayStride 16
-               OpMemberDecorate %12 0 Offset 0
-               OpMemberDecorate %12 1 Offset 16
-               OpDecorate %12 Block
-               OpDecorate %14 DescriptorSet 0
-               OpDecorate %14 Binding 0
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeInt 32 1
-          %7 = OpTypeFloat 32
-          %8 = OpTypeStruct %6 %7 %6 %6
-          %9 = OpTypeInt 32 0
-         %10 = OpConstant %9 10
-         %11 = OpTypeArray %9 %10
-         %12 = OpTypeStruct %8 %11
-         %13 = OpTypePointer Uniform %12
-         %14 = OpVariable %13 Uniform
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
-  ASSERT_TRUE(IsValid(env, context.get()));
-
-  FactManager fact_manager;
-  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
-  protobufs::TransformationSequence transformation_sequence;
-
-  // Add some uniform facts.
-
-  // buf.s.x == 200
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 200,
-                            MakeUniformBufferElementDescriptor(0, 0, {0, 0})));
-
-  // buf.s.y == 0.5
-  const float float_value = 0.5;
-  uint32_t float_value_as_uint;
-  memcpy(&float_value_as_uint, &float_value, sizeof(float_value));
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_value_as_uint,
-                            MakeUniformBufferElementDescriptor(0, 0, {0, 1})));
-
-  // buf.s.z == 300
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 300,
-                            MakeUniformBufferElementDescriptor(0, 0, {0, 2})));
-
-  // buf.s.w == 400
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 400,
-                            MakeUniformBufferElementDescriptor(0, 0, {0, 3})));
-
-  // buf.w[6] = 22
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 22,
-                            MakeUniformBufferElementDescriptor(0, 0, {1, 6})));
-
-  // buf.w[8] = 23
-  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 23,
-                            MakeUniformBufferElementDescriptor(0, 0, {1, 8})));
-
-  // Assert some things about the module that are not true prior to adding the
-  // pass
-
-  {
-    // No uniform int pointer
-    opt::analysis::Integer temp_type_signed_int(32, true);
-    opt::analysis::Integer* registered_type_signed_int =
-        context->get_type_mgr()
-            ->GetRegisteredType(&temp_type_signed_int)
-            ->AsInteger();
-    opt::analysis::Pointer type_pointer_uniform_signed_int(
-        registered_type_signed_int, SpvStorageClassUniform);
-    ASSERT_EQ(0,
-              context->get_type_mgr()->GetId(&type_pointer_uniform_signed_int));
-
-    // No uniform uint pointer
-    opt::analysis::Integer temp_type_unsigned_int(32, false);
-    opt::analysis::Integer* registered_type_unsigned_int =
-        context->get_type_mgr()
-            ->GetRegisteredType(&temp_type_unsigned_int)
-            ->AsInteger();
-    opt::analysis::Pointer type_pointer_uniform_unsigned_int(
-        registered_type_unsigned_int, SpvStorageClassUniform);
-    ASSERT_EQ(
-        0, context->get_type_mgr()->GetId(&type_pointer_uniform_unsigned_int));
-
-    // No uniform float pointer
-    opt::analysis::Float temp_type_float(32);
-    opt::analysis::Float* registered_type_float =
-        context->get_type_mgr()->GetRegisteredType(&temp_type_float)->AsFloat();
-    opt::analysis::Pointer type_pointer_uniform_float(registered_type_float,
-                                                      SpvStorageClassUniform);
-    ASSERT_EQ(0, context->get_type_mgr()->GetId(&type_pointer_uniform_float));
-
-    // No int constants 200, 300 nor 400
-    opt::analysis::IntConstant int_constant_200(registered_type_signed_int,
-                                                {200});
-    opt::analysis::IntConstant int_constant_300(registered_type_signed_int,
-                                                {300});
-    opt::analysis::IntConstant int_constant_400(registered_type_signed_int,
-                                                {400});
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_200));
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_300));
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_400));
-
-    // No float constant 0.5
-    opt::analysis::FloatConstant float_constant_zero_point_five(
-        registered_type_float, {float_value_as_uint});
-    ASSERT_EQ(nullptr, context->get_constant_mgr()->FindConstant(
-                           &float_constant_zero_point_five));
-
-    // No uint constant 22
-    opt::analysis::IntConstant uint_constant_22(registered_type_unsigned_int,
-                                                {22});
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&uint_constant_22));
-
-    // No uint constant 23
-    opt::analysis::IntConstant uint_constant_23(registered_type_unsigned_int,
-                                                {23});
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&uint_constant_23));
-
-    // No int constants 0, 1, 2, 3, 6, 8
-    opt::analysis::IntConstant int_constant_0(registered_type_signed_int, {0});
-    opt::analysis::IntConstant int_constant_1(registered_type_signed_int, {1});
-    opt::analysis::IntConstant int_constant_2(registered_type_signed_int, {2});
-    opt::analysis::IntConstant int_constant_3(registered_type_signed_int, {3});
-    opt::analysis::IntConstant int_constant_6(registered_type_signed_int, {6});
-    opt::analysis::IntConstant int_constant_8(registered_type_signed_int, {8});
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_0));
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_1));
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_2));
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_3));
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_6));
-    ASSERT_EQ(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_8));
-  }
-
-  FuzzerPassAddUsefulConstructs pass(context.get(), &fact_manager,
-                                     &fuzzer_context, &transformation_sequence);
-  pass.Apply();
-  ASSERT_TRUE(IsValid(env, context.get()));
-
-  // Now assert some things about the module that should be true following the
-  // pass.
-
-  // We reconstruct all necessary types and constants to guard against the type
-  // and constant managers for the module having been invalidated.
-
-  {
-    // Uniform int pointer now present
-    opt::analysis::Integer temp_type_signed_int(32, true);
-    opt::analysis::Integer* registered_type_signed_int =
-        context->get_type_mgr()
-            ->GetRegisteredType(&temp_type_signed_int)
-            ->AsInteger();
-    opt::analysis::Pointer type_pointer_uniform_signed_int(
-        registered_type_signed_int, SpvStorageClassUniform);
-    ASSERT_NE(0,
-              context->get_type_mgr()->GetId(&type_pointer_uniform_signed_int));
-
-    // Uniform uint pointer now present
-    opt::analysis::Integer temp_type_unsigned_int(32, false);
-    opt::analysis::Integer* registered_type_unsigned_int =
-        context->get_type_mgr()
-            ->GetRegisteredType(&temp_type_unsigned_int)
-            ->AsInteger();
-    opt::analysis::Pointer type_pointer_uniform_unsigned_int(
-        registered_type_unsigned_int, SpvStorageClassUniform);
-    ASSERT_NE(
-        0, context->get_type_mgr()->GetId(&type_pointer_uniform_unsigned_int));
-
-    // Uniform float pointer now present
-    opt::analysis::Float temp_type_float(32);
-    opt::analysis::Float* registered_type_float =
-        context->get_type_mgr()->GetRegisteredType(&temp_type_float)->AsFloat();
-    opt::analysis::Pointer type_pointer_uniform_float(registered_type_float,
-                                                      SpvStorageClassUniform);
-    ASSERT_NE(0, context->get_type_mgr()->GetId(&type_pointer_uniform_float));
-
-    // int constants 200, 300, 400 now present
-    opt::analysis::IntConstant int_constant_200(registered_type_signed_int,
-                                                {200});
-    opt::analysis::IntConstant int_constant_300(registered_type_signed_int,
-                                                {300});
-    opt::analysis::IntConstant int_constant_400(registered_type_signed_int,
-                                                {400});
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_200));
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_300));
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_400));
-
-    // float constant 0.5 now present
-    opt::analysis::FloatConstant float_constant_zero_point_five(
-        registered_type_float, {float_value_as_uint});
-    ASSERT_NE(nullptr, context->get_constant_mgr()->FindConstant(
-                           &float_constant_zero_point_five));
-
-    // uint constant 22 now present
-    opt::analysis::IntConstant uint_constant_22(registered_type_unsigned_int,
-                                                {22});
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&uint_constant_22));
-
-    // uint constant 23 now present
-    opt::analysis::IntConstant uint_constant_23(registered_type_unsigned_int,
-                                                {23});
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&uint_constant_23));
-
-    // int constants 0, 1, 2, 3, 6, 8 now present
-    opt::analysis::IntConstant int_constant_0(registered_type_signed_int, {0});
-    opt::analysis::IntConstant int_constant_1(registered_type_signed_int, {1});
-    opt::analysis::IntConstant int_constant_2(registered_type_signed_int, {2});
-    opt::analysis::IntConstant int_constant_3(registered_type_signed_int, {3});
-    opt::analysis::IntConstant int_constant_6(registered_type_signed_int, {6});
-    opt::analysis::IntConstant int_constant_8(registered_type_signed_int, {8});
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_0));
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_1));
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_2));
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_3));
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_6));
-    ASSERT_NE(nullptr,
-              context->get_constant_mgr()->FindConstant(&int_constant_8));
-  }
-}
-
-}  // namespace
-}  // namespace fuzz
-}  // namespace spvtools
diff --git a/test/fuzz/fuzzer_pass_construct_composites_test.cpp b/test/fuzz/fuzzer_pass_construct_composites_test.cpp
new file mode 100644
index 0000000..cc21f74
--- /dev/null
+++ b/test/fuzz/fuzzer_pass_construct_composites_test.cpp
@@ -0,0 +1,187 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_construct_composites.h"
+#include "source/fuzz/pseudo_random_generator.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(FuzzerPassConstructCompositesTest, IsomorphicStructs) {
+  // This test declares various isomorphic structs, and a struct that is made up
+  // of these isomorphic structs.  The pass to construct composites is then
+  // applied several times to check that no issues arise related to using a
+  // value of one struct type when a value of an isomorphic struct type is
+  // required.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpConstant %6 0
+          %8 = OpTypeStruct %6 %6 %6
+          %9 = OpTypeStruct %6 %6 %6
+         %10 = OpTypeStruct %6 %6 %6
+         %11 = OpTypeStruct %6 %6 %6
+         %12 = OpTypeStruct %6 %6 %6
+         %13 = OpTypeStruct %8 %9 %10 %11 %12
+         %14 = OpConstantComposite %8 %7 %7 %7
+         %15 = OpConstantComposite %9 %7 %7 %7
+         %16 = OpConstantComposite %10 %7 %7 %7
+         %17 = OpConstantComposite %11 %7 %7 %7
+         %18 = OpConstantComposite %12 %7 %7 %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+
+  auto prng = MakeUnique<PseudoRandomGenerator>(0);
+
+  for (uint32_t i = 0; i < 10; i++) {
+    const auto context =
+        BuildModule(env, consumer, shader, kFuzzAssembleOption);
+    ASSERT_TRUE(IsValid(env, context.get()));
+
+    FactManager fact_manager;
+    spvtools::ValidatorOptions validator_options;
+    TransformationContext transformation_context(&fact_manager,
+                                                 validator_options);
+
+    FuzzerContext fuzzer_context(prng.get(), 100);
+    protobufs::TransformationSequence transformation_sequence;
+
+    FuzzerPassConstructComposites fuzzer_pass(
+        context.get(), &transformation_context, &fuzzer_context,
+        &transformation_sequence);
+
+    fuzzer_pass.Apply();
+
+    // We just check that the result is valid.
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+}
+
+TEST(FuzzerPassConstructCompositesTest, IsomorphicArrays) {
+  // This test declares various isomorphic arrays, and a struct that is made up
+  // of these isomorphic arrays.  The pass to construct composites is then
+  // applied several times to check that no issues arise related to using a
+  // value of one array type when a value of an isomorphic array type is
+  // required.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+         %50 = OpTypeInt 32 0
+         %51 = OpConstant %50 3
+          %7 = OpConstant %6 0
+          %8 = OpTypeArray %6 %51
+          %9 = OpTypeArray %6 %51
+         %10 = OpTypeArray %6 %51
+         %11 = OpTypeArray %6 %51
+         %12 = OpTypeArray %6 %51
+         %13 = OpTypeStruct %8 %9 %10 %11 %12
+         %14 = OpConstantComposite %8 %7 %7 %7
+         %15 = OpConstantComposite %9 %7 %7 %7
+         %16 = OpConstantComposite %10 %7 %7 %7
+         %17 = OpConstantComposite %11 %7 %7 %7
+         %18 = OpConstantComposite %12 %7 %7 %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpNop
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+
+  auto prng = MakeUnique<PseudoRandomGenerator>(0);
+
+  for (uint32_t i = 0; i < 10; i++) {
+    const auto context =
+        BuildModule(env, consumer, shader, kFuzzAssembleOption);
+    ASSERT_TRUE(IsValid(env, context.get()));
+
+    FactManager fact_manager;
+    spvtools::ValidatorOptions validator_options;
+    TransformationContext transformation_context(&fact_manager,
+                                                 validator_options);
+
+    FuzzerContext fuzzer_context(prng.get(), 100);
+    protobufs::TransformationSequence transformation_sequence;
+
+    FuzzerPassConstructComposites fuzzer_pass(
+        context.get(), &transformation_context, &fuzzer_context,
+        &transformation_sequence);
+
+    fuzzer_pass.Apply();
+
+    // We just check that the result is valid.
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp
index dc7ba3a..0833c1d 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);
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
   protobufs::TransformationSequence transformation_sequence;
 
-  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager,
-                                      &fuzzer_context, &transformation_sequence,
-                                      {});
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
 
   fuzzer_pass.DonateSingleModule(donor_context.get(), false);
 
@@ -269,13 +272,17 @@
   ASSERT_TRUE(IsValid(env, donor_context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
   protobufs::TransformationSequence transformation_sequence;
 
-  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager,
-                                      &fuzzer_context, &transformation_sequence,
-                                      {});
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
 
   fuzzer_pass.DonateSingleModule(donor_context.get(), false);
 
@@ -393,13 +400,17 @@
   ASSERT_TRUE(IsValid(env, donor_context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
   protobufs::TransformationSequence transformation_sequence;
 
-  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager,
-                                      &fuzzer_context, &transformation_sequence,
-                                      {});
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
 
   fuzzer_pass.DonateSingleModule(donor_context.get(), false);
 
@@ -481,13 +492,17 @@
   ASSERT_TRUE(IsValid(env, donor_context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
   protobufs::TransformationSequence transformation_sequence;
 
-  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager,
-                                      &fuzzer_context, &transformation_sequence,
-                                      {});
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
 
   fuzzer_pass.DonateSingleModule(donor_context.get(), false);
 
@@ -496,6 +511,993 @@
   ASSERT_TRUE(IsValid(env, recipient_context.get()));
 }
 
+TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Private %6
+          %8 = OpConstantNull %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto recipient_context =
+      BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  const auto donor_context =
+      BuildModule(env, consumer, donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), false);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImages) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+               OpName %4 "main"
+               OpName %10 "mySampler"
+               OpName %21 "myTexture"
+               OpName %33 "v"
+               OpDecorate %10 RelaxedPrecision
+               OpDecorate %10 DescriptorSet 0
+               OpDecorate %10 Binding 0
+               OpDecorate %11 RelaxedPrecision
+               OpDecorate %21 RelaxedPrecision
+               OpDecorate %21 DescriptorSet 0
+               OpDecorate %21 Binding 1
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %34 RelaxedPrecision
+               OpDecorate %40 RelaxedPrecision
+               OpDecorate %42 RelaxedPrecision
+               OpDecorate %43 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeImage %6 2D 0 0 0 1 Unknown
+          %8 = OpTypeSampledImage %7
+          %9 = OpTypePointer UniformConstant %8
+         %10 = OpVariable %9 UniformConstant
+         %12 = OpTypeInt 32 1
+         %13 = OpConstant %12 2
+         %15 = OpTypeVector %12 2
+         %17 = OpTypeInt 32 0
+         %18 = OpConstant %17 0
+         %20 = OpTypePointer UniformConstant %7
+         %21 = OpVariable %20 UniformConstant
+         %23 = OpConstant %12 1
+         %25 = OpConstant %17 1
+         %27 = OpTypeBool
+         %31 = OpTypeVector %6 4
+         %32 = OpTypePointer Function %31
+         %35 = OpConstantComposite %15 %23 %23
+         %36 = OpConstant %12 3
+         %37 = OpConstant %12 4
+         %38 = OpConstantComposite %15 %36 %37
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %33 = OpVariable %32 Function
+         %11 = OpLoad %8 %10
+         %14 = OpImage %7 %11
+         %16 = OpImageQuerySizeLod %15 %14 %13
+         %19 = OpCompositeExtract %12 %16 0
+         %22 = OpLoad %7 %21
+         %24 = OpImageQuerySizeLod %15 %22 %23
+         %26 = OpCompositeExtract %12 %24 1
+         %28 = OpSGreaterThan %27 %19 %26
+               OpSelectionMerge %30 None
+               OpBranchConditional %28 %29 %41
+         %29 = OpLabel
+         %34 = OpLoad %8 %10
+         %39 = OpImage %7 %34
+         %40 = OpImageFetch %31 %39 %35 Lod|ConstOffset %13 %38
+               OpStore %33 %40
+               OpBranch %30
+         %41 = OpLabel
+         %42 = OpLoad %7 %21
+         %43 = OpImageFetch %31 %42 %35 Lod|ConstOffset %13 %38
+               OpStore %33 %43
+               OpBranch %30
+         %30 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto recipient_context =
+      BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  const auto donor_context =
+      BuildModule(env, consumer, donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), false);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesSampler) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpDecorate %16 DescriptorSet 0
+               OpDecorate %16 Binding 0
+               OpDecorate %12 DescriptorSet 0
+               OpDecorate %12 Binding 64
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %23 = OpTypeFloat 32
+          %6 = OpTypeImage %23 2D 2 0 0 1 Unknown
+         %47 = OpTypePointer UniformConstant %6
+         %12 = OpVariable %47 UniformConstant
+         %15 = OpTypeSampler
+         %55 = OpTypePointer UniformConstant %15
+         %17 = OpTypeSampledImage %6
+         %16 = OpVariable %55 UniformConstant
+         %37 = OpTypeVector %23 4
+        %109 = OpConstant %23 0
+         %66 = OpConstantComposite %37 %109 %109 %109 %109
+         %56 = OpTypeBool
+         %54 = OpConstantTrue %56
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %50
+         %50 = OpLabel
+         %51 = OpPhi %37 %66 %5 %111 %53
+               OpLoopMerge %52 %53 None
+               OpBranchConditional %54 %53 %52
+         %53 = OpLabel
+        %106 = OpLoad %6 %12
+        %107 = OpLoad %15 %16
+        %110 = OpSampledImage %17 %106 %107
+        %111 = OpImageSampleImplicitLod %37 %110 %66 Bias %109
+               OpBranch %50
+         %52 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto recipient_context =
+      BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  const auto donor_context =
+      BuildModule(env, consumer, donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), false);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageStructField) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+               OpName %4 "main"
+               OpName %10 "mySampler"
+               OpName %21 "myTexture"
+               OpName %33 "v"
+               OpDecorate %10 RelaxedPrecision
+               OpDecorate %10 DescriptorSet 0
+               OpDecorate %10 Binding 0
+               OpDecorate %11 RelaxedPrecision
+               OpDecorate %21 RelaxedPrecision
+               OpDecorate %21 DescriptorSet 0
+               OpDecorate %21 Binding 1
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %34 RelaxedPrecision
+               OpDecorate %40 RelaxedPrecision
+               OpDecorate %42 RelaxedPrecision
+               OpDecorate %43 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeImage %6 2D 0 0 0 1 Unknown
+          %8 = OpTypeSampledImage %7
+          %9 = OpTypePointer UniformConstant %8
+         %10 = OpVariable %9 UniformConstant
+         %12 = OpTypeInt 32 1
+         %13 = OpConstant %12 2
+         %15 = OpTypeVector %12 2
+         %17 = OpTypeInt 32 0
+         %18 = OpConstant %17 0
+         %20 = OpTypePointer UniformConstant %7
+         %21 = OpVariable %20 UniformConstant
+         %23 = OpConstant %12 1
+         %25 = OpConstant %17 1
+         %27 = OpTypeBool
+         %31 = OpTypeVector %6 4
+         %32 = OpTypePointer Function %31
+         %35 = OpConstantComposite %15 %23 %23
+         %36 = OpConstant %12 3
+         %37 = OpConstant %12 4
+         %38 = OpConstantComposite %15 %36 %37
+        %201 = OpTypeStruct %7 %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %33 = OpVariable %32 Function
+         %11 = OpLoad %8 %10
+         %14 = OpImage %7 %11
+         %22 = OpLoad %7 %21
+        %200 = OpCompositeConstruct %201 %14 %22
+        %202 = OpCompositeExtract %7 %200 0
+        %203 = OpCompositeExtract %7 %200 1
+         %24 = OpImageQuerySizeLod %15 %203 %23
+         %16 = OpImageQuerySizeLod %15 %202 %13
+         %26 = OpCompositeExtract %12 %24 1
+         %19 = OpCompositeExtract %12 %16 0
+         %28 = OpSGreaterThan %27 %19 %26
+               OpSelectionMerge %30 None
+               OpBranchConditional %28 %29 %41
+         %29 = OpLabel
+         %34 = OpLoad %8 %10
+         %39 = OpImage %7 %34
+         %40 = OpImageFetch %31 %39 %35 Lod|ConstOffset %13 %38
+               OpStore %33 %40
+               OpBranch %30
+         %41 = OpLabel
+         %42 = OpLoad %7 %21
+         %43 = OpImageFetch %31 %42 %35 Lod|ConstOffset %13 %38
+               OpStore %33 %43
+               OpBranch %30
+         %30 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto recipient_context =
+      BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  const auto donor_context =
+      BuildModule(env, consumer, donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), false);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageFunctionParameter) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+               OpName %4 "main"
+               OpName %10 "mySampler"
+               OpName %21 "myTexture"
+               OpName %33 "v"
+               OpDecorate %10 RelaxedPrecision
+               OpDecorate %10 DescriptorSet 0
+               OpDecorate %10 Binding 0
+               OpDecorate %11 RelaxedPrecision
+               OpDecorate %21 RelaxedPrecision
+               OpDecorate %21 DescriptorSet 0
+               OpDecorate %21 Binding 1
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %34 RelaxedPrecision
+               OpDecorate %40 RelaxedPrecision
+               OpDecorate %42 RelaxedPrecision
+               OpDecorate %43 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeImage %6 2D 0 0 0 1 Unknown
+          %8 = OpTypeSampledImage %7
+          %9 = OpTypePointer UniformConstant %8
+         %10 = OpVariable %9 UniformConstant
+         %12 = OpTypeInt 32 1
+         %13 = OpConstant %12 2
+         %15 = OpTypeVector %12 2
+         %17 = OpTypeInt 32 0
+         %18 = OpConstant %17 0
+         %20 = OpTypePointer UniformConstant %7
+         %21 = OpVariable %20 UniformConstant
+         %23 = OpConstant %12 1
+         %25 = OpConstant %17 1
+         %27 = OpTypeBool
+         %31 = OpTypeVector %6 4
+         %32 = OpTypePointer Function %31
+         %35 = OpConstantComposite %15 %23 %23
+         %36 = OpConstant %12 3
+         %37 = OpConstant %12 4
+         %38 = OpConstantComposite %15 %36 %37
+        %201 = OpTypeFunction %15 %7 %12
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %33 = OpVariable %32 Function
+         %11 = OpLoad %8 %10
+         %14 = OpImage %7 %11
+         %16 = OpFunctionCall %15 %200 %14 %13
+         %19 = OpCompositeExtract %12 %16 0
+         %22 = OpLoad %7 %21
+         %24 = OpImageQuerySizeLod %15 %22 %23
+         %26 = OpCompositeExtract %12 %24 1
+         %28 = OpSGreaterThan %27 %19 %26
+               OpSelectionMerge %30 None
+               OpBranchConditional %28 %29 %41
+         %29 = OpLabel
+         %34 = OpLoad %8 %10
+         %39 = OpImage %7 %34
+         %40 = OpImageFetch %31 %39 %35 Lod|ConstOffset %13 %38
+               OpStore %33 %40
+               OpBranch %30
+         %41 = OpLabel
+         %42 = OpLoad %7 %21
+         %43 = OpImageFetch %31 %42 %35 Lod|ConstOffset %13 %38
+               OpStore %33 %43
+               OpBranch %30
+         %30 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %200 = OpFunction %15 None %201
+        %202 = OpFunctionParameter %7
+        %203 = OpFunctionParameter %12
+        %204 = OpLabel
+        %205 = OpImageQuerySizeLod %15 %202 %203
+               OpReturnValue %205
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto recipient_context =
+      BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  const auto donor_context =
+      BuildModule(env, consumer, donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), false);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateShaderWithImageStorageClass) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpSourceExtension "GL_EXT_samplerless_texture_functions"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+               OpCapability SampledBuffer
+               OpCapability ImageBuffer
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "MainPSPacked"
+               OpExecutionMode %2 OriginUpperLeft
+               OpDecorate %18 DescriptorSet 0
+               OpDecorate %18 Binding 128
+         %49 = OpTypeInt 32 0
+         %50 = OpTypeFloat 32
+         %58 = OpConstant %50 1
+         %66 = OpConstant %49 0
+         %87 = OpTypeVector %50 2
+         %88 = OpConstantComposite %87 %58 %58
+         %17 = OpTypeImage %49 2D 2 0 0 2 R32ui
+        %118 = OpTypePointer UniformConstant %17
+        %123 = OpTypeVector %49 2
+        %132 = OpTypeVoid
+        %133 = OpTypeFunction %132
+        %142 = OpTypePointer Image %49
+         %18 = OpVariable %118 UniformConstant
+          %2 = OpFunction %132 None %133
+        %153 = OpLabel
+        %495 = OpConvertFToU %123 %88
+        %501 = OpImageTexelPointer %142 %18 %495 %66
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto recipient_context =
+      BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  const auto donor_context =
+      BuildModule(env, consumer, donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), true);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArray) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+               OpDecorate %9 ArrayStride 4
+               OpMemberDecorate %10 0 Offset 0
+               OpDecorate %10 BufferBlock
+               OpDecorate %12 DescriptorSet 0
+               OpDecorate %12 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpTypeRuntimeArray %6
+         %10 = OpTypeStruct %9
+         %11 = OpTypePointer Uniform %10
+         %12 = OpVariable %11 Uniform
+         %13 = OpTypeInt 32 0
+         %16 = OpConstant %6 0
+         %18 = OpConstant %6 1
+         %20 = OpTypePointer Uniform %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %14 = OpArrayLength %13 %12 0
+         %15 = OpBitcast %6 %14
+               OpStore %8 %15
+         %17 = OpLoad %6 %8
+         %19 = OpISub %6 %17 %18
+         %21 = OpAccessChain %20 %12 %16 %19
+               OpStore %21 %16
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto recipient_context =
+      BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  const auto donor_context =
+      BuildModule(env, consumer, donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), false);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArrayLivesafe) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+               OpDecorate %16 ArrayStride 4
+               OpMemberDecorate %17 0 Offset 0
+               OpDecorate %17 BufferBlock
+               OpDecorate %19 DescriptorSet 0
+               OpDecorate %19 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %16 = OpTypeRuntimeArray %6
+         %17 = OpTypeStruct %16
+         %18 = OpTypePointer Uniform %17
+         %19 = OpVariable %18 Uniform
+         %20 = OpTypeInt 32 0
+         %23 = OpTypeBool
+         %26 = OpConstant %6 32
+         %27 = OpTypePointer Uniform %6
+         %30 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %10
+         %10 = OpLabel
+               OpLoopMerge %12 %13 None
+               OpBranch %14
+         %14 = OpLabel
+         %15 = OpLoad %6 %8
+         %21 = OpArrayLength %20 %19 0
+         %22 = OpBitcast %6 %21
+         %24 = OpSLessThan %23 %15 %22
+               OpBranchConditional %24 %11 %12
+         %11 = OpLabel
+         %25 = OpLoad %6 %8
+         %28 = OpAccessChain %27 %19 %9 %25
+               OpStore %28 %26
+               OpBranch %13
+         %13 = OpLabel
+         %29 = OpLoad %6 %8
+         %31 = OpIAdd %6 %29 %30
+               OpStore %8 %31
+               OpBranch %10
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto recipient_context =
+      BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  const auto donor_context =
+      BuildModule(env, consumer, donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), true);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithWorkgroupVariables) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Workgroup %6
+          %8 = OpVariable %7 Workgroup
+          %9 = OpConstant %6 2
+         %10 = OpVariable %7 Workgroup
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpStore %8 %9
+         %11 = OpLoad %6 %8
+               OpStore %10 %11
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto recipient_context =
+      BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  const auto donor_context =
+      BuildModule(env, consumer, donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), true);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithAtomics) {
+  std::string recipient_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+               OpMemberDecorate %9 0 Offset 0
+               OpDecorate %9 BufferBlock
+               OpDecorate %11 DescriptorSet 0
+               OpDecorate %11 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 0
+          %7 = OpTypePointer Function %6
+          %9 = OpTypeStruct %6
+         %10 = OpTypePointer Uniform %9
+         %11 = OpVariable %10 Uniform
+         %12 = OpTypeInt 32 1
+         %13 = OpConstant %12 0
+         %14 = OpTypePointer Uniform %6
+         %16 = OpConstant %6 1
+         %17 = OpConstant %6 0
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %15 = OpAccessChain %14 %11 %13
+         %18 = OpAtomicIAdd %6 %15 %16 %17 %16
+               OpStore %8 %18
+         %19 = OpAccessChain %14 %11 %13
+         %20 = OpLoad %6 %8
+         %21 = OpAtomicUMin %6 %19 %16 %17 %20
+               OpStore %8 %21
+         %22 = OpAccessChain %14 %11 %13
+         %23 = OpLoad %6 %8
+         %24 = OpAtomicUMax %6 %22 %16 %17 %23
+               OpStore %8 %24
+         %25 = OpAccessChain %14 %11 %13
+         %26 = OpLoad %6 %8
+         %27 = OpAtomicAnd %6 %25 %16 %17 %26
+               OpStore %8 %27
+         %28 = OpAccessChain %14 %11 %13
+         %29 = OpLoad %6 %8
+         %30 = OpAtomicOr %6 %28 %16 %17 %29
+               OpStore %8 %30
+         %31 = OpAccessChain %14 %11 %13
+         %32 = OpLoad %6 %8
+         %33 = OpAtomicXor %6 %31 %16 %17 %32
+               OpStore %8 %33
+         %34 = OpAccessChain %14 %11 %13
+         %35 = OpLoad %6 %8
+         %36 = OpAtomicExchange %6 %34 %16 %17 %35
+               OpStore %8 %36
+         %37 = OpAccessChain %14 %11 %13
+         %38 = OpLoad %6 %8
+         %39 = OpAtomicCompareExchange %6 %37 %16 %17 %17 %16 %38
+               OpStore %8 %39
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto recipient_context =
+      BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+  const auto donor_context =
+      BuildModule(env, consumer, donor_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  PseudoRandomGenerator prng(0);
+  FuzzerContext fuzzer_context(&prng, 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
+
+  fuzzer_pass.DonateSingleModule(donor_context.get(), true);
+
+  // We just check that the result is valid.  Checking to what it should be
+  // exactly equal to would be very fragile.
+  ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
 TEST(FuzzerPassDonateModulesTest, Miscellaneous1) {
   std::string recipient_shader = R"(
                OpCapability Shader
@@ -658,13 +1660,16 @@
   ASSERT_TRUE(IsValid(env, donor_context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
   protobufs::TransformationSequence transformation_sequence;
 
-  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager,
-                                      &fuzzer_context, &transformation_sequence,
-                                      {});
+  FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+                                      &transformation_context, &fuzzer_context,
+                                      &transformation_sequence, {});
 
   fuzzer_pass.DonateSingleModule(donor_context.get(), false);
 
diff --git a/test/fuzz/fuzzer_replayer_test.cpp b/test/fuzz/fuzzer_replayer_test.cpp
index b91393e..1e7c643 100644
--- a/test/fuzz/fuzzer_replayer_test.cpp
+++ b/test/fuzz/fuzzer_replayer_test.cpp
@@ -1553,6 +1553,47 @@
                OpFunctionEnd
   )";
 
+// Some miscellaneous SPIR-V.
+
+const std::string kTestShader6 = R"(
+               OpCapability Shader
+               OpCapability SampledBuffer
+               OpCapability ImageBuffer
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %40 %41
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource GLSL 450
+               OpDecorate %40 DescriptorSet 0
+               OpDecorate %40 Binding 69
+               OpDecorate %41 DescriptorSet 0
+               OpDecorate %41 Binding 1
+         %54 = OpTypeFloat 32
+         %76 = OpTypeVector %54 4
+         %55 = OpConstant %54 0
+         %56 = OpTypeVector %54 3
+         %94 = OpTypeVector %54 2
+        %112 = OpConstantComposite %94 %55 %55
+         %57 = OpConstantComposite %56 %55 %55 %55
+         %15 = OpTypeImage %54 2D 2 0 0 1 Unknown
+        %114 = OpTypePointer UniformConstant %15
+         %38 = OpTypeSampler
+        %125 = OpTypePointer UniformConstant %38
+        %132 = OpTypeVoid
+        %133 = OpTypeFunction %132
+         %45 = OpTypeSampledImage %15
+         %40 = OpVariable %114 UniformConstant
+         %41 = OpVariable %125 UniformConstant
+          %2 = OpFunction %132 None %133
+        %164 = OpLabel
+        %184 = OpLoad %15 %40
+	%213 = OpLoad %38 %41
+        %216 = OpSampledImage %45 %184 %213
+        %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55
+               OpReturn
+               OpFunctionEnd
+  )";
+
 void AddConstantUniformFact(protobufs::FactSequence* facts,
                             uint32_t descriptor_set, uint32_t binding,
                             std::vector<uint32_t>&& indices, uint32_t value) {
@@ -1591,7 +1632,7 @@
 
   std::vector<fuzzerutil::ModuleSupplier> donor_suppliers;
   for (auto donor : {&kTestShader1, &kTestShader2, &kTestShader3, &kTestShader4,
-                     &kTestShader5}) {
+                     &kTestShader5, &kTestShader6}) {
     donor_suppliers.emplace_back([donor]() {
       return BuildModule(env, kConsoleMessageConsumer, *donor,
                          kFuzzAssembleOption);
@@ -1602,8 +1643,9 @@
     std::vector<uint32_t> fuzzer_binary_out;
     protobufs::TransformationSequence fuzzer_transformation_sequence_out;
 
-    Fuzzer fuzzer(env, seed, true);
-    fuzzer.SetMessageConsumer(kSilentConsumer);
+    spvtools::ValidatorOptions validator_options;
+    Fuzzer fuzzer(env, seed, true, validator_options);
+    fuzzer.SetMessageConsumer(kConsoleMessageConsumer);
     auto fuzzer_result_status =
         fuzzer.Run(binary_in, initial_facts, donor_suppliers,
                    &fuzzer_binary_out, &fuzzer_transformation_sequence_out);
@@ -1613,8 +1655,8 @@
     std::vector<uint32_t> replayer_binary_out;
     protobufs::TransformationSequence replayer_transformation_sequence_out;
 
-    Replayer replayer(env, false);
-    replayer.SetMessageConsumer(kSilentConsumer);
+    Replayer replayer(env, false, validator_options);
+    replayer.SetMessageConsumer(kConsoleMessageConsumer);
     auto replayer_result_status = replayer.Run(
         binary_in, initial_facts, fuzzer_transformation_sequence_out,
         &replayer_binary_out, &replayer_transformation_sequence_out);
@@ -1681,6 +1723,13 @@
                        kNumFuzzerRuns);
 }
 
+TEST(FuzzerReplayerTest, Miscellaneous6) {
+  // Do some fuzzer runs, starting from an initial seed of 57 (seed value chosen
+  // arbitrarily).
+  RunFuzzerAndReplayer(kTestShader6, protobufs::FactSequence(), 57,
+                       kNumFuzzerRuns);
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/test/fuzz/fuzzer_shrinker_test.cpp b/test/fuzz/fuzzer_shrinker_test.cpp
index c906a1e..24b4460 100644
--- a/test/fuzz/fuzzer_shrinker_test.cpp
+++ b/test/fuzz/fuzzer_shrinker_test.cpp
@@ -979,15 +979,19 @@
 // The |step_limit| parameter restricts the number of steps that the shrinker
 // will try; it can be set to something small for a faster (but less thorough)
 // test.
+//
+// The |validator_options| parameter provides validator options that should be
+// used during shrinking.
 void RunAndCheckShrinker(
     const spv_target_env& target_env, const std::vector<uint32_t>& binary_in,
     const protobufs::FactSequence& initial_facts,
     const protobufs::TransformationSequence& transformation_sequence_in,
     const Shrinker::InterestingnessFunction& interestingness_function,
     const std::vector<uint32_t>& expected_binary_out,
-    uint32_t expected_transformations_out_size, uint32_t step_limit) {
+    uint32_t expected_transformations_out_size, uint32_t step_limit,
+    spv_validator_options validator_options) {
   // Run the shrinker.
-  Shrinker shrinker(target_env, step_limit, false);
+  Shrinker shrinker(target_env, step_limit, false, validator_options);
   shrinker.SetMessageConsumer(kSilentConsumer);
 
   std::vector<uint32_t> binary_out;
@@ -1035,7 +1039,8 @@
   // Run the fuzzer and check that it successfully yields a valid binary.
   std::vector<uint32_t> fuzzer_binary_out;
   protobufs::TransformationSequence fuzzer_transformation_sequence_out;
-  Fuzzer fuzzer(env, seed, true);
+  spvtools::ValidatorOptions validator_options;
+  Fuzzer fuzzer(env, seed, true, validator_options);
   fuzzer.SetMessageConsumer(kSilentConsumer);
   auto fuzzer_result_status =
       fuzzer.Run(binary_in, initial_facts, donor_suppliers, &fuzzer_binary_out,
@@ -1048,9 +1053,10 @@
 
   // With the AlwaysInteresting test, we should quickly shrink to the original
   // binary with no transformations remaining.
-  RunAndCheckShrinker(
-      env, binary_in, initial_facts, fuzzer_transformation_sequence_out,
-      AlwaysInteresting().AsFunction(), binary_in, 0, kReasonableStepLimit);
+  RunAndCheckShrinker(env, binary_in, initial_facts,
+                      fuzzer_transformation_sequence_out,
+                      AlwaysInteresting().AsFunction(), binary_in, 0,
+                      kReasonableStepLimit, validator_options);
 
   // With the OnlyInterestingFirstTime test, no shrinking should be achieved.
   RunAndCheckShrinker(
@@ -1058,14 +1064,14 @@
       OnlyInterestingFirstTime().AsFunction(), fuzzer_binary_out,
       static_cast<uint32_t>(
           fuzzer_transformation_sequence_out.transformation_size()),
-      kReasonableStepLimit);
+      kReasonableStepLimit, validator_options);
 
   // The PingPong test is unpredictable; passing an empty expected binary
   // means that we don't check anything beyond that shrinking completes
   // successfully.
-  RunAndCheckShrinker(env, binary_in, initial_facts,
-                      fuzzer_transformation_sequence_out,
-                      PingPong().AsFunction(), {}, 0, kSmallStepLimit);
+  RunAndCheckShrinker(
+      env, binary_in, initial_facts, fuzzer_transformation_sequence_out,
+      PingPong().AsFunction(), {}, 0, kSmallStepLimit, validator_options);
 
   // The InterestingThenRandom test is unpredictable; passing an empty
   // expected binary means that we do not check anything about shrinking
@@ -1073,7 +1079,7 @@
   RunAndCheckShrinker(
       env, binary_in, initial_facts, fuzzer_transformation_sequence_out,
       InterestingThenRandom(PseudoRandomGenerator(seed)).AsFunction(), {}, 0,
-      kSmallStepLimit);
+      kSmallStepLimit, validator_options);
 }
 
 TEST(FuzzerShrinkerTest, Miscellaneous1) {
diff --git a/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_null_test.cpp b/test/fuzz/transformation_add_constant_null_test.cpp
new file mode 100644
index 0000000..0bfee34
--- /dev/null
+++ b/test/fuzz/transformation_add_constant_null_test.cpp
@@ -0,0 +1,140 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_constant_null.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddConstantNullTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 2
+          %9 = OpTypeVector %6 3
+         %10 = OpTypeVector %6 4
+         %11 = OpTypeVector %7 2
+         %20 = OpTypeSampler
+         %21 = OpTypeImage %6 2D 0 0 0 0 Rgba32f
+         %22 = OpTypeSampledImage %21
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Id already in use
+  ASSERT_FALSE(TransformationAddConstantNull(4, 11).IsApplicable(
+      context.get(), transformation_context));
+  // %1 is not a type
+  ASSERT_FALSE(TransformationAddConstantNull(100, 1).IsApplicable(
+      context.get(), transformation_context));
+
+  // %3 is a function type
+  ASSERT_FALSE(TransformationAddConstantNull(100, 3).IsApplicable(
+      context.get(), transformation_context));
+
+  // %20 is a sampler type
+  ASSERT_FALSE(TransformationAddConstantNull(100, 20).IsApplicable(
+      context.get(), transformation_context));
+
+  // %21 is an image type
+  ASSERT_FALSE(TransformationAddConstantNull(100, 21).IsApplicable(
+      context.get(), transformation_context));
+
+  // %22 is a sampled image type
+  ASSERT_FALSE(TransformationAddConstantNull(100, 22).IsApplicable(
+      context.get(), transformation_context));
+
+  TransformationAddConstantNull transformations[] = {
+      // %100 = OpConstantNull %6
+      TransformationAddConstantNull(100, 6),
+
+      // %101 = OpConstantNull %7
+      TransformationAddConstantNull(101, 7),
+
+      // %102 = OpConstantNull %8
+      TransformationAddConstantNull(102, 8),
+
+      // %103 = OpConstantNull %9
+      TransformationAddConstantNull(103, 9),
+
+      // %104 = OpConstantNull %10
+      TransformationAddConstantNull(104, 10),
+
+      // %105 = OpConstantNull %11
+      TransformationAddConstantNull(105, 11)};
+
+  for (auto& transformation : transformations) {
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 2
+          %9 = OpTypeVector %6 3
+         %10 = OpTypeVector %6 4
+         %11 = OpTypeVector %7 2
+         %20 = OpTypeSampler
+         %21 = OpTypeImage %6 2D 0 0 0 0 Rgba32f
+         %22 = OpTypeSampledImage %21
+        %100 = OpConstantNull %6
+        %101 = OpConstantNull %7
+        %102 = OpConstantNull %8
+        %103 = OpConstantNull %9
+        %104 = OpConstantNull %10
+        %105 = OpConstantNull %11
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/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..bbd915b 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,11 +1855,14 @@
   const auto consumer = nullptr;
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  // Make a sequence of instruction messages corresponding to function %8 in
+  // Make a sequence of instruction messages corresponding to function %6 in
   // |donor|.
   std::vector<protobufs::Instruction> instructions =
       GetInstructionsForFunction(env, consumer, donor, 6);
@@ -1821,8 +1875,9 @@
   loop_limiter_info.set_logical_op_id(105);
   TransformationAddFunction add_livesafe_function(instructions, 100, 32,
                                                   {loop_limiter_info}, 0, {});
-  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
-  add_livesafe_function.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
+                                                 transformation_context));
+  add_livesafe_function.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string expected = R"(
                OpCapability Shader
@@ -1958,11 +2013,14 @@
   const auto consumer = nullptr;
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  // Make a sequence of instruction messages corresponding to function %8 in
+  // Make a sequence of instruction messages corresponding to function %6 in
   // |donor|.
   std::vector<protobufs::Instruction> instructions =
       GetInstructionsForFunction(env, consumer, donor, 6);
@@ -1975,8 +2033,9 @@
   loop_limiter_info.set_logical_op_id(105);
   TransformationAddFunction add_livesafe_function(instructions, 100, 32,
                                                   {loop_limiter_info}, 0, {});
-  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
-  add_livesafe_function.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
+                                                 transformation_context));
+  add_livesafe_function.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string expected = R"(
                OpCapability Shader
@@ -2110,11 +2169,14 @@
   const auto consumer = nullptr;
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  // Make a sequence of instruction messages corresponding to function %8 in
+  // Make a sequence of instruction messages corresponding to function %6 in
   // |donor|.
   std::vector<protobufs::Instruction> instructions =
       GetInstructionsForFunction(env, consumer, donor, 6);
@@ -2127,8 +2189,9 @@
   loop_limiter_info.set_logical_op_id(105);
   TransformationAddFunction add_livesafe_function(instructions, 100, 32,
                                                   {loop_limiter_info}, 0, {});
-  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
-  add_livesafe_function.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
+                                                 transformation_context));
+  add_livesafe_function.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string expected = R"(
                OpCapability Shader
@@ -2254,11 +2317,14 @@
   const auto consumer = nullptr;
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  // Make a sequence of instruction messages corresponding to function %8 in
+  // Make a sequence of instruction messages corresponding to function %6 in
   // |donor|.
   std::vector<protobufs::Instruction> instructions =
       GetInstructionsForFunction(env, consumer, donor, 6);
@@ -2271,8 +2337,9 @@
   loop_limiter_info.set_logical_op_id(105);
   TransformationAddFunction add_livesafe_function(instructions, 100, 32,
                                                   {loop_limiter_info}, 0, {});
-  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
-  add_livesafe_function.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
+                                                 transformation_context));
+  add_livesafe_function.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string expected = R"(
                OpCapability Shader
@@ -2408,11 +2475,14 @@
   const auto consumer = nullptr;
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  // Make a sequence of instruction messages corresponding to function %8 in
+  // Make a sequence of instruction messages corresponding to function %6 in
   // |donor|.
   std::vector<protobufs::Instruction> instructions =
       GetInstructionsForFunction(env, consumer, donor, 6);
@@ -2425,8 +2495,9 @@
   loop_limiter_info.set_logical_op_id(105);
   TransformationAddFunction add_livesafe_function(instructions, 100, 32,
                                                   {loop_limiter_info}, 0, {});
-  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
-  add_livesafe_function.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
+                                                 transformation_context));
+  add_livesafe_function.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string expected = R"(
                OpCapability Shader
@@ -2570,6 +2641,9 @@
   const auto consumer = nullptr;
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -2590,15 +2664,17 @@
                                            {loop_limiter_info}, 0, {});
   // The loop limiter info is not good enough; it does not include ids to patch
   // up the OpPhi at the loop merge.
-  ASSERT_FALSE(no_op_phi_data.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      no_op_phi_data.IsApplicable(context.get(), transformation_context));
 
   // Add a phi id for the new edge from the loop back edge block to the loop
   // merge.
   loop_limiter_info.add_phi_id(28);
   TransformationAddFunction with_op_phi_data(instructions, 100, 28,
                                              {loop_limiter_info}, 0, {});
-  ASSERT_TRUE(with_op_phi_data.IsApplicable(context.get(), fact_manager));
-  with_op_phi_data.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      with_op_phi_data.IsApplicable(context.get(), transformation_context));
+  with_op_phi_data.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string expected = R"(
                OpCapability Shader
@@ -2758,6 +2834,9 @@
   const auto consumer = nullptr;
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -2776,8 +2855,9 @@
 
   TransformationAddFunction transformation(instructions, 100, 28,
                                            {loop_limiter_info}, 0, {});
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   std::string expected = R"(
                OpCapability Shader
@@ -2845,6 +2925,120 @@
   ASSERT_TRUE(IsEqual(env, expected, context.get()));
 }
 
+TEST(TransformationAddFunctionTest, StaticallyOutOfBoundsArrayAccess) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypeInt 32 0
+         %10 = OpConstant %9 3
+         %11 = OpTypeArray %8 %10
+         %12 = OpTypePointer Private %11
+         %13 = OpVariable %12 Private
+         %14 = OpConstant %8 3
+         %20 = OpConstant %8 2
+         %15 = OpConstant %8 1
+         %21 = OpTypeBool
+         %16 = OpTypePointer Private %8
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::string donor = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypeInt 32 0
+         %10 = OpConstant %9 3
+         %11 = OpTypeArray %8 %10
+         %12 = OpTypePointer Private %11
+         %13 = OpVariable %12 Private
+         %14 = OpConstant %8 3
+         %15 = OpConstant %8 1
+         %16 = OpTypePointer Private %8
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+         %17 = OpAccessChain %16 %13 %14
+               OpStore %17 %15
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Make a sequence of instruction messages corresponding to function %6 in
+  // |donor|.
+  std::vector<protobufs::Instruction> instructions =
+      GetInstructionsForFunction(env, consumer, donor, 6);
+
+  TransformationAddFunction add_livesafe_function(
+      instructions, 0, 0, {}, 0, {MakeAccessClampingInfo(17, {{100, 101}})});
+  ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
+                                                 transformation_context));
+  add_livesafe_function.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  std::string expected = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypeInt 32 0
+         %10 = OpConstant %9 3
+         %11 = OpTypeArray %8 %10
+         %12 = OpTypePointer Private %11
+         %13 = OpVariable %12 Private
+         %14 = OpConstant %8 3
+         %20 = OpConstant %8 2
+         %15 = OpConstant %8 1
+         %21 = OpTypeBool
+         %16 = OpTypePointer Private %8
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+        %100 = OpULessThanEqual %21 %14 %20
+        %101 = OpSelect %8 %100 %14 %20
+         %17 = OpAccessChain %16 %13 %101
+               OpStore %17 %15
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, expected, context.get()));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/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..5c74ca0 100644
--- a/test/fuzz/transformation_add_global_variable_test.cpp
+++ b/test/fuzz/transformation_add_global_variable_test.cpp
@@ -60,79 +60,105 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Id already in use
-  ASSERT_FALSE(TransformationAddGlobalVariable(4, 10, 0, true)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationAddGlobalVariable(4, 10, SpvStorageClassPrivate, 0, true)
+          .IsApplicable(context.get(), transformation_context));
   // %1 is not a type
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 1, 0, false)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationAddGlobalVariable(100, 1, SpvStorageClassPrivate, 0, false)
+          .IsApplicable(context.get(), transformation_context));
 
   // %7 is not a pointer type
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 7, 0, true)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationAddGlobalVariable(100, 7, SpvStorageClassPrivate, 0, true)
+          .IsApplicable(context.get(), transformation_context));
 
   // %9 does not have Private storage class
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 9, 0, false)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationAddGlobalVariable(100, 9, SpvStorageClassPrivate, 0, false)
+          .IsApplicable(context.get(), transformation_context));
 
   // %15 does not have Private storage class
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 15, 0, true)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationAddGlobalVariable(100, 15, SpvStorageClassPrivate, 0, true)
+          .IsApplicable(context.get(), transformation_context));
 
   // %10 is a pointer to float, while %16 is an int constant
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 16, false)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, SpvStorageClassPrivate,
+                                               16, false)
+                   .IsApplicable(context.get(), transformation_context));
 
   // %10 is a Private pointer to float, while %15 is a variable with type
   // Uniform float pointer
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 15, true)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationAddGlobalVariable(100, 10, SpvStorageClassPrivate, 15, true)
+          .IsApplicable(context.get(), transformation_context));
 
   // %12 is a Private pointer to int, while %10 is a variable with type
   // Private float pointer
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 12, 10, false)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate,
+                                               10, false)
+                   .IsApplicable(context.get(), transformation_context));
 
   // %10 is pointer-to-float, and %14 has type pointer-to-float; that's not OK
   // since the initializer's type should be the *pointee* type.
-  ASSERT_FALSE(TransformationAddGlobalVariable(104, 10, 14, true)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationAddGlobalVariable(104, 10, SpvStorageClassPrivate, 14, true)
+          .IsApplicable(context.get(), transformation_context));
 
   // This would work in principle, but logical addressing does not allow
   // a pointer to a pointer.
-  ASSERT_FALSE(TransformationAddGlobalVariable(104, 17, 14, false)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationAddGlobalVariable(104, 17, SpvStorageClassPrivate,
+                                               14, false)
+                   .IsApplicable(context.get(), transformation_context));
 
   TransformationAddGlobalVariable transformations[] = {
       // %100 = OpVariable %12 Private
-      TransformationAddGlobalVariable(100, 12, 16, true),
+      TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16,
+                                      true),
 
       // %101 = OpVariable %10 Private
-      TransformationAddGlobalVariable(101, 10, 40, false),
+      TransformationAddGlobalVariable(101, 10, SpvStorageClassPrivate, 40,
+                                      false),
 
       // %102 = OpVariable %13 Private
-      TransformationAddGlobalVariable(102, 13, 41, true),
+      TransformationAddGlobalVariable(102, 13, SpvStorageClassPrivate, 41,
+                                      true),
 
       // %103 = OpVariable %12 Private %16
-      TransformationAddGlobalVariable(103, 12, 16, false),
+      TransformationAddGlobalVariable(103, 12, SpvStorageClassPrivate, 16,
+                                      false),
 
       // %104 = OpVariable %19 Private %21
-      TransformationAddGlobalVariable(104, 19, 21, true),
+      TransformationAddGlobalVariable(104, 19, SpvStorageClassPrivate, 21,
+                                      true),
 
       // %105 = OpVariable %19 Private %22
-      TransformationAddGlobalVariable(105, 19, 22, false)};
+      TransformationAddGlobalVariable(105, 19, SpvStorageClassPrivate, 22,
+                                      false)};
 
   for (auto& transformation : transformations) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(100));
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(102));
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(104));
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101));
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(103));
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(105));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(104));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(103));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(105));
 
   ASSERT_TRUE(IsValid(env, context.get()));
 
@@ -223,24 +249,34 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationAddGlobalVariable transformations[] = {
       // %100 = OpVariable %12 Private
-      TransformationAddGlobalVariable(100, 12, 16, true),
+      TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16,
+                                      true),
 
       // %101 = OpVariable %12 Private %16
-      TransformationAddGlobalVariable(101, 12, 16, false),
+      TransformationAddGlobalVariable(101, 12, SpvStorageClassPrivate, 16,
+                                      false),
 
       // %102 = OpVariable %19 Private %21
-      TransformationAddGlobalVariable(102, 19, 21, true)};
+      TransformationAddGlobalVariable(102, 19, SpvStorageClassPrivate, 21,
+                                      true)};
 
   for (auto& transformation : transformations) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(100));
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(102));
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -284,6 +320,85 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationAddGlobalVariableTest, TestAddingWorkgroupGlobals) {
+  // This checks that workgroup globals can be added to a compute shader.
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main"
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Workgroup %6
+         %50 = OpConstant %6 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+#ifndef NDEBUG
+  ASSERT_DEATH(
+      TransformationAddGlobalVariable(8, 7, SpvStorageClassWorkgroup, 50, true)
+          .IsApplicable(context.get(), transformation_context),
+      "By construction this transformation should not have an.*initializer "
+      "when Workgroup storage class is used");
+#endif
+
+  TransformationAddGlobalVariable transformations[] = {
+      // %8 = OpVariable %7 Workgroup
+      TransformationAddGlobalVariable(8, 7, SpvStorageClassWorkgroup, 0, true),
+
+      // %10 = OpVariable %7 Workgroup
+      TransformationAddGlobalVariable(10, 7, SpvStorageClassWorkgroup, 0,
+                                      false)};
+
+  for (auto& transformation : transformations) {
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
+  }
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(8));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(10));
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %4 "main" %8 %10
+               OpExecutionMode %4 LocalSize 1 1 1
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Workgroup %6
+         %50 = OpConstant %6 2
+          %8 = OpVariable %7 Workgroup
+         %10 = OpVariable %7 Workgroup
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/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_adjust_branch_weights_test.cpp b/test/fuzz/transformation_adjust_branch_weights_test.cpp
new file mode 100644
index 0000000..7f8ba31
--- /dev/null
+++ b/test/fuzz/transformation_adjust_branch_weights_test.cpp
@@ -0,0 +1,349 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_adjust_branch_weights.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAdjustBranchWeightsTest, IsApplicableTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %51 %27
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %25 "buf"
+               OpMemberName %25 0 "value"
+               OpName %27 ""
+               OpName %51 "color"
+               OpMemberDecorate %25 0 Offset 0
+               OpDecorate %25 Block
+               OpDecorate %27 DescriptorSet 0
+               OpDecorate %27 Binding 0
+               OpDecorate %51 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+        %150 = OpTypeVector %6 2
+         %10 = OpConstant %6 0.300000012
+         %11 = OpConstant %6 0.400000006
+         %12 = OpConstant %6 0.5
+         %13 = OpConstant %6 1
+         %14 = OpConstantComposite %7 %10 %11 %12 %13
+         %15 = OpTypeInt 32 1
+         %18 = OpConstant %15 0
+         %25 = OpTypeStruct %6
+         %26 = OpTypePointer Uniform %25
+         %27 = OpVariable %26 Uniform
+         %28 = OpTypePointer Uniform %6
+         %32 = OpTypeBool
+        %103 = OpConstantTrue %32
+         %34 = OpConstant %6 0.100000001
+         %48 = OpConstant %15 1
+         %50 = OpTypePointer Output %7
+         %51 = OpVariable %50 Output
+        %100 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %101 = OpVariable %100 Function
+        %102 = OpVariable %100 Function
+               OpBranch %19
+         %19 = OpLabel
+         %60 = OpPhi %7 %14 %5 %58 %20
+         %59 = OpPhi %15 %18 %5 %49 %20
+         %29 = OpAccessChain %28 %27 %18
+         %30 = OpLoad %6 %29
+         %31 = OpConvertFToS %15 %30
+         %33 = OpSLessThan %32 %59 %31
+               OpLoopMerge %21 %20 None
+               OpBranchConditional %33 %20 %21 1 2
+         %20 = OpLabel
+         %39 = OpCompositeExtract %6 %60 0
+         %40 = OpFAdd %6 %39 %34
+         %55 = OpCompositeInsert %7 %40 %60 0
+         %44 = OpCompositeExtract %6 %60 1
+         %45 = OpFSub %6 %44 %34
+         %58 = OpCompositeInsert %7 %45 %55 1
+         %49 = OpIAdd %15 %59 %48
+               OpBranch %19
+         %21 = OpLabel
+               OpStore %51 %60
+               OpSelectionMerge %105 None
+               OpBranchConditional %103 %104 %105
+        %104 = OpLabel
+               OpBranch %105
+        %105 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Tests OpBranchConditional instruction with weigths.
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(33, SpvOpBranchConditional, 0);
+  auto transformation =
+      TransformationAdjustBranchWeights(instruction_descriptor, {0, 1});
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests the two branch weights equal to 0.
+  instruction_descriptor =
+      MakeInstructionDescriptor(33, SpvOpBranchConditional, 0);
+  transformation =
+      TransformationAdjustBranchWeights(instruction_descriptor, {0, 0});
+#ifndef NDEBUG
+  ASSERT_DEATH(
+      transformation.IsApplicable(context.get(), transformation_context),
+      "At least one weight must be non-zero");
+#endif
+
+  // Tests 32-bit unsigned integer overflow.
+  instruction_descriptor =
+      MakeInstructionDescriptor(33, SpvOpBranchConditional, 0);
+  transformation = TransformationAdjustBranchWeights(instruction_descriptor,
+                                                     {UINT32_MAX, 0});
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(33, SpvOpBranchConditional, 0);
+  transformation = TransformationAdjustBranchWeights(instruction_descriptor,
+                                                     {1, UINT32_MAX});
+#ifndef NDEBUG
+  ASSERT_DEATH(
+      transformation.IsApplicable(context.get(), transformation_context),
+      "The sum of the two weights must not be greater than UINT32_MAX");
+#endif
+
+  // Tests OpBranchConditional instruction with no weights.
+  instruction_descriptor =
+      MakeInstructionDescriptor(21, SpvOpBranchConditional, 0);
+  transformation =
+      TransformationAdjustBranchWeights(instruction_descriptor, {0, 1});
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests non-OpBranchConditional instructions.
+  instruction_descriptor = MakeInstructionDescriptor(2, SpvOpTypeVoid, 0);
+  transformation =
+      TransformationAdjustBranchWeights(instruction_descriptor, {5, 6});
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  instruction_descriptor = MakeInstructionDescriptor(20, SpvOpLabel, 0);
+  transformation =
+      TransformationAdjustBranchWeights(instruction_descriptor, {1, 2});
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  instruction_descriptor = MakeInstructionDescriptor(49, SpvOpIAdd, 0);
+  transformation =
+      TransformationAdjustBranchWeights(instruction_descriptor, {1, 2});
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationAdjustBranchWeightsTest, ApplyTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %51 %27
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %25 "buf"
+               OpMemberName %25 0 "value"
+               OpName %27 ""
+               OpName %51 "color"
+               OpMemberDecorate %25 0 Offset 0
+               OpDecorate %25 Block
+               OpDecorate %27 DescriptorSet 0
+               OpDecorate %27 Binding 0
+               OpDecorate %51 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+        %150 = OpTypeVector %6 2
+         %10 = OpConstant %6 0.300000012
+         %11 = OpConstant %6 0.400000006
+         %12 = OpConstant %6 0.5
+         %13 = OpConstant %6 1
+         %14 = OpConstantComposite %7 %10 %11 %12 %13
+         %15 = OpTypeInt 32 1
+         %18 = OpConstant %15 0
+         %25 = OpTypeStruct %6
+         %26 = OpTypePointer Uniform %25
+         %27 = OpVariable %26 Uniform
+         %28 = OpTypePointer Uniform %6
+         %32 = OpTypeBool
+        %103 = OpConstantTrue %32
+         %34 = OpConstant %6 0.100000001
+         %48 = OpConstant %15 1
+         %50 = OpTypePointer Output %7
+         %51 = OpVariable %50 Output
+        %100 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %101 = OpVariable %100 Function
+        %102 = OpVariable %100 Function
+               OpBranch %19
+         %19 = OpLabel
+         %60 = OpPhi %7 %14 %5 %58 %20
+         %59 = OpPhi %15 %18 %5 %49 %20
+         %29 = OpAccessChain %28 %27 %18
+         %30 = OpLoad %6 %29
+         %31 = OpConvertFToS %15 %30
+         %33 = OpSLessThan %32 %59 %31
+               OpLoopMerge %21 %20 None
+               OpBranchConditional %33 %20 %21 1 2
+         %20 = OpLabel
+         %39 = OpCompositeExtract %6 %60 0
+         %40 = OpFAdd %6 %39 %34
+         %55 = OpCompositeInsert %7 %40 %60 0
+         %44 = OpCompositeExtract %6 %60 1
+         %45 = OpFSub %6 %44 %34
+         %58 = OpCompositeInsert %7 %45 %55 1
+         %49 = OpIAdd %15 %59 %48
+               OpBranch %19
+         %21 = OpLabel
+               OpStore %51 %60
+               OpSelectionMerge %105 None
+               OpBranchConditional %103 %104 %105
+        %104 = OpLabel
+               OpBranch %105
+        %105 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(33, SpvOpBranchConditional, 0);
+  auto transformation =
+      TransformationAdjustBranchWeights(instruction_descriptor, {5, 6});
+  transformation.Apply(context.get(), &transformation_context);
+
+  instruction_descriptor =
+      MakeInstructionDescriptor(21, SpvOpBranchConditional, 0);
+  transformation =
+      TransformationAdjustBranchWeights(instruction_descriptor, {7, 8});
+  transformation.Apply(context.get(), &transformation_context);
+
+  std::string variant_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %51 %27
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %25 "buf"
+               OpMemberName %25 0 "value"
+               OpName %27 ""
+               OpName %51 "color"
+               OpMemberDecorate %25 0 Offset 0
+               OpDecorate %25 Block
+               OpDecorate %27 DescriptorSet 0
+               OpDecorate %27 Binding 0
+               OpDecorate %51 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+        %150 = OpTypeVector %6 2
+         %10 = OpConstant %6 0.300000012
+         %11 = OpConstant %6 0.400000006
+         %12 = OpConstant %6 0.5
+         %13 = OpConstant %6 1
+         %14 = OpConstantComposite %7 %10 %11 %12 %13
+         %15 = OpTypeInt 32 1
+         %18 = OpConstant %15 0
+         %25 = OpTypeStruct %6
+         %26 = OpTypePointer Uniform %25
+         %27 = OpVariable %26 Uniform
+         %28 = OpTypePointer Uniform %6
+         %32 = OpTypeBool
+        %103 = OpConstantTrue %32
+         %34 = OpConstant %6 0.100000001
+         %48 = OpConstant %15 1
+         %50 = OpTypePointer Output %7
+         %51 = OpVariable %50 Output
+        %100 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %101 = OpVariable %100 Function
+        %102 = OpVariable %100 Function
+               OpBranch %19
+         %19 = OpLabel
+         %60 = OpPhi %7 %14 %5 %58 %20
+         %59 = OpPhi %15 %18 %5 %49 %20
+         %29 = OpAccessChain %28 %27 %18
+         %30 = OpLoad %6 %29
+         %31 = OpConvertFToS %15 %30
+         %33 = OpSLessThan %32 %59 %31
+               OpLoopMerge %21 %20 None
+               OpBranchConditional %33 %20 %21 5 6
+         %20 = OpLabel
+         %39 = OpCompositeExtract %6 %60 0
+         %40 = OpFAdd %6 %39 %34
+         %55 = OpCompositeInsert %7 %40 %60 0
+         %44 = OpCompositeExtract %6 %60 1
+         %45 = OpFSub %6 %44 %34
+         %58 = OpCompositeInsert %7 %45 %55 1
+         %49 = OpIAdd %15 %59 %48
+               OpBranch %19
+         %21 = OpLabel
+               OpStore %51 %60
+               OpSelectionMerge %105 None
+               OpBranchConditional %103 %104 %105 7 8
+        %104 = OpLabel
+               OpBranch %105
+        %105 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_composite_construct_test.cpp b/test/fuzz/transformation_composite_construct_test.cpp
index d303368..b663866 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,18 +141,18 @@
   TransformationCompositeConstruct make_vec2_array_length_3_bad(
       37, {41, 45, 27, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0),
       200);
-  ASSERT_TRUE(
-      make_vec2_array_length_3.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(
-      make_vec2_array_length_3_bad.IsApplicable(context.get(), fact_manager));
-  make_vec2_array_length_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_vec2_array_length_3.IsApplicable(context.get(),
+                                                    transformation_context));
+  ASSERT_FALSE(make_vec2_array_length_3_bad.IsApplicable(
+      context.get(), transformation_context));
+  make_vec2_array_length_3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(41, {}), MakeDataDescriptor(200, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(45, {}), MakeDataDescriptor(200, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(27, {}), MakeDataDescriptor(200, {2}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(41, {}), MakeDataDescriptor(200, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(45, {}), MakeDataDescriptor(200, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(27, {}), MakeDataDescriptor(200, {2})));
 
   // Make a float[2]
   TransformationCompositeConstruct make_float_array_length_2(
@@ -157,16 +160,16 @@
   // Bad: %41 does not have type float
   TransformationCompositeConstruct make_float_array_length_2_bad(
       9, {41, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201);
-  ASSERT_TRUE(
-      make_float_array_length_2.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(
-      make_float_array_length_2_bad.IsApplicable(context.get(), fact_manager));
-  make_float_array_length_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_float_array_length_2.IsApplicable(context.get(),
+                                                     transformation_context));
+  ASSERT_FALSE(make_float_array_length_2_bad.IsApplicable(
+      context.get(), transformation_context));
+  make_float_array_length_2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(24, {}), MakeDataDescriptor(201, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(40, {}), MakeDataDescriptor(201, {1}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(24, {}), MakeDataDescriptor(201, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(40, {}), MakeDataDescriptor(201, {1})));
 
   // Make a bool[3]
   TransformationCompositeConstruct make_bool_array_length_3(
@@ -176,18 +179,18 @@
   TransformationCompositeConstruct make_bool_array_length_3_bad(
       47, {33, 54, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 0),
       202);
-  ASSERT_TRUE(
-      make_bool_array_length_3.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(
-      make_bool_array_length_3_bad.IsApplicable(context.get(), fact_manager));
-  make_bool_array_length_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_bool_array_length_3.IsApplicable(context.get(),
+                                                    transformation_context));
+  ASSERT_FALSE(make_bool_array_length_3_bad.IsApplicable(
+      context.get(), transformation_context));
+  make_bool_array_length_3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(33, {}), MakeDataDescriptor(202, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {2}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(33, {}), MakeDataDescriptor(202, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {2})));
 
   // make a uvec3[2][2]
   TransformationCompositeConstruct make_uvec3_array_length_2_2(
@@ -195,17 +198,16 @@
   // Bad: Skip count 100 is too large.
   TransformationCompositeConstruct make_uvec3_array_length_2_2_bad(
       58, {33, 54}, MakeInstructionDescriptor(64, SpvOpStore, 100), 203);
-  ASSERT_TRUE(
-      make_uvec3_array_length_2_2.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_uvec3_array_length_2_2_bad.IsApplicable(context.get(),
-                                                            fact_manager));
-  make_uvec3_array_length_2_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_uvec3_array_length_2_2.IsApplicable(context.get(),
+                                                       transformation_context));
+  ASSERT_FALSE(make_uvec3_array_length_2_2_bad.IsApplicable(
+      context.get(), transformation_context));
+  make_uvec3_array_length_2_2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(69, {}), MakeDataDescriptor(203, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(100, {}),
-                                        MakeDataDescriptor(203, {1}),
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(69, {}), MakeDataDescriptor(203, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(100, {}), MakeDataDescriptor(203, {1})));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -393,6 +395,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // make a mat3x4
   TransformationCompositeConstruct make_mat34(
@@ -400,16 +405,17 @@
   // Bad: %35 is mat4x3, not mat3x4.
   TransformationCompositeConstruct make_mat34_bad(
       35, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
-  ASSERT_TRUE(make_mat34.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_mat34_bad.IsApplicable(context.get(), fact_manager));
-  make_mat34.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_mat34.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_mat34_bad.IsApplicable(context.get(), transformation_context));
+  make_mat34.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(28, {}), MakeDataDescriptor(200, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(31, {}), MakeDataDescriptor(200, {2}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(28, {}), MakeDataDescriptor(200, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(31, {}), MakeDataDescriptor(200, {2})));
 
   // make a mat4x3
   TransformationCompositeConstruct make_mat43(
@@ -417,19 +423,19 @@
   // Bad: %25 does not match the matrix's column type.
   TransformationCompositeConstruct make_mat43_bad(
       35, {25, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201);
-  ASSERT_TRUE(make_mat43.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_mat43_bad.IsApplicable(context.get(), fact_manager));
-  make_mat43.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_mat43.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_mat43_bad.IsApplicable(context.get(), transformation_context));
+  make_mat43.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(11, {}), MakeDataDescriptor(201, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(13, {}), MakeDataDescriptor(201, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(16, {}), MakeDataDescriptor(201, {2}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(100, {}),
-                                        MakeDataDescriptor(201, {3}),
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(11, {}), MakeDataDescriptor(201, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(13, {}), MakeDataDescriptor(201, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(16, {}), MakeDataDescriptor(201, {2})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(100, {}), MakeDataDescriptor(201, {3})));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -602,6 +608,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // make an Inner
   TransformationCompositeConstruct make_inner(
@@ -609,14 +618,15 @@
   // Bad: Too few fields to make the struct.
   TransformationCompositeConstruct make_inner_bad(
       9, {25}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
-  ASSERT_TRUE(make_inner.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_inner_bad.IsApplicable(context.get(), fact_manager));
-  make_inner.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_inner.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_inner_bad.IsApplicable(context.get(), transformation_context));
+  make_inner.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(19, {}), MakeDataDescriptor(200, {1}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(19, {}), MakeDataDescriptor(200, {1})));
 
   // make an Outer
   TransformationCompositeConstruct make_outer(
@@ -626,17 +636,17 @@
   TransformationCompositeConstruct make_outer_bad(
       33, {46, 200, 56},
       MakeInstructionDescriptor(200, SpvOpCompositeConstruct, 0), 201);
-  ASSERT_TRUE(make_outer.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_outer_bad.IsApplicable(context.get(), fact_manager));
-  make_outer.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_outer.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_outer_bad.IsApplicable(context.get(), transformation_context));
+  make_outer.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(46, {}), MakeDataDescriptor(201, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(200, {}),
-                                        MakeDataDescriptor(201, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(56, {}), MakeDataDescriptor(201, {2}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(46, {}), MakeDataDescriptor(201, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(200, {}), MakeDataDescriptor(201, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(56, {}), MakeDataDescriptor(201, {2})));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -922,20 +932,24 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationCompositeConstruct make_vec2(
       7, {17, 11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200);
   // Bad: not enough data for a vec2
   TransformationCompositeConstruct make_vec2_bad(
       7, {11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200);
-  ASSERT_TRUE(make_vec2.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_vec2_bad.IsApplicable(context.get(), fact_manager));
-  make_vec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_vec2.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_vec2_bad.IsApplicable(context.get(), transformation_context));
+  make_vec2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(17, {}), MakeDataDescriptor(200, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(11, {}), MakeDataDescriptor(200, {1}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(17, {}), MakeDataDescriptor(200, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(11, {}), MakeDataDescriptor(200, {1})));
 
   TransformationCompositeConstruct make_vec3(
       25, {12, 32}, MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0),
@@ -944,18 +958,17 @@
   TransformationCompositeConstruct make_vec3_bad(
       25, {12, 32, 32},
       MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0), 201);
-  ASSERT_TRUE(make_vec3.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_vec3_bad.IsApplicable(context.get(), fact_manager));
-  make_vec3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_vec3.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_vec3_bad.IsApplicable(context.get(), transformation_context));
+  make_vec3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(12, {0}),
-                                        MakeDataDescriptor(201, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(12, {1}),
-                                        MakeDataDescriptor(201, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(32, {}), MakeDataDescriptor(201, {2}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(12, {0}), MakeDataDescriptor(201, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(12, {1}), MakeDataDescriptor(201, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(32, {}), MakeDataDescriptor(201, {2})));
 
   TransformationCompositeConstruct make_vec4(
       44, {32, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0),
@@ -964,34 +977,34 @@
   TransformationCompositeConstruct make_vec4_bad(
       44, {48, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0),
       202);
-  ASSERT_TRUE(make_vec4.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_vec4_bad.IsApplicable(context.get(), fact_manager));
-  make_vec4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_vec4.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_vec4_bad.IsApplicable(context.get(), transformation_context));
+  make_vec4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(10, {}), MakeDataDescriptor(202, {2}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(11, {}), MakeDataDescriptor(202, {3}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(10, {}), MakeDataDescriptor(202, {2})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(11, {}), MakeDataDescriptor(202, {3})));
 
   TransformationCompositeConstruct make_ivec2(
       51, {126, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203);
   // Bad: if 128 is not available at the instruction that defines 128
   TransformationCompositeConstruct make_ivec2_bad(
       51, {128, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203);
-  ASSERT_TRUE(make_ivec2.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_ivec2_bad.IsApplicable(context.get(), fact_manager));
-  make_ivec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_ivec2.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_ivec2_bad.IsApplicable(context.get(), transformation_context));
+  make_ivec2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(126, {}),
-                                        MakeDataDescriptor(203, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(120, {}),
-                                        MakeDataDescriptor(203, {1}),
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(126, {}), MakeDataDescriptor(203, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(120, {}), MakeDataDescriptor(203, {1})));
 
   TransformationCompositeConstruct make_ivec3(
       114, {56, 117, 56}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
@@ -1000,17 +1013,17 @@
   TransformationCompositeConstruct make_ivec3_bad(
       114, {56, 117, 1300}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
       204);
-  ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_ivec3_bad.IsApplicable(context.get(), fact_manager));
-  make_ivec3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_ivec3_bad.IsApplicable(context.get(), transformation_context));
+  make_ivec3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(117, {}),
-                                        MakeDataDescriptor(204, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {2}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(117, {}), MakeDataDescriptor(204, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {2})));
 
   TransformationCompositeConstruct make_ivec4(
       122, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
@@ -1019,51 +1032,50 @@
   TransformationCompositeConstruct make_ivec4_bad(
       86, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
       205);
-  ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_ivec4_bad.IsApplicable(context.get(), fact_manager));
-  make_ivec4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_ivec4_bad.IsApplicable(context.get(), transformation_context));
+  make_ivec4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(56, {}), MakeDataDescriptor(205, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(117, {}),
-                                        MakeDataDescriptor(205, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(117, {}),
-                                        MakeDataDescriptor(205, {2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(117, {}),
-                                        MakeDataDescriptor(205, {3}),
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(56, {}), MakeDataDescriptor(205, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {2})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {3})));
 
   TransformationCompositeConstruct make_uvec2(
       86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 0), 206);
   TransformationCompositeConstruct make_uvec2_bad(
       86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 200), 206);
-  ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_uvec2_bad.IsApplicable(context.get(), fact_manager));
-  make_uvec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_uvec2_bad.IsApplicable(context.get(), transformation_context));
+  make_uvec2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(18, {}), MakeDataDescriptor(206, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(38, {}), MakeDataDescriptor(206, {1}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(18, {}), MakeDataDescriptor(206, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(38, {}), MakeDataDescriptor(206, {1})));
 
   TransformationCompositeConstruct make_uvec3(
       59, {14, 18, 136}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
   // Bad because 1300 is not an id
   TransformationCompositeConstruct make_uvec3_bad(
       59, {14, 18, 1300}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
-  ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_uvec3_bad.IsApplicable(context.get(), fact_manager));
-  make_uvec3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_uvec3_bad.IsApplicable(context.get(), transformation_context));
+  make_uvec3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(14, {}), MakeDataDescriptor(207, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(18, {}), MakeDataDescriptor(207, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(136, {}),
-                                        MakeDataDescriptor(207, {2}),
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(14, {}), MakeDataDescriptor(207, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(18, {}), MakeDataDescriptor(207, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(136, {}), MakeDataDescriptor(207, {2})));
 
   TransformationCompositeConstruct make_uvec4(
       131, {14, 18, 136, 136},
@@ -1072,20 +1084,19 @@
   TransformationCompositeConstruct make_uvec4_bad(
       86, {14, 18, 136, 136},
       MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208);
-  ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_uvec4_bad.IsApplicable(context.get(), fact_manager));
-  make_uvec4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_uvec4_bad.IsApplicable(context.get(), transformation_context));
+  make_uvec4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(14, {}), MakeDataDescriptor(208, {0}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(18, {}), MakeDataDescriptor(208, {1}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(136, {}),
-                                        MakeDataDescriptor(208, {2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(136, {}),
-                                        MakeDataDescriptor(208, {3}),
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(14, {}), MakeDataDescriptor(208, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(18, {}), MakeDataDescriptor(208, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(136, {}), MakeDataDescriptor(208, {2})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(136, {}), MakeDataDescriptor(208, {3})));
 
   TransformationCompositeConstruct make_bvec2(
       102,
@@ -1102,55 +1113,51 @@
           41,
       },
       MakeInstructionDescriptor(0, SpvOpExtInstImport, 0), 209);
-  ASSERT_TRUE(make_bvec2.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_bvec2_bad.IsApplicable(context.get(), fact_manager));
-  make_bvec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_bvec2.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_bvec2_bad.IsApplicable(context.get(), transformation_context));
+  make_bvec2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(111, {}),
-                                        MakeDataDescriptor(209, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(41, {}), MakeDataDescriptor(209, {1}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(111, {}), MakeDataDescriptor(209, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(41, {}), MakeDataDescriptor(209, {1})));
 
   TransformationCompositeConstruct make_bvec3(
       93, {108, 73}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210);
   // Bad because there are too many components for a bvec3
   TransformationCompositeConstruct make_bvec3_bad(
       93, {108, 108}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210);
-  ASSERT_TRUE(make_bvec3.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_bvec3_bad.IsApplicable(context.get(), fact_manager));
-  make_bvec3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_bvec3.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_bvec3_bad.IsApplicable(context.get(), transformation_context));
+  make_bvec3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {0}),
-                                        MakeDataDescriptor(210, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {1}),
-                                        MakeDataDescriptor(210, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(73, {}), MakeDataDescriptor(210, {2}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {0}), MakeDataDescriptor(210, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {1}), MakeDataDescriptor(210, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(73, {}), MakeDataDescriptor(210, {2})));
 
   TransformationCompositeConstruct make_bvec4(
       70, {108, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211);
   // Bad because 21 is a type, not a result id
   TransformationCompositeConstruct make_bvec4_bad(
       70, {21, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211);
-  ASSERT_TRUE(make_bvec4.IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(make_bvec4_bad.IsApplicable(context.get(), fact_manager));
-  make_bvec4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(make_bvec4.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_bvec4_bad.IsApplicable(context.get(), transformation_context));
+  make_bvec4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {0}),
-                                        MakeDataDescriptor(211, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {1}),
-                                        MakeDataDescriptor(211, {1}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {0}),
-                                        MakeDataDescriptor(211, {2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {1}),
-                                        MakeDataDescriptor(211, {3}),
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {0}), MakeDataDescriptor(211, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {1}), MakeDataDescriptor(211, {1})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {0}), MakeDataDescriptor(211, {2})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(108, {1}), MakeDataDescriptor(211, {3})));
 
   std::string after_transformation = R"(
                OpCapability Shader
diff --git a/test/fuzz/transformation_composite_extract_test.cpp b/test/fuzz/transformation_composite_extract_test.cpp
index 5cc2115..a7674a6 100644
--- a/test/fuzz/transformation_composite_extract_test.cpp
+++ b/test/fuzz/transformation_composite_extract_test.cpp
@@ -96,100 +96,103 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Instruction does not exist.
   ASSERT_FALSE(TransformationCompositeExtract(
                    MakeInstructionDescriptor(36, SpvOpIAdd, 0), 200, 101, {0})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Id for composite is not a composite.
   ASSERT_FALSE(TransformationCompositeExtract(
                    MakeInstructionDescriptor(36, SpvOpIAdd, 0), 200, 27, {})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Composite does not dominate instruction being inserted before.
   ASSERT_FALSE(
       TransformationCompositeExtract(
           MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 200, 101, {0})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Too many indices for extraction from struct composite.
   ASSERT_FALSE(
       TransformationCompositeExtract(
           MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 200, 101, {0, 0})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Too many indices for extraction from struct composite.
   ASSERT_FALSE(
       TransformationCompositeExtract(
           MakeInstructionDescriptor(13, SpvOpIEqual, 0), 200, 104, {0, 0, 0})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Out of bounds index for extraction from struct composite.
   ASSERT_FALSE(
       TransformationCompositeExtract(
           MakeInstructionDescriptor(13, SpvOpIEqual, 0), 200, 104, {0, 3})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Result id already used.
   ASSERT_FALSE(TransformationCompositeExtract(
                    MakeInstructionDescriptor(35, SpvOpFAdd, 0), 80, 103, {0})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   TransformationCompositeExtract transformation_1(
       MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2});
-  ASSERT_TRUE(transformation_1.IsApplicable(context.get(), fact_manager));
-  transformation_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation_1.IsApplicable(context.get(), transformation_context));
+  transformation_1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   TransformationCompositeExtract transformation_2(
       MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 202, 104, {0, 2});
-  ASSERT_TRUE(transformation_2.IsApplicable(context.get(), fact_manager));
-  transformation_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation_2.IsApplicable(context.get(), transformation_context));
+  transformation_2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   TransformationCompositeExtract transformation_3(
       MakeInstructionDescriptor(29, SpvOpAccessChain, 0), 203, 104, {0});
-  ASSERT_TRUE(transformation_3.IsApplicable(context.get(), fact_manager));
-  transformation_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation_3.IsApplicable(context.get(), transformation_context));
+  transformation_3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   TransformationCompositeExtract transformation_4(
       MakeInstructionDescriptor(24, SpvOpStore, 0), 204, 101, {0});
-  ASSERT_TRUE(transformation_4.IsApplicable(context.get(), fact_manager));
-  transformation_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation_4.IsApplicable(context.get(), transformation_context));
+  transformation_4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   TransformationCompositeExtract transformation_5(
       MakeInstructionDescriptor(29, SpvOpBranch, 0), 205, 102, {2});
-  ASSERT_TRUE(transformation_5.IsApplicable(context.get(), fact_manager));
-  transformation_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation_5.IsApplicable(context.get(), transformation_context));
+  transformation_5.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   TransformationCompositeExtract transformation_6(
       MakeInstructionDescriptor(37, SpvOpReturn, 0), 206, 103, {1});
-  ASSERT_TRUE(transformation_6.IsApplicable(context.get(), fact_manager));
-  transformation_6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation_6.IsApplicable(context.get(), transformation_context));
+  transformation_6.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(201, {}),
-                                        MakeDataDescriptor(100, {2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(202, {}),
-                                        MakeDataDescriptor(104, {0, 2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(203, {}),
-                                        MakeDataDescriptor(104, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(204, {}),
-                                        MakeDataDescriptor(101, {0}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(205, {}),
-                                        MakeDataDescriptor(102, {2}),
-                                        context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(206, {}),
-                                        MakeDataDescriptor(103, {1}),
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(201, {}), MakeDataDescriptor(100, {2})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(202, {}), MakeDataDescriptor(104, {0, 2})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(203, {}), MakeDataDescriptor(104, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(204, {}), MakeDataDescriptor(101, {0})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(205, {}), MakeDataDescriptor(102, {2})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(206, {}), MakeDataDescriptor(103, {1})));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -348,49 +351,52 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Cannot insert before the OpVariables of a function.
   ASSERT_FALSE(
       TransformationCompositeExtract(
           MakeInstructionDescriptor(101, SpvOpVariable, 0), 200, 14, {0})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationCompositeExtract(
           MakeInstructionDescriptor(101, SpvOpVariable, 1), 200, 14, {1})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationCompositeExtract(
           MakeInstructionDescriptor(102, SpvOpVariable, 0), 200, 14, {1})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // OK to insert right after the OpVariables.
   ASSERT_FALSE(TransformationCompositeExtract(
                    MakeInstructionDescriptor(102, SpvOpBranch, 1), 200, 14, {1})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert before the OpPhis of a block.
   ASSERT_FALSE(TransformationCompositeExtract(
                    MakeInstructionDescriptor(60, SpvOpPhi, 0), 200, 14, {2})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationCompositeExtract(
                    MakeInstructionDescriptor(59, SpvOpPhi, 0), 200, 14, {3})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // OK to insert after the OpPhis.
   ASSERT_TRUE(
       TransformationCompositeExtract(
           MakeInstructionDescriptor(59, SpvOpAccessChain, 0), 200, 14, {3})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert before OpLoopMerge
   ASSERT_FALSE(TransformationCompositeExtract(
                    MakeInstructionDescriptor(33, SpvOpBranchConditional, 0),
                    200, 14, {3})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert before OpSelectionMerge
   ASSERT_FALSE(TransformationCompositeExtract(
                    MakeInstructionDescriptor(21, SpvOpBranchConditional, 0),
                    200, 14, {2})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace
diff --git a/test/fuzz/transformation_compute_data_synonym_fact_closure_test.cpp b/test/fuzz/transformation_compute_data_synonym_fact_closure_test.cpp
new file mode 100644
index 0000000..5fa74b7
--- /dev/null
+++ b/test/fuzz/transformation_compute_data_synonym_fact_closure_test.cpp
@@ -0,0 +1,377 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_compute_data_synonym_fact_closure.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationComputeDataSynonymFactClosureTest, DataSynonymFacts) {
+  // The SPIR-V types and constants come from the following code.  The body of
+  // the SPIR-V function then constructs a composite that is synonymous with
+  // myT.
+  //
+  // #version 310 es
+  //
+  // precision highp float;
+  //
+  // struct S {
+  //   int a;
+  //   uvec2 b;
+  // };
+  //
+  // struct T {
+  //   bool c[5];
+  //   mat4x2 d;
+  //   S e;
+  // };
+  //
+  // void main() {
+  //   T myT = T(bool[5](true, false, true, false, true),
+  //             mat4x2(vec2(1.0, 2.0), vec2(3.0, 4.0),
+  // 	           vec2(5.0, 6.0), vec2(7.0, 8.0)),
+  //             S(10, uvec2(100u, 200u)));
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %15 "S"
+               OpMemberName %15 0 "a"
+               OpMemberName %15 1 "b"
+               OpName %16 "T"
+               OpMemberName %16 0 "c"
+               OpMemberName %16 1 "d"
+               OpMemberName %16 2 "e"
+               OpName %18 "myT"
+               OpMemberDecorate %15 0 RelaxedPrecision
+               OpMemberDecorate %15 1 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 5
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypeFloat 32
+         %11 = OpTypeVector %10 2
+         %12 = OpTypeMatrix %11 4
+         %13 = OpTypeInt 32 1
+         %14 = OpTypeVector %7 2
+         %15 = OpTypeStruct %13 %14
+         %16 = OpTypeStruct %9 %12 %15
+         %17 = OpTypePointer Function %16
+         %19 = OpConstantTrue %6
+         %20 = OpConstantFalse %6
+         %21 = OpConstantComposite %9 %19 %20 %19 %20 %19
+         %22 = OpConstant %10 1
+         %23 = OpConstant %10 2
+         %24 = OpConstantComposite %11 %22 %23
+         %25 = OpConstant %10 3
+         %26 = OpConstant %10 4
+         %27 = OpConstantComposite %11 %25 %26
+         %28 = OpConstant %10 5
+         %29 = OpConstant %10 6
+         %30 = OpConstantComposite %11 %28 %29
+         %31 = OpConstant %10 7
+         %32 = OpConstant %10 8
+         %33 = OpConstantComposite %11 %31 %32
+         %34 = OpConstantComposite %12 %24 %27 %30 %33
+         %35 = OpConstant %13 10
+         %36 = OpConstant %7 100
+         %37 = OpConstant %7 200
+         %38 = OpConstantComposite %14 %36 %37
+         %39 = OpConstantComposite %15 %35 %38
+         %40 = OpConstantComposite %16 %21 %34 %39
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %18 = OpVariable %17 Function
+               OpStore %18 %40
+        %100 = OpCompositeConstruct %9 %19 %20 %19 %20 %19
+        %101 = OpCompositeConstruct %11 %22 %23
+        %102 = OpCompositeConstruct %11 %25 %26
+        %103 = OpCompositeConstruct %11 %28 %29
+        %104 = OpCompositeConstruct %11 %31 %32
+        %105 = OpCompositeConstruct %12 %101 %102 %103 %104
+        %106 = OpCompositeConstruct %14 %36 %37
+        %107 = OpCompositeConstruct %15 %35 %106
+        %108 = OpCompositeConstruct %16 %100 %105 %107
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  ASSERT_TRUE(TransformationComputeDataSynonymFactClosure(100).IsApplicable(
+      context.get(), transformation_context));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {}),
+                                         MakeDataDescriptor(101, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
+                                         MakeDataDescriptor(101, {0})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {1}),
+                                         MakeDataDescriptor(101, {1})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
+                                         MakeDataDescriptor(101, {1})));
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(24, {}),
+                                  MakeDataDescriptor(101, {}), context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {}),
+                                        MakeDataDescriptor(101, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
+                                        MakeDataDescriptor(101, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {1}),
+                                        MakeDataDescriptor(101, {1})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
+                                         MakeDataDescriptor(101, {1})));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {}),
+                                         MakeDataDescriptor(102, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {0}),
+                                         MakeDataDescriptor(102, {0})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {1}),
+                                         MakeDataDescriptor(102, {1})));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(27, {0}),
+                                  MakeDataDescriptor(102, {0}), context.get());
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {}),
+                                         MakeDataDescriptor(102, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {0}),
+                                        MakeDataDescriptor(102, {0})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {1}),
+                                         MakeDataDescriptor(102, {1})));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(27, {1}),
+                                  MakeDataDescriptor(102, {1}), context.get());
+
+  TransformationComputeDataSynonymFactClosure(100).Apply(
+      context.get(), &transformation_context);
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {}),
+                                        MakeDataDescriptor(102, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {0}),
+                                        MakeDataDescriptor(102, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {1}),
+                                        MakeDataDescriptor(102, {1})));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {}),
+                                         MakeDataDescriptor(103, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {0}),
+                                         MakeDataDescriptor(103, {0})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1}),
+                                         MakeDataDescriptor(103, {1})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {}),
+                                         MakeDataDescriptor(104, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {0}),
+                                         MakeDataDescriptor(104, {0})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {1}),
+                                         MakeDataDescriptor(104, {1})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {}),
+                                         MakeDataDescriptor(105, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {0}),
+                                         MakeDataDescriptor(105, {0})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {1}),
+                                         MakeDataDescriptor(105, {1})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {2}),
+                                         MakeDataDescriptor(105, {2})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {3}),
+                                         MakeDataDescriptor(105, {3})));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(30, {}),
+                                  MakeDataDescriptor(103, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(33, {}),
+                                  MakeDataDescriptor(104, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {0}),
+                                  MakeDataDescriptor(105, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {1}),
+                                  MakeDataDescriptor(105, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {2}),
+                                  MakeDataDescriptor(105, {2}), context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {}),
+                                        MakeDataDescriptor(103, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {0}),
+                                        MakeDataDescriptor(103, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1}),
+                                        MakeDataDescriptor(103, {1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {}),
+                                        MakeDataDescriptor(104, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {0}),
+                                        MakeDataDescriptor(104, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {1}),
+                                        MakeDataDescriptor(104, {1})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {}),
+                                         MakeDataDescriptor(105, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {0}),
+                                        MakeDataDescriptor(105, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {1}),
+                                        MakeDataDescriptor(105, {1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {2}),
+                                        MakeDataDescriptor(105, {2})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {3}),
+                                         MakeDataDescriptor(105, {3})));
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {3}),
+                                  MakeDataDescriptor(105, {3}), context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {0}),
+                                        MakeDataDescriptor(104, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {3}),
+                                        MakeDataDescriptor(105, {3})));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
+                                         MakeDataDescriptor(100, {})));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {0}),
+                                  MakeDataDescriptor(100, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {1}),
+                                  MakeDataDescriptor(100, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {2}),
+                                  MakeDataDescriptor(100, {2}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {3}),
+                                  MakeDataDescriptor(100, {3}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {4}),
+                                  MakeDataDescriptor(100, {4}), context.get());
+
+  TransformationComputeDataSynonymFactClosure(100).Apply(
+      context.get(), &transformation_context);
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
+                                        MakeDataDescriptor(100, {})));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(39, {0}),
+                                         MakeDataDescriptor(107, {0})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(35, {}),
+                                         MakeDataDescriptor(39, {0})));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(39, {0}),
+                                  MakeDataDescriptor(35, {}), context.get());
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(39, {0}),
+                                         MakeDataDescriptor(107, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(35, {}),
+                                        MakeDataDescriptor(39, {0})));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(38, {0}),
+                                         MakeDataDescriptor(36, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(38, {1}),
+                                         MakeDataDescriptor(37, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(106, {0}),
+                                         MakeDataDescriptor(36, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(106, {1}),
+                                         MakeDataDescriptor(37, {})));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(38, {}),
+                                         MakeDataDescriptor(106, {})));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(38, {0}),
+                                  MakeDataDescriptor(36, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(106, {0}),
+                                  MakeDataDescriptor(36, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(38, {1}),
+                                  MakeDataDescriptor(37, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(106, {1}),
+                                  MakeDataDescriptor(37, {}), context.get());
+
+  TransformationComputeDataSynonymFactClosure(100).Apply(
+      context.get(), &transformation_context);
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(38, {0}),
+                                        MakeDataDescriptor(36, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(38, {1}),
+                                        MakeDataDescriptor(37, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(106, {0}),
+                                        MakeDataDescriptor(36, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(106, {1}),
+                                        MakeDataDescriptor(37, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(38, {}),
+                                        MakeDataDescriptor(106, {})));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}),
+                                         MakeDataDescriptor(108, {})));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(107, {0}),
+                                  MakeDataDescriptor(35, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {0}),
+                                  MakeDataDescriptor(108, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {1}),
+                                  MakeDataDescriptor(108, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {2}),
+                                  MakeDataDescriptor(108, {2}), context.get());
+
+  TransformationComputeDataSynonymFactClosure(100).Apply(
+      context.get(), &transformation_context);
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}),
+                                        MakeDataDescriptor(108, {})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0}),
+                                        MakeDataDescriptor(108, {0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1}),
+                                        MakeDataDescriptor(108, {1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2}),
+                                        MakeDataDescriptor(108, {2})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 0}),
+                                        MakeDataDescriptor(108, {0, 0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 1}),
+                                        MakeDataDescriptor(108, {0, 1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 2}),
+                                        MakeDataDescriptor(108, {0, 2})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 3}),
+                                        MakeDataDescriptor(108, {0, 3})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 4}),
+                                        MakeDataDescriptor(108, {0, 4})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 0}),
+                                        MakeDataDescriptor(108, {1, 0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 1}),
+                                        MakeDataDescriptor(108, {1, 1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 2}),
+                                        MakeDataDescriptor(108, {1, 2})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 3}),
+                                        MakeDataDescriptor(108, {1, 3})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 0, 0}),
+                                        MakeDataDescriptor(108, {1, 0, 0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 1, 0}),
+                                        MakeDataDescriptor(108, {1, 1, 0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 2, 0}),
+                                        MakeDataDescriptor(108, {1, 2, 0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 3, 0}),
+                                        MakeDataDescriptor(108, {1, 3, 0})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 0, 1}),
+                                        MakeDataDescriptor(108, {1, 0, 1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 1, 1}),
+                                        MakeDataDescriptor(108, {1, 1, 1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 2, 1}),
+                                        MakeDataDescriptor(108, {1, 2, 1})));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 3, 1}),
+                                        MakeDataDescriptor(108, {1, 3, 1})));
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 0}),
+                                        MakeDataDescriptor(108, {2, 0})));
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 1}),
+                                        MakeDataDescriptor(108, {2, 1})));
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 1, 0}),
+                                        MakeDataDescriptor(108, {2, 1, 0})));
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 1, 1}),
+                                        MakeDataDescriptor(108, {2, 1, 1})));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_copy_object_test.cpp b/test/fuzz/transformation_copy_object_test.cpp
index b85f75b..cf9d135 100644
--- a/test/fuzz/transformation_copy_object_test.cpp
+++ b/test/fuzz/transformation_copy_object_test.cpp
@@ -51,77 +51,92 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
-  ASSERT_EQ(0,
-            fact_manager.GetIdsForWhichSynonymsAreKnown(context.get()).size());
+  ASSERT_EQ(0, transformation_context.GetFactManager()
+                   ->GetIdsForWhichSynonymsAreKnown()
+                   .size());
 
   {
     TransformationCopyObject copy_true(
         7, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100);
-    ASSERT_TRUE(copy_true.IsApplicable(context.get(), fact_manager));
-    copy_true.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(copy_true.IsApplicable(context.get(), transformation_context));
+    copy_true.Apply(context.get(), &transformation_context);
 
     std::vector<uint32_t> ids_for_which_synonyms_are_known =
-        fact_manager.GetIdsForWhichSynonymsAreKnown(context.get());
+        transformation_context.GetFactManager()
+            ->GetIdsForWhichSynonymsAreKnown();
     ASSERT_EQ(2, ids_for_which_synonyms_are_known.size());
     ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
                           ids_for_which_synonyms_are_known.end(),
                           7) != ids_for_which_synonyms_are_known.end());
-    ASSERT_EQ(2, fact_manager.GetSynonymsForId(7, context.get()).size());
+    ASSERT_EQ(
+        2, transformation_context.GetFactManager()->GetSynonymsForId(7).size());
     protobufs::DataDescriptor descriptor_100 = MakeDataDescriptor(100, {});
-    ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(7, {}),
-                                          descriptor_100, context.get()));
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(7, {}), descriptor_100));
   }
 
   {
     TransformationCopyObject copy_false(
         8, MakeInstructionDescriptor(100, SpvOpReturn, 0), 101);
-    ASSERT_TRUE(copy_false.IsApplicable(context.get(), fact_manager));
-    copy_false.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(copy_false.IsApplicable(context.get(), transformation_context));
+    copy_false.Apply(context.get(), &transformation_context);
     std::vector<uint32_t> ids_for_which_synonyms_are_known =
-        fact_manager.GetIdsForWhichSynonymsAreKnown(context.get());
+        transformation_context.GetFactManager()
+            ->GetIdsForWhichSynonymsAreKnown();
     ASSERT_EQ(4, ids_for_which_synonyms_are_known.size());
     ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
                           ids_for_which_synonyms_are_known.end(),
                           8) != ids_for_which_synonyms_are_known.end());
-    ASSERT_EQ(2, fact_manager.GetSynonymsForId(8, context.get()).size());
+    ASSERT_EQ(
+        2, transformation_context.GetFactManager()->GetSynonymsForId(8).size());
     protobufs::DataDescriptor descriptor_101 = MakeDataDescriptor(101, {});
-    ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(8, {}),
-                                          descriptor_101, context.get()));
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(8, {}), descriptor_101));
   }
 
   {
     TransformationCopyObject copy_false_again(
         101, MakeInstructionDescriptor(5, SpvOpReturn, 0), 102);
-    ASSERT_TRUE(copy_false_again.IsApplicable(context.get(), fact_manager));
-    copy_false_again.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        copy_false_again.IsApplicable(context.get(), transformation_context));
+    copy_false_again.Apply(context.get(), &transformation_context);
     std::vector<uint32_t> ids_for_which_synonyms_are_known =
-        fact_manager.GetIdsForWhichSynonymsAreKnown(context.get());
+        transformation_context.GetFactManager()
+            ->GetIdsForWhichSynonymsAreKnown();
     ASSERT_EQ(5, ids_for_which_synonyms_are_known.size());
     ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
                           ids_for_which_synonyms_are_known.end(),
                           101) != ids_for_which_synonyms_are_known.end());
-    ASSERT_EQ(3, fact_manager.GetSynonymsForId(101, context.get()).size());
+    ASSERT_EQ(
+        3,
+        transformation_context.GetFactManager()->GetSynonymsForId(101).size());
     protobufs::DataDescriptor descriptor_102 = MakeDataDescriptor(102, {});
-    ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(101, {}),
-                                          descriptor_102, context.get()));
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(101, {}), descriptor_102));
   }
 
   {
     TransformationCopyObject copy_true_again(
         7, MakeInstructionDescriptor(102, SpvOpReturn, 0), 103);
-    ASSERT_TRUE(copy_true_again.IsApplicable(context.get(), fact_manager));
-    copy_true_again.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        copy_true_again.IsApplicable(context.get(), transformation_context));
+    copy_true_again.Apply(context.get(), &transformation_context);
     std::vector<uint32_t> ids_for_which_synonyms_are_known =
-        fact_manager.GetIdsForWhichSynonymsAreKnown(context.get());
+        transformation_context.GetFactManager()
+            ->GetIdsForWhichSynonymsAreKnown();
     ASSERT_EQ(6, ids_for_which_synonyms_are_known.size());
     ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
                           ids_for_which_synonyms_are_known.end(),
                           7) != ids_for_which_synonyms_are_known.end());
-    ASSERT_EQ(3, fact_manager.GetSynonymsForId(7, context.get()).size());
+    ASSERT_EQ(
+        3, transformation_context.GetFactManager()->GetSynonymsForId(7).size());
     protobufs::DataDescriptor descriptor_103 = MakeDataDescriptor(103, {});
-    ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(7, {}),
-                                          descriptor_103, context.get()));
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(7, {}), descriptor_103));
   }
 
   std::string after_transformation = R"(
@@ -340,116 +355,119 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Inapplicable because %18 is decorated.
   ASSERT_FALSE(TransformationCopyObject(
                    18, MakeInstructionDescriptor(21, SpvOpAccessChain, 0), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because %77 is decorated.
   ASSERT_FALSE(TransformationCopyObject(
                    77, MakeInstructionDescriptor(77, SpvOpBranch, 0), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because %80 is decorated.
   ASSERT_FALSE(TransformationCopyObject(
                    80, MakeInstructionDescriptor(77, SpvOpIAdd, 0), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because %84 is not available at the requested point
   ASSERT_FALSE(
       TransformationCopyObject(
           84, MakeInstructionDescriptor(32, SpvOpCompositeExtract, 0), 200)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Fine because %84 is available at the requested point
   ASSERT_TRUE(
       TransformationCopyObject(
           84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 200)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because id %9 is already in use
   ASSERT_FALSE(
       TransformationCopyObject(
           84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 9)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because the requested point does not exist
   ASSERT_FALSE(TransformationCopyObject(
                    84, MakeInstructionDescriptor(86, SpvOpReturn, 2), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because %9 is not in a function
   ASSERT_FALSE(TransformationCopyObject(
                    9, MakeInstructionDescriptor(9, SpvOpTypeInt, 0), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because the insert point is right before, or inside, a chunk
   // of OpPhis
   ASSERT_FALSE(TransformationCopyObject(
                    9, MakeInstructionDescriptor(30, SpvOpPhi, 0), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationCopyObject(
                    9, MakeInstructionDescriptor(99, SpvOpPhi, 1), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // OK, because the insert point is just after a chunk of OpPhis.
   ASSERT_TRUE(TransformationCopyObject(
                   9, MakeInstructionDescriptor(96, SpvOpAccessChain, 0), 200)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because the insert point is right after an OpSelectionMerge
   ASSERT_FALSE(
       TransformationCopyObject(
           9, MakeInstructionDescriptor(58, SpvOpBranchConditional, 0), 200)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // OK, because the insert point is right before the OpSelectionMerge
   ASSERT_TRUE(TransformationCopyObject(
                   9, MakeInstructionDescriptor(58, SpvOpSelectionMerge, 0), 200)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because the insert point is right after an OpSelectionMerge
   ASSERT_FALSE(TransformationCopyObject(
                    9, MakeInstructionDescriptor(43, SpvOpSwitch, 0), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // OK, because the insert point is right before the OpSelectionMerge
   ASSERT_TRUE(TransformationCopyObject(
                   9, MakeInstructionDescriptor(43, SpvOpSelectionMerge, 0), 200)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because the insert point is right after an OpLoopMerge
   ASSERT_FALSE(
       TransformationCopyObject(
           9, MakeInstructionDescriptor(40, SpvOpBranchConditional, 0), 200)
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // OK, because the insert point is right before the OpLoopMerge
   ASSERT_TRUE(TransformationCopyObject(
                   9, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because id %300 does not exist
   ASSERT_FALSE(TransformationCopyObject(
                    300, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because the following instruction is OpVariable
   ASSERT_FALSE(TransformationCopyObject(
                    9, MakeInstructionDescriptor(180, SpvOpVariable, 0), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationCopyObject(
                    9, MakeInstructionDescriptor(181, SpvOpVariable, 0), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationCopyObject(
                    9, MakeInstructionDescriptor(182, SpvOpVariable, 0), 200)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // OK, because this is just past the group of OpVariable instructions.
   ASSERT_TRUE(TransformationCopyObject(
                   9, MakeInstructionDescriptor(182, SpvOpAccessChain, 0), 200)
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationCopyObjectTest, MiscellaneousCopies) {
@@ -515,6 +533,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   std::vector<TransformationCopyObject> transformations = {
       TransformationCopyObject(19, MakeInstructionDescriptor(22, SpvOpStore, 0),
@@ -533,8 +554,9 @@
           17, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 106)};
 
   for (auto& transformation : transformations) {
-    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    transformation.Apply(context.get(), &transformation_context);
   }
 
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -614,16 +636,19 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Illegal to copy null.
   ASSERT_FALSE(TransformationCopyObject(
                    8, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Illegal to copy an OpUndef of pointer type.
   ASSERT_FALSE(TransformationCopyObject(
                    9, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationCopyObjectTest, PropagateIrrelevantPointeeFact) {
@@ -655,7 +680,11 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
-  fact_manager.AddFactValueOfPointeeIsIrrelevant(8);
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(8);
 
   TransformationCopyObject transformation1(
       8, MakeInstructionDescriptor(9, SpvOpReturn, 0), 100);
@@ -664,18 +693,84 @@
   TransformationCopyObject transformation3(
       100, MakeInstructionDescriptor(9, SpvOpReturn, 0), 102);
 
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
 
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(8));
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(100));
-  ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(102));
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(9));
-  ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(8));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
+  ASSERT_TRUE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(9));
+  ASSERT_FALSE(
+      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
+}
+
+TEST(TransformationCopyObject, DoNotCopyOpSampledImage) {
+  // This checks that we do not try to copy the result id of an OpSampledImage
+  // instruction.
+  std::string shader = R"(
+               OpCapability Shader
+               OpCapability SampledBuffer
+               OpCapability ImageBuffer
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %40 %41
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource GLSL 450
+               OpDecorate %40 DescriptorSet 0
+               OpDecorate %40 Binding 69
+               OpDecorate %41 DescriptorSet 0
+               OpDecorate %41 Binding 1
+         %54 = OpTypeFloat 32
+         %76 = OpTypeVector %54 4
+         %55 = OpConstant %54 0
+         %56 = OpTypeVector %54 3
+         %94 = OpTypeVector %54 2
+        %112 = OpConstantComposite %94 %55 %55
+         %57 = OpConstantComposite %56 %55 %55 %55
+         %15 = OpTypeImage %54 2D 2 0 0 1 Unknown
+        %114 = OpTypePointer UniformConstant %15
+         %38 = OpTypeSampler
+        %125 = OpTypePointer UniformConstant %38
+        %132 = OpTypeVoid
+        %133 = OpTypeFunction %132
+         %45 = OpTypeSampledImage %15
+         %40 = OpVariable %114 UniformConstant
+         %41 = OpVariable %125 UniformConstant
+          %2 = OpFunction %132 None %133
+        %164 = OpLabel
+        %184 = OpLoad %15 %40
+        %213 = OpLoad %38 %41
+        %216 = OpSampledImage %45 %184 %213
+        %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  ASSERT_FALSE(
+      TransformationCopyObject(
+          216, MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0),
+          500)
+          .IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace
diff --git a/test/fuzz/transformation_equation_instruction_test.cpp b/test/fuzz/transformation_equation_instruction_test.cpp
index 81d849b..1e8aa7e 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,59 +58,61 @@
   // Bad: id already in use.
   ASSERT_FALSE(TransformationEquationInstruction(7, SpvOpSNegate, {7},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: identified instruction does not exist.
   ASSERT_FALSE(
       TransformationEquationInstruction(
           14, SpvOpSNegate, {7}, MakeInstructionDescriptor(13, SpvOpLoad, 0))
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: id 100 does not exist
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {100},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: id 20 is an OpUndef
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {20},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: id 30 is not available right before its definition
   ASSERT_FALSE(TransformationEquationInstruction(
                    14, SpvOpSNegate, {30},
                    MakeInstructionDescriptor(30, SpvOpCopyObject, 0))
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: too many arguments to OpSNegate.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {7, 7},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: 40 is a type id.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {40},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: wrong type of argument to OpSNegate.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {41},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   auto transformation1 = TransformationEquationInstruction(
       14, SpvOpSNegate, {7}, return_instruction);
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto transformation2 = TransformationEquationInstruction(
       15, SpvOpSNegate, {14}, return_instruction);
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {})));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -161,6 +166,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   protobufs::InstructionDescriptor return_instruction =
       MakeInstructionDescriptor(13, SpvOpReturn, 0);
@@ -168,32 +176,34 @@
   // Bad: too few arguments to OpLogicalNot.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: 6 is a type id.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {6},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: wrong type of argument to OpLogicalNot.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {21},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   auto transformation1 = TransformationEquationInstruction(
       14, SpvOpLogicalNot, {7}, return_instruction);
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto transformation2 = TransformationEquationInstruction(
       15, SpvOpLogicalNot, {14}, return_instruction);
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {})));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -248,6 +258,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   protobufs::InstructionDescriptor return_instruction =
       MakeInstructionDescriptor(13, SpvOpReturn, 0);
@@ -255,59 +268,64 @@
   // Bad: too many arguments to OpIAdd.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 16, 16},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: boolean argument to OpIAdd.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 32},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: type as argument to OpIAdd.
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {33, 16},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: arguments of mismatched widths
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 31},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: arguments of mismatched widths
   ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {31, 15},
                                                  return_instruction)
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   auto transformation1 = TransformationEquationInstruction(
       14, SpvOpIAdd, {15, 16}, return_instruction);
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto transformation2 = TransformationEquationInstruction(
       19, SpvOpISub, {14, 16}, return_instruction);
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(15, {}), MakeDataDescriptor(19, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(15, {}), MakeDataDescriptor(19, {})));
 
   auto transformation3 = TransformationEquationInstruction(
       20, SpvOpISub, {14, 15}, return_instruction);
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {})));
 
   auto transformation4 = TransformationEquationInstruction(
       22, SpvOpISub, {16, 14}, return_instruction);
-  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation4.IsApplicable(context.get(), transformation_context));
+  transformation4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto transformation5 = TransformationEquationInstruction(
       24, SpvOpSNegate, {22}, return_instruction);
-  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
-  transformation5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation5.IsApplicable(context.get(), transformation_context));
+  transformation5.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(24, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(24, {}), MakeDataDescriptor(15, {})));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -364,69 +382,80 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   protobufs::InstructionDescriptor return_instruction =
       MakeInstructionDescriptor(13, SpvOpReturn, 0);
 
   auto transformation1 = TransformationEquationInstruction(
       14, SpvOpISub, {15, 16}, return_instruction);
-  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
-  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto transformation2 = TransformationEquationInstruction(
       17, SpvOpIAdd, {14, 16}, return_instruction);
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {})));
 
   auto transformation3 = TransformationEquationInstruction(
       18, SpvOpIAdd, {16, 14}, return_instruction);
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {}), context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {})));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {})));
 
   auto transformation4 = TransformationEquationInstruction(
       19, SpvOpISub, {14, 15}, return_instruction);
-  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation4.IsApplicable(context.get(), transformation_context));
+  transformation4.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto transformation5 = TransformationEquationInstruction(
       20, SpvOpSNegate, {19}, return_instruction);
-  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
-  transformation5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation5.IsApplicable(context.get(), transformation_context));
+  transformation5.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {})));
 
   auto transformation6 = TransformationEquationInstruction(
       21, SpvOpISub, {14, 19}, return_instruction);
-  ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
-  transformation6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation6.IsApplicable(context.get(), transformation_context));
+  transformation6.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {})));
 
   auto transformation7 = TransformationEquationInstruction(
       22, SpvOpISub, {14, 18}, return_instruction);
-  ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager));
-  transformation7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation7.IsApplicable(context.get(), transformation_context));
+  transformation7.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto transformation8 = TransformationEquationInstruction(
       23, SpvOpSNegate, {22}, return_instruction);
-  ASSERT_TRUE(transformation8.IsApplicable(context.get(), fact_manager));
-  transformation8.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation8.IsApplicable(context.get(), transformation_context));
+  transformation8.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
-  ASSERT_TRUE(fact_manager.IsSynonymous(
-      MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {}), context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {})));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -457,6 +486,146 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationEquationInstructionTest, Miscellaneous1) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+        %113 = OpConstant %6 24
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  protobufs::InstructionDescriptor return_instruction =
+      MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  auto transformation1 = TransformationEquationInstruction(
+      522, SpvOpISub, {113, 113}, return_instruction);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto transformation2 = TransformationEquationInstruction(
+      570, SpvOpIAdd, {522, 113}, return_instruction);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+        %113 = OpConstant %6 24
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+        %522 = OpISub %6 %113 %113
+        %570 = OpIAdd %6 %522 %113
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(570, {}), MakeDataDescriptor(113, {})));
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, Miscellaneous2) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+        %113 = OpConstant %6 24
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  protobufs::InstructionDescriptor return_instruction =
+      MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  auto transformation1 = TransformationEquationInstruction(
+      522, SpvOpISub, {113, 113}, return_instruction);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  transformation1.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto transformation2 = TransformationEquationInstruction(
+      570, SpvOpIAdd, {522, 113}, return_instruction);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+        %113 = OpConstant %6 24
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+        %522 = OpISub %6 %113 %113
+        %570 = OpIAdd %6 %522 %113
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(570, {}), MakeDataDescriptor(113, {})));
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/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..ed4fd15 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,19 +2443,141 @@
       /*input_id_to_fresh_id*/ {{9, 206}, {10, 207}, {14, 208}, {20, 209}},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
   // The variables that were originally irrelevant, plus input parameters
   // corresponding to them, should be irrelevant.  The rest should not be.
   for (uint32_t variable_id : {9u, 14u, 206u, 208u}) {
-    ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(variable_id));
+    ASSERT_TRUE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(
+            variable_id));
   }
   for (uint32_t variable_id : {10u, 20u, 207u, 209u}) {
-    ASSERT_FALSE(fact_manager.BlockIsDead(variable_id));
+    ASSERT_FALSE(
+        transformation_context.GetFactManager()->BlockIsDead(variable_id));
   }
 }
 
+TEST(TransformationOutlineFunctionTest,
+     DoNotOutlineCodeThatProducesUsedPointer) {
+  // This checks that we cannot outline a region of code if it produces a
+  // pointer result id that gets used outside the region.  This avoids creating
+  // a struct with a pointer member.
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %6 "main"
+               OpExecutionMode %6 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %21 = OpTypeBool
+        %100 = OpTypeInt 32 0
+         %99 = OpConstant %100 0
+        %101 = OpTypeVector %100 2
+        %102 = OpTypePointer Function %100
+        %103 = OpTypePointer Function %101
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+        %104 = OpVariable %103 Function
+               OpBranch %80
+         %80 = OpLabel
+        %105 = OpAccessChain %102 %104 %99
+               OpBranch %106
+        %106 = OpLabel
+               OpStore %105 %99
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 80,
+      /*exit_block*/ 80,
+      /*new_function_struct_return_type_id*/ 300,
+      /*new_function_type_id*/ 301,
+      /*new_function_id*/ 302,
+      /*new_function_region_entry_block*/ 304,
+      /*new_caller_result_id*/ 305,
+      /*new_callee_result_id*/ 306,
+      /*input_id_to_fresh_id*/ {{104, 307}},
+      /*output_id_to_fresh_id*/ {{105, 308}});
+
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationOutlineFunctionTest, ExitBlockHeadsLoop) {
+  // This checks that it is not possible outline a region that ends in a loop
+  // head.
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %15 = OpTypeInt 32 1
+         %35 = OpTypeBool
+         %39 = OpConstant %15 1
+         %40 = OpConstantTrue %35
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %22
+         %22 = OpLabel
+               OpBranch %23
+         %23 = OpLabel
+         %24 = OpPhi %15 %39 %22 %39 %25
+               OpLoopMerge %26 %25 None
+               OpBranchConditional %40 %25 %26
+         %25 = OpLabel
+               OpBranch %23
+         %26 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 22,
+      /*exit_block*/ 23,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 203,
+      /*new_caller_result_id*/ 204,
+      /*new_callee_result_id*/ 205,
+      /*input_id_to_fresh_id*/ {},
+      /*output_id_to_fresh_id*/ {});
+
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
+}
+
 TEST(TransformationOutlineFunctionTest, Miscellaneous1) {
   // This tests outlining of some non-trivial code.
 
@@ -2423,6 +2686,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 150,
@@ -2436,8 +2702,9 @@
       /*input_id_to_fresh_id*/ {{102, 300}, {103, 301}, {40, 302}},
       /*output_id_to_fresh_id*/ {{106, 400}, {107, 401}});
 
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -2588,6 +2855,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 38,
@@ -2601,7 +2871,8 @@
       /*input_id_to_fresh_id*/ {},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationOutlineFunctionTest, Miscellaneous3) {
@@ -2643,6 +2914,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 80,
@@ -2656,8 +2930,9 @@
       /*input_id_to_fresh_id*/ {},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
@@ -2732,6 +3007,9 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   TransformationOutlineFunction transformation(
       /*entry_block*/ 80,
@@ -2745,8 +3023,9 @@
       /*input_id_to_fresh_id*/ {{104, 307}},
       /*output_id_to_fresh_id*/ {});
 
-  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
-  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   std::string after_transformation = R"(
diff --git a/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..37e9510 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"(
@@ -1212,6 +1277,179 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationReplaceIdWithSynonymTest, RuntimeArrayTest) {
+  // This checks that OpRuntimeArray is correctly handled.
+  const std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %8 ArrayStride 8
+               OpMemberDecorate %9 0 Offset 0
+               OpDecorate %9 BufferBlock
+               OpDecorate %11 DescriptorSet 0
+               OpDecorate %11 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeVector %6 2
+          %8 = OpTypeRuntimeArray %7
+          %9 = OpTypeStruct %8
+         %10 = OpTypePointer Uniform %9
+         %11 = OpVariable %10 Uniform
+         %12 = OpConstant %6 0
+         %13 = OpTypeInt 32 0
+         %14 = OpConstant %13 0
+         %15 = OpTypePointer Uniform %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %50 = OpCopyObject %6 %12
+         %51 = OpCopyObject %13 %14
+         %16 = OpAccessChain %15 %11 %12 %12 %14
+               OpStore %16 %12
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Add synonym fact relating %50 and %12.
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(50, 12),
+                                                   context.get());
+  // Add synonym fact relating %51 and %14.
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(51, 14),
+                                                   context.get());
+
+  // Not legal because the index being replaced is a struct index.
+  ASSERT_FALSE(
+      TransformationReplaceIdWithSynonym(
+          MakeIdUseDescriptor(
+              12, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 1),
+          50)
+          .IsApplicable(context.get(), transformation_context));
+
+  // Fine to replace an index into a runtime array.
+  auto replacement1 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          12, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 2),
+      50);
+  ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context));
+  replacement1.Apply(context.get(), &transformation_context);
+
+  // Fine to replace an index into a vector inside the runtime array.
+  auto replacement2 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          14, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 3),
+      51);
+  ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context));
+  replacement2.Apply(context.get(), &transformation_context);
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  const std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %8 ArrayStride 8
+               OpMemberDecorate %9 0 Offset 0
+               OpDecorate %9 BufferBlock
+               OpDecorate %11 DescriptorSet 0
+               OpDecorate %11 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeVector %6 2
+          %8 = OpTypeRuntimeArray %7
+          %9 = OpTypeStruct %8
+         %10 = OpTypePointer Uniform %9
+         %11 = OpVariable %10 Uniform
+         %12 = OpConstant %6 0
+         %13 = OpTypeInt 32 0
+         %14 = OpConstant %13 0
+         %15 = OpTypePointer Uniform %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %50 = OpCopyObject %6 %12
+         %51 = OpCopyObject %13 %14
+         %16 = OpAccessChain %15 %11 %12 %50 %51
+               OpStore %16 %12
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationReplaceIdWithSynonymTest,
+     DoNotReplaceSampleParameterOfOpImageTexelPointer) {
+  const std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 310
+          %4 = OpTypeVoid
+          %5 = OpTypeFunction %4
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpConstant %6 2
+          %9 = OpConstant %6 0
+         %10 = OpConstant %6 10
+         %11 = OpTypeBool
+         %12 = OpConstant %6 1
+         %13 = OpTypeFloat 32
+         %14 = OpTypePointer Image %13
+         %15 = OpTypeImage %13 2D 0 0 0 0 Rgba8
+         %16 = OpTypePointer Private %15
+          %3 = OpVariable %16 Private
+         %17 = OpTypeVector %6 2
+         %18 = OpConstantComposite %17 %9 %9
+          %2 = OpFunction %4 None %5
+         %19 = OpLabel
+        %100 = OpCopyObject %6 %9
+         %20 = OpImageTexelPointer %14 %3 %18 %9
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Add synonym fact relating %100 and %9.
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(100, 9),
+                                                   context.get());
+
+  // Not legal the Sample argument of OpImageTexelPointer needs to be a zero
+  // constant.
+  ASSERT_FALSE(
+      TransformationReplaceIdWithSynonym(
+          MakeIdUseDescriptor(
+              9, MakeInstructionDescriptor(20, SpvOpImageTexelPointer, 0), 2),
+          100)
+          .IsApplicable(context.get(), transformation_context));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/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..30bac02 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
@@ -845,6 +866,62 @@
   ASSERT_TRUE(IsEqual(env, after_split, context.get()));
 }
 
+TEST(TransformationSplitBlockTest, DoNotSplitUseOfOpSampledImage) {
+  // This checks that we cannot split the definition of an OpSampledImage
+  // from its use.
+  std::string shader = R"(
+               OpCapability Shader
+               OpCapability SampledBuffer
+               OpCapability ImageBuffer
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %40 %41
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource GLSL 450
+               OpDecorate %40 DescriptorSet 0
+               OpDecorate %40 Binding 69
+               OpDecorate %41 DescriptorSet 0
+               OpDecorate %41 Binding 1
+         %54 = OpTypeFloat 32
+         %76 = OpTypeVector %54 4
+         %55 = OpConstant %54 0
+         %56 = OpTypeVector %54 3
+         %94 = OpTypeVector %54 2
+        %112 = OpConstantComposite %94 %55 %55
+         %57 = OpConstantComposite %56 %55 %55 %55
+         %15 = OpTypeImage %54 2D 2 0 0 1 Unknown
+        %114 = OpTypePointer UniformConstant %15
+         %38 = OpTypeSampler
+        %125 = OpTypePointer UniformConstant %38
+        %132 = OpTypeVoid
+        %133 = OpTypeFunction %132
+         %45 = OpTypeSampledImage %15
+         %40 = OpVariable %114 UniformConstant
+         %41 = OpVariable %125 UniformConstant
+          %2 = OpFunction %132 None %133
+        %164 = OpLabel
+        %184 = OpLoad %15 %40
+        %213 = OpLoad %38 %41
+        %216 = OpSampledImage %45 %184 %213
+        %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  auto split = TransformationSplitBlock(
+      MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0), 500);
+  ASSERT_FALSE(split.IsApplicable(context.get(), transformation_context));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/test/fuzz/transformation_store_test.cpp b/test/fuzz/transformation_store_test.cpp
index 3fb9b61..07d222f 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()));
   }
 
@@ -336,6 +351,70 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationStoreTest, DoNotAllowStoresToReadOnlyMemory) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpMemberDecorate %10 0 Offset 0
+               OpMemberDecorate %10 1 Offset 4
+               OpDecorate %10 Block
+               OpMemberDecorate %23 0 Offset 0
+               OpDecorate %23 Block
+               OpDecorate %25 DescriptorSet 0
+               OpDecorate %25 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpTypeFloat 32
+         %10 = OpTypeStruct %6 %9
+         %11 = OpTypePointer PushConstant %10
+         %12 = OpVariable %11 PushConstant
+         %13 = OpConstant %6 0
+         %14 = OpTypePointer PushConstant %6
+         %17 = OpConstant %6 1
+         %18 = OpTypePointer PushConstant %9
+         %23 = OpTypeStruct %9
+         %24 = OpTypePointer UniformConstant %23
+         %25 = OpVariable %24 UniformConstant
+         %26 = OpTypePointer UniformConstant %9
+         %50 = OpConstant %9 0
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %15 = OpAccessChain %14 %12 %13
+         %19 = OpAccessChain %18 %12 %17
+         %27 = OpAccessChain %26 %25 %13
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  fact_manager.AddFactBlockIsDead(5);
+
+  ASSERT_FALSE(
+      TransformationStore(15, 13, MakeInstructionDescriptor(27, SpvOpReturn, 0))
+          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationStore(19, 50, MakeInstructionDescriptor(27, SpvOpReturn, 0))
+          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationStore(27, 50, MakeInstructionDescriptor(27, SpvOpReturn, 0))
+          .IsApplicable(context.get(), transformation_context));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/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..a29c511 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));
   temp_dd = MakeDataDescriptor(200, {1});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(10, {}), temp_dd));
 
   TransformationVectorShuffle transformation2(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 201, 20, 12,
       {0xFFFFFFFF, 3, 5});
-  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
-  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  transformation2.Apply(context.get(), &transformation_context);
   temp_dd = MakeDataDescriptor(201, {1});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(11, {}), temp_dd));
   temp_dd = MakeDataDescriptor(201, {2});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(11, {}), temp_dd));
 
   TransformationVectorShuffle transformation3(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 202, 27, 35, {5, 4, 1});
-  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
-  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation3.IsApplicable(context.get(), transformation_context));
+  transformation3.Apply(context.get(), &transformation_context);
   temp_dd = MakeDataDescriptor(202, {0});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(26, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(26, {}), temp_dd));
   temp_dd = MakeDataDescriptor(202, {1});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(25, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(25, {}), temp_dd));
   temp_dd = MakeDataDescriptor(202, {2});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(26, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(26, {}), temp_dd));
 
   TransformationVectorShuffle transformation4(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 203, 42, 46, {0, 1});
-  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
-  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation4.IsApplicable(context.get(), transformation_context));
+  transformation4.Apply(context.get(), &transformation_context);
   temp_dd = MakeDataDescriptor(203, {0});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(40, {}), temp_dd));
   temp_dd = MakeDataDescriptor(203, {1});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(41, {}), temp_dd));
 
   TransformationVectorShuffle transformation5(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 204, 42, 46, {2, 3, 4});
-  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
-  transformation5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation5.IsApplicable(context.get(), transformation_context));
+  transformation5.Apply(context.get(), &transformation_context);
   temp_dd = MakeDataDescriptor(204, {0});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(40, {}), temp_dd));
   temp_dd = MakeDataDescriptor(204, {1});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(41, {}), temp_dd));
   temp_dd = MakeDataDescriptor(204, {2});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(40, {}), temp_dd));
 
   TransformationVectorShuffle transformation6(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 205, 42, 42,
       {0, 1, 2, 3});
-  ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
-  transformation6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation6.IsApplicable(context.get(), transformation_context));
+  transformation6.Apply(context.get(), &transformation_context);
   temp_dd = MakeDataDescriptor(205, {0});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(40, {}), temp_dd));
   temp_dd = MakeDataDescriptor(205, {1});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(41, {}), temp_dd));
   temp_dd = MakeDataDescriptor(205, {2});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(40, {}), temp_dd));
   temp_dd = MakeDataDescriptor(205, {3});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(41, {}), temp_dd));
 
   // swizzle vec4 from vec4 and vec4 using some undefs
   TransformationVectorShuffle transformation7(
       MakeInstructionDescriptor(100, SpvOpReturn, 0), 206, 65, 65,
       {0xFFFFFFFF, 3, 6, 0xFFFFFFFF});
-  ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager));
-  transformation7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(
+      transformation7.IsApplicable(context.get(), transformation_context));
+  transformation7.Apply(context.get(), &transformation_context);
   temp_dd = MakeDataDescriptor(206, {1});
-  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(56, {}), temp_dd,
-                                        context.get()));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(56, {}), temp_dd));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -479,52 +489,55 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
 
   // Cannot insert before the OpVariables of a function.
   ASSERT_FALSE(
       TransformationVectorShuffle(
           MakeInstructionDescriptor(101, SpvOpVariable, 0), 200, 14, 14, {0, 1})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationVectorShuffle(
           MakeInstructionDescriptor(101, SpvOpVariable, 1), 200, 14, 14, {1, 2})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationVectorShuffle(
           MakeInstructionDescriptor(102, SpvOpVariable, 0), 200, 14, 14, {1, 2})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // OK to insert right after the OpVariables.
   ASSERT_FALSE(
       TransformationVectorShuffle(
           MakeInstructionDescriptor(102, SpvOpBranch, 1), 200, 14, 14, {1, 1})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert before the OpPhis of a block.
   ASSERT_FALSE(
       TransformationVectorShuffle(MakeInstructionDescriptor(60, SpvOpPhi, 0),
                                   200, 14, 14, {2, 0})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationVectorShuffle(MakeInstructionDescriptor(59, SpvOpPhi, 0),
                                   200, 14, 14, {3, 0})
-          .IsApplicable(context.get(), fact_manager));
+          .IsApplicable(context.get(), transformation_context));
   // OK to insert after the OpPhis.
   ASSERT_TRUE(TransformationVectorShuffle(
                   MakeInstructionDescriptor(59, SpvOpAccessChain, 0), 200, 14,
                   14, {3, 4})
-                  .IsApplicable(context.get(), fact_manager));
+                  .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert before OpLoopMerge
   ASSERT_FALSE(TransformationVectorShuffle(
                    MakeInstructionDescriptor(33, SpvOpBranchConditional, 0),
                    200, 14, 14, {3})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert before OpSelectionMerge
   ASSERT_FALSE(TransformationVectorShuffle(
                    MakeInstructionDescriptor(21, SpvOpBranchConditional, 0),
                    200, 14, 14, {2})
-                   .IsApplicable(context.get(), fact_manager));
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace