[hlsl-writer] Add function handling. This CL adds the beginning of function handling to the HLSL generator. Bug: tint:7 Change-Id: Id40109c342e7a128b1fe79a0c50967e1dbd125eb Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/26662 Reviewed-by: David Neto <dneto@google.com> Commit-Queue: David Neto <dneto@google.com>
diff --git a/BUILD.gn b/BUILD.gn index 3a2bdfc..1bc2bf1 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -1067,6 +1067,7 @@ "src/writer/hlsl/generator_impl_constructor_test.cc", "src/writer/hlsl/generator_impl_continue_test.cc", "src/writer/hlsl/generator_impl_discard_test.cc", + "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_loop_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 01d2ae6..1c475b0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt
@@ -578,6 +578,7 @@ writer/hlsl/generator_impl_constructor_test.cc writer/hlsl/generator_impl_continue_test.cc writer/hlsl/generator_impl_discard_test.cc + 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_loop_test.cc
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc index fd77319..eb6edf4 100644 --- a/src/writer/hlsl/generator_impl.cc +++ b/src/writer/hlsl/generator_impl.cc
@@ -74,6 +74,18 @@ out_ << std::endl; } + for (const auto& func : module_->functions()) { + if (!EmitFunction(func.get())) { + return false; + } + } + for (const auto& ep : module_->entry_points()) { + if (!EmitEntryPointFunction(ep.get())) { + return false; + } + out_ << std::endl; + } + return true; } @@ -487,6 +499,77 @@ return EmitBlock(stmt->body()); } +bool GeneratorImpl::EmitFunction(ast::Function* func) { + make_indent(); + + // Entry points will be emitted later, skip for now. + if (module_->IsFunctionEntryPoint(func->name())) { + return true; + } + + auto name = func->name(); + + if (!EmitType(func->return_type(), "")) { + return false; + } + + out_ << " " << namer_.NameFor(name) << "("; + + bool first = true; + for (const auto& v : func->params()) { + if (!first) { + out_ << ", "; + } + first = false; + + if (!EmitType(v->type(), v->name())) { + return false; + } + // Array name is output as part of the type + if (!v->type()->IsArray()) { + out_ << " " << v->name(); + } + } + + out_ << ") "; + + if (!EmitBlockAndNewline(func->body())) { + return false; + } + + return true; +} + +bool GeneratorImpl::EmitEntryPointFunction(ast::EntryPoint* ep) { + make_indent(); + + auto current_ep_name = ep->name(); + if (current_ep_name.empty()) { + current_ep_name = ep->function_name(); + } + + auto* func = module_->FindFunctionByName(ep->function_name()); + if (func == nullptr) { + error_ = "unable to find function for entry point: " + ep->function_name(); + return false; + } + + out_ << "void " << namer_.NameFor(current_ep_name) << "() {" << std::endl; + increment_indent(); + + for (const auto& s : *(func->body())) { + if (!EmitStatement(s.get())) { + return false; + } + } + + decrement_indent(); + make_indent(); + out_ << "}" << std::endl; + + return true; +} + bool GeneratorImpl::EmitLiteral(ast::Literal* lit) { if (lit->IsBool()) { out_ << (lit->AsBool()->IsTrue() ? "true" : "false");
diff --git a/src/writer/hlsl/generator_impl.h b/src/writer/hlsl/generator_impl.h index 6c305a1..c24f977 100644 --- a/src/writer/hlsl/generator_impl.h +++ b/src/writer/hlsl/generator_impl.h
@@ -106,6 +106,14 @@ /// @param expr the expression /// @returns true if the expression was emitted bool EmitExpression(ast::Expression* expr); + /// Handles generating a function + /// @param func the function to generate + /// @returns true if the function was emitted + bool EmitFunction(ast::Function* func); + /// Handles emitting the entry point function + /// @param ep the entry point + /// @returns true if the entry point function was emitted + bool EmitEntryPointFunction(ast::EntryPoint* ep); /// Handles an if statement /// @param stmt the statement to emit /// @returns true if the statement was successfully emitted
diff --git a/src/writer/hlsl/generator_impl_function_test.cc b/src/writer/hlsl/generator_impl_function_test.cc new file mode 100644 index 0000000..2216df5 --- /dev/null +++ b/src/writer/hlsl/generator_impl_function_test.cc
@@ -0,0 +1,1107 @@ +// 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/assignment_statement.h" +#include "src/ast/binary_expression.h" +#include "src/ast/binding_decoration.h" +#include "src/ast/call_expression.h" +#include "src/ast/decorated_variable.h" +#include "src/ast/float_literal.h" +#include "src/ast/function.h" +#include "src/ast/identifier_expression.h" +#include "src/ast/if_statement.h" +#include "src/ast/location_decoration.h" +#include "src/ast/member_accessor_expression.h" +#include "src/ast/module.h" +#include "src/ast/return_statement.h" +#include "src/ast/scalar_constructor_expression.h" +#include "src/ast/set_decoration.h" +#include "src/ast/sint_literal.h" +#include "src/ast/type/array_type.h" +#include "src/ast/type/f32_type.h" +#include "src/ast/type/i32_type.h" +#include "src/ast/type/vector_type.h" +#include "src/ast/type/void_type.h" +#include "src/ast/variable.h" +#include "src/ast/variable_decl_statement.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; + +TEST_F(HlslGeneratorImplTest, Emit_Function) { + ast::type::VoidType void_type; + + auto func = std::make_unique<ast::Function>("my_func", ast::VariableList{}, + &void_type); + + auto body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::ReturnStatement>()); + func->set_body(std::move(body)); + + ast::Module m; + m.AddFunction(std::move(func)); + + GeneratorImpl g(&m); + g.increment_indent(); + + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"( void my_func() { + return; + } +)"); +} + +TEST_F(HlslGeneratorImplTest, Emit_Function_Name_Collision) { + ast::type::VoidType void_type; + + auto func = std::make_unique<ast::Function>("GeometryShader", + ast::VariableList{}, &void_type); + + auto body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::ReturnStatement>()); + func->set_body(std::move(body)); + + ast::Module m; + m.AddFunction(std::move(func)); + + GeneratorImpl g(&m); + g.increment_indent(); + + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"( void GeometryShader_tint_0() { + return; + } +)"); +} + +TEST_F(HlslGeneratorImplTest, Emit_Function_WithParams) { + ast::type::F32Type f32; + ast::type::I32Type i32; + + ast::VariableList params; + params.push_back( + std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &f32)); + params.push_back( + std::make_unique<ast::Variable>("b", ast::StorageClass::kNone, &i32)); + + ast::type::VoidType void_type; + auto func = + std::make_unique<ast::Function>("my_func", std::move(params), &void_type); + + auto body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::ReturnStatement>()); + func->set_body(std::move(body)); + + ast::Module m; + m.AddFunction(std::move(func)); + + GeneratorImpl g(&m); + g.increment_indent(); + + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"( void my_func(float a, int b) { + return; + } +)"); +} + +TEST_F(HlslGeneratorImplTest, Emit_Function_EntryPoint_NoName) { + ast::type::VoidType void_type; + + auto func = std::make_unique<ast::Function>("frag_main", ast::VariableList{}, + &void_type); + auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "", + "frag_main"); + + ast::Module m; + m.AddFunction(std::move(func)); + m.AddEntryPoint(std::move(ep)); + + GeneratorImpl g(&m); + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"(void frag_main() { +} + +)"); +} + +TEST_F(HlslGeneratorImplTest, DISABLED_Emit_Function_EntryPoint_WithInOutVars) { + ast::type::VoidType void_type; + ast::type::F32Type f32; + + auto foo_var = std::make_unique<ast::DecoratedVariable>( + std::make_unique<ast::Variable>("foo", ast::StorageClass::kInput, &f32)); + + ast::VariableDecorationList decos; + decos.push_back(std::make_unique<ast::LocationDecoration>(0)); + foo_var->set_decorations(std::move(decos)); + + auto bar_var = std::make_unique<ast::DecoratedVariable>( + std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &f32)); + decos.push_back(std::make_unique<ast::LocationDecoration>(1)); + bar_var->set_decorations(std::move(decos)); + + Context ctx; + ast::Module mod; + TypeDeterminer td(&ctx, &mod); + td.RegisterVariableForTesting(foo_var.get()); + td.RegisterVariableForTesting(bar_var.get()); + + mod.AddGlobalVariable(std::move(foo_var)); + mod.AddGlobalVariable(std::move(bar_var)); + + ast::VariableList params; + auto func = std::make_unique<ast::Function>("frag_main", std::move(params), + &void_type); + + auto body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::AssignmentStatement>( + std::make_unique<ast::IdentifierExpression>("bar"), + std::make_unique<ast::IdentifierExpression>("foo"))); + body->append(std::make_unique<ast::ReturnStatement>()); + func->set_body(std::move(body)); + + mod.AddFunction(std::move(func)); + + auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "", + "frag_main"); + mod.AddEntryPoint(std::move(ep)); + + ASSERT_TRUE(td.Determine()) << td.error(); + + GeneratorImpl g(&mod); + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"(struct frag_main_in { + float foo : TEXCOORD0; +}; + +struct frag_main_out { + float bar : SV_Target1; +}; + +frag_main_out frag_main(frag_main_in tint_in) { + frag_main_out tint_out; + tint_out.bar = tint_in.foo; + return tint_out; +} + +)"); +} + +TEST_F(HlslGeneratorImplTest, + DISABLED_Emit_Function_EntryPoint_WithInOut_Builtins) { + ast::type::VoidType void_type; + ast::type::F32Type f32; + ast::type::VectorType vec4(&f32, 4); + + auto coord_var = + std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>( + "coord", ast::StorageClass::kInput, &vec4)); + + ast::VariableDecorationList decos; + decos.push_back( + std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragCoord)); + coord_var->set_decorations(std::move(decos)); + + auto depth_var = + std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>( + "depth", ast::StorageClass::kOutput, &f32)); + decos.push_back( + std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragDepth)); + depth_var->set_decorations(std::move(decos)); + + Context ctx; + ast::Module mod; + TypeDeterminer td(&ctx, &mod); + td.RegisterVariableForTesting(coord_var.get()); + td.RegisterVariableForTesting(depth_var.get()); + + mod.AddGlobalVariable(std::move(coord_var)); + mod.AddGlobalVariable(std::move(depth_var)); + + ast::VariableList params; + auto func = std::make_unique<ast::Function>("frag_main", std::move(params), + &void_type); + + auto body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::AssignmentStatement>( + std::make_unique<ast::IdentifierExpression>("depth"), + std::make_unique<ast::MemberAccessorExpression>( + std::make_unique<ast::IdentifierExpression>("coord"), + std::make_unique<ast::IdentifierExpression>("x")))); + body->append(std::make_unique<ast::ReturnStatement>()); + func->set_body(std::move(body)); + + mod.AddFunction(std::move(func)); + + auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "", + "frag_main"); + mod.AddEntryPoint(std::move(ep)); + + ASSERT_TRUE(td.Determine()) << td.error(); + + GeneratorImpl g(&mod); + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"(struct frag_main_in { + float gl_FragCoord : SV_Position; +}; + +struct frag_main_out { + float depth : SV_Depth; +}; + +frag_main_out frag_main(frag_main_in tint_in) { + frag_main_out tint_out; + tint_out.depth = tint_in.gl_FragCoord.x; + return tint_out; +} + +)"); +} + +TEST_F(HlslGeneratorImplTest, DISABLED_Emit_Function_EntryPoint_With_Uniform) { + ast::type::VoidType void_type; + ast::type::F32Type f32; + ast::type::VectorType vec4(&f32, 4); + + auto coord_var = + std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>( + "coord", ast::StorageClass::kUniform, &vec4)); + + ast::VariableDecorationList decos; + decos.push_back(std::make_unique<ast::BindingDecoration>(0)); + decos.push_back(std::make_unique<ast::SetDecoration>(1)); + coord_var->set_decorations(std::move(decos)); + + Context ctx; + ast::Module mod; + TypeDeterminer td(&ctx, &mod); + td.RegisterVariableForTesting(coord_var.get()); + + mod.AddGlobalVariable(std::move(coord_var)); + + ast::VariableList params; + auto func = std::make_unique<ast::Function>("frag_main", std::move(params), + &void_type); + + auto var = + std::make_unique<ast::Variable>("v", ast::StorageClass::kFunction, &f32); + var->set_constructor(std::make_unique<ast::MemberAccessorExpression>( + std::make_unique<ast::IdentifierExpression>("coord"), + std::make_unique<ast::IdentifierExpression>("x"))); + + auto body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var))); + body->append(std::make_unique<ast::ReturnStatement>()); + func->set_body(std::move(body)); + + mod.AddFunction(std::move(func)); + + auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "", + "frag_main"); + mod.AddEntryPoint(std::move(ep)); + + ASSERT_TRUE(td.Determine()) << td.error(); + + GeneratorImpl g(&mod); + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"( ... )"); +} + +TEST_F(HlslGeneratorImplTest, + DISABLED_Emit_Function_EntryPoint_With_StorageBuffer) { + ast::type::VoidType void_type; + ast::type::F32Type f32; + ast::type::VectorType vec4(&f32, 4); + + auto coord_var = + std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>( + "coord", ast::StorageClass::kStorageBuffer, &vec4)); + + ast::VariableDecorationList decos; + decos.push_back(std::make_unique<ast::BindingDecoration>(0)); + decos.push_back(std::make_unique<ast::SetDecoration>(1)); + coord_var->set_decorations(std::move(decos)); + + Context ctx; + ast::Module mod; + TypeDeterminer td(&ctx, &mod); + td.RegisterVariableForTesting(coord_var.get()); + + mod.AddGlobalVariable(std::move(coord_var)); + + ast::VariableList params; + auto func = std::make_unique<ast::Function>("frag_main", std::move(params), + &void_type); + + auto var = + std::make_unique<ast::Variable>("v", ast::StorageClass::kFunction, &f32); + var->set_constructor(std::make_unique<ast::MemberAccessorExpression>( + std::make_unique<ast::IdentifierExpression>("coord"), + std::make_unique<ast::IdentifierExpression>("x"))); + + auto body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var))); + body->append(std::make_unique<ast::ReturnStatement>()); + func->set_body(std::move(body)); + + mod.AddFunction(std::move(func)); + + auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "", + "frag_main"); + mod.AddEntryPoint(std::move(ep)); + + ASSERT_TRUE(td.Determine()) << td.error(); + + GeneratorImpl g(&mod); + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"( ... )"); +} + +TEST_F( + HlslGeneratorImplTest, + DISABLED_Emit_Function_Called_By_EntryPoints_WithLocationGlobals_And_Params) { + ast::type::VoidType void_type; + ast::type::F32Type f32; + + auto foo_var = std::make_unique<ast::DecoratedVariable>( + std::make_unique<ast::Variable>("foo", ast::StorageClass::kInput, &f32)); + + ast::VariableDecorationList decos; + decos.push_back(std::make_unique<ast::LocationDecoration>(0)); + foo_var->set_decorations(std::move(decos)); + + auto bar_var = std::make_unique<ast::DecoratedVariable>( + std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &f32)); + decos.push_back(std::make_unique<ast::LocationDecoration>(1)); + bar_var->set_decorations(std::move(decos)); + + auto val_var = std::make_unique<ast::DecoratedVariable>( + std::make_unique<ast::Variable>("val", ast::StorageClass::kOutput, &f32)); + decos.push_back(std::make_unique<ast::LocationDecoration>(0)); + val_var->set_decorations(std::move(decos)); + + Context ctx; + ast::Module mod; + TypeDeterminer td(&ctx, &mod); + td.RegisterVariableForTesting(foo_var.get()); + td.RegisterVariableForTesting(bar_var.get()); + td.RegisterVariableForTesting(val_var.get()); + + mod.AddGlobalVariable(std::move(foo_var)); + mod.AddGlobalVariable(std::move(bar_var)); + mod.AddGlobalVariable(std::move(val_var)); + + ast::VariableList params; + params.push_back(std::make_unique<ast::Variable>( + "param", ast::StorageClass::kFunction, &f32)); + auto sub_func = + std::make_unique<ast::Function>("sub_func", std::move(params), &f32); + + auto body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::AssignmentStatement>( + std::make_unique<ast::IdentifierExpression>("bar"), + std::make_unique<ast::IdentifierExpression>("foo"))); + body->append(std::make_unique<ast::AssignmentStatement>( + std::make_unique<ast::IdentifierExpression>("val"), + std::make_unique<ast::IdentifierExpression>("param"))); + body->append(std::make_unique<ast::ReturnStatement>( + std::make_unique<ast::IdentifierExpression>("foo"))); + sub_func->set_body(std::move(body)); + + mod.AddFunction(std::move(sub_func)); + + auto func_1 = std::make_unique<ast::Function>("frag_1_main", + std::move(params), &void_type); + + ast::ExpressionList expr; + expr.push_back(std::make_unique<ast::ScalarConstructorExpression>( + std::make_unique<ast::FloatLiteral>(&f32, 1.0f))); + + body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::AssignmentStatement>( + std::make_unique<ast::IdentifierExpression>("bar"), + std::make_unique<ast::CallExpression>( + std::make_unique<ast::IdentifierExpression>("sub_func"), + std::move(expr)))); + body->append(std::make_unique<ast::ReturnStatement>()); + func_1->set_body(std::move(body)); + + mod.AddFunction(std::move(func_1)); + + auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, + "ep_1", "frag_1_main"); + mod.AddEntryPoint(std::move(ep1)); + + ASSERT_TRUE(td.Determine()) << td.error(); + + GeneratorImpl g(&mod); + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"(struct ep_1_in { + float foo : TEXCOORD0; +}; + +struct ep_1_out { + float bar : SV_Target1; + float val : SV_Target0; +}; + +float sub_func_ep_1(in ep_1_in tint_in, out ep_1_out tint_out, float param) { + tint_out.bar = tint_in.foo; + tint_out.val = param; + return tint_in.foo; +} + +ep_1_out ep_1(ep_1_in tint_in) { + ep_1_out tint_out; + tint_out.bar = sub_func_ep_1(tint_in, tint_out, 1.00000000f); + return tint_out; +} + +)"); +} + +TEST_F(HlslGeneratorImplTest, + DISABLED_Emit_Function_Called_By_EntryPoints_NoUsedGlobals) { + ast::type::VoidType void_type; + ast::type::F32Type f32; + ast::type::VectorType vec4(&f32, 4); + + auto depth_var = + std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>( + "depth", ast::StorageClass::kOutput, &f32)); + + ast::VariableDecorationList decos; + decos.push_back( + std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragDepth)); + depth_var->set_decorations(std::move(decos)); + + Context ctx; + ast::Module mod; + TypeDeterminer td(&ctx, &mod); + td.RegisterVariableForTesting(depth_var.get()); + + mod.AddGlobalVariable(std::move(depth_var)); + + ast::VariableList params; + params.push_back(std::make_unique<ast::Variable>( + "param", ast::StorageClass::kFunction, &f32)); + auto sub_func = + std::make_unique<ast::Function>("sub_func", std::move(params), &f32); + + auto body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::ReturnStatement>( + std::make_unique<ast::IdentifierExpression>("param"))); + sub_func->set_body(std::move(body)); + + mod.AddFunction(std::move(sub_func)); + + auto func_1 = std::make_unique<ast::Function>("frag_1_main", + std::move(params), &void_type); + + ast::ExpressionList expr; + expr.push_back(std::make_unique<ast::ScalarConstructorExpression>( + std::make_unique<ast::FloatLiteral>(&f32, 1.0f))); + + body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::AssignmentStatement>( + std::make_unique<ast::IdentifierExpression>("depth"), + std::make_unique<ast::CallExpression>( + std::make_unique<ast::IdentifierExpression>("sub_func"), + std::move(expr)))); + body->append(std::make_unique<ast::ReturnStatement>()); + func_1->set_body(std::move(body)); + + mod.AddFunction(std::move(func_1)); + + auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, + "ep_1", "frag_1_main"); + mod.AddEntryPoint(std::move(ep1)); + + ASSERT_TRUE(td.Determine()) << td.error(); + + GeneratorImpl g(&mod); + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"(struct ep_1_out { + float depth : SV_Depth; +}; + +float sub_func(float param) { + return param; +} + +fragment ep_1_out ep_1() { + ep_1_out tint_out; + tint_out.depth = sub_func(1.00000000f); + return tint_out; +} + +)"); +} + +TEST_F( + HlslGeneratorImplTest, + DISABLED_Emit_Function_Called_By_EntryPoints_WithBuiltinGlobals_And_Params) { + ast::type::VoidType void_type; + ast::type::F32Type f32; + ast::type::VectorType vec4(&f32, 4); + + auto coord_var = + std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>( + "coord", ast::StorageClass::kInput, &vec4)); + + ast::VariableDecorationList decos; + decos.push_back( + std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragCoord)); + coord_var->set_decorations(std::move(decos)); + + auto depth_var = + std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>( + "depth", ast::StorageClass::kOutput, &f32)); + decos.push_back( + std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragDepth)); + depth_var->set_decorations(std::move(decos)); + + Context ctx; + ast::Module mod; + TypeDeterminer td(&ctx, &mod); + td.RegisterVariableForTesting(coord_var.get()); + td.RegisterVariableForTesting(depth_var.get()); + + mod.AddGlobalVariable(std::move(coord_var)); + mod.AddGlobalVariable(std::move(depth_var)); + + ast::VariableList params; + params.push_back(std::make_unique<ast::Variable>( + "param", ast::StorageClass::kFunction, &f32)); + auto sub_func = + std::make_unique<ast::Function>("sub_func", std::move(params), &f32); + + auto body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::AssignmentStatement>( + std::make_unique<ast::IdentifierExpression>("depth"), + std::make_unique<ast::MemberAccessorExpression>( + std::make_unique<ast::IdentifierExpression>("coord"), + std::make_unique<ast::IdentifierExpression>("x")))); + body->append(std::make_unique<ast::ReturnStatement>( + std::make_unique<ast::IdentifierExpression>("param"))); + sub_func->set_body(std::move(body)); + + mod.AddFunction(std::move(sub_func)); + + auto func_1 = std::make_unique<ast::Function>("frag_1_main", + std::move(params), &void_type); + + ast::ExpressionList expr; + expr.push_back(std::make_unique<ast::ScalarConstructorExpression>( + std::make_unique<ast::FloatLiteral>(&f32, 1.0f))); + + body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::AssignmentStatement>( + std::make_unique<ast::IdentifierExpression>("depth"), + std::make_unique<ast::CallExpression>( + std::make_unique<ast::IdentifierExpression>("sub_func"), + std::move(expr)))); + body->append(std::make_unique<ast::ReturnStatement>()); + func_1->set_body(std::move(body)); + + mod.AddFunction(std::move(func_1)); + + auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, + "ep_1", "frag_1_main"); + mod.AddEntryPoint(std::move(ep1)); + + ASSERT_TRUE(td.Determine()) << td.error(); + + GeneratorImpl g(&mod); + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"(struct ep_1_in { + float4 coord : SV_Position; +}; + +struct ep_1_out { + float depth : SV_Depth; +}; + +float sub_func_ep_1(in ep_1_in tint_in, out ep_1_out tint_out, float param) { + tint_out.depth = tint_in.coord.x; + return param; +} + +ep_1_out ep_1(ep_1_in tint_in) { + ep_1_out tint_out; + tint_out.depth = sub_func_ep_1(tint_in, tint_out, 1.00000000f); + return tint_out; +} + +)"); +} + +TEST_F(HlslGeneratorImplTest, + DISABLED_Emit_Function_Called_By_EntryPoint_With_Uniform) { + ast::type::VoidType void_type; + ast::type::F32Type f32; + ast::type::VectorType vec4(&f32, 4); + + auto coord_var = + std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>( + "coord", ast::StorageClass::kUniform, &vec4)); + + ast::VariableDecorationList decos; + decos.push_back(std::make_unique<ast::BindingDecoration>(0)); + decos.push_back(std::make_unique<ast::SetDecoration>(1)); + coord_var->set_decorations(std::move(decos)); + + Context ctx; + ast::Module mod; + TypeDeterminer td(&ctx, &mod); + td.RegisterVariableForTesting(coord_var.get()); + + mod.AddGlobalVariable(std::move(coord_var)); + + ast::VariableList params; + params.push_back(std::make_unique<ast::Variable>( + "param", ast::StorageClass::kFunction, &f32)); + auto sub_func = + std::make_unique<ast::Function>("sub_func", std::move(params), &f32); + + auto body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::ReturnStatement>( + std::make_unique<ast::MemberAccessorExpression>( + std::make_unique<ast::IdentifierExpression>("coord"), + std::make_unique<ast::IdentifierExpression>("x")))); + sub_func->set_body(std::move(body)); + + mod.AddFunction(std::move(sub_func)); + + auto func = std::make_unique<ast::Function>("frag_main", std::move(params), + &void_type); + + ast::ExpressionList expr; + expr.push_back(std::make_unique<ast::ScalarConstructorExpression>( + std::make_unique<ast::FloatLiteral>(&f32, 1.0f))); + + auto var = + std::make_unique<ast::Variable>("v", ast::StorageClass::kFunction, &f32); + var->set_constructor(std::make_unique<ast::CallExpression>( + std::make_unique<ast::IdentifierExpression>("sub_func"), + std::move(expr))); + + body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var))); + body->append(std::make_unique<ast::ReturnStatement>()); + func->set_body(std::move(body)); + + mod.AddFunction(std::move(func)); + + auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "", + "frag_main"); + mod.AddEntryPoint(std::move(ep)); + + ASSERT_TRUE(td.Determine()) << td.error(); + + GeneratorImpl g(&mod); + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"( ... )"); +} + +TEST_F(HlslGeneratorImplTest, + DISABLED_Emit_Function_Called_By_EntryPoint_With_StorageBuffer) { + ast::type::VoidType void_type; + ast::type::F32Type f32; + ast::type::VectorType vec4(&f32, 4); + + auto coord_var = + std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>( + "coord", ast::StorageClass::kStorageBuffer, &vec4)); + + ast::VariableDecorationList decos; + decos.push_back(std::make_unique<ast::BindingDecoration>(0)); + decos.push_back(std::make_unique<ast::SetDecoration>(1)); + coord_var->set_decorations(std::move(decos)); + + Context ctx; + ast::Module mod; + TypeDeterminer td(&ctx, &mod); + td.RegisterVariableForTesting(coord_var.get()); + + mod.AddGlobalVariable(std::move(coord_var)); + + ast::VariableList params; + params.push_back(std::make_unique<ast::Variable>( + "param", ast::StorageClass::kFunction, &f32)); + auto sub_func = + std::make_unique<ast::Function>("sub_func", std::move(params), &f32); + + auto body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::ReturnStatement>( + std::make_unique<ast::MemberAccessorExpression>( + std::make_unique<ast::IdentifierExpression>("coord"), + std::make_unique<ast::IdentifierExpression>("x")))); + sub_func->set_body(std::move(body)); + + mod.AddFunction(std::move(sub_func)); + + auto func = std::make_unique<ast::Function>("frag_main", std::move(params), + &void_type); + + ast::ExpressionList expr; + expr.push_back(std::make_unique<ast::ScalarConstructorExpression>( + std::make_unique<ast::FloatLiteral>(&f32, 1.0f))); + + auto var = + std::make_unique<ast::Variable>("v", ast::StorageClass::kFunction, &f32); + var->set_constructor(std::make_unique<ast::CallExpression>( + std::make_unique<ast::IdentifierExpression>("sub_func"), + std::move(expr))); + + body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var))); + body->append(std::make_unique<ast::ReturnStatement>()); + func->set_body(std::move(body)); + + mod.AddFunction(std::move(func)); + + auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "", + "frag_main"); + mod.AddEntryPoint(std::move(ep)); + + ASSERT_TRUE(td.Determine()) << td.error(); + + GeneratorImpl g(&mod); + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"( ... )"); +} + +TEST_F(HlslGeneratorImplTest, + DISABLED_Emit_Function_Called_Two_EntryPoints_WithGlobals) { + ast::type::VoidType void_type; + ast::type::F32Type f32; + + auto foo_var = std::make_unique<ast::DecoratedVariable>( + std::make_unique<ast::Variable>("foo", ast::StorageClass::kInput, &f32)); + + ast::VariableDecorationList decos; + decos.push_back(std::make_unique<ast::LocationDecoration>(0)); + foo_var->set_decorations(std::move(decos)); + + auto bar_var = std::make_unique<ast::DecoratedVariable>( + std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &f32)); + decos.push_back(std::make_unique<ast::LocationDecoration>(1)); + bar_var->set_decorations(std::move(decos)); + + Context ctx; + ast::Module mod; + TypeDeterminer td(&ctx, &mod); + td.RegisterVariableForTesting(foo_var.get()); + td.RegisterVariableForTesting(bar_var.get()); + + mod.AddGlobalVariable(std::move(foo_var)); + mod.AddGlobalVariable(std::move(bar_var)); + + ast::VariableList params; + auto sub_func = + std::make_unique<ast::Function>("sub_func", std::move(params), &f32); + + auto body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::AssignmentStatement>( + std::make_unique<ast::IdentifierExpression>("bar"), + std::make_unique<ast::IdentifierExpression>("foo"))); + body->append(std::make_unique<ast::ReturnStatement>( + std::make_unique<ast::IdentifierExpression>("foo"))); + sub_func->set_body(std::move(body)); + + mod.AddFunction(std::move(sub_func)); + + auto func_1 = std::make_unique<ast::Function>("frag_1_main", + std::move(params), &void_type); + + body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::AssignmentStatement>( + std::make_unique<ast::IdentifierExpression>("bar"), + std::make_unique<ast::CallExpression>( + std::make_unique<ast::IdentifierExpression>("sub_func"), + ast::ExpressionList{}))); + body->append(std::make_unique<ast::ReturnStatement>()); + func_1->set_body(std::move(body)); + + mod.AddFunction(std::move(func_1)); + + auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, + "ep_1", "frag_1_main"); + auto ep2 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, + "ep_2", "frag_1_main"); + mod.AddEntryPoint(std::move(ep1)); + mod.AddEntryPoint(std::move(ep2)); + + ASSERT_TRUE(td.Determine()) << td.error(); + + GeneratorImpl g(&mod); + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"(struct ep_1_in { + float foo : TEXCOORD0; +}; + +struct ep_1_out { + float bar : SV_Target1; +}; + +struct ep_2_in { + float foo : TEXCOORD0; +}; + +struct ep_2_out { + float bar : SV_Target1; +}; + +float sub_func_ep_1(in ep_1_in tint_in, out ep_1_out tint_out) { + tint_out.bar = tint_in.foo; + return tint_in.foo; +} + +float sub_func_ep_2(in ep_2_in tint_in, out ep_2_out tint_out) { + tint_out.bar = tint_in.foo; + return tint_in.foo; +} + +ep_1_out ep_1(ep_1_in tint_in) { + ep_1_out tint_out; + tint_out.bar = sub_func_ep_1(tint_in, tint_out); + return tint_out; +} + +ep_2_out ep_2(ep_2_in tint_in) { + ep_2_out tint_out; + tint_out.bar = sub_func_ep_2(tint_in, tint_out); + return tint_out; +} + +)"); +} + +TEST_F(HlslGeneratorImplTest, + DISABLED_Emit_Function_EntryPoints_WithGlobal_Nested_Return) { + ast::type::VoidType void_type; + ast::type::F32Type f32; + ast::type::I32Type i32; + + auto bar_var = std::make_unique<ast::DecoratedVariable>( + std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &f32)); + ast::VariableDecorationList decos; + decos.push_back(std::make_unique<ast::LocationDecoration>(1)); + bar_var->set_decorations(std::move(decos)); + + Context ctx; + ast::Module mod; + TypeDeterminer td(&ctx, &mod); + td.RegisterVariableForTesting(bar_var.get()); + mod.AddGlobalVariable(std::move(bar_var)); + + ast::VariableList params; + auto func_1 = std::make_unique<ast::Function>("frag_1_main", + std::move(params), &void_type); + + auto body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::AssignmentStatement>( + std::make_unique<ast::IdentifierExpression>("bar"), + std::make_unique<ast::ScalarConstructorExpression>( + std::make_unique<ast::FloatLiteral>(&f32, 1.0f)))); + + auto list = std::make_unique<ast::BlockStatement>(); + list->append(std::make_unique<ast::ReturnStatement>()); + + body->append(std::make_unique<ast::IfStatement>( + std::make_unique<ast::BinaryExpression>( + ast::BinaryOp::kEqual, + std::make_unique<ast::ScalarConstructorExpression>( + std::make_unique<ast::SintLiteral>(&i32, 1)), + std::make_unique<ast::ScalarConstructorExpression>( + std::make_unique<ast::SintLiteral>(&i32, 1))), + std::move(list))); + + body->append(std::make_unique<ast::ReturnStatement>()); + func_1->set_body(std::move(body)); + + mod.AddFunction(std::move(func_1)); + + auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, + "ep_1", "frag_1_main"); + mod.AddEntryPoint(std::move(ep1)); + + ASSERT_TRUE(td.Determine()) << td.error(); + + GeneratorImpl g(&mod); + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"(struct ep_1_out { + float bar : SV_Target0; +}; + +ep_1_out ep_1() { + ep_1_out tint_out; + tint_out.bar = 1.00000000f; + if ((1 == 1)) { + return tint_out; + } + return tint_out; +} + +)"); +} + +// TODO(dsinclair): Requires CallExpression support +TEST_F(HlslGeneratorImplTest, + DISABLED_Emit_Function_Called_Two_EntryPoints_WithoutGlobals) { + ast::type::VoidType void_type; + ast::type::F32Type f32; + + Context ctx; + ast::Module mod; + TypeDeterminer td(&ctx, &mod); + + ast::VariableList params; + auto sub_func = + std::make_unique<ast::Function>("sub_func", std::move(params), &f32); + + auto body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::ReturnStatement>( + std::make_unique<ast::ScalarConstructorExpression>( + std::make_unique<ast::FloatLiteral>(&f32, 1.0)))); + sub_func->set_body(std::move(body)); + + mod.AddFunction(std::move(sub_func)); + + auto func_1 = std::make_unique<ast::Function>("frag_1_main", + std::move(params), &void_type); + + auto var = std::make_unique<ast::Variable>( + "foo", ast::StorageClass::kFunction, &f32); + var->set_constructor(std::make_unique<ast::CallExpression>( + std::make_unique<ast::IdentifierExpression>("sub_func"), + ast::ExpressionList{})); + + body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var))); + body->append(std::make_unique<ast::ReturnStatement>()); + func_1->set_body(std::move(body)); + + mod.AddFunction(std::move(func_1)); + + auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, + "ep_1", "frag_1_main"); + auto ep2 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, + "ep_2", "frag_1_main"); + mod.AddEntryPoint(std::move(ep1)); + mod.AddEntryPoint(std::move(ep2)); + + ASSERT_TRUE(td.Determine()) << td.error(); + + GeneratorImpl g(&mod); + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"(float sub_func() { + return 1.00000000f; +} + +fragment void ep_1() { + float foo = sub_func(); + return; +} + +fragment void ep_2() { + float foo = sub_func(); + return; +} + +)"); +} +TEST_F(HlslGeneratorImplTest, Emit_Function_EntryPoint_WithName) { + ast::type::VoidType void_type; + + auto func = std::make_unique<ast::Function>("comp_main", ast::VariableList{}, + &void_type); + auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kCompute, + "my_main", "comp_main"); + + ast::Module m; + m.AddFunction(std::move(func)); + m.AddEntryPoint(std::move(ep)); + + GeneratorImpl g(&m); + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"(void my_main() { +} + +)"); +} + +TEST_F(HlslGeneratorImplTest, Emit_Function_EntryPoint_WithNameCollision) { + ast::type::VoidType void_type; + + auto func = std::make_unique<ast::Function>("comp_main", ast::VariableList{}, + &void_type); + auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kCompute, + "GeometryShader", "comp_main"); + + ast::Module m; + m.AddFunction(std::move(func)); + m.AddEntryPoint(std::move(ep)); + + GeneratorImpl g(&m); + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"(void GeometryShader_tint_0() { +} + +)"); +} + +TEST_F(HlslGeneratorImplTest, Emit_Function_WithArrayParams) { + ast::type::F32Type f32; + ast::type::ArrayType ary(&f32, 5); + + ast::VariableList params; + params.push_back( + std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &ary)); + + ast::type::VoidType void_type; + auto func = + std::make_unique<ast::Function>("my_func", std::move(params), &void_type); + + auto body = std::make_unique<ast::BlockStatement>(); + body->append(std::make_unique<ast::ReturnStatement>()); + func->set_body(std::move(body)); + + ast::Module m; + m.AddFunction(std::move(func)); + + GeneratorImpl g(&m); + g.increment_indent(); + + ASSERT_TRUE(g.Generate()) << g.error(); + EXPECT_EQ(g.result(), R"( void my_func(float a[5]) { + return; + } +)"); +} + +} // namespace +} // namespace hlsl +} // namespace writer +} // namespace tint