[ir] Testing updates.

This CL renames the `Build` method in the test helper to be
`CreateBuilder` to maek the intention clearer. A second,
`CreateEmptyBuilder` is provided to allow for easier testing of
expressions.

The `current_flow_block` and `builder` are moved to public so they can
be used/updated during testing.

Bug: tint:1718
Change-Id: I663d4c7a3c76e6bf5396ca05f54fe634d35d0d56
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/110781
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/ir/builder_impl.cc b/src/tint/ir/builder_impl.cc
index f332052..fb6200c 100644
--- a/src/tint/ir/builder_impl.cc
+++ b/src/tint/ir/builder_impl.cc
@@ -77,20 +77,20 @@
 
 }  // namespace
 
-BuilderImpl::BuilderImpl(const Program* program) : builder_(program) {}
+BuilderImpl::BuilderImpl(const Program* program) : builder(program) {}
 
 BuilderImpl::~BuilderImpl() = default;
 
 void BuilderImpl::BranchTo(FlowNode* node) {
-    TINT_ASSERT(IR, current_flow_block_);
-    TINT_ASSERT(IR, !IsBranched(current_flow_block_));
+    TINT_ASSERT(IR, current_flow_block);
+    TINT_ASSERT(IR, !IsBranched(current_flow_block));
 
-    builder_.Branch(current_flow_block_, node);
-    current_flow_block_ = nullptr;
+    builder.Branch(current_flow_block, node);
+    current_flow_block = nullptr;
 }
 
 void BuilderImpl::BranchToIfNeeded(FlowNode* node) {
-    if (!current_flow_block_ || IsBranched(current_flow_block_)) {
+    if (!current_flow_block || IsBranched(current_flow_block)) {
         return;
     }
     BranchTo(node);
@@ -112,7 +112,7 @@
 }
 
 ResultType BuilderImpl::Build() {
-    auto* sem = builder_.ir.program->Sem().Module();
+    auto* sem = builder.ir.program->Sem().Module();
 
     for (auto* decl : sem->DependencyOrderedDeclarations()) {
         bool ok = tint::Switch(
@@ -140,27 +140,27 @@
         }
     }
 
-    return ResultType{std::move(builder_.ir)};
+    return ResultType{std::move(builder.ir)};
 }
 
 bool BuilderImpl::EmitFunction(const ast::Function* ast_func) {
     // The flow stack should have been emptied when the previous function finshed building.
     TINT_ASSERT(IR, flow_stack.IsEmpty());
 
-    auto* ir_func = builder_.CreateFunction(ast_func);
+    auto* ir_func = builder.CreateFunction(ast_func);
     current_function_ = ir_func;
-    builder_.ir.functions.Push(ir_func);
+    builder.ir.functions.Push(ir_func);
 
     ast_to_flow_[ast_func] = ir_func;
 
     if (ast_func->IsEntryPoint()) {
-        builder_.ir.entry_points.Push(ir_func);
+        builder.ir.entry_points.Push(ir_func);
     }
 
     {
         FlowStackScope scope(this, ir_func);
 
-        current_flow_block_ = ir_func->start_target;
+        current_flow_block = ir_func->start_target;
         if (!EmitStatements(ast_func->body->statements)) {
             return false;
         }
@@ -171,7 +171,7 @@
     }
 
     TINT_ASSERT(IR, flow_stack.IsEmpty());
-    current_flow_block_ = nullptr;
+    current_flow_block = nullptr;
     current_function_ = nullptr;
 
     return true;
@@ -185,7 +185,7 @@
 
         // If the current flow block has a branch target then the rest of the statements in this
         // block are dead code. Skip them.
-        if (!current_flow_block_ || IsBranched(current_flow_block_)) {
+        if (!current_flow_block || IsBranched(current_flow_block)) {
             break;
         }
     }
@@ -229,7 +229,7 @@
 }
 
 bool BuilderImpl::EmitIf(const ast::IfStatement* stmt) {
-    auto* if_node = builder_.CreateIf(stmt);
+    auto* if_node = builder.CreateIf(stmt);
 
     // TODO(dsinclair): Emit the condition expression into the current block
 
@@ -242,34 +242,34 @@
 
         // TODO(dsinclair): set if condition register into if flow node
 
-        current_flow_block_ = if_node->true_target;
+        current_flow_block = if_node->true_target;
         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);
 
-        current_flow_block_ = if_node->false_target;
+        current_flow_block = if_node->false_target;
         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);
     }
-    current_flow_block_ = nullptr;
+    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;
+        current_flow_block = if_node->merge_target;
     }
 
     return true;
 }
 
 bool BuilderImpl::EmitLoop(const ast::LoopStatement* stmt) {
-    auto* loop_node = builder_.CreateLoop(stmt);
+    auto* loop_node = builder.CreateLoop(stmt);
 
     BranchTo(loop_node);
 
@@ -278,7 +278,7 @@
     {
         FlowStackScope scope(this, loop_node);
 
-        current_flow_block_ = loop_node->start_target;
+        current_flow_block = loop_node->start_target;
         if (!EmitStatement(stmt->body)) {
             return false;
         }
@@ -286,7 +286,7 @@
         // The current block didn't `break`, `return` or `continue`, go to the continuing block.
         BranchToIfNeeded(loop_node->continuing_target);
 
-        current_flow_block_ = loop_node->continuing_target;
+        current_flow_block = loop_node->continuing_target;
         if (stmt->continuing) {
             if (!EmitStatement(stmt->continuing)) {
                 return false;
@@ -299,17 +299,17 @@
 
     // 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;
+    current_flow_block = loop_node->merge_target;
     if (!IsConnected(loop_node->merge_target)) {
-        current_flow_block_ = nullptr;
+        current_flow_block = nullptr;
     }
     return true;
 }
 
 bool BuilderImpl::EmitWhile(const ast::WhileStatement* stmt) {
-    auto* loop_node = builder_.CreateLoop(stmt);
+    auto* loop_node = builder.CreateLoop(stmt);
     // Continue is always empty, just go back to the start
-    builder_.Branch(loop_node->continuing_target, loop_node->start_target);
+    builder.Branch(loop_node->continuing_target, loop_node->start_target);
 
     BranchTo(loop_node);
 
@@ -318,19 +318,19 @@
     {
         FlowStackScope scope(this, loop_node);
 
-        current_flow_block_ = loop_node->start_target;
+        current_flow_block = loop_node->start_target;
 
         // TODO(dsinclair): Emit the instructions for the condition
 
         // Create an if (cond) {} else {break;} control flow
-        auto* if_node = builder_.CreateIf(nullptr);
-        builder_.Branch(if_node->true_target, if_node->merge_target);
-        builder_.Branch(if_node->false_target, loop_node->merge_target);
+        auto* if_node = builder.CreateIf(nullptr);
+        builder.Branch(if_node->true_target, if_node->merge_target);
+        builder.Branch(if_node->false_target, loop_node->merge_target);
         // TODO(dsinclair): set if condition register into if flow node
 
         BranchTo(if_node);
 
-        current_flow_block_ = if_node->merge_target;
+        current_flow_block = if_node->merge_target;
         if (!EmitStatement(stmt->body)) {
             return false;
         }
@@ -339,13 +339,13 @@
     }
     // 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;
     return true;
 }
 
 bool BuilderImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
-    auto* loop_node = builder_.CreateLoop(stmt);
-    builder_.Branch(loop_node->continuing_target, loop_node->start_target);
+    auto* loop_node = builder.CreateLoop(stmt);
+    builder.Branch(loop_node->continuing_target, loop_node->start_target);
 
     if (stmt->initializer) {
         // Emit the for initializer before branching to the loop
@@ -361,19 +361,19 @@
     {
         FlowStackScope scope(this, loop_node);
 
-        current_flow_block_ = loop_node->start_target;
+        current_flow_block = loop_node->start_target;
 
         if (stmt->condition) {
             // TODO(dsinclair): Emit the instructions for the condition
 
             // Create an if (cond) {} else {break;} control flow
-            auto* if_node = builder_.CreateIf(nullptr);
-            builder_.Branch(if_node->true_target, if_node->merge_target);
-            builder_.Branch(if_node->false_target, loop_node->merge_target);
+            auto* if_node = builder.CreateIf(nullptr);
+            builder.Branch(if_node->true_target, if_node->merge_target);
+            builder.Branch(if_node->false_target, loop_node->merge_target);
             // TODO(dsinclair): set if condition register into if flow node
 
             BranchTo(if_node);
-            current_flow_block_ = if_node->merge_target;
+            current_flow_block = if_node->merge_target;
         }
 
         if (!EmitStatement(stmt->body)) {
@@ -383,7 +383,7 @@
         BranchToIfNeeded(loop_node->continuing_target);
 
         if (stmt->continuing) {
-            current_flow_block_ = loop_node->continuing_target;
+            current_flow_block = loop_node->continuing_target;
             if (!EmitStatement(stmt->continuing)) {
                 return false;
             }
@@ -391,12 +391,12 @@
     }
     // 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;
     return true;
 }
 
 bool BuilderImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
-    auto* switch_node = builder_.CreateSwitch(stmt);
+    auto* switch_node = builder.CreateSwitch(stmt);
 
     // TODO(dsinclair): Emit the condition expression into the current block
 
@@ -408,17 +408,17 @@
         FlowStackScope scope(this, switch_node);
 
         for (const auto* c : stmt->body) {
-            current_flow_block_ = builder_.CreateCase(switch_node, c->selectors);
+            current_flow_block = builder.CreateCase(switch_node, c->selectors);
             if (!EmitStatement(c->body)) {
                 return false;
             }
             BranchToIfNeeded(switch_node->merge_target);
         }
     }
-    current_flow_block_ = nullptr;
+    current_flow_block = nullptr;
 
     if (IsConnected(switch_node->merge_target)) {
-        current_flow_block_ = switch_node->merge_target;
+        current_flow_block = switch_node->merge_target;
     }
 
     return true;
@@ -461,7 +461,7 @@
 }
 
 bool BuilderImpl::EmitBreakIf(const ast::BreakIfStatement* stmt) {
-    auto* if_node = builder_.CreateIf(stmt);
+    auto* if_node = builder.CreateIf(stmt);
 
     // TODO(dsinclair): Emit the condition expression into the current block
 
@@ -477,13 +477,13 @@
 
     // TODO(dsinclair): set if condition register into if flow node
 
-    current_flow_block_ = if_node->true_target;
+    current_flow_block = if_node->true_target;
     BranchTo(loop->merge_target);
 
-    current_flow_block_ = if_node->false_target;
+    current_flow_block = if_node->false_target;
     BranchTo(if_node->merge_target);
 
-    current_flow_block_ = if_node->merge_target;
+    current_flow_block = if_node->merge_target;
 
     // 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.
diff --git a/src/tint/ir/builder_impl.h b/src/tint/ir/builder_impl.h
index 186de64..c917413 100644
--- a/src/tint/ir/builder_impl.h
+++ b/src/tint/ir/builder_impl.h
@@ -178,6 +178,12 @@
     /// The stack of flow control blocks.
     utils::Vector<FlowNode*, 8> flow_stack;
 
+    /// The IR builder being used by the impl.
+    Builder builder;
+
+    /// The current flow block for expressions
+    Block* current_flow_block = nullptr;
+
   private:
     enum class ControlFlags { kNone, kExcludeSwitch };
 
@@ -186,11 +192,8 @@
 
     FlowNode* FindEnclosingControl(ControlFlags flags);
 
-    Builder builder_;
-
     diag::List diagnostics_;
 
-    Block* current_flow_block_ = nullptr;
     Function* current_function_ = nullptr;
 
     /// Map from ast nodes to flow nodes, used to retrieve the flow node for a given AST node.
diff --git a/src/tint/ir/builder_impl_test.cc b/src/tint/ir/builder_impl_test.cc
index 14f01ba..510d1e0 100644
--- a/src/tint/ir/builder_impl_test.cc
+++ b/src/tint/ir/builder_impl_test.cc
@@ -28,7 +28,7 @@
     // func -> start -> end
 
     Func("f", utils::Empty, ty.void_(), utils::Empty);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -50,7 +50,7 @@
 TEST_F(IRBuilderImplTest, EntryPoint) {
     Func("f", utils::Empty, ty.void_(), utils::Empty,
          utils::Vector{Stage(ast::PipelineStage::kFragment)});
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -70,7 +70,7 @@
     //
     auto* ast_if = If(true, Block(), Else(Block()));
     WrapInFunction(ast_if);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -113,7 +113,7 @@
     //
     auto* ast_if = If(true, Block(Return()));
     WrapInFunction(ast_if);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -154,7 +154,7 @@
     //
     auto* ast_if = If(true, Block(), Else(Block(Return())));
     WrapInFunction(ast_if);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -195,7 +195,7 @@
     //
     auto* ast_if = If(true, Block(Return()), Else(Block(Return())));
     WrapInFunction(ast_if);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -246,7 +246,7 @@
     auto* ast_loop = Loop(Block(Break()));
     auto* ast_if = If(true, Block(ast_loop));
     WrapInFunction(ast_if);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -289,7 +289,7 @@
     //
     auto* ast_loop = Loop(Block(Break()));
     WrapInFunction(ast_loop);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -333,7 +333,7 @@
     auto* ast_if = If(true, Block(Break()));
     auto* ast_loop = Loop(Block(ast_if, Continue()));
     WrapInFunction(ast_loop);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -392,7 +392,7 @@
     auto* ast_break_if = BreakIf(true);
     auto* ast_loop = Loop(Block(), Block(ast_break_if));
     WrapInFunction(ast_loop);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -452,7 +452,7 @@
     auto* ast_if = If(true, Block(Return()));
     auto* ast_loop = Loop(Block(ast_if, Continue()));
     WrapInFunction(ast_loop);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -520,7 +520,7 @@
     // block.
     auto* ast_loop = Loop(Block(Return(), Continue()));
     WrapInFunction(ast_loop, If(true, Block(Return())));
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -578,7 +578,7 @@
     auto* ast_loop = Loop(Block(Return()), Block(ast_break_if));
     auto* ast_if = If(true, Block(Return()));
     WrapInFunction(Block(ast_loop, ast_if));
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -635,7 +635,7 @@
     auto* ast_if = If(true, Block(Break()), Else(Block(Break())));
     auto* ast_loop = Loop(Block(ast_if, Continue()));
     WrapInFunction(ast_loop);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -744,7 +744,7 @@
     auto* ast_loop_a = Loop(Block(ast_loop_b, ast_if_d));
 
     WrapInFunction(ast_loop_a);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -896,7 +896,7 @@
     //
     auto* ast_while = While(false, Block());
     WrapInFunction(ast_while);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -956,7 +956,7 @@
     //
     auto* ast_while = While(true, Block(Return()));
     WrapInFunction(ast_while);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -1015,7 +1015,7 @@
     //
     auto* ast_for = For(Decl(Var("i", ty.i32())), LessThan("i", 10_a), Increment("i"), Block());
     WrapInFunction(ast_for);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -1067,7 +1067,7 @@
     //
     auto* ast_for = For(nullptr, nullptr, nullptr, Block(Break()));
     WrapInFunction(ast_for);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -1111,7 +1111,7 @@
                            Case(utils::Vector{CaseSelector(1_i)}, Block()), DefaultCase(Block())});
 
     WrapInFunction(ast_switch);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -1159,7 +1159,7 @@
     auto* ast_switch = Switch(1_i, utils::Vector{DefaultCase(Block())});
 
     WrapInFunction(ast_switch);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -1211,7 +1211,7 @@
                                                  DefaultCase(Block())});
 
     WrapInFunction(ast_switch);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
@@ -1275,7 +1275,7 @@
     auto* ast_if = If(true, Block(Return()));
 
     WrapInFunction(ast_switch, ast_if);
-    auto& b = Build();
+    auto& b = CreateBuilder();
 
     auto r = b.Build();
     ASSERT_TRUE(r) << b.error();
diff --git a/src/tint/ir/test_helper.h b/src/tint/ir/test_helper.h
index fdc4787..f2bdeb5 100644
--- a/src/tint/ir/test_helper.h
+++ b/src/tint/ir/test_helper.h
@@ -36,7 +36,7 @@
     /// @note The builder is only created once. Multiple calls to Build() will
     /// return the same builder without rebuilding.
     /// @return the builder
-    BuilderImpl& Build() {
+    BuilderImpl& CreateBuilder() {
         if (gen_) {
             return *gen_;
         }
@@ -47,6 +47,16 @@
         return *gen_;
     }
 
+    /// Creates a BuilderImpl without an originating program. This is used for testing the
+    /// expressions which don't require the full builder implementation. The current flow block
+    /// is initialized with an empty block.
+    /// @returns the BuilderImpl for testing.
+    BuilderImpl& CreateEmptyBuilder() {
+        gen_ = std::make_unique<BuilderImpl>(nullptr);
+        gen_->current_flow_block = gen_->builder.CreateBlock();
+        return *gen_;
+    }
+
     /// The program built with a call to Build()
     std::unique_ptr<Program> program;