spirv-fuzz: Fix TransformationDuplicateRegionWithSelection (#3815)

Introduces two changes:

- duplicated_exit_region refers to a correct block, regardless of the order
  of the blocks in the enclosing function.
- Exclude the case where the continue target is the exit block.
diff --git a/source/fuzz/transformation_duplicate_region_with_selection.cpp b/source/fuzz/transformation_duplicate_region_with_selection.cpp
index 3b6c1df..8c72130 100644
--- a/source/fuzz/transformation_duplicate_region_with_selection.cpp
+++ b/source/fuzz/transformation_duplicate_region_with_selection.cpp
@@ -167,9 +167,11 @@
           ir_context->cfg()->block(loop_merge->GetSingleWordOperand(1));
       // The continue target is a single-entry, single-exit region. Therefore,
       // if the continue target is the exit block, the region might not contain
-      // the loop header.
-      if (continue_target != exit_block &&
-          region_set.count(&block) != region_set.count(continue_target)) {
+      // the loop header. However, we would like to exclude this situation,
+      // since it would be impossible for the modified exit block to branch to
+      // the new selection merge block. In this scenario the exit block is
+      // required to branch to the loop header.
+      if (region_set.count(&block) != region_set.count(continue_target)) {
         return false;
       }
     }
@@ -325,7 +327,7 @@
   uint32_t entry_block_pred_id =
       ir_context->get_instr_block(entry_block_preds[0])->id();
   // Update all the OpPhi instructions in the |entry_block|. Change every
-  // occurence of |entry_block_pred_id| to the id of |new_entry|, because we
+  // occurrence of |entry_block_pred_id| to the id of |new_entry|, because we
   // will insert |new_entry| before |entry_block|.
   for (auto& instr : *entry_block) {
     if (instr.opcode() == SpvOpPhi) {
@@ -345,6 +347,7 @@
   }
 
   opt::BasicBlock* previous_block = nullptr;
+  opt::BasicBlock* duplicated_exit_block = nullptr;
   // Iterate over all blocks of the function to duplicate blocks of the original
   // region and their instructions.
   for (auto& block : blocks) {
@@ -414,12 +417,13 @@
                                                 exit_block);
     }
     previous_block = duplicated_block_ptr;
+    if (block == exit_block) {
+      // After execution of the loop, this variable stores a pointer to the last
+      // duplicated block.
+      duplicated_exit_block = duplicated_block_ptr;
+    }
   }
 
-  // After execution of the loop, this variable stores a pointer to the last
-  // duplicated block.
-  auto duplicated_exit_block = previous_block;
-
   for (auto& block : region_blocks) {
     for (auto& instr : *block) {
       if (instr.result_id() != 0 &&
@@ -500,7 +504,7 @@
           {{SPV_OPERAND_TYPE_ID, {message_.merge_label_fresh_id()}}}));
   exit_block->AddInstruction(MakeUnique<opt::Instruction>(merge_branch_instr));
   duplicated_exit_block->AddInstruction(
-      MakeUnique<opt::Instruction>(merge_branch_instr));
+      std::unique_ptr<opt::Instruction>(merge_branch_instr.Clone(ir_context)));
 
   // Execution needs to start in the |new_entry_block|. Change all
   // the uses of |entry_block_label_instr| outside of the original
diff --git a/test/fuzz/transformation_duplicate_region_with_selection_test.cpp b/test/fuzz/transformation_duplicate_region_with_selection_test.cpp
index 6539c4a..c1fac9a 100644
--- a/test/fuzz/transformation_duplicate_region_with_selection_test.cpp
+++ b/test/fuzz/transformation_duplicate_region_with_selection_test.cpp
@@ -1599,6 +1599,87 @@
   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
 }
 
+TEST(TransformationDuplicateRegionWithSelectionTest,
+     ContinueExitBlockNotApplicable) {
+  // This test handles a case where the exit block is the continue target and
+  // the transformation is not applicable.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "s"
+               OpName %10 "i"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %17 = OpConstant %6 10
+         %18 = OpTypeBool
+         %24 = OpConstant %6 5
+         %30 = OpConstant %6 1
+         %50 = OpConstantTrue %18
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+               OpStore %8 %9
+               OpStore %10 %9
+               OpBranch %11
+         %11 = OpLabel
+               OpLoopMerge %13 %14 None
+               OpBranch %15
+         %15 = OpLabel
+         %16 = OpLoad %6 %10
+         %19 = OpSLessThan %18 %16 %17
+               OpBranchConditional %19 %12 %13
+         %12 = OpLabel
+         %20 = OpLoad %6 %10
+         %21 = OpLoad %6 %8
+         %22 = OpIAdd %6 %21 %20
+               OpStore %8 %22
+         %23 = OpLoad %6 %10
+         %25 = OpIEqual %18 %23 %24
+               OpSelectionMerge %27 None
+               OpBranchConditional %25 %26 %27
+         %26 = OpLabel
+               OpBranch %13
+         %27 = OpLabel
+               OpBranch %14
+         %14 = OpLabel
+         %29 = OpLoad %6 %10
+         %31 = OpIAdd %6 %29 %30
+               OpStore %10 %31
+               OpBranch %11
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+    )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  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);
+
+  TransformationDuplicateRegionWithSelection transformation_bad =
+      TransformationDuplicateRegionWithSelection(
+          500, 50, 501, 27, 14, {{27, 101}, {14, 102}}, {{29, 201}, {31, 202}},
+          {{29, 301}, {31, 302}});
+
+  ASSERT_FALSE(
+      transformation_bad.IsApplicable(context.get(), transformation_context));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools