[spirv-writer] Add initial loop support.
This CL adds the initial generation of a loop construct into the spirv-writer. This does not support break or continue yet. Bug: tint:5 Change-Id: I41f5d9b634a1a4120f880c4143feacc7e58e2147 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/19620 Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
parent
340a51e787
commit
6866cb7677
1
BUILD.gn
1
BUILD.gn
|
@ -558,6 +558,7 @@ source_set("tint_unittests_spv_writer_src") {
|
||||||
"src/writer/spirv/builder_ident_expression_test.cc",
|
"src/writer/spirv/builder_ident_expression_test.cc",
|
||||||
"src/writer/spirv/builder_if_test.cc",
|
"src/writer/spirv/builder_if_test.cc",
|
||||||
"src/writer/spirv/builder_literal_test.cc",
|
"src/writer/spirv/builder_literal_test.cc",
|
||||||
|
"src/writer/spirv/builder_loop_test.cc",
|
||||||
"src/writer/spirv/builder_return_test.cc",
|
"src/writer/spirv/builder_return_test.cc",
|
||||||
"src/writer/spirv/builder_test.cc",
|
"src/writer/spirv/builder_test.cc",
|
||||||
"src/writer/spirv/builder_type_test.cc",
|
"src/writer/spirv/builder_type_test.cc",
|
||||||
|
|
|
@ -431,6 +431,7 @@ if(${TINT_BUILD_SPV_WRITER})
|
||||||
writer/spirv/builder_ident_expression_test.cc
|
writer/spirv/builder_ident_expression_test.cc
|
||||||
writer/spirv/builder_if_test.cc
|
writer/spirv/builder_if_test.cc
|
||||||
writer/spirv/builder_literal_test.cc
|
writer/spirv/builder_literal_test.cc
|
||||||
|
writer/spirv/builder_loop_test.cc
|
||||||
writer/spirv/builder_return_test.cc
|
writer/spirv/builder_return_test.cc
|
||||||
writer/spirv/builder_test.cc
|
writer/spirv/builder_test.cc
|
||||||
writer/spirv/builder_type_test.cc
|
writer/spirv/builder_type_test.cc
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "src/ast/if_statement.h"
|
#include "src/ast/if_statement.h"
|
||||||
#include "src/ast/int_literal.h"
|
#include "src/ast/int_literal.h"
|
||||||
#include "src/ast/location_decoration.h"
|
#include "src/ast/location_decoration.h"
|
||||||
|
#include "src/ast/loop_statement.h"
|
||||||
#include "src/ast/return_statement.h"
|
#include "src/ast/return_statement.h"
|
||||||
#include "src/ast/scalar_constructor_expression.h"
|
#include "src/ast/scalar_constructor_expression.h"
|
||||||
#include "src/ast/set_decoration.h"
|
#include "src/ast/set_decoration.h"
|
||||||
|
@ -808,6 +809,43 @@ bool Builder::GenerateReturnStatement(ast::ReturnStatement* stmt) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Builder::GenerateLoopStatement(ast::LoopStatement* stmt) {
|
||||||
|
auto loop_header = result_op();
|
||||||
|
auto loop_header_id = loop_header.to_i();
|
||||||
|
push_function_inst(spv::Op::OpBranch, {Operand::Int(loop_header_id)});
|
||||||
|
push_function_inst(spv::Op::OpLabel, {loop_header});
|
||||||
|
|
||||||
|
auto merge_block = result_op();
|
||||||
|
auto merge_block_id = merge_block.to_i();
|
||||||
|
auto continue_block = result_op();
|
||||||
|
auto continue_block_id = continue_block.to_i();
|
||||||
|
|
||||||
|
auto body_block = result_op();
|
||||||
|
auto body_block_id = body_block.to_i();
|
||||||
|
|
||||||
|
push_function_inst(
|
||||||
|
spv::Op::OpLoopMerge,
|
||||||
|
{Operand::Int(merge_block_id), Operand::Int(continue_block_id),
|
||||||
|
Operand::Int(SpvLoopControlMaskNone)});
|
||||||
|
|
||||||
|
push_function_inst(spv::Op::OpBranch, {Operand::Int(body_block_id)});
|
||||||
|
push_function_inst(spv::Op::OpLabel, {body_block});
|
||||||
|
if (!GenerateStatementList(stmt->body())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
push_function_inst(spv::Op::OpBranch, {Operand::Int(continue_block_id)});
|
||||||
|
|
||||||
|
push_function_inst(spv::Op::OpLabel, {continue_block});
|
||||||
|
if (!GenerateStatementList(stmt->continuing())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
push_function_inst(spv::Op::OpBranch, {Operand::Int(loop_header_id)});
|
||||||
|
|
||||||
|
push_function_inst(spv::Op::OpLabel, {merge_block});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Builder::GenerateStatementList(const ast::StatementList& list) {
|
bool Builder::GenerateStatementList(const ast::StatementList& list) {
|
||||||
for (const auto& inst : list) {
|
for (const auto& inst : list) {
|
||||||
if (!GenerateStatement(inst.get())) {
|
if (!GenerateStatement(inst.get())) {
|
||||||
|
@ -824,6 +862,9 @@ bool Builder::GenerateStatement(ast::Statement* stmt) {
|
||||||
if (stmt->IsIf()) {
|
if (stmt->IsIf()) {
|
||||||
return GenerateIfStatement(stmt->AsIf());
|
return GenerateIfStatement(stmt->AsIf());
|
||||||
}
|
}
|
||||||
|
if (stmt->IsLoop()) {
|
||||||
|
return GenerateLoopStatement(stmt->AsLoop());
|
||||||
|
}
|
||||||
if (stmt->IsReturn()) {
|
if (stmt->IsReturn()) {
|
||||||
return GenerateReturnStatement(stmt->AsReturn());
|
return GenerateReturnStatement(stmt->AsReturn());
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,6 +202,10 @@ class Builder {
|
||||||
/// @param expr the expression to generate
|
/// @param expr the expression to generate
|
||||||
/// @returns the expression ID on success or 0 otherwise
|
/// @returns the expression ID on success or 0 otherwise
|
||||||
uint32_t GenerateBinaryExpression(ast::BinaryExpression* expr);
|
uint32_t GenerateBinaryExpression(ast::BinaryExpression* expr);
|
||||||
|
/// Generates a loop statement
|
||||||
|
/// @param stmt the statement to generate
|
||||||
|
/// @returns true on successful generation
|
||||||
|
bool GenerateLoopStatement(ast::LoopStatement* stmt);
|
||||||
/// Generates a return statement
|
/// Generates a return statement
|
||||||
/// @param stmt the statement to generate
|
/// @param stmt the statement to generate
|
||||||
/// @returns true on success, false otherwise
|
/// @returns true on success, false otherwise
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
// 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 <memory>
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "src/ast/assignment_statement.h"
|
||||||
|
#include "src/ast/identifier_expression.h"
|
||||||
|
#include "src/ast/int_literal.h"
|
||||||
|
#include "src/ast/loop_statement.h"
|
||||||
|
#include "src/ast/scalar_constructor_expression.h"
|
||||||
|
#include "src/ast/type/i32_type.h"
|
||||||
|
#include "src/context.h"
|
||||||
|
#include "src/type_determiner.h"
|
||||||
|
#include "src/writer/spirv/builder.h"
|
||||||
|
#include "src/writer/spirv/spv_dump.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace writer {
|
||||||
|
namespace spirv {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using BuilderTest = testing::Test;
|
||||||
|
|
||||||
|
TEST_F(BuilderTest, Loop_Empty) {
|
||||||
|
// loop {
|
||||||
|
// }
|
||||||
|
|
||||||
|
ast::LoopStatement expr;
|
||||||
|
|
||||||
|
Context ctx;
|
||||||
|
TypeDeterminer td(&ctx);
|
||||||
|
ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
|
||||||
|
|
||||||
|
Builder b;
|
||||||
|
b.push_function(Function{});
|
||||||
|
|
||||||
|
EXPECT_TRUE(b.GenerateLoopStatement(&expr)) << b.error();
|
||||||
|
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
|
||||||
|
R"(OpBranch %1
|
||||||
|
%1 = OpLabel
|
||||||
|
OpLoopMerge %2 %3 None
|
||||||
|
OpBranch %4
|
||||||
|
%4 = OpLabel
|
||||||
|
OpBranch %3
|
||||||
|
%3 = OpLabel
|
||||||
|
OpBranch %1
|
||||||
|
%2 = OpLabel
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(BuilderTest, Loop_WithoutContinuing) {
|
||||||
|
ast::type::I32Type i32;
|
||||||
|
|
||||||
|
// loop {
|
||||||
|
// v = 2;
|
||||||
|
// }
|
||||||
|
auto var =
|
||||||
|
std::make_unique<ast::Variable>("v", ast::StorageClass::kPrivate, &i32);
|
||||||
|
|
||||||
|
ast::StatementList body;
|
||||||
|
body.push_back(std::make_unique<ast::AssignmentStatement>(
|
||||||
|
std::make_unique<ast::IdentifierExpression>("v"),
|
||||||
|
std::make_unique<ast::ScalarConstructorExpression>(
|
||||||
|
std::make_unique<ast::IntLiteral>(&i32, 2))));
|
||||||
|
|
||||||
|
ast::StatementList continuing;
|
||||||
|
ast::LoopStatement expr(std::move(body), std::move(continuing));
|
||||||
|
|
||||||
|
Context ctx;
|
||||||
|
TypeDeterminer td(&ctx);
|
||||||
|
td.RegisterVariableForTesting(var.get());
|
||||||
|
ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
|
||||||
|
|
||||||
|
Builder b;
|
||||||
|
b.push_function(Function{});
|
||||||
|
ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
|
||||||
|
|
||||||
|
EXPECT_TRUE(b.GenerateLoopStatement(&expr)) << b.error();
|
||||||
|
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
|
||||||
|
%2 = OpTypePointer Private %3
|
||||||
|
%1 = OpVariable %2 Private
|
||||||
|
%8 = OpConstant %3 2
|
||||||
|
)");
|
||||||
|
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
|
||||||
|
R"(OpBranch %4
|
||||||
|
%4 = OpLabel
|
||||||
|
OpLoopMerge %5 %6 None
|
||||||
|
OpBranch %7
|
||||||
|
%7 = OpLabel
|
||||||
|
OpStore %1 %8
|
||||||
|
OpBranch %6
|
||||||
|
%6 = OpLabel
|
||||||
|
OpBranch %4
|
||||||
|
%5 = OpLabel
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(BuilderTest, Loop_WithContinuing) {
|
||||||
|
ast::type::I32Type i32;
|
||||||
|
// loop {
|
||||||
|
// a = 2;
|
||||||
|
// continuing {
|
||||||
|
// a = 3;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
auto var =
|
||||||
|
std::make_unique<ast::Variable>("v", ast::StorageClass::kPrivate, &i32);
|
||||||
|
|
||||||
|
ast::StatementList body;
|
||||||
|
body.push_back(std::make_unique<ast::AssignmentStatement>(
|
||||||
|
std::make_unique<ast::IdentifierExpression>("v"),
|
||||||
|
std::make_unique<ast::ScalarConstructorExpression>(
|
||||||
|
std::make_unique<ast::IntLiteral>(&i32, 2))));
|
||||||
|
|
||||||
|
ast::StatementList continuing;
|
||||||
|
continuing.push_back(std::make_unique<ast::AssignmentStatement>(
|
||||||
|
std::make_unique<ast::IdentifierExpression>("v"),
|
||||||
|
std::make_unique<ast::ScalarConstructorExpression>(
|
||||||
|
std::make_unique<ast::IntLiteral>(&i32, 3))));
|
||||||
|
ast::LoopStatement expr(std::move(body), std::move(continuing));
|
||||||
|
|
||||||
|
Context ctx;
|
||||||
|
TypeDeterminer td(&ctx);
|
||||||
|
td.RegisterVariableForTesting(var.get());
|
||||||
|
ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
|
||||||
|
|
||||||
|
Builder b;
|
||||||
|
b.push_function(Function{});
|
||||||
|
ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
|
||||||
|
|
||||||
|
EXPECT_TRUE(b.GenerateLoopStatement(&expr)) << b.error();
|
||||||
|
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
|
||||||
|
%2 = OpTypePointer Private %3
|
||||||
|
%1 = OpVariable %2 Private
|
||||||
|
%8 = OpConstant %3 2
|
||||||
|
%9 = OpConstant %3 3
|
||||||
|
)");
|
||||||
|
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
|
||||||
|
R"(OpBranch %4
|
||||||
|
%4 = OpLabel
|
||||||
|
OpLoopMerge %5 %6 None
|
||||||
|
OpBranch %7
|
||||||
|
%7 = OpLabel
|
||||||
|
OpStore %1 %8
|
||||||
|
OpBranch %6
|
||||||
|
%6 = OpLabel
|
||||||
|
OpStore %1 %9
|
||||||
|
OpBranch %4
|
||||||
|
%5 = OpLabel
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(BuilderTest, DISABLED_Loop_WithContinuing) {}
|
||||||
|
|
||||||
|
TEST_F(BuilderTest, DISABLED_Loop_WithBreak) {}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace spirv
|
||||||
|
} // namespace writer
|
||||||
|
} // namespace tint
|
Loading…
Reference in New Issue