Remove infrastructure for fallthrough

This Cl removes the internal infrastructor and backend code
generation for the fallthrough statement.

Bug: tint:1644
Change-Id: I2a1de7d527865e5a7221074f4e0fb106599f4c57
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/109005
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
This commit is contained in:
dan sinclair 2022-11-15 00:30:33 +00:00 committed by Dawn LUCI CQ
parent bf586f6dfd
commit 267f1748c8
32 changed files with 39 additions and 571 deletions

View File

@ -247,8 +247,6 @@ libtint_source_set("libtint_core_all_src") {
"ast/f16.h",
"ast/f32.cc",
"ast/f32.h",
"ast/fallthrough_statement.cc",
"ast/fallthrough_statement.h",
"ast/float_literal_expression.cc",
"ast/float_literal_expression.h",
"ast/for_loop_statement.cc",
@ -1047,7 +1045,6 @@ if (tint_build_unittests) {
"ast/external_texture_test.cc",
"ast/f16_test.cc",
"ast/f32_test.cc",
"ast/fallthrough_statement_test.cc",
"ast/float_literal_expression_test.cc",
"ast/for_loop_statement_test.cc",
"ast/function_test.cc",
@ -1159,8 +1156,8 @@ if (tint_build_unittests) {
"resolver/resolver_test.cc",
"resolver/resolver_test_helper.cc",
"resolver/resolver_test_helper.h",
"resolver/side_effects_test.cc",
"resolver/root_identifier_test.cc",
"resolver/side_effects_test.cc",
"resolver/static_assert_test.cc",
"resolver/struct_address_space_use_test.cc",
"resolver/struct_layout_test.cc",
@ -1480,7 +1477,6 @@ if (tint_build_unittests) {
"writer/wgsl/generator_impl_continue_test.cc",
"writer/wgsl/generator_impl_discard_test.cc",
"writer/wgsl/generator_impl_enable_test.cc",
"writer/wgsl/generator_impl_fallthrough_test.cc",
"writer/wgsl/generator_impl_function_test.cc",
"writer/wgsl/generator_impl_global_decl_test.cc",
"writer/wgsl/generator_impl_identifier_test.cc",

View File

@ -139,8 +139,6 @@ list(APPEND TINT_LIB_SRCS
ast/f16.h
ast/f32.cc
ast/f32.h
ast/fallthrough_statement.cc
ast/fallthrough_statement.h
ast/float_literal_expression.cc
ast/float_literal_expression.h
ast/for_loop_statement.cc
@ -786,7 +784,6 @@ if(TINT_BUILD_TESTS)
ast/external_texture_test.cc
ast/f16_test.cc
ast/f32_test.cc
ast/fallthrough_statement_test.cc
ast/float_literal_expression_test.cc
ast/for_loop_statement_test.cc
ast/function_test.cc
@ -1150,7 +1147,6 @@ if(TINT_BUILD_TESTS)
writer/wgsl/generator_impl_continue_test.cc
writer/wgsl/generator_impl_discard_test.cc
writer/wgsl/generator_impl_enable_test.cc
writer/wgsl/generator_impl_fallthrough_test.cc
writer/wgsl/generator_impl_function_test.cc
writer/wgsl/generator_impl_global_decl_test.cc
writer/wgsl/generator_impl_identifier_test.cc

View File

@ -1,36 +0,0 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/program_builder.h"
TINT_INSTANTIATE_TYPEINFO(tint::ast::FallthroughStatement);
namespace tint::ast {
FallthroughStatement::FallthroughStatement(ProgramID pid, NodeID nid, const Source& src)
: Base(pid, nid, src) {}
FallthroughStatement::FallthroughStatement(FallthroughStatement&&) = default;
FallthroughStatement::~FallthroughStatement() = default;
const FallthroughStatement* FallthroughStatement::Clone(CloneContext* ctx) const {
// Clone arguments outside of create() call to have deterministic ordering
auto src = ctx->Clone(source);
return ctx->dst->create<FallthroughStatement>(src);
}
} // namespace tint::ast

View File

@ -1,43 +0,0 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_TINT_AST_FALLTHROUGH_STATEMENT_H_
#define SRC_TINT_AST_FALLTHROUGH_STATEMENT_H_
#include "src/tint/ast/statement.h"
namespace tint::ast {
/// An fallthrough statement
class FallthroughStatement final : public Castable<FallthroughStatement, Statement> {
public:
/// Constructor
/// @param pid the identifier of the program that owns this node
/// @param nid the unique node identifier
/// @param src the source of this node
FallthroughStatement(ProgramID pid, NodeID nid, const Source& src);
/// Move constructor
FallthroughStatement(FallthroughStatement&&);
~FallthroughStatement() override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @param ctx the clone context
/// @return the newly cloned node
const FallthroughStatement* Clone(CloneContext* ctx) const override;
};
} // namespace tint::ast
#endif // SRC_TINT_AST_FALLTHROUGH_STATEMENT_H_

View File

@ -1,45 +0,0 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/ast/test_helper.h"
namespace tint::ast {
namespace {
using FallthroughStatementTest = TestHelper;
TEST_F(FallthroughStatementTest, Creation) {
auto* stmt = create<FallthroughStatement>();
EXPECT_EQ(stmt->source.range.begin.line, 0u);
EXPECT_EQ(stmt->source.range.begin.column, 0u);
EXPECT_EQ(stmt->source.range.end.line, 0u);
EXPECT_EQ(stmt->source.range.end.column, 0u);
}
TEST_F(FallthroughStatementTest, Creation_WithSource) {
auto* stmt = create<FallthroughStatement>(Source{Source::Location{20, 2}});
auto src = stmt->source;
EXPECT_EQ(src.range.begin.line, 20u);
EXPECT_EQ(src.range.begin.column, 2u);
}
TEST_F(FallthroughStatementTest, IsFallthrough) {
auto* stmt = create<FallthroughStatement>();
EXPECT_TRUE(stmt->Is<FallthroughStatement>());
}
} // namespace
} // namespace tint::ast

View File

@ -19,7 +19,6 @@
#include "src/tint/ast/call_statement.h"
#include "src/tint/ast/continue_statement.h"
#include "src/tint/ast/discard_statement.h"
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/ast/if_statement.h"
#include "src/tint/ast/loop_statement.h"
#include "src/tint/ast/return_statement.h"
@ -58,9 +57,6 @@ const char* Statement::Name() const {
if (Is<DiscardStatement>()) {
return "discard statement";
}
if (Is<FallthroughStatement>()) {
return "fallthrough statement";
}
if (Is<IfStatement>()) {
return "if statement";
}

View File

@ -19,7 +19,6 @@
#include "src/tint/ast/break_if_statement.h"
#include "src/tint/ast/break_statement.h"
#include "src/tint/ast/continue_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/if_statement.h"
@ -210,7 +209,6 @@ bool BuilderImpl::EmitStatement(const ast::Statement* stmt) {
// [&](const ast::CallStatement* c) { },
[&](const ast::ContinueStatement* c) { return EmitContinue(c); },
// [&](const ast::DiscardStatement* d) { },
[&](const ast::FallthroughStatement*) { return EmitFallthrough(); },
[&](const ast::IfStatement* i) { return EmitIf(i); },
[&](const ast::LoopStatement* l) { return EmitLoop(l); },
[&](const ast::ForLoopStatement* l) { return EmitForLoop(l); },
@ -415,25 +413,12 @@ bool BuilderImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
{
FlowStackScope scope(this, switch_node);
// TODO(crbug.com/tint/1644): This can be simplifed when fallthrough is removed, a single
// loop can be used to iterate each body statement and emit for the case. Two loops are
// needed in order to have the target for a fallthrough.
for (const auto* c : stmt->body) {
builder_.CreateCase(switch_node, c->selectors);
}
for (size_t i = 0; i < stmt->body.Length(); ++i) {
current_flow_block_ = switch_node->cases[i].start_target;
if (i < (stmt->body.Length() - 1)) {
fallthrough_target_ = switch_node->cases[i + 1].start_target;
}
if (!EmitStatement(stmt->body[i]->body)) {
current_flow_block_ = builder_.CreateCase(switch_node, c->selectors);
if (!EmitStatement(c->body)) {
return false;
}
BranchToIfNeeded(switch_node->merge_target);
fallthrough_target_ = nullptr;
}
}
current_flow_block_ = nullptr;
@ -514,10 +499,4 @@ bool BuilderImpl::EmitBreakIf(const ast::BreakIfStatement* stmt) {
return true;
}
bool BuilderImpl::EmitFallthrough() {
TINT_ASSERT(IR, fallthrough_target_ != nullptr);
BranchTo(fallthrough_target_);
return true;
}
} // namespace tint::ir

View File

@ -134,10 +134,6 @@ class BuilderImpl {
/// @returns true if successful, false otherwise.
bool EmitBreakIf(const ast::BreakIfStatement* stmt);
/// Emits a fallthrough statement
/// @returns true if successful, false otherwise
bool EmitFallthrough();
/// Retrieve the IR Flow node for a given AST node.
/// @param n the node to lookup
/// @returns the FlowNode for the given ast::Node or nullptr if it doesn't exist.
@ -166,9 +162,6 @@ class BuilderImpl {
Block* current_flow_block_ = nullptr;
Function* current_function_ = nullptr;
// TODO(crbug.com/tint/1644): Remove this when fallthrough is removed.
Block* fallthrough_target_ = nullptr;
/// Map from ast nodes to flow nodes, used to retrieve the flow node for a given AST node.
/// Used for testing purposes.
std::unordered_map<const ast::Node*, const FlowNode*> ast_to_flow_;

View File

@ -1313,64 +1313,5 @@ TEST_F(IRBuilderImplTest, Switch_AllReturn) {
EXPECT_EQ(flow->merge_target->branch_target, nullptr);
}
// TODO(crbug.com/tint/1644): Remove when fallthrough is removed.
TEST_F(IRBuilderImplTest, Switch_Fallthrough) {
// func -> switch -> case 1
// -> case 2
// -> default
//
// [case 1] -> switch merge
// [case 2] -> default
// [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(Fallthrough())),
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(2u, flow->cases[2].start_target->inbound_branches.Length());
EXPECT_EQ(2u, 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->cases[2].start_target);
EXPECT_EQ(flow->cases[2].start_target->branch_target, flow->merge_target);
EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
}
} // namespace
} // namespace tint::ir

View File

@ -47,7 +47,6 @@
#include "src/tint/ast/external_texture.h"
#include "src/tint/ast/f16.h"
#include "src/tint/ast/f32.h"
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/ast/float_literal_expression.h"
#include "src/tint/ast/for_loop_statement.h"
#include "src/tint/ast/i32.h"
@ -2932,17 +2931,6 @@ class ProgramBuilder {
/// @returns the selector pointer
const ast::CaseSelector* DefaultCaseSelector() { return create<ast::CaseSelector>(nullptr); }
/// Creates an ast::FallthroughStatement
/// @param source the source information
/// @returns the fallthrough statement pointer
const ast::FallthroughStatement* Fallthrough(const Source& source) {
return create<ast::FallthroughStatement>(source);
}
/// Creates an ast::FallthroughStatement
/// @returns the fallthrough statement pointer
const ast::FallthroughStatement* Fallthrough() { return create<ast::FallthroughStatement>(); }
/// Creates an ast::BuiltinAttribute
/// @param source the source information
/// @param builtin the builtin value

View File

@ -14,7 +14,6 @@
#include "src/tint/ast/break_statement.h"
#include "src/tint/ast/continue_statement.h"
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/ast/switch_statement.h"
#include "src/tint/resolver/resolver_test_helper.h"
@ -378,24 +377,6 @@ TEST_F(ResolverControlBlockValidationTest, NonUniqueCaseSelectorValueSint_Fail)
"12:34 note: previous case declared here");
}
TEST_F(ResolverControlBlockValidationTest, LastClauseLastStatementIsFallthrough_Fail) {
// var a : i32 = 2;
// switch (a) {
// default: { fallthrough; }
// }
auto* var = Var("a", ty.i32(), Expr(2_i));
auto* fallthrough = create<ast::FallthroughStatement>(Source{{12, 34}});
auto* block = Block(Decl(var), //
Switch("a", //
DefaultCase(Block(fallthrough))));
WrapInFunction(block);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: a fallthrough statement must not be used in the last "
"switch case");
}
TEST_F(ResolverControlBlockValidationTest, SwitchCase_Pass) {
// var a : i32 = 2;
// switch (a) {

View File

@ -35,7 +35,6 @@
#include "src/tint/ast/external_texture.h"
#include "src/tint/ast/f16.h"
#include "src/tint/ast/f32.h"
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/ast/for_loop_statement.h"
#include "src/tint/ast/i32.h"
#include "src/tint/ast/id_attribute.h"
@ -320,7 +319,7 @@ class DependencyScanner {
[&](const ast::StaticAssert* assertion) { TraverseExpression(assertion->condition); },
[&](Default) {
if (!stmt->IsAnyOf<ast::BreakStatement, ast::ContinueStatement,
ast::DiscardStatement, ast::FallthroughStatement>()) {
ast::DiscardStatement>()) {
UnhandledNode(diagnostics_, stmt);
}
});

View File

@ -1273,8 +1273,6 @@ TEST_F(ResolverDependencyGraphTraversalTest, SymbolsReached) {
Switch(V, //
Case(CaseSelector(1_i), //
Block(Assign(V, V))), //
Case(CaseSelector(2_i), //
Block(Fallthrough())), //
DefaultCase(Block(Assign(V, V)))), //
Return(V), //
Break(), //

View File

@ -31,7 +31,6 @@
#include "src/tint/ast/depth_texture.h"
#include "src/tint/ast/disable_validation_attribute.h"
#include "src/tint/ast/discard_statement.h"
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/ast/for_loop_statement.h"
#include "src/tint/ast/id_attribute.h"
#include "src/tint/ast/if_statement.h"
@ -1217,7 +1216,6 @@ sem::Statement* Resolver::Statement(const ast::Statement* stmt) {
[&](const ast::CompoundAssignmentStatement* c) { return CompoundAssignmentStatement(c); },
[&](const ast::ContinueStatement* c) { return ContinueStatement(c); },
[&](const ast::DiscardStatement* d) { return DiscardStatement(d); },
[&](const ast::FallthroughStatement* f) { return FallthroughStatement(f); },
[&](const ast::IncrementDecrementStatement* i) { return IncrementDecrementStatement(i); },
[&](const ast::ReturnStatement* r) { return ReturnStatement(r); },
[&](const ast::VariableDeclStatement* v) { return VariableDeclStatement(v); },
@ -3146,7 +3144,7 @@ sem::SwitchStatement* Resolver::SwitchStatement(const ast::SwitchStatement* stmt
if (behaviors.Contains(sem::Behavior::kBreak)) {
behaviors.Add(sem::Behavior::kNext);
}
behaviors.Remove(sem::Behavior::kBreak, sem::Behavior::kFallthrough);
behaviors.Remove(sem::Behavior::kBreak);
return validator_.SwitchStatement(stmt);
});
@ -3308,16 +3306,6 @@ sem::Statement* Resolver::DiscardStatement(const ast::DiscardStatement* stmt) {
});
}
sem::Statement* Resolver::FallthroughStatement(const ast::FallthroughStatement* stmt) {
auto* sem =
builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
return StatementScope(stmt, sem, [&] {
sem->Behaviors() = sem::Behavior::kFallthrough;
return validator_.FallthroughStatement(sem);
});
}
sem::Statement* Resolver::IncrementDecrementStatement(
const ast::IncrementDecrementStatement* stmt) {
auto* sem =

View File

@ -212,7 +212,6 @@ class Resolver {
sem::Statement* CompoundAssignmentStatement(const ast::CompoundAssignmentStatement*);
sem::Statement* ContinueStatement(const ast::ContinueStatement*);
sem::Statement* DiscardStatement(const ast::DiscardStatement*);
sem::Statement* FallthroughStatement(const ast::FallthroughStatement*);
sem::ForLoopStatement* ForLoopStatement(const ast::ForLoopStatement*);
sem::WhileStatement* WhileStatement(const ast::WhileStatement*);
sem::GlobalVariable* GlobalVariable(const ast::Variable*);

View File

@ -477,10 +477,9 @@ class UniformityGraph {
}
// Propagate all variables assignments to the containing scope if the behavior is
// either 'Next' or 'Fallthrough'.
// 'Next'.
auto& behaviors = sem_.Get(b)->Behaviors();
if (behaviors.Contains(sem::Behavior::kNext) ||
behaviors.Contains(sem::Behavior::kFallthrough)) {
if (behaviors.Contains(sem::Behavior::kNext)) {
for (auto var : scoped_assignments) {
current_function_->variables.Set(var.key, var.value);
}
@ -613,8 +612,6 @@ class UniformityGraph {
[&](const ast::DiscardStatement*) { return cf; },
[&](const ast::FallthroughStatement*) { return cf; },
[&](const ast::ForLoopStatement* f) {
auto* sem_loop = sem_.Get(f);
auto* cfx = CreateNode("loop_start");
@ -937,46 +934,35 @@ class UniformityGraph {
info.type = "switch";
auto* cf_n = v;
bool previous_case_has_fallthrough = false;
for (auto* c : s->body) {
auto* sem_case = sem_.Get(c);
if (previous_case_has_fallthrough) {
cf_n = ProcessStatement(cf_n, c->body);
} else {
current_function_->variables.Push();
cf_n = ProcessStatement(v, c->body);
}
current_function_->variables.Push();
cf_n = ProcessStatement(v, c->body);
if (cf_end) {
cf_end->AddEdge(cf_n);
}
bool has_fallthrough =
sem_case->Behaviors().Contains(sem::Behavior::kFallthrough);
if (!has_fallthrough) {
if (sem_case->Behaviors().Contains(sem::Behavior::kNext)) {
// Propagate variable values to the switch exit nodes.
for (auto* var : current_function_->local_var_decls) {
// Skip variables that were declared inside the switch.
if (auto* lv = var->As<sem::LocalVariable>();
lv && lv->Statement()->FindFirstParent(
[&](auto* st) { return st == sem_switch; })) {
continue;
}
// Add an edge from the variable exit node to its new value.
auto* exit_node = info.var_exit_nodes.GetOrCreate(var, [&]() {
auto name =
builder_->Symbols().NameFor(var->Declaration()->symbol);
return CreateNode(name + "_value_" + info.type + "_exit");
});
exit_node->AddEdge(current_function_->variables.Get(var));
if (sem_case->Behaviors().Contains(sem::Behavior::kNext)) {
// Propagate variable values to the switch exit nodes.
for (auto* var : current_function_->local_var_decls) {
// Skip variables that were declared inside the switch.
if (auto* lv = var->As<sem::LocalVariable>();
lv && lv->Statement()->FindFirstParent(
[&](auto* st) { return st == sem_switch; })) {
continue;
}
// Add an edge from the variable exit node to its new value.
auto* exit_node = info.var_exit_nodes.GetOrCreate(var, [&]() {
auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
return CreateNode(name + "_value_" + info.type + "_exit");
});
exit_node->AddEdge(current_function_->variables.Get(var));
}
current_function_->variables.Pop();
}
previous_case_has_fallthrough = has_fallthrough;
current_function_->variables.Pop();
}
// Update nodes for any variables assigned in the switch statement.

View File

@ -28,7 +28,6 @@
#include "src/tint/ast/depth_texture.h"
#include "src/tint/ast/disable_validation_attribute.h"
#include "src/tint/ast/discard_statement.h"
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/ast/for_loop_statement.h"
#include "src/tint/ast/id_attribute.h"
#include "src/tint/ast/if_statement.h"
@ -1538,26 +1537,6 @@ bool Validator::Call(const sem::Call* call, sem::Statement* current_statement) c
return true;
}
bool Validator::FallthroughStatement(const sem::Statement* stmt) const {
if (auto* block = As<sem::BlockStatement>(stmt->Parent())) {
if (auto* c = As<sem::CaseStatement>(block->Parent())) {
if (block->Declaration()->Last() == stmt->Declaration()) {
if (auto* s = As<sem::SwitchStatement>(c->Parent())) {
if (c->Declaration() != s->Declaration()->body.Back()) {
return true;
}
AddError("a fallthrough statement must not be used in the last switch case",
stmt->Declaration()->source);
return false;
}
}
}
}
AddError("fallthrough must only be used as the last statement of a case block",
stmt->Declaration()->source);
return false;
}
bool Validator::LoopStatement(const sem::LoopStatement* stmt) const {
if (stmt->Behaviors().Empty()) {
AddError("loop does not exit", stmt->Declaration()->source.Begin());

View File

@ -223,11 +223,6 @@ class Validator {
/// @returns true on success, false otherwise
bool WhileStatement(const sem::WhileStatement* stmt) const;
/// Validates a fallthrough statement
/// @param stmt the fallthrough to validate
/// @returns true on success, false otherwise
bool FallthroughStatement(const sem::Statement* stmt) const;
/// Validates a function
/// @param func the function to validate
/// @param stage the current pipeline stage

View File

@ -24,8 +24,6 @@ std::ostream& operator<<(std::ostream& out, Behavior behavior) {
return out << "Break";
case Behavior::kContinue:
return out << "Continue";
case Behavior::kFallthrough:
return out << "Fallthrough";
case Behavior::kNext:
return out << "Next";
}

View File

@ -25,7 +25,6 @@ enum class Behavior {
kReturn,
kBreak,
kContinue,
kFallthrough,
kNext,
};

View File

@ -22,7 +22,6 @@
#include <vector>
#include "src/tint/ast/call_statement.h"
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/ast/id_attribute.h"
#include "src/tint/ast/internal_attribute.h"
#include "src/tint/ast/interpolate_attribute.h"
@ -104,8 +103,8 @@ namespace {
const char kTempNamePrefix[] = "tint_tmp";
bool last_is_break_or_fallthrough(const ast::BlockStatement* stmts) {
return IsAnyOf<ast::BreakStatement, ast::FallthroughStatement>(stmts->Last());
bool last_is_break(const ast::BlockStatement* stmts) {
return IsAnyOf<ast::BreakStatement>(stmts->Last());
}
const char* convert_texel_format_to_glsl(const ast::TexelFormat format) {
@ -1821,7 +1820,7 @@ bool GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
if (!EmitStatements(stmt->body->statements)) {
return false;
}
if (!last_is_break_or_fallthrough(stmt->body)) {
if (!last_is_break(stmt->body)) {
line() << "break;";
}
}
@ -2748,10 +2747,6 @@ bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
},
[&](const ast::ContinueStatement* c) { return EmitContinue(c); },
[&](const ast::DiscardStatement* d) { return EmitDiscard(d); },
[&](const ast::FallthroughStatement*) {
line() << "/* fallthrough */";
return true;
},
[&](const ast::IfStatement* i) { return EmitIf(i); },
[&](const ast::LoopStatement* l) { return EmitLoop(l); },
[&](const ast::ForLoopStatement* l) { return EmitForLoop(l); },

View File

@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/writer/glsl/test_helper.h"
using namespace tint::number_suffixes; // NOLINT
@ -53,22 +52,6 @@ TEST_F(GlslGeneratorImplTest_Case, Emit_Case_BreaksByDefault) {
)");
}
TEST_F(GlslGeneratorImplTest_Case, Emit_Case_WithFallthrough) {
auto* s = Switch(1_i, Case(CaseSelector(5_i), Block(create<ast::FallthroughStatement>())),
DefaultCase());
WrapInFunction(s);
GeneratorImpl& gen = Build();
gen.increment_indent();
ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.error();
EXPECT_EQ(gen.result(), R"( case 5: {
/* fallthrough */
}
)");
}
TEST_F(GlslGeneratorImplTest_Case, Emit_Case_MultipleSelectors) {
auto* s = Switch(1_i,
Case(

View File

@ -23,7 +23,6 @@
#include <vector>
#include "src/tint/ast/call_statement.h"
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/ast/id_attribute.h"
#include "src/tint/ast/internal_attribute.h"
#include "src/tint/ast/interpolate_attribute.h"
@ -2506,18 +2505,7 @@ bool GeneratorImpl::EmitCase(const ast::SwitchStatement* s, size_t case_idx) {
return false;
}
// Inline all fallthrough case statements. FXC cannot handle fallthroughs.
while (tint::Is<ast::FallthroughStatement>(stmt->body->Last())) {
case_idx++;
stmt = s->body[case_idx];
// Generate each fallthrough case statement in a new block. This is done to
// prevent symbol collision of variables declared in these cases statements.
if (!EmitBlock(stmt->body)) {
return false;
}
}
if (!tint::IsAnyOf<ast::BreakStatement, ast::FallthroughStatement>(stmt->body->Last())) {
if (!tint::IsAnyOf<ast::BreakStatement>(stmt->body->Last())) {
line() << "break;";
}
@ -3534,10 +3522,6 @@ bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
[&](const ast::DiscardStatement* d) { //
return EmitDiscard(d);
},
[&](const ast::FallthroughStatement*) { //
line() << "/* fallthrough */";
return true;
},
[&](const ast::IfStatement* i) { //
return EmitIf(i);
},

View File

@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/writer/hlsl/test_helper.h"
using namespace tint::number_suffixes; // NOLINT
@ -53,28 +52,6 @@ TEST_F(HlslGeneratorImplTest_Case, Emit_Case_BreaksByDefault) {
)");
}
TEST_F(HlslGeneratorImplTest_Case, Emit_Case_WithFallthrough) {
auto* s = Switch(1_i, //
Case(CaseSelector(4_i), Block(create<ast::FallthroughStatement>())), //
Case(CaseSelector(5_i), Block(create<ast::ReturnStatement>())), //
DefaultCase());
WrapInFunction(s);
GeneratorImpl& gen = Build();
gen.increment_indent();
ASSERT_TRUE(gen.EmitCase(s, 0)) << gen.error();
EXPECT_EQ(gen.result(), R"( case 4: {
/* fallthrough */
{
return;
}
break;
}
)");
}
TEST_F(HlslGeneratorImplTest_Case, Emit_Case_MultipleSelectors) {
auto* s = Switch(1_i,
Case(utils::Vector{CaseSelector(5_i), CaseSelector(6_i)},

View File

@ -25,7 +25,6 @@
#include "src/tint/ast/bool_literal_expression.h"
#include "src/tint/ast/call_statement.h"
#include "src/tint/ast/disable_validation_attribute.h"
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/ast/float_literal_expression.h"
#include "src/tint/ast/id_attribute.h"
#include "src/tint/ast/interpolate_attribute.h"
@ -85,8 +84,8 @@
namespace tint::writer::msl {
namespace {
bool last_is_break_or_fallthrough(const ast::BlockStatement* stmts) {
return IsAnyOf<ast::BreakStatement, ast::FallthroughStatement>(stmts->Last());
bool last_is_break(const ast::BlockStatement* stmts) {
return IsAnyOf<ast::BreakStatement>(stmts->Last());
}
void PrintF32(std::ostream& out, float value) {
@ -1587,7 +1586,7 @@ bool GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
}
}
if (!last_is_break_or_fallthrough(stmt->body)) {
if (!last_is_break(stmt->body)) {
line() << "break;";
}
}
@ -2426,10 +2425,6 @@ bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
[&](const ast::DiscardStatement* d) { //
return EmitDiscard(d);
},
[&](const ast::FallthroughStatement*) { //
line() << "/* fallthrough */";
return true;
},
[&](const ast::IfStatement* i) { //
return EmitIf(i);
},

View File

@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/writer/msl/test_helper.h"
using namespace tint::number_suffixes; // NOLINT
@ -53,22 +52,6 @@ TEST_F(MslGeneratorImplTest, Emit_Case_BreaksByDefault) {
)");
}
TEST_F(MslGeneratorImplTest, Emit_Case_WithFallthrough) {
auto* s = Switch(1_i, Case(CaseSelector(5_i), Block(create<ast::FallthroughStatement>())),
DefaultCase());
WrapInFunction(s);
GeneratorImpl& gen = Build();
gen.increment_indent();
ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.error();
EXPECT_EQ(gen.result(), R"( case 5: {
/* fallthrough */
}
)");
}
TEST_F(MslGeneratorImplTest, Emit_Case_MultipleSelectors) {
auto* s = Switch(1_i,
Case(

View File

@ -19,7 +19,6 @@
#include "spirv/unified1/GLSL.std.450.h"
#include "src/tint/ast/call_statement.h"
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/ast/id_attribute.h"
#include "src/tint/ast/internal_attribute.h"
#include "src/tint/ast/traverse_expressions.h"
@ -86,10 +85,6 @@ uint32_t pipeline_stage_to_execution_model(ast::PipelineStage stage) {
return model;
}
bool LastIsFallthrough(const ast::BlockStatement* stmts) {
return !stmts->Empty() && stmts->Last()->Is<ast::FallthroughStatement>();
}
/// Returns the matrix type that is `type` or that is wrapped by
/// one or more levels of an arrays inside of `type`.
/// @param type the given type, which must not be null
@ -3515,9 +3510,7 @@ bool Builder::GenerateSwitchStatement(const ast::SwitchStatement* stmt) {
bool generated_default = false;
auto& body = stmt->body;
// We output the case statements in order they were entered in the original
// source. Each fallthrough goes to the next case entry, so is a forward
// branch, otherwise the branch is to the merge block which comes after
// the switch statement.
// source. The branch is to the merge block which comes after the switch statement.
for (uint32_t i = 0; i < body.Length(); i++) {
auto* item = body[i];
@ -3531,17 +3524,7 @@ bool Builder::GenerateSwitchStatement(const ast::SwitchStatement* stmt) {
if (!GenerateBlockStatement(item->body)) {
return false;
}
if (LastIsFallthrough(item->body)) {
if (i == (body.Length() - 1)) {
// This case is caught by Resolver validation
TINT_UNREACHABLE(Writer, builder_.Diagnostics());
return false;
}
if (!push_function_inst(spv::Op::OpBranch, {Operand(case_ids[i + 1])})) {
return false;
}
} else if (InsideBasicBlock()) {
if (InsideBasicBlock()) {
if (!push_function_inst(spv::Op::OpBranch, {Operand(merge_block_id)})) {
return false;
}
@ -3671,10 +3654,6 @@ bool Builder::GenerateStatement(const ast::Statement* stmt) {
[&](const ast::CallStatement* c) { return GenerateCallExpression(c->expr) != 0; },
[&](const ast::ContinueStatement* c) { return GenerateContinueStatement(c); },
[&](const ast::DiscardStatement* d) { return GenerateDiscardStatement(d); },
[&](const ast::FallthroughStatement*) {
// Do nothing here, the fallthrough gets handled by the switch code.
return true;
},
[&](const ast::IfStatement* i) { return GenerateIfStatement(i); },
[&](const ast::LoopStatement* l) { return GenerateLoopStatement(l); },
[&](const ast::ReturnStatement* r) { return GenerateReturnStatement(r); },

View File

@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/writer/spirv/spv_dump.h"
#include "src/tint/writer/spirv/test_helper.h"

View File

@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/writer/spirv/spv_dump.h"
#include "src/tint/writer/spirv/test_helper.h"
@ -328,69 +327,6 @@ OpFunctionEnd
)");
}
TEST_F(BuilderTest, Switch_CaseWithFallthrough) {
// switch(a) {
// case 1i:
// v = 1i;
// fallthrough;
// case 2i:
// v = 2i;
// default: {}
// v = 3i;
// }
auto* v = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
auto* a = GlobalVar("a", ty.i32(), ast::AddressSpace::kPrivate);
auto* func = Func("a_func", utils::Empty, ty.void_(),
utils::Vector{
Switch(Expr("a"), //
Case(CaseSelector(1_i), //
Block(Assign("v", 1_i), Fallthrough())), //
Case(CaseSelector(2_i), //
Block(Assign("v", 2_i))), //
DefaultCase(Block(Assign("v", 3_i)))),
});
spirv::Builder& b = Build();
ASSERT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
ASSERT_TRUE(b.GenerateGlobalVariable(a)) << b.error();
ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
EXPECT_EQ(DumpBuilder(b), R"(OpName %1 "v"
OpName %5 "a"
OpName %8 "a_func"
%3 = OpTypeInt 32 1
%2 = OpTypePointer Private %3
%4 = OpConstantNull %3
%1 = OpVariable %2 Private %4
%5 = OpVariable %2 Private %4
%7 = OpTypeVoid
%6 = OpTypeFunction %7
%15 = OpConstant %3 1
%16 = OpConstant %3 2
%17 = OpConstant %3 3
%8 = OpFunction %7 None %6
%9 = OpLabel
%11 = OpLoad %3 %5
OpSelectionMerge %10 None
OpSwitch %11 %12 1 %13 2 %14
%13 = OpLabel
OpStore %1 %15
OpBranch %14
%14 = OpLabel
OpStore %1 %16
OpBranch %10
%12 = OpLabel
OpStore %1 %17
OpBranch %10
%10 = OpLabel
OpReturn
OpFunctionEnd
)");
}
TEST_F(BuilderTest, Switch_WithNestedBreak) {
// switch (a) {
// case 1:
@ -460,7 +396,7 @@ TEST_F(BuilderTest, Switch_AllReturn) {
// return 1i;
// }
// case 2i: {
// fallthrough;
// return 1i;
// }
// default: {
// return 3i;
@ -469,9 +405,9 @@ TEST_F(BuilderTest, Switch_AllReturn) {
auto* fn = Func("f", utils::Empty, ty.i32(),
utils::Vector{
Switch(1_i, //
Case(CaseSelector(1_i), Block(Return(1_i))), //
Case(CaseSelector(2_i), Block(Fallthrough())), //
Switch(1_i, //
Case(CaseSelector(1_i), Block(Return(1_i))), //
Case(CaseSelector(2_i), Block(Return(1_i))), //
DefaultCase(Block(Return(3_i)))),
});
@ -491,7 +427,7 @@ OpSwitch %6 %7 1 %8 2 %9
%8 = OpLabel
OpReturnValue %6
%9 = OpLabel
OpBranch %7
OpReturnValue %6
%7 = OpLabel
OpReturnValue %10
%5 = OpLabel

View File

@ -970,7 +970,6 @@ bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
[&](const ast::CompoundAssignmentStatement* c) { return EmitCompoundAssign(c); },
[&](const ast::ContinueStatement* c) { return EmitContinue(c); },
[&](const ast::DiscardStatement* d) { return EmitDiscard(d); },
[&](const ast::FallthroughStatement* f) { return EmitFallthrough(f); },
[&](const ast::IfStatement* i) { return EmitIf(i); },
[&](const ast::IncrementDecrementStatement* l) { return EmitIncrementDecrement(l); },
[&](const ast::LoopStatement* l) { return EmitLoop(l); },
@ -1093,11 +1092,6 @@ bool GeneratorImpl::EmitContinue(const ast::ContinueStatement*) {
return true;
}
bool GeneratorImpl::EmitFallthrough(const ast::FallthroughStatement*) {
line() << "fallthrough;";
return true;
}
bool GeneratorImpl::EmitIf(const ast::IfStatement* stmt) {
{
auto out = line();

View File

@ -25,7 +25,6 @@
#include "src/tint/ast/compound_assignment_statement.h"
#include "src/tint/ast/continue_statement.h"
#include "src/tint/ast/discard_statement.h"
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/ast/for_loop_statement.h"
#include "src/tint/ast/if_statement.h"
#include "src/tint/ast/index_accessor_expression.h"
@ -124,10 +123,6 @@ class GeneratorImpl : public TextGenerator {
/// @param expr the expression
/// @returns true if the expression was emitted
bool EmitExpression(std::ostream& out, const ast::Expression* expr);
/// Handles generating a fallthrough statement
/// @param stmt the fallthrough statement
/// @returns true if the statement was successfully emitted
bool EmitFallthrough(const ast::FallthroughStatement* stmt);
/// Handles generating a function
/// @param func the function to generate
/// @returns true if the function was emitted

View File

@ -1,39 +0,0 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/writer/wgsl/test_helper.h"
using namespace tint::number_suffixes; // NOLINT
namespace tint::writer::wgsl {
namespace {
using WgslGeneratorImplTest = TestHelper;
TEST_F(WgslGeneratorImplTest, Emit_Fallthrough) {
auto* f = create<ast::FallthroughStatement>();
WrapInFunction(Switch(1_i, //
Case(CaseSelector(1_i), Block(f)), //
DefaultCase()));
GeneratorImpl& gen = Build();
gen.increment_indent();
ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
EXPECT_EQ(gen.result(), " fallthrough;\n");
}
} // namespace
} // namespace tint::writer::wgsl