[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:
parent
8f3c6356d4
commit
99ad0e8c94
1
BUILD.gn
1
BUILD.gn
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue