Fix check for unreachable blocks in merge-return (#2762)

Merge return expects unreachable merge block to look a certain way, and
unreachable continue blocks to look a certain way.  What if an
unreachable block is both a merge and a continue?  The continue is
suppose to take precedent, but merge-return implements it with the merge
taking precedent.  This change flips that around.

Fixes #2746
diff --git a/source/opt/merge_return_pass.cpp b/source/opt/merge_return_pass.cpp
index b938b2f..18c49f5 100644
--- a/source/opt/merge_return_pass.cpp
+++ b/source/opt/merge_return_pass.cpp
@@ -820,12 +820,7 @@
 
     StructuredCFGAnalysis* struct_cfg_analysis =
         context()->GetStructuredCFGAnalysis();
-    if (struct_cfg_analysis->IsMergeBlock(bb.id())) {
-      // |bb| must be an empty block ending with OpUnreachable.
-      if (bb.begin()->opcode() != SpvOpUnreachable) {
-        return true;
-      }
-    } else if (struct_cfg_analysis->IsContinueBlock(bb.id())) {
+    if (struct_cfg_analysis->IsContinueBlock(bb.id())) {
       // |bb| must be an empty block ending with a branch to the header.
       Instruction* inst = &*bb.begin();
       if (inst->opcode() != SpvOpBranch) {
@@ -836,6 +831,11 @@
           struct_cfg_analysis->ContainingLoop(bb.id())) {
         return true;
       }
+    } else if (struct_cfg_analysis->IsMergeBlock(bb.id())) {
+      // |bb| must be an empty block ending with OpUnreachable.
+      if (bb.begin()->opcode() != SpvOpUnreachable) {
+        return true;
+      }
     } else {
       return true;
     }
diff --git a/test/opt/pass_merge_return_test.cpp b/test/opt/pass_merge_return_test.cpp
index baf7820..a305680 100644
--- a/test/opt/pass_merge_return_test.cpp
+++ b/test/opt/pass_merge_return_test.cpp
@@ -1779,6 +1779,7 @@
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<MergeReturnPass>(text, true);
 }
+
 TEST_F(MergeReturnPassTest, PhiInSecondMerge) {
   //  Add and use a phi in the second merge block from the return.
   const std::string text =
@@ -1831,6 +1832,50 @@
   SinglePassRunAndMatch<MergeReturnPass>(text, true);
 }
 
+TEST_F(MergeReturnPassTest, UnreachableMergeAndContinue) {
+  // Make sure that the pass can handle a single block that is both a merge and
+  // a continue.
+  const std::string text =
+      R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 310
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+          %2 = OpFunction %void None %4
+          %7 = OpLabel
+               OpBranch %8
+          %8 = OpLabel
+               OpLoopMerge %9 %10 None
+               OpBranch %11
+         %11 = OpLabel
+               OpSelectionMerge %10 None
+               OpBranchConditional %true %12 %13
+         %12 = OpLabel
+               OpReturn
+         %13 = OpLabel
+               OpReturn
+         %10 = OpLabel
+               OpBranch %8
+          %9 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  auto result = SinglePassRunAndDisassemble<MergeReturnPass>(text, true, true);
+
+  // Not looking for any particular output.  Other tests do that.
+  // Just want to make sure the check for unreachable blocks does not emit an
+  // error.
+  EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools