[IR] Add support for `while` statements.
This CL adds `while` statement support into the IR builder, converting them into loop flow nodes. Bug: tint:1718 Change-Id: I6dbaa24a0082463281dc933f02805169836fedd6 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/107803 Reviewed-by: Ben Clayton <bclayton@google.com> Commit-Queue: Dan Sinclair <dsinclair@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
7cd8db1155
commit
0dce067e28
|
@ -59,7 +59,7 @@ If* Builder::CreateIf(const ast::Statement* stmt) {
|
|||
return ir_if;
|
||||
}
|
||||
|
||||
Loop* Builder::CreateLoop(const ast::LoopStatement* stmt) {
|
||||
Loop* Builder::CreateLoop(const ast::Statement* stmt) {
|
||||
auto* ir_loop = ir.flow_nodes.Create<Loop>(stmt);
|
||||
ir_loop->start_target = CreateBlock();
|
||||
ir_loop->continuing_target = CreateBlock();
|
||||
|
|
|
@ -60,10 +60,10 @@ class Builder {
|
|||
/// @returns the flow node
|
||||
If* CreateIf(const ast::Statement* stmt);
|
||||
|
||||
/// Creates a loop flow node for the given ast::LoopStatement
|
||||
/// @param stmt the ast::LoopStatement
|
||||
/// Creates a loop flow node for the given ast loop, while or for statement
|
||||
/// @param stmt the ast loop, while or for statement
|
||||
/// @returns the flow node
|
||||
Loop* CreateLoop(const ast::LoopStatement* stmt);
|
||||
Loop* CreateLoop(const ast::Statement* stmt);
|
||||
|
||||
/// Creates a switch flow node for the given ast::SwitchStatement
|
||||
/// @param stmt the ast::SwitchStatment
|
||||
|
|
|
@ -22,10 +22,12 @@
|
|||
#include "src/tint/ast/fallthrough_statement.h"
|
||||
#include "src/tint/ast/function.h"
|
||||
#include "src/tint/ast/if_statement.h"
|
||||
#include "src/tint/ast/loop_statement.h"
|
||||
#include "src/tint/ast/return_statement.h"
|
||||
#include "src/tint/ast/statement.h"
|
||||
#include "src/tint/ast/static_assert.h"
|
||||
#include "src/tint/ast/switch_statement.h"
|
||||
#include "src/tint/ast/while_statement.h"
|
||||
#include "src/tint/ir/function.h"
|
||||
#include "src/tint/ir/if.h"
|
||||
#include "src/tint/ir/loop.h"
|
||||
|
@ -209,7 +211,7 @@ bool BuilderImpl::EmitStatement(const ast::Statement* stmt) {
|
|||
[&](const ast::IfStatement* i) { return EmitIf(i); },
|
||||
[&](const ast::LoopStatement* l) { return EmitLoop(l); },
|
||||
// [&](const ast::ForLoopStatement* l) { },
|
||||
// [&](const ast::WhileStatement* l) { },
|
||||
[&](const ast::WhileStatement* l) { return EmitWhile(l); },
|
||||
[&](const ast::ReturnStatement* r) { return EmitReturn(r); },
|
||||
[&](const ast::SwitchStatement* s) { return EmitSwitch(s); },
|
||||
// [&](const ast::VariableDeclStatement* v) { },
|
||||
|
@ -313,6 +315,43 @@ bool BuilderImpl::EmitLoop(const ast::LoopStatement* stmt) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool BuilderImpl::EmitWhile(const ast::WhileStatement* 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);
|
||||
|
||||
BranchTo(loop_node);
|
||||
|
||||
ast_to_flow_[stmt] = loop_node;
|
||||
|
||||
{
|
||||
FlowStackScope scope(this, loop_node);
|
||||
|
||||
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);
|
||||
// TODO(dsinclair): set if condition register into if flow node
|
||||
|
||||
BranchTo(if_node);
|
||||
|
||||
current_flow_block_ = if_node->merge_target;
|
||||
if (!EmitStatement(stmt->body)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BuilderImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
|
||||
auto* switch_node = builder_.CreateSwitch(stmt);
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ class IfStatement;
|
|||
class LoopStatement;
|
||||
class ReturnStatement;
|
||||
class Statement;
|
||||
class WhileStatement;
|
||||
} // namespace tint::ast
|
||||
namespace tint::ir {
|
||||
class Block;
|
||||
|
@ -102,6 +103,11 @@ class BuilderImpl {
|
|||
/// @returns true if successful, false otherwise.
|
||||
bool EmitLoop(const ast::LoopStatement* stmt);
|
||||
|
||||
/// Emits a loop control node to the IR.
|
||||
/// @param stmt the while statement
|
||||
/// @returns true if successful, false otherwise.
|
||||
bool EmitWhile(const ast::WhileStatement* stmt);
|
||||
|
||||
/// Emits a switch statement
|
||||
/// @param stmt the switch statement
|
||||
/// @returns true if successful, false otherwise.
|
||||
|
|
|
@ -822,6 +822,126 @@ TEST_F(IRBuilderImplTest, Loop_Nested) {
|
|||
EXPECT_EQ(loop_flow_a->merge_target->branch_target, func->end_target);
|
||||
}
|
||||
|
||||
TEST_F(IRBuilderImplTest, While) {
|
||||
// {
|
||||
// while false {
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func -> while -> loop_start -> if true
|
||||
// -> if false
|
||||
//
|
||||
// [if true] -> if merge
|
||||
// [if false] -> while merge
|
||||
// [if merge] -> loop continuing
|
||||
// [loop continuing] -> loop start
|
||||
// [while merge] -> func end
|
||||
//
|
||||
auto* ast_while = While(false, Block());
|
||||
WrapInFunction(ast_while);
|
||||
auto& b = Build();
|
||||
|
||||
auto r = b.Build();
|
||||
ASSERT_TRUE(r) << b.error();
|
||||
auto m = r.Move();
|
||||
|
||||
auto* ir_while = b.FlowNodeForAstNode(ast_while);
|
||||
ASSERT_NE(ir_while, nullptr);
|
||||
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->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_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(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);
|
||||
}
|
||||
|
||||
TEST_F(IRBuilderImplTest, While_Return) {
|
||||
// {
|
||||
// while true {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func -> while -> if true
|
||||
// if false
|
||||
//
|
||||
// [if true] -> if merge
|
||||
// [if false] -> while merge
|
||||
// [if merge] -> func end
|
||||
// [while merge] -> func end
|
||||
//
|
||||
auto* ast_while = While(true, Block(Return()));
|
||||
WrapInFunction(ast_while);
|
||||
auto& b = Build();
|
||||
|
||||
auto r = b.Build();
|
||||
ASSERT_TRUE(r) << b.error();
|
||||
auto m = r.Move();
|
||||
|
||||
auto* ir_while = b.FlowNodeForAstNode(ast_while);
|
||||
ASSERT_NE(ir_while, nullptr);
|
||||
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->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_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(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);
|
||||
}
|
||||
|
||||
TEST_F(IRBuilderImplTest, Switch) {
|
||||
// func -> switch -> case 1
|
||||
// -> case 2
|
||||
|
|
|
@ -14,11 +14,17 @@
|
|||
|
||||
#include "src/tint/ir/loop.h"
|
||||
|
||||
#include "src/tint/ast/for_loop_statement.h"
|
||||
#include "src/tint/ast/loop_statement.h"
|
||||
#include "src/tint/ast/while_statement.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::ir::Loop);
|
||||
|
||||
namespace tint::ir {
|
||||
|
||||
Loop::Loop(const ast::LoopStatement* stmt) : Base(), source(stmt) {}
|
||||
Loop::Loop(const ast::Statement* s) : Base(), source(s) {
|
||||
TINT_ASSERT(IR, (s->IsAnyOf<ast::LoopStatement, ast::WhileStatement, ast::ForLoopStatement>()));
|
||||
}
|
||||
|
||||
Loop::~Loop() = default;
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#ifndef SRC_TINT_IR_LOOP_H_
|
||||
#define SRC_TINT_IR_LOOP_H_
|
||||
|
||||
#include "src/tint/ast/loop_statement.h"
|
||||
#include "src/tint/ast/statement.h"
|
||||
#include "src/tint/ir/block.h"
|
||||
#include "src/tint/ir/flow_node.h"
|
||||
|
||||
|
@ -25,12 +25,12 @@ namespace tint::ir {
|
|||
class Loop : public Castable<Loop, FlowNode> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param stmt the ast::LoopStatement
|
||||
explicit Loop(const ast::LoopStatement* stmt);
|
||||
/// @param stmt the loop, while or for statement.
|
||||
explicit Loop(const ast::Statement* stmt);
|
||||
~Loop() override;
|
||||
|
||||
/// The ast loop statement this ir loop is created from.
|
||||
const ast::LoopStatement* source;
|
||||
/// The ast loop, while or for statement this ir loop is created from.
|
||||
const ast::Statement* source;
|
||||
|
||||
/// The start block is the first block in a loop.
|
||||
Block* start_target = nullptr;
|
||||
|
|
Loading…
Reference in New Issue