spirv-fuzz: Consider all ids from dead blocks irrelevant (#3795)

This PR modifies the FactManager methods IdIsIrrelevant and GetIrrelevantIds so
that an id is always considered irrelevant if it comes from a dead block.

Fixes #3733.
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 d5d9f3f..32c9c75 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
@@ -53,11 +53,15 @@
 
 void DataSynonymAndIdEquationFacts::AddFact(
     const protobufs::FactDataSynonym& fact,
+    const DeadBlockFacts& dead_block_facts,
     const IrrelevantValueFacts& irrelevant_value_facts,
     opt::IRContext* context) {
+  (void)dead_block_facts;        // Keep release compilers happy.
   (void)irrelevant_value_facts;  // Keep release compilers happy.
-  assert(!irrelevant_value_facts.IdIsIrrelevant(fact.data1().object()) &&
-         !irrelevant_value_facts.IdIsIrrelevant(fact.data2().object()) &&
+  assert(!irrelevant_value_facts.IdIsIrrelevant(fact.data1().object(),
+                                                dead_block_facts, context) &&
+         !irrelevant_value_facts.IdIsIrrelevant(fact.data2().object(),
+                                                dead_block_facts, context) &&
          "Irrelevant ids cannot be synonymous with other ids.");
 
   // Add the fact, including all facts relating sub-components of the data
@@ -67,10 +71,13 @@
 
 void DataSynonymAndIdEquationFacts::AddFact(
     const protobufs::FactIdEquation& fact,
+    const DeadBlockFacts& dead_block_facts,
     const IrrelevantValueFacts& irrelevant_value_facts,
     opt::IRContext* context) {
+  (void)dead_block_facts;        // Keep release compilers happy.
   (void)irrelevant_value_facts;  // Keep release compilers happy.
-  assert(!irrelevant_value_facts.IdIsIrrelevant(fact.lhs_id()) &&
+  assert(!irrelevant_value_facts.IdIsIrrelevant(fact.lhs_id(), dead_block_facts,
+                                                context) &&
          "Irrelevant ids are not allowed.");
 
   protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {});
@@ -82,7 +89,8 @@
   // equation.
   std::vector<const protobufs::DataDescriptor*> rhs_dds;
   for (auto rhs_id : fact.rhs_id()) {
-    assert(!irrelevant_value_facts.IdIsIrrelevant(rhs_id) &&
+    assert(!irrelevant_value_facts.IdIsIrrelevant(rhs_id, dead_block_facts,
+                                                  context) &&
            "Irrelevant ids are not allowed.");
 
     // Register a data descriptor based on this id in the equivalence relation
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 df35ad6..7b3a21f 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
@@ -27,6 +27,8 @@
 namespace fuzz {
 namespace fact_manager {
 
+// Forward reference to the DeadBlockFacts class.
+class DeadBlockFacts;
 // Forward reference to the IrrelevantValueFacts class.
 class IrrelevantValueFacts;
 
@@ -35,14 +37,18 @@
 class DataSynonymAndIdEquationFacts {
  public:
   // See method in FactManager which delegates to this method.
-  // |irrelevant_value_facts| is passed for consistency checks.
+  // |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,
                opt::IRContext* context);
 
   // See method in FactManager which delegates to this method.
-  // |irrelevant_value_facts| is passed for consistency checks.
+  // |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,
                opt::IRContext* context);
 
diff --git a/source/fuzz/fact_manager/dead_block_facts.cpp b/source/fuzz/fact_manager/dead_block_facts.cpp
index 86d51af..5f4f8bc 100644
--- a/source/fuzz/fact_manager/dead_block_facts.cpp
+++ b/source/fuzz/fact_manager/dead_block_facts.cpp
@@ -26,6 +26,10 @@
   return dead_block_ids_.count(block_id) != 0;
 }
 
+const std::unordered_set<uint32_t>& DeadBlockFacts::GetDeadBlocks() const {
+  return dead_block_ids_;
+}
+
 }  // namespace fact_manager
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/source/fuzz/fact_manager/dead_block_facts.h b/source/fuzz/fact_manager/dead_block_facts.h
index ca3f71a..b2da90b 100644
--- a/source/fuzz/fact_manager/dead_block_facts.h
+++ b/source/fuzz/fact_manager/dead_block_facts.h
@@ -33,6 +33,9 @@
   // See method in FactManager which delegates to this method.
   bool BlockIsDead(uint32_t block_id) const;
 
+  // Returns a set of all the block ids that have been declared dead.
+  const std::unordered_set<uint32_t>& GetDeadBlocks() const;
+
  private:
   std::unordered_set<uint32_t> dead_block_ids_;
 };
diff --git a/source/fuzz/fact_manager/fact_manager.cpp b/source/fuzz/fact_manager/fact_manager.cpp
index 79c2037..3a65ba8 100644
--- a/source/fuzz/fact_manager/fact_manager.cpp
+++ b/source/fuzz/fact_manager/fact_manager.cpp
@@ -106,7 +106,8 @@
                                              context);
     case protobufs::Fact::kDataSynonymFact:
       data_synonym_and_id_equation_facts_.AddFact(
-          fact.data_synonym_fact(), irrelevant_value_facts_, context);
+          fact.data_synonym_fact(), dead_block_facts_, irrelevant_value_facts_,
+          context);
       return true;
     case protobufs::Fact::kBlockIsDeadFact:
       dead_block_facts_.AddFact(fact.block_is_dead_fact());
@@ -126,8 +127,8 @@
   protobufs::FactDataSynonym fact;
   *fact.mutable_data1() = data1;
   *fact.mutable_data2() = data2;
-  data_synonym_and_id_equation_facts_.AddFact(fact, irrelevant_value_facts_,
-                                              context);
+  data_synonym_and_id_equation_facts_.AddFact(fact, dead_block_facts_,
+                                              irrelevant_value_facts_, context);
 }
 
 std::vector<uint32_t> FactManager::GetConstantsAvailableFromUniformsForType(
@@ -207,12 +208,15 @@
   return irrelevant_value_facts_.PointeeValueIsIrrelevant(pointer_id);
 }
 
-bool FactManager::IdIsIrrelevant(uint32_t result_id) const {
-  return irrelevant_value_facts_.IdIsIrrelevant(result_id);
+bool FactManager::IdIsIrrelevant(uint32_t result_id,
+                                 opt::IRContext* context) const {
+  return irrelevant_value_facts_.IdIsIrrelevant(result_id, dead_block_facts_,
+                                                context);
 }
 
-const std::unordered_set<uint32_t>& FactManager::GetIrrelevantIds() const {
-  return irrelevant_value_facts_.GetIrrelevantIds();
+std::unordered_set<uint32_t> FactManager::GetIrrelevantIds(
+    opt::IRContext* context) const {
+  return irrelevant_value_facts_.GetIrrelevantIds(dead_block_facts_, context);
 }
 
 void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id,
@@ -240,8 +244,8 @@
   for (auto an_rhs_id : rhs_id) {
     fact.add_rhs_id(an_rhs_id);
   }
-  data_synonym_and_id_equation_facts_.AddFact(fact, irrelevant_value_facts_,
-                                              context);
+  data_synonym_and_id_equation_facts_.AddFact(fact, dead_block_facts_,
+                                              irrelevant_value_facts_, context);
 }
 
 void FactManager::ComputeClosureOfFacts(
diff --git a/source/fuzz/fact_manager/fact_manager.h b/source/fuzz/fact_manager/fact_manager.h
index b1f4f35..b899d9f 100644
--- a/source/fuzz/fact_manager/fact_manager.h
+++ b/source/fuzz/fact_manager/fact_manager.h
@@ -194,12 +194,13 @@
   // |pointer_id| is irrelevant.
   bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
 
-  // Returns true iff there exists a fact that the |result_id| is irrelevant.
-  bool IdIsIrrelevant(uint32_t result_id) const;
+  // Returns true if there exists a fact that the |result_id| is irrelevant or
+  // if |result_id| is declared in a block that has been declared dead.
+  bool IdIsIrrelevant(uint32_t result_id, opt::IRContext* context) const;
 
-  // Returns an unordered set of all the ids which have been declared
-  // irrelevant.
-  const std::unordered_set<uint32_t>& GetIrrelevantIds() const;
+  // Returns a set of all the ids which have been declared irrelevant, or which
+  // have been declared inside a dead block.
+  std::unordered_set<uint32_t> GetIrrelevantIds(opt::IRContext* context) const;
 
   // End of irrelevant value facts
   //==============================
diff --git a/source/fuzz/fact_manager/irrelevant_value_facts.cpp b/source/fuzz/fact_manager/irrelevant_value_facts.cpp
index dda17b3..cc4d20d 100644
--- a/source/fuzz/fact_manager/irrelevant_value_facts.cpp
+++ b/source/fuzz/fact_manager/irrelevant_value_facts.cpp
@@ -16,6 +16,8 @@
 
 #include "source/fuzz/data_descriptor.h"
 #include "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h"
+#include "source/fuzz/fact_manager/dead_block_facts.h"
+#include "source/fuzz/fuzzer_util.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -60,13 +62,54 @@
   return pointers_to_irrelevant_pointees_ids_.count(pointer_id) != 0;
 }
 
-bool IrrelevantValueFacts::IdIsIrrelevant(uint32_t pointer_id) const {
-  return irrelevant_ids_.count(pointer_id) != 0;
+bool IrrelevantValueFacts::IdIsIrrelevant(
+    uint32_t result_id, const DeadBlockFacts& dead_block_facts,
+    opt::IRContext* context) const {
+  // The id is irrelevant if it has been declared irrelevant.
+  if (irrelevant_ids_.count(result_id)) {
+    return true;
+  }
+
+  // The id must have a non-pointer type to be irrelevant.
+  auto def = context->get_def_use_mgr()->GetDef(result_id);
+  if (!def) {
+    return false;
+  }
+  auto type = context->get_type_mgr()->GetType(def->type_id());
+  if (!type || type->AsPointer()) {
+    return false;
+  }
+
+  // The id is irrelevant if it is in a dead block.
+  return context->get_instr_block(result_id) &&
+         dead_block_facts.BlockIsDead(
+             context->get_instr_block(result_id)->id());
 }
 
-const std::unordered_set<uint32_t>& IrrelevantValueFacts::GetIrrelevantIds()
-    const {
-  return irrelevant_ids_;
+std::unordered_set<uint32_t> IrrelevantValueFacts::GetIrrelevantIds(
+    const DeadBlockFacts& dead_block_facts, opt::IRContext* context) const {
+  // Get all the ids that have been declared irrelevant.
+  auto irrelevant_ids = irrelevant_ids_;
+
+  // Get all the non-pointer ids declared in dead blocks that have a type.
+  for (uint32_t block_id : dead_block_facts.GetDeadBlocks()) {
+    auto block = fuzzerutil::MaybeFindBlock(context, block_id);
+    // It is possible and allowed for the block not to exist, e.g. it could have
+    // been merged with another block.
+    if (!block) {
+      continue;
+    }
+    block->ForEachInst([context, &irrelevant_ids](opt::Instruction* inst) {
+      // The instruction must have a result id and a type, and it must not be a
+      // pointer.
+      if (inst->HasResultId() && inst->type_id() &&
+          !context->get_type_mgr()->GetType(inst->type_id())->AsPointer()) {
+        irrelevant_ids.emplace(inst->result_id());
+      }
+    });
+  }
+
+  return irrelevant_ids;
 }
 
 }  // namespace fact_manager
diff --git a/source/fuzz/fact_manager/irrelevant_value_facts.h b/source/fuzz/fact_manager/irrelevant_value_facts.h
index 76ff55d..71393c5 100644
--- a/source/fuzz/fact_manager/irrelevant_value_facts.h
+++ b/source/fuzz/fact_manager/irrelevant_value_facts.h
@@ -26,6 +26,8 @@
 
 // Forward reference to the DataSynonymAndIdEquationFacts class.
 class DataSynonymAndIdEquationFacts;
+// Forward reference to the DeadBlockFacts class.
+class DeadBlockFacts;
 
 // The purpose of this class is to group the fields and data used to represent
 // facts about various irrelevant values in the module.
@@ -51,10 +53,17 @@
   bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
 
   // See method in FactManager which delegates to this method.
-  bool IdIsIrrelevant(uint32_t pointer_id) const;
+  // |dead_block_facts| and |context| are passed to check whether |result_id| is
+  // declared inside a dead block, in which case it is irrelevant.
+  bool IdIsIrrelevant(uint32_t result_id,
+                      const DeadBlockFacts& dead_block_facts,
+                      opt::IRContext* context) const;
 
   // See method in FactManager which delegates to this method.
-  const std::unordered_set<uint32_t>& GetIrrelevantIds() const;
+  // |dead_block_facts| and |context| are passed to also add all the ids
+  // declared in dead blocks to the set of irrelevant ids.
+  std::unordered_set<uint32_t> GetIrrelevantIds(
+      const DeadBlockFacts& dead_block_facts, opt::IRContext* context) const;
 
  private:
   std::unordered_set<uint32_t> pointers_to_irrelevant_pointees_ids_;
diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
index 62fcfea..d54e17a 100644
--- a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
+++ b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
@@ -77,12 +77,14 @@
         std::vector<opt::Instruction*> available_instructions =
             FindAvailableInstructions(
                 function, block, inst_it,
-                [this](opt::IRContext*, opt::Instruction* instruction) -> bool {
+                [this](opt::IRContext* ir_context,
+                       opt::Instruction* instruction) -> bool {
                   return instruction->result_id() && instruction->type_id() &&
                          instruction->opcode() != SpvOpUndef &&
                          !GetTransformationContext()
                               ->GetFactManager()
-                              ->IdIsIrrelevant(instruction->result_id());
+                              ->IdIsIrrelevant(instruction->result_id(),
+                                               ir_context);
                 });
 
         // Try the opcodes for which we know how to make ids at random until
diff --git a/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp b/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp
index 88cc830..d1cd477 100644
--- a/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp
@@ -157,7 +157,7 @@
 
     // Exclude irrelevant ids.
     if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
-            pair.first)) {
+            pair.first, GetIRContext())) {
       continue;
     }
 
@@ -195,7 +195,7 @@
 
       // The synonym must not be irrelevant.
       if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
-              synonym->object())) {
+              synonym->object(), GetIRContext())) {
         continue;
       }
 
diff --git a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
index 453448b..945bc1f 100644
--- a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
+++ b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
@@ -75,7 +75,8 @@
 
                   if (!GetTransformationContext()
                            ->GetFactManager()
-                           ->IdIsIrrelevant(instruction->result_id()) &&
+                           ->IdIsIrrelevant(instruction->result_id(),
+                                            GetIRContext()) &&
                       !fuzzerutil::CanMakeSynonymOf(ir_context,
                                                     *GetTransformationContext(),
                                                     instruction)) {
diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
index 9cbf590..2691b2a 100644
--- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
@@ -148,7 +148,7 @@
           }
 
           assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
-                     synonym_to_try->object()) &&
+                     synonym_to_try->object(), GetIRContext()) &&
                  "Irrelevant ids can't participate in DataSynonym facts");
           ApplyTransformation(TransformationCompositeExtract(
               MakeInstructionDescriptor(GetIRContext(),
diff --git a/source/fuzz/fuzzer_pass_construct_composites.cpp b/source/fuzz/fuzzer_pass_construct_composites.cpp
index 6443e89..9dde70d 100644
--- a/source/fuzz/fuzzer_pass_construct_composites.cpp
+++ b/source/fuzz/fuzzer_pass_construct_composites.cpp
@@ -77,7 +77,7 @@
               // to produce a synonym out of the id.
               return GetTransformationContext()
                          ->GetFactManager()
-                         ->IdIsIrrelevant(inst->result_id()) ||
+                         ->IdIsIrrelevant(inst->result_id(), GetIRContext()) ||
                      fuzzerutil::CanMakeSynonymOf(
                          ir_context, *GetTransformationContext(), inst);
             });
diff --git a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp
index 0e40b49..5446cf8 100644
--- a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp
+++ b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp
@@ -48,7 +48,7 @@
     // constant with opposite signedness, and this can only be done if they are
     // not irrelevant.
     if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
-            constant_id)) {
+            constant_id, GetIRContext())) {
       continue;
     }
 
@@ -60,7 +60,7 @@
     }
 
     assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
-               toggled_id) &&
+               toggled_id, GetIRContext()) &&
            "FindOrCreateToggledConstant can't produce an irrelevant id");
 
     // Record synonymous constants
diff --git a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
index 20575e1..d939efd 100644
--- a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
+++ b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
@@ -73,7 +73,7 @@
   for (auto constant : GetIRContext()->GetConstants()) {
     uint32_t constant_id = constant->result_id();
     if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
-            constant_id)) {
+            constant_id, GetIRContext())) {
       continue;
     }
 
@@ -84,7 +84,7 @@
     }
 
     assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
-               toggled_id) &&
+               toggled_id, GetIRContext()) &&
            "FindOrCreateToggledConstant can't produce an irrelevant id");
 
     // Record synonymous constants
diff --git a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
index 8d9acaa..07fe645 100644
--- a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
+++ b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
@@ -96,7 +96,8 @@
                   // able to produce a synonym out of the id.
                   if (!GetTransformationContext()
                            ->GetFactManager()
-                           ->IdIsIrrelevant(instruction->result_id()) &&
+                           ->IdIsIrrelevant(instruction->result_id(),
+                                            GetIRContext()) &&
                       !fuzzerutil::CanMakeSynonymOf(ir_context,
                                                     *GetTransformationContext(),
                                                     instruction)) {
diff --git a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp
index cc92e28..c6bdd16 100644
--- a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp
+++ b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp
@@ -46,8 +46,8 @@
 
   // Find all the irrelevant ids that still exist in the module and all the
   // types for which irrelevant ids exist.
-  for (auto id :
-       GetTransformationContext()->GetFactManager()->GetIrrelevantIds()) {
+  for (auto id : GetTransformationContext()->GetFactManager()->GetIrrelevantIds(
+           GetIRContext())) {
     // Check that the id still exists in the module.
     auto declaration = GetIRContext()->get_def_use_mgr()->GetDef(id);
     if (!declaration) {
diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp
index 2502da7..b550a74 100644
--- a/source/fuzz/fuzzer_util.cpp
+++ b/source/fuzz/fuzzer_util.cpp
@@ -33,7 +33,7 @@
     if (inst.opcode() == SpvOpConstant && inst.type_id() == type_id &&
         inst.GetInOperand(0).words == words &&
         transformation_context.GetFactManager()->IdIsIrrelevant(
-            inst.result_id()) == is_irrelevant) {
+            inst.result_id(), ir_context) == is_irrelevant) {
       return inst.result_id();
     }
   }
@@ -261,8 +261,8 @@
     // We can only make a synonym of an instruction that generates an id.
     return false;
   }
-  if (transformation_context.GetFactManager()->IdIsIrrelevant(
-          inst->result_id())) {
+  if (transformation_context.GetFactManager()->IdIsIrrelevant(inst->result_id(),
+                                                              ir_context)) {
     // An irrelevant id can't be a synonym of anything.
     return false;
   }
@@ -1149,7 +1149,7 @@
     if (inst.opcode() == SpvOpConstantComposite &&
         inst.type_id() == composite_type_id &&
         transformation_context.GetFactManager()->IdIsIrrelevant(
-            inst.result_id()) == is_irrelevant &&
+            inst.result_id(), ir_context) == is_irrelevant &&
         inst.NumInOperands() == component_ids.size()) {
       bool is_match = true;
 
@@ -1229,7 +1229,7 @@
       if (inst.opcode() == (value ? SpvOpConstantTrue : SpvOpConstantFalse) &&
           inst.type_id() == type_id &&
           transformation_context.GetFactManager()->IdIsIrrelevant(
-              inst.result_id()) == is_irrelevant) {
+              inst.result_id(), ir_context) == is_irrelevant) {
         return inst.result_id();
       }
     }
diff --git a/source/fuzz/transformation_composite_construct.cpp b/source/fuzz/transformation_composite_construct.cpp
index 15af53e..c92349d 100644
--- a/source/fuzz/transformation_composite_construct.cpp
+++ b/source/fuzz/transformation_composite_construct.cpp
@@ -96,7 +96,8 @@
 
     // We should be able to create a synonym of |component| if it's not
     // irrelevant.
-    if (!transformation_context.GetFactManager()->IdIsIrrelevant(component) &&
+    if (!transformation_context.GetFactManager()->IdIsIrrelevant(component,
+                                                                 ir_context) &&
         !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
                                       inst)) {
       return false;
@@ -159,7 +160,7 @@
            subvector_index < component_type->AsVector()->element_count();
            subvector_index++) {
         if (!transformation_context->GetFactManager()->IdIsIrrelevant(
-                component)) {
+                component, ir_context)) {
           transformation_context->GetFactManager()->AddFactDataSynonym(
               MakeDataDescriptor(component, {subvector_index}),
               MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
@@ -170,7 +171,7 @@
       // The other cases are simple: the component is made directly synonymous
       // with the element of the composite being constructed.
       if (!transformation_context->GetFactManager()->IdIsIrrelevant(
-              component)) {
+              component, ir_context)) {
         transformation_context->GetFactManager()->AddFactDataSynonym(
             MakeDataDescriptor(component, {}),
             MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
diff --git a/source/fuzz/transformation_composite_extract.cpp b/source/fuzz/transformation_composite_extract.cpp
index 9f4d554..809454d 100644
--- a/source/fuzz/transformation_composite_extract.cpp
+++ b/source/fuzz/transformation_composite_extract.cpp
@@ -56,7 +56,7 @@
     return false;
   }
   if (!transformation_context.GetFactManager()->IdIsIrrelevant(
-          message_.composite_id()) &&
+          message_.composite_id(), ir_context) &&
       !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
                                     composite_instruction)) {
     // |composite_id| will participate in DataSynonym facts. Thus, it can't be
@@ -115,7 +115,7 @@
   // Add the fact that the id storing the extracted element is synonymous with
   // the index into the structure.
   if (!transformation_context->GetFactManager()->IdIsIrrelevant(
-          message_.composite_id())) {
+          message_.composite_id(), ir_context)) {
     std::vector<uint32_t> indices;
     for (auto an_index : message_.index()) {
       indices.push_back(an_index);
diff --git a/source/fuzz/transformation_composite_insert.cpp b/source/fuzz/transformation_composite_insert.cpp
index 75eebd4..e57ffe8 100644
--- a/source/fuzz/transformation_composite_insert.cpp
+++ b/source/fuzz/transformation_composite_insert.cpp
@@ -143,7 +143,7 @@
 
   // If |composite_id| is irrelevant then don't add any synonyms.
   if (transformation_context->GetFactManager()->IdIsIrrelevant(
-          message_.composite_id())) {
+          message_.composite_id(), ir_context)) {
     return;
   }
   uint32_t current_node_type_id = composite_type_id;
@@ -184,7 +184,7 @@
   // The element which has been changed is synonymous to the found object
   // itself. Add this fact only if |object_id| is not irrelevant.
   if (!transformation_context->GetFactManager()->IdIsIrrelevant(
-          message_.object_id())) {
+          message_.object_id(), ir_context)) {
     transformation_context->GetFactManager()->AddFactDataSynonym(
         MakeDataDescriptor(message_.object_id(), {}),
         MakeDataDescriptor(message_.fresh_id(), std::vector<uint32_t>(index)),
diff --git a/source/fuzz/transformation_equation_instruction.cpp b/source/fuzz/transformation_equation_instruction.cpp
index e27cd29..ae6d236 100644
--- a/source/fuzz/transformation_equation_instruction.cpp
+++ b/source/fuzz/transformation_equation_instruction.cpp
@@ -60,7 +60,8 @@
     if (inst->opcode() == SpvOpUndef) {
       return false;
     }
-    if (transformation_context.GetFactManager()->IdIsIrrelevant(id)) {
+    if (transformation_context.GetFactManager()->IdIsIrrelevant(id,
+                                                                ir_context)) {
       return false;
     }
     if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
diff --git a/source/fuzz/transformation_push_id_through_variable.cpp b/source/fuzz/transformation_push_id_through_variable.cpp
index 647bfa0..167e83b 100644
--- a/source/fuzz/transformation_push_id_through_variable.cpp
+++ b/source/fuzz/transformation_push_id_through_variable.cpp
@@ -76,7 +76,7 @@
 
   // We should be able to create a synonym of |value_id| if it's not irrelevant.
   if (!transformation_context.GetFactManager()->IdIsIrrelevant(
-          message_.value_id()) &&
+          message_.value_id(), ir_context) &&
       !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
                                     value_instruction)) {
     return false;
@@ -154,7 +154,7 @@
   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 
   if (!transformation_context->GetFactManager()->IdIsIrrelevant(
-          message_.value_id())) {
+          message_.value_id(), ir_context)) {
     // Adds the fact that |message_.value_synonym_id|
     // and |message_.value_id| are synonymous.
     transformation_context->GetFactManager()->AddFactDataSynonym(
diff --git a/source/fuzz/transformation_record_synonymous_constants.cpp b/source/fuzz/transformation_record_synonymous_constants.cpp
index a93e1d4..87a9c51 100644
--- a/source/fuzz/transformation_record_synonymous_constants.cpp
+++ b/source/fuzz/transformation_record_synonymous_constants.cpp
@@ -41,9 +41,9 @@
   }
 
   if (transformation_context.GetFactManager()->IdIsIrrelevant(
-          message_.constant1_id()) ||
+          message_.constant1_id(), ir_context) ||
       transformation_context.GetFactManager()->IdIsIrrelevant(
-          message_.constant2_id())) {
+          message_.constant2_id(), ir_context)) {
     return false;
   }
 
diff --git a/source/fuzz/transformation_replace_irrelevant_id.cpp b/source/fuzz/transformation_replace_irrelevant_id.cpp
index 5ac182a..84cd849 100644
--- a/source/fuzz/transformation_replace_irrelevant_id.cpp
+++ b/source/fuzz/transformation_replace_irrelevant_id.cpp
@@ -37,8 +37,8 @@
   auto id_of_interest = message_.id_use_descriptor().id_of_interest();
 
   // The id must be irrelevant.
-  if (!transformation_context.GetFactManager()->IdIsIrrelevant(
-          id_of_interest)) {
+  if (!transformation_context.GetFactManager()->IdIsIrrelevant(id_of_interest,
+                                                               ir_context)) {
     return false;
   }
 
diff --git a/source/fuzz/transformation_replace_parameter_with_global.cpp b/source/fuzz/transformation_replace_parameter_with_global.cpp
index 489d339..7b0abea 100644
--- a/source/fuzz/transformation_replace_parameter_with_global.cpp
+++ b/source/fuzz/transformation_replace_parameter_with_global.cpp
@@ -184,7 +184,7 @@
   // Mark the pointee of the global variable storing the parameter's value as
   // irrelevant if replaced parameter is irrelevant.
   if (transformation_context->GetFactManager()->IdIsIrrelevant(
-          message_.parameter_id())) {
+          message_.parameter_id(), ir_context)) {
     transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
         message_.global_variable_fresh_id(), ir_context);
   }
diff --git a/source/fuzz/transformation_vector_shuffle.cpp b/source/fuzz/transformation_vector_shuffle.cpp
index b3bd593..4564107 100644
--- a/source/fuzz/transformation_vector_shuffle.cpp
+++ b/source/fuzz/transformation_vector_shuffle.cpp
@@ -59,7 +59,7 @@
   }
   // We should be able to create a synonym of |vector1| if it's not irrelevant.
   if (!transformation_context.GetFactManager()->IdIsIrrelevant(
-          message_.vector1()) &&
+          message_.vector1(), ir_context) &&
       !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
                                     vector1_instruction)) {
     return false;
@@ -72,7 +72,7 @@
   }
   // We should be able to create a synonym of |vector2| if it's not irrelevant.
   if (!transformation_context.GetFactManager()->IdIsIrrelevant(
-          message_.vector2()) &&
+          message_.vector2(), ir_context) &&
       !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
                                     vector2_instruction)) {
     return false;
@@ -153,6 +153,13 @@
   ir_context->InvalidateAnalysesExceptFor(
       opt::IRContext::Analysis::kAnalysisNone);
 
+  // If the new instruction is irrelevant (because it is in a dead block), it
+  // cannot participate in any DataSynonym fact.
+  if (transformation_context->GetFactManager()->IdIsIrrelevant(
+          message_.fresh_id(), ir_context)) {
+    return;
+  }
+
   // Add synonym facts relating the defined elements of the shuffle result to
   // the vector components that they come from.
   for (uint32_t component_index = 0;
@@ -178,7 +185,7 @@
         GetVectorType(ir_context, message_.vector1())->element_count()) {
       // Irrelevant id cannot participate in DataSynonym facts.
       if (transformation_context->GetFactManager()->IdIsIrrelevant(
-              message_.vector1())) {
+              message_.vector1(), ir_context)) {
         continue;
       }
 
@@ -187,7 +194,7 @@
     } else {
       // Irrelevant id cannot participate in DataSynonym facts.
       if (transformation_context->GetFactManager()->IdIsIrrelevant(
-              message_.vector2())) {
+              message_.vector2(), ir_context)) {
         continue;
       }
 
diff --git a/test/fuzz/fact_manager_test.cpp b/test/fuzz/fact_manager_test.cpp
index 64104df..ad7e52a 100644
--- a/test/fuzz/fact_manager_test.cpp
+++ b/test/fuzz/fact_manager_test.cpp
@@ -1440,13 +1440,13 @@
 
   FactManager fact_manager;
 
-  ASSERT_FALSE(fact_manager.IdIsIrrelevant(12));
-  ASSERT_FALSE(fact_manager.IdIsIrrelevant(13));
+  ASSERT_FALSE(fact_manager.IdIsIrrelevant(12, context.get()));
+  ASSERT_FALSE(fact_manager.IdIsIrrelevant(13, context.get()));
 
   fact_manager.AddFactIdIsIrrelevant(12, context.get());
 
-  ASSERT_TRUE(fact_manager.IdIsIrrelevant(12));
-  ASSERT_FALSE(fact_manager.IdIsIrrelevant(13));
+  ASSERT_TRUE(fact_manager.IdIsIrrelevant(12, context.get()));
+  ASSERT_FALSE(fact_manager.IdIsIrrelevant(13, context.get()));
 }
 
 TEST(FactManagerTest, GetIrrelevantIds) {
@@ -1476,20 +1476,136 @@
 
   FactManager fact_manager;
 
-  ASSERT_TRUE(fact_manager.GetIrrelevantIds() ==
-              std::unordered_set<uint32_t>({}));
+  ASSERT_EQ(fact_manager.GetIrrelevantIds(context.get()),
+            std::unordered_set<uint32_t>({}));
 
   fact_manager.AddFactIdIsIrrelevant(12, context.get());
 
-  ASSERT_TRUE(fact_manager.GetIrrelevantIds() ==
-              std::unordered_set<uint32_t>({12}));
+  ASSERT_EQ(fact_manager.GetIrrelevantIds(context.get()),
+            std::unordered_set<uint32_t>({12}));
 
   fact_manager.AddFactIdIsIrrelevant(13, context.get());
 
-  ASSERT_TRUE(fact_manager.GetIrrelevantIds() ==
-              std::unordered_set<uint32_t>({12, 13}));
+  ASSERT_EQ(fact_manager.GetIrrelevantIds(context.get()),
+            std::unordered_set<uint32_t>({12, 13}));
 }
 
+TEST(FactManagerTest, BlockIsDead) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 310
+          %3 = OpTypeVoid
+          %4 = OpTypeFunction %3
+          %5 = OpTypeBool
+          %6 = OpConstantTrue %5
+          %7 = OpTypeInt 32 1
+          %8 = OpTypePointer Function %7
+          %2 = OpFunction %3 None %4
+          %9 = OpLabel
+               OpSelectionMerge %10 None
+               OpBranchConditional %6 %11 %12
+         %11 = OpLabel
+               OpBranch %10
+         %12 = OpLabel
+               OpBranch %10
+         %10 = 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;
+
+  ASSERT_FALSE(fact_manager.BlockIsDead(9));
+  ASSERT_FALSE(fact_manager.BlockIsDead(11));
+  ASSERT_FALSE(fact_manager.BlockIsDead(12));
+
+  fact_manager.AddFactBlockIsDead(12);
+
+  ASSERT_FALSE(fact_manager.BlockIsDead(9));
+  ASSERT_FALSE(fact_manager.BlockIsDead(11));
+  ASSERT_TRUE(fact_manager.BlockIsDead(12));
+}
+
+TEST(FactManagerTest, IdsFromDeadBlocksAreIrrelevant) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 310
+          %3 = OpTypeVoid
+          %4 = OpTypeFunction %3
+          %5 = OpTypeBool
+          %6 = OpConstantTrue %5
+          %7 = OpTypeInt 32 1
+          %8 = OpTypePointer Function %7
+          %9 = OpConstant %7 1
+          %2 = OpFunction %3 None %4
+         %10 = OpLabel
+         %11 = OpVariable %8 Function
+               OpSelectionMerge %12 None
+               OpBranchConditional %6 %13 %14
+         %13 = OpLabel
+               OpBranch %12
+         %14 = OpLabel
+         %15 = OpCopyObject %8 %11
+         %16 = OpCopyObject %7 %9
+         %17 = OpFunctionCall %3 %18
+               OpBranch %12
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %18 = OpFunction %3 None %4
+         %19 = OpLabel
+         %20 = OpVariable %8 Function
+         %21 = OpCopyObject %7 %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;
+
+  ASSERT_FALSE(fact_manager.BlockIsDead(14));
+  ASSERT_FALSE(fact_manager.BlockIsDead(19));
+
+  // Initially no id is irrelevant.
+  ASSERT_FALSE(fact_manager.IdIsIrrelevant(16, context.get()));
+  ASSERT_FALSE(fact_manager.IdIsIrrelevant(17, context.get()));
+  ASSERT_EQ(fact_manager.GetIrrelevantIds(context.get()),
+            std::unordered_set<uint32_t>({}));
+
+  fact_manager.AddFactBlockIsDead(14);
+
+  // %16 and %17 should now be considered irrelevant.
+  ASSERT_TRUE(fact_manager.IdIsIrrelevant(16, context.get()));
+  ASSERT_TRUE(fact_manager.IdIsIrrelevant(17, context.get()));
+  ASSERT_EQ(fact_manager.GetIrrelevantIds(context.get()),
+            std::unordered_set<uint32_t>({16, 17}));
+
+  // Similarly for %21.
+  ASSERT_FALSE(fact_manager.IdIsIrrelevant(21, context.get()));
+
+  fact_manager.AddFactBlockIsDead(19);
+
+  ASSERT_TRUE(fact_manager.IdIsIrrelevant(21, context.get()));
+  ASSERT_EQ(fact_manager.GetIrrelevantIds(context.get()),
+            std::unordered_set<uint32_t>({16, 17, 21}));
+}
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/test/fuzz/transformation_add_constant_boolean_test.cpp b/test/fuzz/transformation_add_constant_boolean_test.cpp
index 1a40329..958232c 100644
--- a/test/fuzz/transformation_add_constant_boolean_test.cpp
+++ b/test/fuzz/transformation_add_constant_boolean_test.cpp
@@ -107,10 +107,10 @@
   irrelevant_false.Apply(context.get(), &transformation_context);
   ASSERT_TRUE(IsValid(env, context.get()));
 
