[IR] Add support for `for` statements
This CL adds the ability to convert a for statement into a loop control flow node. Bug: tint:1718 Change-Id: Ibd55ae3b202518d3362267eaa1f507dce6a9fe56 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/107804 Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Dan Sinclair <dsinclair@chromium.org> Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
0dce067e28
commit
1c755b68e6
|
@ -20,6 +20,7 @@
|
||||||
#include "src/tint/ast/break_statement.h"
|
#include "src/tint/ast/break_statement.h"
|
||||||
#include "src/tint/ast/continue_statement.h"
|
#include "src/tint/ast/continue_statement.h"
|
||||||
#include "src/tint/ast/fallthrough_statement.h"
|
#include "src/tint/ast/fallthrough_statement.h"
|
||||||
|
#include "src/tint/ast/for_loop_statement.h"
|
||||||
#include "src/tint/ast/function.h"
|
#include "src/tint/ast/function.h"
|
||||||
#include "src/tint/ast/if_statement.h"
|
#include "src/tint/ast/if_statement.h"
|
||||||
#include "src/tint/ast/loop_statement.h"
|
#include "src/tint/ast/loop_statement.h"
|
||||||
|
@ -210,7 +211,7 @@ bool BuilderImpl::EmitStatement(const ast::Statement* stmt) {
|
||||||
[&](const ast::FallthroughStatement*) { return EmitFallthrough(); },
|
[&](const ast::FallthroughStatement*) { return EmitFallthrough(); },
|
||||||
[&](const ast::IfStatement* i) { return EmitIf(i); },
|
[&](const ast::IfStatement* i) { return EmitIf(i); },
|
||||||
[&](const ast::LoopStatement* l) { return EmitLoop(l); },
|
[&](const ast::LoopStatement* l) { return EmitLoop(l); },
|
||||||
// [&](const ast::ForLoopStatement* l) { },
|
[&](const ast::ForLoopStatement* l) { return EmitForLoop(l); },
|
||||||
[&](const ast::WhileStatement* l) { return EmitWhile(l); },
|
[&](const ast::WhileStatement* l) { return EmitWhile(l); },
|
||||||
[&](const ast::ReturnStatement* r) { return EmitReturn(r); },
|
[&](const ast::ReturnStatement* r) { return EmitReturn(r); },
|
||||||
[&](const ast::SwitchStatement* s) { return EmitSwitch(s); },
|
[&](const ast::SwitchStatement* s) { return EmitSwitch(s); },
|
||||||
|
@ -352,6 +353,58 @@ bool BuilderImpl::EmitWhile(const ast::WhileStatement* stmt) {
|
||||||
return true;
|
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);
|
||||||
|
|
||||||
|
if (stmt->initializer) {
|
||||||
|
// Emit the for initializer before branching to the loop
|
||||||
|
if (!EmitStatement(stmt->initializer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BranchTo(loop_node);
|
||||||
|
|
||||||
|
ast_to_flow_[stmt] = loop_node;
|
||||||
|
|
||||||
|
{
|
||||||
|
FlowStackScope scope(this, loop_node);
|
||||||
|
|
||||||
|
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);
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
if (stmt->continuing) {
|
||||||
|
current_flow_block_ = loop_node->continuing_target;
|
||||||
|
if (!EmitStatement(stmt->continuing)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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) {
|
bool BuilderImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
|
||||||
auto* switch_node = builder_.CreateSwitch(stmt);
|
auto* switch_node = builder_.CreateSwitch(stmt);
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ class BlockStatement;
|
||||||
class BreakIfStatement;
|
class BreakIfStatement;
|
||||||
class BreakStatement;
|
class BreakStatement;
|
||||||
class ContinueStatement;
|
class ContinueStatement;
|
||||||
|
class ForLoopStatement;
|
||||||
class Function;
|
class Function;
|
||||||
class IfStatement;
|
class IfStatement;
|
||||||
class LoopStatement;
|
class LoopStatement;
|
||||||
|
@ -108,6 +109,11 @@ class BuilderImpl {
|
||||||
/// @returns true if successful, false otherwise.
|
/// @returns true if successful, false otherwise.
|
||||||
bool EmitWhile(const ast::WhileStatement* stmt);
|
bool EmitWhile(const ast::WhileStatement* stmt);
|
||||||
|
|
||||||
|
/// Emits a loop control node to the IR.
|
||||||
|
/// @param stmt the for loop statement
|
||||||
|
/// @returns true if successful, false otherwise.
|
||||||
|
bool EmitForLoop(const ast::ForLoopStatement* stmt);
|
||||||
|
|
||||||
/// Emits a switch statement
|
/// Emits a switch statement
|
||||||
/// @param stmt the switch statement
|
/// @param stmt the switch statement
|
||||||
/// @returns true if successful, false otherwise.
|
/// @returns true if successful, false otherwise.
|
||||||
|
|
|
@ -942,6 +942,103 @@ TEST_F(IRBuilderImplTest, While_Return) {
|
||||||
EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
|
EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(dsinclair): Enable when variable declarations and increment are supported
|
||||||
|
TEST_F(IRBuilderImplTest, DISABLED_For) {
|
||||||
|
// for(var i: 0; i < 10; i++) {
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func -> loop -> loop start -> if true
|
||||||
|
// -> if false
|
||||||
|
//
|
||||||
|
// [if true] -> if merge
|
||||||
|
// [if false] -> loop merge
|
||||||
|
// [if merge] -> loop continuing
|
||||||
|
// [loop continuing] -> loop start
|
||||||
|
// [loop merge] -> func end
|
||||||
|
//
|
||||||
|
auto* ast_for = For(Decl(Var("i", ty.i32())), LessThan("i", 10_a), Increment("i"), Block());
|
||||||
|
WrapInFunction(ast_for);
|
||||||
|
auto& b = Build();
|
||||||
|
|
||||||
|
auto r = b.Build();
|
||||||
|
ASSERT_TRUE(r) << b.error();
|
||||||
|
auto m = r.Move();
|
||||||
|
|
||||||
|
auto* ir_for = b.FlowNodeForAstNode(ast_for);
|
||||||
|
ASSERT_NE(ir_for, nullptr);
|
||||||
|
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->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, For_NoInitCondOrContinuing) {
|
||||||
|
// for (;;) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func -> loop -> loop start -> loop merge -> func end
|
||||||
|
//
|
||||||
|
auto* ast_for = For(nullptr, nullptr, nullptr, Block(Break()));
|
||||||
|
WrapInFunction(ast_for);
|
||||||
|
auto& b = Build();
|
||||||
|
|
||||||
|
auto r = b.Build();
|
||||||
|
ASSERT_TRUE(r) << b.error();
|
||||||
|
auto m = r.Move();
|
||||||
|
|
||||||
|
auto* ir_for = b.FlowNodeForAstNode(ast_for);
|
||||||
|
ASSERT_NE(ir_for, nullptr);
|
||||||
|
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_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(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);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(IRBuilderImplTest, Switch) {
|
TEST_F(IRBuilderImplTest, Switch) {
|
||||||
// func -> switch -> case 1
|
// func -> switch -> case 1
|
||||||
// -> case 2
|
// -> case 2
|
||||||
|
|
Loading…
Reference in New Issue