From 7df4a845d95e2722acac8e03a855a0fd013e5b50 Mon Sep 17 00:00:00 2001 From: dan sinclair Date: Wed, 19 Aug 2020 17:44:45 +0000 Subject: [PATCH] [hlsl-writer] Add CallExpression. This Cl adds support for call expressions into the HLSL backend. Bug: tint:7 Change-Id: Id07e3d95e745aa016a658c3ec5d099f74f21a80e Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/26781 Commit-Queue: dan sinclair Reviewed-by: David Neto --- BUILD.gn | 1 + src/CMakeLists.txt | 1 + src/writer/hlsl/generator_impl.cc | 125 +++++++++++++++++- src/writer/hlsl/generator_impl.h | 17 +++ src/writer/hlsl/generator_impl_call_test.cc | 95 +++++++++++++ .../hlsl/generator_impl_function_test.cc | 26 ++-- 6 files changed, 248 insertions(+), 17 deletions(-) create mode 100644 src/writer/hlsl/generator_impl_call_test.cc diff --git a/BUILD.gn b/BUILD.gn index 1bc2bf1207..c833695641 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1063,6 +1063,7 @@ source_set("tint_unittests_hlsl_writer_src") { "src/writer/hlsl/generator_impl_binary_test.cc", "src/writer/hlsl/generator_impl_block_test.cc", "src/writer/hlsl/generator_impl_break_test.cc", + "src/writer/hlsl/generator_impl_call_test.cc", "src/writer/hlsl/generator_impl_case_test.cc", "src/writer/hlsl/generator_impl_constructor_test.cc", "src/writer/hlsl/generator_impl_continue_test.cc", diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1c475b0d0a..e1f7c533de 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -574,6 +574,7 @@ if (${TINT_BUILD_HLSL_WRITER}) writer/hlsl/generator_impl_binary_test.cc writer/hlsl/generator_impl_block_test.cc writer/hlsl/generator_impl_break_test.cc + writer/hlsl/generator_impl_call_test.cc writer/hlsl/generator_impl_case_test.cc writer/hlsl/generator_impl_constructor_test.cc writer/hlsl/generator_impl_continue_test.cc diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc index 6e53100c09..750c52d5a3 100644 --- a/src/writer/hlsl/generator_impl.cc +++ b/src/writer/hlsl/generator_impl.cc @@ -19,12 +19,15 @@ #include "src/ast/assignment_statement.h" #include "src/ast/binary_expression.h" #include "src/ast/bool_literal.h" +#include "src/ast/call_expression.h" +#include "src/ast/call_statement.h" #include "src/ast/case_statement.h" #include "src/ast/decorated_variable.h" #include "src/ast/else_statement.h" #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/loop_statement.h" #include "src/ast/member_accessor_expression.h" #include "src/ast/return_statement.h" @@ -318,6 +321,72 @@ bool GeneratorImpl::EmitBreak(ast::BreakStatement*) { return true; } +bool GeneratorImpl::EmitCall(ast::CallExpression* expr) { + if (!expr->func()->IsIdentifier()) { + error_ = "invalid function name"; + return 0; + } + + auto* ident = expr->func()->AsIdentifier(); + if (!ident->has_path() && ast::intrinsic::IsIntrinsic(ident->name())) { + error_ = "Intrinsics not supported in HLSL backend."; + return false; + } + + if (!ident->has_path()) { + auto name = ident->name(); + auto it = ep_func_name_remapped_.find(current_ep_name_ + "_" + name); + if (it != ep_func_name_remapped_.end()) { + name = it->second; + } + + auto* func = module_->FindFunctionByName(ident->name()); + if (func == nullptr) { + error_ = "Unable to find function: " + name; + return false; + } + + out_ << name << "("; + + bool first = true; + if (has_referenced_in_var_needing_struct(func)) { + auto var_name = current_ep_var_name(VarType::kIn); + if (!var_name.empty()) { + out_ << var_name; + first = false; + } + } + if (has_referenced_out_var_needing_struct(func)) { + auto var_name = current_ep_var_name(VarType::kOut); + if (!var_name.empty()) { + if (!first) { + out_ << ", "; + } + first = false; + out_ << var_name; + } + } + + const auto& params = expr->params(); + for (const auto& param : params) { + if (!first) { + out_ << ", "; + } + first = false; + + if (!EmitExpression(param.get())) { + return false; + } + } + + out_ << ")"; + } else { + error_ = "Imported functions not supported in HLSL backend."; + return false; + } + return true; +} + bool GeneratorImpl::EmitCase(ast::CaseStatement* stmt) { make_indent(); @@ -436,6 +505,9 @@ bool GeneratorImpl::EmitExpression(ast::Expression* expr) { if (expr->IsBinary()) { return EmitBinary(expr->AsBinary()); } + if (expr->IsCall()) { + return EmitCall(expr->AsCall()); + } if (expr->IsConstructor()) { return EmitConstructor(expr->AsConstructor()); } @@ -525,6 +597,40 @@ bool GeneratorImpl::EmitElse(ast::ElseStatement* stmt) { return EmitBlock(stmt->body()); } +bool GeneratorImpl::has_referenced_in_var_needing_struct(ast::Function* func) { + for (auto data : func->referenced_location_variables()) { + auto* var = data.first; + if (var->storage_class() == ast::StorageClass::kInput) { + return true; + } + } + + for (auto data : func->referenced_builtin_variables()) { + auto* var = data.first; + if (var->storage_class() == ast::StorageClass::kInput) { + return true; + } + } + return false; +} + +bool GeneratorImpl::has_referenced_out_var_needing_struct(ast::Function* func) { + for (auto data : func->referenced_location_variables()) { + auto* var = data.first; + if (var->storage_class() == ast::StorageClass::kOutput) { + return true; + } + } + + for (auto data : func->referenced_builtin_variables()) { + auto* var = data.first; + if (var->storage_class() == ast::StorageClass::kOutput) { + return true; + } + } + return false; +} + bool GeneratorImpl::has_referenced_var_needing_struct(ast::Function* func) { for (auto data : func->referenced_location_variables()) { auto* var = data.first; @@ -585,7 +691,16 @@ bool GeneratorImpl::EmitFunctionInternal(ast::Function* func, return false; } - out_ << " " << namer_.NameFor(name) << "("; + out_ << " "; + + if (emit_duplicate_functions) { + name = generate_name(name + "_" + ep_name); + ep_func_name_remapped_[ep_name + "_" + func->name()] = name; + } else { + name = namer_.NameFor(name); + } + + out_ << name << "("; bool first = true; @@ -994,6 +1109,14 @@ bool GeneratorImpl::EmitStatement(ast::Statement* stmt) { if (stmt->IsBreak()) { return EmitBreak(stmt->AsBreak()); } + if (stmt->IsCall()) { + make_indent(); + if (!EmitCall(stmt->AsCall()->expr())) { + return false; + } + out_ << ";" << std::endl; + return true; + } if (stmt->IsContinue()) { return EmitContinue(stmt->AsContinue()); } diff --git a/src/writer/hlsl/generator_impl.h b/src/writer/hlsl/generator_impl.h index b65b94da96..25789ab587 100644 --- a/src/writer/hlsl/generator_impl.h +++ b/src/writer/hlsl/generator_impl.h @@ -74,6 +74,10 @@ class GeneratorImpl : public TextGenerator { /// @param stmt the statement to emit /// @returns true if the statement was emitted successfully bool EmitBreak(ast::BreakStatement* stmt); + /// Handles generating a call expression + /// @param expr the call expression + /// @returns true if the call expression is emitted + bool EmitCall(ast::CallExpression* expr); /// Handles a case statement /// @param stmt the statement /// @returns true if the statment was emitted successfully @@ -189,6 +193,14 @@ class GeneratorImpl : public TextGenerator { /// @param builtin the builtin to convert /// @returns the string name of the builtin or blank on error std::string builtin_to_attribute(ast::Builtin builtin) const; + /// Determines if the function needs the input struct passed to it. + /// @param func the function to check + /// @returns true if there are input struct variables used in the function + bool has_referenced_in_var_needing_struct(ast::Function* func); + /// Determines if the function needs the output struct passed to it. + /// @param func the function to check + /// @returns true if there are output struct variables used in the function + bool has_referenced_out_var_needing_struct(ast::Function* func); /// Determines if any used module variable requires an input or output struct. /// @param func the function to check /// @returns true if an input or output struct is required. @@ -215,6 +227,11 @@ class GeneratorImpl : public TextGenerator { ScopeStack global_variables_; std::unordered_map ep_name_to_in_data_; std::unordered_map ep_name_to_out_data_; + + // This maps an input of "_" to a remapped + // function name. If there is no entry for a given key then function did + // not need to be remapped for the entry point and can be emitted directly. + std::unordered_map ep_func_name_remapped_; }; } // namespace hlsl diff --git a/src/writer/hlsl/generator_impl_call_test.cc b/src/writer/hlsl/generator_impl_call_test.cc new file mode 100644 index 0000000000..e1082a0fbd --- /dev/null +++ b/src/writer/hlsl/generator_impl_call_test.cc @@ -0,0 +1,95 @@ +// 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/call_statement.h" +#include "src/ast/function.h" +#include "src/ast/identifier_expression.h" +#include "src/ast/module.h" +#include "src/ast/type/void_type.h" +#include "src/writer/hlsl/generator_impl.h" + +namespace tint { +namespace writer { +namespace hlsl { +namespace { + +using HlslGeneratorImplTest = testing::Test; + +TEST_F(HlslGeneratorImplTest, EmitExpression_Call_WithoutParams) { + ast::type::VoidType void_type; + + auto id = std::make_unique("my_func"); + ast::CallExpression call(std::move(id), {}); + + auto func = std::make_unique("my_func", ast::VariableList{}, + &void_type); + + ast::Module m; + m.AddFunction(std::move(func)); + + GeneratorImpl g(&m); + ASSERT_TRUE(g.EmitExpression(&call)) << g.error(); + EXPECT_EQ(g.result(), "my_func()"); +} + +TEST_F(HlslGeneratorImplTest, EmitExpression_Call_WithParams) { + ast::type::VoidType void_type; + + auto id = std::make_unique("my_func"); + ast::ExpressionList params; + params.push_back(std::make_unique("param1")); + params.push_back(std::make_unique("param2")); + ast::CallExpression call(std::move(id), std::move(params)); + + auto func = std::make_unique("my_func", ast::VariableList{}, + &void_type); + + ast::Module m; + m.AddFunction(std::move(func)); + + GeneratorImpl g(&m); + ASSERT_TRUE(g.EmitExpression(&call)) << g.error(); + EXPECT_EQ(g.result(), "my_func(param1, param2)"); +} + +TEST_F(HlslGeneratorImplTest, EmitStatement_Call) { + ast::type::VoidType void_type; + + auto id = std::make_unique("my_func"); + ast::ExpressionList params; + params.push_back(std::make_unique("param1")); + params.push_back(std::make_unique("param2")); + ast::CallStatement call( + std::make_unique(std::move(id), std::move(params))); + + auto func = std::make_unique("my_func", ast::VariableList{}, + &void_type); + + ast::Module m; + m.AddFunction(std::move(func)); + + GeneratorImpl g(&m); + g.increment_indent(); + ASSERT_TRUE(g.EmitStatement(&call)) << g.error(); + EXPECT_EQ(g.result(), " my_func(param1, param2);\n"); +} + +} // namespace +} // namespace hlsl +} // namespace writer +} // namespace tint diff --git a/src/writer/hlsl/generator_impl_function_test.cc b/src/writer/hlsl/generator_impl_function_test.cc index 09835542a9..deeede917d 100644 --- a/src/writer/hlsl/generator_impl_function_test.cc +++ b/src/writer/hlsl/generator_impl_function_test.cc @@ -379,10 +379,8 @@ TEST_F(HlslGeneratorImplTest, EXPECT_EQ(g.result(), R"( ... )"); } -// TODO(dsinclair): Requires CallExpression -TEST_F( - HlslGeneratorImplTest, - DISABLED_Emit_Function_Called_By_EntryPoints_WithLocationGlobals_And_Params) { +TEST_F(HlslGeneratorImplTest, + Emit_Function_Called_By_EntryPoints_WithLocationGlobals_And_Params) { ast::type::VoidType void_type; ast::type::F32Type f32; @@ -483,9 +481,8 @@ ep_1_out ep_1(ep_1_in tint_in) { )"); } -// TODO(dsinclair): Requires CallExpression TEST_F(HlslGeneratorImplTest, - DISABLED_Emit_Function_Called_By_EntryPoints_NoUsedGlobals) { + Emit_Function_Called_By_EntryPoints_NoUsedGlobals) { ast::type::VoidType void_type; ast::type::F32Type f32; ast::type::VectorType vec4(&f32, 4); @@ -553,7 +550,7 @@ float sub_func(float param) { return param; } -fragment ep_1_out ep_1() { +ep_1_out ep_1() { ep_1_out tint_out; tint_out.depth = sub_func(1.00000000f); return tint_out; @@ -562,10 +559,8 @@ fragment ep_1_out ep_1() { )"); } -// TODO(dsinclair): Requires CallExpression -TEST_F( - HlslGeneratorImplTest, - DISABLED_Emit_Function_Called_By_EntryPoints_WithBuiltinGlobals_And_Params) { +TEST_F(HlslGeneratorImplTest, + Emit_Function_Called_By_EntryPoints_WithBuiltinGlobals_And_Params) { ast::type::VoidType void_type; ast::type::F32Type f32; ast::type::VectorType vec4(&f32, 4); @@ -640,7 +635,7 @@ TEST_F( GeneratorImpl g(&mod); ASSERT_TRUE(g.Generate()) << g.error(); EXPECT_EQ(g.result(), R"(struct ep_1_in { - float4 coord : SV_Position; + vector coord : SV_Position; }; struct ep_1_out { @@ -972,9 +967,8 @@ ep_1_out ep_1() { )"); } -// TODO(dsinclair): Requires CallExpression support TEST_F(HlslGeneratorImplTest, - DISABLED_Emit_Function_Called_Two_EntryPoints_WithoutGlobals) { + Emit_Function_Called_Two_EntryPoints_WithoutGlobals) { ast::type::VoidType void_type; ast::type::F32Type f32; @@ -1025,12 +1019,12 @@ TEST_F(HlslGeneratorImplTest, return 1.00000000f; } -fragment void ep_1() { +void ep_1() { float foo = sub_func(); return; } -fragment void ep_2() { +void ep_2() { float foo = sub_func(); return; }