[validator] Remove requirement to have an entry point
The SPIR-V and HLSL sanitizing transforms add an empty one if necessary. Fixed: tint:679 Change-Id: Ic98ff3109d7381b1fbc2de68d95d57e15c7a67c0 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/46700 Commit-Queue: Ben Clayton <bclayton@google.com> Auto-Submit: James Price <jrprice@google.com> Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
1fe1a5e641
commit
2f04dc94ce
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "src/ast/stage_decoration.h"
|
||||||
#include "src/ast/variable_decl_statement.h"
|
#include "src/ast/variable_decl_statement.h"
|
||||||
#include "src/program_builder.h"
|
#include "src/program_builder.h"
|
||||||
#include "src/semantic/expression.h"
|
#include "src/semantic/expression.h"
|
||||||
|
@ -32,6 +33,7 @@ Transform::Output Hlsl::Run(const Program* in, const DataMap&) {
|
||||||
ProgramBuilder out;
|
ProgramBuilder out;
|
||||||
CloneContext ctx(&out, in);
|
CloneContext ctx(&out, in);
|
||||||
PromoteArrayInitializerToConstVar(ctx);
|
PromoteArrayInitializerToConstVar(ctx);
|
||||||
|
AddEmptyEntryPoint(ctx);
|
||||||
ctx.Clone();
|
ctx.Clone();
|
||||||
return Output{Program(std::move(out))};
|
return Output{Program(std::move(out))};
|
||||||
}
|
}
|
||||||
|
@ -105,5 +107,16 @@ void Hlsl::PromoteArrayInitializerToConstVar(CloneContext& ctx) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Hlsl::AddEmptyEntryPoint(CloneContext& ctx) const {
|
||||||
|
for (auto* func : ctx.src->AST().Functions()) {
|
||||||
|
if (func->IsEntryPoint()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.dst->Func(
|
||||||
|
"_tint_unused_entry_point", {}, ctx.dst->ty.void_(), {},
|
||||||
|
{ctx.dst->create<ast::StageDecoration>(ast::PipelineStage::kVertex)});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace transform
|
} // namespace transform
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
|
|
@ -44,6 +44,8 @@ class Hlsl : public Transform {
|
||||||
/// the array usage statement.
|
/// the array usage statement.
|
||||||
/// See crbug.com/tint/406 for more details
|
/// See crbug.com/tint/406 for more details
|
||||||
void PromoteArrayInitializerToConstVar(CloneContext& ctx) const;
|
void PromoteArrayInitializerToConstVar(CloneContext& ctx) const;
|
||||||
|
/// Add an empty shader entry point if none exist in the module.
|
||||||
|
void AddEmptyEntryPoint(CloneContext& ctx) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace transform
|
} // namespace transform
|
||||||
|
|
|
@ -143,6 +143,20 @@ fn main() -> void {
|
||||||
EXPECT_EQ(expect, str(got));
|
EXPECT_EQ(expect, str(got));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(HlslTest, AddEmptyEntryPoint) {
|
||||||
|
auto* src = R"()";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
[[stage(vertex)]]
|
||||||
|
fn _tint_unused_entry_point() -> void {
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<Hlsl>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace transform
|
} // namespace transform
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include "src/ast/call_statement.h"
|
#include "src/ast/call_statement.h"
|
||||||
#include "src/ast/return_statement.h"
|
#include "src/ast/return_statement.h"
|
||||||
|
#include "src/ast/stage_decoration.h"
|
||||||
#include "src/program_builder.h"
|
#include "src/program_builder.h"
|
||||||
#include "src/semantic/function.h"
|
#include "src/semantic/function.h"
|
||||||
#include "src/semantic/statement.h"
|
#include "src/semantic/statement.h"
|
||||||
|
@ -42,6 +43,7 @@ Transform::Output Spirv::Run(const Program* in, const DataMap&) {
|
||||||
ProgramBuilder out2;
|
ProgramBuilder out2;
|
||||||
CloneContext ctx2(&out2, &tmp);
|
CloneContext ctx2(&out2, &tmp);
|
||||||
HandleSampleMaskBuiltins(ctx2);
|
HandleSampleMaskBuiltins(ctx2);
|
||||||
|
AddEmptyEntryPoint(ctx2);
|
||||||
ctx2.Clone();
|
ctx2.Clone();
|
||||||
|
|
||||||
return Output{Program(std::move(out2))};
|
return Output{Program(std::move(out2))};
|
||||||
|
@ -234,6 +236,17 @@ void Spirv::HandleSampleMaskBuiltins(CloneContext& ctx) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Spirv::AddEmptyEntryPoint(CloneContext& ctx) const {
|
||||||
|
for (auto* func : ctx.src->AST().Functions()) {
|
||||||
|
if (func->IsEntryPoint()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.dst->Func(
|
||||||
|
"_tint_unused_entry_point", {}, ctx.dst->ty.void_(), {},
|
||||||
|
{ctx.dst->create<ast::StageDecoration>(ast::PipelineStage::kCompute)});
|
||||||
|
}
|
||||||
|
|
||||||
Symbol Spirv::HoistToInputVariables(
|
Symbol Spirv::HoistToInputVariables(
|
||||||
CloneContext& ctx,
|
CloneContext& ctx,
|
||||||
const ast::Function* func,
|
const ast::Function* func,
|
||||||
|
|
|
@ -47,6 +47,8 @@ class Spirv : public Transform {
|
||||||
void HandleEntryPointIOTypes(CloneContext& ctx) const;
|
void HandleEntryPointIOTypes(CloneContext& ctx) const;
|
||||||
/// Change type of sample mask builtin variables to single element arrays.
|
/// Change type of sample mask builtin variables to single element arrays.
|
||||||
void HandleSampleMaskBuiltins(CloneContext& ctx) const;
|
void HandleSampleMaskBuiltins(CloneContext& ctx) const;
|
||||||
|
/// Add an empty shader entry point if none exist in the module.
|
||||||
|
void AddEmptyEntryPoint(CloneContext& ctx) const;
|
||||||
|
|
||||||
/// Recursively create module-scope input variables for `ty` and add
|
/// Recursively create module-scope input variables for `ty` and add
|
||||||
/// function-scope variables for structs to `func`.
|
/// function-scope variables for structs to `func`.
|
||||||
|
|
|
@ -833,6 +833,20 @@ fn main() -> void {
|
||||||
EXPECT_EQ(expect, str(got));
|
EXPECT_EQ(expect, str(got));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SpirvTest, AddEmptyEntryPoint) {
|
||||||
|
auto* src = R"()";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
[[stage(compute)]]
|
||||||
|
fn _tint_unused_entry_point() -> void {
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<Spirv>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
// Test that different transforms within the sanitizer interact correctly.
|
// Test that different transforms within the sanitizer interact correctly.
|
||||||
TEST_F(SpirvTest, MultipleTransforms) {
|
TEST_F(SpirvTest, MultipleTransforms) {
|
||||||
auto* src = R"(
|
auto* src = R"(
|
||||||
|
|
|
@ -78,25 +78,7 @@ TEST_F(ValidateFunctionTest, PipelineStage_MustBeUnique_Fail) {
|
||||||
"12:34 v-0020: only one stage decoration permitted per entry point");
|
"12:34 v-0020: only one stage decoration permitted per entry point");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Pass) {
|
TEST_F(ValidateFunctionTest, NoPipelineEntryPoints) {
|
||||||
// [[stage(vertex)]]
|
|
||||||
// fn vtx_func() -> void { return; }
|
|
||||||
|
|
||||||
Func("vtx_func", ast::VariableList{}, ty.void_(),
|
|
||||||
ast::StatementList{
|
|
||||||
create<ast::ReturnStatement>(),
|
|
||||||
},
|
|
||||||
ast::DecorationList{
|
|
||||||
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
|
|
||||||
});
|
|
||||||
|
|
||||||
ValidatorImpl& v = Build();
|
|
||||||
|
|
||||||
EXPECT_TRUE(v.Validate()) << v.error();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Fail) {
|
|
||||||
// fn vtx_func() -> void { return; }
|
|
||||||
Func("vtx_func", ast::VariableList{}, ty.void_(),
|
Func("vtx_func", ast::VariableList{}, ty.void_(),
|
||||||
ast::StatementList{
|
ast::StatementList{
|
||||||
create<ast::ReturnStatement>(),
|
create<ast::ReturnStatement>(),
|
||||||
|
@ -105,10 +87,7 @@ TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Fail) {
|
||||||
|
|
||||||
ValidatorImpl& v = Build();
|
ValidatorImpl& v = Build();
|
||||||
|
|
||||||
EXPECT_FALSE(v.Validate());
|
EXPECT_TRUE(v.Validate()) << v.error();
|
||||||
EXPECT_EQ(v.error(),
|
|
||||||
"v-0003: At least one of vertex, fragment or compute shader must "
|
|
||||||
"be present");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, FunctionVarInitWithParam) {
|
TEST_F(ValidateFunctionTest, FunctionVarInitWithParam) {
|
||||||
|
|
|
@ -138,10 +138,8 @@ bool ValidatorImpl::ValidateGlobalVariable(const ast::Variable* var) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ValidatorImpl::ValidateEntryPoint(const ast::FunctionList& funcs) {
|
bool ValidatorImpl::ValidateEntryPoint(const ast::FunctionList& funcs) {
|
||||||
auto shader_is_present = false;
|
|
||||||
for (auto* func : funcs) {
|
for (auto* func : funcs) {
|
||||||
if (func->IsEntryPoint()) {
|
if (func->IsEntryPoint()) {
|
||||||
shader_is_present = true;
|
|
||||||
auto stage_deco_count = 0;
|
auto stage_deco_count = 0;
|
||||||
for (auto* deco : func->decorations()) {
|
for (auto* deco : func->decorations()) {
|
||||||
if (deco->Is<ast::StageDecoration>()) {
|
if (deco->Is<ast::StageDecoration>()) {
|
||||||
|
@ -158,12 +156,6 @@ bool ValidatorImpl::ValidateEntryPoint(const ast::FunctionList& funcs) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!shader_is_present) {
|
|
||||||
add_error(Source{}, "v-0003",
|
|
||||||
"At least one of vertex, fragment or compute shader must "
|
|
||||||
"be present");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ TEST_F(ValidatorTest, GlobalConstNoStorageClass_Pass) {
|
||||||
|
|
||||||
ValidatorImpl& v = Build();
|
ValidatorImpl& v = Build();
|
||||||
|
|
||||||
EXPECT_FALSE(v.Validate()) << v.error();
|
EXPECT_TRUE(v.Validate()) << v.error();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidatorTest, GlobalVariableUnique_Pass) {
|
TEST_F(ValidatorTest, GlobalVariableUnique_Pass) {
|
||||||
|
|
Loading…
Reference in New Issue