spirv-fuzz: Fixes related to irrelevant ids (#3901)

Fixes #3899.
Fixes #3900.
diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
index 9cbf590..ae9a754 100644
--- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
@@ -147,6 +147,15 @@
                                                : parent_block->terminator();
           }
 
+          if (GetTransformationContext()->GetFactManager()->BlockIsDead(
+                  GetIRContext()
+                      ->get_instr_block(instruction_to_insert_before)
+                      ->id())) {
+            // We cannot create a synonym via a composite extraction in a dead
+            // block, as the resulting id is irrelevant.
+            continue;
+          }
+
           assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
                      synonym_to_try->object()) &&
                  "Irrelevant ids can't participate in DataSynonym facts");
@@ -155,6 +164,11 @@
                                         instruction_to_insert_before),
               id_with_which_to_replace_use, synonym_to_try->object(),
               fuzzerutil::RepeatedFieldToVector(synonym_to_try->index())));
+          assert(GetTransformationContext()->GetFactManager()->IsSynonymous(
+                     MakeDataDescriptor(id_with_which_to_replace_use, {}),
+                     *synonym_to_try) &&
+                 "The extracted id must be synonymous with the component from "
+                 "which it was extracted.");
         }
 
         ApplyTransformation(TransformationReplaceIdWithSynonym(
diff --git a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp
index 0be00c7..71f3507 100644
--- a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp
+++ b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp
@@ -127,6 +127,14 @@
               continue;
             }
 
+            // We cannot replace a variable initializer with a non-constant.
+            if (TransformationReplaceIrrelevantId::
+                    AttemptsToReplaceVariableInitializerWithNonConstant(
+                        *use_inst, *GetIRContext()->get_def_use_mgr()->GetDef(
+                                       replacement_id))) {
+              continue;
+            }
+
             // Only consider this replacement if the use point is within a basic
             // block and the id is available at the use point.
             //
diff --git a/source/fuzz/transformation_replace_irrelevant_id.cpp b/source/fuzz/transformation_replace_irrelevant_id.cpp
index 9866c08..7cd4eff 100644
--- a/source/fuzz/transformation_replace_irrelevant_id.cpp
+++ b/source/fuzz/transformation_replace_irrelevant_id.cpp
@@ -69,10 +69,17 @@
       !ir_context->get_type_mgr()->GetType(type_id_of_interest)->AsPointer() &&
       "An irrelevant id cannot be a pointer");
 
+  uint32_t use_in_operand_index =
+      message_.id_use_descriptor().in_operand_index();
+
   // The id use must be replaceable with any other id of the same type.
-  if (!fuzzerutil::IdUseCanBeReplaced(
-          ir_context, use_instruction,
-          message_.id_use_descriptor().in_operand_index())) {
+  if (!fuzzerutil::IdUseCanBeReplaced(ir_context, use_instruction,
+                                      use_in_operand_index)) {
+    return false;
+  }
+
+  if (AttemptsToReplaceVariableInitializerWithNonConstant(
+          *use_instruction, *replacement_id_def)) {
     return false;
   }
 
@@ -111,5 +118,13 @@
   return std::unordered_set<uint32_t>();
 }
 
+bool TransformationReplaceIrrelevantId::
+    AttemptsToReplaceVariableInitializerWithNonConstant(
+        const opt::Instruction& use_instruction,
+        const opt::Instruction& replacement_for_use) {
+  return use_instruction.opcode() == SpvOpVariable &&
+         !spvOpcodeIsConstant(replacement_for_use.opcode());
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/source/fuzz/transformation_replace_irrelevant_id.h b/source/fuzz/transformation_replace_irrelevant_id.h
index 81bde34..0210520 100644
--- a/source/fuzz/transformation_replace_irrelevant_id.h
+++ b/source/fuzz/transformation_replace_irrelevant_id.h
@@ -50,6 +50,13 @@
 
   protobufs::Transformation ToMessage() const override;
 
+  // Returns true if and only if |use_instruction| is OpVariable and
+  // |replacement_for_use| is not a constant instruction - i.e., if it would be
+  // illegal to replace the variable's initializer with the given instruction.
+  static bool AttemptsToReplaceVariableInitializerWithNonConstant(
+      const opt::Instruction& use_instruction,
+      const opt::Instruction& replacement_for_use);
+
  private:
   protobufs::TransformationReplaceIrrelevantId message_;
 };
diff --git a/test/fuzz/transformation_replace_irrelevant_id_test.cpp b/test/fuzz/transformation_replace_irrelevant_id_test.cpp
index cc6cc51..d38e726 100644
--- a/test/fuzz/transformation_replace_irrelevant_id_test.cpp
+++ b/test/fuzz/transformation_replace_irrelevant_id_test.cpp
@@ -184,6 +184,55 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationReplaceIrrelevantIdTest,
+     DoNotReplaceVariableInitializerWithNonConstant) {
+  // Checks that it is not possible to replace the initializer of a variable
+  // with a non-constant id (such as a function parameter).
+  const std::string reference_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
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %6
+         %13 = OpConstant %6 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %2 None %8
+          %9 = OpFunctionParameter %6
+         %11 = OpLabel
+         %12 = OpVariable %7 Function %13
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+  transformation_context.GetFactManager()->AddFactIdIsIrrelevant(13);
+
+  // We cannot replace the use of %13 in the initializer of %12 with %9 because
+  // %9 is not a constant.
+  ASSERT_FALSE(TransformationReplaceIrrelevantId(
+                   MakeIdUseDescriptor(
+                       13, MakeInstructionDescriptor(12, SpvOpVariable, 0), 1),
+                   9)
+                   .IsApplicable(context.get(), transformation_context));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools