[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_if_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_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_if_test.cc
|
||||
writer/spirv/builder_literal_test.cc
|
||||
writer/spirv/builder_loop_test.cc
|
||||
writer/spirv/builder_return_test.cc
|
||||
writer/spirv/builder_test.cc
|
||||
writer/spirv/builder_type_test.cc
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "src/ast/if_statement.h"
|
||||
#include "src/ast/int_literal.h"
|
||||
#include "src/ast/location_decoration.h"
|
||||
#include "src/ast/loop_statement.h"
|
||||
#include "src/ast/return_statement.h"
|
||||
#include "src/ast/scalar_constructor_expression.h"
|
||||
#include "src/ast/set_decoration.h"
|
||||
|
@ -808,6 +809,43 @@ bool Builder::GenerateReturnStatement(ast::ReturnStatement* stmt) {
|
|||
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) {
|
||||
for (const auto& inst : list) {
|
||||
if (!GenerateStatement(inst.get())) {
|
||||
|
@ -824,6 +862,9 @@ bool Builder::GenerateStatement(ast::Statement* stmt) {
|
|||
if (stmt->IsIf()) {
|
||||
return GenerateIfStatement(stmt->AsIf());
|
||||
}
|
||||
if (stmt->IsLoop()) {
|
||||
return GenerateLoopStatement(stmt->AsLoop());
|
||||
}
|
||||
if (stmt->IsReturn()) {
|
||||
return GenerateReturnStatement(stmt->AsReturn());
|
||||
}
|
||||
|
|
|
@ -202,6 +202,10 @@ class Builder {
|
|||
/// @param expr the expression to generate
|
||||
/// @returns the expression ID on success or 0 otherwise
|
||||
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
|
||||
/// @param stmt the statement to generate
|
||||
/// @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