[ir] Add conditions to if and switch nodes.

This CL updates the if and switch nodes to store the condition value in
a register. The EmitExpression is updated to return a Register and the
builder updated to emit the expressions for the if, break-if, while,
and switch expressions.

Bug: tint:1718
Change-Id: Ie710812c74e8b9423a4aa997db451d9cdf304feb
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/110784
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/builder_impl.cc b/src/tint/ir/builder_impl.cc
index 4525b33..b2dab25 100644
--- a/src/tint/ir/builder_impl.cc
+++ b/src/tint/ir/builder_impl.cc
@@ -234,7 +234,12 @@
 bool BuilderImpl::EmitIf(const ast::IfStatement* stmt) {
     auto* if_node = builder.CreateIf(stmt);
 
-    // TODO(dsinclair): Emit the condition expression into the current block
+    // Emit the if condition into the end of the preceeding block
+    auto reg = EmitExpression(stmt->condition);
+    if (!reg) {
+        return false;
+    }
+    if_node->condition = reg.Get();
 
     BranchTo(if_node);
 
@@ -243,8 +248,6 @@
     {
         FlowStackScope scope(this, if_node);
 
-        // TODO(dsinclair): set if condition register into if flow node
-
         current_flow_block = if_node->true_target;
         if (!EmitStatement(stmt->body)) {
             return false;
@@ -323,13 +326,17 @@
 
         current_flow_block = loop_node->start_target;
 
-        // TODO(dsinclair): Emit the instructions for the condition
+        // Emit the while condition into the start target of the loop
+        auto reg = EmitExpression(stmt->condition);
+        if (!reg) {
+            return false;
+        }
 
         // 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);
-        // TODO(dsinclair): set if condition register into if flow node
+        if_node->condition = reg.Get();
 
         BranchTo(if_node);
 
@@ -367,13 +374,17 @@
         current_flow_block = loop_node->start_target;
 
         if (stmt->condition) {
-            // TODO(dsinclair): Emit the instructions for the condition
+            // Emit the condition into the target target of the loop
+            auto reg = EmitExpression(stmt->condition);
+            if (!reg) {
+                return false;
+            }
 
             // 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);
-            // TODO(dsinclair): set if condition register into if flow node
+            if_node->condition = reg.Get();
 
             BranchTo(if_node);
             current_flow_block = if_node->merge_target;
@@ -401,7 +412,12 @@
 bool BuilderImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
     auto* switch_node = builder.CreateSwitch(stmt);
 
-    // TODO(dsinclair): Emit the condition expression into the current block
+    // Emit the condition into the preceeding block
+    auto reg = EmitExpression(stmt->condition);
+    if (!reg) {
+        return false;
+    }
+    switch_node->condition = reg.Get();
 
     BranchTo(switch_node);
 
@@ -466,7 +482,12 @@
 bool BuilderImpl::EmitBreakIf(const ast::BreakIfStatement* stmt) {
     auto* if_node = builder.CreateIf(stmt);
 
-    // TODO(dsinclair): Emit the condition expression into the current block
+    // Emit the break-if condition into the end of the preceeding block
+    auto reg = EmitExpression(stmt->condition);
+    if (!reg) {
+        return false;
+    }
+    if_node->condition = reg.Get();
 
     BranchTo(if_node);
 
@@ -478,8 +499,6 @@
 
     auto* loop = current_control->As<Loop>();
 
-    // TODO(dsinclair): set if condition register into if flow node
-
     current_flow_block = if_node->true_target;
     BranchTo(loop->merge_target);
 
@@ -496,7 +515,7 @@
     return true;
 }
 
-bool BuilderImpl::EmitExpression(const ast::Expression* expr) {
+utils::Result<Register> BuilderImpl::EmitExpression(const ast::Expression* expr) {
     return tint::Switch(
         expr,
         // [&](const ast::IndexAccessorExpression* a) { return EmitIndexAccessor(a); },
@@ -512,7 +531,7 @@
             diagnostics_.add_warning(
                 tint::diag::System::IR,
                 "unknown expression type: " + std::string(expr->TypeInfo().name), expr->source);
-            return false;
+            return utils::Failure;
         });
 }
 
diff --git a/src/tint/ir/builder_impl.h b/src/tint/ir/builder_impl.h
index f1113dc..18c4d3e 100644
--- a/src/tint/ir/builder_impl.h
+++ b/src/tint/ir/builder_impl.h
@@ -139,7 +139,7 @@
     /// Emits an expression
     /// @param expr the expression to emit
     /// @returns true if successful, false otherwise
-    bool EmitExpression(const ast::Expression* expr);
+    utils::Result<Register> EmitExpression(const ast::Expression* expr);
 
     /// Emits a variable
     /// @param var the variable to emit
diff --git a/src/tint/ir/builder_impl_test.cc b/src/tint/ir/builder_impl_test.cc
index f10b285..04c8d00 100644
--- a/src/tint/ir/builder_impl_test.cc
+++ b/src/tint/ir/builder_impl_test.cc
@@ -80,8 +80,6 @@
     ASSERT_NE(ir_if, nullptr);
     EXPECT_TRUE(ir_if->Is<ir::If>());
 
-    // TODO(dsinclair): check condition
-
     auto* flow = ir_if->As<ir::If>();
     ASSERT_NE(flow->true_target, nullptr);
     ASSERT_NE(flow->false_target, nullptr);
@@ -101,6 +99,11 @@
     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);
+
+    // Check condition
+    auto op = flow->condition;
+    ASSERT_TRUE(op.IsBool());
+    EXPECT_TRUE(op.AsBool());
 }
 
 TEST_F(IR_BuilderImplTest, IfStatement_TrueReturns) {
@@ -497,6 +500,11 @@
 
     EXPECT_EQ(func->start_target->branch_target, ir_loop);
     EXPECT_EQ(loop_flow->merge_target->branch_target, nullptr);
+
+    // Check condition
+    auto op = if_flow->condition;
+    ASSERT_TRUE(op.IsBool());
+    EXPECT_TRUE(op.AsBool());
 }
 
 TEST_F(IR_BuilderImplTest, Loop_WithOnlyReturn) {
@@ -937,6 +945,11 @@
     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);
+
+    // Check condition
+    auto op = if_flow->condition;
+    ASSERT_TRUE(op.IsBool());
+    EXPECT_FALSE(op.AsBool());
 }
 
 TEST_F(IR_BuilderImplTest, While_Return) {
@@ -1056,6 +1069,11 @@
     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);
+
+    // Check condition
+    auto op = if_flow->condition;
+    ASSERT_TRUE(op.IsBool());
+    EXPECT_FALSE(op.AsBool());
 }
 
 TEST_F(IR_BuilderImplTest, For_NoInitCondOrContinuing) {
@@ -1151,6 +1169,11 @@
     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);
+
+    // Check condition
+    auto op = flow->condition;
+    ASSERT_TRUE(op.IsI32());
+    EXPECT_EQ(1_i, op.AsI32());
 }
 
 TEST_F(IR_BuilderImplTest, Switch_OnlyDefault) {
diff --git a/src/tint/ir/debug.cc b/src/tint/ir/debug.cc
index 749c654..bcac46d 100644
--- a/src/tint/ir/debug.cc
+++ b/src/tint/ir/debug.cc
@@ -221,7 +221,7 @@
                 Walk(b->branch_target);
             },
             [&](const ir::Switch* s) {
-                indent() << "Switch" << std::endl;
+                indent() << "Switch (" << s->condition.AsString() << ")" << std::endl;
 
                 {
                     ScopedIndent switch_indent(&indent_size);
@@ -237,15 +237,15 @@
                 Walk(s->merge_target);
             },
             [&](const ir::If* i) {
-                indent() << "if" << std::endl;
+                indent() << "if (" << i->condition.AsString() << ")" << std::endl;
                 {
                     ScopedIndent if_indent(&indent_size);
                     ScopedStopNode scope(&stop_nodes, i->merge_target);
 
-                    indent() << "If true" << std::endl;
+                    indent() << "true branch" << std::endl;
                     Walk(i->true_target);
 
-                    indent() << "If false" << std::endl;
+                    indent() << "false branch" << std::endl;
                     Walk(i->false_target);
                 }
 
diff --git a/src/tint/ir/if.h b/src/tint/ir/if.h
index 2d28aa1..109b990 100644
--- a/src/tint/ir/if.h
+++ b/src/tint/ir/if.h
@@ -17,6 +17,7 @@
 
 #include "src/tint/ast/if_statement.h"
 #include "src/tint/ir/flow_node.h"
+#include "src/tint/ir/register.h"
 
 // Forward declarations
 namespace tint::ir {
@@ -43,6 +44,8 @@
     /// 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;
+    /// Register holding the condition result
+    Register condition;
 };
 
 }  // namespace tint::ir
diff --git a/src/tint/ir/register.cc b/src/tint/ir/register.cc
index 815cb02..ba156e2 100644
--- a/src/tint/ir/register.cc
+++ b/src/tint/ir/register.cc
@@ -16,6 +16,8 @@
 
 namespace tint::ir {
 
+Register::Register() : kind_(Kind::kUninitialized), data_(Id(0)) {}
+
 Register::Register(Id id) : kind_(Kind::kTemp), data_(id) {}
 
 Register::Register(f32 f) : kind_(Kind::kF32), data_(f) {}
@@ -57,6 +59,8 @@
             return "%v" + std::to_string(AsVarData().id);
         case Kind::kBool:
             return AsBool() ? "true" : "false";
+        case Kind::kUninitialized:
+            break;
     }
     return "unknown register";
 }
diff --git a/src/tint/ir/register.h b/src/tint/ir/register.h
index dd56aa9..54d863d 100644
--- a/src/tint/ir/register.h
+++ b/src/tint/ir/register.h
@@ -44,6 +44,10 @@
     };
 
     /// Constructor
+    /// Creates a uninitialized register
+    Register();
+
+    /// Constructor
     /// @param id the id for the register
     explicit Register(Id id);
 
@@ -134,6 +138,8 @@
   private:
     /// The type of the register
     enum class Kind {
+        /// A uninitialized register
+        kUninitialized,
         /// A temporary allocated register
         kTemp,
         /// A f32 register
diff --git a/src/tint/ir/register_test.cc b/src/tint/ir/register_test.cc
index a19a773..af4b659 100644
--- a/src/tint/ir/register_test.cc
+++ b/src/tint/ir/register_test.cc
@@ -132,5 +132,17 @@
     EXPECT_FALSE(r.IsBool());
 }
 
+TEST_F(IR_RegisterTest, uninitialized) {
+    Register r;
+
+    EXPECT_FALSE(r.IsF32());
+    EXPECT_FALSE(r.IsF16());
+    EXPECT_FALSE(r.IsI32());
+    EXPECT_FALSE(r.IsU32());
+    EXPECT_FALSE(r.IsTemp());
+    EXPECT_FALSE(r.IsVar());
+    EXPECT_FALSE(r.IsBool());
+}
+
 }  // namespace
 }  // namespace tint::ir
diff --git a/src/tint/ir/switch.h b/src/tint/ir/switch.h
index 39d3d06..73284cf 100644
--- a/src/tint/ir/switch.h
+++ b/src/tint/ir/switch.h
@@ -17,6 +17,7 @@
 
 #include "src/tint/ir/block.h"
 #include "src/tint/ir/flow_node.h"
+#include "src/tint/ir/register.h"
 
 // Forward declarations
 namespace tint::ast {
@@ -50,6 +51,9 @@
 
     /// The switch case statements
     utils::Vector<Case, 4> cases;
+
+    /// Register holding the condition result
+    Register condition;
 };
 
 }  // namespace tint::ir