[hlsl-writer] Generate intrinsics.

This CL adds the beginning of intrinsic emission for the HLSL backend.
The `outer_product`, `is_normal` and `select` intrinsics are currently
missing.

Bug: tint:7
Change-Id: Ice7a2b285eeb52041e3accd9751e127d6c5a0177
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/26927
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
dan sinclair 2020-08-19 19:17:54 +00:00 committed by Commit Bot service account
parent 3739a23f24
commit a1087ee513
5 changed files with 270 additions and 2 deletions

View File

@ -1073,6 +1073,7 @@ source_set("tint_unittests_hlsl_writer_src") {
"src/writer/hlsl/generator_impl_function_test.cc",
"src/writer/hlsl/generator_impl_identifier_test.cc",
"src/writer/hlsl/generator_impl_if_test.cc",
"src/writer/hlsl/generator_impl_intrinsic_test.cc",
"src/writer/hlsl/generator_impl_loop_test.cc",
"src/writer/hlsl/generator_impl_member_accessor_test.cc",
"src/writer/hlsl/generator_impl_module_constant_test.cc",

View File

@ -584,6 +584,7 @@ if (${TINT_BUILD_HLSL_WRITER})
writer/hlsl/generator_impl_function_test.cc
writer/hlsl/generator_impl_identifier_test.cc
writer/hlsl/generator_impl_if_test.cc
writer/hlsl/generator_impl_intrinsic_test.cc
writer/hlsl/generator_impl_loop_test.cc
writer/hlsl/generator_impl_member_accessor_test.cc
writer/hlsl/generator_impl_module_constant_test.cc

View File

@ -339,6 +339,49 @@ bool GeneratorImpl::EmitBreak(ast::BreakStatement*) {
return true;
}
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 == "dpdy") {
return "ddy";
}
if (name == "dpdy_fine") {
return "ddy_fine";
}
if (name == "dpdy_coarse") {
return "ddy_coarse";
}
if (name == "dpdx") {
return "ddx";
}
if (name == "dpdx_fine") {
return "ddx_fine";
}
if (name == "dpdx_coarse") {
return "ddx_coarse";
}
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";
@ -347,8 +390,91 @@ bool GeneratorImpl::EmitCall(ast::CallExpression* expr) {
auto* ident = expr->func()->AsIdentifier();
if (!ident->has_path() && ast::intrinsic::IsIntrinsic(ident->name())) {
error_ = "Intrinsics not supported in HLSL backend.";
return false;
const auto& params = expr->params();
if (ident->name() == "select") {
error_ = "select not supported in HLSL backend yet";
return false;
} else if (ident->name() == "is_normal") {
error_ = "is_normal not supported in HLSL backend yet";
return false;
} else 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

@ -197,6 +197,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);
/// Converts a builtin to an attribute name
/// @param builtin the builtin to convert
/// @returns the string name of the builtin or blank on error

View File

@ -0,0 +1,136 @@
// 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/hlsl/generator_impl.h"
namespace tint {
namespace writer {
namespace hlsl {
namespace {
using HlslGeneratorImplTest = testing::Test;
struct IntrinsicData {
const char* name;
const char* hlsl_name;
};
inline std::ostream& operator<<(std::ostream& out, IntrinsicData data) {
out << data.name;
return out;
}
using HlslIntrinsicTest = testing::TestWithParam<IntrinsicData>;
TEST_P(HlslIntrinsicTest, Emit) {
auto param = GetParam();
ast::Module m;
GeneratorImpl g(&m);
EXPECT_EQ(g.generate_intrinsic_name(param.name), param.hlsl_name);
}
INSTANTIATE_TEST_SUITE_P(
HlslGeneratorImplTest,
HlslIntrinsicTest,
testing::Values(IntrinsicData{"any", "any"},
IntrinsicData{"all", "all"},
IntrinsicData{"dot", "dot"},
IntrinsicData{"dpdx", "ddx"},
IntrinsicData{"dpdx_coarse", "ddx_coarse"},
IntrinsicData{"dpdx_fine", "ddx_fine"},
IntrinsicData{"dpdy", "ddy"},
IntrinsicData{"dpdy_coarse", "ddy_coarse"},
IntrinsicData{"dpdy_fine", "ddy_fine"},
IntrinsicData{"fwidth", "fwidth"},
IntrinsicData{"fwidth_coarse", "fwidth"},
IntrinsicData{"fwidth_fine", "fwidth"},
IntrinsicData{"is_finite", "isfinite"},
IntrinsicData{"is_inf", "isinf"},
IntrinsicData{"is_nan", "isnan"}));
TEST_F(HlslGeneratorImplTest, DISABLED_Intrinsic_IsNormal) {
FAIL();
}
TEST_F(HlslGeneratorImplTest, DISABLED_Intrinsic_Select) {
FAIL();
}
TEST_F(HlslGeneratorImplTest, 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(HlslGeneratorImplTest, Intrinsic_Bad_Name) {
ast::Module m;
GeneratorImpl g(&m);
EXPECT_EQ(g.generate_intrinsic_name("unknown name"), "");
}
TEST_F(HlslGeneratorImplTest, 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 hlsl
} // namespace writer
} // namespace tint