diff --git a/src/tint/ast/disable_validation_attribute.cc b/src/tint/ast/disable_validation_attribute.cc index 465a0ec7fe..eff1c1ff4a 100644 --- a/src/tint/ast/disable_validation_attribute.cc +++ b/src/tint/ast/disable_validation_attribute.cc @@ -45,6 +45,8 @@ std::string DisableValidationAttribute::InternalName() const { return "disable_validation__ignore_invalid_pointer_argument"; case DisabledValidation::kIgnorePointerAliasing: return "disable_validation__ignore_pointer_aliasing"; + case DisabledValidation::kIgnoreStructMemberLimit: + return "disable_validation__ignore_struct_member"; } return ""; } diff --git a/src/tint/ast/disable_validation_attribute.h b/src/tint/ast/disable_validation_attribute.h index f52b10717d..9614bc14b2 100644 --- a/src/tint/ast/disable_validation_attribute.h +++ b/src/tint/ast/disable_validation_attribute.h @@ -45,6 +45,8 @@ enum class DisabledValidation { /// When applied to a function declaration, the validator will not complain if multiple /// pointer arguments alias when that function is called. kIgnorePointerAliasing, + /// When applied to a struct, validation of max number of members is skipped. + kIgnoreStructMemberLimit, }; /// An internal attribute used to tell the validator to ignore specific diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc index 7471466cdd..42924387e4 100644 --- a/src/tint/resolver/resolver.cc +++ b/src/tint/resolver/resolver.cc @@ -3573,6 +3573,20 @@ type::Type* Resolver::Alias(const ast::Alias* alias) { } sem::Struct* Resolver::Structure(const ast::Struct* str) { + if (validator_.IsValidationEnabled(str->attributes, + ast::DisabledValidation::kIgnoreStructMemberLimit)) { + // Maximum number of members in a structure type + // https://gpuweb.github.io/gpuweb/wgsl/#limits + const size_t kMaxNumStructMembers = 16383; + if (str->members.Length() > kMaxNumStructMembers) { + AddError("struct '" + builder_->Symbols().NameFor(str->name->symbol) + "' has " + + std::to_string(str->members.Length()) + " members, maximum is " + + std::to_string(kMaxNumStructMembers), + str->source); + return nullptr; + } + } + if (!validator_.NoDuplicateAttributes(str->attributes)) { return nullptr; } diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc index c9ce479a76..25155fff46 100644 --- a/src/tint/resolver/resolver_test.cc +++ b/src/tint/resolver/resolver_test.cc @@ -2394,5 +2394,45 @@ TEST_F(ResolverTest, ScopeDepth_IfElseChain) { #endif // !defined(NDEBUG) +const size_t kMaxNumStructMembers = 16383; + +TEST_F(ResolverTest, MaxNumStructMembers_Valid) { + utils::Vector members; + members.Reserve(kMaxNumStructMembers); + for (size_t i = 0; i < kMaxNumStructMembers; ++i) { + members.Push(Member("m" + std::to_string(i), ty.i32())); + } + Structure("S", std::move(members)); + EXPECT_TRUE(r()->Resolve()) << r()->error(); +} + +TEST_F(ResolverTest, MaxNumStructMembers_Invalid) { + utils::Vector members; + members.Reserve(kMaxNumStructMembers + 1); + for (size_t i = 0; i < kMaxNumStructMembers + 1; ++i) { + members.Push(Member("m" + std::to_string(i), ty.i32())); + } + Structure(Source{{12, 34}}, "S", std::move(members)); + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), "12:34 error: struct 'S' has 16384 members, maximum is 16383"); +} + +TEST_F(ResolverTest, MaxNumStructMembers_WithIgnoreStructMemberLimit_Valid) { + utils::Vector members; + members.Reserve(kMaxNumStructMembers); + for (size_t i = 0; i < kMaxNumStructMembers; ++i) { + members.Push(Member("m" + std::to_string(i), ty.i32())); + } + + // Add 10 more members, but we set the limit to be ignored on the struct + for (size_t i = 0; i < 10; ++i) { + members.Push(Member("ignored" + std::to_string(i), ty.i32())); + } + + Structure("S", std::move(members), + utils::Vector{Disable(ast::DisabledValidation::kIgnoreStructMemberLimit)}); + EXPECT_TRUE(r()->Resolve()) << r()->error(); +} + } // namespace } // namespace tint::resolver diff --git a/src/tint/transform/pad_structs.cc b/src/tint/transform/pad_structs.cc index 79bc63158b..8516aa3d98 100644 --- a/src/tint/transform/pad_structs.cc +++ b/src/tint/transform/pad_structs.cc @@ -18,6 +18,7 @@ #include #include +#include "src/tint/ast/disable_validation_attribute.h" #include "src/tint/ast/parameter.h" #include "src/tint/program_builder.h" #include "src/tint/sem/call.h" @@ -36,7 +37,10 @@ void CreatePadding(utils::Vector* new_members, utils::Hashset* padding_members, ProgramBuilder* b, uint32_t bytes) { - for (uint32_t i = 0; i < bytes / 4u; ++i) { + const size_t count = bytes / 4u; + padding_members->Reserve(count); + new_members->Reserve(count); + for (uint32_t i = 0; i < count; ++i) { auto name = b->Symbols().New("pad"); auto* member = b->Member(name, b->ty.u32()); padding_members->Add(member); @@ -99,8 +103,15 @@ Transform::ApplyResult PadStructs::Apply(const Program* src, const DataMap&, Dat if (offset < struct_size && !has_runtime_sized_array) { CreatePadding(&new_members, &padding_members, ctx.dst, struct_size - offset); } - auto* new_struct = - b.create(ctx.Clone(ast_str->name), std::move(new_members), utils::Empty); + + utils::Vector struct_attribs; + if (!padding_members.IsEmpty()) { + struct_attribs = + utils::Vector{b.Disable(ast::DisabledValidation::kIgnoreStructMemberLimit)}; + } + + auto* new_struct = b.create(ctx.Clone(ast_str->name), std::move(new_members), + std::move(struct_attribs)); replaced_structs[ast_str] = new_struct; return new_struct; }); diff --git a/src/tint/transform/pad_structs_test.cc b/src/tint/transform/pad_structs_test.cc index 6b1a70f7fb..0826467738 100644 --- a/src/tint/transform/pad_structs_test.cc +++ b/src/tint/transform/pad_structs_test.cc @@ -47,6 +47,7 @@ fn main() { } )"; auto* expect = R"( +@internal(disable_validation__ignore_struct_member) struct S { x : i32, pad : u32, @@ -81,6 +82,7 @@ fn main() { } )"; auto* expect = R"( +@internal(disable_validation__ignore_struct_member) struct S { x : i32, pad : u32, @@ -118,6 +120,7 @@ fn main() { } )"; auto* expect = R"( +@internal(disable_validation__ignore_struct_member) struct S { x : i32, pad : u32, @@ -158,6 +161,7 @@ fn main() { } )"; auto* expect = R"( +@internal(disable_validation__ignore_struct_member) struct S { x : i32, pad : u32, @@ -198,6 +202,7 @@ fn main() { } )"; auto* expect = R"( +@internal(disable_validation__ignore_struct_member) struct S { x : i32, pad : u32, @@ -273,6 +278,7 @@ fn main() { } )"; auto* expect = R"( +@internal(disable_validation__ignore_struct_member) struct S { a : i32, pad : u32, @@ -320,6 +326,7 @@ fn main() { } )"; auto* expect = R"( +@internal(disable_validation__ignore_struct_member) struct S { a : i32, pad : u32, @@ -367,6 +374,7 @@ fn main() { } )"; auto* expect = R"( +@internal(disable_validation__ignore_struct_member) struct S { a : i32, pad : u32, @@ -501,6 +509,7 @@ struct T { b : i32, } +@internal(disable_validation__ignore_struct_member) struct S { a : vec4, b : array, @@ -537,6 +546,7 @@ fn main() { } )"; auto* expect = R"( +@internal(disable_validation__ignore_struct_member) struct S { a : f32, pad : u32, @@ -573,6 +583,7 @@ fn main() { } )"; auto* expect = R"( +@internal(disable_validation__ignore_struct_member) struct S { a : f32, pad : u32,