[spirv-reader][ir] Ignore continuing blocks without inbound branches.

If there are no branches into the continuing block, we can skip emitting
it. This works around an issue in SPIR-V where the continuing with no
in-bound branches can reference IDs from any of the blocks above it.
This makes it really hard to propagate the value up to the continuing
block.

Bug: 42250952
Change-Id: If855dbb4a743a93ea0adc3b72dd87bbc1cef8481
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/246274
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: 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 f1d14c9..b7a97fd 100644
--- a/src/tint/lang/spirv/reader/parser/branch_test.cc
+++ b/src/tint/lang/spirv/reader/parser/branch_test.cc
@@ -3872,11 +3872,10 @@
         unreachable
       }
       $B3: {  # continuing
-        %4:i32 = spirv.add<i32> 2i, 2i
         next_iteration  # -> $B2
       }
     }
-    %5:i32 = spirv.add<i32> 3i, 3i
+    %4:i32 = spirv.add<i32> 3i, 3i
     ret
   }
 }
@@ -4109,7 +4108,6 @@
         exit_loop  # loop_1
       }
       $B3: {  # continuing
-        %3:i32 = spirv.add<i32> 2i, 2i
         next_iteration  # -> $B2
       }
     }
@@ -4602,7 +4600,6 @@
         exit_loop  # loop_1
       }
       $B3: {  # continuing
-        %3:i32 = spirv.add<i32> 2i, 2i
         next_iteration  # -> $B2
       }
     }
@@ -5649,11 +5646,10 @@
         unreachable
       }
       $B3: {  # continuing
-        %5:i32 = spirv.add<i32> 3i, 3i
         next_iteration  # -> $B2
       }
     }
-    %6:i32 = spirv.add<i32> 1i, 3i
+    %5:i32 = spirv.add<i32> 1i, 3i
     ret
   }
 }
@@ -5713,11 +5709,10 @@
         unreachable
       }
       $B3: {  # continuing
-        %6:i32 = spirv.add<i32> 1i, 2i
         next_iteration  # -> $B2
       }
     }
-    %7:i32 = spirv.add<i32> 1i, 3i
+    %6:i32 = spirv.add<i32> 1i, 3i
     ret
   }
 }
@@ -7133,6 +7128,73 @@
 }
 )");
 }
+TEST_F(SpirvParserTest, ConvertHoisted) {
+    EXPECT_IR(R"(
+               OpCapability Shader
+               OpMemoryModel Logical Simple
+               OpEntryPoint Fragment %100 "main"
+               OpExecutionMode %100 OriginUpperLeft
+       %void = OpTypeVoid
+          %2 = OpTypeFunction %void
+       %bool = OpTypeBool
+       %uint = OpTypeInt 32 0
+      %float = OpTypeFloat 32
+       %true = OpConstantTrue %bool
+   %float_50 = OpConstant %float 50
+        %100 = OpFunction %void None %2
+         %10 = OpLabel
+               OpBranch %30
+         %30 = OpLabel
+               OpLoopMerge %90 %80 None
+               OpBranchConditional %true %90 %40
+         %40 = OpLabel
+               OpSelectionMerge %50 None
+               OpBranchConditional %true %45 %50
+         %45 = OpLabel
+        %600 = OpCopyObject %float %float_50
+               OpBranch %50
+         %50 = OpLabel
+               OpBranch %90
+         %80 = OpLabel
+         %82 = OpConvertFToU %uint %600
+               OpBranch %30
+         %90 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)",
+              R"(
+%main = @fragment func():void {
+  $B1: {
+    loop [b: $B2, c: $B3] {  # loop_1
+      $B2: {  # body
+        if true [t: $B4, f: $B5] {  # if_1
+          $B4: {  # true
+            exit_loop  # loop_1
+          }
+          $B5: {  # false
+            if true [t: $B6, f: $B7] {  # if_2
+              $B6: {  # true
+                %2:f32 = let 50.0f
+                exit_if  # if_2
+              }
+              $B7: {  # false
+                exit_if  # if_2
+              }
+            }
+            exit_loop  # loop_1
+          }
+        }
+        unreachable
+      }
+      $B3: {  # continuing
+        next_iteration  # -> $B2
+      }
+    }
+    ret
+  }
+}
+)");
+}
 
 }  // namespace
 }  // namespace tint::spirv::reader
diff --git a/src/tint/lang/spirv/reader/parser/parser.cc b/src/tint/lang/spirv/reader/parser/parser.cc
index a725e62..45482cf 100644
--- a/src/tint/lang/spirv/reader/parser/parser.cc
+++ b/src/tint/lang/spirv/reader/parser/parser.cc
@@ -1300,7 +1300,7 @@
         }
     }
 
-    bool InBlock(core::ir::Block* blk) { return current_blocks_.count(blk) > 0; }
+    bool InBlock(core::ir::Block* blk) { return current_blocks_.contains(blk); }
 
     // A block parent is a container for a scope, like a `{}`d section in code. It controls the
     // block addition to the current blocks and the ID stack entry for the block.
@@ -1774,7 +1774,14 @@
             id_stack_.emplace_back();
 
             auto continue_id = loop_merge_inst->GetSingleWordInOperand(1);
-            if (continue_id != src.id()) {
+
+            // We only need to emit the continuing block if:
+            //  a) It is not the loop header
+            //  b) It has inbound branches. This works around a case where you can have a continuing
+            //     where uses values which are very difficult to propagate, but the continuing is
+            //     never reached anyway, so the propagation is useless.
+            if (continue_id != src.id() &&
+                !loop->Continuing()->InboundSiblingBranches().IsEmpty()) {
                 const auto& bb_continue = current_spirv_function_->FindBlock(continue_id);
 
                 current_blocks_.insert(loop->Continuing());