writer/[hlsl,msl]: Emit a implicit return for entrypoints

WGSL no longer requires a return statement at the end of each function, and the generated entrypoint functions are not valid without a return.

Fixed: tint:446
Change-Id: I702e4217f4ac41013e30927d532895c6835f6ca9
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/37704
Auto-Submit: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ben Clayton 2021-01-13 16:34:51 +00:00 committed by Commit Bot service account
parent 1870a48d5f
commit 7c5980991a
5 changed files with 155 additions and 0 deletions

View File

@ -1576,6 +1576,14 @@ bool GeneratorImpl::EmitEntryPointFunction(std::ostream& out,
return false;
}
}
auto* last_statement = func->get_last_statement();
if (last_statement == nullptr ||
!last_statement->Is<ast::ReturnStatement>()) {
ast::ReturnStatement ret(Source{});
if (!EmitStatement(out, &ret)) {
return false;
}
}
generating_entry_point_ = false;
decrement_indent();

View File

@ -110,6 +110,74 @@ TEST_F(HlslGeneratorImplTest_Function, Emit_Function_WithParams) {
)");
}
TEST_F(HlslGeneratorImplTest_Function,
Emit_FunctionDecoration_EntryPoint_NoReturn_Void) {
auto* func =
Func("main", ast::VariableList{}, ty.void_,
ast::StatementList{/* no explicit return */},
ast::FunctionDecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kFragment),
});
mod->AddFunction(func);
ASSERT_TRUE(td.Determine()) << td.error();
ASSERT_TRUE(gen.Generate(out)) << gen.error();
EXPECT_EQ(result(), R"(void main() {
return;
}
)");
}
TEST_F(HlslGeneratorImplTest_Function,
Emit_FunctionDecoration_EntryPoint_NoReturn_InOut) {
auto* foo_var = Var("foo", ast::StorageClass::kInput, ty.f32, nullptr,
ast::VariableDecorationList{
create<ast::LocationDecoration>(0),
});
auto* bar_var = Var("bar", ast::StorageClass::kOutput, ty.f32, nullptr,
ast::VariableDecorationList{
create<ast::LocationDecoration>(1),
});
td.RegisterVariableForTesting(foo_var);
td.RegisterVariableForTesting(bar_var);
mod->AddGlobalVariable(foo_var);
mod->AddGlobalVariable(bar_var);
auto* func =
Func("main", ast::VariableList{}, ty.void_,
ast::StatementList{
create<ast::AssignmentStatement>(Expr("bar"), Expr("foo")),
/* no explicit return */},
ast::FunctionDecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kFragment),
});
mod->AddFunction(func);
ASSERT_TRUE(td.Determine()) << td.error();
ASSERT_TRUE(gen.Generate(out)) << gen.error();
EXPECT_EQ(result(), R"(struct main_in {
float foo : TEXCOORD0;
};
struct main_out {
float bar : SV_Target1;
};
main_out main(main_in tint_in) {
main_out tint_out;
tint_out.bar = tint_in.foo;
return tint_out;
}
)");
}
TEST_F(HlslGeneratorImplTest_Function,
Emit_FunctionDecoration_EntryPoint_WithInOutVars) {
auto* foo_var = Var("foo", ast::StorageClass::kInput, ty.f32, nullptr,
@ -821,6 +889,7 @@ TEST_F(HlslGeneratorImplTest_Function,
ASSERT_TRUE(gen.Generate(out)) << gen.error();
EXPECT_EQ(result(), R"(void GeometryShader_tint_0() {
return;
}
)");

View File

@ -1515,6 +1515,14 @@ bool GeneratorImpl::EmitEntryPointFunction(ast::Function* func) {
return false;
}
}
auto* last_statement = func->get_last_statement();
if (last_statement == nullptr ||
!last_statement->Is<ast::ReturnStatement>()) {
ast::ReturnStatement ret(Source{});
if (!EmitStatement(&ret)) {
return false;
}
}
generating_entry_point_ = false;
decrement_indent();

View File

@ -119,6 +119,74 @@ TEST_F(MslGeneratorImplTest, Emit_Function_WithParams) {
)");
}
TEST_F(MslGeneratorImplTest, Emit_FunctionDecoration_EntryPoint_NoReturn_Void) {
auto* func = Func("main", ast::VariableList{}, ty.void_,
ast::StatementList{/* no explicit return */},
ast::FunctionDecorationList{create<ast::StageDecoration>(
ast::PipelineStage::kFragment)});
mod->AddFunction(func);
ASSERT_TRUE(td.Determine()) << td.error();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
fragment void main_tint_0() {
return;
}
)");
}
TEST_F(MslGeneratorImplTest,
Emit_FunctionDecoration_EntryPoint_NoReturn_InOut) {
auto* foo_var =
Var("foo", ast::StorageClass::kInput, ty.f32, nullptr,
ast::VariableDecorationList{create<ast::LocationDecoration>(0)});
auto* bar_var =
Var("bar", ast::StorageClass::kOutput, ty.f32, nullptr,
ast::VariableDecorationList{create<ast::LocationDecoration>(1)});
td.RegisterVariableForTesting(foo_var);
td.RegisterVariableForTesting(bar_var);
mod->AddGlobalVariable(foo_var);
mod->AddGlobalVariable(bar_var);
auto* func =
Func("main", ast::VariableList{}, ty.void_,
ast::StatementList{
create<ast::AssignmentStatement>(Expr("bar"), Expr("foo")),
/* no explicit return */},
ast::FunctionDecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
mod->AddFunction(func);
ASSERT_TRUE(td.Determine()) << td.error();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
struct main_in {
float foo [[user(locn0)]];
};
struct main_out {
float bar [[color(1)]];
};
fragment main_out main_tint_0(main_in tint_in [[stage_in]]) {
main_out tint_out = {};
tint_out.bar = tint_in.foo;
return tint_out;
}
)");
}
TEST_F(MslGeneratorImplTest, Emit_FunctionDecoration_EntryPoint_WithInOutVars) {
auto* foo_var =
Var("foo", ast::StorageClass::kInput, ty.f32, nullptr,
@ -811,6 +879,7 @@ TEST_F(MslGeneratorImplTest,
EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
kernel void main_tint_0() {
return;
}
)");

View File

@ -59,6 +59,7 @@ TEST_F(MslGeneratorImplTest, Generate) {
EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
kernel void my_func() {
return;
}
)");