spirv-fuzz: Respect control flow rules when merging returns (#4279)


Fixes #4278.

Some minor code cleanup is incorporated.
diff --git a/source/fuzz/fuzzer_pass_merge_function_returns.cpp b/source/fuzz/fuzzer_pass_merge_function_returns.cpp
index 70fa820..ee82eca 100644
--- a/source/fuzz/fuzzer_pass_merge_function_returns.cpp
+++ b/source/fuzz/fuzzer_pass_merge_function_returns.cpp
@@ -172,8 +172,9 @@
     }
 
     // Get the ids needed by the transformation.
-    uint32_t outer_header_id = GetFuzzerContext()->GetFreshId();
-    uint32_t outer_return_id = GetFuzzerContext()->GetFreshId();
+    const uint32_t outer_header_id = GetFuzzerContext()->GetFreshId();
+    const uint32_t unreachable_continue_id = GetFuzzerContext()->GetFreshId();
+    const uint32_t outer_return_id = GetFuzzerContext()->GetFreshId();
 
     bool function_is_void =
         GetIRContext()->get_type_mgr()->GetType(function->type_id())->AsVoid();
@@ -209,8 +210,8 @@
     // Apply the transformation if it is applicable (it could be inapplicable if
     // adding new predecessors to merge blocks breaks dominance rules).
     MaybeApplyTransformation(TransformationMergeFunctionReturns(
-        function->result_id(), outer_header_id, outer_return_id, return_val_id,
-        returnable_val_id, merge_blocks_info));
+        function->result_id(), outer_header_id, unreachable_continue_id,
+        outer_return_id, return_val_id, returnable_val_id, merge_blocks_info));
   }
 }
 
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index 2ca03db..eadf824 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -1634,6 +1634,9 @@
   // A fresh id for the header of the new outer loop.
   uint32 outer_header_id = 2;
 
+  // A fresh id for an unreachable continue construct for the new outer loop.
+  uint32 unreachable_continue_id = 7;
+
   // A fresh id for the new return block of the function,
   // i.e. the merge block of the new outer loop.
   uint32 outer_return_id = 3;
diff --git a/source/fuzz/transformation_merge_function_returns.cpp b/source/fuzz/transformation_merge_function_returns.cpp
index 1aefedc..022e1b6 100644
--- a/source/fuzz/transformation_merge_function_returns.cpp
+++ b/source/fuzz/transformation_merge_function_returns.cpp
@@ -25,11 +25,13 @@
     : message_(std::move(message)) {}
 
 TransformationMergeFunctionReturns::TransformationMergeFunctionReturns(
-    uint32_t function_id, uint32_t outer_header_id, uint32_t outer_return_id,
+    uint32_t function_id, uint32_t outer_header_id,
+    uint32_t unreachable_continue_id, uint32_t outer_return_id,
     uint32_t return_val_id, uint32_t any_returnable_val_id,
     const std::vector<protobufs::ReturnMergingInfo>& returns_merging_info) {
   message_.set_function_id(function_id);
   message_.set_outer_header_id(outer_header_id);
+  message_.set_unreachable_continue_id(unreachable_continue_id);
   message_.set_outer_return_id(outer_return_id);
   message_.set_return_val_id(return_val_id);
   message_.set_any_returnable_val_id(any_returnable_val_id);
@@ -66,7 +68,9 @@
 
   // Check that the fresh ids provided are fresh and distinct.
   std::set<uint32_t> used_fresh_ids;
-  for (uint32_t id : {message_.outer_header_id(), message_.outer_return_id()}) {
+  for (uint32_t id :
+       {message_.outer_header_id(), message_.unreachable_continue_id(),
+        message_.outer_return_id()}) {
     if (!id || !CheckIdIsFreshAndNotUsedByThisTransformation(id, ir_context,
                                                              &used_fresh_ids)) {
       return false;
@@ -499,25 +503,20 @@
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.outer_header_id());
 
-  // Add the instruction: OpLoopMerge %outer_return_id %outer_header_id None
-  // The header is the continue block of the outer loop.
+  // Add the instruction:
+  //   OpLoopMerge %outer_return_id %unreachable_continue_id None
   outer_loop_header->AddInstruction(MakeUnique<opt::Instruction>(
       ir_context, SpvOpLoopMerge, 0, 0,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.outer_return_id()}},
-          {SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}},
+          {SPV_OPERAND_TYPE_ID, {message_.unreachable_continue_id()}},
           {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}));
 
-  // Add conditional branch:
-  //   OpBranchConditional %true %block_after_entry %outer_header_id
-  // This will always branch to %block_after_entry, but it also creates a back
-  // edge for the loop (which is never traversed).
+  // Add unconditional branch to %block_after_entry.
   outer_loop_header->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpBranchConditional, 0, 0,
+      ir_context, SpvOpBranch, 0, 0,
       opt::Instruction::OperandList{
-          {SPV_OPERAND_TYPE_ID, {constant_true}},
-          {SPV_OPERAND_TYPE_ID, {block_after_entry}},
-          {SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}}}));
+          {SPV_OPERAND_TYPE_ID, {block_after_entry}}}));
 
   // Insert the header right after the entry block.
   function->InsertBasicBlockAfter(std::move(outer_loop_header),
@@ -581,6 +580,24 @@
   // Insert the new return block at the end of the function.
   function->AddBasicBlock(std::move(outer_return_block));
 
+  // Create the unreachable continue block associated with the enclosing loop.
+  auto unreachable_continue_block =
+      MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpLabel, 0, message_.unreachable_continue_id(),
+          opt::Instruction::OperandList()));
+
+  fuzzerutil::UpdateModuleIdBound(ir_context,
+                                  message_.unreachable_continue_id());
+
+  // Insert an branch back to the loop header, to create a back edge.
+  unreachable_continue_block->AddInstruction(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpBranch, 0, 0,
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}}}));
+
+  // Insert the unreachable continue block at the end of the function.
+  function->AddBasicBlock(std::move(unreachable_continue_block));
+
   // All analyses must be invalidated because the structure of the module was
   // changed.
   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
@@ -590,13 +607,14 @@
     const {
   std::unordered_set<uint32_t> result;
   result.emplace(message_.outer_header_id());
+  result.emplace(message_.unreachable_continue_id());
   result.emplace(message_.outer_return_id());
   // |message_.return_val_info| can be 0 if the function is void.
   if (message_.return_val_id()) {
     result.emplace(message_.return_val_id());
   }
 
-  for (auto merging_info : message_.return_merging_info()) {
+  for (const auto& merging_info : message_.return_merging_info()) {
     result.emplace(merging_info.is_returning_id());
     // |maybe_return_val_id| can be 0 if the function is void.
     if (merging_info.maybe_return_val_id()) {
diff --git a/source/fuzz/transformation_merge_function_returns.h b/source/fuzz/transformation_merge_function_returns.h
index 8f0937c..b3208ac 100644
--- a/source/fuzz/transformation_merge_function_returns.h
+++ b/source/fuzz/transformation_merge_function_returns.h
@@ -25,7 +25,8 @@
       protobufs::TransformationMergeFunctionReturns message);
 
   TransformationMergeFunctionReturns(
-      uint32_t function_id, uint32_t outer_header_id, uint32_t outer_return_id,
+      uint32_t function_id, uint32_t outer_header_id,
+      uint32_t unreachable_continue_id, uint32_t outer_return_id,
       uint32_t return_val_id, uint32_t any_returnable_val_id,
       const std::vector<protobufs::ReturnMergingInfo>& returns_merging_info);
 
@@ -40,7 +41,7 @@
   //   statements, this id will be ignored.
   // - Merge blocks of reachable loops that contain return statements only
   //   consist of OpLabel, OpPhi or OpBranch instructions.
-  // - The model contains OpConstantTrue and OpConstantFalse instructions.
+  // - The module contains OpConstantTrue and OpConstantFalse instructions.
   // - For all merge blocks of reachable loops that contain return statements,
   //   either:
   //   - a mapping is provided in |message_.return_merging_info|, all of the
diff --git a/test/fuzz/transformation_merge_function_returns_test.cpp b/test/fuzz/transformation_merge_function_returns_test.cpp
index e60d345..400d49a 100644
--- a/test/fuzz/transformation_merge_function_returns_test.cpp
+++ b/test/fuzz/transformation_merge_function_returns_test.cpp
@@ -147,12 +147,12 @@
       MakeUnique<FactManager>(context.get()), validator_options);
 
   // Function %1 does not exist.
-  ASSERT_FALSE(TransformationMergeFunctionReturns(1, 100, 101, 0, 0, {{}})
+  ASSERT_FALSE(TransformationMergeFunctionReturns(1, 100, 200, 101, 0, 0, {{}})
                    .IsApplicable(context.get(), transformation_context));
 
   // The entry block (%22) of function %15 does not branch unconditionally to
   // the following block.
-  ASSERT_FALSE(TransformationMergeFunctionReturns(16, 100, 101, 0, 0, {{}})
+  ASSERT_FALSE(TransformationMergeFunctionReturns(16, 100, 200, 101, 0, 0, {{}})
                    .IsApplicable(context.get(), transformation_context));
 
   // Block %28 is the merge block of a loop containing a return instruction, but
@@ -160,7 +160,7 @@
   // not OpLabel, OpPhi or OpBranch).
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          18, 100, 101, 0, 0, {{MakeReturnMergingInfo(29, 102, 0, {{}})}})
+          18, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(29, 102, 0, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // Block %34 is the merge block of a loop containing a return instruction, but
@@ -168,21 +168,24 @@
   // that are not OpLabel, OpPhi or OpBranch).
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          20, 100, 101, 0, 0, {{MakeReturnMergingInfo(35, 102, 0, {{}})}})
+          20, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(35, 102, 0, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // Id %1000 cannot be found in the module and there is no id of the correct
   // type (float) available at the end of the entry block of function %21.
-  ASSERT_FALSE(TransformationMergeFunctionReturns(22, 100, 101, 102, 1000, {{}})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationMergeFunctionReturns(22, 100, 200, 101, 102, 1000, {{}})
+          .IsApplicable(context.get(), transformation_context));
 
   // Id %47 is of type float, while function %45 has return type int.
-  ASSERT_FALSE(TransformationMergeFunctionReturns(45, 100, 101, 102, 47, {{}})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationMergeFunctionReturns(45, 100, 200, 101, 102, 47, {{}})
+          .IsApplicable(context.get(), transformation_context));
 
   // Id %50 is not available at the end of the entry block of function %45.
-  ASSERT_FALSE(TransformationMergeFunctionReturns(45, 100, 101, 102, 50, {{}})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationMergeFunctionReturns(45, 100, 200, 101, 102, 50, {{}})
+          .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationMergeFunctionReturnsTest, MissingBooleans) {
@@ -235,8 +238,9 @@
     TransformationContext transformation_context(
         MakeUnique<FactManager>(context.get()), validator_options);
 
-    ASSERT_FALSE(TransformationMergeFunctionReturns(3, 100, 101, 0, 0, {{}})
-                     .IsApplicable(context.get(), transformation_context));
+    ASSERT_FALSE(
+        TransformationMergeFunctionReturns(3, 100, 200, 101, 0, 0, {{}})
+            .IsApplicable(context.get(), transformation_context));
   }
   {
     // OpConstantFalse is missing.
@@ -287,8 +291,9 @@
     TransformationContext transformation_context(
         MakeUnique<FactManager>(context.get()), validator_options);
 
-    ASSERT_FALSE(TransformationMergeFunctionReturns(3, 100, 101, 0, 0, {{}})
-                     .IsApplicable(context.get(), transformation_context));
+    ASSERT_FALSE(
+        TransformationMergeFunctionReturns(3, 100, 200, 101, 0, 0, {{}})
+            .IsApplicable(context.get(), transformation_context));
   }
 }
 
@@ -386,59 +391,65 @@
   // Fresh id %100 is used twice.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          17, 100, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
+          17, 100, 200, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // Fresh id %100 is used twice.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          17, 100, 101, 0, 0, {{MakeReturnMergingInfo(20, 100, 0, {{}})}})
+          17, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(20, 100, 0, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // %0 cannot be a fresh id for the new merge block.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          17, 100, 0, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
+          17, 100, 200, 0, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
+          .IsApplicable(context.get(), transformation_context));
+
+  // %0 cannot be a fresh id for the new continue block.
+  ASSERT_FALSE(
+      TransformationMergeFunctionReturns(
+          17, 100, 0, 200, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // %0 cannot be a fresh id for the new header block.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          17, 0, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
+          17, 0, 200, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // %0 cannot be a fresh id for the new |is_returning| instruction in an
   // existing merge block.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          17, 100, 101, 0, 0, {{MakeReturnMergingInfo(20, 0, 0, {{}})}})
+          17, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(20, 0, 0, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // %0 cannot be a fresh id for the new |return_val| instruction in the new
   // return block.
-  ASSERT_FALSE(
-      TransformationMergeFunctionReturns(
-          14, 100, 101, 0, 10, {{MakeReturnMergingInfo(27, 102, 103, {{}})}})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationMergeFunctionReturns(
+                   14, 100, 200, 101, 0, 10,
+                   {{MakeReturnMergingInfo(27, 102, 103, {{}})}})
+                   .IsApplicable(context.get(), transformation_context));
 
   // %0 cannot be a fresh id for the new |maybe_return_val| instruction in an
   // existing merge block, inside a non-void function.
-  ASSERT_FALSE(
-      TransformationMergeFunctionReturns(
-          14, 100, 101, 102, 10, {{MakeReturnMergingInfo(27, 103, 0, {{}})}})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationMergeFunctionReturns(
+                   14, 100, 200, 101, 102, 10,
+                   {{MakeReturnMergingInfo(27, 103, 0, {{}})}})
+                   .IsApplicable(context.get(), transformation_context));
 
   // Fresh id %102 is repeated.
-  ASSERT_FALSE(
-      TransformationMergeFunctionReturns(
-          14, 100, 101, 102, 10, {{MakeReturnMergingInfo(27, 102, 104, {{}})}})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationMergeFunctionReturns(
+                   14, 100, 200, 101, 102, 10,
+                   {{MakeReturnMergingInfo(27, 102, 104, {{}})}})
+                   .IsApplicable(context.get(), transformation_context));
 
   // Id %11 (type int) does not have the correct type (float) for OpPhi
   // instruction %31.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          16, 100, 101, 0, 0,
+          16, 100, 200, 101, 0, 0,
           {{MakeReturnMergingInfo(36, 103, 104, {{{31, 11}, {32, 11}}})}})
           .IsApplicable(context.get(), transformation_context));
 
@@ -446,21 +457,21 @@
   // instruction %31.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          16, 100, 101, 0, 0,
+          16, 100, 200, 101, 0, 0,
           {{MakeReturnMergingInfo(36, 102, 0, {{{31, 11}, {32, 11}}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // Id %43 is not available at the end of the entry block.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          16, 100, 101, 0, 0,
+          16, 100, 200, 101, 0, 0,
           {{MakeReturnMergingInfo(36, 102, 0, {{{31, 44}, {32, 11}}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // There is not a mapping for id %31 (float OpPhi instruction in a loop merge
   // block) and no suitable id is available at the end of the entry block.
   ASSERT_FALSE(TransformationMergeFunctionReturns(
-                   16, 100, 101, 0, 0,
+                   16, 100, 200, 101, 0, 0,
                    {{MakeReturnMergingInfo(36, 102, 0, {{{32, 11}}})}})
                    .IsApplicable(context.get(), transformation_context));
 
@@ -468,7 +479,7 @@
   // available at the end of the entry block.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          16, 100, 101, 0, 0,
+          16, 100, 200, 101, 0, 0,
           {{MakeReturnMergingInfo(36, 102, 0, {{{31, 1000}, {32, 11}}})}})
           .IsApplicable(context.get(), transformation_context));
 }
@@ -560,7 +571,7 @@
 
   // The 0s are allowed because the function's return type is void.
   auto transformation1 =
-      TransformationMergeFunctionReturns(14, 100, 101, 0, 0, {{}});
+      TransformationMergeFunctionReturns(14, 100, 200, 101, 0, 0, {{}});
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation1, context.get(),
@@ -570,13 +581,14 @@
 
   // %12 is available at the end of the entry block of %19 (it is a global
   // variable).
-  ASSERT_TRUE(TransformationMergeFunctionReturns(19, 110, 111, 112, 12, {{}})
-                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(
+      TransformationMergeFunctionReturns(19, 110, 210, 111, 112, 12, {{}})
+          .IsApplicable(context.get(), transformation_context));
 
   // %1000 cannot be found in the module, but there is a suitable id available
   // at the end of the entry block (%12).
   auto transformation2 =
-      TransformationMergeFunctionReturns(19, 110, 111, 112, 1000, {{}});
+      TransformationMergeFunctionReturns(19, 110, 210, 111, 112, 1000, {{}});
   ASSERT_TRUE(
       transformation2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation2, context.get(),
@@ -586,13 +598,14 @@
 
   // %27 is available at the end of the entry block of %26 (it is a function
   // parameter).
-  ASSERT_TRUE(TransformationMergeFunctionReturns(26, 120, 121, 122, 27, {{}})
-                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(
+      TransformationMergeFunctionReturns(26, 120, 220, 121, 122, 27, {{}})
+          .IsApplicable(context.get(), transformation_context));
 
   // %1000 cannot be found in the module, but there is a suitable id available
   // at the end of the entry block (%27).
   auto transformation3 =
-      TransformationMergeFunctionReturns(26, 120, 121, 122, 1000, {{}});
+      TransformationMergeFunctionReturns(26, 120, 220, 121, 122, 1000, {{}});
   ASSERT_TRUE(
       transformation3.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation3, context.get(),
@@ -602,13 +615,14 @@
 
   // %35 is available at the end of the entry block of %33 (it is in the entry
   // block).
-  ASSERT_TRUE(TransformationMergeFunctionReturns(26, 130, 131, 132, 27, {{}})
-                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(
+      TransformationMergeFunctionReturns(26, 130, 230, 131, 132, 27, {{}})
+          .IsApplicable(context.get(), transformation_context));
 
   // %1000 cannot be found in the module, but there is a suitable id available
   // at the end of the entry block (%35).
   auto transformation4 =
-      TransformationMergeFunctionReturns(33, 130, 131, 132, 1000, {{}});
+      TransformationMergeFunctionReturns(33, 130, 230, 131, 132, 1000, {{}});
   ASSERT_TRUE(
       transformation4.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation4, context.get(),
@@ -642,8 +656,8 @@
          %15 = OpLabel
                OpBranch %100
         %100 = OpLabel
-               OpLoopMerge %101 %100 None
-               OpBranchConditional %11 %16 %100
+               OpLoopMerge %101 %200 None
+               OpBranch %16
          %16 = OpLabel
                OpSelectionMerge %17 None
                OpBranchConditional %11 %18 %17
@@ -653,13 +667,15 @@
                OpBranch %101
         %101 = OpLabel
                OpReturn
+        %200 = OpLabel
+               OpBranch %100
                OpFunctionEnd
          %19 = OpFunction %5 None %6
          %20 = OpLabel
                OpBranch %110
         %110 = OpLabel
-               OpLoopMerge %111 %110 None
-               OpBranchConditional %11 %21 %110
+               OpLoopMerge %111 %210 None
+               OpBranch %21
          %21 = OpLabel
                OpSelectionMerge %22 None
                OpBranchConditional %11 %23 %24
@@ -673,14 +689,16 @@
         %111 = OpLabel
         %112 = OpPhi %5 %12 %23 %25 %24
                OpReturnValue %112
+        %210 = OpLabel
+               OpBranch %110
                OpFunctionEnd
          %26 = OpFunction %7 None %8
          %27 = OpFunctionParameter %7
          %28 = OpLabel
                OpBranch %120
         %120 = OpLabel
-               OpLoopMerge %121 %120 None
-               OpBranchConditional %11 %29 %120
+               OpLoopMerge %121 %220 None
+               OpBranch %29
          %29 = OpLabel
                OpSelectionMerge %30 None
                OpBranchConditional %11 %31 %30
@@ -692,14 +710,16 @@
         %121 = OpLabel
         %122 = OpPhi %7 %27 %30 %32 %31
                OpReturnValue %122
+        %220 = OpLabel
+               OpBranch %120
                OpFunctionEnd
          %33 = OpFunction %7 None %9
          %34 = OpLabel
          %35 = OpConvertSToF %7 %12
                OpBranch %130
         %130 = OpLabel
-               OpLoopMerge %131 %130 None
-               OpBranchConditional %11 %36 %130
+               OpLoopMerge %131 %230 None
+               OpBranch %36
          %36 = OpLabel
                OpSelectionMerge %37 None
                OpBranchConditional %11 %38 %37
@@ -711,6 +731,8 @@
         %131 = OpLabel
         %132 = OpPhi %7 %35 %37 %39 %38
                OpReturnValue %132
+        %230 = OpLabel
+               OpBranch %130
                OpFunctionEnd
 )";
 
@@ -743,8 +765,8 @@
          %15 = OpLabel
                OpBranch %16
          %16 = OpLabel
-               OpLoopMerge %17 %16 None
-               OpBranchConditional %8 %18 %16
+               OpLoopMerge %17 %916 None
+               OpBranch %18
          %18 = OpLabel
                OpLoopMerge %19 %20 None
                OpBranchConditional %8 %19 %21
@@ -767,8 +789,8 @@
          %28 = OpLabel
                OpBranch %29
          %29 = OpLabel
-               OpLoopMerge %30 %29 None
-               OpBranchConditional %8 %30 %29
+               OpLoopMerge %30 %929 None
+               OpBranch %30
          %30 = OpLabel
                OpLoopMerge %31 %32 None
                OpBranch %33
@@ -792,6 +814,10 @@
                OpBranch %38
          %38 = OpLabel
                OpReturnValue %12
+        %916 = OpLabel
+               OpBranch %16
+        %929 = OpLabel
+               OpBranch %29
                OpFunctionEnd
 )";
 
@@ -805,7 +831,7 @@
       MakeUnique<FactManager>(context.get()), validator_options);
 
   auto transformation = TransformationMergeFunctionReturns(
-      14, 100, 101, 102, 11,
+      14, 100, 200, 101, 102, 11,
       {{MakeReturnMergingInfo(19, 103, 104, {{}}),
         MakeReturnMergingInfo(17, 105, 106, {{}}),
         MakeReturnMergingInfo(31, 107, 108, {{{35, 10}, {36, 12}}}),
@@ -841,11 +867,11 @@
          %15 = OpLabel
                OpBranch %100
         %100 = OpLabel
-               OpLoopMerge %101 %100 None
-               OpBranchConditional %8 %16 %100
+               OpLoopMerge %101 %200 None
+               OpBranch %16
          %16 = OpLabel
-               OpLoopMerge %17 %16 None
-               OpBranchConditional %8 %18 %16
+               OpLoopMerge %17 %916 None
+               OpBranch %18
          %18 = OpLabel
                OpLoopMerge %19 %20 None
                OpBranchConditional %8 %19 %21
@@ -872,8 +898,8 @@
          %28 = OpLabel
                OpBranch %29
          %29 = OpLabel
-               OpLoopMerge %30 %29 None
-               OpBranchConditional %8 %30 %29
+               OpLoopMerge %30 %929 None
+               OpBranch %30
          %30 = OpLabel
                OpLoopMerge %31 %32 None
                OpBranch %33
@@ -901,9 +927,15 @@
                OpBranchConditional %109 %101 %38
          %38 = OpLabel
                OpBranch %101
+        %916 = OpLabel
+               OpBranch %16
+        %929 = OpLabel
+               OpBranch %29
         %101 = OpLabel
         %102 = OpPhi %5 %106 %17 %110 %23 %12 %38
                OpReturnValue %102
+        %200 = OpLabel
+               OpBranch %100
                OpFunctionEnd
 )";
 
@@ -997,7 +1029,7 @@
   // No mapping from merge block %16 to fresh ids is given, so overflow ids are
   // needed.
   auto transformation1 =
-      TransformationMergeFunctionReturns(12, 100, 101, 102, 10, {{}});
+      TransformationMergeFunctionReturns(12, 100, 200, 101, 102, 10, {{}});
 
 #ifndef NDEBUG
   ASSERT_DEATH(
@@ -1016,7 +1048,7 @@
   // No mapping from merge block %27 to fresh ids is given, so overflow ids are
   // needed.
   auto transformation2 =
-      TransformationMergeFunctionReturns(24, 110, 111, 0, 0, {{}});
+      TransformationMergeFunctionReturns(24, 110, 210, 111, 0, 0, {{}});
 
 #ifndef NDEBUG
   ASSERT_DEATH(
@@ -1054,8 +1086,8 @@
          %13 = OpLabel
                OpBranch %100
         %100 = OpLabel
-               OpLoopMerge %101 %100 None
-               OpBranchConditional %8 %14 %100
+               OpLoopMerge %101 %200 None
+               OpBranch %14
          %14 = OpLabel
          %15 = OpIAdd %5 %10 %10
                OpLoopMerge %16 %17 None
@@ -1081,13 +1113,15 @@
         %101 = OpLabel
         %102 = OpPhi %5 %1001 %16 %22 %23
                OpReturnValue %102
+        %200 = OpLabel
+               OpBranch %100
                OpFunctionEnd
          %24 = OpFunction %3 None %4
          %25 = OpLabel
                OpBranch %110
         %110 = OpLabel
-               OpLoopMerge %111 %110 None
-               OpBranchConditional %8 %26 %110
+               OpLoopMerge %111 %210 None
+               OpBranch %26
          %26 = OpLabel
                OpLoopMerge %27 %28 None
                OpBranch %29
@@ -1110,6 +1144,8 @@
                OpBranch %111
         %111 = OpLabel
                OpReturn
+        %210 = OpLabel
+               OpBranch %110
                OpFunctionEnd
 )";
 
@@ -1179,7 +1215,7 @@
   // corresponding mapping.
 
   auto transformation = TransformationMergeFunctionReturns(
-      12, 101, 102, 0, 0,
+      12, 101, 200, 102, 0, 0,
       {{MakeReturnMergingInfo(17, 103, 0, {{{25, 7}, {35, 8}}})}});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
@@ -1212,8 +1248,8 @@
          %15 = OpConvertSToF %10 %13
                OpBranch %101
         %101 = OpLabel
-               OpLoopMerge %102 %101 None
-               OpBranchConditional %6 %16 %101
+               OpLoopMerge %102 %200 None
+               OpBranch %16
          %16 = OpLabel
                OpLoopMerge %17 %18 None
                OpBranch %19
@@ -1238,6 +1274,8 @@
                OpBranch %102
         %102 = OpLabel
                OpReturn
+        %200 = OpLabel
+               OpBranch %101
                OpFunctionEnd
 )";
 
@@ -1324,14 +1362,14 @@
   // block %14 is added.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}})
+          2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // In function %18, The definition of id %26 will still dominate its use in
   // instruction %27 (inside merge block %21), because %27 is an OpPhi
   // instruction.
   auto transformation = TransformationMergeFunctionReturns(
-      18, 100, 101, 0, 0, {{MakeReturnMergingInfo(21, 102, 103, {{}})}});
+      18, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(21, 102, 103, {{}})}});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -1376,8 +1414,8 @@
          %19 = OpLabel
                OpBranch %100
         %100 = OpLabel
-               OpLoopMerge %101 %100 None
-               OpBranchConditional %6 %20 %100
+               OpLoopMerge %101 %200 None
+               OpBranch %20
          %20 = OpLabel
                OpLoopMerge %21 %22 None
                OpBranch %23
@@ -1399,6 +1437,8 @@
                OpBranch %101
         %101 = OpLabel
                OpReturn
+        %200 = OpLabel
+               OpBranch %100
                OpFunctionEnd
 )";
 
@@ -1482,17 +1522,17 @@
   // block %14 to merge block %10 is added.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}})
+          2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // In function %18, the definition of id %26 will not dominate its use in
   // instruction %28 (inside block %27) after a new branch from return
   // block %25 to merge block %21 is added.
-  ASSERT_FALSE(TransformationMergeFunctionReturns(
-                   2, 100, 101, 0, 0,
-                   {{MakeReturnMergingInfo(10, 102, 0, {{}}),
-                     MakeReturnMergingInfo(21, 103, 0, {{}})}})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationMergeFunctionReturns(
+          2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 0, {{}}),
+                                    MakeReturnMergingInfo(21, 103, 0, {{}})}})
+          .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules3) {
@@ -1556,7 +1596,7 @@
   // fact that the id definition dominates the uses does not depend on it
   // dominating the merge block.
   auto transformation = TransformationMergeFunctionReturns(
-      2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}});
+      2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -1579,8 +1619,8 @@
           %8 = OpLabel
                OpBranch %100
         %100 = OpLabel
-               OpLoopMerge %101 %100 None
-               OpBranchConditional %6 %9 %100
+               OpLoopMerge %101 %200 None
+               OpBranch %9
           %9 = OpLabel
                OpLoopMerge %10 %11 None
                OpBranch %12
@@ -1608,6 +1648,8 @@
                OpBranch %101
         %101 = OpLabel
                OpReturn
+        %200 = OpLabel
+               OpBranch %100
                OpFunctionEnd
 )";
 
@@ -1700,7 +1742,7 @@
   // instruction %19 after the transformation is applied, because %13 dominates
   // all of the return blocks.
   auto transformation = TransformationMergeFunctionReturns(
-      2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}});
+      2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -1710,10 +1752,10 @@
   // In function %20, the definition of id %28 will not dominate its use in
   // instruction %32 after the transformation is applied, because %28 dominates
   // only one of the return blocks.
-  ASSERT_FALSE(
-      TransformationMergeFunctionReturns(
-          20, 100, 101, 0, 0, {{MakeReturnMergingInfo(23, 102, 103, {{}})}})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationMergeFunctionReturns(
+                   20, 100, 200, 101, 0, 0,
+                   {{MakeReturnMergingInfo(23, 102, 103, {{}})}})
+                   .IsApplicable(context.get(), transformation_context));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -1731,8 +1773,8 @@
           %8 = OpLabel
                OpBranch %100
         %100 = OpLabel
-               OpLoopMerge %101 %100 None
-               OpBranchConditional %6 %9 %100
+               OpLoopMerge %101 %200 None
+               OpBranch %9
           %9 = OpLabel
                OpLoopMerge %10 %11 None
                OpBranch %12
@@ -1759,6 +1801,8 @@
                OpBranch %101
         %101 = OpLabel
                OpReturn
+        %200 = OpLabel
+               OpBranch %100
                OpFunctionEnd
          %20 = OpFunction %3 None %4
          %21 = OpLabel
@@ -1830,7 +1874,7 @@
       MakeUnique<FactManager>(context.get()), validator_options);
 
   auto transformation =
-      TransformationMergeFunctionReturns(2, 100, 101, 0, 0, {});
+      TransformationMergeFunctionReturns(2, 100, 200, 101, 0, 0, {});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -1863,8 +1907,8 @@
           %8 = OpLabel
                OpBranch %100
         %100 = OpLabel
-               OpLoopMerge %101 %100 None
-               OpBranchConditional %6 %9 %100
+               OpLoopMerge %101 %200 None
+               OpBranch %9
           %9 = OpLabel
          %10 = OpPhi %5 %6 %100
                OpSelectionMerge %11 None
@@ -1875,6 +1919,8 @@
                OpBranch %101
         %101 = OpLabel
                OpReturn
+        %200 = OpLabel
+               OpBranch %100
                OpFunctionEnd
 )";