spirv-fuzz: Refactor conditions in the fact manager (#3867)

Refactors conditions and names of some methods in the fact manager. Part of #3698.
diff --git a/source/fuzz/fact_manager/constant_uniform_facts.cpp b/source/fuzz/fact_manager/constant_uniform_facts.cpp
index 23e3829..a629c0d 100644
--- a/source/fuzz/fact_manager/constant_uniform_facts.cpp
+++ b/source/fuzz/fact_manager/constant_uniform_facts.cpp
@@ -163,7 +163,8 @@
   return true;
 }
 
-bool ConstantUniformFacts::AddFact(const protobufs::FactConstantUniform& fact) {
+bool ConstantUniformFacts::MaybeAddFact(
+    const protobufs::FactConstantUniform& fact) {
   // Try to find a unique instruction that declares a variable such that the
   // variable is decorated with the descriptor set and binding associated with
   // the constant uniform fact.
diff --git a/source/fuzz/fact_manager/constant_uniform_facts.h b/source/fuzz/fact_manager/constant_uniform_facts.h
index a136a05..41d253e 100644
--- a/source/fuzz/fact_manager/constant_uniform_facts.h
+++ b/source/fuzz/fact_manager/constant_uniform_facts.h
@@ -31,7 +31,7 @@
   explicit ConstantUniformFacts(opt::IRContext* ir_context);
 
   // See method in FactManager which delegates to this method.
-  bool AddFact(const protobufs::FactConstantUniform& fact);
+  bool MaybeAddFact(const protobufs::FactConstantUniform& fact);
 
   // See method in FactManager which delegates to this method.
   std::vector<uint32_t> GetConstantsAvailableFromUniformsForType(
diff --git a/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp b/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp
index 5fc2d7f..de16472 100644
--- a/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp
+++ b/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp
@@ -55,32 +55,39 @@
     opt::IRContext* ir_context)
     : ir_context_(ir_context) {}
 
-void DataSynonymAndIdEquationFacts::AddFact(
+bool DataSynonymAndIdEquationFacts::MaybeAddFact(
     const protobufs::FactDataSynonym& fact,
     const DeadBlockFacts& dead_block_facts,
     const IrrelevantValueFacts& irrelevant_value_facts) {
-  (void)dead_block_facts;        // Keep release compilers happy.
-  (void)irrelevant_value_facts;  // Keep release compilers happy.
-  assert(!irrelevant_value_facts.IdIsIrrelevant(fact.data1().object(),
-                                                dead_block_facts) &&
-         !irrelevant_value_facts.IdIsIrrelevant(fact.data2().object(),
-                                                dead_block_facts) &&
-         "Irrelevant ids cannot be synonymous with other ids.");
+  if (irrelevant_value_facts.IdIsIrrelevant(fact.data1().object(),
+                                            dead_block_facts) ||
+      irrelevant_value_facts.IdIsIrrelevant(fact.data2().object(),
+                                            dead_block_facts)) {
+    // Irrelevant ids cannot be synonymous with other ids.
+    return false;
+  }
 
   // Add the fact, including all facts relating sub-components of the data
   // descriptors that are involved.
   AddDataSynonymFactRecursive(fact.data1(), fact.data2());
+  return true;
 }
 
-void DataSynonymAndIdEquationFacts::AddFact(
+bool DataSynonymAndIdEquationFacts::MaybeAddFact(
     const protobufs::FactIdEquation& fact,
     const DeadBlockFacts& dead_block_facts,
     const IrrelevantValueFacts& irrelevant_value_facts) {
-  (void)dead_block_facts;        // Keep release compilers happy.
-  (void)irrelevant_value_facts;  // Keep release compilers happy.
-  assert(
-      !irrelevant_value_facts.IdIsIrrelevant(fact.lhs_id(), dead_block_facts) &&
-      "Irrelevant ids are not allowed.");
+  if (irrelevant_value_facts.IdIsIrrelevant(fact.lhs_id(), dead_block_facts)) {
+    // Irrelevant ids cannot participate in IdEquation facts.
+    return false;
+  }
+
+  for (auto id : fact.rhs_id()) {
+    if (irrelevant_value_facts.IdIsIrrelevant(id, dead_block_facts)) {
+      // Irrelevant ids cannot participate in IdEquation facts.
+      return false;
+    }
+  }
 
   protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {});
 
@@ -91,9 +98,6 @@
   // equation.
   std::vector<const protobufs::DataDescriptor*> rhs_dds;
   for (auto rhs_id : fact.rhs_id()) {
-    assert(!irrelevant_value_facts.IdIsIrrelevant(rhs_id, dead_block_facts) &&
-           "Irrelevant ids are not allowed.");
-
     // Register a data descriptor based on this id in the equivalence relation
     // if needed, and then record the equivalence class representative.
     rhs_dds.push_back(RegisterDataDescriptor(MakeDataDescriptor(rhs_id, {})));
@@ -101,6 +105,7 @@
 
   // Now add the fact.
   AddEquationFactRecursive(lhs_dd, static_cast<SpvOp>(fact.opcode()), rhs_dds);
+  return true;
 }
 
 DataSynonymAndIdEquationFacts::OperationSet
diff --git a/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h b/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h
index e84632b..46f02d0 100644
--- a/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h
+++ b/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h
@@ -38,19 +38,21 @@
  public:
   explicit DataSynonymAndIdEquationFacts(opt::IRContext* ir_context);
 
-  // See method in FactManager which delegates to this method.
-  // |dead_block_facts| and |irrelevant_value_facts| are passed for consistency
-  // checks.
-  void AddFact(const protobufs::FactDataSynonym& fact,
-               const DeadBlockFacts& dead_block_facts,
-               const IrrelevantValueFacts& irrelevant_value_facts);
+  // See method in FactManager which delegates to this method. Returns true if
+  // neither |fact.data1()| nor |fact.data2()| contain an
+  // irrelevant id. Otherwise, returns false. |dead_block_facts| and
+  // |irrelevant_value_facts| are passed for consistency checks.
+  bool MaybeAddFact(const protobufs::FactDataSynonym& fact,
+                    const DeadBlockFacts& dead_block_facts,
+                    const IrrelevantValueFacts& irrelevant_value_facts);
 
-  // See method in FactManager which delegates to this method.
-  // |dead_block_facts| and |irrelevant_value_facts| are passed for consistency
-  // checks.
-  void AddFact(const protobufs::FactIdEquation& fact,
-               const DeadBlockFacts& dead_block_facts,
-               const IrrelevantValueFacts& irrelevant_value_facts);
+  // See method in FactManager which delegates to this method. Returns true if
+  // neither |fact.lhs_id()| nor any of |fact.rhs_id()| is irrelevant. Returns
+  // false otherwise. |dead_block_facts| and |irrelevant_value_facts| are passed
+  // for consistency checks.
+  bool MaybeAddFact(const protobufs::FactIdEquation& fact,
+                    const DeadBlockFacts& dead_block_facts,
+                    const IrrelevantValueFacts& irrelevant_value_facts);
 
   // See method in FactManager which delegates to this method.
   std::vector<const protobufs::DataDescriptor*> GetSynonymsForId(
diff --git a/source/fuzz/fact_manager/dead_block_facts.cpp b/source/fuzz/fact_manager/dead_block_facts.cpp
index 5f4f8bc..f3e0ef8 100644
--- a/source/fuzz/fact_manager/dead_block_facts.cpp
+++ b/source/fuzz/fact_manager/dead_block_facts.cpp
@@ -14,12 +14,22 @@
 
 #include "source/fuzz/fact_manager/dead_block_facts.h"
 
+#include "source/fuzz/fuzzer_util.h"
+
 namespace spvtools {
 namespace fuzz {
 namespace fact_manager {
 
-void DeadBlockFacts::AddFact(const protobufs::FactBlockIsDead& fact) {
+DeadBlockFacts::DeadBlockFacts(opt::IRContext* ir_context)
+    : ir_context_(ir_context) {}
+
+bool DeadBlockFacts::MaybeAddFact(const protobufs::FactBlockIsDead& fact) {
+  if (!fuzzerutil::MaybeFindBlock(ir_context_, fact.block_id())) {
+    return false;
+  }
+
   dead_block_ids_.insert(fact.block_id());
+  return true;
 }
 
 bool DeadBlockFacts::BlockIsDead(uint32_t block_id) const {
diff --git a/source/fuzz/fact_manager/dead_block_facts.h b/source/fuzz/fact_manager/dead_block_facts.h
index b2da90b..8ac5c3c 100644
--- a/source/fuzz/fact_manager/dead_block_facts.h
+++ b/source/fuzz/fact_manager/dead_block_facts.h
@@ -18,6 +18,7 @@
 #include <unordered_set>
 
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/opt/ir_context.h"
 
 namespace spvtools {
 namespace fuzz {
@@ -27,8 +28,12 @@
 // facts about data blocks.
 class DeadBlockFacts {
  public:
-  // See method in FactManager which delegates to this method.
-  void AddFact(const protobufs::FactBlockIsDead& fact);
+  explicit DeadBlockFacts(opt::IRContext* ir_context);
+
+  // Marks |fact.block_id()| as being dead. Returns true if |fact.block_id()|
+  // represents a result id of some OpLabel instruction in |ir_context_|.
+  // Returns false otherwise.
+  bool MaybeAddFact(const protobufs::FactBlockIsDead& fact);
 
   // See method in FactManager which delegates to this method.
   bool BlockIsDead(uint32_t block_id) const;
@@ -38,6 +43,7 @@
 
  private:
   std::unordered_set<uint32_t> dead_block_ids_;
+  opt::IRContext* ir_context_;
 };
 
 }  // namespace fact_manager
diff --git a/source/fuzz/fact_manager/fact_manager.cpp b/source/fuzz/fact_manager/fact_manager.cpp
index 425b0cc..29050e9 100644
--- a/source/fuzz/fact_manager/fact_manager.cpp
+++ b/source/fuzz/fact_manager/fact_manager.cpp
@@ -90,38 +90,49 @@
 FactManager::FactManager(opt::IRContext* ir_context)
     : constant_uniform_facts_(ir_context),
       data_synonym_and_id_equation_facts_(ir_context),
-      dead_block_facts_(),
-      livesafe_function_facts_(),
+      dead_block_facts_(ir_context),
+      livesafe_function_facts_(ir_context),
       irrelevant_value_facts_(ir_context) {}
 
-void FactManager::AddFacts(const MessageConsumer& message_consumer,
-                           const protobufs::FactSequence& initial_facts) {
-  for (auto& fact : initial_facts.fact()) {
-    if (!AddFact(fact)) {
+void FactManager::AddInitialFacts(const MessageConsumer& message_consumer,
+                                  const protobufs::FactSequence& facts) {
+  for (auto& fact : facts.fact()) {
+    if (!MaybeAddFact(fact)) {
       auto message = "Invalid fact " + ToString(fact) + " ignored.";
       message_consumer(SPV_MSG_WARNING, nullptr, {}, message.c_str());
     }
   }
 }
 
-bool FactManager::AddFact(const fuzz::protobufs::Fact& fact) {
+bool FactManager::MaybeAddFact(const fuzz::protobufs::Fact& fact) {
   switch (fact.fact_case()) {
-    case protobufs::Fact::kConstantUniformFact:
-      return constant_uniform_facts_.AddFact(fact.constant_uniform_fact());
-    case protobufs::Fact::kDataSynonymFact:
-      data_synonym_and_id_equation_facts_.AddFact(
-          fact.data_synonym_fact(), dead_block_facts_, irrelevant_value_facts_);
-      return true;
     case protobufs::Fact::kBlockIsDeadFact:
-      dead_block_facts_.AddFact(fact.block_is_dead_fact());
-      return true;
+      return dead_block_facts_.MaybeAddFact(fact.block_is_dead_fact());
+    case protobufs::Fact::kConstantUniformFact:
+      return constant_uniform_facts_.MaybeAddFact(fact.constant_uniform_fact());
+    case protobufs::Fact::kDataSynonymFact:
+      return data_synonym_and_id_equation_facts_.MaybeAddFact(
+          fact.data_synonym_fact(), dead_block_facts_, irrelevant_value_facts_);
     case protobufs::Fact::kFunctionIsLivesafeFact:
-      livesafe_function_facts_.AddFact(fact.function_is_livesafe_fact());
-      return true;
-    default:
-      assert(false && "Unknown fact type.");
+      return livesafe_function_facts_.MaybeAddFact(
+          fact.function_is_livesafe_fact());
+    case protobufs::Fact::kIdEquationFact:
+      return data_synonym_and_id_equation_facts_.MaybeAddFact(
+          fact.id_equation_fact(), dead_block_facts_, irrelevant_value_facts_);
+    case protobufs::Fact::kIdIsIrrelevant:
+      return irrelevant_value_facts_.MaybeAddFact(
+          fact.id_is_irrelevant(), data_synonym_and_id_equation_facts_);
+    case protobufs::Fact::kPointeeValueIsIrrelevantFact:
+      return irrelevant_value_facts_.MaybeAddFact(
+          fact.pointee_value_is_irrelevant_fact(),
+          data_synonym_and_id_equation_facts_);
+    case protobufs::Fact::FACT_NOT_SET:
+      assert(false && "The fact must be set");
       return false;
   }
+
+  assert(false && "Unreachable");
+  return false;
 }
 
 void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1,
@@ -129,8 +140,10 @@
   protobufs::FactDataSynonym fact;
   *fact.mutable_data1() = data1;
   *fact.mutable_data2() = data2;
-  data_synonym_and_id_equation_facts_.AddFact(fact, dead_block_facts_,
-                                              irrelevant_value_facts_);
+  auto success = data_synonym_and_id_equation_facts_.MaybeAddFact(
+      fact, dead_block_facts_, irrelevant_value_facts_);
+  (void)success;  // Keep compilers happy in release mode.
+  assert(success && "Unable to create DataSynonym fact");
 }
 
 std::vector<uint32_t> FactManager::GetConstantsAvailableFromUniformsForType(
@@ -190,7 +203,9 @@
 void FactManager::AddFactBlockIsDead(uint32_t block_id) {
   protobufs::FactBlockIsDead fact;
   fact.set_block_id(block_id);
-  dead_block_facts_.AddFact(fact);
+  auto success = dead_block_facts_.MaybeAddFact(fact);
+  (void)success;  // Keep compilers happy in release mode.
+  assert(success && "|block_id| is invalid");
 }
 
 bool FactManager::FunctionIsLivesafe(uint32_t function_id) const {
@@ -200,7 +215,9 @@
 void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) {
   protobufs::FactFunctionIsLivesafe fact;
   fact.set_function_id(function_id);
-  livesafe_function_facts_.AddFact(fact);
+  auto success = livesafe_function_facts_.MaybeAddFact(fact);
+  (void)success;  // Keep compilers happy in release mode.
+  assert(success && "|function_id| is invalid");
 }
 
 bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const {
@@ -218,13 +235,19 @@
 void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) {
   protobufs::FactPointeeValueIsIrrelevant fact;
   fact.set_pointer_id(pointer_id);
-  irrelevant_value_facts_.AddFact(fact, data_synonym_and_id_equation_facts_);
+  auto success = irrelevant_value_facts_.MaybeAddFact(
+      fact, data_synonym_and_id_equation_facts_);
+  (void)success;  // Keep compilers happy in release mode.
+  assert(success && "|pointer_id| is invalid");
 }
 
 void FactManager::AddFactIdIsIrrelevant(uint32_t result_id) {
   protobufs::FactIdIsIrrelevant fact;
   fact.set_result_id(result_id);
-  irrelevant_value_facts_.AddFact(fact, data_synonym_and_id_equation_facts_);
+  auto success = irrelevant_value_facts_.MaybeAddFact(
+      fact, data_synonym_and_id_equation_facts_);
+  (void)success;  // Keep compilers happy in release mode.
+  assert(success && "|result_id| is invalid");
 }
 
 void FactManager::AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
@@ -235,8 +258,10 @@
   for (auto an_rhs_id : rhs_id) {
     fact.add_rhs_id(an_rhs_id);
   }
-  data_synonym_and_id_equation_facts_.AddFact(fact, dead_block_facts_,
-                                              irrelevant_value_facts_);
+  auto success = data_synonym_and_id_equation_facts_.MaybeAddFact(
+      fact, dead_block_facts_, irrelevant_value_facts_);
+  (void)success;  // Keep compilers happy in release mode.
+  assert(success && "Can't create IdIsIrrelevant fact");
 }
 
 void FactManager::ComputeClosureOfFacts(
diff --git a/source/fuzz/fact_manager/fact_manager.h b/source/fuzz/fact_manager/fact_manager.h
index d3758b1..78769c1 100644
--- a/source/fuzz/fact_manager/fact_manager.h
+++ b/source/fuzz/fact_manager/fact_manager.h
@@ -45,24 +45,27 @@
   explicit FactManager(opt::IRContext* ir_context);
 
   // Adds all the facts from |facts|, checking them for validity with respect to
-  // |context|.  Warnings about invalid facts are communicated via
+  // |ir_context_|. Warnings about invalid facts are communicated via
   // |message_consumer|; such facts are otherwise ignored.
-  void AddFacts(const MessageConsumer& message_consumer,
-                const protobufs::FactSequence& facts);
+  void AddInitialFacts(const MessageConsumer& message_consumer,
+                       const protobufs::FactSequence& facts);
 
-  // Checks the fact for validity with respect to |context|.  Returns false,
-  // with no side effects, if the fact is invalid.  Otherwise adds |fact| to the
+  // Checks the fact for validity with respect to |ir_context_|. Returns false,
+  // with no side effects, if the fact is invalid. Otherwise adds |fact| to the
   // fact manager.
-  bool AddFact(const protobufs::Fact& fact);
+  bool MaybeAddFact(const protobufs::Fact& fact);
 
-  // Record the fact that |data1| and |data2| are synonymous.
+  // Record the fact that |data1| and |data2| are synonymous. Neither |data1|
+  // nor |data2| may contain an irrelevant id.
   void AddFactDataSynonym(const protobufs::DataDescriptor& data1,
                           const protobufs::DataDescriptor& data2);
 
-  // Records the fact that |block_id| is dead.
+  // Records the fact that |block_id| is dead. |block_id| must be a result id
+  // of some OpLabel instruction in the |ir_context_|.
   void AddFactBlockIsDead(uint32_t block_id);
 
-  // Records the fact that |function_id| is livesafe.
+  // Records the fact that |function_id| is livesafe. |function_id| must be a
+  // result id of some non-entry-point function in the module.
   void AddFactFunctionIsLivesafe(uint32_t function_id);
 
   // Records the fact that the value of the pointee associated with |pointer_id|
@@ -72,13 +75,14 @@
 
   // Records a fact that the |result_id| is irrelevant (i.e. it doesn't affect
   // the semantics of the module).
-  // |result_id| must exist in the module and actually be a pointer.
+  // |result_id| must exist in the module and it may not be a pointer.
   void AddFactIdIsIrrelevant(uint32_t result_id);
 
   // Records the fact that |lhs_id| is defined by the equation:
   //
   //   |lhs_id| = |opcode| |rhs_id[0]| ... |rhs_id[N-1]|
   //
+  // Neither |lhs_id| nor any of |rhs_id| may be irrelevant.
   void AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
                          const std::vector<uint32_t>& rhs_id);
 
@@ -117,7 +121,7 @@
       uint32_t type_id) const;
 
   // Provides details of all uniform elements that are known to be equal to the
-  // constant associated with |constant_id| in |ir_context|.
+  // constant associated with |constant_id| in |ir_context_|.
   std::vector<protobufs::UniformBufferElementDescriptor>
   GetUniformDescriptorsForConstant(uint32_t constant_id) const;
 
diff --git a/source/fuzz/fact_manager/irrelevant_value_facts.cpp b/source/fuzz/fact_manager/irrelevant_value_facts.cpp
index ac5ad8b..07836ad 100644
--- a/source/fuzz/fact_manager/irrelevant_value_facts.cpp
+++ b/source/fuzz/fact_manager/irrelevant_value_facts.cpp
@@ -27,36 +27,52 @@
 IrrelevantValueFacts::IrrelevantValueFacts(opt::IRContext* ir_context)
     : ir_context_(ir_context) {}
 
-void IrrelevantValueFacts::AddFact(
+bool IrrelevantValueFacts::MaybeAddFact(
     const protobufs::FactPointeeValueIsIrrelevant& fact,
     const DataSynonymAndIdEquationFacts& data_synonym_and_id_equation_facts) {
-  (void)data_synonym_and_id_equation_facts;  // Keep release compilers happy.
-  assert(data_synonym_and_id_equation_facts.GetSynonymsForId(fact.pointer_id())
-             .empty() &&
-         "The id cannot participate in DataSynonym facts.");
-  auto pointer_def = ir_context_->get_def_use_mgr()->GetDef(fact.pointer_id());
-  assert(pointer_def && "The id must exist in the module.");
-  auto type = ir_context_->get_type_mgr()->GetType(pointer_def->type_id());
-  (void)type;  // Keep release compilers happy.
-  assert(type && type->AsPointer() && "The id must be a pointer.");
+  const auto* inst = ir_context_->get_def_use_mgr()->GetDef(fact.pointer_id());
+  if (!inst || !inst->type_id()) {
+    // The id must exist in the module and have type id.
+    return false;
+  }
+
+  if (!ir_context_->get_type_mgr()->GetType(inst->type_id())->AsPointer()) {
+    // The id must be a pointer.
+    return false;
+  }
+
+  if (!data_synonym_and_id_equation_facts.GetSynonymsForId(fact.pointer_id())
+           .empty()) {
+    // Irrelevant id cannot participate in DataSynonym facts.
+    return false;
+  }
 
   pointers_to_irrelevant_pointees_ids_.insert(fact.pointer_id());
+  return true;
 }
 
-void IrrelevantValueFacts::AddFact(
+bool IrrelevantValueFacts::MaybeAddFact(
     const protobufs::FactIdIsIrrelevant& fact,
     const DataSynonymAndIdEquationFacts& data_synonym_and_id_equation_facts) {
-  (void)data_synonym_and_id_equation_facts;  // Keep release compilers happy.
-  assert(data_synonym_and_id_equation_facts.GetSynonymsForId(fact.result_id())
-             .empty() &&
-         "The id cannot participate in DataSynonym facts.");
-  auto pointer_def = ir_context_->get_def_use_mgr()->GetDef(fact.result_id());
-  assert(pointer_def && "The id must exist in the module.");
-  auto type = ir_context_->get_type_mgr()->GetType(pointer_def->type_id());
-  (void)type;  // Keep release compilers happy.
-  assert(type && !type->AsPointer() && "The id must not be a pointer.");
+  const auto* inst = ir_context_->get_def_use_mgr()->GetDef(fact.result_id());
+  if (!inst || !inst->type_id()) {
+    // The id must exist in the module and have type id.
+    return false;
+  }
+
+  if (ir_context_->get_type_mgr()->GetType(inst->type_id())->AsPointer()) {
+    // The id may not be a pointer.
+    return false;
+  }
+
+  if (!data_synonym_and_id_equation_facts.GetSynonymsForId(fact.result_id())
+           .empty()) {
+    // Irrelevant id cannot participate in DataSynonym facts.
+    return false;
+  }
 
   irrelevant_ids_.insert(fact.result_id());
+  return true;
 }
 
 bool IrrelevantValueFacts::PointeeValueIsIrrelevant(uint32_t pointer_id) const {
diff --git a/source/fuzz/fact_manager/irrelevant_value_facts.h b/source/fuzz/fact_manager/irrelevant_value_facts.h
index ad70e6b..9faddc0 100644
--- a/source/fuzz/fact_manager/irrelevant_value_facts.h
+++ b/source/fuzz/fact_manager/irrelevant_value_facts.h
@@ -35,17 +35,21 @@
  public:
   explicit IrrelevantValueFacts(opt::IRContext* ir_context);
 
-  // See method in FactManager which delegates to this method.
-  // |data_synonym_and_id_equation_facts| and |context| are passed for
-  // consistency checks.
-  void AddFact(
+  // See method in FactManager which delegates to this method. Returns true if
+  // |fact.pointer_id()| is a result id of pointer type in the |ir_context_| and
+  // |fact.pointer_id()| does not participate in DataSynonym facts. Returns
+  // false otherwise. |data_synonym_and_id_equation_facts| and |context| are
+  // passed for consistency checks.
+  bool MaybeAddFact(
       const protobufs::FactPointeeValueIsIrrelevant& fact,
       const DataSynonymAndIdEquationFacts& data_synonym_and_id_equation_facts);
 
-  // See method in FactManager which delegates to this method.
-  // |data_synonym_and_id_equation_facts| and |context| are passed for
-  // consistency checks.
-  void AddFact(
+  // See method in FactManager which delegates to this method. Returns true if
+  // |fact.result_id()| is a result id of non-pointer type in the |ir_context_|
+  // and |fact.result_id()| does not participate in DataSynonym facts. Returns
+  // false otherwise. |data_synonym_and_id_equation_facts| and |context| are
+  // passed for consistency checks.
+  bool MaybeAddFact(
       const protobufs::FactIdIsIrrelevant& fact,
       const DataSynonymAndIdEquationFacts& data_synonym_and_id_equation_facts);
 
diff --git a/source/fuzz/fact_manager/livesafe_function_facts.cpp b/source/fuzz/fact_manager/livesafe_function_facts.cpp
index 6f36afb..553ac57 100644
--- a/source/fuzz/fact_manager/livesafe_function_facts.cpp
+++ b/source/fuzz/fact_manager/livesafe_function_facts.cpp
@@ -14,13 +14,27 @@
 
 #include "source/fuzz/fact_manager/livesafe_function_facts.h"
 
+#include "source/fuzz/fuzzer_util.h"
+
 namespace spvtools {
 namespace fuzz {
 namespace fact_manager {
 
-void LivesafeFunctionFacts::AddFact(
+LivesafeFunctionFacts::LivesafeFunctionFacts(opt::IRContext* ir_context)
+    : ir_context_(ir_context) {}
+
+bool LivesafeFunctionFacts::MaybeAddFact(
     const protobufs::FactFunctionIsLivesafe& fact) {
+  if (!fuzzerutil::FindFunction(ir_context_, fact.function_id())) {
+    return false;
+  }
+
+  if (fuzzerutil::FunctionIsEntryPoint(ir_context_, fact.function_id())) {
+    return false;
+  }
+
   livesafe_function_ids_.insert(fact.function_id());
+  return true;
 }
 
 bool LivesafeFunctionFacts::FunctionIsLivesafe(uint32_t function_id) const {
diff --git a/source/fuzz/fact_manager/livesafe_function_facts.h b/source/fuzz/fact_manager/livesafe_function_facts.h
index 8c48506..2156d64 100644
--- a/source/fuzz/fact_manager/livesafe_function_facts.h
+++ b/source/fuzz/fact_manager/livesafe_function_facts.h
@@ -18,6 +18,7 @@
 #include <unordered_set>
 
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/opt/ir_context.h"
 
 namespace spvtools {
 namespace fuzz {
@@ -27,14 +28,19 @@
 // facts about livesafe functions.
 class LivesafeFunctionFacts {
  public:
-  // See method in FactManager which delegates to this method.
-  void AddFact(const protobufs::FactFunctionIsLivesafe& fact);
+  explicit LivesafeFunctionFacts(opt::IRContext* ir_context);
+
+  // See method in FactManager which delegates to this method. Returns true if
+  // |fact.function_id()| is a result id of some non-entry-point function in
+  // |ir_context_|. Returns false otherwise.
+  bool MaybeAddFact(const protobufs::FactFunctionIsLivesafe& fact);
 
   // See method in FactManager which delegates to this method.
   bool FunctionIsLivesafe(uint32_t function_id) const;
 
  private:
   std::unordered_set<uint32_t> livesafe_function_ids_;
+  opt::IRContext* ir_context_;
 };
 
 }  // namespace fact_manager
diff --git a/source/fuzz/force_render_red.cpp b/source/fuzz/force_render_red.cpp
index f9ef36d..fd0587a 100644
--- a/source/fuzz/force_render_red.cpp
+++ b/source/fuzz/force_render_red.cpp
@@ -185,7 +185,7 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(ir_context.get()), validator_options);
   for (auto& fact : initial_facts.fact()) {
-    transformation_context.GetFactManager()->AddFact(fact);
+    transformation_context.GetFactManager()->MaybeAddFact(fact);
   }
 
   auto entry_point_function =
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index 9680dda..37260ca 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -216,8 +216,8 @@
 
   transformation_context_ = MakeUnique<TransformationContext>(
       MakeUnique<FactManager>(ir_context_.get()), validator_options_);
-  transformation_context_->GetFactManager()->AddFacts(consumer_,
-                                                      initial_facts_);
+  transformation_context_->GetFactManager()->AddInitialFacts(consumer_,
+                                                             initial_facts_);
 
   RepeatedPassInstances pass_instances{};
 
diff --git a/source/fuzz/replayer.cpp b/source/fuzz/replayer.cpp
index 4636a20..81d36b8 100644
--- a/source/fuzz/replayer.cpp
+++ b/source/fuzz/replayer.cpp
@@ -108,7 +108,8 @@
       MakeUnique<TransformationContext>(
           MakeUnique<FactManager>(ir_context.get()), validator_options_,
           MakeUnique<CounterOverflowIdSource>(first_overflow_id));
-  transformation_context->GetFactManager()->AddFacts(consumer_, initial_facts_);
+  transformation_context->GetFactManager()->AddInitialFacts(consumer_,
+                                                            initial_facts_);
 
   // We track the largest id bound observed, to ensure that it only increases
   // as transformations are applied.
diff --git a/source/fuzz/transformation_add_dead_block.cpp b/source/fuzz/transformation_add_dead_block.cpp
index 4ff22cb..3d77a80 100644
--- a/source/fuzz/transformation_add_dead_block.cpp
+++ b/source/fuzz/transformation_add_dead_block.cpp
@@ -157,10 +157,6 @@
   enclosing_function->InsertBasicBlockAfter(std::move(new_block),
                                             existing_block);
 
-  // Record the fact that the new block is dead.
-  transformation_context->GetFactManager()->AddFactBlockIsDead(
-      message_.fresh_id());
-
   // Fix up OpPhi instructions in the successor block, so that the values they
   // yield when control has transferred from the new block are the same as if
   // control had transferred from |message_.existing_block|.  This is guaranteed
@@ -181,6 +177,10 @@
   // Do not rely on any existing analysis results since the control flow graph
   // of the module has changed.
   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+
+  // Record the fact that the new block is dead.
+  transformation_context->GetFactManager()->AddFactBlockIsDead(
+      message_.fresh_id());
 }
 
 protobufs::Transformation TransformationAddDeadBlock::ToMessage() const {
diff --git a/source/fuzz/transformation_add_function.cpp b/source/fuzz/transformation_add_function.cpp
index 52c7f5f..aea2d08 100644
--- a/source/fuzz/transformation_add_function.cpp
+++ b/source/fuzz/transformation_add_function.cpp
@@ -173,11 +173,15 @@
     success = TryToMakeFunctionLivesafe(ir_context, *transformation_context);
     assert(success && "It should be possible to make the function livesafe.");
     (void)(success);  // Keep release builds happy.
+  }
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 
+  assert(message_.instruction(0).opcode() == SpvOpFunction &&
+         "The first instruction of an 'add function' transformation must be "
+         "OpFunction.");
+
+  if (message_.is_livesafe()) {
     // Inform the fact manager that the function is livesafe.
-    assert(message_.instruction(0).opcode() == SpvOpFunction &&
-           "The first instruction of an 'add function' transformation must be "
-           "OpFunction.");
     transformation_context->GetFactManager()->AddFactFunctionIsLivesafe(
         message_.instruction(0).result_id());
   } else {
@@ -189,7 +193,6 @@
       }
     }
   }
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 
   // Record the fact that all pointer parameters and variables declared in the
   // function should be regarded as having irrelevant values.  This allows other
diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp
index 764e888..a2cef7a 100644
--- a/source/fuzz/transformation_outline_function.cpp
+++ b/source/fuzz/transformation_outline_function.cpp
@@ -358,14 +358,6 @@
       region_input_ids, region_output_ids, input_id_to_fresh_id_map, ir_context,
       transformation_context);
 
-  // If the original function was livesafe, the new function should also be
-  // livesafe.
-  if (transformation_context->GetFactManager()->FunctionIsLivesafe(
-          original_region_entry_block->GetParent()->result_id())) {
-    transformation_context->GetFactManager()->AddFactFunctionIsLivesafe(
-        message_.new_function_id());
-  }
-
   // Adapt the region to be outlined so that its input ids are replaced with the
   // ids of the outlined function's input parameters, and so that output ids
   // are similarly remapped.
@@ -375,10 +367,10 @@
 
   // Fill out the body of the outlined function according to the region that is
   // being outlined.
-  PopulateOutlinedFunction(
-      *original_region_entry_block, *original_region_exit_block, region_blocks,
-      region_output_ids, output_id_to_fresh_id_map, ir_context,
-      outlined_function.get(), transformation_context);
+  PopulateOutlinedFunction(*original_region_entry_block,
+                           *original_region_exit_block, region_blocks,
+                           region_output_ids, output_id_to_fresh_id_map,
+                           ir_context, outlined_function.get());
 
   // Collapse the region that has been outlined into a function down to a single
   // block that calls said function.
@@ -389,11 +381,28 @@
       std::move(cloned_exit_block_terminator), original_region_entry_block);
 
   // Add the outlined function to the module.
+  const auto* outlined_function_ptr = outlined_function.get();
   ir_context->module()->AddFunction(std::move(outlined_function));
 
   // Major surgery has been conducted on the module, so invalidate all analyses.
   ir_context->InvalidateAnalysesExceptFor(
       opt::IRContext::Analysis::kAnalysisNone);
+
+  // If the original function was livesafe, the new function should also be
+  // livesafe.
+  if (transformation_context->GetFactManager()->FunctionIsLivesafe(
+          original_region_entry_block->GetParent()->result_id())) {
+    transformation_context->GetFactManager()->AddFactFunctionIsLivesafe(
+        message_.new_function_id());
+  }
+
+  // Record the fact that all blocks in the outlined region are dead if the
+  // first block is dead.
+  if (transformation_context->GetFactManager()->BlockIsDead(
+          original_region_entry_block->id())) {
+    transformation_context->GetFactManager()->AddFactBlockIsDead(
+        outlined_function_ptr->entry()->id());
+  }
 }
 
 protobufs::Transformation TransformationOutlineFunction::ToMessage() const {
@@ -750,8 +759,7 @@
     const std::set<opt::BasicBlock*>& region_blocks,
     const std::vector<uint32_t>& region_output_ids,
     const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map,
-    opt::IRContext* ir_context, opt::Function* outlined_function,
-    TransformationContext* transformation_context) const {
+    opt::IRContext* ir_context, opt::Function* outlined_function) const {
   // When we create the exit block for the outlined region, we use this pointer
   // to track of it so that we can manipulate it later.
   opt::BasicBlock* outlined_region_exit_block = nullptr;
@@ -765,14 +773,6 @@
           opt::Instruction::OperandList()));
   outlined_region_entry_block->SetParent(outlined_function);
 
-  // If the original region's entry block was dead, the outlined region's entry
-  // block is also dead.
-  if (transformation_context->GetFactManager()->BlockIsDead(
-          original_region_entry_block.id())) {
-    transformation_context->GetFactManager()->AddFactBlockIsDead(
-        outlined_region_entry_block->id());
-  }
-
   if (&original_region_entry_block == &original_region_exit_block) {
     outlined_region_exit_block = outlined_region_entry_block.get();
   }
@@ -879,7 +879,7 @@
 }
 
 void TransformationOutlineFunction::ShrinkOriginalRegion(
-    opt::IRContext* ir_context, std::set<opt::BasicBlock*>& region_blocks,
+    opt::IRContext* ir_context, const std::set<opt::BasicBlock*>& region_blocks,
     const std::vector<uint32_t>& region_input_ids,
     const std::vector<uint32_t>& region_output_ids,
     const std::map<uint32_t, uint32_t>& output_id_to_type_id,
diff --git a/source/fuzz/transformation_outline_function.h b/source/fuzz/transformation_outline_function.h
index fda1ba0..6bc7d7e 100644
--- a/source/fuzz/transformation_outline_function.h
+++ b/source/fuzz/transformation_outline_function.h
@@ -189,8 +189,7 @@
       const std::set<opt::BasicBlock*>& region_blocks,
       const std::vector<uint32_t>& region_output_ids,
       const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map,
-      opt::IRContext* ir_context, opt::Function* outlined_function,
-      TransformationContext* transformation_context) const;
+      opt::IRContext* ir_context, opt::Function* outlined_function) const;
 
   // Shrinks the outlined region, given by |region_blocks|, down to the single
   // block |original_region_entry_block|.  This block is itself shrunk to just
@@ -209,7 +208,8 @@
   // function is called, this information cannot be gotten from the def-use
   // manager.
   void ShrinkOriginalRegion(
-      opt::IRContext* ir_context, std::set<opt::BasicBlock*>& region_blocks,
+      opt::IRContext* ir_context,
+      const std::set<opt::BasicBlock*>& region_blocks,
       const std::vector<uint32_t>& region_input_ids,
       const std::vector<uint32_t>& region_output_ids,
       const std::map<uint32_t, uint32_t>& output_id_to_type_id,
diff --git a/source/fuzz/transformation_split_block.cpp b/source/fuzz/transformation_split_block.cpp
index d8a3dc8..b383c40 100644
--- a/source/fuzz/transformation_split_block.cpp
+++ b/source/fuzz/transformation_split_block.cpp
@@ -124,6 +124,10 @@
     phi_inst->SetInOperand(1, {block_to_split->id()});
   });
 
+  // Invalidate all analyses
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+
   // If the block being split was dead, the new block arising from the split is
   // also dead.
   if (transformation_context->GetFactManager()->BlockIsDead(
@@ -131,10 +135,6 @@
     transformation_context->GetFactManager()->AddFactBlockIsDead(
         message_.fresh_id());
   }
-
-  // Invalidate all analyses
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationSplitBlock::ToMessage() const {
diff --git a/test/fuzz/data_synonym_transformation_test.cpp b/test/fuzz/data_synonym_transformation_test.cpp
index 723eb4d..2345ff6 100644
--- a/test/fuzz/data_synonym_transformation_test.cpp
+++ b/test/fuzz/data_synonym_transformation_test.cpp
@@ -125,19 +125,20 @@
   spvtools::ValidatorOptions validator_options;
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  transformation_context.GetFactManager()->AddFact(
+
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(12, {}, 100, {0}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(13, {}, 100, {1}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(22, {}, 100, {2}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(28, {}, 101, {0}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(23, {}, 101, {1}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(32, {}, 101, {2}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(23, {}, 101, {3}));
 
   // Replace %12 with %100[0] in '%25 = OpAccessChain %24 %20 %12'
@@ -411,11 +412,12 @@
   spvtools::ValidatorOptions validator_options;
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  transformation_context.GetFactManager()->AddFact(
+
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(23, {}, 100, {0}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(25, {}, 100, {1}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(50, {}, 100, {2}));
 
   // Replace %23 with %100[0] in '%26 = OpFAdd %7 %23 %25'
@@ -579,19 +581,20 @@
   spvtools::ValidatorOptions validator_options;
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  transformation_context.GetFactManager()->AddFact(
+
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(16, {}, 100, {0}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(45, {}, 100, {1}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(27, {}, 101, {0}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(36, {}, 101, {1}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(27, {}, 101, {2}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(22, {}, 102, {0}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(15, {}, 102, {1}));
 
   // Replace %45 with %100[1] in '%46 = OpCompositeConstruct %32 %35 %45'
@@ -867,45 +870,46 @@
   spvtools::ValidatorOptions validator_options;
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  transformation_context.GetFactManager()->AddFact(
+
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(20, {0}, 100, {0}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(20, {1}, 100, {1}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(20, {2}, 100, {2}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(54, {}, 100, {3}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(15, {0}, 101, {0}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(15, {1}, 101, {1}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(19, {0}, 101, {2}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(19, {1}, 101, {3}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(27, {}, 102, {0}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(15, {0}, 102, {1}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(15, {1}, 102, {2}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(33, {}, 103, {0}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(47, {0}, 103, {1}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(47, {1}, 103, {2}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(47, {2}, 103, {3}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(42, {}, 104, {0}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(45, {}, 104, {1}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(38, {0}, 105, {0}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(38, {1}, 105, {1}));
-  transformation_context.GetFactManager()->AddFact(
+  transformation_context.GetFactManager()->MaybeAddFact(
       MakeSynonymFact(46, {}, 105, {2}));
 
   // Replace %20 with %100[0:2] in '%80 = OpCopyObject %16 %20'
diff --git a/test/fuzz/fact_manager_test.cpp b/test/fuzz/fact_manager_test.cpp
index 995b64c..8322c7c 100644
--- a/test/fuzz/fact_manager_test.cpp
+++ b/test/fuzz/fact_manager_test.cpp
@@ -45,7 +45,7 @@
       descriptor;
   protobufs::Fact fact;
   *fact.mutable_constant_uniform_fact() = constant_uniform_fact;
-  return fact_manager->AddFact(fact);
+  return fact_manager->MaybeAddFact(fact);
 }
 
 TEST(FactManagerTest, ConstantsAvailableViaUniforms) {
diff --git a/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp b/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp
index 666180b..0e10a42 100644
--- a/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp
+++ b/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp
@@ -32,21 +32,21 @@
 // Adds synonym facts to the fact manager.
 void SetUpIdSynonyms(FactManager* fact_manager) {
   // Synonyms {9, 11, 15, 16, 21, 22}
-  fact_manager->AddFact(MakeSynonymFact(11, 9));
-  fact_manager->AddFact(MakeSynonymFact(15, 9));
-  fact_manager->AddFact(MakeSynonymFact(16, 9));
-  fact_manager->AddFact(MakeSynonymFact(21, 9));
-  fact_manager->AddFact(MakeSynonymFact(22, 9));
+  fact_manager->MaybeAddFact(MakeSynonymFact(11, 9));
+  fact_manager->MaybeAddFact(MakeSynonymFact(15, 9));
+  fact_manager->MaybeAddFact(MakeSynonymFact(16, 9));
+  fact_manager->MaybeAddFact(MakeSynonymFact(21, 9));
+  fact_manager->MaybeAddFact(MakeSynonymFact(22, 9));
 
   // Synonyms {10, 23}
-  fact_manager->AddFact(MakeSynonymFact(10, 23));
+  fact_manager->MaybeAddFact(MakeSynonymFact(10, 23));
 
   // Synonyms {14, 27}
-  fact_manager->AddFact(MakeSynonymFact(14, 27));
+  fact_manager->MaybeAddFact(MakeSynonymFact(14, 27));
 
   // Synonyms {24, 26, 30}
-  fact_manager->AddFact(MakeSynonymFact(26, 24));
-  fact_manager->AddFact(MakeSynonymFact(30, 24));
+  fact_manager->MaybeAddFact(MakeSynonymFact(26, 24));
+  fact_manager->MaybeAddFact(MakeSynonymFact(30, 24));
 }
 
 // Returns true if the given lists have the same elements, regardless of their
diff --git a/test/fuzz/transformation_add_opphi_synonym_test.cpp b/test/fuzz/transformation_add_opphi_synonym_test.cpp
index 8715125..a6f067f 100644
--- a/test/fuzz/transformation_add_opphi_synonym_test.cpp
+++ b/test/fuzz/transformation_add_opphi_synonym_test.cpp
@@ -31,12 +31,12 @@
 
 // Adds synonym facts to the fact manager.
 void SetUpIdSynonyms(FactManager* fact_manager) {
-  fact_manager->AddFact(MakeSynonymFact(11, 9));
-  fact_manager->AddFact(MakeSynonymFact(13, 9));
-  fact_manager->AddFact(MakeSynonymFact(14, 9));
-  fact_manager->AddFact(MakeSynonymFact(19, 9));
-  fact_manager->AddFact(MakeSynonymFact(20, 9));
-  fact_manager->AddFact(MakeSynonymFact(10, 21));
+  fact_manager->MaybeAddFact(MakeSynonymFact(11, 9));
+  fact_manager->MaybeAddFact(MakeSynonymFact(13, 9));
+  fact_manager->MaybeAddFact(MakeSynonymFact(14, 9));
+  fact_manager->MaybeAddFact(MakeSynonymFact(19, 9));
+  fact_manager->MaybeAddFact(MakeSynonymFact(20, 9));
+  fact_manager->MaybeAddFact(MakeSynonymFact(10, 21));
 }
 
 TEST(TransformationAddOpPhiSynonymTest, Inapplicable) {
@@ -89,7 +89,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   SetUpIdSynonyms(transformation_context.GetFactManager());
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(23, 24));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(23, 24));
 
   // %13 is not a block label.
   ASSERT_FALSE(TransformationAddOpPhiSynonym(13, {{}}, 100)
@@ -211,8 +212,8 @@
   SetUpIdSynonyms(transformation_context.GetFactManager());
 
   // Add some further synonym facts.
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(28, 9));
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(30, 9));
+  transformation_context.GetFactManager()->MaybeAddFact(MakeSynonymFact(28, 9));
+  transformation_context.GetFactManager()->MaybeAddFact(MakeSynonymFact(30, 9));
 
   auto transformation1 = TransformationAddOpPhiSynonym(17, {{{15, 13}}}, 100);
   ASSERT_TRUE(
@@ -356,8 +357,9 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // Declare synonyms
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(3, 15));
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(12, 16));
+  transformation_context.GetFactManager()->MaybeAddFact(MakeSynonymFact(3, 15));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(12, 16));
 
   // Remove the VariablePointers capability.
   context.get()->get_feature_mgr()->RemoveCapability(
diff --git a/test/fuzz/transformation_replace_constant_with_uniform_test.cpp b/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
index 507e98f..1e084e7 100644
--- a/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
+++ b/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
@@ -31,7 +31,7 @@
       descriptor;
   protobufs::Fact fact;
   *fact.mutable_constant_uniform_fact() = constant_uniform_fact;
-  return transformation_context->GetFactManager()->AddFact(fact);
+  return transformation_context->GetFactManager()->MaybeAddFact(fact);
 }
 
 TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) {
diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
index c200b2f..85cdd53 100644
--- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp
+++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
@@ -200,17 +200,17 @@
 
 // Equips the fact manager with synonym facts for the above shader.
 void SetUpIdSynonyms(FactManager* fact_manager) {
-  fact_manager->AddFact(MakeSynonymFact(15, 200));
-  fact_manager->AddFact(MakeSynonymFact(15, 201));
-  fact_manager->AddFact(MakeSynonymFact(15, 202));
-  fact_manager->AddFact(MakeSynonymFact(55, 203));
-  fact_manager->AddFact(MakeSynonymFact(54, 204));
-  fact_manager->AddFact(MakeSynonymFact(74, 205));
-  fact_manager->AddFact(MakeSynonymFact(78, 206));
-  fact_manager->AddFact(MakeSynonymFact(84, 207));
-  fact_manager->AddFact(MakeSynonymFact(33, 208));
-  fact_manager->AddFact(MakeSynonymFact(12, 209));
-  fact_manager->AddFact(MakeSynonymFact(19, 210));
+  fact_manager->MaybeAddFact(MakeSynonymFact(15, 200));
+  fact_manager->MaybeAddFact(MakeSynonymFact(15, 201));
+  fact_manager->MaybeAddFact(MakeSynonymFact(15, 202));
+  fact_manager->MaybeAddFact(MakeSynonymFact(55, 203));
+  fact_manager->MaybeAddFact(MakeSynonymFact(54, 204));
+  fact_manager->MaybeAddFact(MakeSynonymFact(74, 205));
+  fact_manager->MaybeAddFact(MakeSynonymFact(78, 206));
+  fact_manager->MaybeAddFact(MakeSynonymFact(84, 207));
+  fact_manager->MaybeAddFact(MakeSynonymFact(33, 208));
+  fact_manager->MaybeAddFact(MakeSynonymFact(12, 209));
+  fact_manager->MaybeAddFact(MakeSynonymFact(19, 210));
 }
 
 TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) {
@@ -520,8 +520,10 @@
   spvtools::ValidatorOptions validator_options;
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(10, 100));
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(8, 101));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(10, 100));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(8, 101));
 
   // Replace %10 with %100 in:
   // %11 = OpLoad %6 %10
@@ -650,7 +652,8 @@
   spvtools::ValidatorOptions validator_options;
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(14, 100));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(14, 100));
 
   // Replace %14 with %100 in:
   // %16 = OpFunctionCall %2 %10 %14
@@ -815,19 +818,32 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Add synonym facts corresponding to the OpCopyObject operations that have
   // been applied to all constants in the module.
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(16, 100));
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(21, 101));
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(17, 102));
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(57, 103));
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(18, 104));
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(40, 105));
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(32, 106));
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(43, 107));
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(55, 108));
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(8, 109));
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(47, 110));
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(28, 111));
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(45, 112));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(16, 100));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(21, 101));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(17, 102));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(57, 103));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(18, 104));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(40, 105));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(32, 106));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(43, 107));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(55, 108));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(8, 109));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(47, 110));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(28, 111));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(45, 112));
 
   // Replacements of the form %16 -> %100
 
@@ -1300,9 +1316,11 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // Add synonym fact relating %50 and %12.
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(50, 12));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(50, 12));
   // Add synonym fact relating %51 and %14.
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(51, 14));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(51, 14));
 
   // Not legal because the index being replaced is a struct index.
   ASSERT_FALSE(
@@ -1409,7 +1427,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // Add synonym fact relating %100 and %9.
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(100, 9));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(100, 9));
 
   // Not legal the Sample argument of OpImageTexelPointer needs to be a zero
   // constant.
@@ -1469,7 +1488,8 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Add synonym fact relating %10 and %13 (equivalent integer constant with
   // different signedness).
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(10, 13));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(10, 13));
 
   // Legal because OpSNegate always considers the integer as signed
   auto replacement1 = TransformationReplaceIdWithSynonym(
@@ -1611,7 +1631,8 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Add synonym fact relating %10 and %13 (equivalent integer vectors with
   // different signedness).
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(14, 15));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(14, 15));
 
   // Legal because OpIAdd does not consider the signedness of the operands
   auto replacement1 = TransformationReplaceIdWithSynonym(
@@ -1630,7 +1651,8 @@
 
   // Add synonym fact relating %12 and %13 (equivalent integer constants with
   // different signedness).
-  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(12, 13));
+  transformation_context.GetFactManager()->MaybeAddFact(
+      MakeSynonymFact(12, 13));
 
   // Legal because the indices of OpAccessChain are always treated as signed
   auto replacement2 = TransformationReplaceIdWithSynonym(