From 3e7dc82b357eef62580ad8f876497c5362fd476e Mon Sep 17 00:00:00 2001 From: dan sinclair Date: Thu, 4 Jun 2020 17:05:35 +0000 Subject: [PATCH] [spirv-writer] Generate any intrinsic This CL adds the necessary code to generate an OpAny instruction. Bug: tint:5 Change-Id: I558b2cbf4bade3b4ab17997d24dcffddc32e2b41 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/22620 Reviewed-by: David Neto --- BUILD.gn | 3 + src/CMakeLists.txt | 3 + src/ast/intrinsic.cc | 40 ++++++++++++ src/ast/intrinsic.h | 43 +++++++++++++ src/type_determiner.cc | 26 ++------ src/writer/spirv/builder.cc | 51 +++++++++++++-- src/writer/spirv/builder.h | 6 ++ src/writer/spirv/builder_intrinsic_test.cc | 74 ++++++++++++++++++++++ 8 files changed, 220 insertions(+), 26 deletions(-) create mode 100644 src/ast/intrinsic.cc create mode 100644 src/ast/intrinsic.h create mode 100644 src/writer/spirv/builder_intrinsic_test.cc diff --git a/BUILD.gn b/BUILD.gn index 6c7a192e2f..d50d3cb3b0 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -250,6 +250,8 @@ source_set("libtint_core_src") { "src/ast/import.h", "src/ast/int_literal.cc", "src/ast/int_literal.h", + "src/ast/intrinsic.cc", + "src/ast/intrinsic.h", "src/ast/kill_statement.cc", "src/ast/kill_statement.h", "src/ast/literal.cc", @@ -686,6 +688,7 @@ source_set("tint_unittests_spv_writer_src") { "src/writer/spirv/builder_global_variable_test.cc", "src/writer/spirv/builder_ident_expression_test.cc", "src/writer/spirv/builder_if_test.cc", + "src/writer/spirv/builder_intrinsic_test.cc", "src/writer/spirv/builder_kill_test.cc", "src/writer/spirv/builder_literal_test.cc", "src/writer/spirv/builder_loop_test.cc", diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b18b1d4374..4292b9abb9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -87,6 +87,8 @@ set(TINT_LIB_SRCS ast/import.h ast/int_literal.cc ast/int_literal.h + ast/intrinsic.cc + ast/intrinsic.h ast/kill_statement.cc ast/kill_statement.h ast/literal.h @@ -422,6 +424,7 @@ if(${TINT_BUILD_SPV_WRITER}) writer/spirv/builder_global_variable_test.cc writer/spirv/builder_ident_expression_test.cc writer/spirv/builder_if_test.cc + writer/spirv/builder_intrinsic_test.cc writer/spirv/builder_kill_test.cc writer/spirv/builder_literal_test.cc writer/spirv/builder_loop_test.cc diff --git a/src/ast/intrinsic.cc b/src/ast/intrinsic.cc new file mode 100644 index 0000000000..30311f54b6 --- /dev/null +++ b/src/ast/intrinsic.cc @@ -0,0 +1,40 @@ +// 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/ast/intrinsic.h" + +namespace tint { +namespace ast { +namespace intrinsic { + +bool IsDerivative(const std::string& name) { + return name == "dpdx" || name == "dpdx_fine" || name == "dpdx_coarse" || + name == "dpdy" || name == "dpdy_fine" || name == "dpdy_coarse" || + name == "fwidth" || name == "fwidth_fine" || name == "fwidth_coarse"; +} + +bool IsFloatClassificationIntrinsic(const std::string& name) { + return name == "is_finite" || name == "is_inf" || name == "is_nan" || + name == "is_normal"; +} + +bool IsIntrinsic(const std::string& name) { + return IsDerivative(name) || name == "all" || name == "any" || + IsFloatClassificationIntrinsic(name) || name == "dot" || + name == "outer_product"; +} + +} // namespace intrinsic +} // namespace ast +} // namespace tint diff --git a/src/ast/intrinsic.h b/src/ast/intrinsic.h new file mode 100644 index 0000000000..f827b296e4 --- /dev/null +++ b/src/ast/intrinsic.h @@ -0,0 +1,43 @@ +// 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_AST_INTRINSIC_H_ +#define SRC_AST_INTRINSIC_H_ + +#include + +namespace tint { +namespace ast { +namespace intrinsic { + +/// Determine if the given |name | is a derivative intrinsic +/// @param name the name to check +/// @returns true if the given |name| is a derivative intrinsic +bool IsDerivative(const std::string& name); + +/// Determines if the given |name| is a float classification intrinsic +/// @param name the name to check +/// @returns true if the given |name| is a float intrinsic +bool IsFloatClassificationIntrinsic(const std::string& name); + +/// Determines if the given |name| is an intrinsic +/// @param name the name to check +/// @returns true if the given |name| is an intrinsic +bool IsIntrinsic(const std::string& name); + +} // namespace intrinsic +} // namespace ast +} // namespace tint + +#endif // SRC_AST_INTRINSIC_H_ diff --git a/src/type_determiner.cc b/src/type_determiner.cc index 4a562dfef2..8d455bd2ef 100644 --- a/src/type_determiner.cc +++ b/src/type_determiner.cc @@ -29,6 +29,7 @@ #include "src/ast/else_statement.h" #include "src/ast/identifier_expression.h" #include "src/ast/if_statement.h" +#include "src/ast/intrinsic.h" #include "src/ast/loop_statement.h" #include "src/ast/member_accessor_expression.h" #include "src/ast/return_statement.h" @@ -46,25 +47,6 @@ #include "src/ast/variable_decl_statement.h" namespace tint { -namespace { - -bool IsDerivative(const std::string& name) { - return name == "dpdx" || name == "dpdx_fine" || name == "dpdx_coarse" || - name == "dpdy" || name == "dpdy_fine" || name == "dpdy_coarse" || - name == "fwidth" || name == "fwidth_fine" || name == "fwidth_coarse"; -} - -bool IsFloatIntrinsic(const std::string& name) { - return name == "is_finite" || name == "is_inf" || name == "is_nan" || - name == "is_normal"; -} - -bool IsIntrinsic(const std::string& name) { - return IsDerivative(name) || name == "all" || name == "any" || - IsFloatIntrinsic(name) || name == "dot" || name == "outer_product"; -} - -} // namespace TypeDeterminer::TypeDeterminer(Context* ctx, ast::Module* mod) : ctx_(*ctx), mod_(mod) {} @@ -321,7 +303,7 @@ bool TypeDeterminer::DetermineCall(ast::CallExpression* expr) { if (expr->func()->IsIdentifier()) { auto* ident = expr->func()->AsIdentifier(); - if (IsIntrinsic(ident->name())) { + if (ast::intrinsic::IsIntrinsic(ident->name())) { if (!DetermineIntrinsic(ident->name(), expr)) return false; @@ -364,7 +346,7 @@ bool TypeDeterminer::DetermineCall(ast::CallExpression* expr) { bool TypeDeterminer::DetermineIntrinsic(const std::string& name, ast::CallExpression* expr) { - if (IsDerivative(name)) { + if (ast::intrinsic::IsDerivative(name)) { if (expr->params().size() != 1) { set_error(expr->source(), "incorrect number of parameters for " + name); return false; @@ -383,7 +365,7 @@ bool TypeDeterminer::DetermineIntrinsic(const std::string& name, ctx_.type_mgr().Get(std::make_unique())); return true; } - if (IsFloatIntrinsic(name)) { + if (ast::intrinsic::IsFloatClassificationIntrinsic(name)) { if (expr->params().size() != 1) { set_error(expr->source(), "incorrect number of parameters for " + name); return false; diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc index 7feaab3249..7d34b2f460 100644 --- a/src/writer/spirv/builder.cc +++ b/src/writer/spirv/builder.cc @@ -33,6 +33,7 @@ #include "src/ast/float_literal.h" #include "src/ast/identifier_expression.h" #include "src/ast/if_statement.h" +#include "src/ast/intrinsic.h" #include "src/ast/location_decoration.h" #include "src/ast/loop_statement.h" #include "src/ast/member_accessor_expression.h" @@ -1163,15 +1164,23 @@ uint32_t Builder::GenerateBinaryExpression(ast::BinaryExpression* expr) { } uint32_t Builder::GenerateCallExpression(ast::CallExpression* expr) { - // TODO(dsinclair): Support regular function calls - if (!expr->func()->IsIdentifier() || - !expr->func()->AsIdentifier()->has_path()) { - error_ = "function calls not supported yet."; + if (!expr->func()->IsIdentifier()) { + error_ = "invalid function name"; return 0; } auto* ident = expr->func()->AsIdentifier(); + if (!ident->has_path() && ast::intrinsic::IsIntrinsic(ident->name())) { + return GenerateIntrinsic(ident->name(), expr); + } + + // TODO(dsinclair): Support regular function calls + if (!ident->has_path()) { + error_ = "function calls not supported yet."; + return 0; + } + auto type_id = GenerateTypeIfNeeded(expr->func()->result_type()); if (type_id == 0) { return 0; @@ -1215,6 +1224,40 @@ uint32_t Builder::GenerateCallExpression(ast::CallExpression* expr) { return result_id; } +uint32_t Builder::GenerateIntrinsic(const std::string& name, + ast::CallExpression* call) { + auto result = result_op(); + auto result_id = result.to_i(); + + auto result_type_id = GenerateTypeIfNeeded(call->result_type()); + if (result_type_id == 0) { + return 0; + } + + std::vector params = {Operand::Int(result_type_id), result}; + for (const auto& p : call->params()) { + auto val_id = GenerateExpression(p.get()); + if (val_id == 0) { + return 0; + } + val_id = GenerateLoadIfNeeded(p->result_type(), val_id); + + params.push_back(Operand::Int(val_id)); + } + + spv::Op op = spv::Op::OpNop; + if (name == "any") { + op = spv::Op::OpAny; + } + if (op == spv::Op::OpNop) { + error_ = "unable to determine operator for: " + name; + return 0; + } + push_function_inst(op, params); + + return result_id; +} + uint32_t Builder::GenerateCastExpression(ast::CastExpression* cast) { auto result = result_op(); auto result_id = result.to_i(); diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h index 2a6fd65eac..5a430212aa 100644 --- a/src/writer/spirv/builder.h +++ b/src/writer/spirv/builder.h @@ -286,6 +286,12 @@ class Builder { /// @param expr the expression to generate /// @returns the expression ID on success or 0 otherwise uint32_t GenerateCallExpression(ast::CallExpression* expr); + /// Generates an intrinsic call + /// @param name the intrinsic name + /// @param call the call expression + /// @returns the expression ID on success or 0 otherwise + uint32_t GenerateIntrinsic(const std::string& name, + ast::CallExpression* call); /// Generates a cast expression /// @param expr the expression to generate /// @returns the expression ID on success or 0 otherwise diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc new file mode 100644 index 0000000000..054f9f42d4 --- /dev/null +++ b/src/writer/spirv/builder_intrinsic_test.cc @@ -0,0 +1,74 @@ +// 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/call_expression.h" +#include "src/ast/identifier_expression.h" +#include "src/ast/type/bool_type.h" +#include "src/ast/type/vector_type.h" +#include "src/ast/variable.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, Call_Any) { + ast::type::BoolType bool_type; + ast::type::VectorType vec3(&bool_type, 3); + + auto var = + std::make_unique("v", ast::StorageClass::kPrivate, &vec3); + + ast::ExpressionList params; + params.push_back(std::make_unique("v")); + ast::CallExpression expr(std::make_unique("any"), + std::move(params)); + + Context ctx; + ast::Module mod; + TypeDeterminer td(&ctx, &mod); + td.RegisterVariableForTesting(var.get()); + + ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error(); + + Builder b(&mod); + b.push_function(Function{}); + ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error(); + + EXPECT_EQ(b.GenerateCallExpression(&expr), 6u) << b.error(); + EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeBool +%3 = OpTypeVector %4 3 +%2 = OpTypePointer Private %3 +%5 = OpConstantNull %3 +%1 = OpVariable %2 Private %5 +)"); + EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), + R"(%7 = OpLoad %3 %1 +%6 = OpAny %4 %7 +)"); +} + +} // namespace +} // namespace spirv +} // namespace writer +} // namespace tint