validation: store type of sample_index and sample_mask builtins must be 'u32'

Bug: tint:506
Change-Id: I786a74640d9ad9d98a76cdbe4dfcbfffdb0ccf2c
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/55121
Kokoro: Kokoro <noreply+kokoro@google.com>
Auto-Submit: Sarah Mashayekhi <sarahmashay@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
Sarah 2021-06-18 19:58:27 +00:00 committed by Tint LUCI CQ
parent 8b2be2d1e2
commit 443c41de33
4 changed files with 143 additions and 42 deletions

View File

@ -20,31 +20,112 @@ namespace {
class ResolverBuiltinsValidationTest : public resolver::TestHelper,
public testing::Test {};
TEST_F(ResolverBuiltinsValidationTest, FrontFacingParamIsBool_Pass) {
TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_Struct_Fail) {
// struct MyInputs {
// [[builtin(sample_mask)]] m: f32;
// };
// [[stage(fragment)]]
// fn fragShader(is_front: MyInputs) -> [[location(0)]] f32 { return 1.0; }
auto* s = Structure(
"MyInputs", {Member("m", ty.f32(),
ast::DecorationList{Builtin(
Source{{12, 34}}, ast::Builtin::kSampleMask)})});
Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(sample_mask) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, SampleMaskIsNotU32_Fail) {
// [[stage(fragment)]]
// fn fs_main(
// [[builtin(front_facing)]] is_front: bool
// [[builtin(sample_mask)]] arg: bool
// ) -> [[location(0)]] f32 { return 1.0; }
auto* is_front =
Param("is_front", ty.bool_(),
ast::DecorationList{Builtin(ast::Builtin::kFrontFacing)});
Func("fs_main", ast::VariableList{is_front}, ty.f32(), {Return(1.0f)},
auto* arg = Param("arg", ty.bool_(),
ast::DecorationList{
Builtin(Source{{12, 34}}, ast::Builtin::kSampleMask)});
Func("fs_main", ast::VariableList{arg}, ty.f32(), {Return(1.0f)},
ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
{Location(0)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(sample_mask) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Struct_Fail) {
// struct MyInputs {
// [[builtin(sample_index)]] m: f32;
// };
// [[stage(fragment)]]
// fn fragShader(is_front: MyInputs) -> [[location(0)]] f32 { return 1.0; }
auto* s = Structure(
"MyInputs", {Member("m", ty.f32(),
ast::DecorationList{Builtin(
Source{{12, 34}}, ast::Builtin::kSampleIndex)})});
Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(sample_index) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Fail) {
// [[stage(fragment)]]
// fn fs_main(
// [[builtin(sample_index)]] arg: bool
// ) -> [[location(0)]] f32 { return 1.0; }
auto* arg = Param("arg", ty.bool_(),
ast::DecorationList{
Builtin(Source{{12, 34}}, ast::Builtin::kSampleIndex)});
Func("fs_main", ast::VariableList{arg}, ty.f32(), {Return(1.0f)},
ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
{Location(0)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(sample_index) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, FragmentBuiltin_Pass) {
// [[stage(fragment)]]
// fn fs_main(
// [[builtin(front_facing)]] ff : bool,
// [[builtin(sample_index)]] si: u32,
// [[builtin(sample_mask)]] sm : u32
// ) -> [[location(0)]] f32 { return 1.0; }
auto* ff = Param("ff", ty.bool_(),
ast::DecorationList{Builtin(ast::Builtin::kFrontFacing)});
auto* si = Param("si", ty.u32(),
ast::DecorationList{Builtin(ast::Builtin::kSampleIndex)});
auto* sm = Param("sm", ty.u32(),
ast::DecorationList{Builtin(ast::Builtin::kSampleMask)});
Func("fs_main", ast::VariableList{ff, si, sm}, ty.f32(), {Return(1.0f)},
ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
{Location(0)});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsBool_Pass) {
// struct MyInputs {
// [[builtin(front_facing)]] pos: bool;
TEST_F(ResolverBuiltinsValidationTest, FragmentBuiltinStruct_Pass) {
// Struct MyInputs {
// [[builtin(front_facing)]] ff: bool;
// [[builtin(sample_index)]] si: u32;
// [[builtin(sample_mask)]] sm : u32;
// };
// [[stage(fragment)]]
// fn fragShader(is_front: MyInputs) -> [[location(0)]] f32 { return 1.0; }
auto* s = Structure(
"MyInputs",
{Member("pos", ty.bool_(),
ast::DecorationList{Builtin(ast::Builtin::kFrontFacing)})});
{Member("ff", ty.bool_(),
ast::DecorationList{Builtin(ast::Builtin::kFrontFacing)}),
Member("si", ty.u32(),
ast::DecorationList{Builtin(ast::Builtin::kSampleIndex)}),
Member("sm", ty.u32(),
ast::DecorationList{Builtin(ast::Builtin::kSampleMask)})});
Func("fragShader", {Param("is_front", ty.Of(s))}, ty.f32(), {Return(1.0f)},
{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
EXPECT_TRUE(r()->Resolve()) << r()->error();
@ -65,7 +146,7 @@ TEST_F(ResolverBuiltinsValidationTest, FrontFacingParamIsNotBool_Fail) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error v-15001: front_facing builtin must be boolean");
"12:34 error: store type of builtin(front_facing) must be 'bool'");
}
TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsNotBool_Fail) {
@ -84,7 +165,7 @@ TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsNotBool_Fail) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error v-15001: front_facing builtin must be boolean");
"12:34 error: store type of builtin(front_facing) must be 'bool'");
}
TEST_F(ResolverBuiltinsValidationTest, Length_Float_Scalar) {

View File

@ -370,7 +370,7 @@ TEST_F(ResolverEntryPointValidationTest,
// fn main(param : Input) {}
auto* input = Structure(
"Input",
{Member("a", ty.f32(),
{Member("a", ty.u32(),
{Location(Source{{13, 43}}, 0),
Builtin(Source{{14, 52}}, ast::Builtin::kSampleIndex)})});
auto* param = Param("param", ty.Of(input));

View File

@ -121,6 +121,16 @@ bool IsValidationDisabled(const ast::DecorationList& decorations,
return false;
}
// Helper to stringify a pipeline IO decoration.
std::string deco_to_str(const ast::Decoration* deco) {
std::stringstream str;
if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
str << "builtin(" << builtin->value() << ")";
} else if (auto* location = deco->As<ast::LocationDecoration>()) {
str << "location(" << location->value() << ")";
}
return str.str();
}
} // namespace
Resolver::Resolver(ProgramBuilder* builder)
@ -891,22 +901,44 @@ bool Resolver::ValidateVariable(const VariableInfo* info) {
}
bool Resolver::ValidateParameter(const VariableInfo* info) {
auto* var = info->declaration;
for (auto* deco : var->decorations()) {
if (!ValidateVariable(info)) {
return false;
}
for (auto* deco : info->declaration->decorations()) {
if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
if (builtin->value() == ast::Builtin::kFrontFacing) {
auto* storage_type = info->type->UnwrapRef();
if (!(storage_type->Is<sem::Bool>())) {
diagnostics_.add_error("v-15001",
"front_facing builtin must be boolean",
deco->source());
return false;
}
if (!ValidateBuiltinDecoration(builtin, info->type)) {
return false;
}
}
}
return true;
}
return ValidateVariable(info);
bool Resolver::ValidateBuiltinDecoration(const ast::BuiltinDecoration* deco,
const sem::Type* storage_type) {
auto* type = storage_type->UnwrapRef();
switch (deco->value()) {
case ast::Builtin::kFrontFacing:
if (!type->Is<sem::Bool>()) {
diagnostics_.add_error(
"store type of " + deco_to_str(deco) + " must be 'bool'",
deco->source());
return false;
}
break;
case ast::Builtin::kSampleMask:
case ast::Builtin::kSampleIndex:
if (!type->Is<sem::U32>()) {
diagnostics_.add_error(
"store type of " + deco_to_str(deco) + " must be 'u32'",
deco->source());
return false;
}
break;
default:
break;
}
return true;
}
bool Resolver::ValidateFunction(const ast::Function* func,
@ -1014,16 +1046,7 @@ bool Resolver::ValidateEntryPoint(const ast::Function* func,
kParameter,
kReturnType,
};
// Helper to stringify a pipeline IO decoration.
auto deco_to_str = [](const ast::Decoration* deco) {
std::stringstream str;
if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
str << "builtin(" << builtin->value() << ")";
} else if (auto* location = deco->As<ast::LocationDecoration>()) {
str << "location(" << location->value() << ")";
}
return str.str();
};
// Inner lambda that is applied to a type and all of its members.
auto validate_entry_point_decorations_inner =
[&](const ast::DecorationList& decos, sem::Type* ty, Source source,
@ -3178,13 +3201,8 @@ bool Resolver::ValidateStructure(const sem::Struct* str) {
return false;
}
if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
if (builtin->value() == ast::Builtin::kFrontFacing) {
if (!(member->Type()->Is<sem::Bool>())) {
diagnostics_.add_error("v-15001",
"front_facing builtin must be boolean",
deco->source());
return false;
}
if (!ValidateBuiltinDecoration(builtin, member->Type())) {
return false;
}
}
}

View File

@ -266,6 +266,8 @@ class Resolver {
bool ValidateAtomic(const ast::Atomic* a, const sem::Atomic* s);
bool ValidateAtomicUses();
bool ValidateAssignment(const ast::AssignmentStatement* a);
bool ValidateBuiltinDecoration(const ast::BuiltinDecoration* deco,
const sem::Type* storage_type);
bool ValidateCallStatement(ast::CallStatement* stmt);
bool ValidateEntryPoint(const ast::Function* func, const FunctionInfo* info);
bool ValidateFunction(const ast::Function* func, const FunctionInfo* info);