spirv-fuzz: Do not replace the Sample argument in OpImageTexelPointer (#3311)

The Sample argument of OpImageTexelPointer is sometimes required to be
a zero constant.  It thus cannot be replaced with a synonym in
general.  This change avoids replacing this argument with a synonym.
diff --git a/source/fuzz/transformation_replace_id_with_synonym.cpp b/source/fuzz/transformation_replace_id_with_synonym.cpp
index 3a10682..e427f3c 100644
--- a/source/fuzz/transformation_replace_id_with_synonym.cpp
+++ b/source/fuzz/transformation_replace_id_with_synonym.cpp
@@ -170,6 +170,15 @@
       return false;
     }
   }
+
+  if (use_instruction->opcode() == SpvOpImageTexelPointer &&
+      use_in_operand_index == 2) {
+    // The OpImageTexelPointer instruction has a Sample parameter that in some
+    // situations must be an id for the value 0.  To guard against disrupting
+    // that requirement, we do not replace this argument to that instruction.
+    return false;
+  }
+
   return true;
 }
 
diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
index 6b514a3..37e9510 100644
--- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp
+++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
@@ -1393,6 +1393,63 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationReplaceIdWithSynonymTest,
+     DoNotReplaceSampleParameterOfOpImageTexelPointer) {
+  const std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 310
+          %4 = OpTypeVoid
+          %5 = OpTypeFunction %4
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpConstant %6 2
+          %9 = OpConstant %6 0
+         %10 = OpConstant %6 10
+         %11 = OpTypeBool
+         %12 = OpConstant %6 1
+         %13 = OpTypeFloat 32
+         %14 = OpTypePointer Image %13
+         %15 = OpTypeImage %13 2D 0 0 0 0 Rgba8
+         %16 = OpTypePointer Private %15
+          %3 = OpVariable %16 Private
+         %17 = OpTypeVector %6 2
+         %18 = OpConstantComposite %17 %9 %9
+          %2 = OpFunction %4 None %5
+         %19 = OpLabel
+        %100 = OpCopyObject %6 %9
+         %20 = OpImageTexelPointer %14 %3 %18 %9
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  // Add synonym fact relating %100 and %9.
+  transformation_context.GetFactManager()->AddFact(MakeSynonymFact(100, 9),
+                                                   context.get());
+
+  // Not legal the Sample argument of OpImageTexelPointer needs to be a zero
+  // constant.
+  ASSERT_FALSE(
+      TransformationReplaceIdWithSynonym(
+          MakeIdUseDescriptor(
+              9, MakeInstructionDescriptor(20, SpvOpImageTexelPointer, 0), 2),
+          100)
+          .IsApplicable(context.get(), transformation_context));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools