spirv-fuzz: Add IdIsIrrelevant fact (#3561)

Part of #3177.
This PR adds a fact to the fact manager.
diff --git a/source/fuzz/fact_manager.cpp b/source/fuzz/fact_manager.cpp
index 320049a..eb66dfe 100644
--- a/source/fuzz/fact_manager.cpp
+++ b/source/fuzz/fact_manager.cpp
@@ -1343,32 +1343,49 @@
 //==============================
 
 //==============================
-// Irrelevant pointee value facts
+// Irrelevant value facts
 
 // The purpose of this class is to group the fields and data used to represent
-// facts about pointers whose pointee values are irrelevant.
-class FactManager::IrrelevantPointeeValueFacts {
+// facts about various irrelevant values in the module.
+class FactManager::IrrelevantValueFacts {
  public:
   // See method in FactManager which delegates to this method.
   void AddFact(const protobufs::FactPointeeValueIsIrrelevant& fact);
 
   // See method in FactManager which delegates to this method.
+  void AddFact(const protobufs::FactIdIsIrrelevant& fact);
+
+  // See method in FactManager which delegates to this method.
   bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
 
+  // See method in FactManager which delegates to this method.
+  bool IdIsIrrelevant(uint32_t pointer_id) const;
+
  private:
-  std::set<uint32_t> pointers_to_irrelevant_pointees_ids_;
+  std::unordered_set<uint32_t> pointers_to_irrelevant_pointees_ids_;
+  std::unordered_set<uint32_t> irrelevant_ids_;
 };
 
-void FactManager::IrrelevantPointeeValueFacts::AddFact(
+void FactManager::IrrelevantValueFacts::AddFact(
     const protobufs::FactPointeeValueIsIrrelevant& fact) {
   pointers_to_irrelevant_pointees_ids_.insert(fact.pointer_id());
 }
 
-bool FactManager::IrrelevantPointeeValueFacts::PointeeValueIsIrrelevant(
+void FactManager::IrrelevantValueFacts::AddFact(
+    const protobufs::FactIdIsIrrelevant& fact) {
+  irrelevant_ids_.insert(fact.result_id());
+}
+
+bool FactManager::IrrelevantValueFacts::PointeeValueIsIrrelevant(
     uint32_t pointer_id) const {
   return pointers_to_irrelevant_pointees_ids_.count(pointer_id) != 0;
 }
 
+bool FactManager::IrrelevantValueFacts::IdIsIrrelevant(
+    uint32_t pointer_id) const {
+  return irrelevant_ids_.count(pointer_id) != 0;
+}
+
 // End of arbitrarily-valued variable facts
 //==============================
 
@@ -1378,8 +1395,7 @@
           MakeUnique<DataSynonymAndIdEquationFacts>()),
       dead_block_facts_(MakeUnique<DeadBlockFacts>()),
       livesafe_function_facts_(MakeUnique<LivesafeFunctionFacts>()),
-      irrelevant_pointee_value_facts_(
-          MakeUnique<IrrelevantPointeeValueFacts>()) {}
+      irrelevant_value_facts_(MakeUnique<IrrelevantValueFacts>()) {}
 
 FactManager::~FactManager() = default;
 
@@ -1420,6 +1436,8 @@
 void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1,
                                      const protobufs::DataDescriptor& data2,
                                      opt::IRContext* context) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550):
+  //  assert that neither |data1| nor |data2| are irrelevant.
   protobufs::FactDataSynonym fact;
   *fact.mutable_data1() = data1;
   *fact.mutable_data2() = data2;
@@ -1500,18 +1518,30 @@
 }
 
 bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const {
-  return irrelevant_pointee_value_facts_->PointeeValueIsIrrelevant(pointer_id);
+  return irrelevant_value_facts_->PointeeValueIsIrrelevant(pointer_id);
+}
+
+bool FactManager::IdIsIrrelevant(uint32_t result_id) const {
+  return irrelevant_value_facts_->IdIsIrrelevant(result_id);
 }
 
 void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) {
   protobufs::FactPointeeValueIsIrrelevant fact;
   fact.set_pointer_id(pointer_id);
-  irrelevant_pointee_value_facts_->AddFact(fact);
+  irrelevant_value_facts_->AddFact(fact);
+}
+
+void FactManager::AddFactIdIsIrrelevant(uint32_t result_id) {
+  protobufs::FactIdIsIrrelevant fact;
+  fact.set_result_id(result_id);
+  irrelevant_value_facts_->AddFact(fact);
 }
 
 void FactManager::AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
                                     const std::vector<uint32_t>& rhs_id,
                                     opt::IRContext* context) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550):
+  //  assert that elements of |rhs_id| and |lhs_id| are not irrelevant.
   protobufs::FactIdEquation fact;
   fact.set_lhs_id(lhs_id);
   fact.set_opcode(opcode);
diff --git a/source/fuzz/fact_manager.h b/source/fuzz/fact_manager.h
index f520e42..f83e2ff 100644
--- a/source/fuzz/fact_manager.h
+++ b/source/fuzz/fact_manager.h
@@ -68,6 +68,10 @@
   // is irrelevant: it does not affect the observable behaviour of the module.
   void AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id);
 
+  // Records a fact that the |result_id| is irrelevant (i.e. it doesn't affect
+  // the semantics of the module)
+  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]|
@@ -181,13 +185,16 @@
   //==============================
 
   //==============================
-  // Querying facts about pointers with irrelevant pointee values
+  // Querying facts about irrelevant values
 
   // Returns true if and ony if the value of the pointee associated with
   // |pointer_id| is irrelevant.
   bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
 
-  // End of irrelevant pointee value facts
+  // Returns true iff there exists a fact that the |result_id| is irrelevant.
+  bool IdIsIrrelevant(uint32_t result_id) const;
+
+  // End of irrelevant value facts
   //==============================
 
  private:
@@ -213,10 +220,10 @@
   std::unique_ptr<LivesafeFunctionFacts>
       livesafe_function_facts_;  // Unique pointer to internal data.
 
-  class IrrelevantPointeeValueFacts;  // Opaque class for management of
-  // facts about pointers whose pointee values do not matter.
-  std::unique_ptr<IrrelevantPointeeValueFacts>
-      irrelevant_pointee_value_facts_;  // Unique pointer to internal data.
+  class IrrelevantValueFacts;  // Opaque class for management of
+  // facts about various irrelevant values in the module.
+  std::unique_ptr<IrrelevantValueFacts>
+      irrelevant_value_facts_;  // Unique pointer to internal data.
 };
 
 }  // namespace fuzz
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index f103fbb..e106608 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -170,6 +170,7 @@
     FactFunctionIsLivesafe function_is_livesafe_fact = 4;
     FactPointeeValueIsIrrelevant pointee_value_is_irrelevant_fact = 5;
     FactIdEquation id_equation_fact = 6;
+    FactIdIsIrrelevant id_is_irrelevant = 7;
   }
 }
 
@@ -182,6 +183,7 @@
   // can be made to this block.
 
   uint32 block_id = 1;
+
 }
 
 message FactConstantUniform {
@@ -222,6 +224,7 @@
   // functions.
 
   uint32 function_id = 1;
+
 }
 
 message FactIdEquation {
@@ -249,6 +252,18 @@
 
 }
 
+message FactIdIsIrrelevant {
+
+  // Records a fact that |result_id| is irrelevant (i.e. it's usage doesn't
+  // change the semantics of the module). This implies that a use of this id
+  // can later be replaced with some other id of the same type, or the
+  // definition of |result_id| can be changed so that it yields a different value.
+
+  // An irrelevant id.
+  uint32 result_id = 1;
+
+}
+
 message FactPointeeValueIsIrrelevant {
 
   // Records the fact that value of the data pointed to by a pointer id does
@@ -258,6 +273,7 @@
 
   // A result id of pointer type
   uint32 pointer_id = 1;
+
 }
 
 message AccessChainClampingInfo {
diff --git a/test/fuzz/fact_manager_test.cpp b/test/fuzz/fact_manager_test.cpp
index 623b6b3..bce10b9 100644
--- a/test/fuzz/fact_manager_test.cpp
+++ b/test/fuzz/fact_manager_test.cpp
@@ -1283,6 +1283,41 @@
   ASSERT_FALSE(context->get_constant_mgr()->FindConstant(&constant_one));
 }
 
+TEST(FactManagerTest, IdIsIrrelevant) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %12 = OpConstant %6 0
+         %13 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  ASSERT_FALSE(fact_manager.IdIsIrrelevant(12));
+  ASSERT_FALSE(fact_manager.IdIsIrrelevant(13));
+
+  fact_manager.AddFactIdIsIrrelevant(12);
+
+  ASSERT_TRUE(fact_manager.IdIsIrrelevant(12));
+  ASSERT_FALSE(fact_manager.IdIsIrrelevant(13));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools