[msl-writer] Emit intrinsics.

This CL adds intrinsics to the MSL backend.

Bug: tint:8, tint:159
Change-Id: I03e3c4bdf234ec4ca437ab1b1a0d4835e3342b0c
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/25500
Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
dan sinclair 2020-07-25 14:39:23 +00:00 committed by dan sinclair
parent 8f3c6356d4
commit 99ad0e8c94
5 changed files with 252 additions and 4 deletions

View File

@ -996,6 +996,7 @@ source_set("tint_unittests_msl_writer_src") {
"src/writer/msl/generator_impl_identifier_test.cc",
"src/writer/msl/generator_impl_if_test.cc",
"src/writer/msl/generator_impl_import_test.cc",
"src/writer/msl/generator_impl_intrinsic_test.cc",
"src/writer/msl/generator_impl_loop_test.cc",
"src/writer/msl/generator_impl_member_accessor_test.cc",
"src/writer/msl/generator_impl_module_constant_test.cc",

View File

@ -527,6 +527,7 @@ if(${TINT_BUILD_MSL_WRITER})
writer/msl/generator_impl_identifier_test.cc
writer/msl/generator_impl_if_test.cc
writer/msl/generator_impl_import_test.cc
writer/msl/generator_impl_intrinsic_test.cc
writer/msl/generator_impl_loop_test.cc
writer/msl/generator_impl_member_accessor_test.cc
writer/msl/generator_impl_module_constant_test.cc

View File

@ -400,6 +400,43 @@ std::string GeneratorImpl::current_ep_var_name(VarType type) {
return name;
}
std::string GeneratorImpl::generate_intrinsic_name(const std::string& name) {
if (name == "any") {
return "any";
}
if (name == "all") {
return "all";
}
if (name == "dot") {
return "dot";
}
if (name == "is_finite") {
return "isfinite";
}
if (name == "is_inf") {
return "isinf";
}
if (name == "is_nan") {
return "isnan";
}
if (name == "is_normal") {
return "isnormal";
}
if (name == "select") {
return "select";
}
if (name == "dpdy" || name == "dpdy_fine" || name == "dpdy_coarse") {
return "dfdy";
}
if (name == "dpdx" || name == "dpdx_fine" || name == "dpdx_coarse") {
return "dfdx";
}
if (name == "fwidth" || name == "fwidth_fine" || name == "fwidth_coarse") {
return "fwidth";
}
return "";
}
bool GeneratorImpl::EmitCall(ast::CallExpression* expr) {
if (!expr->func()->IsIdentifier()) {
error_ = "invalid function name";
@ -407,11 +444,86 @@ bool GeneratorImpl::EmitCall(ast::CallExpression* expr) {
}
auto* ident = expr->func()->AsIdentifier();
if (!ident->has_path() && ast::intrinsic::IsIntrinsic(ident->name())) {
// TODO(dsinclair): Generate intrinsic
error_ = "intrinsics not generated yet";
return false;
const auto& params = expr->params();
if (ident->name() == "outer_product") {
error_ = "outer_product not supported yet";
return false;
// TODO(dsinclair): This gets tricky. We need to generate two variables to
// hold the outer_product expressions, but we maybe inside an expression
// ourselves. So, this will need to, possibly, output the variables
// _before_ the expression which contains the outer product.
//
// This then has the follow on, what if we have `(false &&
// outer_product())` in that case, we shouldn't evaluate the expressions
// at all because of short circuting.
//
// So .... this turns out to be hard ...
// // We create variables to hold the two parameters in case they're
// // function calls with side effects.
// auto* param0 = param[0].get();
// auto* name0 = generate_name("outer_product_expr_0");
// auto* param1 = param[1].get();
// auto* name1 = generate_name("outer_product_expr_1");
// make_indent();
// if (!EmitType(expr->result_type(), "")) {
// return false;
// }
// out_ << "(";
// auto param1_type = params[1]->result_type()->UnwrapPtrIfNeeded();
// if (!param1_type->IsVector()) {
// error_ = "invalid param type in outer_product got: " +
// param1_type->type_name();
// return false;
// }
// for (uint32_t i = 0; i < param1_type->AsVector()->size(); ++i) {
// if (i > 0) {
// out_ << ", ";
// }
// if (!EmitExpression(params[0].get())) {
// return false;
// }
// out_ << " * ";
// if (!EmitExpression(params[1].get())) {
// return false;
// }
// out_ << "[" << i << "]";
// }
// out_ << ")";
} else {
auto name = generate_intrinsic_name(ident->name());
if (name.empty()) {
error_ = "unable to determine intrinsic name for intrinsic: " +
ident->name();
return false;
}
make_indent();
out_ << name << "(";
bool first = true;
for (const auto& param : params) {
if (!first) {
out_ << ", ";
}
first = false;
if (!EmitExpression(param.get())) {
return false;
}
}
out_ << ")";
}
return true;
}
if (!ident->has_path()) {

View File

@ -224,6 +224,10 @@ class GeneratorImpl : public TextGenerator {
/// @param prefix the prefix of the name to generate
/// @returns the name
std::string generate_name(const std::string& prefix);
/// Generates an intrinsic name from the given name
/// @param name the name to convert to an intrinsic
/// @returns the intrinsic name or blank on error
std::string generate_intrinsic_name(const std::string& name);
/// Checks if the global variable is in an input or output struct
/// @param var the variable to check

View File

@ -0,0 +1,130 @@
// 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 "gtest/gtest.h"
#include "src/ast/call_expression.h"
#include "src/ast/identifier_expression.h"
#include "src/ast/module.h"
#include "src/ast/type/f32_type.h"
#include "src/ast/type/vector_type.h"
#include "src/context.h"
#include "src/type_determiner.h"
#include "src/writer/msl/generator_impl.h"
namespace tint {
namespace writer {
namespace msl {
namespace {
using MslGeneratorImplTest = testing::Test;
struct IntrinsicData {
const char* name;
const char* msl_name;
};
inline std::ostream& operator<<(std::ostream& out, IntrinsicData data) {
out << data.name;
return out;
}
using MslIntrinsicTest = testing::TestWithParam<IntrinsicData>;
TEST_P(MslIntrinsicTest, Emit) {
auto param = GetParam();
ast::Module m;
GeneratorImpl g(&m);
EXPECT_EQ(g.generate_intrinsic_name(param.name), param.msl_name);
}
INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest,
MslIntrinsicTest,
testing::Values(IntrinsicData{"any", "any"},
IntrinsicData{"all", "all"},
IntrinsicData{"dot", "dot"},
IntrinsicData{"dpdx", "dfdx"},
IntrinsicData{"dpdx_coarse", "dfdx"},
IntrinsicData{"dpdx_fine", "dfdx"},
IntrinsicData{"dpdy", "dfdy"},
IntrinsicData{"dpdy_coarse", "dfdy"},
IntrinsicData{"dpdy_fine", "dfdy"},
IntrinsicData{"fwidth", "fwidth"},
IntrinsicData{"fwidth_coarse",
"fwidth"},
IntrinsicData{"fwidth_fine", "fwidth"},
IntrinsicData{"is_finite", "isfinite"},
IntrinsicData{"is_inf", "isinf"},
IntrinsicData{"is_nan", "isnan"},
IntrinsicData{"is_normal", "isnormal"},
IntrinsicData{"select", "select"}));
TEST_F(MslGeneratorImplTest, DISABLED_Intrinsic_OuterProduct) {
ast::type::F32Type f32;
ast::type::VectorType vec2(&f32, 2);
ast::type::VectorType vec3(&f32, 3);
auto a =
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &vec2);
auto b =
std::make_unique<ast::Variable>("b", ast::StorageClass::kNone, &vec3);
ast::ExpressionList params;
params.push_back(std::make_unique<ast::IdentifierExpression>("a"));
params.push_back(std::make_unique<ast::IdentifierExpression>("b"));
ast::CallExpression call(
std::make_unique<ast::IdentifierExpression>("outer_product"),
std::move(params));
Context ctx;
ast::Module m;
TypeDeterminer td(&ctx, &m);
td.RegisterVariableForTesting(a.get());
td.RegisterVariableForTesting(b.get());
m.AddGlobalVariable(std::move(a));
m.AddGlobalVariable(std::move(b));
ASSERT_TRUE(td.Determine()) << td.error();
ASSERT_TRUE(td.DetermineResultType(&call)) << td.error();
GeneratorImpl g(&m);
g.increment_indent();
ASSERT_TRUE(g.EmitExpression(&call)) << g.error();
EXPECT_EQ(g.result(), " float3x2(a * b[0], a * b[1], a * b[2])");
}
TEST_F(MslGeneratorImplTest, Intrinsic_Bad_Name) {
ast::Module m;
GeneratorImpl g(&m);
EXPECT_EQ(g.generate_intrinsic_name("unknown name"), "");
}
TEST_F(MslGeneratorImplTest, Intrinsic_Call) {
ast::ExpressionList params;
params.push_back(std::make_unique<ast::IdentifierExpression>("param1"));
params.push_back(std::make_unique<ast::IdentifierExpression>("param2"));
ast::CallExpression call(std::make_unique<ast::IdentifierExpression>("dot"),
std::move(params));
ast::Module m;
GeneratorImpl g(&m);
g.increment_indent();
ASSERT_TRUE(g.EmitExpression(&call)) << g.error();
EXPECT_EQ(g.result(), " dot(param1, param2)");
}
} // namespace
} // namespace msl
} // namespace writer
} // namespace tint