From 6866cb7677eca8ed79b8cc9e30d684cd95c5c91a Mon Sep 17 00:00:00 2001 From: dan sinclair Date: Mon, 20 Apr 2020 14:07:29 +0000 Subject: [PATCH] [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 --- BUILD.gn | 1 + src/CMakeLists.txt | 1 + src/writer/spirv/builder.cc | 41 ++++++ src/writer/spirv/builder.h | 4 + src/writer/spirv/builder_loop_test.cc | 173 ++++++++++++++++++++++++++ 5 files changed, 220 insertions(+) create mode 100644 src/writer/spirv/builder_loop_test.cc diff --git a/BUILD.gn b/BUILD.gn index 04bf5dc708..8cd94fca2b 100644 --- a/BUILD.gn +++ b/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", diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9d2c2fe04a..cb677ab823 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc index e9e45d7c04..0e1a76f92a 100644 --- a/src/writer/spirv/builder.cc +++ b/src/writer/spirv/builder.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()); } diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h index 0faf52d1ec..f1ae870c7b 100644 --- a/src/writer/spirv/builder.h +++ b/src/writer/spirv/builder.h @@ -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 diff --git a/src/writer/spirv/builder_loop_test.cc b/src/writer/spirv/builder_loop_test.cc new file mode 100644 index 0000000000..2143823c09 --- /dev/null +++ b/src/writer/spirv/builder_loop_test.cc @@ -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 + +#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("v", ast::StorageClass::kPrivate, &i32); + + ast::StatementList body; + body.push_back(std::make_unique( + std::make_unique("v"), + std::make_unique( + std::make_unique(&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("v", ast::StorageClass::kPrivate, &i32); + + ast::StatementList body; + body.push_back(std::make_unique( + std::make_unique("v"), + std::make_unique( + std::make_unique(&i32, 2)))); + + ast::StatementList continuing; + continuing.push_back(std::make_unique( + std::make_unique("v"), + std::make_unique( + std::make_unique(&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