[validate] Update to validate pipeline stages.
This CL converts the validator to work against the pipeline stages attached to functions instead of entry point nodes. Change-Id: Ia6274423e49426766c527d1123880ef6e0140959 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/28700 Commit-Queue: dan sinclair <dsinclair@chromium.org> Reviewed-by: David Neto <dneto@google.com> Reviewed-by: Sarah Mashayekhi <sarahmashay@google.com>
This commit is contained in:
parent
0592643782
commit
51ac24ab10
|
@ -379,14 +379,12 @@ TEST_F(ValidateControlBlockTest, SwitchCase_Pass) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateControlBlockTest, SwitchCaseAlias_Pass) {
|
TEST_F(ValidateControlBlockTest, SwitchCaseAlias_Pass) {
|
||||||
// entry_point vertex = main
|
|
||||||
// type MyInt = u32;
|
// type MyInt = u32;
|
||||||
// fn main()->void {
|
// var v: MyInt;
|
||||||
// var v: MyInt;
|
// switch(v){
|
||||||
// switch(v){
|
// default: {}
|
||||||
// default: {}
|
|
||||||
// }
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
ast::type::U32Type u32;
|
ast::type::U32Type u32;
|
||||||
ast::type::AliasType my_int{"MyInt", &u32};
|
ast::type::AliasType my_int{"MyInt", &u32};
|
||||||
|
|
||||||
|
@ -406,21 +404,11 @@ TEST_F(ValidateControlBlockTest, SwitchCaseAlias_Pass) {
|
||||||
block->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
block->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||||
block->append(
|
block->append(
|
||||||
std::make_unique<ast::SwitchStatement>(std::move(cond), std::move(body)));
|
std::make_unique<ast::SwitchStatement>(std::move(cond), std::move(body)));
|
||||||
block->append(std::make_unique<ast::ReturnStatement>());
|
|
||||||
|
|
||||||
ast::type::VoidType void_type;
|
|
||||||
ast::VariableList params;
|
|
||||||
auto func =
|
|
||||||
std::make_unique<ast::Function>("main", std::move(params), &void_type);
|
|
||||||
func->set_body(std::move(block));
|
|
||||||
auto entry_point = std::make_unique<ast::EntryPoint>(
|
|
||||||
ast::PipelineStage::kVertex, "", "main");
|
|
||||||
mod()->AddFunction(std::move(func));
|
|
||||||
mod()->AddAliasType(&my_int);
|
mod()->AddAliasType(&my_int);
|
||||||
mod()->AddEntryPoint(std::move(entry_point));
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(block.get())) << td()->error();
|
||||||
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
EXPECT_TRUE(v()->ValidateStatements(block.get())) << v()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -18,9 +18,11 @@
|
||||||
#include "spirv/unified1/GLSL.std.450.h"
|
#include "spirv/unified1/GLSL.std.450.h"
|
||||||
#include "src/ast/call_statement.h"
|
#include "src/ast/call_statement.h"
|
||||||
#include "src/ast/entry_point.h"
|
#include "src/ast/entry_point.h"
|
||||||
|
#include "src/ast/pipeline_stage.h"
|
||||||
#include "src/ast/return_statement.h"
|
#include "src/ast/return_statement.h"
|
||||||
#include "src/ast/scalar_constructor_expression.h"
|
#include "src/ast/scalar_constructor_expression.h"
|
||||||
#include "src/ast/sint_literal.h"
|
#include "src/ast/sint_literal.h"
|
||||||
|
#include "src/ast/stage_decoration.h"
|
||||||
#include "src/ast/type/f32_type.h"
|
#include "src/ast/type/f32_type.h"
|
||||||
#include "src/ast/type/i32_type.h"
|
#include "src/ast/type/i32_type.h"
|
||||||
#include "src/ast/type/void_type.h"
|
#include "src/ast/type/void_type.h"
|
||||||
|
@ -74,19 +76,23 @@ TEST_F(ValidateFunctionTest, FunctionEndWithoutReturnStatementEmptyBody_Fail) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_Pass) {
|
TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_Pass) {
|
||||||
|
// [[stage(vertex)]]
|
||||||
// fn func -> void { return; }
|
// fn func -> void { return; }
|
||||||
ast::type::VoidType void_type;
|
ast::type::VoidType void_type;
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
|
|
||||||
auto func =
|
auto func =
|
||||||
std::make_unique<ast::Function>("func", std::move(params), &void_type);
|
std::make_unique<ast::Function>("func", std::move(params), &void_type);
|
||||||
auto body = std::make_unique<ast::BlockStatement>();
|
auto body = std::make_unique<ast::BlockStatement>();
|
||||||
body->append(std::make_unique<ast::ReturnStatement>());
|
body->append(std::make_unique<ast::ReturnStatement>());
|
||||||
func->set_body(std::move(body));
|
func->set_body(std::move(body));
|
||||||
|
func->add_decoration(
|
||||||
|
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
|
||||||
mod()->AddFunction(std::move(func));
|
mod()->AddFunction(std::move(func));
|
||||||
AddFakeEntryPoint();
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->DetermineFunctions(mod()->functions())) << td()->error();
|
||||||
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
EXPECT_TRUE(v()->ValidateFunctions(mod(), mod()->functions()))
|
||||||
|
<< v()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_fail) {
|
TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_fail) {
|
||||||
|
@ -220,93 +226,46 @@ TEST_F(ValidateFunctionTest, RecursionIsNotAllowedExpr_Fail) {
|
||||||
EXPECT_EQ(v()->error(), "12:34: v-0004: recursion is not allowed: 'func'");
|
EXPECT_EQ(v()->error(), "12:34: v-0004: recursion is not allowed: 'func'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, EntryPointFunctionMissing_Fail) {
|
TEST_F(ValidateFunctionTest, Function_WithPipelineStage_NotVoid_Fail) {
|
||||||
// entry_point vertex as "main" = vtx_main
|
// [[stage(vertex)]]
|
||||||
// fn frag_main() -> void { return; }
|
|
||||||
ast::type::VoidType void_type;
|
|
||||||
ast::VariableList params;
|
|
||||||
auto func = std::make_unique<ast::Function>("vtx_main", 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));
|
|
||||||
|
|
||||||
auto entry_point = std::make_unique<ast::EntryPoint>(
|
|
||||||
Source{12, 34}, ast::PipelineStage::kVertex, "main", "frag_main");
|
|
||||||
|
|
||||||
mod()->AddFunction(std::move(func));
|
|
||||||
mod()->AddEntryPoint(std::move(entry_point));
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
|
||||||
EXPECT_EQ(v()->error(),
|
|
||||||
"12:34: v-0019: Function used in entry point does not exist: "
|
|
||||||
"'frag_main'");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, EntryPointFunctionExist_Pass) {
|
|
||||||
// entry_point vertex as "main" = vtx_main
|
|
||||||
// fn vtx_main() -> void { return; }
|
|
||||||
ast::type::VoidType void_type;
|
|
||||||
ast::VariableList params;
|
|
||||||
auto func = std::make_unique<ast::Function>("vtx_main", 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));
|
|
||||||
|
|
||||||
auto entry_point = std::make_unique<ast::EntryPoint>(
|
|
||||||
ast::PipelineStage::kVertex, "main", "vtx_main");
|
|
||||||
|
|
||||||
mod()->AddFunction(std::move(func));
|
|
||||||
mod()->AddEntryPoint(std::move(entry_point));
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
|
||||||
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, EntryPointFunctionNotVoid_Fail) {
|
|
||||||
// entry_point vertex as "main" = vtx_main
|
|
||||||
// fn vtx_main() -> i32 { return 0; }
|
// fn vtx_main() -> i32 { return 0; }
|
||||||
ast::type::I32Type i32;
|
ast::type::I32Type i32;
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
auto func =
|
auto func = std::make_unique<ast::Function>(Source{12, 34}, "vtx_main",
|
||||||
std::make_unique<ast::Function>("vtx_main", std::move(params), &i32);
|
std::move(params), &i32);
|
||||||
auto return_expr = std::make_unique<ast::ScalarConstructorExpression>(
|
auto return_expr = std::make_unique<ast::ScalarConstructorExpression>(
|
||||||
std::make_unique<ast::SintLiteral>(&i32, 0));
|
std::make_unique<ast::SintLiteral>(&i32, 0));
|
||||||
|
|
||||||
auto body = std::make_unique<ast::BlockStatement>();
|
auto body = std::make_unique<ast::BlockStatement>();
|
||||||
body->append(std::make_unique<ast::ReturnStatement>(std::move(return_expr)));
|
body->append(std::make_unique<ast::ReturnStatement>(std::move(return_expr)));
|
||||||
func->set_body(std::move(body));
|
func->set_body(std::move(body));
|
||||||
|
func->add_decoration(
|
||||||
auto entry_point = std::make_unique<ast::EntryPoint>(
|
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
|
||||||
Source{12, 34}, ast::PipelineStage::kVertex, "main", "vtx_main");
|
|
||||||
|
|
||||||
mod()->AddFunction(std::move(func));
|
mod()->AddFunction(std::move(func));
|
||||||
mod()->AddEntryPoint(std::move(entry_point));
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod()));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
"12:34: v-0024: Entry point function must return void: 'vtx_main'");
|
"12:34: v-0024: Entry point function must return void: 'vtx_main'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, EntryPointFunctionWithParams_Fail) {
|
TEST_F(ValidateFunctionTest, Function_WithPipelineStage_WithParams_Fail) {
|
||||||
// entry_point vertex as "func" = vtx_func
|
// [[stage(vertex)]]
|
||||||
// fn vtx_func(a : i32) -> void { return; }
|
// fn vtx_func(a : i32) -> void { return; }
|
||||||
ast::type::I32Type i32;
|
ast::type::I32Type i32;
|
||||||
ast::type::VoidType void_type;
|
ast::type::VoidType void_type;
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
params.push_back(
|
params.push_back(
|
||||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32));
|
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32));
|
||||||
auto func = std::make_unique<ast::Function>("vtx_func", std::move(params),
|
auto func = std::make_unique<ast::Function>(Source{12, 34}, "vtx_func",
|
||||||
&void_type);
|
std::move(params), &void_type);
|
||||||
auto body = std::make_unique<ast::BlockStatement>();
|
auto body = std::make_unique<ast::BlockStatement>();
|
||||||
body->append(std::make_unique<ast::ReturnStatement>());
|
body->append(std::make_unique<ast::ReturnStatement>());
|
||||||
func->set_body(std::move(body));
|
func->set_body(std::move(body));
|
||||||
|
func->add_decoration(
|
||||||
auto entry_point = std::make_unique<ast::EntryPoint>(
|
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
|
||||||
Source{12, 34}, ast::PipelineStage::kVertex, "func", "vtx_func");
|
|
||||||
|
|
||||||
mod()->AddFunction(std::move(func));
|
mod()->AddFunction(std::move(func));
|
||||||
mod()->AddEntryPoint(std::move(entry_point));
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod()));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
|
@ -314,26 +273,31 @@ TEST_F(ValidateFunctionTest, EntryPointFunctionWithParams_Fail) {
|
||||||
"'vtx_func'");
|
"'vtx_func'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, EntryPointFunctionPairMustBeUniqueDuplicate_Fail) {
|
TEST_F(ValidateFunctionTest, PipelineStageNamePair_MustBeUnique_Fail) {
|
||||||
// entry_point vertex = vtx_main
|
// [[stage(vertex)]]
|
||||||
// entry_point vertex = vtx_main
|
// fn main() -> void { return ;}
|
||||||
// fn vtx_main() -> void { return; }
|
// [[stage(vertex)]]
|
||||||
|
// fn main() -> void { return; }
|
||||||
ast::type::VoidType void_type;
|
ast::type::VoidType void_type;
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
auto func = std::make_unique<ast::Function>("vtx_main", std::move(params),
|
auto func = std::make_unique<ast::Function>(Source{5, 6}, "main",
|
||||||
&void_type);
|
std::move(params), &void_type);
|
||||||
auto body = std::make_unique<ast::BlockStatement>();
|
auto body = std::make_unique<ast::BlockStatement>();
|
||||||
body->append(std::make_unique<ast::ReturnStatement>());
|
body->append(std::make_unique<ast::ReturnStatement>());
|
||||||
func->set_body(std::move(body));
|
func->set_body(std::move(body));
|
||||||
|
func->add_decoration(
|
||||||
auto entry_point = std::make_unique<ast::EntryPoint>(
|
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
|
||||||
ast::PipelineStage::kVertex, "", "vtx_main");
|
|
||||||
auto entry_point_copy = std::make_unique<ast::EntryPoint>(
|
|
||||||
Source{12, 34}, ast::PipelineStage::kVertex, "", "vtx_main");
|
|
||||||
|
|
||||||
mod()->AddFunction(std::move(func));
|
mod()->AddFunction(std::move(func));
|
||||||
mod()->AddEntryPoint(std::move(entry_point));
|
|
||||||
mod()->AddEntryPoint(std::move(entry_point_copy));
|
func = std::make_unique<ast::Function>(Source{12, 34}, "main",
|
||||||
|
std::move(params), &void_type);
|
||||||
|
body = std::make_unique<ast::BlockStatement>();
|
||||||
|
body->append(std::make_unique<ast::ReturnStatement>());
|
||||||
|
func->set_body(std::move(body));
|
||||||
|
func->add_decoration(
|
||||||
|
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
|
||||||
|
mod()->AddFunction(std::move(func));
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod()));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
|
@ -341,46 +305,37 @@ TEST_F(ValidateFunctionTest, EntryPointFunctionPairMustBeUniqueDuplicate_Fail) {
|
||||||
"must be unique");
|
"must be unique");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, EntryPointFunctionPairMustBeUniqueTowVertex_Fail) {
|
TEST_F(ValidateFunctionTest, PipelineStageNamePair_MustBeUnique_Pass) {
|
||||||
// entry_point vertex as "main" = vtx_func1
|
// [[stage(vertex)]]
|
||||||
// entry_point vertex as "main" = vtx_func0
|
// fn main() -> void { return; }
|
||||||
// fn vtx_func1() -> void { return; }
|
// [[stage(fragment)]]
|
||||||
// fn vtx_func0() -> void { return; }
|
// fn main() -> void { return; }
|
||||||
ast::type::VoidType void_type;
|
ast::type::VoidType void_type;
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
auto func = std::make_unique<ast::Function>("vtx_func0", std::move(params),
|
auto func = std::make_unique<ast::Function>(Source{5, 6}, "main",
|
||||||
&void_type);
|
std::move(params), &void_type);
|
||||||
auto body = std::make_unique<ast::BlockStatement>();
|
auto body = std::make_unique<ast::BlockStatement>();
|
||||||
body->append(std::make_unique<ast::ReturnStatement>());
|
body->append(std::make_unique<ast::ReturnStatement>());
|
||||||
func->set_body(std::move(body));
|
func->set_body(std::move(body));
|
||||||
|
func->add_decoration(
|
||||||
ast::VariableList params1;
|
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
|
||||||
auto func1 = std::make_unique<ast::Function>("vtx_func1", std::move(params1),
|
|
||||||
&void_type);
|
|
||||||
auto body1 = std::make_unique<ast::BlockStatement>();
|
|
||||||
body1->append(std::make_unique<ast::ReturnStatement>());
|
|
||||||
func1->set_body(std::move(body1));
|
|
||||||
|
|
||||||
auto entry_point = std::make_unique<ast::EntryPoint>(
|
|
||||||
ast::PipelineStage::kVertex, "main", "vtx_func0");
|
|
||||||
auto entry_point1 = std::make_unique<ast::EntryPoint>(
|
|
||||||
Source{12, 34}, ast::PipelineStage::kVertex, "main", "vtx_func1");
|
|
||||||
|
|
||||||
mod()->AddFunction(std::move(func));
|
mod()->AddFunction(std::move(func));
|
||||||
mod()->AddFunction(std::move(func1));
|
|
||||||
mod()->AddEntryPoint(std::move(entry_point));
|
func = std::make_unique<ast::Function>(Source{12, 34}, "main",
|
||||||
mod()->AddEntryPoint(std::move(entry_point1));
|
std::move(params), &void_type);
|
||||||
|
body = std::make_unique<ast::BlockStatement>();
|
||||||
|
body->append(std::make_unique<ast::ReturnStatement>());
|
||||||
|
func->set_body(std::move(body));
|
||||||
|
func->add_decoration(
|
||||||
|
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kFragment));
|
||||||
|
mod()->AddFunction(std::move(func));
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
||||||
EXPECT_EQ(v()->error(),
|
|
||||||
"12:34: v-0020: The pair of <entry point name, pipeline stage> "
|
|
||||||
"must be unique");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest,
|
TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Pass) {
|
||||||
EntryPointFunctionPairMustBeUniqueSameFuncDiffStage_Pass) {
|
// [[stage(vertex)]]
|
||||||
// entry_point vertex as "main" = vtx_func
|
|
||||||
// entry_point fragment as "main" = vtx_func
|
|
||||||
// fn vtx_func() -> void { return; }
|
// fn vtx_func() -> void { return; }
|
||||||
ast::type::VoidType void_type;
|
ast::type::VoidType void_type;
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
|
@ -389,42 +344,15 @@ TEST_F(ValidateFunctionTest,
|
||||||
auto body = std::make_unique<ast::BlockStatement>();
|
auto body = std::make_unique<ast::BlockStatement>();
|
||||||
body->append(std::make_unique<ast::ReturnStatement>());
|
body->append(std::make_unique<ast::ReturnStatement>());
|
||||||
func->set_body(std::move(body));
|
func->set_body(std::move(body));
|
||||||
|
func->add_decoration(
|
||||||
auto entry_point = std::make_unique<ast::EntryPoint>(
|
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
|
||||||
ast::PipelineStage::kVertex, "main", "vtx_func");
|
|
||||||
auto entry_point1 = std::make_unique<ast::EntryPoint>(
|
|
||||||
Source{12, 34}, ast::PipelineStage::kFragment, "main", "vtx_func");
|
|
||||||
|
|
||||||
mod()->AddFunction(std::move(func));
|
mod()->AddFunction(std::move(func));
|
||||||
mod()->AddEntryPoint(std::move(entry_point));
|
|
||||||
mod()->AddEntryPoint(std::move(entry_point1));
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
|
||||||
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest,
|
|
||||||
AtLeastOneOfVertexFragmentComputeShaderMustBePeresent_Pass) {
|
|
||||||
// entry_point vertex as "main" = vtx_func
|
|
||||||
// fn vtx_func() -> void { return; }
|
|
||||||
ast::type::VoidType void_type;
|
|
||||||
ast::VariableList params;
|
|
||||||
auto func = std::make_unique<ast::Function>("vtx_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));
|
|
||||||
|
|
||||||
auto entry_point = std::make_unique<ast::EntryPoint>(
|
|
||||||
ast::PipelineStage::kVertex, "main", "vtx_func");
|
|
||||||
mod()->AddFunction(std::move(func));
|
|
||||||
mod()->AddEntryPoint(std::move(entry_point));
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest,
|
TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Fail) {
|
||||||
AtLeastOneOfVertexFragmentComputeShaderMustBePeresent_Fail) {
|
|
||||||
// fn vtx_func() -> void { return; }
|
// fn vtx_func() -> void { return; }
|
||||||
ast::type::VoidType void_type;
|
ast::type::VoidType void_type;
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
|
|
|
@ -49,11 +49,7 @@ bool ValidatorImpl::Validate(const ast::Module* module) {
|
||||||
if (!CheckImports(module)) {
|
if (!CheckImports(module)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ValidateFunctions(module->functions())) {
|
if (!ValidateFunctions(module, module->functions())) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// ValidateEntryPoints must be done after populating function_stack_
|
|
||||||
if (!ValidateEntryPoints(module->entry_points())) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,71 +82,66 @@ bool ValidatorImpl::ValidateGlobalVariables(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ValidatorImpl::ValidateEntryPoints(const ast::EntryPointList& eps) {
|
bool ValidatorImpl::ValidateFunctions(const ast::Module* mod,
|
||||||
|
const ast::FunctionList& funcs) {
|
||||||
ScopeStack<ast::PipelineStage> entry_point_map;
|
ScopeStack<ast::PipelineStage> entry_point_map;
|
||||||
entry_point_map.push_scope();
|
entry_point_map.push_scope();
|
||||||
for (const auto& ep : eps) {
|
|
||||||
auto* ep_ptr = ep.get();
|
size_t pipeline_count = 0;
|
||||||
ast::Function* func = nullptr;
|
for (const auto& func : funcs) {
|
||||||
if (!function_stack_.get(ep_ptr->function_name(), &func)) {
|
// The entry points will be checked later to see if their duplicated
|
||||||
set_error(ep_ptr->source(),
|
if (function_stack_.has(func->name()) &&
|
||||||
"v-0019: Function used in entry point does not exist: '" +
|
!entry_point_map.has(func->name())) {
|
||||||
ep_ptr->function_name() + "'");
|
set_error(func->source(),
|
||||||
|
"v-0016: function names must be unique '" + func->name() + "'");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!func->return_type()->IsVoid()) {
|
if (func->pipeline_stage() != ast::PipelineStage::kNone) {
|
||||||
set_error(ep_ptr->source(),
|
pipeline_count++;
|
||||||
"v-0024: Entry point function must return void: '" +
|
|
||||||
ep_ptr->function_name() + "'");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (func->params().size() != 0) {
|
if (!func->return_type()->IsVoid()) {
|
||||||
set_error(ep_ptr->source(),
|
set_error(func->source(),
|
||||||
"v-0023: Entry point function must accept no parameters: '" +
|
"v-0024: Entry point function must return void: '" +
|
||||||
ep_ptr->function_name() + "'");
|
func->name() + "'");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::PipelineStage pipeline_stage;
|
|
||||||
if (entry_point_map.get(ep_ptr->name(), &pipeline_stage)) {
|
|
||||||
if (pipeline_stage == ep_ptr->stage()) {
|
|
||||||
set_error(ep_ptr->source(),
|
|
||||||
"v-0020: The pair of <entry point name, pipeline stage> must "
|
|
||||||
"be unique");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (func->params().size() != 0) {
|
||||||
|
set_error(func->source(),
|
||||||
|
"v-0023: Entry point function must accept no parameters: '" +
|
||||||
|
func->name() + "'");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::PipelineStage pipeline_stage;
|
||||||
|
if (entry_point_map.get(func->name(), &pipeline_stage)) {
|
||||||
|
if (pipeline_stage == func->pipeline_stage()) {
|
||||||
|
set_error(
|
||||||
|
func->source(),
|
||||||
|
"v-0020: The pair of <entry point name, pipeline stage> must "
|
||||||
|
"be unique");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry_point_map.set(func->name(), func->pipeline_stage());
|
||||||
}
|
}
|
||||||
entry_point_map.set(ep_ptr->name(), ep_ptr->stage());
|
|
||||||
|
function_stack_.set(func->name(), func.get());
|
||||||
|
current_function_ = func.get();
|
||||||
|
if (!ValidateFunction(func.get())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
current_function_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eps.empty()) {
|
if (pipeline_count == 0 && mod->entry_points().empty()) {
|
||||||
set_error(Source{0, 0},
|
set_error(Source{0, 0},
|
||||||
"v-0003: At least one of vertex, fragment or compute shader must "
|
"v-0003: At least one of vertex, fragment or compute shader must "
|
||||||
"be present");
|
"be present");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
entry_point_map.pop_scope();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ValidatorImpl::ValidateFunctions(const ast::FunctionList& funcs) {
|
|
||||||
for (const auto& func : funcs) {
|
|
||||||
auto* func_ptr = func.get();
|
|
||||||
if (function_stack_.has(func_ptr->name())) {
|
|
||||||
set_error(func_ptr->source(), "v-0016: function names must be unique '" +
|
|
||||||
func_ptr->name() + "'");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function_stack_.set(func_ptr->name(), func_ptr);
|
|
||||||
current_function_ = func_ptr;
|
|
||||||
if (!ValidateFunction(func_ptr)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
current_function_ = nullptr;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,9 +58,11 @@ class ValidatorImpl {
|
||||||
/// @returns true if the validation was successful
|
/// @returns true if the validation was successful
|
||||||
bool ValidateGlobalVariables(const ast::VariableList& global_vars);
|
bool ValidateGlobalVariables(const ast::VariableList& global_vars);
|
||||||
/// Validates Functions
|
/// Validates Functions
|
||||||
|
/// @param mod the module
|
||||||
/// @param funcs the functions to check
|
/// @param funcs the functions to check
|
||||||
/// @returns true if the validation was successful
|
/// @returns true if the validation was successful
|
||||||
bool ValidateFunctions(const ast::FunctionList& funcs);
|
bool ValidateFunctions(const ast::Module* mod,
|
||||||
|
const ast::FunctionList& funcs);
|
||||||
/// Validates a function
|
/// Validates a function
|
||||||
/// @param func the function to check
|
/// @param func the function to check
|
||||||
/// @returns true if the validation was successful
|
/// @returns true if the validation was successful
|
||||||
|
|
|
@ -34,9 +34,11 @@
|
||||||
#include "src/ast/int_literal.h"
|
#include "src/ast/int_literal.h"
|
||||||
#include "src/ast/loop_statement.h"
|
#include "src/ast/loop_statement.h"
|
||||||
#include "src/ast/member_accessor_expression.h"
|
#include "src/ast/member_accessor_expression.h"
|
||||||
|
#include "src/ast/pipeline_stage.h"
|
||||||
#include "src/ast/return_statement.h"
|
#include "src/ast/return_statement.h"
|
||||||
#include "src/ast/scalar_constructor_expression.h"
|
#include "src/ast/scalar_constructor_expression.h"
|
||||||
#include "src/ast/sint_literal.h"
|
#include "src/ast/sint_literal.h"
|
||||||
|
#include "src/ast/stage_decoration.h"
|
||||||
#include "src/ast/struct.h"
|
#include "src/ast/struct.h"
|
||||||
#include "src/ast/struct_member.h"
|
#include "src/ast/struct_member.h"
|
||||||
#include "src/ast/switch_statement.h"
|
#include "src/ast/switch_statement.h"
|
||||||
|
@ -263,10 +265,8 @@ TEST_F(ValidatorTest, GlobalVariableWithStorageClass_Pass) {
|
||||||
auto global_var = std::make_unique<ast::Variable>(
|
auto global_var = std::make_unique<ast::Variable>(
|
||||||
Source{12, 34}, "global_var", ast::StorageClass::kInput, &f32);
|
Source{12, 34}, "global_var", ast::StorageClass::kInput, &f32);
|
||||||
mod()->AddGlobalVariable(std::move(global_var));
|
mod()->AddGlobalVariable(std::move(global_var));
|
||||||
AddFakeEntryPoint();
|
EXPECT_TRUE(v()->ValidateGlobalVariables(mod()->global_variables()))
|
||||||
|
<< v()->error();
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
|
||||||
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidatorTest, GlobalVariableNoStorageClass_Fail) {
|
TEST_F(ValidatorTest, GlobalVariableNoStorageClass_Fail) {
|
||||||
|
@ -341,11 +341,13 @@ TEST_F(ValidatorTest, UsingUndefinedVariableGlobalVariable_Fail) {
|
||||||
|
|
||||||
TEST_F(ValidatorTest, UsingUndefinedVariableGlobalVariable_Pass) {
|
TEST_F(ValidatorTest, UsingUndefinedVariableGlobalVariable_Pass) {
|
||||||
// var global_var: f32 = 2.1;
|
// var global_var: f32 = 2.1;
|
||||||
// fn my_func() -> f32 {
|
// fn my_func() -> void {
|
||||||
// global_var = 3.14;
|
// global_var = 3.14;
|
||||||
// return 3.14;
|
// return;
|
||||||
// }
|
// }
|
||||||
ast::type::F32Type f32;
|
ast::type::F32Type f32;
|
||||||
|
ast::type::VoidType void_type;
|
||||||
|
|
||||||
auto global_var = std::make_unique<ast::Variable>(
|
auto global_var = std::make_unique<ast::Variable>(
|
||||||
"global_var", ast::StorageClass::kPrivate, &f32);
|
"global_var", ast::StorageClass::kPrivate, &f32);
|
||||||
global_var->set_constructor(
|
global_var->set_constructor(
|
||||||
|
@ -354,30 +356,23 @@ TEST_F(ValidatorTest, UsingUndefinedVariableGlobalVariable_Pass) {
|
||||||
mod()->AddGlobalVariable(std::move(global_var));
|
mod()->AddGlobalVariable(std::move(global_var));
|
||||||
|
|
||||||
auto lhs = std::make_unique<ast::IdentifierExpression>("global_var");
|
auto lhs = std::make_unique<ast::IdentifierExpression>("global_var");
|
||||||
auto* lhs_ptr = lhs.get();
|
|
||||||
auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
|
auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
|
||||||
std::make_unique<ast::FloatLiteral>(&f32, 3.14f));
|
std::make_unique<ast::FloatLiteral>(&f32, 3.14f));
|
||||||
auto* rhs_ptr = rhs.get();
|
|
||||||
auto return_expr = std::make_unique<ast::ScalarConstructorExpression>(
|
|
||||||
std::make_unique<ast::FloatLiteral>(&f32, 3.14f));
|
|
||||||
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
auto func =
|
auto func =
|
||||||
std::make_unique<ast::Function>("my_func", std::move(params), &f32);
|
std::make_unique<ast::Function>("my_func", std::move(params), &void_type);
|
||||||
|
|
||||||
auto body = std::make_unique<ast::BlockStatement>();
|
auto body = std::make_unique<ast::BlockStatement>();
|
||||||
body->append(std::make_unique<ast::AssignmentStatement>(
|
body->append(std::make_unique<ast::AssignmentStatement>(
|
||||||
Source{12, 34}, std::move(lhs), std::move(rhs)));
|
Source{12, 34}, std::move(lhs), std::move(rhs)));
|
||||||
body->append(std::make_unique<ast::ReturnStatement>(std::move(return_expr)));
|
body->append(std::make_unique<ast::ReturnStatement>());
|
||||||
func->set_body(std::move(body));
|
func->set_body(std::move(body));
|
||||||
auto* func_ptr = func.get();
|
func->add_decoration(
|
||||||
|
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
|
||||||
mod()->AddFunction(std::move(func));
|
mod()->AddFunction(std::move(func));
|
||||||
AddFakeEntryPoint();
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_TRUE(td()->DetermineFunction(func_ptr)) << td()->error();
|
|
||||||
ASSERT_NE(lhs_ptr->result_type(), nullptr);
|
|
||||||
ASSERT_NE(rhs_ptr->result_type(), nullptr);
|
|
||||||
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,9 +462,31 @@ TEST_F(ValidatorTest, GlobalVariableUnique_Pass) {
|
||||||
var1->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
var1->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||||
std::make_unique<ast::SintLiteral>(&i32, 0)));
|
std::make_unique<ast::SintLiteral>(&i32, 0)));
|
||||||
mod()->AddGlobalVariable(std::move(var1));
|
mod()->AddGlobalVariable(std::move(var1));
|
||||||
AddFakeEntryPoint();
|
|
||||||
|
|
||||||
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
EXPECT_TRUE(v()->ValidateGlobalVariables(mod()->global_variables()))
|
||||||
|
<< v()->error();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidatorTest, GlobalVariableNotUnique_Fail) {
|
||||||
|
// var global_var : f32 = 0.1;
|
||||||
|
// var global_var : i32 = 0;
|
||||||
|
ast::type::F32Type f32;
|
||||||
|
ast::type::I32Type i32;
|
||||||
|
auto var0 = std::make_unique<ast::Variable>(
|
||||||
|
"global_var", ast::StorageClass::kPrivate, &f32);
|
||||||
|
var0->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||||
|
std::make_unique<ast::FloatLiteral>(&f32, 0.1)));
|
||||||
|
mod()->AddGlobalVariable(std::move(var0));
|
||||||
|
|
||||||
|
auto var1 = std::make_unique<ast::Variable>(
|
||||||
|
Source{12, 34}, "global_var", ast::StorageClass::kPrivate, &f32);
|
||||||
|
var1->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||||
|
std::make_unique<ast::SintLiteral>(&i32, 0)));
|
||||||
|
mod()->AddGlobalVariable(std::move(var1));
|
||||||
|
|
||||||
|
EXPECT_FALSE(v()->ValidateGlobalVariables(mod()->global_variables()));
|
||||||
|
EXPECT_EQ(v()->error(),
|
||||||
|
"12:34: v-0011: redeclared global identifier 'global_var'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidatorTest, AssignToConstant_Fail) {
|
TEST_F(ValidatorTest, AssignToConstant_Fail) {
|
||||||
|
@ -503,28 +520,6 @@ TEST_F(ValidatorTest, AssignToConstant_Fail) {
|
||||||
EXPECT_EQ(v()->error(), "12:34: v-0021: cannot re-assign a constant: 'a'");
|
EXPECT_EQ(v()->error(), "12:34: v-0021: cannot re-assign a constant: 'a'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidatorTest, GlobalVariableNotUnique_Fail) {
|
|
||||||
// var global_var : f32 = 0.1;
|
|
||||||
// var global_var : i32 = 0;
|
|
||||||
ast::type::F32Type f32;
|
|
||||||
ast::type::I32Type i32;
|
|
||||||
auto var0 = std::make_unique<ast::Variable>(
|
|
||||||
"global_var", ast::StorageClass::kPrivate, &f32);
|
|
||||||
var0->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
|
||||||
std::make_unique<ast::FloatLiteral>(&f32, 0.1)));
|
|
||||||
mod()->AddGlobalVariable(std::move(var0));
|
|
||||||
|
|
||||||
auto var1 = std::make_unique<ast::Variable>(
|
|
||||||
Source{12, 34}, "global_var", ast::StorageClass::kPrivate, &f32);
|
|
||||||
var1->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
|
||||||
std::make_unique<ast::SintLiteral>(&i32, 0)));
|
|
||||||
mod()->AddGlobalVariable(std::move(var1));
|
|
||||||
|
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
|
||||||
EXPECT_EQ(v()->error(),
|
|
||||||
"12:34: v-0011: redeclared global identifier 'global_var'");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ValidatorTest, GlobalVariableFunctionVariableNotUnique_Fail) {
|
TEST_F(ValidatorTest, GlobalVariableFunctionVariableNotUnique_Fail) {
|
||||||
// var a: f32 = 2.1;
|
// var a: f32 = 2.1;
|
||||||
// fn my_func -> void {
|
// fn my_func -> void {
|
||||||
|
@ -699,10 +694,11 @@ TEST_F(ValidatorTest, RedeclaredIdentifierDifferentFunctions_Pass) {
|
||||||
std::move(var1)));
|
std::move(var1)));
|
||||||
body1->append(std::make_unique<ast::ReturnStatement>());
|
body1->append(std::make_unique<ast::ReturnStatement>());
|
||||||
func1->set_body(std::move(body1));
|
func1->set_body(std::move(body1));
|
||||||
|
func1->add_decoration(
|
||||||
|
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
|
||||||
|
|
||||||
mod()->AddFunction(std::move(func0));
|
mod()->AddFunction(std::move(func0));
|
||||||
mod()->AddFunction(std::move(func1));
|
mod()->AddFunction(std::move(func1));
|
||||||
AddFakeEntryPoint();
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
||||||
|
|
|
@ -24,22 +24,4 @@ ValidatorTestHelper::ValidatorTestHelper() {
|
||||||
|
|
||||||
ValidatorTestHelper::~ValidatorTestHelper() = default;
|
ValidatorTestHelper::~ValidatorTestHelper() = default;
|
||||||
|
|
||||||
void ValidatorTestHelper::AddFakeEntryPoint() {
|
|
||||||
// entry_point vertex as "fake_entry_point" = fake_func
|
|
||||||
// fn fake_func() -> void {}
|
|
||||||
auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kVertex,
|
|
||||||
"fake_entry_point", "fake_func");
|
|
||||||
ast::VariableList fake_params;
|
|
||||||
auto fake_func = std::make_unique<ast::Function>(
|
|
||||||
"fake_func", std::move(fake_params),
|
|
||||||
ctx_.type_mgr().Get(std::make_unique<ast::type::VoidType>()));
|
|
||||||
auto fake_body = std::make_unique<ast::BlockStatement>();
|
|
||||||
auto return_stmt = std::make_unique<ast::ReturnStatement>();
|
|
||||||
fake_body->append(std::move(return_stmt));
|
|
||||||
fake_func->set_body(std::move(fake_body));
|
|
||||||
mod()->AddFunction(std::move(fake_func));
|
|
||||||
mod()->AddEntryPoint(std::move(ep));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
|
|
@ -37,8 +37,6 @@ class ValidatorTestHelper {
|
||||||
/// A handle to the created module
|
/// A handle to the created module
|
||||||
/// @return a pointer to the test module
|
/// @return a pointer to the test module
|
||||||
ast::Module* mod() { return &mod_; }
|
ast::Module* mod() { return &mod_; }
|
||||||
/// Creates a function and add an entry point to it
|
|
||||||
void AddFakeEntryPoint();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<ValidatorImpl> v_;
|
std::unique_ptr<ValidatorImpl> v_;
|
||||||
|
|
Loading…
Reference in New Issue