[spirv-reader][ir] Fix issue where true branches to false

In the case where one branch from `OpBranchConditional` branches to the
other branch we were incorrectly determining that there was no
pre-merge. In this case, we need to treat the entire branch as the
premerge.

Bug: 391487552
Change-Id: Id6b86ae8f5312fb974c929c49bcbacc1c559b635
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/230454
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/lang/spirv/reader/parser/branch_test.cc b/src/tint/lang/spirv/reader/parser/branch_test.cc
index 0760a87..2c004cb 100644
--- a/src/tint/lang/spirv/reader/parser/branch_test.cc
+++ b/src/tint/lang/spirv/reader/parser/branch_test.cc
@@ -400,7 +400,7 @@
 )");
 }
 
-TEST_F(SpirvParserTest, DISABLED_BranchConditional_TrueBranchesToFalse) {
+TEST_F(SpirvParserTest, BranchConditional_TrueBranchesToFalse) {
     EXPECT_IR(R"(
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -412,12 +412,12 @@
         %one = OpConstant %i32 1
         %two = OpConstant %i32 2
       %three = OpConstant %i32 3
-       %true = OpConstantTrue %bool
+      %false = OpConstantFalse %bool
     %ep_type = OpTypeFunction %void
        %main = OpFunction %void None %ep_type
          %20 = OpLabel
                OpSelectionMerge %99 None
-               OpBranchConditional %true %40 %30
+               OpBranchConditional %false %40 %30
          %30 = OpLabel
          %50 = OpIAdd %i32 %one %one
                OpReturn
@@ -431,7 +431,7 @@
               R"(
 %main = @compute @workgroup_size(1u, 1u, 1u) func():void {
   $B1: {
-    if true [t: $B2, f: $B3] {  # if_1
+    if false [t: $B2, f: $B3] {  # if_1
       $B2: {  # true
         %2:i32 = spirv.add<i32> 2i, 2i
         exit_if  # if_1
@@ -440,7 +440,15 @@
         exit_if  # if_1
       }
     }
-    %3:i32 = spirv.add<i32> 1i, 1i
+    if true [t: $B4, f: $B5] {  # if_2
+      $B4: {  # true
+        %3:i32 = spirv.add<i32> 1i, 1i
+        ret
+      }
+      $B5: {  # false
+        unreachable
+      }
+    }
     ret
   }
 }
diff --git a/src/tint/lang/spirv/reader/parser/parser.cc b/src/tint/lang/spirv/reader/parser/parser.cc
index 4c3fcc5..a364a3f 100644
--- a/src/tint/lang/spirv/reader/parser/parser.cc
+++ b/src/tint/lang/spirv/reader/parser/parser.cc
@@ -1190,15 +1190,30 @@
             current_spirv_function_, &*(current_spirv_function_->FindBlock(false_id)),
             &*(current_spirv_function_->FindBlock(merge_id.value())), &false_blocks);
 
-        // It's possible one of the blocks returns, so bail early if they don't both end at the
-        // merge.
-        if (true_blocks.back()->id() != merge_id || false_blocks.back()->id() != merge_id) {
+        auto& true_end = true_blocks.back();
+        auto& false_end = false_blocks.back();
+
+        // We only consider the block as returning if it didn't return through
+        // the merge block. (I.e. it's a direct exit from inside the branch
+        // itself.
+        bool true_returns = true_end->id() != merge_id && true_end->IsReturn();
+        bool false_returns = false_end->id() != merge_id && false_end->IsReturn();
+        // If one of the blocks returns but the other doesn't, then we can't
+        // have a premerge block.
+        if (true_returns != false_returns) {
             return std::nullopt;
         }
 
-        // Remove the merge blocks.
-        true_blocks.pop_back();
-        false_blocks.pop_back();
+        // If they don't return, both blocks must merge to the same place.
+        if (!true_returns && (true_end->id() != false_end->id())) {
+            return std::nullopt;
+        }
+
+        // If these aren't returns, then remove the merge blocks.
+        if (!true_returns) {
+            true_blocks.pop_back();
+            false_blocks.pop_back();
+        }
 
         std::optional<uint32_t> id = std::nullopt;
         while (!true_blocks.empty() && !false_blocks.empty()) {