[IR] Add switch control flow node.
This CL updates the IR builder to create control flow nodes for a switch statement and the contained case statements. Bug: tint:1718 Change-Id: I05b73db11ab14676cc123f436ae5912b1dbee0d5 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/107801 Reviewed-by: Ben Clayton <bclayton@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
This commit is contained in:
parent
b9ed75cf2f
commit
d336733c83
|
@ -71,6 +71,21 @@ Loop* Builder::CreateLoop(const ast::LoopStatement* stmt) {
|
||||||
return ir_loop;
|
return ir_loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Switch* Builder::CreateSwitch(const ast::SwitchStatement* stmt) {
|
||||||
|
auto* ir_switch = ir.flow_nodes.Create<Switch>(stmt);
|
||||||
|
ir_switch->merge_target = CreateBlock();
|
||||||
|
return ir_switch;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block* Builder::CreateCase(Switch* s, const utils::VectorRef<const ast::CaseSelector*> selectors) {
|
||||||
|
s->cases.Push(Switch::Case{selectors, CreateBlock()});
|
||||||
|
|
||||||
|
Block* b = s->cases.Back().start_target;
|
||||||
|
// 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) {
|
||||||
TINT_ASSERT(IR, from);
|
TINT_ASSERT(IR, from);
|
||||||
TINT_ASSERT(IR, to);
|
TINT_ASSERT(IR, to);
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
namespace tint {
|
namespace tint {
|
||||||
class Program;
|
class Program;
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
namespace tint::ast {
|
||||||
|
class CaseSelector;
|
||||||
|
} // namespace tint::ast
|
||||||
|
|
||||||
namespace tint::ir {
|
namespace tint::ir {
|
||||||
|
|
||||||
|
@ -62,6 +65,17 @@ class Builder {
|
||||||
/// @returns the flow node
|
/// @returns the flow node
|
||||||
Loop* CreateLoop(const ast::LoopStatement* stmt);
|
Loop* CreateLoop(const ast::LoopStatement* stmt);
|
||||||
|
|
||||||
|
/// Creates a switch flow node for the given ast::SwitchStatement
|
||||||
|
/// @param stmt the ast::SwitchStatment
|
||||||
|
/// @returns the flow node
|
||||||
|
Switch* CreateSwitch(const ast::SwitchStatement* stmt);
|
||||||
|
|
||||||
|
/// Creates a case flow node for the given case branch.
|
||||||
|
/// @param s the switch to create the case into
|
||||||
|
/// @param selectors the case selectors for the case statement
|
||||||
|
/// @returns the start block for the case flow node
|
||||||
|
Block* CreateCase(Switch* s, const utils::VectorRef<const ast::CaseSelector*> selectors);
|
||||||
|
|
||||||
/// Branches the given block to the given flow node.
|
/// Branches the given block to the given flow node.
|
||||||
/// @param from the block to branch from
|
/// @param from the block to branch from
|
||||||
/// @param to the node to branch too
|
/// @param to the node to branch too
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "src/tint/ast/return_statement.h"
|
#include "src/tint/ast/return_statement.h"
|
||||||
#include "src/tint/ast/statement.h"
|
#include "src/tint/ast/statement.h"
|
||||||
#include "src/tint/ast/static_assert.h"
|
#include "src/tint/ast/static_assert.h"
|
||||||
|
#include "src/tint/ast/switch_statement.h"
|
||||||
#include "src/tint/ir/function.h"
|
#include "src/tint/ir/function.h"
|
||||||
#include "src/tint/ir/if.h"
|
#include "src/tint/ir/if.h"
|
||||||
#include "src/tint/ir/loop.h"
|
#include "src/tint/ir/loop.h"
|
||||||
|
@ -209,7 +210,7 @@ bool BuilderImpl::EmitStatement(const ast::Statement* stmt) {
|
||||||
// [&](const ast::ForLoopStatement* l) { },
|
// [&](const ast::ForLoopStatement* l) { },
|
||||||
// [&](const ast::WhileStatement* l) { },
|
// [&](const ast::WhileStatement* l) { },
|
||||||
[&](const ast::ReturnStatement* r) { return EmitReturn(r); },
|
[&](const ast::ReturnStatement* r) { return EmitReturn(r); },
|
||||||
// [&](const ast::SwitchStatement* s) { },
|
[&](const ast::SwitchStatement* s) { return EmitSwitch(s); },
|
||||||
// [&](const ast::VariableDeclStatement* v) { },
|
// [&](const ast::VariableDeclStatement* v) { },
|
||||||
[&](const ast::StaticAssert*) {
|
[&](const ast::StaticAssert*) {
|
||||||
return true; // Not emitted
|
return true; // Not emitted
|
||||||
|
@ -254,15 +255,6 @@ bool BuilderImpl::EmitIf(const ast::IfStatement* stmt) {
|
||||||
}
|
}
|
||||||
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 (IsBranched(if_node->true_target) && IsBranched(if_node->false_target)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
current_flow_block_ = if_node->merge_target;
|
|
||||||
|
|
||||||
// If the true branch did not execute control flow, then go to the merge target
|
// If the true branch did not execute control flow, then go to the merge target
|
||||||
if (!IsBranched(if_node->true_target)) {
|
if (!IsBranched(if_node->true_target)) {
|
||||||
builder_.Branch(if_node->true_target, if_node->merge_target);
|
builder_.Branch(if_node->true_target, if_node->merge_target);
|
||||||
|
@ -272,6 +264,13 @@ bool BuilderImpl::EmitIf(const ast::IfStatement* stmt) {
|
||||||
builder_.Branch(if_node->false_target, if_node->merge_target);
|
builder_.Branch(if_node->false_target, if_node->merge_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,6 +312,35 @@ bool BuilderImpl::EmitLoop(const ast::LoopStatement* stmt) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BuilderImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
|
||||||
|
auto* switch_node = builder_.CreateSwitch(stmt);
|
||||||
|
|
||||||
|
// TODO(dsinclair): Emit the condition expression into the current block
|
||||||
|
|
||||||
|
BranchTo(switch_node);
|
||||||
|
|
||||||
|
ast_to_flow_[stmt] = switch_node;
|
||||||
|
|
||||||
|
{
|
||||||
|
FlowStackScope scope(this, switch_node);
|
||||||
|
|
||||||
|
for (const auto* c : stmt->body) {
|
||||||
|
current_flow_block_ = builder_.CreateCase(switch_node, c->selectors);
|
||||||
|
if (!EmitStatement(c->body)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BranchToIfNeeded(switch_node->merge_target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current_flow_block_ = nullptr;
|
||||||
|
|
||||||
|
if (IsConnected(switch_node->merge_target)) {
|
||||||
|
current_flow_block_ = switch_node->merge_target;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool BuilderImpl::EmitReturn(const ast::ReturnStatement*) {
|
bool BuilderImpl::EmitReturn(const ast::ReturnStatement*) {
|
||||||
// TODO(dsinclair): Emit the return value ....
|
// TODO(dsinclair): Emit the return value ....
|
||||||
|
|
||||||
|
|
|
@ -102,6 +102,11 @@ class BuilderImpl {
|
||||||
/// @returns true if successful, false otherwise.
|
/// @returns true if successful, false otherwise.
|
||||||
bool EmitLoop(const ast::LoopStatement* stmt);
|
bool EmitLoop(const ast::LoopStatement* stmt);
|
||||||
|
|
||||||
|
/// Emits a switch statement
|
||||||
|
/// @param stmt the switch statement
|
||||||
|
/// @returns true if successfull, false otherwise.
|
||||||
|
bool EmitSwitch(const ast::SwitchStatement* stmt);
|
||||||
|
|
||||||
/// Emits a break statement
|
/// Emits a break statement
|
||||||
/// @param stmt the break statement
|
/// @param stmt the break statement
|
||||||
/// @returns true if successfull, false otherwise.
|
/// @returns true if successfull, false otherwise.
|
||||||
|
|
|
@ -14,9 +14,14 @@
|
||||||
|
|
||||||
#include "src/tint/ir/test_helper.h"
|
#include "src/tint/ir/test_helper.h"
|
||||||
|
|
||||||
|
#include "src/tint/ast/case_selector.h"
|
||||||
|
#include "src/tint/ast/int_literal_expression.h"
|
||||||
|
|
||||||
namespace tint::ir {
|
namespace tint::ir {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using namespace tint::number_suffixes; // NOLINT
|
||||||
|
|
||||||
using IRBuilderImplTest = TestHelper;
|
using IRBuilderImplTest = TestHelper;
|
||||||
|
|
||||||
TEST_F(IRBuilderImplTest, Func) {
|
TEST_F(IRBuilderImplTest, Func) {
|
||||||
|
@ -817,5 +822,222 @@ TEST_F(IRBuilderImplTest, Loop_Nested) {
|
||||||
EXPECT_EQ(loop_flow_a->merge_target->branch_target, func->end_target);
|
EXPECT_EQ(loop_flow_a->merge_target->branch_target, func->end_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(IRBuilderImplTest, Switch) {
|
||||||
|
// func -> switch -> case 1
|
||||||
|
// -> case 2
|
||||||
|
// -> default
|
||||||
|
//
|
||||||
|
// [case 1] -> switch merge
|
||||||
|
// [case 2] -> switch merge
|
||||||
|
// [default] -> switch merge
|
||||||
|
// [switch merge] -> func end
|
||||||
|
//
|
||||||
|
auto* ast_switch = Switch(
|
||||||
|
1_i, utils::Vector{Case(utils::Vector{CaseSelector(0_i)}, Block()),
|
||||||
|
Case(utils::Vector{CaseSelector(1_i)}, Block()), DefaultCase(Block())});
|
||||||
|
|
||||||
|
WrapInFunction(ast_switch);
|
||||||
|
auto& b = Build();
|
||||||
|
|
||||||
|
auto r = b.Build();
|
||||||
|
ASSERT_TRUE(r) << b.error();
|
||||||
|
auto m = r.Move();
|
||||||
|
|
||||||
|
auto* ir_switch = b.FlowNodeForAstNode(ast_switch);
|
||||||
|
ASSERT_NE(ir_switch, nullptr);
|
||||||
|
ASSERT_TRUE(ir_switch->Is<ir::Switch>());
|
||||||
|
|
||||||
|
auto* flow = ir_switch->As<ir::Switch>();
|
||||||
|
ASSERT_NE(flow->merge_target, nullptr);
|
||||||
|
ASSERT_EQ(3u, flow->cases.Length());
|
||||||
|
|
||||||
|
ASSERT_EQ(1u, m.functions.Length());
|
||||||
|
auto* func = m.functions[0];
|
||||||
|
|
||||||
|
ASSERT_EQ(1u, flow->cases[0].selectors.Length());
|
||||||
|
ASSERT_TRUE(flow->cases[0].selectors[0]->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
EXPECT_EQ(0_i, flow->cases[0].selectors[0]->expr->As<ast::IntLiteralExpression>()->value);
|
||||||
|
|
||||||
|
ASSERT_EQ(1u, flow->cases[1].selectors.Length());
|
||||||
|
ASSERT_TRUE(flow->cases[1].selectors[0]->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
EXPECT_EQ(1_i, flow->cases[1].selectors[0]->expr->As<ast::IntLiteralExpression>()->value);
|
||||||
|
|
||||||
|
ASSERT_EQ(1u, flow->cases[2].selectors.Length());
|
||||||
|
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, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IRBuilderImplTest, Switch_OnlyDefault) {
|
||||||
|
// func -> switch -> default -> switch merge -> func end
|
||||||
|
//
|
||||||
|
auto* ast_switch = Switch(1_i, utils::Vector{DefaultCase(Block())});
|
||||||
|
|
||||||
|
WrapInFunction(ast_switch);
|
||||||
|
auto& b = Build();
|
||||||
|
|
||||||
|
auto r = b.Build();
|
||||||
|
ASSERT_TRUE(r) << b.error();
|
||||||
|
auto m = r.Move();
|
||||||
|
|
||||||
|
auto* ir_switch = b.FlowNodeForAstNode(ast_switch);
|
||||||
|
ASSERT_NE(ir_switch, nullptr);
|
||||||
|
ASSERT_TRUE(ir_switch->Is<ir::Switch>());
|
||||||
|
|
||||||
|
auto* flow = ir_switch->As<ir::Switch>();
|
||||||
|
ASSERT_NE(flow->merge_target, nullptr);
|
||||||
|
ASSERT_EQ(1u, flow->cases.Length());
|
||||||
|
|
||||||
|
ASSERT_EQ(1u, m.functions.Length());
|
||||||
|
auto* func = m.functions[0];
|
||||||
|
|
||||||
|
ASSERT_EQ(1u, flow->cases[0].selectors.Length());
|
||||||
|
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, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IRBuilderImplTest, Switch_WithBreak) {
|
||||||
|
// {
|
||||||
|
// switch(1) {
|
||||||
|
// case 0: {
|
||||||
|
// break;
|
||||||
|
// if true { return;} // Dead code
|
||||||
|
// }
|
||||||
|
// default: {}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func -> switch -> case 1
|
||||||
|
// -> default
|
||||||
|
//
|
||||||
|
// [case 1] -> switch merge
|
||||||
|
// [default] -> switch merge
|
||||||
|
// [switch merge] -> func end
|
||||||
|
auto* ast_switch = Switch(1_i, utils::Vector{Case(utils::Vector{CaseSelector(0_i)},
|
||||||
|
Block(Break(), If(true, Block(Return())))),
|
||||||
|
DefaultCase(Block())});
|
||||||
|
|
||||||
|
WrapInFunction(ast_switch);
|
||||||
|
auto& b = Build();
|
||||||
|
|
||||||
|
auto r = b.Build();
|
||||||
|
ASSERT_TRUE(r) << b.error();
|
||||||
|
auto m = r.Move();
|
||||||
|
|
||||||
|
auto* ir_switch = b.FlowNodeForAstNode(ast_switch);
|
||||||
|
ASSERT_NE(ir_switch, nullptr);
|
||||||
|
ASSERT_TRUE(ir_switch->Is<ir::Switch>());
|
||||||
|
|
||||||
|
auto* flow = ir_switch->As<ir::Switch>();
|
||||||
|
ASSERT_NE(flow->merge_target, nullptr);
|
||||||
|
ASSERT_EQ(2u, flow->cases.Length());
|
||||||
|
|
||||||
|
ASSERT_EQ(1u, m.functions.Length());
|
||||||
|
auto* func = m.functions[0];
|
||||||
|
|
||||||
|
ASSERT_EQ(1u, flow->cases[0].selectors.Length());
|
||||||
|
ASSERT_TRUE(flow->cases[0].selectors[0]->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
EXPECT_EQ(0_i, flow->cases[0].selectors[0]->expr->As<ast::IntLiteralExpression>()->value);
|
||||||
|
|
||||||
|
ASSERT_EQ(1u, flow->cases[1].selectors.Length());
|
||||||
|
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());
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IRBuilderImplTest, Switch_AllReturn) {
|
||||||
|
// {
|
||||||
|
// switch(1) {
|
||||||
|
// case 0: {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// default: {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if true { return; } // Dead code
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func -> switch -> case 1
|
||||||
|
// -> default
|
||||||
|
//
|
||||||
|
// [case 1] -> func end
|
||||||
|
// [default] -> func end
|
||||||
|
// [switch merge] -> nullptr
|
||||||
|
//
|
||||||
|
auto* ast_switch =
|
||||||
|
Switch(1_i, utils::Vector{Case(utils::Vector{CaseSelector(0_i)}, Block(Return())),
|
||||||
|
DefaultCase(Block(Return()))});
|
||||||
|
|
||||||
|
auto* ast_if = If(true, Block(Return()));
|
||||||
|
|
||||||
|
WrapInFunction(ast_switch, ast_if);
|
||||||
|
auto& b = Build();
|
||||||
|
|
||||||
|
auto r = b.Build();
|
||||||
|
ASSERT_TRUE(r) << b.error();
|
||||||
|
auto m = r.Move();
|
||||||
|
|
||||||
|
ASSERT_EQ(b.FlowNodeForAstNode(ast_if), nullptr);
|
||||||
|
|
||||||
|
auto* ir_switch = b.FlowNodeForAstNode(ast_switch);
|
||||||
|
ASSERT_NE(ir_switch, nullptr);
|
||||||
|
ASSERT_TRUE(ir_switch->Is<ir::Switch>());
|
||||||
|
|
||||||
|
auto* flow = ir_switch->As<ir::Switch>();
|
||||||
|
ASSERT_NE(flow->merge_target, nullptr);
|
||||||
|
ASSERT_EQ(2u, flow->cases.Length());
|
||||||
|
|
||||||
|
ASSERT_EQ(1u, m.functions.Length());
|
||||||
|
auto* func = m.functions[0];
|
||||||
|
|
||||||
|
ASSERT_EQ(1u, flow->cases[0].selectors.Length());
|
||||||
|
ASSERT_TRUE(flow->cases[0].selectors[0]->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
EXPECT_EQ(0_i, flow->cases[0].selectors[0]->expr->As<ast::IntLiteralExpression>()->value);
|
||||||
|
|
||||||
|
ASSERT_EQ(1u, flow->cases[1].selectors.Length());
|
||||||
|
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(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);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace tint::ir
|
} // namespace tint::ir
|
||||||
|
|
|
@ -18,7 +18,7 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::Switch);
|
||||||
|
|
||||||
namespace tint::ir {
|
namespace tint::ir {
|
||||||
|
|
||||||
Switch::Switch() : Base() {}
|
Switch::Switch(const ast::SwitchStatement* stmt) : Base(), source(stmt) {}
|
||||||
|
|
||||||
Switch::~Switch() = default;
|
Switch::~Switch() = default;
|
||||||
|
|
||||||
|
|
|
@ -18,17 +18,38 @@
|
||||||
#include "src/tint/ir/block.h"
|
#include "src/tint/ir/block.h"
|
||||||
#include "src/tint/ir/flow_node.h"
|
#include "src/tint/ir/flow_node.h"
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
namespace tint::ast {
|
||||||
|
class CaseSelector;
|
||||||
|
class SwitchStatement;
|
||||||
|
} // namespace tint::ast
|
||||||
|
|
||||||
namespace tint::ir {
|
namespace tint::ir {
|
||||||
|
|
||||||
/// Flow node representing a switch statement
|
/// Flow node representing a switch statement
|
||||||
class Switch : public Castable<Switch, FlowNode> {
|
class Switch : public Castable<Switch, FlowNode> {
|
||||||
public:
|
public:
|
||||||
|
/// A case label in the struct
|
||||||
|
struct Case {
|
||||||
|
/// The case selector for this node
|
||||||
|
const utils::VectorRef<const ast::CaseSelector*> selectors;
|
||||||
|
/// The start block for the case block.
|
||||||
|
Block* start_target;
|
||||||
|
};
|
||||||
|
|
||||||
/// Constructor
|
/// Constructor
|
||||||
Switch();
|
/// @param stmt the originating ast switch statement
|
||||||
|
explicit Switch(const ast::SwitchStatement* stmt);
|
||||||
~Switch() override;
|
~Switch() override;
|
||||||
|
|
||||||
|
/// The originating switch statment in the AST
|
||||||
|
const ast::SwitchStatement* source;
|
||||||
|
|
||||||
/// The switch merge target
|
/// The switch merge target
|
||||||
Block* merge_target;
|
Block* merge_target;
|
||||||
|
|
||||||
|
/// The switch case statements
|
||||||
|
utils::Vector<Case, 4> cases;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tint::ir
|
} // namespace tint::ir
|
||||||
|
|
Loading…
Reference in New Issue