[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>
diff --git a/src/transform/hlsl.cc b/src/transform/hlsl.cc
index 0a08e24..918d6e3 100644
--- a/src/transform/hlsl.cc
+++ b/src/transform/hlsl.cc
@@ -16,6 +16,7 @@
#include <utility>
+#include "src/ast/stage_decoration.h"
#include "src/ast/variable_decl_statement.h"
#include "src/program_builder.h"
#include "src/semantic/expression.h"
@@ -32,6 +33,7 @@
ProgramBuilder out;
CloneContext ctx(&out, in);
PromoteArrayInitializerToConstVar(ctx);
+ AddEmptyEntryPoint(ctx);
ctx.Clone();
return Output{Program(std::move(out))};
}
@@ -105,5 +107,16 @@
}
}
+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 tint
diff --git a/src/transform/hlsl.h b/src/transform/hlsl.h
index 53ad35f..df903a7 100644
--- a/src/transform/hlsl.h
+++ b/src/transform/hlsl.h
@@ -44,6 +44,8 @@
/// the array usage statement.
/// See crbug.com/tint/406 for more details
void PromoteArrayInitializerToConstVar(CloneContext& ctx) const;
+ /// Add an empty shader entry point if none exist in the module.
+ void AddEmptyEntryPoint(CloneContext& ctx) const;
};
} // namespace transform
diff --git a/src/transform/hlsl_test.cc b/src/transform/hlsl_test.cc
index bc6b264..4ce2259 100644
--- a/src/transform/hlsl_test.cc
+++ b/src/transform/hlsl_test.cc
@@ -143,6 +143,20 @@
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 transform
} // namespace tint
diff --git a/src/transform/spirv.cc b/src/transform/spirv.cc
index d29cfa4..bbbff3f 100644
--- a/src/transform/spirv.cc
+++ b/src/transform/spirv.cc
@@ -19,6 +19,7 @@
#include "src/ast/call_statement.h"
#include "src/ast/return_statement.h"
+#include "src/ast/stage_decoration.h"
#include "src/program_builder.h"
#include "src/semantic/function.h"
#include "src/semantic/statement.h"
@@ -42,6 +43,7 @@
ProgramBuilder out2;
CloneContext ctx2(&out2, &tmp);
HandleSampleMaskBuiltins(ctx2);
+ AddEmptyEntryPoint(ctx2);
ctx2.Clone();
return Output{Program(std::move(out2))};
@@ -234,6 +236,17 @@
}
}
+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(
CloneContext& ctx,
const ast::Function* func,
diff --git a/src/transform/spirv.h b/src/transform/spirv.h
index 40bc54e..7ac53e1 100644
--- a/src/transform/spirv.h
+++ b/src/transform/spirv.h
@@ -47,6 +47,8 @@
void HandleEntryPointIOTypes(CloneContext& ctx) const;
/// Change type of sample mask builtin variables to single element arrays.
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
/// function-scope variables for structs to `func`.
diff --git a/src/transform/spirv_test.cc b/src/transform/spirv_test.cc
index 9df9ed2..bb21aea 100644
--- a/src/transform/spirv_test.cc
+++ b/src/transform/spirv_test.cc
@@ -833,6 +833,20 @@
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_F(SpirvTest, MultipleTransforms) {
auto* src = R"(
diff --git a/src/validator/validator_function_test.cc b/src/validator/validator_function_test.cc
index d74e103..0d3d549 100644
--- a/src/validator/validator_function_test.cc
+++ b/src/validator/validator_function_test.cc
@@ -78,25 +78,7 @@
"12:34 v-0020: only one stage decoration permitted per entry point");
}
-TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Pass) {
- // [[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; }
+TEST_F(ValidateFunctionTest, NoPipelineEntryPoints) {
Func("vtx_func", ast::VariableList{}, ty.void_(),
ast::StatementList{
create<ast::ReturnStatement>(),
@@ -105,10 +87,7 @@
ValidatorImpl& v = Build();
- EXPECT_FALSE(v.Validate());
- EXPECT_EQ(v.error(),
- "v-0003: At least one of vertex, fragment or compute shader must "
- "be present");
+ EXPECT_TRUE(v.Validate()) << v.error();
}
TEST_F(ValidateFunctionTest, FunctionVarInitWithParam) {
diff --git a/src/validator/validator_impl.cc b/src/validator/validator_impl.cc
index 6b10921..acc10fa 100644
--- a/src/validator/validator_impl.cc
+++ b/src/validator/validator_impl.cc
@@ -138,10 +138,8 @@
}
bool ValidatorImpl::ValidateEntryPoint(const ast::FunctionList& funcs) {
- auto shader_is_present = false;
for (auto* func : funcs) {
if (func->IsEntryPoint()) {
- shader_is_present = true;
auto stage_deco_count = 0;
for (auto* deco : func->decorations()) {
if (deco->Is<ast::StageDecoration>()) {
@@ -158,12 +156,6 @@
}
}
}
- 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;
}
diff --git a/src/validator/validator_test.cc b/src/validator/validator_test.cc
index c79eff1..4a69b34 100644
--- a/src/validator/validator_test.cc
+++ b/src/validator/validator_test.cc
@@ -65,7 +65,7 @@
ValidatorImpl& v = Build();
- EXPECT_FALSE(v.Validate()) << v.error();
+ EXPECT_TRUE(v.Validate()) << v.error();
}
TEST_F(ValidatorTest, GlobalVariableUnique_Pass) {