[spirv-reader] Refactor emission for OpBranch

Bug: tint:3
Change-Id: If1d603990f133f8ba0b3a305f7f7f3db4623e9ce
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/22602
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index f826786..d91c7e5 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -399,7 +399,9 @@
     std::unique_ptr<ast::Statement> statement) {
   assert(!statements_stack_.empty());
   auto* result = statement.get();
-  statements_stack_.back().statements.emplace_back(std::move(statement));
+  if (result != nullptr) {
+    statements_stack_.back().statements.emplace_back(std::move(statement));
+  }
   return result;
 }
 
@@ -1789,37 +1791,9 @@
       }
       return true;
     case SpvOpBranch: {
-      const auto dest = terminator.GetSingleWordInOperand(0);
-      const auto kind = block_info.succ_edge.find(dest)->second;
-      switch (kind) {
-        case EdgeKind::kBack:
-          // Nothing to do. The loop backedge is implicit.
-          return true;
-        case EdgeKind::kSwitchBreak:
-        case EdgeKind::kLoopBreak:
-          AddStatement(std::make_unique<ast::BreakStatement>());
-          return true;
-        case EdgeKind::kLoopContinue:
-          // An unconditional continue to the next block is redundant and ugly.
-          // Skip it in that case.
-          if (GetBlockInfo(dest)->pos == 1 + block_info.pos) {
-            return true;
-          }
-          // Otherwise, emit a regular continue statement.
-          AddStatement(std::make_unique<ast::ContinueStatement>());
-          return true;
-        case EdgeKind::kIfBreak:
-          // For an unconditional branch, the break out to an if-selection
-          // merge block is implicit.
-          return true;
-        case EdgeKind::kCaseFallThrough:
-          AddStatement(std::make_unique<ast::FallthroughStatement>());
-          return true;
-        case EdgeKind::kForward:
-          // Unconditional forward branch is implicit.
-          return true;
-      }
-      break;
+      const auto dest_id = terminator.GetSingleWordInOperand(0);
+      AddStatement(MakeBranch(block_info, *GetBlockInfo(dest_id)));
+      return true;
     }
     default:
       break;
@@ -1828,6 +1802,38 @@
   return success();
 }
 
+std::unique_ptr<ast::Statement> FunctionEmitter::MakeBranch(
+    const BlockInfo& src_info,
+    const BlockInfo& dest_info) const {
+  auto kind = src_info.succ_edge.find(dest_info.id)->second;
+  switch (kind) {
+    case EdgeKind::kBack:
+      // Nothing to do. The loop backedge is implicit.
+      break;
+    case EdgeKind::kSwitchBreak:
+    case EdgeKind::kLoopBreak:
+      return std::make_unique<ast::BreakStatement>();
+    case EdgeKind::kLoopContinue:
+      // An unconditional continue to the next block is redundant and ugly.
+      // Skip it in that case.
+      if (dest_info.pos == 1 + src_info.pos) {
+        break;
+      }
+      // Otherwise, emit a regular continue statement.
+      return std::make_unique<ast::ContinueStatement>();
+    case EdgeKind::kIfBreak:
+      // For an unconditional branch, the break out to an if-selection
+      // merge block is implicit.
+      break;
+    case EdgeKind::kCaseFallThrough:
+      return std::make_unique<ast::FallthroughStatement>();
+    case EdgeKind::kForward:
+      // Unconditional forward branch is implicit.
+      break;
+  }
+  return {nullptr};
+}
+
 bool FunctionEmitter::EmitStatementsInBasicBlock(const BlockInfo& block_info,
                                                  bool* already_emitted) {
   if (*already_emitted) {
diff --git a/src/reader/spirv/function.h b/src/reader/spirv/function.h
index f53dbef..f37f23f 100644
--- a/src/reader/spirv/function.h
+++ b/src/reader/spirv/function.h
@@ -329,6 +329,14 @@
   /// @returns false if emission failed
   bool EmitNormalTerminator(const BlockInfo& block_info);
 
+  /// Returns a new statement to represent the given branch representing a
+  /// "normal" terminator, as in the sense of EmitNormalTerminator.  If no
+  /// WGSL statement is required, the statement will nullptr.
+  /// @param src_info the source block
+  /// @param dest_info the destination block
+  std::unique_ptr<ast::Statement> MakeBranch(const BlockInfo& src_info,
+                                             const BlockInfo& dest_info) const;
+
   /// Emits a normal instruction: not a terminator, label, or variable
   /// declaration.
   /// @param inst the instruction
@@ -403,6 +411,7 @@
   BlockInfo* HeaderIfBreakable(const Construct* c);
 
   /// Appends a new statement to the top of the statement stack.
+  /// Does nothing if the statement is null.
   /// @param statement the new statement
   /// @returns a pointer to the statement.
   ast::Statement* AddStatement(std::unique_ptr<ast::Statement> statement);