[ir] Allow branching arguments.

When branching to a different flow node, there is a need to pass
arguments to the branch. These arguments could be the value of the
return.

This extracts a `Branch` out to an object with a target and arguments
and then updates the IR to use the new Branch structure.

Bug: tint:1718
Change-Id: Ic8de8046f58056327a04c8afe3b597810c80ccdb
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/116546
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/ir/block.h b/src/tint/ir/block.h
index ac9028b..02e81a5 100644
--- a/src/tint/ir/block.h
+++ b/src/tint/ir/block.h
@@ -15,6 +15,7 @@
 #ifndef SRC_TINT_IR_BLOCK_H_
 #define SRC_TINT_IR_BLOCK_H_
 
+#include "src/tint/ir/branch.h"
 #include "src/tint/ir/flow_node.h"
 #include "src/tint/ir/instruction.h"
 #include "src/tint/utils/vector.h"
@@ -31,7 +32,7 @@
     ~Block() override;
 
     /// The node this block branches too.
-    const FlowNode* branch_target = nullptr;
+    Branch branch = {};
 
     /// The instructions in the block
     utils::Vector<const Instruction*, 16> instructions;
diff --git a/src/tint/ir/branch.h b/src/tint/ir/branch.h
new file mode 100644
index 0000000..4fc8d7c
--- /dev/null
+++ b/src/tint/ir/branch.h
@@ -0,0 +1,36 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_IR_BRANCH_H_
+#define SRC_TINT_IR_BRANCH_H_
+
+#include "src/tint/ir/flow_node.h"
+#include "src/tint/ir/value.h"
+
+namespace tint::ir {
+
+/// A information on a branch to another block
+struct Branch {
+    /// The block being branched too.
+    FlowNode* target = nullptr;
+
+    /// The arguments provided for that branch. These arguments could be the
+    /// return value in the case of a branch to the terminator, or they could
+    /// be the basic block arguments passed into the block.
+    utils::Vector<Value*, 2> args;
+};
+
+}  // namespace tint::ir
+
+#endif  // SRC_TINT_IR_BRANCH_H_
diff --git a/src/tint/ir/builder.cc b/src/tint/ir/builder.cc
index 0f43e23..1452dc4 100644
--- a/src/tint/ir/builder.cc
+++ b/src/tint/ir/builder.cc
@@ -47,48 +47,49 @@
 
 If* Builder::CreateIf() {
     auto* ir_if = ir.flow_nodes.Create<If>();
-    ir_if->true_target = CreateBlock();
-    ir_if->false_target = CreateBlock();
-    ir_if->merge_target = CreateBlock();
+    ir_if->true_.target = CreateBlock();
+    ir_if->false_.target = CreateBlock();
+    ir_if->merge.target = CreateBlock();
 
     // An if always branches to both the true and false block.
-    ir_if->true_target->inbound_branches.Push(ir_if);
-    ir_if->false_target->inbound_branches.Push(ir_if);
+    ir_if->true_.target->inbound_branches.Push(ir_if);
+    ir_if->false_.target->inbound_branches.Push(ir_if);
 
     return ir_if;
 }
 
 Loop* Builder::CreateLoop() {
     auto* ir_loop = ir.flow_nodes.Create<Loop>();
-    ir_loop->start_target = CreateBlock();
-    ir_loop->continuing_target = CreateBlock();
-    ir_loop->merge_target = CreateBlock();
+    ir_loop->start.target = CreateBlock();
+    ir_loop->continuing.target = CreateBlock();
+    ir_loop->merge.target = CreateBlock();
 
     // A loop always branches to the start block.
-    ir_loop->start_target->inbound_branches.Push(ir_loop);
+    ir_loop->start.target->inbound_branches.Push(ir_loop);
 
     return ir_loop;
 }
 
 Switch* Builder::CreateSwitch() {
     auto* ir_switch = ir.flow_nodes.Create<Switch>();
-    ir_switch->merge_target = CreateBlock();
+    ir_switch->merge.target = CreateBlock();
     return ir_switch;
 }
 
 Block* Builder::CreateCase(Switch* s, utils::VectorRef<Switch::CaseSelector> selectors) {
-    s->cases.Push(Switch::Case{selectors, CreateBlock()});
+    s->cases.Push(Switch::Case{selectors, {CreateBlock(), utils::Empty}});
 
-    Block* b = s->cases.Back().start_target;
+    Block* b = s->cases.Back().start.target->As<Block>();
     // Switch branches into the case block
     b->inbound_branches.Push(s);
     return b;
 }
 
-void Builder::Branch(Block* from, FlowNode* to) {
+void Builder::Branch(Block* from, FlowNode* to, utils::VectorRef<Value*> args) {
     TINT_ASSERT(IR, from);
     TINT_ASSERT(IR, to);
-    from->branch_target = to;
+    from->branch.target = to;
+    from->branch.args = args;
     to->inbound_branches.Push(from);
 }
 
diff --git a/src/tint/ir/builder.h b/src/tint/ir/builder.h
index f760b5c..147e4c4 100644
--- a/src/tint/ir/builder.h
+++ b/src/tint/ir/builder.h
@@ -79,7 +79,8 @@
     /// Branches the given block to the given flow node.
     /// @param from the block to branch from
     /// @param to the node to branch too
-    void Branch(Block* from, FlowNode* to);
+    /// @param args arguments to the branch
+    void Branch(Block* from, FlowNode* to, utils::VectorRef<Value*> args);
 
     /// Creates a constant::Value
     /// @param args the arguments
diff --git a/src/tint/ir/builder_impl.cc b/src/tint/ir/builder_impl.cc
index cf7321d..dbcad5e 100644
--- a/src/tint/ir/builder_impl.cc
+++ b/src/tint/ir/builder_impl.cc
@@ -64,7 +64,7 @@
 };
 
 bool IsBranched(const Block* b) {
-    return b->branch_target != nullptr;
+    return b->branch.target != nullptr;
 }
 
 bool IsConnected(const FlowNode* b) {
@@ -92,11 +92,11 @@
 
 BuilderImpl::~BuilderImpl() = default;
 
-void BuilderImpl::BranchTo(FlowNode* node) {
+void BuilderImpl::BranchTo(FlowNode* node, utils::VectorRef<Value*> args) {
     TINT_ASSERT(IR, current_flow_block);
     TINT_ASSERT(IR, !IsBranched(current_flow_block));
 
-    builder.Branch(current_flow_block, node);
+    builder.Branch(current_flow_block, node, args);
     current_flow_block = nullptr;
 }
 
@@ -265,27 +265,27 @@
     {
         FlowStackScope scope(this, if_node);
 
-        current_flow_block = if_node->true_target;
+        current_flow_block = if_node->true_.target->As<Block>();
         if (!EmitStatement(stmt->body)) {
             return false;
         }
         // If the true branch did not execute control flow, then go to the merge target
-        BranchToIfNeeded(if_node->merge_target);
+        BranchToIfNeeded(if_node->merge.target);
 
-        current_flow_block = if_node->false_target;
+        current_flow_block = if_node->false_.target->As<Block>();
         if (stmt->else_statement && !EmitStatement(stmt->else_statement)) {
             return false;
         }
         // If the false branch did not execute control flow, then go to the merge target
-        BranchToIfNeeded(if_node->merge_target);
+        BranchToIfNeeded(if_node->merge.target);
     }
     current_flow_block = nullptr;
 
     // If both branches went somewhere, then they both returned, continued or broke. So,
     // there is no need for the if merge-block and there is nothing to branch to the merge
     // block anyway.
-    if (IsConnected(if_node->merge_target)) {
-        current_flow_block = if_node->merge_target;
+    if (IsConnected(if_node->merge.target)) {
+        current_flow_block = if_node->merge.target->As<Block>();
     }
 
     return true;
@@ -301,15 +301,15 @@
     {
         FlowStackScope scope(this, loop_node);
 
-        current_flow_block = loop_node->start_target;
+        current_flow_block = loop_node->start.target->As<Block>();
         if (!EmitStatement(stmt->body)) {
             return false;
         }
 
         // The current block didn't `break`, `return` or `continue`, go to the continuing block.
-        BranchToIfNeeded(loop_node->continuing_target);
+        BranchToIfNeeded(loop_node->continuing.target);
 
-        current_flow_block = loop_node->continuing_target;
+        current_flow_block = loop_node->continuing.target->As<Block>();
         if (stmt->continuing) {
             if (!EmitStatement(stmt->continuing)) {
                 return false;
@@ -317,13 +317,13 @@
         }
 
         // Branch back to the start node if the continue target didn't branch out already
-        BranchToIfNeeded(loop_node->start_target);
+        BranchToIfNeeded(loop_node->start.target);
     }
 
     // The loop merge can get disconnected if the loop returns directly, or the continuing target
     // branches, eventually, to the merge, but nothing branched to the continuing target.
-    current_flow_block = loop_node->merge_target;
-    if (!IsConnected(loop_node->merge_target)) {
+    current_flow_block = loop_node->merge.target->As<Block>();
+    if (!IsConnected(loop_node->merge.target)) {
         current_flow_block = nullptr;
     }
     return true;
@@ -332,7 +332,9 @@
 bool BuilderImpl::EmitWhile(const ast::WhileStatement* stmt) {
     auto* loop_node = builder.CreateLoop();
     // Continue is always empty, just go back to the start
-    builder.Branch(loop_node->continuing_target, loop_node->start_target);
+    TINT_ASSERT(IR, loop_node->continuing.target->Is<Block>());
+    builder.Branch(loop_node->continuing.target->As<Block>(), loop_node->start.target,
+                   utils::Empty);
 
     BranchTo(loop_node);
 
@@ -341,7 +343,7 @@
     {
         FlowStackScope scope(this, loop_node);
 
-        current_flow_block = loop_node->start_target;
+        current_flow_block = loop_node->start.target->As<Block>();
 
         // Emit the while condition into the start target of the loop
         auto reg = EmitExpression(stmt->condition);
@@ -351,28 +353,33 @@
 
         // Create an `if (cond) {} else {break;}` control flow
         auto* if_node = builder.CreateIf();
-        builder.Branch(if_node->true_target, if_node->merge_target);
-        builder.Branch(if_node->false_target, loop_node->merge_target);
+        TINT_ASSERT(IR, if_node->true_.target->Is<Block>());
+        builder.Branch(if_node->true_.target->As<Block>(), if_node->merge.target, utils::Empty);
+
+        TINT_ASSERT(IR, if_node->false_.target->Is<Block>());
+        builder.Branch(if_node->false_.target->As<Block>(), loop_node->merge.target, utils::Empty);
         if_node->condition = reg.Get();
 
         BranchTo(if_node);
 
-        current_flow_block = if_node->merge_target;
+        current_flow_block = if_node->merge.target->As<Block>();
         if (!EmitStatement(stmt->body)) {
             return false;
         }
 
-        BranchToIfNeeded(loop_node->continuing_target);
+        BranchToIfNeeded(loop_node->continuing.target);
     }
     // The while loop always has a path to the merge target as the break statement comes before
     // anything inside the loop.
-    current_flow_block = loop_node->merge_target;
+    current_flow_block = loop_node->merge.target->As<Block>();
     return true;
 }
 
 bool BuilderImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
     auto* loop_node = builder.CreateLoop();
-    builder.Branch(loop_node->continuing_target, loop_node->start_target);
+    TINT_ASSERT(IR, loop_node->continuing.target->Is<Block>());
+    builder.Branch(loop_node->continuing.target->As<Block>(), loop_node->start.target,
+                   utils::Empty);
 
     if (stmt->initializer) {
         // Emit the for initializer before branching to the loop
@@ -388,7 +395,7 @@
     {
         FlowStackScope scope(this, loop_node);
 
-        current_flow_block = loop_node->start_target;
+        current_flow_block = loop_node->start.target->As<Block>();
 
         if (stmt->condition) {
             // Emit the condition into the target target of the loop
@@ -399,22 +406,26 @@
 
             // Create an `if (cond) {} else {break;}` control flow
             auto* if_node = builder.CreateIf();
-            builder.Branch(if_node->true_target, if_node->merge_target);
-            builder.Branch(if_node->false_target, loop_node->merge_target);
+            TINT_ASSERT(IR, if_node->true_.target->Is<Block>());
+            builder.Branch(if_node->true_.target->As<Block>(), if_node->merge.target, utils::Empty);
+
+            TINT_ASSERT(IR, if_node->false_.target->Is<Block>());
+            builder.Branch(if_node->false_.target->As<Block>(), loop_node->merge.target,
+                           utils::Empty);
             if_node->condition = reg.Get();
 
             BranchTo(if_node);
-            current_flow_block = if_node->merge_target;
+            current_flow_block = if_node->merge.target->As<Block>();
         }
 
         if (!EmitStatement(stmt->body)) {
             return false;
         }
 
-        BranchToIfNeeded(loop_node->continuing_target);
+        BranchToIfNeeded(loop_node->continuing.target);
 
         if (stmt->continuing) {
-            current_flow_block = loop_node->continuing_target;
+            current_flow_block = loop_node->continuing.target->As<Block>();
             if (!EmitStatement(stmt->continuing)) {
                 return false;
             }
@@ -422,7 +433,7 @@
     }
     // The while loop always has a path to the merge target as the break statement comes before
     // anything inside the loop.
-    current_flow_block = loop_node->merge_target;
+    current_flow_block = loop_node->merge.target->As<Block>();
     return true;
 }
 
@@ -458,24 +469,29 @@
             if (!EmitStatement(c->Body()->Declaration())) {
                 return false;
             }
-            BranchToIfNeeded(switch_node->merge_target);
+            BranchToIfNeeded(switch_node->merge.target);
         }
     }
     current_flow_block = nullptr;
 
-    if (IsConnected(switch_node->merge_target)) {
-        current_flow_block = switch_node->merge_target;
+    if (IsConnected(switch_node->merge.target)) {
+        current_flow_block = switch_node->merge.target->As<Block>();
     }
 
     return true;
 }
 
-bool BuilderImpl::EmitReturn(const ast::ReturnStatement*) {
-    // TODO(dsinclair): Emit the return value. Need to determine how we want to
-    // emit this. Emit a `return_value %yy` instruction? There is no `return`
-    // instruction as we just branch.
+bool BuilderImpl::EmitReturn(const ast::ReturnStatement* stmt) {
+    utils::Vector<Value*, 1> ret_value;
+    if (stmt->value) {
+        auto ret = EmitExpression(stmt->value);
+        if (!ret) {
+            return false;
+        }
+        ret_value.Push(ret.Get());
+    }
 
-    BranchTo(current_function_->end_target);
+    BranchTo(current_function_->end_target, std::move(ret_value));
     return true;
 }
 
@@ -484,9 +500,9 @@
     TINT_ASSERT(IR, current_control);
 
     if (auto* c = current_control->As<Loop>()) {
-        BranchTo(c->merge_target);
+        BranchTo(c->merge.target);
     } else if (auto* s = current_control->As<Switch>()) {
-        BranchTo(s->merge_target);
+        BranchTo(s->merge.target);
     } else {
         TINT_UNREACHABLE(IR, diagnostics_);
         return false;
@@ -500,7 +516,7 @@
     TINT_ASSERT(IR, current_control);
 
     if (auto* c = current_control->As<Loop>()) {
-        BranchTo(c->continuing_target);
+        BranchTo(c->continuing.target);
     } else {
         TINT_UNREACHABLE(IR, diagnostics_);
     }
@@ -528,17 +544,17 @@
 
     auto* loop = current_control->As<Loop>();
 
-    current_flow_block = if_node->true_target;
-    BranchTo(loop->merge_target);
+    current_flow_block = if_node->true_.target->As<Block>();
+    BranchTo(loop->merge.target);
 
-    current_flow_block = if_node->false_target;
-    BranchTo(if_node->merge_target);
+    current_flow_block = if_node->false_.target->As<Block>();
+    BranchTo(if_node->merge.target);
 
-    current_flow_block = if_node->merge_target;
+    current_flow_block = if_node->merge.target->As<Block>();
 
     // The `break-if` has to be the last item in the continuing block. The false branch of the
     // `break-if` will always take us back to the start of the loop.
-    BranchTo(loop->start_target);
+    BranchTo(loop->start.target);
 
     return true;
 }
diff --git a/src/tint/ir/builder_impl.h b/src/tint/ir/builder_impl.h
index 8c81319..c086677 100644
--- a/src/tint/ir/builder_impl.h
+++ b/src/tint/ir/builder_impl.h
@@ -202,7 +202,7 @@
   private:
     enum class ControlFlags { kNone, kExcludeSwitch };
 
-    void BranchTo(ir::FlowNode* node);
+    void BranchTo(ir::FlowNode* node, utils::VectorRef<Value*> args = {});
     void BranchToIfNeeded(ir::FlowNode* node);
 
     FlowNode* FindEnclosingControl(ControlFlags flags);
diff --git a/src/tint/ir/builder_impl_test.cc b/src/tint/ir/builder_impl_test.cc
index c8b0b9f..eb3811e 100644
--- a/src/tint/ir/builder_impl_test.cc
+++ b/src/tint/ir/builder_impl_test.cc
@@ -43,7 +43,7 @@
     EXPECT_EQ(1u, f->start_target->inbound_branches.Length());
     EXPECT_EQ(1u, f->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(f->start_target->branch_target, f->end_target);
+    EXPECT_EQ(f->start_target->branch.target, f->end_target);
 }
 
 TEST_F(IR_BuilderImplTest, EntryPoint) {
@@ -76,24 +76,24 @@
     EXPECT_TRUE(ir_if->Is<ir::If>());
 
     auto* flow = ir_if->As<ir::If>();
-    ASSERT_NE(flow->true_target, nullptr);
-    ASSERT_NE(flow->false_target, nullptr);
-    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_NE(flow->true_.target, nullptr);
+    ASSERT_NE(flow->false_.target, nullptr);
+    ASSERT_NE(flow->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
     EXPECT_EQ(1u, flow->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->true_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->false_target->inbound_branches.Length());
-    EXPECT_EQ(2u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->false_.target->inbound_branches.Length());
+    EXPECT_EQ(2u, flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
     EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(func->start_target->branch_target, flow);
-    EXPECT_EQ(flow->true_target->branch_target, flow->merge_target);
-    EXPECT_EQ(flow->false_target->branch_target, flow->merge_target);
-    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(func->start_target->branch.target, flow);
+    EXPECT_EQ(flow->true_.target->As<ir::Block>()->branch.target, flow->merge.target);
+    EXPECT_EQ(flow->false_.target->As<ir::Block>()->branch.target, flow->merge.target);
+    EXPECT_EQ(flow->merge.target->As<ir::Block>()->branch.target, func->end_target);
 
     // Check condition
     ASSERT_TRUE(flow->condition->Is<Constant>());
@@ -121,24 +121,24 @@
     EXPECT_TRUE(ir_if->Is<ir::If>());
 
     auto* flow = ir_if->As<ir::If>();
-    ASSERT_NE(flow->true_target, nullptr);
-    ASSERT_NE(flow->false_target, nullptr);
-    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_NE(flow->true_.target, nullptr);
+    ASSERT_NE(flow->false_.target, nullptr);
+    ASSERT_NE(flow->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
     EXPECT_EQ(1u, flow->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->true_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->false_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->false_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
     EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(func->start_target->branch_target, flow);
-    EXPECT_EQ(flow->true_target->branch_target, func->end_target);
-    EXPECT_EQ(flow->false_target->branch_target, flow->merge_target);
-    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(func->start_target->branch.target, flow);
+    EXPECT_EQ(flow->true_.target->As<ir::Block>()->branch.target, func->end_target);
+    EXPECT_EQ(flow->false_.target->As<ir::Block>()->branch.target, flow->merge.target);
+    EXPECT_EQ(flow->merge.target->As<ir::Block>()->branch.target, func->end_target);
 }
 
 TEST_F(IR_BuilderImplTest, IfStatement_FalseReturns) {
@@ -160,24 +160,24 @@
     EXPECT_TRUE(ir_if->Is<ir::If>());
 
     auto* flow = ir_if->As<ir::If>();
-    ASSERT_NE(flow->true_target, nullptr);
-    ASSERT_NE(flow->false_target, nullptr);
-    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_NE(flow->true_.target, nullptr);
+    ASSERT_NE(flow->false_.target, nullptr);
+    ASSERT_NE(flow->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
     EXPECT_EQ(1u, flow->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->true_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->false_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->false_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
     EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(func->start_target->branch_target, flow);
-    EXPECT_EQ(flow->true_target->branch_target, flow->merge_target);
-    EXPECT_EQ(flow->false_target->branch_target, func->end_target);
-    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(func->start_target->branch.target, flow);
+    EXPECT_EQ(flow->true_.target->As<ir::Block>()->branch.target, flow->merge.target);
+    EXPECT_EQ(flow->false_.target->As<ir::Block>()->branch.target, func->end_target);
+    EXPECT_EQ(flow->merge.target->As<ir::Block>()->branch.target, func->end_target);
 }
 
 TEST_F(IR_BuilderImplTest, IfStatement_BothReturn) {
@@ -199,23 +199,23 @@
     EXPECT_TRUE(ir_if->Is<ir::If>());
 
     auto* flow = ir_if->As<ir::If>();
-    ASSERT_NE(flow->true_target, nullptr);
-    ASSERT_NE(flow->false_target, nullptr);
-    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_NE(flow->true_.target, nullptr);
+    ASSERT_NE(flow->false_.target, nullptr);
+    ASSERT_NE(flow->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
     EXPECT_EQ(1u, flow->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->true_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->false_target->inbound_branches.Length());
-    EXPECT_EQ(0u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->false_.target->inbound_branches.Length());
+    EXPECT_EQ(0u, flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
     EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(func->start_target->branch_target, flow);
-    EXPECT_EQ(flow->true_target->branch_target, func->end_target);
-    EXPECT_EQ(flow->false_target->branch_target, func->end_target);
+    EXPECT_EQ(func->start_target->branch.target, flow);
+    EXPECT_EQ(flow->true_.target->As<ir::Block>()->branch.target, func->end_target);
+    EXPECT_EQ(flow->false_.target->As<ir::Block>()->branch.target, func->end_target);
 }
 
 TEST_F(IR_BuilderImplTest, IfStatement_JumpChainToMerge) {
@@ -248,29 +248,30 @@
     EXPECT_TRUE(ir_if->Is<ir::If>());
 
     auto* if_flow = ir_if->As<ir::If>();
-    ASSERT_NE(if_flow->true_target, nullptr);
-    ASSERT_NE(if_flow->false_target, nullptr);
-    ASSERT_NE(if_flow->merge_target, nullptr);
+    ASSERT_NE(if_flow->true_.target, nullptr);
+    ASSERT_NE(if_flow->false_.target, nullptr);
+    ASSERT_NE(if_flow->merge.target, nullptr);
 
     auto* ir_loop = FlowNodeForAstNode(ast_loop);
     ASSERT_NE(ir_loop, nullptr);
     EXPECT_TRUE(ir_loop->Is<ir::Loop>());
 
     auto* loop_flow = ir_loop->As<ir::Loop>();
-    ASSERT_NE(loop_flow->start_target, nullptr);
-    ASSERT_NE(loop_flow->continuing_target, nullptr);
-    ASSERT_NE(loop_flow->merge_target, nullptr);
+    ASSERT_NE(loop_flow->start.target, nullptr);
+    ASSERT_NE(loop_flow->continuing.target, nullptr);
+    ASSERT_NE(loop_flow->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
-    EXPECT_EQ(func->start_target->branch_target, if_flow);
-    EXPECT_EQ(if_flow->true_target->branch_target, loop_flow);
-    EXPECT_EQ(loop_flow->start_target->branch_target, loop_flow->merge_target);
-    EXPECT_EQ(loop_flow->merge_target->branch_target, if_flow->merge_target);
-    EXPECT_EQ(loop_flow->continuing_target->branch_target, loop_flow->start_target);
-    EXPECT_EQ(if_flow->false_target->branch_target, if_flow->merge_target);
-    EXPECT_EQ(if_flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(func->start_target->branch.target, if_flow);
+    EXPECT_EQ(if_flow->true_.target->As<ir::Block>()->branch.target, loop_flow);
+    EXPECT_EQ(loop_flow->start.target->As<ir::Block>()->branch.target, loop_flow->merge.target);
+    EXPECT_EQ(loop_flow->merge.target->As<ir::Block>()->branch.target, if_flow->merge.target);
+    EXPECT_EQ(loop_flow->continuing.target->As<ir::Block>()->branch.target,
+              loop_flow->start.target);
+    EXPECT_EQ(if_flow->false_.target->As<ir::Block>()->branch.target, if_flow->merge.target);
+    EXPECT_EQ(if_flow->merge.target->As<ir::Block>()->branch.target, func->end_target);
 }
 
 TEST_F(IR_BuilderImplTest, Loop_WithBreak) {
@@ -289,24 +290,24 @@
     EXPECT_TRUE(ir_loop->Is<ir::Loop>());
 
     auto* flow = ir_loop->As<ir::Loop>();
-    ASSERT_NE(flow->start_target, nullptr);
-    ASSERT_NE(flow->continuing_target, nullptr);
-    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_NE(flow->start.target, nullptr);
+    ASSERT_NE(flow->continuing.target, nullptr);
+    ASSERT_NE(flow->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
     EXPECT_EQ(1u, flow->inbound_branches.Length());
-    EXPECT_EQ(2u, flow->start_target->inbound_branches.Length());
-    EXPECT_EQ(0u, flow->continuing_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(2u, flow->start.target->inbound_branches.Length());
+    EXPECT_EQ(0u, flow->continuing.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
     EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(func->start_target->branch_target, flow);
-    EXPECT_EQ(flow->start_target->branch_target, flow->merge_target);
-    EXPECT_EQ(flow->continuing_target->branch_target, flow->start_target);
-    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(func->start_target->As<ir::Block>()->branch.target, flow);
+    EXPECT_EQ(flow->start.target->As<ir::Block>()->branch.target, flow->merge.target);
+    EXPECT_EQ(flow->continuing.target->As<ir::Block>()->branch.target, flow->start.target);
+    EXPECT_EQ(flow->merge.target->As<ir::Block>()->branch.target, func->end_target);
 }
 
 TEST_F(IR_BuilderImplTest, Loop_WithContinue) {
@@ -331,39 +332,40 @@
     EXPECT_TRUE(ir_loop->Is<ir::Loop>());
 
     auto* loop_flow = ir_loop->As<ir::Loop>();
-    ASSERT_NE(loop_flow->start_target, nullptr);
-    ASSERT_NE(loop_flow->continuing_target, nullptr);
-    ASSERT_NE(loop_flow->merge_target, nullptr);
+    ASSERT_NE(loop_flow->start.target, nullptr);
+    ASSERT_NE(loop_flow->continuing.target, nullptr);
+    ASSERT_NE(loop_flow->merge.target, nullptr);
 
     auto* ir_if = FlowNodeForAstNode(ast_if);
     ASSERT_NE(ir_if, nullptr);
     ASSERT_TRUE(ir_if->Is<ir::If>());
 
     auto* if_flow = ir_if->As<ir::If>();
-    ASSERT_NE(if_flow->true_target, nullptr);
-    ASSERT_NE(if_flow->false_target, nullptr);
-    ASSERT_NE(if_flow->merge_target, nullptr);
+    ASSERT_NE(if_flow->true_.target, nullptr);
+    ASSERT_NE(if_flow->false_.target, nullptr);
+    ASSERT_NE(if_flow->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
     EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
-    EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
-    EXPECT_EQ(1u, loop_flow->continuing_target->inbound_branches.Length());
-    EXPECT_EQ(1u, loop_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow->continuing.target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, if_flow->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
     EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(func->start_target->branch_target, loop_flow);
-    EXPECT_EQ(loop_flow->start_target->branch_target, if_flow);
-    EXPECT_EQ(if_flow->true_target->branch_target, loop_flow->merge_target);
-    EXPECT_EQ(if_flow->false_target->branch_target, if_flow->merge_target);
-    EXPECT_EQ(loop_flow->continuing_target->branch_target, loop_flow->start_target);
-    EXPECT_EQ(loop_flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(func->start_target->branch.target, loop_flow);
+    EXPECT_EQ(loop_flow->start.target->As<ir::Block>()->branch.target, if_flow);
+    EXPECT_EQ(if_flow->true_.target->As<ir::Block>()->branch.target, loop_flow->merge.target);
+    EXPECT_EQ(if_flow->false_.target->As<ir::Block>()->branch.target, if_flow->merge.target);
+    EXPECT_EQ(loop_flow->continuing.target->As<ir::Block>()->branch.target,
+              loop_flow->start.target);
+    EXPECT_EQ(loop_flow->merge.target->As<ir::Block>()->branch.target, func->end_target);
 }
 
 TEST_F(IR_BuilderImplTest, Loop_WithContinuing_BreakIf) {
@@ -388,40 +390,42 @@
     EXPECT_TRUE(ir_loop->Is<ir::Loop>());
 
     auto* loop_flow = ir_loop->As<ir::Loop>();
-    ASSERT_NE(loop_flow->start_target, nullptr);
-    ASSERT_NE(loop_flow->continuing_target, nullptr);
-    ASSERT_NE(loop_flow->merge_target, nullptr);
+    ASSERT_NE(loop_flow->start.target, nullptr);
+    ASSERT_NE(loop_flow->continuing.target, nullptr);
+    ASSERT_NE(loop_flow->merge.target, nullptr);
 
     auto* ir_break_if = FlowNodeForAstNode(ast_break_if);
     ASSERT_NE(ir_break_if, nullptr);
     ASSERT_TRUE(ir_break_if->Is<ir::If>());
 
     auto* break_if_flow = ir_break_if->As<ir::If>();
-    ASSERT_NE(break_if_flow->true_target, nullptr);
-    ASSERT_NE(break_if_flow->false_target, nullptr);
-    ASSERT_NE(break_if_flow->merge_target, nullptr);
+    ASSERT_NE(break_if_flow->true_.target, nullptr);
+    ASSERT_NE(break_if_flow->false_.target, nullptr);
+    ASSERT_NE(break_if_flow->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
     EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
-    EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
-    EXPECT_EQ(1u, loop_flow->continuing_target->inbound_branches.Length());
-    EXPECT_EQ(1u, loop_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow->continuing.target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, break_if_flow->inbound_branches.Length());
-    EXPECT_EQ(1u, break_if_flow->true_target->inbound_branches.Length());
-    EXPECT_EQ(1u, break_if_flow->false_target->inbound_branches.Length());
-    EXPECT_EQ(1u, break_if_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, break_if_flow->true_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, break_if_flow->false_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, break_if_flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
     EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(func->start_target->branch_target, loop_flow);
-    EXPECT_EQ(loop_flow->start_target->branch_target, loop_flow->continuing_target);
-    EXPECT_EQ(loop_flow->continuing_target->branch_target, break_if_flow);
-    EXPECT_EQ(break_if_flow->true_target->branch_target, loop_flow->merge_target);
-    EXPECT_EQ(break_if_flow->false_target->branch_target, break_if_flow->merge_target);
-    EXPECT_EQ(break_if_flow->merge_target->branch_target, loop_flow->start_target);
-    EXPECT_EQ(loop_flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(func->start_target->branch.target, loop_flow);
+    EXPECT_EQ(loop_flow->start.target->As<ir::Block>()->branch.target,
+              loop_flow->continuing.target);
+    EXPECT_EQ(loop_flow->continuing.target->As<ir::Block>()->branch.target, break_if_flow);
+    EXPECT_EQ(break_if_flow->true_.target->As<ir::Block>()->branch.target, loop_flow->merge.target);
+    EXPECT_EQ(break_if_flow->false_.target->As<ir::Block>()->branch.target,
+              break_if_flow->merge.target);
+    EXPECT_EQ(break_if_flow->merge.target->As<ir::Block>()->branch.target, loop_flow->start.target);
+    EXPECT_EQ(loop_flow->merge.target->As<ir::Block>()->branch.target, func->end_target);
 }
 
 TEST_F(IR_BuilderImplTest, Loop_WithReturn) {
@@ -446,40 +450,41 @@
     EXPECT_TRUE(ir_loop->Is<ir::Loop>());
 
     auto* loop_flow = ir_loop->As<ir::Loop>();
-    ASSERT_NE(loop_flow->start_target, nullptr);
-    ASSERT_NE(loop_flow->continuing_target, nullptr);
-    ASSERT_NE(loop_flow->merge_target, nullptr);
+    ASSERT_NE(loop_flow->start.target, nullptr);
+    ASSERT_NE(loop_flow->continuing.target, nullptr);
+    ASSERT_NE(loop_flow->merge.target, nullptr);
 
     auto* ir_if = FlowNodeForAstNode(ast_if);
     ASSERT_NE(ir_if, nullptr);
     ASSERT_TRUE(ir_if->Is<ir::If>());
 
     auto* if_flow = ir_if->As<ir::If>();
-    ASSERT_NE(if_flow->true_target, nullptr);
-    ASSERT_NE(if_flow->false_target, nullptr);
-    ASSERT_NE(if_flow->merge_target, nullptr);
+    ASSERT_NE(if_flow->true_.target, nullptr);
+    ASSERT_NE(if_flow->false_.target, nullptr);
+    ASSERT_NE(if_flow->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
     EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
-    EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
-    EXPECT_EQ(1u, loop_flow->continuing_target->inbound_branches.Length());
-    EXPECT_EQ(0u, loop_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow->continuing.target->inbound_branches.Length());
+    EXPECT_EQ(0u, loop_flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, if_flow->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
     EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(loop_flow->start_target->branch_target, if_flow);
-    EXPECT_EQ(if_flow->true_target->branch_target, func->end_target);
-    EXPECT_EQ(if_flow->false_target->branch_target, if_flow->merge_target);
-    EXPECT_EQ(loop_flow->continuing_target->branch_target, loop_flow->start_target);
+    EXPECT_EQ(loop_flow->start.target->As<ir::Block>()->branch.target, if_flow);
+    EXPECT_EQ(if_flow->true_.target->As<ir::Block>()->branch.target, func->end_target);
+    EXPECT_EQ(if_flow->false_.target->As<ir::Block>()->branch.target, if_flow->merge.target);
+    EXPECT_EQ(loop_flow->continuing.target->As<ir::Block>()->branch.target,
+              loop_flow->start.target);
 
-    EXPECT_EQ(func->start_target->branch_target, ir_loop);
-    EXPECT_EQ(loop_flow->merge_target->branch_target, nullptr);
+    EXPECT_EQ(func->start_target->branch.target, ir_loop);
+    EXPECT_EQ(loop_flow->merge.target->As<ir::Block>()->branch.target, nullptr);
 
     // Check condition
     ASSERT_TRUE(if_flow->condition->Is<Constant>());
@@ -518,24 +523,25 @@
     EXPECT_TRUE(ir_loop->Is<ir::Loop>());
 
     auto* loop_flow = ir_loop->As<ir::Loop>();
-    ASSERT_NE(loop_flow->start_target, nullptr);
-    ASSERT_NE(loop_flow->continuing_target, nullptr);
-    ASSERT_NE(loop_flow->merge_target, nullptr);
+    ASSERT_NE(loop_flow->start.target, nullptr);
+    ASSERT_NE(loop_flow->continuing.target, nullptr);
+    ASSERT_NE(loop_flow->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
     EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
-    EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
-    EXPECT_EQ(0u, loop_flow->continuing_target->inbound_branches.Length());
-    EXPECT_EQ(0u, loop_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
+    EXPECT_EQ(0u, loop_flow->continuing.target->inbound_branches.Length());
+    EXPECT_EQ(0u, loop_flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
     EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(loop_flow->start_target->branch_target, func->end_target);
-    EXPECT_EQ(loop_flow->continuing_target->branch_target, loop_flow->start_target);
+    EXPECT_EQ(loop_flow->start.target->As<ir::Block>()->branch.target, func->end_target);
+    EXPECT_EQ(loop_flow->continuing.target->As<ir::Block>()->branch.target,
+              loop_flow->start.target);
 
-    EXPECT_EQ(func->start_target->branch_target, ir_loop);
+    EXPECT_EQ(func->start_target->branch.target, ir_loop);
 }
 
 TEST_F(IR_BuilderImplTest, Loop_WithOnlyReturn_ContinuingBreakIf) {
@@ -574,9 +580,9 @@
     EXPECT_TRUE(ir_loop->Is<ir::Loop>());
 
     auto* loop_flow = ir_loop->As<ir::Loop>();
-    ASSERT_NE(loop_flow->start_target, nullptr);
-    ASSERT_NE(loop_flow->continuing_target, nullptr);
-    ASSERT_NE(loop_flow->merge_target, nullptr);
+    ASSERT_NE(loop_flow->start.target, nullptr);
+    ASSERT_NE(loop_flow->continuing.target, nullptr);
+    ASSERT_NE(loop_flow->merge.target, nullptr);
 
     auto* ir_if = FlowNodeForAstNode(ast_if);
     EXPECT_EQ(ir_if, nullptr);
@@ -586,25 +592,25 @@
     EXPECT_TRUE(ir_break_if->Is<ir::If>());
 
     auto* break_if_flow = ir_break_if->As<ir::If>();
-    ASSERT_NE(break_if_flow->true_target, nullptr);
-    ASSERT_NE(break_if_flow->false_target, nullptr);
-    ASSERT_NE(break_if_flow->merge_target, nullptr);
+    ASSERT_NE(break_if_flow->true_.target, nullptr);
+    ASSERT_NE(break_if_flow->false_.target, nullptr);
+    ASSERT_NE(break_if_flow->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
     EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
-    EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
-    EXPECT_EQ(0u, loop_flow->continuing_target->inbound_branches.Length());
-    EXPECT_EQ(1u, loop_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
+    EXPECT_EQ(0u, loop_flow->continuing.target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
     // This is 1 because only the loop branch happens. The subsequent if return is dead code.
     EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(loop_flow->start_target->branch_target, func->end_target);
-    EXPECT_EQ(loop_flow->continuing_target->branch_target, break_if_flow);
+    EXPECT_EQ(loop_flow->start.target->As<ir::Block>()->branch.target, func->end_target);
+    EXPECT_EQ(loop_flow->continuing.target->As<ir::Block>()->branch.target, break_if_flow);
 
-    EXPECT_EQ(func->start_target->branch_target, ir_loop);
+    EXPECT_EQ(func->start_target->branch.target, ir_loop);
 }
 
 TEST_F(IR_BuilderImplTest, Loop_WithIf_BothBranchesBreak) {
@@ -629,42 +635,43 @@
     EXPECT_TRUE(ir_loop->Is<ir::Loop>());
 
     auto* loop_flow = ir_loop->As<ir::Loop>();
-    ASSERT_NE(loop_flow->start_target, nullptr);
-    ASSERT_NE(loop_flow->continuing_target, nullptr);
-    ASSERT_NE(loop_flow->merge_target, nullptr);
+    ASSERT_NE(loop_flow->start.target, nullptr);
+    ASSERT_NE(loop_flow->continuing.target, nullptr);
+    ASSERT_NE(loop_flow->merge.target, nullptr);
 
     auto* ir_if = FlowNodeForAstNode(ast_if);
     ASSERT_NE(ir_if, nullptr);
     ASSERT_TRUE(ir_if->Is<ir::If>());
 
     auto* if_flow = ir_if->As<ir::If>();
-    ASSERT_NE(if_flow->true_target, nullptr);
-    ASSERT_NE(if_flow->false_target, nullptr);
-    ASSERT_NE(if_flow->merge_target, nullptr);
+    ASSERT_NE(if_flow->true_.target, nullptr);
+    ASSERT_NE(if_flow->false_.target, nullptr);
+    ASSERT_NE(if_flow->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
     EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
-    EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
-    EXPECT_EQ(0u, loop_flow->continuing_target->inbound_branches.Length());
-    EXPECT_EQ(2u, loop_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
+    EXPECT_EQ(0u, loop_flow->continuing.target->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, if_flow->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
-    EXPECT_EQ(0u, if_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length());
+    EXPECT_EQ(0u, if_flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
     EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
 
     // Note, the `continue` is dead code because both if branches go out of loop, so it just gets
     // dropped.
 
-    EXPECT_EQ(func->start_target->branch_target, loop_flow);
-    EXPECT_EQ(loop_flow->start_target->branch_target, if_flow);
-    EXPECT_EQ(if_flow->true_target->branch_target, loop_flow->merge_target);
-    EXPECT_EQ(if_flow->false_target->branch_target, loop_flow->merge_target);
-    EXPECT_EQ(loop_flow->continuing_target->branch_target, loop_flow->start_target);
-    EXPECT_EQ(loop_flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(func->start_target->branch.target, loop_flow);
+    EXPECT_EQ(loop_flow->start.target->As<ir::Block>()->branch.target, if_flow);
+    EXPECT_EQ(if_flow->true_.target->As<ir::Block>()->branch.target, loop_flow->merge.target);
+    EXPECT_EQ(if_flow->false_.target->As<ir::Block>()->branch.target, loop_flow->merge.target);
+    EXPECT_EQ(loop_flow->continuing.target->As<ir::Block>()->branch.target,
+              loop_flow->start.target);
+    EXPECT_EQ(loop_flow->merge.target->As<ir::Block>()->branch.target, func->end_target);
 }
 
 TEST_F(IR_BuilderImplTest, Loop_Nested) {
@@ -735,129 +742,135 @@
     ASSERT_NE(ir_loop_a, nullptr);
     EXPECT_TRUE(ir_loop_a->Is<ir::Loop>());
     auto* loop_flow_a = ir_loop_a->As<ir::Loop>();
-    ASSERT_NE(loop_flow_a->start_target, nullptr);
-    ASSERT_NE(loop_flow_a->continuing_target, nullptr);
-    ASSERT_NE(loop_flow_a->merge_target, nullptr);
+    ASSERT_NE(loop_flow_a->start.target, nullptr);
+    ASSERT_NE(loop_flow_a->continuing.target, nullptr);
+    ASSERT_NE(loop_flow_a->merge.target, nullptr);
 
     auto* ir_loop_b = FlowNodeForAstNode(ast_loop_b);
     ASSERT_NE(ir_loop_b, nullptr);
     EXPECT_TRUE(ir_loop_b->Is<ir::Loop>());
     auto* loop_flow_b = ir_loop_b->As<ir::Loop>();
-    ASSERT_NE(loop_flow_b->start_target, nullptr);
-    ASSERT_NE(loop_flow_b->continuing_target, nullptr);
-    ASSERT_NE(loop_flow_b->merge_target, nullptr);
+    ASSERT_NE(loop_flow_b->start.target, nullptr);
+    ASSERT_NE(loop_flow_b->continuing.target, nullptr);
+    ASSERT_NE(loop_flow_b->merge.target, nullptr);
 
     auto* ir_loop_c = FlowNodeForAstNode(ast_loop_c);
     ASSERT_NE(ir_loop_c, nullptr);
     EXPECT_TRUE(ir_loop_c->Is<ir::Loop>());
     auto* loop_flow_c = ir_loop_c->As<ir::Loop>();
-    ASSERT_NE(loop_flow_c->start_target, nullptr);
-    ASSERT_NE(loop_flow_c->continuing_target, nullptr);
-    ASSERT_NE(loop_flow_c->merge_target, nullptr);
+    ASSERT_NE(loop_flow_c->start.target, nullptr);
+    ASSERT_NE(loop_flow_c->continuing.target, nullptr);
+    ASSERT_NE(loop_flow_c->merge.target, nullptr);
 
     auto* ir_loop_d = FlowNodeForAstNode(ast_loop_d);
     ASSERT_NE(ir_loop_d, nullptr);
     EXPECT_TRUE(ir_loop_d->Is<ir::Loop>());
     auto* loop_flow_d = ir_loop_d->As<ir::Loop>();
-    ASSERT_NE(loop_flow_d->start_target, nullptr);
-    ASSERT_NE(loop_flow_d->continuing_target, nullptr);
-    ASSERT_NE(loop_flow_d->merge_target, nullptr);
+    ASSERT_NE(loop_flow_d->start.target, nullptr);
+    ASSERT_NE(loop_flow_d->continuing.target, nullptr);
+    ASSERT_NE(loop_flow_d->merge.target, nullptr);
 
     auto* ir_if_a = FlowNodeForAstNode(ast_if_a);
     ASSERT_NE(ir_if_a, nullptr);
     EXPECT_TRUE(ir_if_a->Is<ir::If>());
     auto* if_flow_a = ir_if_a->As<ir::If>();
-    ASSERT_NE(if_flow_a->true_target, nullptr);
-    ASSERT_NE(if_flow_a->false_target, nullptr);
-    ASSERT_NE(if_flow_a->merge_target, nullptr);
+    ASSERT_NE(if_flow_a->true_.target, nullptr);
+    ASSERT_NE(if_flow_a->false_.target, nullptr);
+    ASSERT_NE(if_flow_a->merge.target, nullptr);
 
     auto* ir_if_b = FlowNodeForAstNode(ast_if_b);
     ASSERT_NE(ir_if_b, nullptr);
     EXPECT_TRUE(ir_if_b->Is<ir::If>());
     auto* if_flow_b = ir_if_b->As<ir::If>();
-    ASSERT_NE(if_flow_b->true_target, nullptr);
-    ASSERT_NE(if_flow_b->false_target, nullptr);
-    ASSERT_NE(if_flow_b->merge_target, nullptr);
+    ASSERT_NE(if_flow_b->true_.target, nullptr);
+    ASSERT_NE(if_flow_b->false_.target, nullptr);
+    ASSERT_NE(if_flow_b->merge.target, nullptr);
 
     auto* ir_if_c = FlowNodeForAstNode(ast_if_c);
     ASSERT_NE(ir_if_c, nullptr);
     EXPECT_TRUE(ir_if_c->Is<ir::If>());
     auto* if_flow_c = ir_if_c->As<ir::If>();
-    ASSERT_NE(if_flow_c->true_target, nullptr);
-    ASSERT_NE(if_flow_c->false_target, nullptr);
-    ASSERT_NE(if_flow_c->merge_target, nullptr);
+    ASSERT_NE(if_flow_c->true_.target, nullptr);
+    ASSERT_NE(if_flow_c->false_.target, nullptr);
+    ASSERT_NE(if_flow_c->merge.target, nullptr);
 
     auto* ir_if_d = FlowNodeForAstNode(ast_if_d);
     ASSERT_NE(ir_if_d, nullptr);
     EXPECT_TRUE(ir_if_d->Is<ir::If>());
     auto* if_flow_d = ir_if_d->As<ir::If>();
-    ASSERT_NE(if_flow_d->true_target, nullptr);
-    ASSERT_NE(if_flow_d->false_target, nullptr);
-    ASSERT_NE(if_flow_d->merge_target, nullptr);
+    ASSERT_NE(if_flow_d->true_.target, nullptr);
+    ASSERT_NE(if_flow_d->false_.target, nullptr);
+    ASSERT_NE(if_flow_d->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
     EXPECT_EQ(1u, loop_flow_a->inbound_branches.Length());
-    EXPECT_EQ(2u, loop_flow_a->start_target->inbound_branches.Length());
-    EXPECT_EQ(1u, loop_flow_a->continuing_target->inbound_branches.Length());
-    EXPECT_EQ(1u, loop_flow_a->merge_target->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow_a->start.target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow_a->continuing.target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow_a->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, loop_flow_b->inbound_branches.Length());
-    EXPECT_EQ(2u, loop_flow_b->start_target->inbound_branches.Length());
-    EXPECT_EQ(2u, loop_flow_b->continuing_target->inbound_branches.Length());
-    EXPECT_EQ(1u, loop_flow_b->merge_target->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow_b->start.target->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow_b->continuing.target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow_b->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, loop_flow_c->inbound_branches.Length());
-    EXPECT_EQ(2u, loop_flow_c->start_target->inbound_branches.Length());
-    EXPECT_EQ(0u, loop_flow_c->continuing_target->inbound_branches.Length());
-    EXPECT_EQ(1u, loop_flow_c->merge_target->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow_c->start.target->inbound_branches.Length());
+    EXPECT_EQ(0u, loop_flow_c->continuing.target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow_c->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, loop_flow_d->inbound_branches.Length());
-    EXPECT_EQ(2u, loop_flow_d->start_target->inbound_branches.Length());
-    EXPECT_EQ(1u, loop_flow_d->continuing_target->inbound_branches.Length());
-    EXPECT_EQ(1u, loop_flow_d->merge_target->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow_d->start.target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow_d->continuing.target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow_d->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, if_flow_a->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow_a->true_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow_a->false_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow_a->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_a->true_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_a->false_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_a->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, if_flow_b->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow_b->true_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow_b->false_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow_b->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_b->true_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_b->false_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_b->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, if_flow_c->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow_c->true_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow_c->false_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow_c->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_c->true_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_c->false_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_c->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, if_flow_d->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow_d->true_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow_d->false_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow_d->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_d->true_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_d->false_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_d->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
     EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(func->start_target->branch_target, loop_flow_a);
-    EXPECT_EQ(loop_flow_a->start_target->branch_target, loop_flow_b);
-    EXPECT_EQ(loop_flow_b->start_target->branch_target, if_flow_a);
-    EXPECT_EQ(if_flow_a->true_target->branch_target, loop_flow_b->merge_target);
-    EXPECT_EQ(if_flow_a->false_target->branch_target, if_flow_a->merge_target);
-    EXPECT_EQ(if_flow_a->merge_target->branch_target, if_flow_b);
-    EXPECT_EQ(if_flow_b->true_target->branch_target, loop_flow_b->continuing_target);
-    EXPECT_EQ(if_flow_b->false_target->branch_target, if_flow_b->merge_target);
-    EXPECT_EQ(if_flow_b->merge_target->branch_target, loop_flow_b->continuing_target);
-    EXPECT_EQ(loop_flow_b->continuing_target->branch_target, loop_flow_c);
-    EXPECT_EQ(loop_flow_c->start_target->branch_target, loop_flow_c->merge_target);
-    EXPECT_EQ(loop_flow_c->continuing_target->branch_target, loop_flow_c->start_target);
-    EXPECT_EQ(loop_flow_c->merge_target->branch_target, loop_flow_d);
-    EXPECT_EQ(loop_flow_d->start_target->branch_target, loop_flow_d->continuing_target);
-    EXPECT_EQ(loop_flow_d->continuing_target->branch_target, if_flow_c);
-    EXPECT_EQ(if_flow_c->true_target->branch_target, loop_flow_d->merge_target);
-    EXPECT_EQ(if_flow_c->false_target->branch_target, if_flow_c->merge_target);
-    EXPECT_EQ(if_flow_c->merge_target->branch_target, loop_flow_d->start_target);
-    EXPECT_EQ(loop_flow_d->merge_target->branch_target, loop_flow_b->start_target);
-    EXPECT_EQ(loop_flow_b->merge_target->branch_target, if_flow_d);
-    EXPECT_EQ(if_flow_d->true_target->branch_target, loop_flow_a->merge_target);
-    EXPECT_EQ(if_flow_d->false_target->branch_target, if_flow_d->merge_target);
-    EXPECT_EQ(if_flow_d->merge_target->branch_target, loop_flow_a->continuing_target);
-    EXPECT_EQ(loop_flow_a->continuing_target->branch_target, loop_flow_a->start_target);
-    EXPECT_EQ(loop_flow_a->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(func->start_target->branch.target, loop_flow_a);
+    EXPECT_EQ(loop_flow_a->start.target->As<ir::Block>()->branch.target, loop_flow_b);
+    EXPECT_EQ(loop_flow_b->start.target->As<ir::Block>()->branch.target, if_flow_a);
+    EXPECT_EQ(if_flow_a->true_.target->As<ir::Block>()->branch.target, loop_flow_b->merge.target);
+    EXPECT_EQ(if_flow_a->false_.target->As<ir::Block>()->branch.target, if_flow_a->merge.target);
+    EXPECT_EQ(if_flow_a->merge.target->As<ir::Block>()->branch.target, if_flow_b);
+    EXPECT_EQ(if_flow_b->true_.target->As<ir::Block>()->branch.target,
+              loop_flow_b->continuing.target);
+    EXPECT_EQ(if_flow_b->false_.target->As<ir::Block>()->branch.target, if_flow_b->merge.target);
+    EXPECT_EQ(if_flow_b->merge.target->As<ir::Block>()->branch.target,
+              loop_flow_b->continuing.target);
+    EXPECT_EQ(loop_flow_b->continuing.target->As<ir::Block>()->branch.target, loop_flow_c);
+    EXPECT_EQ(loop_flow_c->start.target->As<ir::Block>()->branch.target, loop_flow_c->merge.target);
+    EXPECT_EQ(loop_flow_c->continuing.target->As<ir::Block>()->branch.target,
+              loop_flow_c->start.target);
+    EXPECT_EQ(loop_flow_c->merge.target->As<ir::Block>()->branch.target, loop_flow_d);
+    EXPECT_EQ(loop_flow_d->start.target->As<ir::Block>()->branch.target,
+              loop_flow_d->continuing.target);
+    EXPECT_EQ(loop_flow_d->continuing.target->As<ir::Block>()->branch.target, if_flow_c);
+    EXPECT_EQ(if_flow_c->true_.target->As<ir::Block>()->branch.target, loop_flow_d->merge.target);
+    EXPECT_EQ(if_flow_c->false_.target->As<ir::Block>()->branch.target, if_flow_c->merge.target);
+    EXPECT_EQ(if_flow_c->merge.target->As<ir::Block>()->branch.target, loop_flow_d->start.target);
+    EXPECT_EQ(loop_flow_d->merge.target->As<ir::Block>()->branch.target, loop_flow_b->start.target);
+    EXPECT_EQ(loop_flow_b->merge.target->As<ir::Block>()->branch.target, if_flow_d);
+    EXPECT_EQ(if_flow_d->true_.target->As<ir::Block>()->branch.target, loop_flow_a->merge.target);
+    EXPECT_EQ(if_flow_d->false_.target->As<ir::Block>()->branch.target, if_flow_d->merge.target);
+    EXPECT_EQ(if_flow_d->merge.target->As<ir::Block>()->branch.target,
+              loop_flow_a->continuing.target);
+    EXPECT_EQ(loop_flow_a->continuing.target->As<ir::Block>()->branch.target,
+              loop_flow_a->start.target);
+    EXPECT_EQ(loop_flow_a->merge.target->As<ir::Block>()->branch.target, func->end_target);
 }
 
 TEST_F(IR_BuilderImplTest, While) {
@@ -886,36 +899,36 @@
     ASSERT_TRUE(ir_while->Is<ir::Loop>());
 
     auto* flow = ir_while->As<ir::Loop>();
-    ASSERT_NE(flow->start_target, nullptr);
-    ASSERT_NE(flow->continuing_target, nullptr);
-    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_NE(flow->start.target, nullptr);
+    ASSERT_NE(flow->continuing.target, nullptr);
+    ASSERT_NE(flow->merge.target, nullptr);
 
-    ASSERT_NE(flow->start_target->branch_target, nullptr);
-    ASSERT_TRUE(flow->start_target->branch_target->Is<ir::If>());
-    auto* if_flow = flow->start_target->branch_target->As<ir::If>();
-    ASSERT_NE(if_flow->true_target, nullptr);
-    ASSERT_NE(if_flow->false_target, nullptr);
-    ASSERT_NE(if_flow->merge_target, nullptr);
+    ASSERT_NE(flow->start.target->As<ir::Block>()->branch.target, nullptr);
+    ASSERT_TRUE(flow->start.target->As<ir::Block>()->branch.target->Is<ir::If>());
+    auto* if_flow = flow->start.target->As<ir::Block>()->branch.target->As<ir::If>();
+    ASSERT_NE(if_flow->true_.target, nullptr);
+    ASSERT_NE(if_flow->false_.target, nullptr);
+    ASSERT_NE(if_flow->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
     EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
     EXPECT_EQ(1u, flow->inbound_branches.Length());
-    EXPECT_EQ(2u, flow->start_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->continuing_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(2u, flow->start.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->continuing.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length());
 
-    EXPECT_EQ(func->start_target->branch_target, flow);
-    EXPECT_EQ(flow->start_target->branch_target, if_flow);
-    EXPECT_EQ(if_flow->true_target->branch_target, if_flow->merge_target);
-    EXPECT_EQ(if_flow->false_target->branch_target, flow->merge_target);
-    EXPECT_EQ(if_flow->merge_target->branch_target, flow->continuing_target);
-    EXPECT_EQ(flow->continuing_target->branch_target, flow->start_target);
-    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(func->start_target->branch.target, flow);
+    EXPECT_EQ(flow->start.target->As<ir::Block>()->branch.target, if_flow);
+    EXPECT_EQ(if_flow->true_.target->As<ir::Block>()->branch.target, if_flow->merge.target);
+    EXPECT_EQ(if_flow->false_.target->As<ir::Block>()->branch.target, flow->merge.target);
+    EXPECT_EQ(if_flow->merge.target->As<ir::Block>()->branch.target, flow->continuing.target);
+    EXPECT_EQ(flow->continuing.target->As<ir::Block>()->branch.target, flow->start.target);
+    EXPECT_EQ(flow->merge.target->As<ir::Block>()->branch.target, func->end_target);
 
     // Check condition
     ASSERT_TRUE(if_flow->condition->Is<Constant>());
@@ -950,36 +963,36 @@
     ASSERT_TRUE(ir_while->Is<ir::Loop>());
 
     auto* flow = ir_while->As<ir::Loop>();
-    ASSERT_NE(flow->start_target, nullptr);
-    ASSERT_NE(flow->continuing_target, nullptr);
-    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_NE(flow->start.target, nullptr);
+    ASSERT_NE(flow->continuing.target, nullptr);
+    ASSERT_NE(flow->merge.target, nullptr);
 
-    ASSERT_NE(flow->start_target->branch_target, nullptr);
-    ASSERT_TRUE(flow->start_target->branch_target->Is<ir::If>());
-    auto* if_flow = flow->start_target->branch_target->As<ir::If>();
-    ASSERT_NE(if_flow->true_target, nullptr);
-    ASSERT_NE(if_flow->false_target, nullptr);
-    ASSERT_NE(if_flow->merge_target, nullptr);
+    ASSERT_NE(flow->start.target->As<ir::Block>()->branch.target, nullptr);
+    ASSERT_TRUE(flow->start.target->As<ir::Block>()->branch.target->Is<ir::If>());
+    auto* if_flow = flow->start.target->As<ir::Block>()->branch.target->As<ir::If>();
+    ASSERT_NE(if_flow->true_.target, nullptr);
+    ASSERT_NE(if_flow->false_.target, nullptr);
+    ASSERT_NE(if_flow->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
     EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
     EXPECT_EQ(1u, flow->inbound_branches.Length());
-    EXPECT_EQ(2u, flow->start_target->inbound_branches.Length());
-    EXPECT_EQ(0u, flow->continuing_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(2u, flow->start.target->inbound_branches.Length());
+    EXPECT_EQ(0u, flow->continuing.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length());
 
-    EXPECT_EQ(func->start_target->branch_target, flow);
-    EXPECT_EQ(flow->start_target->branch_target, if_flow);
-    EXPECT_EQ(if_flow->true_target->branch_target, if_flow->merge_target);
-    EXPECT_EQ(if_flow->false_target->branch_target, flow->merge_target);
-    EXPECT_EQ(if_flow->merge_target->branch_target, func->end_target);
-    EXPECT_EQ(flow->continuing_target->branch_target, flow->start_target);
-    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(func->start_target->branch.target, flow);
+    EXPECT_EQ(flow->start.target->As<ir::Block>()->branch.target, if_flow);
+    EXPECT_EQ(if_flow->true_.target->As<ir::Block>()->branch.target, if_flow->merge.target);
+    EXPECT_EQ(if_flow->false_.target->As<ir::Block>()->branch.target, flow->merge.target);
+    EXPECT_EQ(if_flow->merge.target->As<ir::Block>()->branch.target, func->end_target);
+    EXPECT_EQ(flow->continuing.target->As<ir::Block>()->branch.target, flow->start.target);
+    EXPECT_EQ(flow->merge.target->As<ir::Block>()->branch.target, func->end_target);
 }
 
 // TODO(dsinclair): Enable when variable declarations and increment are supported
@@ -1007,36 +1020,36 @@
     ASSERT_TRUE(ir_for->Is<ir::Loop>());
 
     auto* flow = ir_for->As<ir::Loop>();
-    ASSERT_NE(flow->start_target, nullptr);
-    ASSERT_NE(flow->continuing_target, nullptr);
-    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_NE(flow->start.target, nullptr);
+    ASSERT_NE(flow->continuing.target, nullptr);
+    ASSERT_NE(flow->merge.target, nullptr);
 
-    ASSERT_NE(flow->start_target->branch_target, nullptr);
-    ASSERT_TRUE(flow->start_target->branch_target->Is<ir::If>());
-    auto* if_flow = flow->start_target->branch_target->As<ir::If>();
-    ASSERT_NE(if_flow->true_target, nullptr);
-    ASSERT_NE(if_flow->false_target, nullptr);
-    ASSERT_NE(if_flow->merge_target, nullptr);
+    ASSERT_NE(flow->start.target->As<ir::Block>()->branch.target, nullptr);
+    ASSERT_TRUE(flow->start.target->As<ir::Block>()->branch.target->Is<ir::If>());
+    auto* if_flow = flow->start.target->As<ir::Block>()->branch.target->As<ir::If>();
+    ASSERT_NE(if_flow->true_.target, nullptr);
+    ASSERT_NE(if_flow->false_.target, nullptr);
+    ASSERT_NE(if_flow->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
     EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
     EXPECT_EQ(1u, flow->inbound_branches.Length());
-    EXPECT_EQ(2u, flow->start_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->continuing_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
-    EXPECT_EQ(1u, if_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(2u, flow->start.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->continuing.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length());
 
-    EXPECT_EQ(func->start_target->branch_target, flow);
-    EXPECT_EQ(flow->start_target->branch_target, if_flow);
-    EXPECT_EQ(if_flow->true_target->branch_target, if_flow->merge_target);
-    EXPECT_EQ(if_flow->false_target->branch_target, flow->merge_target);
-    EXPECT_EQ(if_flow->merge_target->branch_target, flow->continuing_target);
-    EXPECT_EQ(flow->continuing_target->branch_target, flow->start_target);
-    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(func->start_target->branch.target, flow);
+    EXPECT_EQ(flow->start.target->As<ir::Block>()->branch.target, if_flow);
+    EXPECT_EQ(if_flow->true_.target->As<ir::Block>()->branch.target, if_flow->merge.target);
+    EXPECT_EQ(if_flow->false_.target->As<ir::Block>()->branch.target, flow->merge.target);
+    EXPECT_EQ(if_flow->merge.target->As<ir::Block>()->branch.target, flow->continuing.target);
+    EXPECT_EQ(flow->continuing.target->As<ir::Block>()->branch.target, flow->start.target);
+    EXPECT_EQ(flow->merge.target->As<ir::Block>()->branch.target, func->end_target);
 
     // Check condition
     ASSERT_TRUE(if_flow->condition->Is<Constant>());
@@ -1063,22 +1076,22 @@
     ASSERT_TRUE(ir_for->Is<ir::Loop>());
 
     auto* flow = ir_for->As<ir::Loop>();
-    ASSERT_NE(flow->start_target, nullptr);
-    ASSERT_NE(flow->continuing_target, nullptr);
-    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_NE(flow->start.target, nullptr);
+    ASSERT_NE(flow->continuing.target, nullptr);
+    ASSERT_NE(flow->merge.target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
     EXPECT_EQ(1u, flow->inbound_branches.Length());
-    EXPECT_EQ(2u, flow->start_target->inbound_branches.Length());
-    EXPECT_EQ(0u, flow->continuing_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(2u, flow->start.target->inbound_branches.Length());
+    EXPECT_EQ(0u, flow->continuing.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(flow->start_target->branch_target, flow->merge_target);
-    EXPECT_EQ(flow->continuing_target->branch_target, flow->start_target);
-    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(flow->start.target->As<ir::Block>()->branch.target, flow->merge.target);
+    EXPECT_EQ(flow->continuing.target->As<ir::Block>()->branch.target, flow->start.target);
+    EXPECT_EQ(flow->merge.target->As<ir::Block>()->branch.target, func->end_target);
 }
 
 TEST_F(IR_BuilderImplTest, Switch) {
@@ -1105,7 +1118,7 @@
     ASSERT_TRUE(ir_switch->Is<ir::Switch>());
 
     auto* flow = ir_switch->As<ir::Switch>();
-    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_NE(flow->merge.target, nullptr);
     ASSERT_EQ(3u, flow->cases.Length());
 
     ASSERT_EQ(1u, m.functions.Length());
@@ -1123,17 +1136,17 @@
     EXPECT_TRUE(flow->cases[2].selectors[0].IsDefault());
 
     EXPECT_EQ(1u, flow->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->cases[0].start_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->cases[1].start_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->cases[2].start_target->inbound_branches.Length());
-    EXPECT_EQ(3u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[0].start.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[1].start.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[2].start.target->inbound_branches.Length());
+    EXPECT_EQ(3u, flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(func->start_target->branch_target, ir_switch);
-    EXPECT_EQ(flow->cases[0].start_target->branch_target, flow->merge_target);
-    EXPECT_EQ(flow->cases[1].start_target->branch_target, flow->merge_target);
-    EXPECT_EQ(flow->cases[2].start_target->branch_target, flow->merge_target);
-    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(func->start_target->branch.target, ir_switch);
+    EXPECT_EQ(flow->cases[0].start.target->As<ir::Block>()->branch.target, flow->merge.target);
+    EXPECT_EQ(flow->cases[1].start.target->As<ir::Block>()->branch.target, flow->merge.target);
+    EXPECT_EQ(flow->cases[2].start.target->As<ir::Block>()->branch.target, flow->merge.target);
+    EXPECT_EQ(flow->merge.target->As<ir::Block>()->branch.target, func->end_target);
 
     // Check condition
     ASSERT_TRUE(flow->condition->Is<Constant>());
@@ -1157,7 +1170,7 @@
     ASSERT_TRUE(ir_switch->Is<ir::Switch>());
 
     auto* flow = ir_switch->As<ir::Switch>();
-    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_NE(flow->merge.target, nullptr);
     ASSERT_EQ(1u, flow->cases.Length());
 
     ASSERT_EQ(1u, m.functions.Length());
@@ -1167,13 +1180,13 @@
     EXPECT_TRUE(flow->cases[0].selectors[0].IsDefault());
 
     EXPECT_EQ(1u, flow->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->cases[0].start_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[0].start.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(func->start_target->branch_target, ir_switch);
-    EXPECT_EQ(flow->cases[0].start_target->branch_target, flow->merge_target);
-    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(func->start_target->branch.target, ir_switch);
+    EXPECT_EQ(flow->cases[0].start.target->As<ir::Block>()->branch.target, flow->merge.target);
+    EXPECT_EQ(flow->merge.target->As<ir::Block>()->branch.target, func->end_target);
 }
 
 TEST_F(IR_BuilderImplTest, Switch_WithBreak) {
@@ -1207,7 +1220,7 @@
     ASSERT_TRUE(ir_switch->Is<ir::Switch>());
 
     auto* flow = ir_switch->As<ir::Switch>();
-    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_NE(flow->merge.target, nullptr);
     ASSERT_EQ(2u, flow->cases.Length());
 
     ASSERT_EQ(1u, m.functions.Length());
@@ -1221,16 +1234,16 @@
     EXPECT_TRUE(flow->cases[1].selectors[0].IsDefault());
 
     EXPECT_EQ(1u, flow->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->cases[0].start_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->cases[1].start_target->inbound_branches.Length());
-    EXPECT_EQ(2u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[0].start.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[1].start.target->inbound_branches.Length());
+    EXPECT_EQ(2u, flow->merge.target->inbound_branches.Length());
     // This is 1 because the if is dead-code eliminated and the return doesn't happen.
     EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(func->start_target->branch_target, ir_switch);
-    EXPECT_EQ(flow->cases[0].start_target->branch_target, flow->merge_target);
-    EXPECT_EQ(flow->cases[1].start_target->branch_target, flow->merge_target);
-    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(func->start_target->branch.target, ir_switch);
+    EXPECT_EQ(flow->cases[0].start.target->As<ir::Block>()->branch.target, flow->merge.target);
+    EXPECT_EQ(flow->cases[1].start.target->As<ir::Block>()->branch.target, flow->merge.target);
+    EXPECT_EQ(flow->merge.target->As<ir::Block>()->branch.target, func->end_target);
 }
 
 TEST_F(IR_BuilderImplTest, Switch_AllReturn) {
@@ -1271,7 +1284,7 @@
     ASSERT_TRUE(ir_switch->Is<ir::Switch>());
 
     auto* flow = ir_switch->As<ir::Switch>();
-    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_NE(flow->merge.target, nullptr);
     ASSERT_EQ(2u, flow->cases.Length());
 
     ASSERT_EQ(1u, m.functions.Length());
@@ -1285,15 +1298,15 @@
     EXPECT_TRUE(flow->cases[1].selectors[0].IsDefault());
 
     EXPECT_EQ(1u, flow->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->cases[0].start_target->inbound_branches.Length());
-    EXPECT_EQ(1u, flow->cases[1].start_target->inbound_branches.Length());
-    EXPECT_EQ(0u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[0].start.target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[1].start.target->inbound_branches.Length());
+    EXPECT_EQ(0u, flow->merge.target->inbound_branches.Length());
     EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
 
-    EXPECT_EQ(func->start_target->branch_target, ir_switch);
-    EXPECT_EQ(flow->cases[0].start_target->branch_target, func->end_target);
-    EXPECT_EQ(flow->cases[1].start_target->branch_target, func->end_target);
-    EXPECT_EQ(flow->merge_target->branch_target, nullptr);
+    EXPECT_EQ(func->start_target->branch.target, ir_switch);
+    EXPECT_EQ(flow->cases[0].start.target->As<ir::Block>()->branch.target, func->end_target);
+    EXPECT_EQ(flow->cases[1].start.target->As<ir::Block>()->branch.target, func->end_target);
+    EXPECT_EQ(flow->merge.target->As<ir::Block>()->branch.target, nullptr);
 }
 
 TEST_F(IR_BuilderImplTest, EmitLiteral_Bool_True) {
@@ -1391,7 +1404,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 + 4
 )");
 }
@@ -1406,7 +1419,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 - 4
 )");
 }
@@ -1421,7 +1434,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 * 4
 )");
 }
@@ -1436,7 +1449,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 / 4
 )");
 }
@@ -1451,7 +1464,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 % 4
 )");
 }
@@ -1466,7 +1479,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 & 4
 )");
 }
@@ -1481,7 +1494,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 | 4
 )");
 }
@@ -1496,7 +1509,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 ^ 4
 )");
 }
@@ -1511,7 +1524,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (bool) = true && false
 )");
 }
@@ -1526,7 +1539,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (bool) = false || true
 )");
 }
@@ -1541,7 +1554,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (bool) = 3 == 4
 )");
 }
@@ -1556,7 +1569,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (bool) = 3 != 4
 )");
 }
@@ -1571,7 +1584,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (bool) = 3 < 4
 )");
 }
@@ -1586,7 +1599,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (bool) = 3 > 4
 )");
 }
@@ -1601,7 +1614,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (bool) = 3 <= 4
 )");
 }
@@ -1616,7 +1629,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (bool) = 3 >= 4
 )");
 }
@@ -1631,7 +1644,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 << 4
 )");
 }
@@ -1646,7 +1659,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 >> 4
 )");
 }
@@ -1662,7 +1675,7 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 >> 4
 %2 (u32) = %1 (u32) + 9
 %3 (bool) = 1 < %2 (u32)
@@ -1683,9 +1696,10 @@
     ASSERT_TRUE(r) << b.error();
 
     Disassembler d(b.builder.ir);
-    d.EmitBlockInstructions(b.current_flow_block);
+    d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
     EXPECT_EQ(d.AsString(), R"(%1 (f32) = bitcast(3)
 )");
 }
+
 }  // namespace
 }  // namespace tint::ir
diff --git a/src/tint/ir/debug.cc b/src/tint/ir/debug.cc
index aaff666..617b207 100644
--- a/src/tint/ir/debug.cc
+++ b/src/tint/ir/debug.cc
@@ -59,24 +59,24 @@
                 if (node_to_name.count(b) == 0) {
                     out << name_for(b) << R"( [label="block"])" << std::endl;
                 }
-                out << name_for(b) << " -> " << name_for(b->branch_target);
+                out << name_for(b) << " -> " << name_for(b->branch.target);
 
                 // Dashed lines to merge blocks
-                if (merge_nodes.count(b->branch_target) != 0) {
+                if (merge_nodes.count(b->branch.target) != 0) {
                     out << " [style=dashed]";
                 }
 
                 out << std::endl;
-                Graph(b->branch_target);
+                Graph(b->branch.target);
             },
             [&](const ir::Switch* s) {
                 out << name_for(s) << R"( [label="switch"])" << std::endl;
-                out << name_for(s->merge_target) << R"( [label="switch merge"])" << std::endl;
-                merge_nodes.insert(s->merge_target);
+                out << name_for(s->merge.target) << R"( [label="switch merge"])" << std::endl;
+                merge_nodes.insert(s->merge.target);
 
                 size_t i = 0;
                 for (const auto& c : s->cases) {
-                    out << name_for(c.start_target)
+                    out << name_for(c.start.target)
                         << R"( [label="case )" + std::to_string(i++) + R"("])" << std::endl;
                 }
                 out << name_for(s) << " -> {";
@@ -84,56 +84,56 @@
                     if (&c != &(s->cases[0])) {
                         out << ", ";
                     }
-                    out << name_for(c.start_target);
+                    out << name_for(c.start.target);
                 }
                 out << "}" << std::endl;
 
                 for (const auto& c : s->cases) {
-                    Graph(c.start_target);
+                    Graph(c.start.target);
                 }
-                Graph(s->merge_target);
+                Graph(s->merge.target);
             },
             [&](const ir::If* i) {
                 out << name_for(i) << R"( [label="if"])" << std::endl;
-                out << name_for(i->true_target) << R"( [label="true"])" << std::endl;
-                out << name_for(i->false_target) << R"( [label="false"])" << std::endl;
-                out << name_for(i->merge_target) << R"( [label="if merge"])" << std::endl;
-                merge_nodes.insert(i->merge_target);
+                out << name_for(i->true_.target) << R"( [label="true"])" << std::endl;
+                out << name_for(i->false_.target) << R"( [label="false"])" << std::endl;
+                out << name_for(i->merge.target) << R"( [label="if merge"])" << std::endl;
+                merge_nodes.insert(i->merge.target);
 
                 out << name_for(i) << " -> {";
-                out << name_for(i->true_target) << ", " << name_for(i->false_target);
+                out << name_for(i->true_.target) << ", " << name_for(i->false_.target);
                 out << "}" << std::endl;
 
                 // Subgraph if true/false branches so they draw on the same line
                 out << "subgraph sub_" << name_for(i) << " {" << std::endl;
                 out << R"(rank="same")" << std::endl;
-                out << name_for(i->true_target) << std::endl;
-                out << name_for(i->false_target) << std::endl;
+                out << name_for(i->true_.target) << std::endl;
+                out << name_for(i->false_.target) << std::endl;
                 out << "}" << std::endl;
 
-                Graph(i->true_target);
-                Graph(i->false_target);
-                Graph(i->merge_target);
+                Graph(i->true_.target);
+                Graph(i->false_.target);
+                Graph(i->merge.target);
             },
             [&](const ir::Loop* l) {
                 out << name_for(l) << R"( [label="loop"])" << std::endl;
-                out << name_for(l->start_target) << R"( [label="start"])" << std::endl;
-                out << name_for(l->continuing_target) << R"( [label="continuing"])" << std::endl;
-                out << name_for(l->merge_target) << R"( [label="loop merge"])" << std::endl;
-                merge_nodes.insert(l->merge_target);
+                out << name_for(l->start.target) << R"( [label="start"])" << std::endl;
+                out << name_for(l->continuing.target) << R"( [label="continuing"])" << std::endl;
+                out << name_for(l->merge.target) << R"( [label="loop merge"])" << std::endl;
+                merge_nodes.insert(l->merge.target);
 
                 // Subgraph the continuing and merge so they get drawn on the same line
                 out << "subgraph sub_" << name_for(l) << " {" << std::endl;
                 out << R"(rank="same")" << std::endl;
-                out << name_for(l->continuing_target) << std::endl;
-                out << name_for(l->merge_target) << std::endl;
+                out << name_for(l->continuing.target) << std::endl;
+                out << name_for(l->merge.target) << std::endl;
                 out << "}" << std::endl;
 
-                out << name_for(l) << " -> " << name_for(l->start_target) << std::endl;
+                out << name_for(l) << " -> " << name_for(l->start.target) << std::endl;
 
-                Graph(l->start_target);
-                Graph(l->continuing_target);
-                Graph(l->merge_target);
+                Graph(l->start.target);
+                Graph(l->continuing.target);
+                Graph(l->merge.target);
             },
             [&](const ir::Terminator*) {
                 // Already done
diff --git a/src/tint/ir/disassembler.cc b/src/tint/ir/disassembler.cc
index c2b9f98..e65bcca 100644
--- a/src/tint/ir/disassembler.cc
+++ b/src/tint/ir/disassembler.cc
@@ -66,6 +66,16 @@
     }
 }
 
+size_t Disassembler::GetIdForNode(const FlowNode* node) {
+    auto it = flow_node_to_id_.find(node);
+    if (it != flow_node_to_id_.end()) {
+        return it->second;
+    }
+    size_t id = next_node_id_++;
+    flow_node_to_id_[node] = id;
+    return id;
+}
+
 void Disassembler::Walk(const FlowNode* node) {
     if ((visited_.count(node) > 0) || (stop_nodes_.count(node) > 0)) {
         return;
@@ -75,7 +85,7 @@
     tint::Switch(
         node,
         [&](const ir::Function* f) {
-            Indent() << "Function" << std::endl;
+            Indent() << "%" << GetIdForNode(f) << " = Function" << std::endl;
 
             {
                 ScopedIndent func_indent(&indent_size_);
@@ -85,60 +95,74 @@
             Walk(f->end_target);
         },
         [&](const ir::Block* b) {
-            Indent() << "Block" << std::endl;
+            Indent() << "%" << GetIdForNode(b) << " = Block" << std::endl;
             EmitBlockInstructions(b);
-            Walk(b->branch_target);
+
+            if (b->branch.target->Is<Terminator>()) {
+                Indent() << "Return ";
+            } else {
+                Indent() << "Branch ";
+            }
+            out_ << GetIdForNode(b->branch.target);
+
+            for (const auto* v : b->branch.args) {
+                out_ << " ";
+                v->ToString(out_, mod_.symbols);
+            }
+            out_ << std::endl;
+
+            Walk(b->branch.target);
         },
         [&](const ir::Switch* s) {
-            Indent() << "Switch (" << s->condition << ")" << std::endl;
+            Indent() << "%" << GetIdForNode(s) << " = Switch (" << s->condition << ")" << std::endl;
 
             {
                 ScopedIndent switch_indent(&indent_size_);
-                ScopedStopNode scope(&stop_nodes_, s->merge_target);
+                ScopedStopNode scope(&stop_nodes_, s->merge.target);
                 for (const auto& c : s->cases) {
                     Indent() << "Case" << std::endl;
                     ScopedIndent case_indent(&indent_size_);
-                    Walk(c.start_target);
+                    Walk(c.start.target);
                 }
             }
 
             Indent() << "Switch Merge" << std::endl;
-            Walk(s->merge_target);
+            Walk(s->merge.target);
         },
         [&](const ir::If* i) {
-            Indent() << "if (" << i->condition << ")" << std::endl;
+            Indent() << "%" << GetIdForNode(i) << " = if (" << i->condition << ")" << std::endl;
             {
                 ScopedIndent if_indent(&indent_size_);
-                ScopedStopNode scope(&stop_nodes_, i->merge_target);
+                ScopedStopNode scope(&stop_nodes_, i->merge.target);
 
                 Indent() << "true branch" << std::endl;
-                Walk(i->true_target);
+                Walk(i->true_.target);
 
                 Indent() << "false branch" << std::endl;
-                Walk(i->false_target);
+                Walk(i->false_.target);
             }
 
             Indent() << "if merge" << std::endl;
-            Walk(i->merge_target);
+            Walk(i->merge.target);
         },
         [&](const ir::Loop* l) {
-            Indent() << "loop" << std::endl;
+            Indent() << "%" << GetIdForNode(l) << " = loop" << std::endl;
             {
-                ScopedStopNode loop_scope(&stop_nodes_, l->merge_target);
+                ScopedStopNode loop_scope(&stop_nodes_, l->merge.target);
                 ScopedIndent loop_indent(&indent_size_);
                 {
-                    ScopedStopNode inner_scope(&stop_nodes_, l->continuing_target);
+                    ScopedStopNode inner_scope(&stop_nodes_, l->continuing.target);
                     Indent() << "loop start" << std::endl;
-                    Walk(l->start_target);
+                    Walk(l->start.target);
                 }
 
                 Indent() << "loop continuing" << std::endl;
                 ScopedIndent continuing_indent(&indent_size_);
-                Walk(l->continuing_target);
+                Walk(l->continuing.target);
             }
 
             Indent() << "loop merge" << std::endl;
-            Walk(l->merge_target);
+            Walk(l->merge.target);
         },
         [&](const ir::Terminator*) { Indent() << "Function end" << std::endl; });
 }
diff --git a/src/tint/ir/disassembler.h b/src/tint/ir/disassembler.h
index 46b4ffd..03cbea1 100644
--- a/src/tint/ir/disassembler.h
+++ b/src/tint/ir/disassembler.h
@@ -17,6 +17,7 @@
 
 #include <sstream>
 #include <string>
+#include <unordered_map>
 #include <unordered_set>
 
 #include "src/tint/ir/flow_node.h"
@@ -46,11 +47,14 @@
   private:
     std::ostream& Indent();
     void Walk(const FlowNode* node);
+    size_t GetIdForNode(const FlowNode* node);
 
     const Module& mod_;
     std::stringstream out_;
     std::unordered_set<const FlowNode*> visited_;
     std::unordered_set<const FlowNode*> stop_nodes_;
+    std::unordered_map<const FlowNode*, size_t> flow_node_to_id_;
+    size_t next_node_id_ = 0;
     uint32_t indent_size_ = 0;
 };
 
diff --git a/src/tint/ir/if.h b/src/tint/ir/if.h
index 3a366b3..5c4acaa 100644
--- a/src/tint/ir/if.h
+++ b/src/tint/ir/if.h
@@ -15,6 +15,7 @@
 #ifndef SRC_TINT_IR_IF_H_
 #define SRC_TINT_IR_IF_H_
 
+#include "src/tint/ir/branch.h"
 #include "src/tint/ir/flow_node.h"
 #include "src/tint/ir/value.h"
 
@@ -33,12 +34,12 @@
     ~If() override;
 
     /// The true branch block
-    Block* true_target = nullptr;
+    Branch true_ = {};
     /// The false branch block
-    Block* false_target = nullptr;
+    Branch false_ = {};
     /// An block to reconvert the true/false barnches. The block always exists, but there maybe no
     /// branches into it. (e.g. if both branches `return`)
-    Block* merge_target = nullptr;
+    Branch merge = {};
     /// Value holding the condition result
     const Value* condition = nullptr;
 };
diff --git a/src/tint/ir/loop.h b/src/tint/ir/loop.h
index 5db8379..2b026e6 100644
--- a/src/tint/ir/loop.h
+++ b/src/tint/ir/loop.h
@@ -16,6 +16,7 @@
 #define SRC_TINT_IR_LOOP_H_
 
 #include "src/tint/ir/block.h"
+#include "src/tint/ir/branch.h"
 #include "src/tint/ir/flow_node.h"
 
 namespace tint::ir {
@@ -28,13 +29,13 @@
     ~Loop() override;
 
     /// The start block is the first block in a loop.
-    Block* start_target = nullptr;
+    Branch start = {};
     /// The continue target of the block.
-    Block* continuing_target = nullptr;
+    Branch continuing = {};
     /// The loop merge target. If the `loop` does a `return` then this block may not actually
     /// end up in the control flow. We need it if the loop does a `break` we know where to break
     /// too.
-    Block* merge_target = nullptr;
+    Branch merge = {};
 };
 
 }  // namespace tint::ir
diff --git a/src/tint/ir/switch.h b/src/tint/ir/switch.h
index 7fad948..6d3a952 100644
--- a/src/tint/ir/switch.h
+++ b/src/tint/ir/switch.h
@@ -17,6 +17,7 @@
 
 #include "src/tint/constant/value.h"
 #include "src/tint/ir/block.h"
+#include "src/tint/ir/branch.h"
 #include "src/tint/ir/flow_node.h"
 #include "src/tint/ir/value.h"
 
@@ -39,7 +40,7 @@
         /// The case selector for this node
         utils::Vector<CaseSelector, 4> selectors;
         /// The start block for the case block.
-        Block* start_target;
+        Branch start = {};
     };
 
     /// Constructor
@@ -47,7 +48,7 @@
     ~Switch() override;
 
     /// The switch merge target
-    Block* merge_target;
+    Branch merge = {};
 
     /// The switch case statements
     utils::Vector<Case, 4> cases;