-  ASSERT_FALSE(fact_manager.IdIsIrrelevant(100));
-  ASSERT_FALSE(fact_manager.IdIsIrrelevant(101));
-  ASSERT_TRUE(fact_manager.IdIsIrrelevant(102));
-  ASSERT_TRUE(fact_manager.IdIsIrrelevant(103));
+  ASSERT_FALSE(fact_manager.IdIsIrrelevant(100, context.get()));
+  ASSERT_FALSE(fact_manager.IdIsIrrelevant(101, context.get()));
+  ASSERT_TRUE(fact_manager.IdIsIrrelevant(102, context.get()));
+  ASSERT_TRUE(fact_manager.IdIsIrrelevant(103, context.get()));
 
   std::string after_transformation = R"(
                OpCapability Shader
diff --git a/test/fuzz/transformation_add_constant_composite_test.cpp b/test/fuzz/transformation_add_constant_composite_test.cpp
index 75e23ad..fdd68ca 100644
--- a/test/fuzz/transformation_add_constant_composite_test.cpp
+++ b/test/fuzz/transformation_add_constant_composite_test.cpp
@@ -135,11 +135,11 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   for (uint32_t id = 100; id <= 106; ++id) {
-    ASSERT_FALSE(fact_manager.IdIsIrrelevant(id));
+    ASSERT_FALSE(fact_manager.IdIsIrrelevant(id, context.get()));
   }
 
   for (uint32_t id = 107; id <= 113; ++id) {
-    ASSERT_TRUE(fact_manager.IdIsIrrelevant(id));
+    ASSERT_TRUE(fact_manager.IdIsIrrelevant(id, context.get()));
   }
 
   std::string after_transformation = R"(
diff --git a/test/fuzz/transformation_add_constant_scalar_test.cpp b/test/fuzz/transformation_add_constant_scalar_test.cpp
index d75ac7a..3aacf41 100644
--- a/test/fuzz/transformation_add_constant_scalar_test.cpp
+++ b/test/fuzz/transformation_add_constant_scalar_test.cpp
@@ -272,11 +272,11 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   for (uint32_t result_id = 19; result_id <= 24; ++result_id) {
-    ASSERT_FALSE(fact_manager.IdIsIrrelevant(result_id));
+    ASSERT_FALSE(fact_manager.IdIsIrrelevant(result_id, context.get()));
   }
 
   for (uint32_t result_id = 25; result_id <= 30; ++result_id) {
-    ASSERT_TRUE(fact_manager.IdIsIrrelevant(result_id));
+    ASSERT_TRUE(fact_manager.IdIsIrrelevant(result_id, context.get()));
   }
 
   std::string variant_shader = R"(
diff --git a/test/fuzz/transformation_add_parameter_test.cpp b/test/fuzz/transformation_add_parameter_test.cpp
index 4826b95..ce73888 100644
--- a/test/fuzz/transformation_add_parameter_test.cpp
+++ b/test/fuzz/transformation_add_parameter_test.cpp
@@ -136,28 +136,28 @@
     ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context));
     correct.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_TRUE(fact_manager.IdIsIrrelevant(60));
+    ASSERT_TRUE(fact_manager.IdIsIrrelevant(60, context.get()));
   }
   {
     TransformationAddParameter correct(17, 62, 7, {{}}, 63);
     ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context));
     correct.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_TRUE(fact_manager.IdIsIrrelevant(62));
+    ASSERT_TRUE(fact_manager.IdIsIrrelevant(62, context.get()));
   }
   {
     TransformationAddParameter correct(29, 64, 31, {{}}, 65);
     ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context));
     correct.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_TRUE(fact_manager.IdIsIrrelevant(64));
+    ASSERT_TRUE(fact_manager.IdIsIrrelevant(64, context.get()));
   }
   {
     TransformationAddParameter correct(34, 66, 7, {{}}, 67);
     ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context));
     correct.Apply(context.get(), &transformation_context);
     ASSERT_TRUE(IsValid(env, context.get()));
-    ASSERT_TRUE(fact_manager.IdIsIrrelevant(66));
+    ASSERT_TRUE(fact_manager.IdIsIrrelevant(66, context.get()));
   }
 
   std::string expected_shader = R"(
diff --git a/test/fuzz/transformation_flatten_conditional_branch_test.cpp b/test/fuzz/transformation_flatten_conditional_branch_test.cpp
index 095acbc..91802ae 100644
--- a/test/fuzz/transformation_flatten_conditional_branch_test.cpp
+++ b/test/fuzz/transformation_flatten_conditional_branch_test.cpp
@@ -490,7 +490,8 @@
   transformation1.Apply(context.get(), &transformation_context);
 
   // Check that the placeholder id was marked as irrelevant.
-  ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(103));
+  ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(
+      103, context.get()));
 
   // Make a new transformation context with a source of overflow ids.
   TransformationContext new_transformation_context(