spirv-fuzz: Fix to transformation that adds a synonym via a loop (#3898)

Fixes #3897.
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index 06c585c..35e0b9a 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -932,7 +932,7 @@
   // A transformation that uses a loop to create a synonym for an integer
   // constant C (scalar or vector) using an initial value I, a step value S and
   // a number of iterations N such that C = I - N * S. For each iteration, S is
-  // added to the total.
+  // subtracted from the total.
   // The loop can be made up of one or two blocks, and it is inserted before a
   // block with a single predecessor. In the one-block case, it is of the form:
   //
@@ -942,7 +942,7 @@
   //    %eventual_syn_id = OpISub %type_of_I %temp_id %step_val_id
   // %incremented_ctr_id = OpIAdd %int %ctr_id %int_1
   //            %cond_id = OpSLessThan %bool %incremented_ctr_id %num_iterations_id
-  //                       OpLoopMerge %block_after_loop_id %loop_id %none
+  //                       OpLoopMerge %block_after_loop_id %loop_id None
   //                       OpBranchConditional %cond_id %loop_id %block_after_loop_id
   //
   // A new OpPhi instruction is then added to %block_after_loop_id, as follows:
diff --git a/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp b/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp
index 5252d19..45d3fc8 100644
--- a/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp
+++ b/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp
@@ -387,12 +387,12 @@
       [this](opt::Instruction* instruction, uint32_t operand_index) {
         assert(instruction->opcode() != SpvOpLoopMerge &&
                instruction->opcode() != SpvOpSelectionMerge &&
-               instruction->opcode() != SpvOpSwitch &&
-               "The block should not be referenced by OpLoopMerge, "
-               "OpSelectionMerge or OpSwitch instructions, by construction.");
+               "The block should not be referenced by OpLoopMerge or "
+               "OpSelectionMerge, by construction.");
         // Replace all uses of the label inside branch instructions.
         if (instruction->opcode() == SpvOpBranch ||
-            instruction->opcode() == SpvOpBranchConditional) {
+            instruction->opcode() == SpvOpBranchConditional ||
+            instruction->opcode() == SpvOpSwitch) {
           instruction->SetOperand(operand_index, {message_.loop_id()});
         }
       });
diff --git a/test/fuzz/transformation_add_loop_to_create_int_constant_synonym_test.cpp b/test/fuzz/transformation_add_loop_to_create_int_constant_synonym_test.cpp
index 93c2d11..7245453 100644
--- a/test/fuzz/transformation_add_loop_to_create_int_constant_synonym_test.cpp
+++ b/test/fuzz/transformation_add_loop_to_create_int_constant_synonym_test.cpp
@@ -1021,6 +1021,113 @@
                    .IsApplicable(context.get(), transformation_context));
 }
 
+TEST(TransformationAddLoopToCreateIntConstantSynonymTest, InserBeforeOpSwitch) {
+  // Checks that it is acceptable for a loop to be added before a target of an
+  // OpSwitch instruction.
+  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
+          %7 = OpConstant %6 0
+         %20 = OpConstant %6 1
+         %21 = OpConstant %6 2
+         %22 = OpTypeBool
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %10 None
+               OpSwitch %7 %9 0 %8
+          %9 = OpLabel
+               OpBranch %10
+          %8 = 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()));
+
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  auto transformation1 = TransformationAddLoopToCreateIntConstantSynonym(
+      20, 21, 20, 20, 9, 100, 101, 102, 103, 104, 105, 106, 0);
+  ASSERT_TRUE(
+      transformation1.IsApplicable(context.get(), transformation_context));
+  ApplyAndCheckFreshIds(transformation1, context.get(),
+                        &transformation_context);
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(20, {}), MakeDataDescriptor(100, {})));
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto transformation2 = TransformationAddLoopToCreateIntConstantSynonym(
+      20, 21, 20, 20, 8, 200, 201, 202, 203, 204, 205, 206, 0);
+  ASSERT_TRUE(
+      transformation2.IsApplicable(context.get(), transformation_context));
+  ApplyAndCheckFreshIds(transformation2, context.get(),
+                        &transformation_context);
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(20, {}), MakeDataDescriptor(200, {})));
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformations = 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 = OpConstant %6 0
+         %20 = OpConstant %6 1
+         %21 = OpConstant %6 2
+         %22 = OpTypeBool
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %10 None
+               OpSwitch %7 %101 0 %201
+        %101 = OpLabel
+        %102 = OpPhi %6 %7 %5 %105 %101
+        %103 = OpPhi %6 %21 %5 %104 %101
+        %104 = OpISub %6 %103 %20
+        %105 = OpIAdd %6 %102 %20
+        %106 = OpSLessThan %22 %105 %20
+               OpLoopMerge %9 %101 None
+               OpBranchConditional %106 %101 %9
+          %9 = OpLabel
+        %100 = OpPhi %6 %104 %101
+               OpBranch %10
+        %201 = OpLabel
+        %202 = OpPhi %6 %7 %5 %205 %201
+        %203 = OpPhi %6 %21 %5 %204 %201
+        %204 = OpISub %6 %203 %20
+        %205 = OpIAdd %6 %202 %20
+        %206 = OpSLessThan %22 %205 %20
+               OpLoopMerge %8 %201 None
+               OpBranchConditional %206 %201 %8
+          %8 = OpLabel
+        %200 = OpPhi %6 %204 %201
+               OpBranch %10
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools