diff --git a/samples/main.cc b/samples/main.cc index 6a5d80e88d..eab18f4dca 100644 --- a/samples/main.cc +++ b/samples/main.cc @@ -750,8 +750,6 @@ int main(int argc, const char** argv) { << "]:" << std::endl; std::cout << "\t\t resource_type = " << ResourceTypeToString(binding.resource_type) << std::endl; - std::cout << "\t\t min_buffer_binding_size = " - << binding.min_buffer_binding_size << std::endl; std::cout << "\t\t dim = " << TextureDimensionToString(binding.dim) << std::endl; std::cout << "\t\t sampled_kind = " diff --git a/src/BUILD.gn b/src/BUILD.gn index b5398bdd9f..baaf5aee3d 100644 --- a/src/BUILD.gn +++ b/src/BUILD.gn @@ -317,8 +317,12 @@ source_set("libtint_core_src") { "ast/struct_block_decoration.h", "ast/struct_member.cc", "ast/struct_member.h", + "ast/struct_member_align_decoration.cc", + "ast/struct_member_align_decoration.h", "ast/struct_member_offset_decoration.cc", "ast/struct_member_offset_decoration.h", + "ast/struct_member_size_decoration.cc", + "ast/struct_member_size_decoration.h", "ast/switch_statement.cc", "ast/switch_statement.h", "ast/type_constructor_expression.cc", @@ -367,11 +371,14 @@ source_set("libtint_core_src") { "resolver/resolver.cc", "resolver/resolver.h", "scope_stack.h", + "semantic/array.h", "semantic/call.h", + "semantic/call_target.h", "semantic/expression.h", "semantic/info.h", "semantic/intrinsic.h", "semantic/node.h", + "semantic/sem_array.cc", "semantic/sem_call.cc", "semantic/sem_call_target.cc", "semantic/sem_expression.cc", @@ -381,6 +388,7 @@ source_set("libtint_core_src") { "semantic/sem_member_accessor_expression.cc", "semantic/sem_node.cc", "semantic/sem_statement.cc", + "semantic/sem_struct.cc", "semantic/sem_variable.cc", "semantic/type_mappings.h", "source.cc", diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0ba3a57c73..006c202ff1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -132,8 +132,12 @@ set(TINT_LIB_SRCS ast/struct_block_decoration.h ast/struct_member.cc ast/struct_member.h + ast/struct_member_align_decoration.cc + ast/struct_member_align_decoration.h ast/struct_member_offset_decoration.cc ast/struct_member_offset_decoration.h + ast/struct_member_size_decoration.cc + ast/struct_member_size_decoration.h ast/switch_statement.cc ast/switch_statement.h ast/type_constructor_expression.cc @@ -182,11 +186,14 @@ set(TINT_LIB_SRCS resolver/resolver.cc resolver/resolver.h scope_stack.h + semantic/array.h semantic/call.h + semantic/call_target.h semantic/expression.h semantic/info.h semantic/intrinsic.h semantic/node.h + semantic/sem_array.cc semantic/sem_call.cc semantic/sem_call_target.cc semantic/sem_expression.cc @@ -196,6 +203,7 @@ set(TINT_LIB_SRCS semantic/sem_intrinsic.cc semantic/sem_node.cc semantic/sem_statement.cc + semantic/sem_struct.cc semantic/sem_variable.cc semantic/type_mappings.h source.cc @@ -436,7 +444,9 @@ if(${TINT_BUILD_TESTS}) ast/sint_literal_test.cc ast/stage_decoration_test.cc ast/stride_decoration_test.cc + ast/struct_member_align_decoration_test.cc ast/struct_member_offset_decoration_test.cc + ast/struct_member_size_decoration_test.cc ast/struct_member_test.cc ast/struct_test.cc ast/switch_statement_test.cc @@ -458,9 +468,11 @@ if(${TINT_BUILD_TESTS}) intrinsic_table_test.cc program_test.cc resolver/intrinsic_test.cc + resolver/is_storeable_test.cc resolver/resolver_test_helper.cc resolver/resolver_test_helper.h resolver/resolver_test.cc + resolver/struct_layout_test.cc resolver/validation_test.cc scope_stack_test.cc semantic/sem_intrinsic_test.cc diff --git a/src/ast/module_clone_test.cc b/src/ast/module_clone_test.cc index 39095fdf66..4e3fea63b7 100644 --- a/src/ast/module_clone_test.cc +++ b/src/ast/module_clone_test.cc @@ -28,9 +28,8 @@ TEST(ModuleCloneTest, Clone) { // See also fuzzers/tint_ast_clone_fuzzer.cc for further coverage of cloning. Source::File file("test.wgsl", R"([[block]] struct S { - [[offset(0)]] + [[size(4)]] m0 : u32; - [[offset(4)]] m1 : array; }; @@ -38,7 +37,7 @@ const c0 : i32 = 10; const c1 : bool = true; type t0 = [[stride(16)]] array>; -type t1 = [[stride(32)]] array>; +type t1 = array>; var g0 : u32 = 20u; var g1 : f32 = 123.0; diff --git a/src/ast/struct_member_align_decoration.cc b/src/ast/struct_member_align_decoration.cc new file mode 100644 index 0000000000..03a353c94b --- /dev/null +++ b/src/ast/struct_member_align_decoration.cc @@ -0,0 +1,46 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/ast/struct_member_align_decoration.h" + +#include "src/clone_context.h" +#include "src/program_builder.h" + +TINT_INSTANTIATE_TYPEINFO(tint::ast::StructMemberAlignDecoration); + +namespace tint { +namespace ast { + +StructMemberAlignDecoration::StructMemberAlignDecoration(const Source& source, + uint32_t align) + : Base(source), align_(align) {} + +StructMemberAlignDecoration::~StructMemberAlignDecoration() = default; + +void StructMemberAlignDecoration::to_str(const semantic::Info&, + std::ostream& out, + size_t indent) const { + make_indent(out, indent); + out << "align " << std::to_string(align_); +} + +StructMemberAlignDecoration* StructMemberAlignDecoration::Clone( + CloneContext* ctx) const { + // Clone arguments outside of create() call to have deterministic ordering + auto src = ctx->Clone(source()); + return ctx->dst->create(src, align_); +} + +} // namespace ast +} // namespace tint diff --git a/src/ast/struct_member_align_decoration.h b/src/ast/struct_member_align_decoration.h new file mode 100644 index 0000000000..e408e325dd --- /dev/null +++ b/src/ast/struct_member_align_decoration.h @@ -0,0 +1,59 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SRC_AST_STRUCT_MEMBER_ALIGN_DECORATION_H_ +#define SRC_AST_STRUCT_MEMBER_ALIGN_DECORATION_H_ + +#include + +#include "src/ast/decoration.h" + +namespace tint { +namespace ast { + +/// A struct member align decoration +class StructMemberAlignDecoration + : public Castable { + public: + /// constructor + /// @param source the source of this decoration + /// @param align the align value + StructMemberAlignDecoration(const Source& source, uint32_t align); + ~StructMemberAlignDecoration() override; + + /// @returns the align value + uint32_t align() const { return align_; } + + /// Outputs the decoration to the given stream + /// @param sem the semantic info for the program + /// @param out the stream to write to + /// @param indent number of spaces to indent the node when writing + void to_str(const semantic::Info& sem, + std::ostream& out, + size_t indent) const override; + + /// Clones this node and all transitive child nodes using the `CloneContext` + /// `ctx`. + /// @param ctx the clone context + /// @return the newly cloned node + StructMemberAlignDecoration* Clone(CloneContext* ctx) const override; + + private: + uint32_t const align_; +}; + +} // namespace ast +} // namespace tint + +#endif // SRC_AST_STRUCT_MEMBER_ALIGN_DECORATION_H_ diff --git a/src/ast/struct_member_align_decoration_test.cc b/src/ast/struct_member_align_decoration_test.cc new file mode 100644 index 0000000000..fc6e5d358d --- /dev/null +++ b/src/ast/struct_member_align_decoration_test.cc @@ -0,0 +1,37 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/ast/struct_member_align_decoration.h" + +#include "src/ast/test_helper.h" + +namespace tint { +namespace ast { +namespace { + +using StructMemberAlignDecorationTest = TestHelper; + +TEST_F(StructMemberAlignDecorationTest, Creation) { + auto* d = create(2); + EXPECT_EQ(2u, d->align()); +} + +TEST_F(StructMemberAlignDecorationTest, Is) { + auto* d = create(2); + EXPECT_TRUE(d->Is()); +} + +} // namespace +} // namespace ast +} // namespace tint diff --git a/src/ast/struct_member_offset_decoration.h b/src/ast/struct_member_offset_decoration.h index d793e7532b..3ab806d4ac 100644 --- a/src/ast/struct_member_offset_decoration.h +++ b/src/ast/struct_member_offset_decoration.h @@ -21,6 +21,8 @@ namespace tint { namespace ast { /// A struct member offset decoration +// [DEPRECATED] - Replaced with StructMemberAlignDecoration and +// StructMemberSizeDecoration class StructMemberOffsetDecoration : public Castable { public: diff --git a/src/ast/struct_member_size_decoration.cc b/src/ast/struct_member_size_decoration.cc new file mode 100644 index 0000000000..54e536a045 --- /dev/null +++ b/src/ast/struct_member_size_decoration.cc @@ -0,0 +1,46 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/ast/struct_member_size_decoration.h" + +#include "src/clone_context.h" +#include "src/program_builder.h" + +TINT_INSTANTIATE_TYPEINFO(tint::ast::StructMemberSizeDecoration); + +namespace tint { +namespace ast { + +StructMemberSizeDecoration::StructMemberSizeDecoration(const Source& source, + uint32_t size) + : Base(source), size_(size) {} + +StructMemberSizeDecoration::~StructMemberSizeDecoration() = default; + +void StructMemberSizeDecoration::to_str(const semantic::Info&, + std::ostream& out, + size_t indent) const { + make_indent(out, indent); + out << "size " << std::to_string(size_); +} + +StructMemberSizeDecoration* StructMemberSizeDecoration::Clone( + CloneContext* ctx) const { + // Clone arguments outside of create() call to have deterministic ordering + auto src = ctx->Clone(source()); + return ctx->dst->create(src, size_); +} + +} // namespace ast +} // namespace tint diff --git a/src/ast/struct_member_size_decoration.h b/src/ast/struct_member_size_decoration.h new file mode 100644 index 0000000000..f99fd500ce --- /dev/null +++ b/src/ast/struct_member_size_decoration.h @@ -0,0 +1,59 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SRC_AST_STRUCT_MEMBER_SIZE_DECORATION_H_ +#define SRC_AST_STRUCT_MEMBER_SIZE_DECORATION_H_ + +#include + +#include "src/ast/decoration.h" + +namespace tint { +namespace ast { + +/// A struct member size decoration +class StructMemberSizeDecoration + : public Castable { + public: + /// constructor + /// @param source the source of this decoration + /// @param size the size value + StructMemberSizeDecoration(const Source& source, uint32_t size); + ~StructMemberSizeDecoration() override; + + /// @returns the size value + uint32_t size() const { return size_; } + + /// Outputs the decoration to the given stream + /// @param sem the semantic info for the program + /// @param out the stream to write to + /// @param indent number of spaces to indent the node when writing + void to_str(const semantic::Info& sem, + std::ostream& out, + size_t indent) const override; + + /// Clones this node and all transitive child nodes using the `CloneContext` + /// `ctx`. + /// @param ctx the clone context + /// @return the newly cloned node + StructMemberSizeDecoration* Clone(CloneContext* ctx) const override; + + private: + uint32_t const size_; +}; + +} // namespace ast +} // namespace tint + +#endif // SRC_AST_STRUCT_MEMBER_SIZE_DECORATION_H_ diff --git a/src/ast/struct_member_size_decoration_test.cc b/src/ast/struct_member_size_decoration_test.cc new file mode 100644 index 0000000000..61427b72ff --- /dev/null +++ b/src/ast/struct_member_size_decoration_test.cc @@ -0,0 +1,37 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/ast/struct_member_size_decoration.h" + +#include "src/ast/test_helper.h" + +namespace tint { +namespace ast { +namespace { + +using StructMemberOffsetDecorationTest = TestHelper; + +TEST_F(StructMemberOffsetDecorationTest, Creation) { + auto* d = create(2); + EXPECT_EQ(2u, d->size()); +} + +TEST_F(StructMemberOffsetDecorationTest, Is) { + auto* d = create(2); + EXPECT_TRUE(d->Is()); +} + +} // namespace +} // namespace ast +} // namespace tint diff --git a/src/ast/struct_member_test.cc b/src/ast/struct_member_test.cc index 17c4425560..c760c85590 100644 --- a/src/ast/struct_member_test.cc +++ b/src/ast/struct_member_test.cc @@ -22,11 +22,11 @@ namespace { using StructMemberTest = TestHelper; TEST_F(StructMemberTest, Creation) { - auto* st = Member("a", ty.i32(), {MemberOffset(4)}); + auto* st = Member("a", ty.i32(), {MemberSize(4)}); EXPECT_EQ(st->symbol(), Symbol(1)); EXPECT_EQ(st->type(), ty.i32()); EXPECT_EQ(st->decorations().size(), 1u); - EXPECT_TRUE(st->decorations()[0]->Is()); + EXPECT_TRUE(st->decorations()[0]->Is()); EXPECT_EQ(st->source().range.begin.line, 0u); EXPECT_EQ(st->source().range.begin.column, 0u); EXPECT_EQ(st->source().range.end.line, 0u); @@ -68,14 +68,14 @@ TEST_F(StructMemberTest, Assert_NullDecoration) { EXPECT_FATAL_FAILURE( { ProgramBuilder b; - b.Member("a", b.ty.i32(), {b.MemberOffset(4), nullptr}); + b.Member("a", b.ty.i32(), {b.MemberSize(4), nullptr}); }, "internal compiler error"); } TEST_F(StructMemberTest, ToStr) { - auto* st = Member("a", ty.i32(), {MemberOffset(4)}); - EXPECT_EQ(str(st), "StructMember{[[ offset 4 ]] a: __i32}\n"); + auto* st = Member("a", ty.i32(), {MemberSize(4)}); + EXPECT_EQ(str(st), "StructMember{[[ size 4 ]] a: __i32}\n"); } TEST_F(StructMemberTest, ToStrNoDecorations) { diff --git a/src/inspector/inspector.cc b/src/inspector/inspector.cc index b049940d2d..b2e96f5b61 100644 --- a/src/inspector/inspector.cc +++ b/src/inspector/inspector.cc @@ -23,6 +23,7 @@ #include "src/ast/sint_literal.h" #include "src/ast/uint_literal.h" #include "src/semantic/function.h" +#include "src/semantic/struct.h" #include "src/semantic/variable.h" #include "src/type/access_control_type.h" #include "src/type/array_type.h" @@ -379,12 +380,18 @@ std::vector Inspector::GetUniformBufferResourceBindings( continue; } + auto* sem = program_->Sem().Get(str); + if (!sem) { + error_ = "Missing semantic information for structure " + + program_->Symbols().NameFor(str->symbol()); + continue; + } + ResourceBinding entry; entry.resource_type = ResourceBinding::ResourceType::kUniformBuffer; entry.bind_group = binding_info.group->value(); entry.binding = binding_info.binding->value(); - entry.min_buffer_binding_size = - decl->type()->MinBufferBindingSize(type::MemoryLayout::kUniformBuffer); + entry.size = sem->Size(); result.push_back(entry); } @@ -541,7 +548,15 @@ std::vector Inspector::GetStorageBufferResourceBindingsImpl( continue; } - if (!decl->type()->UnwrapIfNeeded()->Is()) { + auto* str = decl->type()->UnwrapIfNeeded()->As(); + if (!str) { + continue; + } + + auto* sem = program_->Sem().Get(str); + if (!sem) { + error_ = "Missing semantic information for structure " + + program_->Symbols().NameFor(str->symbol()); continue; } @@ -551,8 +566,7 @@ std::vector Inspector::GetStorageBufferResourceBindingsImpl( : ResourceBinding::ResourceType::kStorageBuffer; entry.bind_group = binding_info.group->value(); entry.binding = binding_info.binding->value(); - entry.min_buffer_binding_size = - decl->type()->MinBufferBindingSize(type::MemoryLayout::kStorageBuffer); + entry.size = sem->Size(); result.push_back(entry); } diff --git a/src/inspector/inspector.h b/src/inspector/inspector.h index d1c047ade1..ec91efb980 100644 --- a/src/inspector/inspector.h +++ b/src/inspector/inspector.h @@ -112,8 +112,8 @@ struct ResourceBinding { uint32_t bind_group; /// Identifier to identify this binding within the bind group uint32_t binding; - /// Minimum size required for this binding, in bytes, if defined. - uint64_t min_buffer_binding_size; + /// Size for this binding, in bytes, if defined. + uint64_t size; /// Dimensionality of this binding, if defined. TextureDimension dim; /// Kind of data being sampled, if defined. diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc index 5e7b95ec66..a48af7a2a5 100644 --- a/src/inspector/inspector_test.cc +++ b/src/inspector/inspector_test.cc @@ -190,22 +190,15 @@ class InspectorHelper : public ProgramBuilder { /// Generates a struct type /// @param name name for the type - /// @param members_info a vector of {type, offset} where each entry is the - /// type and offset of a member of the struct + /// @param member_types a vector of member types /// @param is_block whether or not to decorate as a Block /// @returns a struct type - type::Struct* MakeStructType( - const std::string& name, - std::vector> members_info, - bool is_block) { + type::Struct* MakeStructType(const std::string& name, + std::vector member_types, + bool is_block) { ast::StructMemberList members; - for (auto& member_info : members_info) { - type::Type* type; - uint32_t offset; - std::tie(type, offset) = member_info; - - members.push_back(Member(StructMemberName(members.size(), type), type, - {MemberOffset(offset)})); + for (auto* type : member_types) { + members.push_back(Member(StructMemberName(members.size(), type), type)); } ast::DecorationList decos; @@ -214,20 +207,21 @@ class InspectorHelper : public ProgramBuilder { } auto* str = create(members, decos); - return ty.struct_(name, str); + auto* str_ty = ty.struct_(name, str); + AST().AddConstructedType(str_ty); + return str_ty; } /// Generates types appropriate for using in an uniform buffer /// @param name name for the type - /// @param members_info a vector of {type, offset} where each entry is the - /// type and offset of a member of the struct + /// @param member_types a vector of member types /// @returns a tuple {struct type, access control type}, where the struct has /// the layout for an uniform buffer, and the control type wraps the /// struct. std::tuple MakeUniformBufferTypes( const std::string& name, - std::vector> members_info) { - auto* struct_type = MakeStructType(name, members_info, true); + std::vector member_types) { + auto* struct_type = MakeStructType(name, member_types, true); auto* access_type = create(ast::AccessControl::kReadOnly, struct_type); return {struct_type, std::move(access_type)}; @@ -235,15 +229,14 @@ class InspectorHelper : public ProgramBuilder { /// Generates types appropriate for using in a storage buffer /// @param name name for the type - /// @param members_info a vector of {type, offset} where each entry is the - /// type and offset of a member of the struct + /// @param member_types a vector of member types /// @returns a tuple {struct type, access control type}, where the struct has /// the layout for a storage buffer, and the control type wraps the /// struct. std::tuple MakeStorageBufferTypes( const std::string& name, - std::vector> members_info) { - auto* struct_type = MakeStructType(name, members_info, false); + std::vector member_types) { + auto* struct_type = MakeStructType(name, member_types, false); auto* access_type = create( ast::AccessControl::kReadWrite, struct_type); return {struct_type, std::move(access_type)}; @@ -251,16 +244,14 @@ class InspectorHelper : public ProgramBuilder { /// Generates types appropriate for using in a read-only storage buffer /// @param name name for the type - /// @param members_info a vector of {type, offset} where each entry is the - /// type and offset of a member of the struct + /// @param member_types a vector of member types /// @returns a tuple {struct type, access control type}, where the struct has /// the layout for a read-only storage buffer, and the control type /// wraps the struct. std::tuple - MakeReadOnlyStorageBufferTypes( - const std::string& name, - std::vector> members_info) { - auto* struct_type = MakeStructType(name, members_info, false); + MakeReadOnlyStorageBufferTypes(const std::string& name, + std::vector member_types) { + auto* struct_type = MakeStructType(name, member_types, false); auto* access_type = create(ast::AccessControl::kReadOnly, struct_type); return {struct_type, std::move(access_type)}; @@ -1404,21 +1395,21 @@ TEST_F(InspectorGetResourceBindingsTest, Simple) { type::Struct* ub_struct_type; type::AccessControl* ub_control_type; std::tie(ub_struct_type, ub_control_type) = - MakeUniformBufferTypes("ub_type", {{ty.i32(), 0}}); + MakeUniformBufferTypes("ub_type", {ty.i32()}); AddUniformBuffer("ub_var", ub_control_type, 0, 0); MakeStructVariableReferenceBodyFunction("ub_func", "ub_var", {{0, ty.i32()}}); type::Struct* sb_struct_type; type::AccessControl* sb_control_type; std::tie(sb_struct_type, sb_control_type) = - MakeStorageBufferTypes("sb_type", {{ty.i32(), 0}}); + MakeStorageBufferTypes("sb_type", {ty.i32()}); AddStorageBuffer("sb_var", sb_control_type, 1, 0); MakeStructVariableReferenceBodyFunction("sb_func", "sb_var", {{0, ty.i32()}}); type::Struct* ro_struct_type; type::AccessControl* ro_control_type; std::tie(ro_struct_type, ro_control_type) = - MakeReadOnlyStorageBufferTypes("ro_type", {{ty.i32(), 0}}); + MakeReadOnlyStorageBufferTypes("ro_type", {ty.i32()}); AddStorageBuffer("ro_var", ro_control_type, 1, 1); MakeStructVariableReferenceBodyFunction("ro_func", "ro_var", {{0, ty.i32()}}); @@ -1499,7 +1490,7 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, NonEntryPointFunc) { type::Struct* foo_struct_type; type::AccessControl* foo_control_type; std::tie(foo_struct_type, foo_control_type) = - MakeUniformBufferTypes("foo_type", {{ty.i32(), 0}}); + MakeUniformBufferTypes("foo_type", {ty.i32()}); AddUniformBuffer("foo_ub", foo_control_type, 0, 0); MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", {{0, ty.i32()}}); @@ -1520,8 +1511,7 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, NonEntryPointFunc) { TEST_F(InspectorGetUniformBufferResourceBindingsTest, MissingBlockDeco) { ast::DecorationList decos; auto* str = create( - ast::StructMemberList{ - Member(StructMemberName(0, ty.i32()), ty.i32(), {MemberOffset(0)})}, + ast::StructMemberList{Member(StructMemberName(0, ty.i32()), ty.i32())}, decos); auto* foo_type = ty.struct_("foo_type", str); @@ -1546,7 +1536,7 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, Simple) { type::Struct* foo_struct_type; type::AccessControl* foo_control_type; std::tie(foo_struct_type, foo_control_type) = - MakeUniformBufferTypes("foo_type", {{ty.i32(), 0}}); + MakeUniformBufferTypes("foo_type", {ty.i32()}); AddUniformBuffer("foo_ub", foo_control_type, 0, 0); MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", {{0, ty.i32()}}); @@ -1567,14 +1557,14 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, Simple) { result[0].resource_type); EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(16u, result[0].min_buffer_binding_size); + EXPECT_EQ(4u, result[0].size); } TEST_F(InspectorGetUniformBufferResourceBindingsTest, MultipleMembers) { type::Struct* foo_struct_type; type::AccessControl* foo_control_type; - std::tie(foo_struct_type, foo_control_type) = MakeUniformBufferTypes( - "foo_type", {{ty.i32(), 0}, {ty.u32(), 4}, {ty.f32(), 8}}); + std::tie(foo_struct_type, foo_control_type) = + MakeUniformBufferTypes("foo_type", {ty.i32(), ty.u32(), ty.f32()}); AddUniformBuffer("foo_ub", foo_control_type, 0, 0); MakeStructVariableReferenceBodyFunction( @@ -1596,14 +1586,14 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, MultipleMembers) { result[0].resource_type); EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(16u, result[0].min_buffer_binding_size); + EXPECT_EQ(12u, result[0].size); } TEST_F(InspectorGetUniformBufferResourceBindingsTest, MultipleUniformBuffers) { type::Struct* ub_struct_type; type::AccessControl* ub_control_type; - std::tie(ub_struct_type, ub_control_type) = MakeUniformBufferTypes( - "ub_type", {{ty.i32(), 0}, {ty.u32(), 4}, {ty.f32(), 8}}); + std::tie(ub_struct_type, ub_control_type) = + MakeUniformBufferTypes("ub_type", {ty.i32(), ty.u32(), ty.f32()}); AddUniformBuffer("ub_foo", ub_control_type, 0, 0); AddUniformBuffer("ub_bar", ub_control_type, 0, 1); AddUniformBuffer("ub_baz", ub_control_type, 2, 0); @@ -1639,26 +1629,29 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, MultipleUniformBuffers) { result[0].resource_type); EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(16u, result[0].min_buffer_binding_size); + EXPECT_EQ(12u, result[0].size); EXPECT_EQ(ResourceBinding::ResourceType::kUniformBuffer, result[1].resource_type); EXPECT_EQ(0u, result[1].bind_group); EXPECT_EQ(1u, result[1].binding); - EXPECT_EQ(16u, result[1].min_buffer_binding_size); + EXPECT_EQ(12u, result[1].size); EXPECT_EQ(ResourceBinding::ResourceType::kUniformBuffer, result[2].resource_type); EXPECT_EQ(2u, result[2].bind_group); EXPECT_EQ(0u, result[2].binding); - EXPECT_EQ(16u, result[2].min_buffer_binding_size); + EXPECT_EQ(12u, result[2].size); } TEST_F(InspectorGetUniformBufferResourceBindingsTest, ContainingArray) { type::Struct* foo_struct_type; type::AccessControl* foo_control_type; - std::tie(foo_struct_type, foo_control_type) = MakeUniformBufferTypes( - "foo_type", {{ty.i32(), 0}, {u32_array_type(4), 4}}); + // TODO(bclayton) - This is not a legal structure layout for uniform buffer + // usage. Once crbug.com/tint/628 is implemented, this will fail validation + // and will need to be fixed. + std::tie(foo_struct_type, foo_control_type) = + MakeUniformBufferTypes("foo_type", {ty.i32(), u32_array_type(4)}); AddUniformBuffer("foo_ub", foo_control_type, 0, 0); MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", {{0, ty.i32()}}); @@ -1679,14 +1672,14 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, ContainingArray) { result[0].resource_type); EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(32u, result[0].min_buffer_binding_size); + EXPECT_EQ(20u, result[0].size); } TEST_F(InspectorGetStorageBufferResourceBindingsTest, Simple) { type::Struct* foo_struct_type; type::AccessControl* foo_control_type; std::tie(foo_struct_type, foo_control_type) = - MakeStorageBufferTypes("foo_type", {{ty.i32(), 0}}); + MakeStorageBufferTypes("foo_type", {ty.i32()}); AddStorageBuffer("foo_sb", foo_control_type, 0, 0); MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}}); @@ -1707,14 +1700,14 @@ TEST_F(InspectorGetStorageBufferResourceBindingsTest, Simple) { result[0].resource_type); EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(4u, result[0].min_buffer_binding_size); + EXPECT_EQ(4u, result[0].size); } TEST_F(InspectorGetStorageBufferResourceBindingsTest, MultipleMembers) { type::Struct* foo_struct_type; type::AccessControl* foo_control_type; - std::tie(foo_struct_type, foo_control_type) = MakeStorageBufferTypes( - "foo_type", {{ty.i32(), 0}, {ty.u32(), 4}, {ty.f32(), 8}}); + std::tie(foo_struct_type, foo_control_type) = + MakeStorageBufferTypes("foo_type", {ty.i32(), ty.u32(), ty.f32()}); AddStorageBuffer("foo_sb", foo_control_type, 0, 0); MakeStructVariableReferenceBodyFunction( @@ -1736,14 +1729,14 @@ TEST_F(InspectorGetStorageBufferResourceBindingsTest, MultipleMembers) { result[0].resource_type); EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(12u, result[0].min_buffer_binding_size); + EXPECT_EQ(12u, result[0].size); } TEST_F(InspectorGetStorageBufferResourceBindingsTest, MultipleStorageBuffers) { type::Struct* sb_struct_type; type::AccessControl* sb_control_type; - std::tie(sb_struct_type, sb_control_type) = MakeStorageBufferTypes( - "sb_type", {{ty.i32(), 0}, {ty.u32(), 4}, {ty.f32(), 8}}); + std::tie(sb_struct_type, sb_control_type) = + MakeStorageBufferTypes("sb_type", {ty.i32(), ty.u32(), ty.f32()}); AddStorageBuffer("sb_foo", sb_control_type, 0, 0); AddStorageBuffer("sb_bar", sb_control_type, 0, 1); AddStorageBuffer("sb_baz", sb_control_type, 2, 0); @@ -1782,26 +1775,26 @@ TEST_F(InspectorGetStorageBufferResourceBindingsTest, MultipleStorageBuffers) { result[0].resource_type); EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(12u, result[0].min_buffer_binding_size); + EXPECT_EQ(12u, result[0].size); EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer, result[1].resource_type); EXPECT_EQ(0u, result[1].bind_group); EXPECT_EQ(1u, result[1].binding); - EXPECT_EQ(12u, result[1].min_buffer_binding_size); + EXPECT_EQ(12u, result[1].size); EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer, result[2].resource_type); EXPECT_EQ(2u, result[2].bind_group); EXPECT_EQ(0u, result[2].binding); - EXPECT_EQ(12u, result[2].min_buffer_binding_size); + EXPECT_EQ(12u, result[2].size); } TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingArray) { type::Struct* foo_struct_type; type::AccessControl* foo_control_type; - std::tie(foo_struct_type, foo_control_type) = MakeStorageBufferTypes( - "foo_type", {{ty.i32(), 0}, {u32_array_type(4), 4}}); + std::tie(foo_struct_type, foo_control_type) = + MakeStorageBufferTypes("foo_type", {ty.i32(), u32_array_type(4)}); AddStorageBuffer("foo_sb", foo_control_type, 0, 0); MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}}); @@ -1822,14 +1815,14 @@ TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingArray) { result[0].resource_type); EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(20u, result[0].min_buffer_binding_size); + EXPECT_EQ(20u, result[0].size); } TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingRuntimeArray) { type::Struct* foo_struct_type; type::AccessControl* foo_control_type; - std::tie(foo_struct_type, foo_control_type) = MakeStorageBufferTypes( - "foo_type", {{ty.i32(), 0}, {u32_array_type(0), 4}}); + std::tie(foo_struct_type, foo_control_type) = + MakeStorageBufferTypes("foo_type", {ty.i32(), u32_array_type(0)}); AddStorageBuffer("foo_sb", foo_control_type, 0, 0); MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}}); @@ -1850,14 +1843,14 @@ TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingRuntimeArray) { result[0].resource_type); EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(8u, result[0].min_buffer_binding_size); + EXPECT_EQ(8u, result[0].size); } TEST_F(InspectorGetStorageBufferResourceBindingsTest, SkipReadOnly) { type::Struct* foo_struct_type; type::AccessControl* foo_control_type; std::tie(foo_struct_type, foo_control_type) = - MakeReadOnlyStorageBufferTypes("foo_type", {{ty.i32(), 0}}); + MakeReadOnlyStorageBufferTypes("foo_type", {ty.i32()}); AddStorageBuffer("foo_sb", foo_control_type, 0, 0); MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}}); @@ -1879,7 +1872,7 @@ TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, Simple) { type::Struct* foo_struct_type; type::AccessControl* foo_control_type; std::tie(foo_struct_type, foo_control_type) = - MakeReadOnlyStorageBufferTypes("foo_type", {{ty.i32(), 0}}); + MakeReadOnlyStorageBufferTypes("foo_type", {ty.i32()}); AddStorageBuffer("foo_sb", foo_control_type, 0, 0); MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}}); @@ -1900,15 +1893,15 @@ TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, Simple) { result[0].resource_type); EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(4u, result[0].min_buffer_binding_size); + EXPECT_EQ(4u, result[0].size); } TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, MultipleStorageBuffers) { type::Struct* sb_struct_type; type::AccessControl* sb_control_type; - std::tie(sb_struct_type, sb_control_type) = MakeReadOnlyStorageBufferTypes( - "sb_type", {{ty.i32(), 0}, {ty.u32(), 4}, {ty.f32(), 8}}); + std::tie(sb_struct_type, sb_control_type) = + MakeReadOnlyStorageBufferTypes("sb_type", {ty.i32(), ty.u32(), ty.f32()}); AddStorageBuffer("sb_foo", sb_control_type, 0, 0); AddStorageBuffer("sb_bar", sb_control_type, 0, 1); AddStorageBuffer("sb_baz", sb_control_type, 2, 0); @@ -1947,26 +1940,26 @@ TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, result[0].resource_type); EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(12u, result[0].min_buffer_binding_size); + EXPECT_EQ(12u, result[0].size); EXPECT_EQ(ResourceBinding::ResourceType::kReadOnlyStorageBuffer, result[1].resource_type); EXPECT_EQ(0u, result[1].bind_group); EXPECT_EQ(1u, result[1].binding); - EXPECT_EQ(12u, result[1].min_buffer_binding_size); + EXPECT_EQ(12u, result[1].size); EXPECT_EQ(ResourceBinding::ResourceType::kReadOnlyStorageBuffer, result[2].resource_type); EXPECT_EQ(2u, result[2].bind_group); EXPECT_EQ(0u, result[2].binding); - EXPECT_EQ(12u, result[2].min_buffer_binding_size); + EXPECT_EQ(12u, result[2].size); } TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, ContainingArray) { type::Struct* foo_struct_type; type::AccessControl* foo_control_type; - std::tie(foo_struct_type, foo_control_type) = MakeReadOnlyStorageBufferTypes( - "foo_type", {{ty.i32(), 0}, {u32_array_type(4), 4}}); + std::tie(foo_struct_type, foo_control_type) = + MakeReadOnlyStorageBufferTypes("foo_type", {ty.i32(), u32_array_type(4)}); AddStorageBuffer("foo_sb", foo_control_type, 0, 0); MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}}); @@ -1987,15 +1980,15 @@ TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, ContainingArray) { result[0].resource_type); EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(20u, result[0].min_buffer_binding_size); + EXPECT_EQ(20u, result[0].size); } TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, ContainingRuntimeArray) { type::Struct* foo_struct_type; type::AccessControl* foo_control_type; - std::tie(foo_struct_type, foo_control_type) = MakeReadOnlyStorageBufferTypes( - "foo_type", {{ty.i32(), 0}, {u32_array_type(0), 4}}); + std::tie(foo_struct_type, foo_control_type) = + MakeReadOnlyStorageBufferTypes("foo_type", {ty.i32(), u32_array_type(0)}); AddStorageBuffer("foo_sb", foo_control_type, 0, 0); MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}}); @@ -2016,14 +2009,14 @@ TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, result[0].resource_type); EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(8u, result[0].min_buffer_binding_size); + EXPECT_EQ(8u, result[0].size); } TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, SkipNonReadOnly) { type::Struct* foo_struct_type; type::AccessControl* foo_control_type; std::tie(foo_struct_type, foo_control_type) = - MakeStorageBufferTypes("foo_type", {{ty.i32(), 0}}); + MakeStorageBufferTypes("foo_type", {ty.i32()}); AddStorageBuffer("foo_sb", foo_control_type, 0, 0); MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}}); diff --git a/src/program_builder.h b/src/program_builder.h index 941f93ea36..39832a213c 100644 --- a/src/program_builder.h +++ b/src/program_builder.h @@ -31,7 +31,9 @@ #include "src/ast/scalar_constructor_expression.h" #include "src/ast/sint_literal.h" #include "src/ast/stride_decoration.h" +#include "src/ast/struct_member_align_decoration.h" #include "src/ast/struct_member_offset_decoration.h" +#include "src/ast/struct_member_size_decoration.h" #include "src/ast/type_constructor_expression.h" #include "src/ast/uint_literal.h" #include "src/ast/variable_decl_statement.h" @@ -955,6 +957,36 @@ class ProgramBuilder { return create(source_, val); } + /// Creates a ast::StructMemberSizeDecoration + /// @param source the source information + /// @param val the size value + /// @returns the size decoration pointer + ast::StructMemberSizeDecoration* MemberSize(Source source, uint32_t val) { + return create(source, val); + } + + /// Creates a ast::StructMemberSizeDecoration + /// @param val the size value + /// @returns the size decoration pointer + ast::StructMemberSizeDecoration* MemberSize(uint32_t val) { + return create(source_, val); + } + + /// Creates a ast::StructMemberAlignDecoration + /// @param source the source information + /// @param val the align value + /// @returns the align decoration pointer + ast::StructMemberAlignDecoration* MemberAlign(Source source, uint32_t val) { + return create(source, val); + } + + /// Creates a ast::StructMemberAlignDecoration + /// @param val the align value + /// @returns the align decoration pointer + ast::StructMemberAlignDecoration* MemberAlign(uint32_t val) { + return create(source_, val); + } + /// Creates an ast::Function and registers it with the ast::Module. /// @param source the source information /// @param name the function name @@ -995,37 +1027,64 @@ class ProgramBuilder { return func; } + /// Creates a ast::Struct and type::Struct, registering the type::Struct with + /// the AST().ConstructedTypes(). + /// @param source the source information + /// @param name the struct name + /// @param members the struct members + /// @param decorations the optional struct decorations + /// @returns the struct type + type::Struct* Structure(const Source& source, + const std::string& name, + ast::StructMemberList members, + ast::DecorationList decorations = {}) { + auto* impl = + create(source, std::move(members), std::move(decorations)); + auto* type = ty.struct_(name, impl); + AST().AddConstructedType(type); + return type; + } + + /// Creates a ast::Struct and type::Struct, registering the type::Struct with + /// the AST().ConstructedTypes(). + /// @param name the struct name + /// @param members the struct members + /// @param decorations the optional struct decorations + /// @returns the struct type + type::Struct* Structure(const std::string& name, + ast::StructMemberList members, + ast::DecorationList decorations = {}) { + auto* impl = + create(std::move(members), std::move(decorations)); + auto* type = ty.struct_(name, impl); + AST().AddConstructedType(type); + return type; + } + /// Creates a ast::StructMember /// @param source the source information /// @param name the struct member name /// @param type the struct member type + /// @param decorations the optional struct member decorations /// @returns the struct member pointer ast::StructMember* Member(const Source& source, const std::string& name, - type::Type* type) { + type::Type* type, + ast::DecorationList decorations = {}) { return create(source, Symbols().Register(name), type, - ast::DecorationList{}); + std::move(decorations)); } /// Creates a ast::StructMember /// @param name the struct member name /// @param type the struct member type - /// @returns the struct member pointer - ast::StructMember* Member(const std::string& name, type::Type* type) { - return create(source_, Symbols().Register(name), type, - ast::DecorationList{}); - } - - /// Creates a ast::StructMember - /// @param name the struct member name - /// @param type the struct member type - /// @param decorations the struct member decorations + /// @param decorations the optional struct member decorations /// @returns the struct member pointer ast::StructMember* Member(const std::string& name, type::Type* type, - ast::DecorationList decorations) { + ast::DecorationList decorations = {}) { return create(source_, Symbols().Register(name), type, - decorations); + std::move(decorations)); } /// Creates a ast::StructMember with the given byte offset diff --git a/src/reader/spirv/parser_impl_convert_type_test.cc b/src/reader/spirv/parser_impl_convert_type_test.cc index e4d2fbaeee..ab21b4db01 100644 --- a/src/reader/spirv/parser_impl_convert_type_test.cc +++ b/src/reader/spirv/parser_impl_convert_type_test.cc @@ -331,8 +331,7 @@ TEST_F(SpvParserTest, ConvertType_RuntimeArray) { EXPECT_TRUE(arr_type->IsRuntimeArray()); ASSERT_NE(arr_type, nullptr); EXPECT_EQ(arr_type->size(), 0u); - EXPECT_EQ(arr_type->array_stride(), 0u); - EXPECT_FALSE(arr_type->has_array_stride()); + EXPECT_EQ(arr_type->decorations().size(), 0u); auto* elem_type = arr_type->type(); ASSERT_NE(elem_type, nullptr); EXPECT_TRUE(elem_type->Is()); @@ -365,8 +364,10 @@ TEST_F(SpvParserTest, ConvertType_RuntimeArray_ArrayStride_Valid) { auto* arr_type = type->As(); EXPECT_TRUE(arr_type->IsRuntimeArray()); ASSERT_NE(arr_type, nullptr); - EXPECT_EQ(arr_type->array_stride(), 64u); - EXPECT_TRUE(arr_type->has_array_stride()); + ASSERT_EQ(arr_type->decorations().size(), 1u); + auto* stride = arr_type->decorations()[0]; + ASSERT_TRUE(stride->Is()); + ASSERT_EQ(stride->As()->stride(), 64u); EXPECT_TRUE(p->error().empty()); } @@ -413,8 +414,7 @@ TEST_F(SpvParserTest, ConvertType_Array) { EXPECT_FALSE(arr_type->IsRuntimeArray()); ASSERT_NE(arr_type, nullptr); EXPECT_EQ(arr_type->size(), 42u); - EXPECT_EQ(arr_type->array_stride(), 0u); - EXPECT_FALSE(arr_type->has_array_stride()); + EXPECT_EQ(arr_type->decorations().size(), 0u); auto* elem_type = arr_type->type(); ASSERT_NE(elem_type, nullptr); EXPECT_TRUE(elem_type->Is()); @@ -499,8 +499,12 @@ TEST_F(SpvParserTest, ConvertType_ArrayStride_Valid) { EXPECT_TRUE(type->Is()); auto* arr_type = type->As(); ASSERT_NE(arr_type, nullptr); - ASSERT_EQ(arr_type->array_stride(), 8u); - EXPECT_TRUE(arr_type->has_array_stride()); + + ASSERT_EQ(arr_type->decorations().size(), 1u); + auto* stride = arr_type->decorations()[0]; + ASSERT_TRUE(stride->Is()); + ASSERT_EQ(stride->As()->stride(), 8u); + EXPECT_TRUE(p->error().empty()); } diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc index b025f44dbc..34fc690546 100644 --- a/src/reader/wgsl/parser_impl.cc +++ b/src/reader/wgsl/parser_impl.cc @@ -104,7 +104,9 @@ const char kBuiltinDecoration[] = "builtin"; const char kConstantIdDecoration[] = "constant_id"; const char kGroupDecoration[] = "group"; const char kLocationDecoration[] = "location"; -const char kOffsetDecoration[] = "offset"; +const char kOffsetDecoration[] = "offset"; // DEPRECATED +const char kSizeDecoration[] = "size"; +const char kAlignDecoration[] = "align"; const char kSetDecoration[] = "set"; const char kStageDecoration[] = "stage"; const char kStrideDecoration[] = "stride"; @@ -115,11 +117,12 @@ bool is_decoration(Token t) { return false; auto s = t.to_str(); - return s == kAccessDecoration || s == kBindingDecoration || - s == kBlockDecoration || s == kBuiltinDecoration || - s == kConstantIdDecoration || s == kLocationDecoration || + return s == kAccessDecoration || s == kAlignDecoration || + s == kBindingDecoration || s == kBlockDecoration || + s == kBuiltinDecoration || s == kConstantIdDecoration || + s == kGroupDecoration || s == kLocationDecoration || s == kOffsetDecoration || s == kSetDecoration || - s == kGroupDecoration || s == kStageDecoration || + s == kSizeDecoration || s == kStageDecoration || s == kStrideDecoration || s == kWorkgroupSizeDecoration; } @@ -2919,6 +2922,28 @@ Maybe ParserImpl::decoration() { }); } + if (s == kSizeDecoration) { + const char* use = "size decoration"; + return expect_paren_block(use, [&]() -> Result { + auto val = expect_positive_sint(use); + if (val.errored) + return Failure::kErrored; + + return create(t.source(), val.value); + }); + } + + if (s == kAlignDecoration) { + const char* use = "align decoration"; + return expect_paren_block(use, [&]() -> Result { + auto val = expect_positive_sint(use); + if (val.errored) + return Failure::kErrored; + + return create(t.source(), val.value); + }); + } + if (s == kConstantIdDecoration) { const char* use = "constant_id decoration"; return expect_paren_block(use, [&]() -> Result { diff --git a/src/reader/wgsl/parser_impl_error_msg_test.cc b/src/reader/wgsl/parser_impl_error_msg_test.cc index 3fae7f63fa..00cd58f915 100644 --- a/src/reader/wgsl/parser_impl_error_msg_test.cc +++ b/src/reader/wgsl/parser_impl_error_msg_test.cc @@ -704,35 +704,64 @@ TEST_F(ParserImplErrorTest, GlobalDeclStructMemberMissingSemicolon) { " ^\n"); } -TEST_F(ParserImplErrorTest, GlobalDeclStructMemberOffsetMissingLParen) { - EXPECT("struct S { [[offset 1)]] i : i32; };", - "test.wgsl:1:21 error: expected '(' for offset decoration\n" - "struct S { [[offset 1)]] i : i32; };\n" - " ^\n"); +TEST_F(ParserImplErrorTest, GlobalDeclStructMemberAlignMissingLParen) { + EXPECT("struct S { [[align 1)]] i : i32; };", + "test.wgsl:1:20 error: expected '(' for align decoration\n" + "struct S { [[align 1)]] i : i32; };\n" + " ^\n"); } -TEST_F(ParserImplErrorTest, GlobalDeclStructMemberOffsetMissingRParen) { - EXPECT("struct S { [[offset(1]] i : i32; };", - "test.wgsl:1:22 error: expected ')' for offset decoration\n" - "struct S { [[offset(1]] i : i32; };\n" - " ^^\n"); -} - -TEST_F(ParserImplErrorTest, GlobalDeclStructMemberOffsetInvaldValue) { - EXPECT("struct S { [[offset(x)]] i : i32; };", - "test.wgsl:1:21 error: expected signed integer literal for offset " - "decoration\n" - "struct S { [[offset(x)]] i : i32; };\n" - " ^\n"); -} - -TEST_F(ParserImplErrorTest, GlobalDeclStructMemberOffsetNegativeValue) { - EXPECT("struct S { [[offset(-2)]] i : i32; };", - "test.wgsl:1:21 error: offset decoration must be positive\n" - "struct S { [[offset(-2)]] i : i32; };\n" +TEST_F(ParserImplErrorTest, GlobalDeclStructMemberAlignMissingRParen) { + EXPECT("struct S { [[align(1]] i : i32; };", + "test.wgsl:1:21 error: expected ')' for align decoration\n" + "struct S { [[align(1]] i : i32; };\n" " ^^\n"); } +TEST_F(ParserImplErrorTest, GlobalDeclStructMemberAlignInvaldValue) { + EXPECT("struct S { [[align(x)]] i : i32; };", + "test.wgsl:1:20 error: expected signed integer literal for align " + "decoration\n" + "struct S { [[align(x)]] i : i32; };\n" + " ^\n"); +} + +TEST_F(ParserImplErrorTest, GlobalDeclStructMemberAlignNegativeValue) { + EXPECT("struct S { [[align(-2)]] i : i32; };", + "test.wgsl:1:20 error: align decoration must be positive\n" + "struct S { [[align(-2)]] i : i32; };\n" + " ^^\n"); +} + +TEST_F(ParserImplErrorTest, GlobalDeclStructMemberSizeMissingLParen) { + EXPECT("struct S { [[size 1)]] i : i32; };", + "test.wgsl:1:19 error: expected '(' for size decoration\n" + "struct S { [[size 1)]] i : i32; };\n" + " ^\n"); +} + +TEST_F(ParserImplErrorTest, GlobalDeclStructMemberSizeMissingRParen) { + EXPECT("struct S { [[size(1]] i : i32; };", + "test.wgsl:1:20 error: expected ')' for size decoration\n" + "struct S { [[size(1]] i : i32; };\n" + " ^^\n"); +} + +TEST_F(ParserImplErrorTest, GlobalDeclStructMemberSizeInvaldValue) { + EXPECT("struct S { [[size(x)]] i : i32; };", + "test.wgsl:1:19 error: expected signed integer literal for size " + "decoration\n" + "struct S { [[size(x)]] i : i32; };\n" + " ^\n"); +} + +TEST_F(ParserImplErrorTest, GlobalDeclStructMemberSizeNegativeValue) { + EXPECT("struct S { [[size(-2)]] i : i32; };", + "test.wgsl:1:19 error: size decoration must be positive\n" + "struct S { [[size(-2)]] i : i32; };\n" + " ^^\n"); +} + TEST_F(ParserImplErrorTest, GlobalDeclTypeAliasMissingIdentifier) { EXPECT("type 1 = f32;", "test.wgsl:1:6 error: expected identifier for type alias\n" diff --git a/src/reader/wgsl/parser_impl_global_decl_test.cc b/src/reader/wgsl/parser_impl_global_decl_test.cc index 08a7a5021d..de3d47169a 100644 --- a/src/reader/wgsl/parser_impl_global_decl_test.cc +++ b/src/reader/wgsl/parser_impl_global_decl_test.cc @@ -173,8 +173,7 @@ TEST_F(ParserImplTest, GlobalDecl_ParsesStruct) { } TEST_F(ParserImplTest, GlobalDecl_Struct_WithStride) { - auto p = - parser("struct A { [[offset(0)]] data: [[stride(4)]] array; };"); + auto p = parser("struct A { data: [[stride(4)]] array; };"); p->expect_global_decl(); ASSERT_FALSE(p->has_error()) << p->error(); @@ -194,12 +193,15 @@ TEST_F(ParserImplTest, GlobalDecl_Struct_WithStride) { const auto* ty = str->impl()->members()[0]->type(); ASSERT_TRUE(ty->Is()); const auto* arr = ty->As(); - EXPECT_TRUE(arr->has_array_stride()); - EXPECT_EQ(arr->array_stride(), 4u); + + ASSERT_EQ(arr->decorations().size(), 1u); + auto* stride = arr->decorations()[0]; + ASSERT_TRUE(stride->Is()); + ASSERT_EQ(stride->As()->stride(), 4u); } TEST_F(ParserImplTest, GlobalDecl_Struct_WithDecoration) { - auto p = parser("[[block]] struct A { [[offset(0)]] data: f32; };"); + auto p = parser("[[block]] struct A { data: f32; };"); p->expect_global_decl(); ASSERT_FALSE(p->has_error()) << p->error(); diff --git a/src/reader/wgsl/parser_impl_struct_body_decl_test.cc b/src/reader/wgsl/parser_impl_struct_body_decl_test.cc index c7f588b48b..16bcf4b13c 100644 --- a/src/reader/wgsl/parser_impl_struct_body_decl_test.cc +++ b/src/reader/wgsl/parser_impl_struct_body_decl_test.cc @@ -44,16 +44,28 @@ TEST_F(ParserImplTest, StructBodyDecl_ParsesEmpty) { ASSERT_EQ(m.value.size(), 0u); } -TEST_F(ParserImplTest, StructBodyDecl_InvalidMember) { +TEST_F(ParserImplTest, StructBodyDecl_InvalidAlign) { auto p = parser(R"( { - [[offset(nan)]] a : i32; + [[align(nan)]] a : i32; })"); auto m = p->expect_struct_body_decl(); ASSERT_TRUE(p->has_error()); ASSERT_TRUE(m.errored); EXPECT_EQ(p->error(), - "3:12: expected signed integer literal for offset decoration"); + "3:11: expected signed integer literal for align decoration"); +} + +TEST_F(ParserImplTest, StructBodyDecl_InvalidSize) { + auto p = parser(R"( +{ + [[size(nan)]] a : i32; +})"); + auto m = p->expect_struct_body_decl(); + ASSERT_TRUE(p->has_error()); + ASSERT_TRUE(m.errored); + EXPECT_EQ(p->error(), + "3:10: expected signed integer literal for size decoration"); } TEST_F(ParserImplTest, StructBodyDecl_MissingClosingBracket) { diff --git a/src/reader/wgsl/parser_impl_struct_decl_test.cc b/src/reader/wgsl/parser_impl_struct_decl_test.cc index 23a3dd05d8..869baa2b6c 100644 --- a/src/reader/wgsl/parser_impl_struct_decl_test.cc +++ b/src/reader/wgsl/parser_impl_struct_decl_test.cc @@ -24,7 +24,7 @@ TEST_F(ParserImplTest, StructDecl_Parses) { auto p = parser(R"( struct S { a : i32; - [[offset(4)]] b : f32; + b : f32; })"); auto decos = p->decoration_list(); EXPECT_FALSE(decos.errored); diff --git a/src/reader/wgsl/parser_impl_struct_member_decoration_decl_test.cc b/src/reader/wgsl/parser_impl_struct_member_decoration_decl_test.cc index 533a5bbdf1..107c358d77 100644 --- a/src/reader/wgsl/parser_impl_struct_member_decoration_decl_test.cc +++ b/src/reader/wgsl/parser_impl_struct_member_decoration_decl_test.cc @@ -39,7 +39,7 @@ TEST_F(ParserImplTest, DecorationDecl_EmptyBlock) { } TEST_F(ParserImplTest, DecorationDecl_Single) { - auto p = parser("[[offset(4)]]"); + auto p = parser("[[size(4)]]"); auto decos = p->decoration_list(); EXPECT_FALSE(p->has_error()); EXPECT_FALSE(decos.errored); @@ -47,26 +47,35 @@ TEST_F(ParserImplTest, DecorationDecl_Single) { ASSERT_EQ(decos.value.size(), 1u); auto* deco = decos.value[0]->As(); ASSERT_NE(deco, nullptr); - EXPECT_TRUE(deco->Is()); + EXPECT_TRUE(deco->Is()); } TEST_F(ParserImplTest, DecorationDecl_InvalidDecoration) { - auto p = parser("[[offset(nan)]]"); + auto p = parser("[[size(nan)]]"); auto decos = p->decoration_list(); EXPECT_TRUE(p->has_error()) << p->error(); EXPECT_TRUE(decos.errored); EXPECT_FALSE(decos.matched); EXPECT_EQ(p->error(), - "1:10: expected signed integer literal for offset decoration"); + "1:8: expected signed integer literal for size decoration"); } TEST_F(ParserImplTest, DecorationDecl_MissingClose) { - auto p = parser("[[offset(4)"); + auto p = parser("[[size(4)"); auto decos = p->decoration_list(); EXPECT_TRUE(p->has_error()) << p->error(); EXPECT_TRUE(decos.errored); EXPECT_FALSE(decos.matched); - EXPECT_EQ(p->error(), "1:12: expected ']]' for decoration list"); + EXPECT_EQ(p->error(), "1:10: expected ']]' for decoration list"); +} + +TEST_F(ParserImplTest, StructMemberDecorationDecl_SizeMissingClose) { + auto p = parser("[[size(4)"); + auto decos = p->decoration_list(); + EXPECT_TRUE(p->has_error()) << p->error(); + EXPECT_TRUE(decos.errored); + EXPECT_FALSE(decos.matched); + EXPECT_EQ(p->error(), "1:10: expected ']]' for decoration list"); } } // namespace diff --git a/src/reader/wgsl/parser_impl_struct_member_test.cc b/src/reader/wgsl/parser_impl_struct_member_test.cc index 911c3be118..68ce72aaa9 100644 --- a/src/reader/wgsl/parser_impl_struct_member_test.cc +++ b/src/reader/wgsl/parser_impl_struct_member_test.cc @@ -23,7 +23,7 @@ TEST_F(ParserImplTest, StructMember_Parses) { auto p = parser("a : i32;"); auto& builder = p->builder(); - auto* i32 = builder.create(); + auto* i32 = builder.ty.i32(); auto decos = p->decoration_list(); EXPECT_FALSE(decos.errored); @@ -45,11 +45,11 @@ TEST_F(ParserImplTest, StructMember_Parses) { ASSERT_EQ(m->source().range.end.column, 2u); } -TEST_F(ParserImplTest, StructMember_ParsesWithDecoration) { +TEST_F(ParserImplTest, StructMember_ParsesWithOffsetDecoration_DEPRECATED) { auto p = parser("[[offset(2)]] a : i32;"); auto& builder = p->builder(); - auto* i32 = builder.create(); + auto* i32 = builder.ty.i32(); auto decos = p->decoration_list(); EXPECT_FALSE(decos.errored); @@ -75,12 +75,99 @@ TEST_F(ParserImplTest, StructMember_ParsesWithDecoration) { ASSERT_EQ(m->source().range.end.column, 16u); } -TEST_F(ParserImplTest, StructMember_ParsesWithMultipleDecorations) { - auto p = parser(R"([[offset(2)]] -[[offset(4)]] a : i32;)"); +TEST_F(ParserImplTest, StructMember_ParsesWithAlignDecoration) { + auto p = parser("[[align(2)]] a : i32;"); auto& builder = p->builder(); - auto* i32 = builder.create(); + auto* i32 = builder.ty.i32(); + + auto decos = p->decoration_list(); + EXPECT_FALSE(decos.errored); + EXPECT_TRUE(decos.matched); + EXPECT_EQ(decos.value.size(), 1u); + + auto m = p->expect_struct_member(decos.value); + ASSERT_FALSE(p->has_error()); + ASSERT_FALSE(m.errored); + ASSERT_NE(m.value, nullptr); + + EXPECT_EQ(m->symbol(), builder.Symbols().Get("a")); + EXPECT_EQ(m->type(), i32); + EXPECT_EQ(m->decorations().size(), 1u); + EXPECT_TRUE(m->decorations()[0]->Is()); + EXPECT_EQ( + m->decorations()[0]->As()->align(), 2u); + + ASSERT_EQ(m->source().range.begin.line, 1u); + ASSERT_EQ(m->source().range.begin.column, 14u); + ASSERT_EQ(m->source().range.end.line, 1u); + ASSERT_EQ(m->source().range.end.column, 15u); +} + +TEST_F(ParserImplTest, StructMember_ParsesWithSizeDecoration) { + auto p = parser("[[size(2)]] a : i32;"); + + auto& builder = p->builder(); + auto* i32 = builder.ty.i32(); + + auto decos = p->decoration_list(); + EXPECT_FALSE(decos.errored); + EXPECT_TRUE(decos.matched); + EXPECT_EQ(decos.value.size(), 1u); + + auto m = p->expect_struct_member(decos.value); + ASSERT_FALSE(p->has_error()); + ASSERT_FALSE(m.errored); + ASSERT_NE(m.value, nullptr); + + EXPECT_EQ(m->symbol(), builder.Symbols().Get("a")); + EXPECT_EQ(m->type(), i32); + EXPECT_EQ(m->decorations().size(), 1u); + EXPECT_TRUE(m->decorations()[0]->Is()); + EXPECT_EQ(m->decorations()[0]->As()->size(), + 2u); + + ASSERT_EQ(m->source().range.begin.line, 1u); + ASSERT_EQ(m->source().range.begin.column, 13u); + ASSERT_EQ(m->source().range.end.line, 1u); + ASSERT_EQ(m->source().range.end.column, 14u); +} + +TEST_F(ParserImplTest, StructMember_ParsesWithDecoration) { + auto p = parser("[[size(2)]] a : i32;"); + + auto& builder = p->builder(); + auto* i32 = builder.ty.i32(); + + auto decos = p->decoration_list(); + EXPECT_FALSE(decos.errored); + EXPECT_TRUE(decos.matched); + EXPECT_EQ(decos.value.size(), 1u); + + auto m = p->expect_struct_member(decos.value); + ASSERT_FALSE(p->has_error()); + ASSERT_FALSE(m.errored); + ASSERT_NE(m.value, nullptr); + + EXPECT_EQ(m->symbol(), builder.Symbols().Get("a")); + EXPECT_EQ(m->type(), i32); + EXPECT_EQ(m->decorations().size(), 1u); + EXPECT_TRUE(m->decorations()[0]->Is()); + EXPECT_EQ(m->decorations()[0]->As()->size(), + 2u); + + ASSERT_EQ(m->source().range.begin.line, 1u); + ASSERT_EQ(m->source().range.begin.column, 13u); + ASSERT_EQ(m->source().range.end.line, 1u); + ASSERT_EQ(m->source().range.end.column, 14u); +} + +TEST_F(ParserImplTest, StructMember_ParsesWithMultipleDecorations) { + auto p = parser(R"([[size(2)]] +[[align(4)]] a : i32;)"); + + auto& builder = p->builder(); + auto* i32 = builder.ty.i32(); auto decos = p->decoration_list(); EXPECT_FALSE(decos.errored); @@ -95,23 +182,21 @@ TEST_F(ParserImplTest, StructMember_ParsesWithMultipleDecorations) { EXPECT_EQ(m->symbol(), builder.Symbols().Get("a")); EXPECT_EQ(m->type(), i32); EXPECT_EQ(m->decorations().size(), 2u); - EXPECT_TRUE(m->decorations()[0]->Is()); + EXPECT_TRUE(m->decorations()[0]->Is()); + EXPECT_EQ(m->decorations()[0]->As()->size(), + 2u); + EXPECT_TRUE(m->decorations()[1]->Is()); EXPECT_EQ( - m->decorations()[0]->As()->offset(), - 2u); - EXPECT_TRUE(m->decorations()[1]->Is()); - EXPECT_EQ( - m->decorations()[1]->As()->offset(), - 4u); + m->decorations()[1]->As()->align(), 4u); ASSERT_EQ(m->source().range.begin.line, 2u); - ASSERT_EQ(m->source().range.begin.column, 15u); + ASSERT_EQ(m->source().range.begin.column, 14u); ASSERT_EQ(m->source().range.end.line, 2u); - ASSERT_EQ(m->source().range.end.column, 16u); + ASSERT_EQ(m->source().range.end.column, 15u); } TEST_F(ParserImplTest, StructMember_InvalidDecoration) { - auto p = parser("[[offset(nan)]] a : i32;"); + auto p = parser("[[size(nan)]] a : i32;"); auto decos = p->decoration_list(); EXPECT_TRUE(decos.errored); EXPECT_FALSE(decos.matched); @@ -122,11 +207,11 @@ TEST_F(ParserImplTest, StructMember_InvalidDecoration) { ASSERT_TRUE(p->has_error()); EXPECT_EQ(p->error(), - "1:10: expected signed integer literal for offset decoration"); + "1:8: expected signed integer literal for size decoration"); } TEST_F(ParserImplTest, StructMember_InvalidVariable) { - auto p = parser("[[offset(4)]] a : B;"); + auto p = parser("[[size(4)]] a : B;"); auto decos = p->decoration_list(); EXPECT_FALSE(decos.errored); EXPECT_TRUE(decos.matched); @@ -135,7 +220,7 @@ TEST_F(ParserImplTest, StructMember_InvalidVariable) { ASSERT_TRUE(p->has_error()); ASSERT_TRUE(m.errored); ASSERT_EQ(m.value, nullptr); - EXPECT_EQ(p->error(), "1:19: unknown constructed type 'B'"); + EXPECT_EQ(p->error(), "1:17: unknown constructed type 'B'"); } TEST_F(ParserImplTest, StructMember_MissingSemicolon) { diff --git a/src/reader/wgsl/parser_impl_type_decl_test.cc b/src/reader/wgsl/parser_impl_type_decl_test.cc index ab4d9f2896..995c41160b 100644 --- a/src/reader/wgsl/parser_impl_type_decl_test.cc +++ b/src/reader/wgsl/parser_impl_type_decl_test.cc @@ -344,7 +344,7 @@ TEST_F(ParserImplTest, TypeDecl_Array) { ASSERT_FALSE(a->IsRuntimeArray()); ASSERT_EQ(a->size(), 5u); ASSERT_TRUE(a->type()->Is()); - ASSERT_FALSE(a->has_array_stride()); + EXPECT_EQ(a->decorations().size(), 0u); } TEST_F(ParserImplTest, TypeDecl_Array_Stride) { @@ -360,8 +360,11 @@ TEST_F(ParserImplTest, TypeDecl_Array_Stride) { ASSERT_FALSE(a->IsRuntimeArray()); ASSERT_EQ(a->size(), 5u); ASSERT_TRUE(a->type()->Is()); - ASSERT_TRUE(a->has_array_stride()); - EXPECT_EQ(a->array_stride(), 16u); + + ASSERT_EQ(a->decorations().size(), 1u); + auto* stride = a->decorations()[0]; + ASSERT_TRUE(stride->Is()); + ASSERT_EQ(stride->As()->stride(), 16u); } TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Stride) { @@ -376,8 +379,11 @@ TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Stride) { auto* a = t->As(); ASSERT_TRUE(a->IsRuntimeArray()); ASSERT_TRUE(a->type()->Is()); - ASSERT_TRUE(a->has_array_stride()); - EXPECT_EQ(a->array_stride(), 16u); + + ASSERT_EQ(a->decorations().size(), 1u); + auto* stride = a->decorations()[0]; + ASSERT_TRUE(stride->Is()); + ASSERT_EQ(stride->As()->stride(), 16u); } TEST_F(ParserImplTest, TypeDecl_Array_MultipleDecorations_OneBlock) { diff --git a/src/resolver/is_storeable_test.cc b/src/resolver/is_storeable_test.cc new file mode 100644 index 0000000000..e11d5396bd --- /dev/null +++ b/src/resolver/is_storeable_test.cc @@ -0,0 +1,137 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/resolver/resolver.h" + +#include "gmock/gmock.h" +#include "src/resolver/resolver_test_helper.h" + +namespace tint { +namespace resolver { +namespace { + +using ResolverIsStorableTest = ResolverTest; + +TEST_F(ResolverIsStorableTest, Void) { + auto* void_ty = ty.void_(); + + EXPECT_FALSE(r()->IsStorable(void_ty)); +} + +TEST_F(ResolverIsStorableTest, Scalar) { + auto* bool_ = ty.bool_(); + auto* i32 = ty.i32(); + auto* u32 = ty.u32(); + auto* f32 = ty.f32(); + + EXPECT_TRUE(r()->IsStorable(bool_)); + EXPECT_TRUE(r()->IsStorable(i32)); + EXPECT_TRUE(r()->IsStorable(u32)); + EXPECT_TRUE(r()->IsStorable(f32)); +} + +TEST_F(ResolverIsStorableTest, Vector) { + auto* vec2_i32 = ty.vec2(); + auto* vec3_i32 = ty.vec3(); + auto* vec4_i32 = ty.vec4(); + auto* vec2_u32 = ty.vec2(); + auto* vec3_u32 = ty.vec3(); + auto* vec4_u32 = ty.vec4(); + auto* vec2_f32 = ty.vec2(); + auto* vec3_f32 = ty.vec3(); + auto* vec4_f32 = ty.vec4(); + + EXPECT_TRUE(r()->IsStorable(vec2_i32)); + EXPECT_TRUE(r()->IsStorable(vec3_i32)); + EXPECT_TRUE(r()->IsStorable(vec4_i32)); + EXPECT_TRUE(r()->IsStorable(vec2_u32)); + EXPECT_TRUE(r()->IsStorable(vec3_u32)); + EXPECT_TRUE(r()->IsStorable(vec4_u32)); + EXPECT_TRUE(r()->IsStorable(vec2_f32)); + EXPECT_TRUE(r()->IsStorable(vec3_f32)); + EXPECT_TRUE(r()->IsStorable(vec4_f32)); +} + +TEST_F(ResolverIsStorableTest, Matrix) { + auto* mat2x2 = ty.mat2x2(); + auto* mat2x3 = ty.mat2x3(); + auto* mat2x4 = ty.mat2x4(); + auto* mat3x2 = ty.mat3x2(); + auto* mat3x3 = ty.mat3x3(); + auto* mat3x4 = ty.mat3x4(); + auto* mat4x2 = ty.mat4x2(); + auto* mat4x3 = ty.mat4x3(); + auto* mat4x4 = ty.mat4x4(); + + EXPECT_TRUE(r()->IsStorable(mat2x2)); + EXPECT_TRUE(r()->IsStorable(mat2x3)); + EXPECT_TRUE(r()->IsStorable(mat2x4)); + EXPECT_TRUE(r()->IsStorable(mat3x2)); + EXPECT_TRUE(r()->IsStorable(mat3x3)); + EXPECT_TRUE(r()->IsStorable(mat3x4)); + EXPECT_TRUE(r()->IsStorable(mat4x2)); + EXPECT_TRUE(r()->IsStorable(mat4x3)); + EXPECT_TRUE(r()->IsStorable(mat4x4)); +} + +TEST_F(ResolverIsStorableTest, Pointer) { + auto* ptr_ty = ty.pointer(ast::StorageClass::kPrivate); + + EXPECT_FALSE(r()->IsStorable(ptr_ty)); +} + +TEST_F(ResolverIsStorableTest, AliasVoid) { + auto* alias = ty.alias("myalias", ty.void_()); + + EXPECT_FALSE(r()->IsStorable(alias)); +} + +TEST_F(ResolverIsStorableTest, AliasI32) { + auto* alias = ty.alias("myalias", ty.i32()); + + EXPECT_TRUE(r()->IsStorable(alias)); +} + +TEST_F(ResolverIsStorableTest, ArraySizedOfStorable) { + auto* arr = ty.array(ty.i32(), 5); + + EXPECT_TRUE(r()->IsStorable(arr)); +} + +TEST_F(ResolverIsStorableTest, ArrayUnsizedOfStorable) { + auto* arr = ty.array(); + + EXPECT_TRUE(r()->IsStorable(arr)); +} + +TEST_F(ResolverIsStorableTest, Struct_AllMembersStorable) { + ast::StructMemberList members{Member("a", ty.i32()), Member("b", ty.f32())}; + auto* s = create(Source{}, members, ast::DecorationList{}); + auto* s_ty = ty.struct_("mystruct", s); + + EXPECT_TRUE(r()->IsStorable(s_ty)); +} + +TEST_F(ResolverIsStorableTest, Struct_SomeMembersNonStorable) { + auto* ptr_ty = ty.pointer(ast::StorageClass::kPrivate); + ast::StructMemberList members{Member("a", ty.i32()), Member("b", ptr_ty)}; + auto* s = create(Source{}, members, ast::DecorationList{}); + auto* s_ty = ty.struct_("mystruct", s); + + EXPECT_FALSE(r()->IsStorable(s_ty)); +} + +} // namespace +} // namespace resolver +} // namespace tint diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc index 24ea098f4f..ddec567c60 100644 --- a/src/resolver/resolver.cc +++ b/src/resolver/resolver.cc @@ -30,11 +30,14 @@ #include "src/ast/switch_statement.h" #include "src/ast/unary_op_expression.h" #include "src/ast/variable_decl_statement.h" +#include "src/semantic/array.h" #include "src/semantic/call.h" #include "src/semantic/function.h" #include "src/semantic/member_accessor_expression.h" #include "src/semantic/statement.h" +#include "src/semantic/struct.h" #include "src/semantic/variable.h" +#include "src/type/access_control_type.h" namespace tint { namespace resolver { @@ -59,6 +62,20 @@ class ScopedAssignment { T old_value_; }; +/// Rounds `value` to the next multiple of `alignment` +/// Assumes `alignment` is positive. +template +T RoundUp(T alignment, T value) { + return ((value + alignment - 1) / alignment) * alignment; +} + +/// Returns true if `value` is a power-of-two. +/// Assumes `alignment` is positive. +template +bool IsPowerOfTwo(T value) { + return (value & (value - 1)) == 0; +} + } // namespace Resolver::Resolver(ProgramBuilder* builder) @@ -98,7 +115,47 @@ bool Resolver::Resolve() { return result; } +bool Resolver::IsStorable(type::Type* type) { + if (type == nullptr) { + return false; + } + if (type->is_scalar() || type->Is() || + type->Is()) { + return true; + } + if (type::Array* array_type = type->As()) { + return IsStorable(array_type->type()); + } + if (type::Struct* struct_type = type->As()) { + for (const auto* member : struct_type->impl()->members()) { + if (!IsStorable(member->type())) { + return false; + } + } + return true; + } + if (type::Alias* alias_type = type->As()) { + return IsStorable(alias_type->type()); + } + return false; +} + bool Resolver::ResolveInternal() { + for (auto* ty : builder_->Types()) { + if (auto* str = ty->As()) { + if (!Structure(str)) { + return false; + } + continue; + } + if (auto* arr = ty->As()) { + if (!Array(arr)) { + return false; + } + continue; + } + } + for (auto* var : builder_->AST().GlobalVariables()) { variable_stack_.set_global(var->symbol(), CreateVariableInfo(var)); @@ -962,6 +1019,204 @@ void Resolver::CreateSemanticNodes() const { } } +bool Resolver::DefaultAlignAndSize(type::Type* ty, + uint32_t& align, + uint32_t& size) { + static constexpr uint32_t vector_size[] = { + /* padding */ 0, + /* padding */ 0, + /*vec2*/ 8, + /*vec3*/ 12, + /*vec4*/ 16, + }; + static constexpr uint32_t vector_align[] = { + /* padding */ 0, + /* padding */ 0, + /*vec2*/ 8, + /*vec3*/ 16, + /*vec4*/ 16, + }; + + ty = ty->UnwrapAliasIfNeeded(); + if (ty->is_scalar()) { + // Note: Also captures booleans, but these are not host-sharable. + align = 4; + size = 4; + return true; + } else if (auto* vec = ty->As()) { + if (vec->size() < 2 || vec->size() > 4) { + TINT_UNREACHABLE(diagnostics_) + << "Invalid vector size: vec" << vec->size(); + return false; + } + align = vector_align[vec->size()]; + size = vector_size[vec->size()]; + return true; + } else if (auto* mat = ty->As()) { + if (mat->columns() < 2 || mat->columns() > 4 || mat->rows() < 2 || + mat->rows() > 4) { + TINT_UNREACHABLE(diagnostics_) + << "Invalid matrix size: mat" << mat->columns() << "x" << mat->rows(); + return false; + } + align = vector_align[mat->rows()]; + size = vector_align[mat->rows()] * mat->columns(); + return true; + } else if (auto* s = ty->As()) { + if (auto* sem = Structure(s)) { + align = sem->Align(); + size = sem->Size(); + return true; + } + return false; + } else if (auto* arr = ty->As()) { + if (auto* sem = Array(arr)) { + align = sem->Align(); + size = sem->Size(); + return true; + } + return false; + } + TINT_UNREACHABLE(diagnostics_) << "Invalid type " << ty->TypeInfo().name; + return false; +} + +const semantic::Array* Resolver::Array(type::Array* arr) { + if (auto* sem = builder_->Sem().Get(arr)) { + // Semantic info already constructed for this array type + return sem; + } + + // First check the element type is legal + auto* el_ty = arr->type(); + if (!IsStorable(el_ty)) { + builder_->Diagnostics().add_error( + std::string(el_ty->FriendlyName(builder_->Symbols())) + + " cannot be used as an element type of an array"); + return nullptr; + } + + auto create_semantic = [&](uint32_t stride) -> semantic::Array* { + uint32_t el_align = 0; + uint32_t el_size = 0; + if (!DefaultAlignAndSize(arr->type(), el_align, el_size)) { + return nullptr; + } + + auto align = el_align; + // WebGPU requires runtime arrays have at least one element, but the AST + // records an element count of 0 for it. + auto size = std::max(arr->size(), 1) * stride; + auto* sem = builder_->create(arr, align, size, stride); + builder_->Sem().Add(arr, sem); + return sem; + }; + + // Look for explicit stride via [[stride(n)]] decoration + for (auto* deco : arr->decorations()) { + if (auto* stride = deco->As()) { + return create_semantic(stride->stride()); + } + } + + // Calculate implicit stride + uint32_t el_align = 0; + uint32_t el_size = 0; + if (!DefaultAlignAndSize(el_ty, el_align, el_size)) { + return nullptr; + } + + return create_semantic(RoundUp(el_align, el_size)); +} + +const semantic::Struct* Resolver::Structure(type::Struct* str) { + if (auto* sem = builder_->Sem().Get(str)) { + // Semantic info already constructed for this structure type + return sem; + } + + semantic::StructMemberList sem_members; + sem_members.reserve(str->impl()->members().size()); + + // Calculate the effective size and alignment of each field, and the overall + // size of the structure. + // For size, use the size attribute if provided, otherwise use the default + // size for the type. + // For alignment, use the alignment attribute if provided, otherwise use the + // default alignment for the member type. + // Diagnostic errors are raised if a basic rule is violated. + // Validation of storage-class rules requires analysing the actual variable + // usage of the structure, and so is performed as part of the variable + // validation. + // TODO(crbug.com/tint/628): Actually implement storage-class validation. + uint32_t struct_size = 0; + uint32_t struct_align = 1; + + for (auto* member : str->impl()->members()) { + // First check the member type is legal + if (!IsStorable(member->type())) { + builder_->Diagnostics().add_error( + std::string(member->type()->FriendlyName(builder_->Symbols())) + + " cannot be used as the type of a structure member"); + return nullptr; + } + + uint32_t offset = struct_size; + uint32_t align = 0; + uint32_t size = 0; + if (!DefaultAlignAndSize(member->type(), align, size)) { + return nullptr; + } + + for (auto* deco : member->decorations()) { + if (auto* o = deco->As()) { + // [DEPRECATED] + if (o->offset() < struct_size) { + diagnostics_.add_error("offsets must be in ascending order", + o->source()); + return nullptr; + } + offset = o->offset(); + align = 1; + } else if (auto* a = deco->As()) { + if (a->align() <= 0 || !IsPowerOfTwo(a->align())) { + diagnostics_.add_error( + "align value must be a positive, power-of-two integer", + a->source()); + return nullptr; + } + align = a->align(); + } else if (auto* s = deco->As()) { + if (s->size() < size) { + diagnostics_.add_error( + "size must be at least as big as the type's size (" + + std::to_string(size) + ")", + s->source()); + return nullptr; + } + size = s->size(); + } + } + + offset = RoundUp(align, offset); + + auto* sem_member = + builder_->create(member, offset, size); + builder_->Sem().Add(member, sem_member); + sem_members.emplace_back(sem_member); + + struct_size = offset + size; + struct_align = std::max(struct_align, align); + } + + struct_size = RoundUp(struct_align, struct_size); + + auto* sem = builder_->create(str, std::move(sem_members), + struct_align, struct_size); + builder_->Sem().Add(str, sem); + return sem; +} + template bool Resolver::BlockScope(BlockInfo::Type type, F&& callback) { BlockInfo block_info(type, current_block_); diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h index b2e5bae6b6..485bff8749 100644 --- a/src/resolver/resolver.h +++ b/src/resolver/resolver.h @@ -42,8 +42,12 @@ class UnaryOpExpression; class Variable; } // namespace ast namespace semantic { +class Array; class Statement; } // namespace semantic +namespace type { +class Struct; +} // namespace type namespace resolver { @@ -63,6 +67,10 @@ class Resolver { /// @returns true if the resolver was successful bool Resolve(); + /// @param type the given type + /// @returns true if the given type is storable. + static bool IsStorable(type::Type* type); + private: /// Structure holding semantic information about a variable. /// Used to build the semantic::Variable nodes at the end of resolving. @@ -141,41 +149,6 @@ class Resolver { /// @returns true on success, false on error bool ResolveInternal(); - /// Resolves functions - /// @param funcs the functions to check - /// @returns true on success, false on error - bool Functions(const ast::FunctionList& funcs); - /// Resolves a function. Requires all dependency - /// (callee) functions to have DetermineFunction() called on them first. - /// @param func the function to check - /// @returns true on success, false on error - bool Function(ast::Function* func); - /// Resolves a block statement - /// @param stmt the block statement - /// @returns true if determination was successful - bool BlockStatement(const ast::BlockStatement* stmt); - /// Resolves the list of statements - /// @param stmts the statements to resolve - /// @returns true on success, false on error - bool Statements(const ast::StatementList& stmts); - /// Resolves a statement - /// @param stmt the statement to check - /// @returns true on success, false on error - bool Statement(ast::Statement* stmt); - /// Resolves an expression list - /// @param list the expression list to check - /// @returns true on success, false on error - bool Expressions(const ast::ExpressionList& list); - /// Resolves an expression - /// @param expr the expression to check - /// @returns true on success, false on error - bool Expression(ast::Expression* expr); - /// Resolves the storage class for variables. This assumes that it is only - /// called for things in function scope, not module scope. - /// @param stmt the statement to check - /// @returns false on error - bool VariableStorageClass(ast::Statement* stmt); - /// Creates the nodes and adds them to the semantic::Info mappings of the /// ProgramBuilder. void CreateSemanticNodes() const; @@ -195,20 +168,43 @@ class Resolver { void set_referenced_from_function_if_needed(VariableInfo* var, bool local); - bool ArrayAccessor(ast::ArrayAccessorExpression* expr); - bool Binary(ast::BinaryExpression* expr); - bool Bitcast(ast::BitcastExpression* expr); - bool Call(ast::CallExpression* expr); - bool CaseStatement(ast::CaseStatement* stmt); - bool Constructor(ast::ConstructorExpression* expr); - bool Identifier(ast::IdentifierExpression* expr); - bool IfStatement(ast::IfStatement* stmt); - bool IntrinsicCall(ast::CallExpression* call, - semantic::IntrinsicType intrinsic_type); - bool MemberAccessor(ast::MemberAccessorExpression* expr); - bool UnaryOp(ast::UnaryOpExpression* expr); + // AST and Type traversal methods + // Each return true on success, false on failure. + bool ArrayAccessor(ast::ArrayAccessorExpression*); + bool Binary(ast::BinaryExpression*); + bool Bitcast(ast::BitcastExpression*); + bool BlockStatement(const ast::BlockStatement*); + bool Call(ast::CallExpression*); + bool CaseStatement(ast::CaseStatement*); + bool Constructor(ast::ConstructorExpression*); + bool Expression(ast::Expression*); + bool Expressions(const ast::ExpressionList&); + bool Function(ast::Function*); + bool Functions(const ast::FunctionList&); + bool Identifier(ast::IdentifierExpression*); + bool IfStatement(ast::IfStatement*); + bool IntrinsicCall(ast::CallExpression*, semantic::IntrinsicType); + bool MemberAccessor(ast::MemberAccessorExpression*); + bool Statement(ast::Statement*); + bool Statements(const ast::StatementList&); + bool UnaryOp(ast::UnaryOpExpression*); + bool VariableDeclStatement(const ast::VariableDeclStatement*); + bool VariableStorageClass(ast::Statement*); - bool VariableDeclStatement(const ast::VariableDeclStatement* stmt); + /// @returns the semantic information for the array `arr`, building it if it + /// hasn't been constructed already. If an error is raised, nullptr is + /// returned. + const semantic::Array* Array(type::Array*); + + /// @returns the semantic information for the structure `str`, building it if + /// it hasn't been constructed already. If an error is raised, nullptr is + /// returned. + const semantic::Struct* Structure(type::Struct* str); + + /// @param align the output default alignment in bytes for the type `ty` + /// @param size the output default size in bytes for the type `ty` + /// @returns true on success, false on error + bool DefaultAlignAndSize(type::Type* ty, uint32_t& align, uint32_t& size); VariableInfo* CreateVariableInfo(ast::Variable*); diff --git a/src/resolver/struct_layout_test.cc b/src/resolver/struct_layout_test.cc new file mode 100644 index 0000000000..53dd37b60a --- /dev/null +++ b/src/resolver/struct_layout_test.cc @@ -0,0 +1,333 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/resolver/resolver.h" + +#include "gmock/gmock.h" +#include "src/resolver/resolver_test_helper.h" +#include "src/semantic/struct.h" + +namespace tint { +namespace resolver { +namespace { + +using ResolverStructLayoutTest = ResolverTest; + +TEST_F(ResolverStructLayoutTest, Scalars) { + auto* s = Structure("S", { + Member("a", ty.f32()), + Member("b", ty.u32()), + Member("c", ty.i32()), + }); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_EQ(sem->Size(), 12u); + EXPECT_EQ(sem->Align(), 4u); + ASSERT_EQ(sem->Members().size(), 3u); + EXPECT_EQ(sem->Members()[0]->Offset(), 0u); + EXPECT_EQ(sem->Members()[0]->Size(), 4u); + EXPECT_EQ(sem->Members()[1]->Offset(), 4u); + EXPECT_EQ(sem->Members()[1]->Size(), 4u); + EXPECT_EQ(sem->Members()[2]->Offset(), 8u); + EXPECT_EQ(sem->Members()[2]->Size(), 4u); +} + +TEST_F(ResolverStructLayoutTest, Alias) { + auto* s = Structure("S", { + Member("a", ty.alias("a", ty.f32())), + Member("b", ty.alias("b", ty.f32())), + }); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_EQ(sem->Size(), 8u); + EXPECT_EQ(sem->Align(), 4u); + ASSERT_EQ(sem->Members().size(), 2u); + EXPECT_EQ(sem->Members()[0]->Offset(), 0u); + EXPECT_EQ(sem->Members()[0]->Size(), 4u); + EXPECT_EQ(sem->Members()[1]->Offset(), 4u); + EXPECT_EQ(sem->Members()[1]->Size(), 4u); +} + +TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayStaticSize) { + auto* s = Structure("S", { + Member("a", ty.array()), + Member("b", ty.array()), + Member("c", ty.array()), + }); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_EQ(sem->Size(), 36u); + EXPECT_EQ(sem->Align(), 4u); + ASSERT_EQ(sem->Members().size(), 3u); + EXPECT_EQ(sem->Members()[0]->Offset(), 0u); + EXPECT_EQ(sem->Members()[0]->Size(), 12u); + EXPECT_EQ(sem->Members()[1]->Offset(), 12u); + EXPECT_EQ(sem->Members()[1]->Size(), 20u); + EXPECT_EQ(sem->Members()[2]->Offset(), 32u); + EXPECT_EQ(sem->Members()[2]->Size(), 4u); +} + +TEST_F(ResolverStructLayoutTest, ExplicitStrideArrayStaticSize) { + auto* s = Structure("S", { + Member("a", ty.array(/*stride*/ 8)), + Member("b", ty.array(/*stride*/ 16)), + Member("c", ty.array(/*stride*/ 32)), + }); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_EQ(sem->Size(), 136u); + EXPECT_EQ(sem->Align(), 4u); + ASSERT_EQ(sem->Members().size(), 3u); + EXPECT_EQ(sem->Members()[0]->Offset(), 0u); + EXPECT_EQ(sem->Members()[0]->Size(), 24u); + EXPECT_EQ(sem->Members()[1]->Offset(), 24u); + EXPECT_EQ(sem->Members()[1]->Size(), 80u); + EXPECT_EQ(sem->Members()[2]->Offset(), 104u); + EXPECT_EQ(sem->Members()[2]->Size(), 32u); +} + +TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayRuntimeSized) { + auto* s = Structure("S", { + Member("c", ty.array()), + }); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_EQ(sem->Size(), 4u); + EXPECT_EQ(sem->Align(), 4u); + ASSERT_EQ(sem->Members().size(), 1u); + EXPECT_EQ(sem->Members()[0]->Offset(), 0u); + EXPECT_EQ(sem->Members()[0]->Size(), 4u); +} + +TEST_F(ResolverStructLayoutTest, ExplicitStrideArrayRuntimeSized) { + auto* s = Structure("S", { + Member("c", ty.array(/*stride*/ 32)), + }); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_EQ(sem->Size(), 32u); + EXPECT_EQ(sem->Align(), 4u); + ASSERT_EQ(sem->Members().size(), 1u); + EXPECT_EQ(sem->Members()[0]->Offset(), 0u); + EXPECT_EQ(sem->Members()[0]->Size(), 32u); +} + +TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfExplicitStrideArray) { + auto* inner = ty.array(/*stride*/ 16); // size: 32 + auto* outer = ty.array(inner, 12); // size: 12 * 32 + auto* s = Structure("S", { + Member("c", outer), + }); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_EQ(sem->Size(), 384u); + EXPECT_EQ(sem->Align(), 4u); + ASSERT_EQ(sem->Members().size(), 1u); + EXPECT_EQ(sem->Members()[0]->Offset(), 0u); + EXPECT_EQ(sem->Members()[0]->Size(), 384u); +} + +TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfStructure) { + auto* inner = Structure("Inner", { + Member("a", ty.vec2()), + Member("b", ty.vec3()), + Member("c", ty.vec4()), + }); // size: 48 + auto* outer = ty.array(inner, 12); // size: 12 * 48 + auto* s = Structure("S", { + Member("c", outer), + }); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_EQ(sem->Size(), 576u); + EXPECT_EQ(sem->Align(), 16u); + ASSERT_EQ(sem->Members().size(), 1u); + EXPECT_EQ(sem->Members()[0]->Offset(), 0u); + EXPECT_EQ(sem->Members()[0]->Size(), 576u); +} + +TEST_F(ResolverStructLayoutTest, Vector) { + auto* s = Structure("S", { + Member("a", ty.vec2()), + Member("b", ty.vec3()), + Member("c", ty.vec4()), + }); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_EQ(sem->Size(), 48u); + EXPECT_EQ(sem->Align(), 16u); + ASSERT_EQ(sem->Members().size(), 3u); + EXPECT_EQ(sem->Members()[0]->Offset(), 0u); // vec2 + EXPECT_EQ(sem->Members()[0]->Size(), 8u); + EXPECT_EQ(sem->Members()[1]->Offset(), 16u); // vec3 + EXPECT_EQ(sem->Members()[1]->Size(), 12u); + EXPECT_EQ(sem->Members()[2]->Offset(), 32u); // vec4 + EXPECT_EQ(sem->Members()[2]->Size(), 16u); +} + +TEST_F(ResolverStructLayoutTest, Matrix) { + auto* s = Structure("S", { + Member("a", ty.mat2x2()), + Member("b", ty.mat2x3()), + Member("c", ty.mat2x4()), + Member("d", ty.mat3x2()), + Member("e", ty.mat3x3()), + Member("f", ty.mat3x4()), + Member("g", ty.mat4x2()), + Member("h", ty.mat4x3()), + Member("i", ty.mat4x4()), + }); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_EQ(sem->Size(), 368u); + EXPECT_EQ(sem->Align(), 16u); + ASSERT_EQ(sem->Members().size(), 9u); + EXPECT_EQ(sem->Members()[0]->Offset(), 0u); // mat2x2 + EXPECT_EQ(sem->Members()[0]->Size(), 16u); + EXPECT_EQ(sem->Members()[1]->Offset(), 16u); // mat2x3 + EXPECT_EQ(sem->Members()[1]->Size(), 32u); + EXPECT_EQ(sem->Members()[2]->Offset(), 48u); // mat2x4 + EXPECT_EQ(sem->Members()[2]->Size(), 32u); + EXPECT_EQ(sem->Members()[3]->Offset(), 80u); // mat3x2 + EXPECT_EQ(sem->Members()[3]->Size(), 24u); + EXPECT_EQ(sem->Members()[4]->Offset(), 112u); // mat3x3 + EXPECT_EQ(sem->Members()[4]->Size(), 48u); + EXPECT_EQ(sem->Members()[5]->Offset(), 160u); // mat3x4 + EXPECT_EQ(sem->Members()[5]->Size(), 48u); + EXPECT_EQ(sem->Members()[6]->Offset(), 208u); // mat4x2 + EXPECT_EQ(sem->Members()[6]->Size(), 32u); + EXPECT_EQ(sem->Members()[7]->Offset(), 240u); // mat4x3 + EXPECT_EQ(sem->Members()[7]->Size(), 64u); + EXPECT_EQ(sem->Members()[8]->Offset(), 304u); // mat4x4 + EXPECT_EQ(sem->Members()[8]->Size(), 64u); +} + +TEST_F(ResolverStructLayoutTest, NestedStruct) { + auto* inner = Structure("Inner", { + Member("a", ty.mat3x3()), + }); + auto* s = Structure("S", { + Member("a", ty.i32()), + Member("b", inner), + Member("c", ty.i32()), + }); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_EQ(sem->Size(), 80u); + EXPECT_EQ(sem->Align(), 16u); + ASSERT_EQ(sem->Members().size(), 3u); + EXPECT_EQ(sem->Members()[0]->Offset(), 0u); + EXPECT_EQ(sem->Members()[0]->Size(), 4u); + EXPECT_EQ(sem->Members()[1]->Offset(), 16u); + EXPECT_EQ(sem->Members()[1]->Size(), 48u); + EXPECT_EQ(sem->Members()[2]->Offset(), 64u); + EXPECT_EQ(sem->Members()[2]->Size(), 4u); +} + +TEST_F(ResolverStructLayoutTest, SizeDecorations) { + auto* inner = Structure("Inner", { + Member("a", ty.f32(), {MemberSize(8)}), + Member("b", ty.f32(), {MemberSize(16)}), + Member("c", ty.f32(), {MemberSize(8)}), + }); + auto* s = Structure("S", { + Member("a", ty.f32(), {MemberSize(4)}), + Member("b", ty.u32(), {MemberSize(8)}), + Member("c", inner), + Member("d", ty.i32(), {MemberSize(32)}), + }); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_EQ(sem->Size(), 76u); + EXPECT_EQ(sem->Align(), 4u); + ASSERT_EQ(sem->Members().size(), 4u); + EXPECT_EQ(sem->Members()[0]->Offset(), 0u); + EXPECT_EQ(sem->Members()[0]->Size(), 4u); + EXPECT_EQ(sem->Members()[1]->Offset(), 4u); + EXPECT_EQ(sem->Members()[1]->Size(), 8u); + EXPECT_EQ(sem->Members()[2]->Offset(), 12u); + EXPECT_EQ(sem->Members()[2]->Size(), 32u); + EXPECT_EQ(sem->Members()[3]->Offset(), 44u); + EXPECT_EQ(sem->Members()[3]->Size(), 32u); +} + +TEST_F(ResolverStructLayoutTest, AlignDecorations) { + auto* inner = Structure("Inner", { + Member("a", ty.f32(), {MemberAlign(8)}), + Member("b", ty.f32(), {MemberAlign(16)}), + Member("c", ty.f32(), {MemberAlign(4)}), + }); + auto* s = Structure("S", { + Member("a", ty.f32(), {MemberAlign(4)}), + Member("b", ty.u32(), {MemberAlign(8)}), + Member("c", inner), + Member("d", ty.i32(), {MemberAlign(32)}), + }); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_EQ(sem->Size(), 96u); + EXPECT_EQ(sem->Align(), 32u); + ASSERT_EQ(sem->Members().size(), 4u); + EXPECT_EQ(sem->Members()[0]->Offset(), 0u); + EXPECT_EQ(sem->Members()[0]->Size(), 4u); + EXPECT_EQ(sem->Members()[1]->Offset(), 8u); + EXPECT_EQ(sem->Members()[1]->Size(), 4u); + EXPECT_EQ(sem->Members()[2]->Offset(), 16u); + EXPECT_EQ(sem->Members()[2]->Size(), 32u); + EXPECT_EQ(sem->Members()[3]->Offset(), 64u); + EXPECT_EQ(sem->Members()[3]->Size(), 4u); +} + +} // namespace +} // namespace resolver +} // namespace tint diff --git a/src/resolver/validation_test.cc b/src/resolver/validation_test.cc index 87c111eb43..f3d3cbd590 100644 --- a/src/resolver/validation_test.cc +++ b/src/resolver/validation_test.cc @@ -556,6 +556,38 @@ TEST_F(ResolverValidationTest, Stmt_BreakNotInLoopOrSwitch) { "12:34 error: break statement must be in a loop or switch case"); } +TEST_F(ResolverValidationTest, NonPOTStructMemberAlignDecoration) { + Structure("S", { + Member("a", ty.f32(), {MemberAlign(Source{{12, 34}}, 3)}), + }); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: align value must be a positive, power-of-two integer"); +} + +TEST_F(ResolverValidationTest, ZeroStructMemberAlignDecoration) { + Structure("S", { + Member("a", ty.f32(), {MemberAlign(Source{{12, 34}}, 0)}), + }); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: align value must be a positive, power-of-two integer"); +} + +TEST_F(ResolverValidationTest, ZeroStructMemberSizeDecoration) { + Structure("S", { + Member("a", ty.f32(), {MemberSize(Source{{12, 34}}, 0)}), + }); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: size must be at least as big as the type's size (4)"); +} + } // namespace } // namespace resolver } // namespace tint diff --git a/src/semantic/array.h b/src/semantic/array.h new file mode 100644 index 0000000000..c620d49024 --- /dev/null +++ b/src/semantic/array.h @@ -0,0 +1,69 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SRC_SEMANTIC_ARRAY_H_ +#define SRC_SEMANTIC_ARRAY_H_ + +#include + +#include "src/semantic/node.h" + +namespace tint { + +// Forward declarations +namespace type { +class Array; +} // namespace type + +namespace semantic { + +/// Array holds the semantic information for Array nodes. +class Array : public Castable { + public: + /// Constructor + /// @param type the Array type + /// @param align the byte alignment of the structure + /// @param size the byte size of the structure + /// @param stride the number of bytes from the start of one element of the + /// array to the start of the next element + Array(type::Array* type, uint32_t align, uint32_t size, uint32_t stride); + + /// @return the resolved type of the Array + type::Array* Type() const { return type_; } + + /// @returns the byte alignment of the array + /// @note this may differ from the alignment of a structure member of this + /// array type, if the member is annotated with the `[[align(n)]]` decoration. + uint32_t Align() const { return align_; } + + /// @returns the byte size of the array + /// @note this may differ from the size of a structure member of this array + /// type, if the member is annotated with the `[[size(n)]]` decoration. + uint32_t Size() const { return size_; } + + /// @returns the number of bytes from the start of one element of the + /// array to the start of the next element + uint32_t Stride() const { return stride_; } + + private: + type::Array* const type_; + uint32_t const align_; + uint32_t const size_; + uint32_t const stride_; +}; + +} // namespace semantic +} // namespace tint + +#endif // SRC_SEMANTIC_ARRAY_H_ diff --git a/src/semantic/call.h b/src/semantic/call.h index 954585c88f..8ea4dc1988 100644 --- a/src/semantic/call.h +++ b/src/semantic/call.h @@ -29,9 +29,9 @@ class Call : public Castable { /// @param declaration the AST node /// @param target the call target /// @param statement the statement that owns this expression - explicit Call(ast::Expression* declaration, - const CallTarget* target, - Statement* statement); + Call(ast::Expression* declaration, + const CallTarget* target, + Statement* statement); /// Destructor ~Call() override; diff --git a/src/semantic/info.h b/src/semantic/info.h index 6e478ff256..ec14ceef8f 100644 --- a/src/semantic/info.h +++ b/src/semantic/info.h @@ -23,12 +23,6 @@ #include "src/semantic/type_mappings.h" namespace tint { - -// Forward declarations -namespace ast { -class Node; -} // namespace ast - namespace semantic { /// Info holds all the resolved semantic information for a Program. @@ -48,26 +42,29 @@ class Info { /// @return this Program Info& operator=(Info&& rhs); - /// Get looks up the semantic information for the AST node `ast_node`. - /// @param ast_node the AST node + /// Get looks up the semantic information for the AST or type node `node`. + /// @param node the AST or type node /// @returns a pointer to the semantic node if found, otherwise nullptr - template > - const SEM* Get(const AST* ast_node) const { - auto it = ast_to_sem_.find(ast_node); - if (it == ast_to_sem_.end()) { + template > + const SEM* Get(const AST_OR_TYPE* node) const { + auto it = map.find(node); + if (it == map.end()) { return nullptr; } return it->second->template As(); } - /// Add registers the semantic node `sem_node` for the AST node `ast_node`. - /// @param ast_node the AST node + /// Add registers the semantic node `sem_node` for the AST or type node + /// `node`. + /// @param node the AST or type node /// @param sem_node the semantic node - template - void Add(const AST* ast_node, const SemanticNodeTypeFor* sem_node) { + template + void Add(const AST_OR_TYPE* node, + const SemanticNodeTypeFor* sem_node) { // Check there's no semantic info already existing for the node - assert(Get(ast_node) == nullptr); - ast_to_sem_.emplace(ast_node, sem_node); + assert(Get(node) == nullptr); + map.emplace(node, sem_node); } /// Wrap returns a new Info created with the contents of `inner`. @@ -79,12 +76,12 @@ class Info { /// @return the Info that wraps `inner` static Info Wrap(const Info& inner) { Info out; - out.ast_to_sem_ = inner.ast_to_sem_; + out.map = inner.map; return out; } private: - std::unordered_map ast_to_sem_; + std::unordered_map map; }; } // namespace semantic diff --git a/src/semantic/sem_array.cc b/src/semantic/sem_array.cc new file mode 100644 index 0000000000..c54a3fcfe7 --- /dev/null +++ b/src/semantic/sem_array.cc @@ -0,0 +1,26 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/semantic/array.h" + +TINT_INSTANTIATE_TYPEINFO(tint::semantic::Array); + +namespace tint { +namespace semantic { + +Array::Array(type::Array* type, uint32_t align, uint32_t size, uint32_t stride) + : type_(type), align_(align), size_(size), stride_(stride) {} + +} // namespace semantic +} // namespace tint diff --git a/src/semantic/sem_struct.cc b/src/semantic/sem_struct.cc new file mode 100644 index 0000000000..0d3b9718e6 --- /dev/null +++ b/src/semantic/sem_struct.cc @@ -0,0 +1,39 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/semantic/struct.h" + +TINT_INSTANTIATE_TYPEINFO(tint::semantic::Struct); +TINT_INSTANTIATE_TYPEINFO(tint::semantic::StructMember); + +namespace tint { +namespace semantic { + +Struct::Struct(type::Struct* type, + StructMemberList members, + uint32_t align, + uint32_t size) + : type_(type), members_(std::move(members)), align_(align), size_(size) {} + +Struct::~Struct() = default; + +StructMember::StructMember(ast::StructMember* declaration, + uint32_t offset, + uint32_t size) + : declaration_(declaration), offset_(offset), size_(size) {} + +StructMember::~StructMember() = default; + +} // namespace semantic +} // namespace tint diff --git a/src/semantic/struct.h b/src/semantic/struct.h new file mode 100644 index 0000000000..3d996552db --- /dev/null +++ b/src/semantic/struct.h @@ -0,0 +1,112 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SRC_SEMANTIC_STRUCT_H_ +#define SRC_SEMANTIC_STRUCT_H_ + +#include + +#include + +#include "src/semantic/node.h" + +namespace tint { + +// Forward declarations +namespace ast { +class StructMember; +} // namespace ast +namespace type { +class Struct; +} // namespace type + +namespace semantic { + +class StructMember; + +/// A vector of StructMember pointers. +using StructMemberList = std::vector; + +/// Struct holds the semantic information for structures. +class Struct : public Castable { + public: + /// Constructor + /// @param type the structure type + /// @param members the structure members + /// @param align the byte alignment of the structure + /// @param size the byte size of the structure + Struct(type::Struct* type, + StructMemberList members, + uint32_t align, + uint32_t size); + + /// Destructor + ~Struct() override; + + /// @returns the structure type + type::Struct* Type() const { return type_; } + + /// @returns the members of the structure + const StructMemberList& Members() const { return members_; } + + /// @returns the byte alignment of the structure + /// @note this may differ from the alignment of a structure member of this + /// structure type, if the member is annotated with the `[[align(n)]]` + /// decoration. + uint32_t Align() const { return align_; } + + /// @returns the byte size of the structure + /// @note this may differ from the size of a structure member of this + /// structure type, if the member is annotated with the `[[size(n)]]` + /// decoration. + uint32_t Size() const { return size_; } + + private: + type::Struct* const type_; + StructMemberList const members_; + uint32_t const align_; + uint32_t const size_; +}; + +/// StructMember holds the semantic information for structure members. +class StructMember : public Castable { + public: + /// Constructor + /// @param declaration the AST declaration node + /// @param offset the byte offset from the base of the structure + /// @param size the byte size + StructMember(ast::StructMember* declaration, uint32_t offset, uint32_t size); + + /// Destructor + ~StructMember() override; + + /// @returns the AST declaration node + ast::StructMember* Declaration() const { return declaration_; } + + /// @returns byte offset from base of structure + uint32_t Offset() const { return offset_; } + + /// @returns byte size + uint32_t Size() const { return size_; } + + private: + ast::StructMember* const declaration_; + uint32_t const offset_; // Byte offset from base of structure + uint32_t const size_; // Byte size +}; + +} // namespace semantic +} // namespace tint + +#endif // SRC_SEMANTIC_STRUCT_H_ diff --git a/src/semantic/type_mappings.h b/src/semantic/type_mappings.h index 6b1c331433..d0b48d8d7c 100644 --- a/src/semantic/type_mappings.h +++ b/src/semantic/type_mappings.h @@ -19,44 +19,52 @@ namespace tint { // Forward declarations namespace ast { - class CallExpression; class Expression; class Function; class MemberAccessorExpression; +class StructMember; class Variable; - } // namespace ast +namespace type { +class Array; +class Struct; +} // namespace type namespace semantic { +// Forward declarations +class Array; class Call; class Expression; class Function; class MemberAccessorExpression; +class Struct; +class StructMember; class Variable; /// TypeMappings is a struct that holds dummy `operator()` methods that's used -/// by SemanticNodeTypeFor to map AST node types to their corresponding semantic -/// node types. -/// The standard operator overload resolving rules will be used to infer the -/// return type based on the argument type. +/// by SemanticNodeTypeFor to map AST / type node types to their corresponding +/// semantic node types. The standard operator overload resolving rules will be +/// used to infer the return type based on the argument type. struct TypeMappings { //! @cond Doxygen_Suppress - semantic::Expression* operator()(ast::Expression*); - semantic::Function* operator()(ast::Function*); - semantic::Variable* operator()(ast::Variable*); - semantic::Call* operator()(ast::CallExpression*); - semantic::MemberAccessorExpression* operator()( - ast::MemberAccessorExpression*); + Array* operator()(type::Array*); + Call* operator()(ast::CallExpression*); + Expression* operator()(ast::Expression*); + Function* operator()(ast::Function*); + MemberAccessorExpression* operator()(ast::MemberAccessorExpression*); + Struct* operator()(type::Struct*); + StructMember* operator()(ast::StructMember*); + Variable* operator()(ast::Variable*); //! @endcond }; /// SemanticNodeTypeFor resolves to the appropriate semantic::Node type for the -/// AST node type `AST`. -template +/// AST or type node `AST_OR_TYPE`. +template using SemanticNodeTypeFor = typename std::remove_pointer()))>::type; + TypeMappings()(std::declval()))>::type; } // namespace semantic } // namespace tint diff --git a/src/transform/first_index_offset.cc b/src/transform/first_index_offset.cc index 61ab7250d9..6acabe0fba 100644 --- a/src/transform/first_index_offset.cc +++ b/src/transform/first_index_offset.cc @@ -154,26 +154,20 @@ Transform::Output FirstIndexOffset::Run(const Program* in) { ast::Variable* FirstIndexOffset::State::AddUniformBuffer() { auto* u32_type = dst->create(); - ast::StructMemberList members; uint32_t offset = 0; + ast::StructMemberList members; if (has_vertex_index) { - ast::DecorationList member_dec; - member_dec.push_back( - dst->create(Source{}, offset)); members.push_back(dst->create( Source{}, dst->Symbols().Register(kFirstVertexName), u32_type, - std::move(member_dec))); + ast::DecorationList{})); vertex_index_offset = offset; offset += 4; } if (has_instance_index) { - ast::DecorationList member_dec; - member_dec.push_back( - dst->create(Source{}, offset)); members.push_back(dst->create( Source{}, dst->Symbols().Register(kFirstInstanceName), u32_type, - std::move(member_dec))); + ast::DecorationList{})); instance_index_offset = offset; offset += 4; } diff --git a/src/transform/first_index_offset.h b/src/transform/first_index_offset.h index f2d04ecd58..dd187d2702 100644 --- a/src/transform/first_index_offset.h +++ b/src/transform/first_index_offset.h @@ -46,8 +46,8 @@ namespace transform { /// After: /// [[block]] /// struct TintFirstIndexOffsetData { -/// [[offset(0)]] tint_first_vertex_index : u32; -/// [[offset(4)]] tint_first_instance_index : u32; +/// tint_first_vertex_index : u32; +/// tint_first_instance_index : u32; /// }; /// [[builtin(vertex_index)]] var tint_first_index_offset_vert_idx : u32; /// [[binding(N), group(M)]] var tint_first_index_data : diff --git a/src/transform/first_index_offset_test.cc b/src/transform/first_index_offset_test.cc index 46189157e8..3370cf4a7e 100644 --- a/src/transform/first_index_offset_test.cc +++ b/src/transform/first_index_offset_test.cc @@ -98,7 +98,6 @@ fn entry() -> void { [[block]] struct TintFirstIndexOffsetData { - [[offset(0)]] tint_first_vertex_index : u32; }; @@ -147,7 +146,6 @@ fn entry() -> void { [[block]] struct TintFirstIndexOffsetData { - [[offset(0)]] tint_first_instance_index : u32; }; @@ -199,9 +197,7 @@ fn entry() -> void { [[block]] struct TintFirstIndexOffsetData { - [[offset(0)]] tint_first_vertex_index : u32; - [[offset(4)]] tint_first_instance_index : u32; }; @@ -255,7 +251,6 @@ fn entry() -> void { [[block]] struct TintFirstIndexOffsetData { - [[offset(0)]] tint_first_vertex_index : u32; }; diff --git a/src/transform/hlsl_test.cc b/src/transform/hlsl_test.cc index 57340f1e24..a24f68b2a1 100644 --- a/src/transform/hlsl_test.cc +++ b/src/transform/hlsl_test.cc @@ -96,7 +96,6 @@ TEST_F(HlslTest, PromoteArrayInitializerToConstVar_Bug406) { auto* src = R"( [[block]] struct Uniforms { - [[offset(0)]] transform : mat2x2; }; @@ -121,7 +120,6 @@ fn main() -> void { auto* expect = R"( [[block]] struct Uniforms { - [[offset(0)]] transform : mat2x2; }; diff --git a/src/transform/msl.h b/src/transform/msl.h index 7b5e16c0ab..38a2cd1ab5 100644 --- a/src/transform/msl.h +++ b/src/transform/msl.h @@ -34,6 +34,7 @@ class Msl : public Transform { /// @returns the transformation result Output Run(const Program* program) override; + private: /// Hoist location-decorated entry point parameters out to struct members. void HandleEntryPointIOTypes(CloneContext& ctx) const; }; diff --git a/src/transform/vertex_pulling.cc b/src/transform/vertex_pulling.cc index b9d65257d5..b82a4d56c1 100644 --- a/src/transform/vertex_pulling.cc +++ b/src/transform/vertex_pulling.cc @@ -230,13 +230,9 @@ void VertexPulling::State::AddVertexStorageBuffers() { // Creating the struct type ast::StructMemberList members; - ast::DecorationList member_dec; - member_dec.push_back( - ctx.dst->create(Source{}, 0u)); - members.push_back(ctx.dst->create( Source{}, ctx.dst->Symbols().Register(kStructBufferName), - internal_array_type, std::move(member_dec))); + internal_array_type, ast::DecorationList{})); ast::DecorationList decos; decos.push_back(ctx.dst->create(Source{})); diff --git a/src/transform/vertex_pulling_test.cc b/src/transform/vertex_pulling_test.cc index 6c80e16f95..b4e1080d96 100644 --- a/src/transform/vertex_pulling_test.cc +++ b/src/transform/vertex_pulling_test.cc @@ -83,7 +83,6 @@ fn main() -> void {} auto* expect = R"( [[block]] struct TintVertexData { - [[offset(0)]] _tint_vertex_data : [[stride(4)]] array; }; @@ -120,7 +119,6 @@ fn main() -> void {} [[block]] struct TintVertexData { - [[offset(0)]] _tint_vertex_data : [[stride(4)]] array; }; @@ -163,7 +161,6 @@ fn main() -> void {} [[block]] struct TintVertexData { - [[offset(0)]] _tint_vertex_data : [[stride(4)]] array; }; @@ -206,7 +203,6 @@ fn main() -> void {} [[block]] struct TintVertexData { - [[offset(0)]] _tint_vertex_data : [[stride(4)]] array; }; @@ -254,7 +250,6 @@ fn main() -> void {} [[block]] struct TintVertexData { - [[offset(0)]] _tint_vertex_data : [[stride(4)]] array; }; @@ -316,7 +311,6 @@ fn main() -> void {} [[block]] struct TintVertexData { - [[offset(0)]] _tint_vertex_data : [[stride(4)]] array; }; @@ -371,7 +365,6 @@ fn main() -> void {} [[block]] struct TintVertexData { - [[offset(0)]] _tint_vertex_data : [[stride(4)]] array; }; diff --git a/src/type/access_control_type.cc b/src/type/access_control_type.cc index 43c7924207..4ecefe355a 100644 --- a/src/type/access_control_type.cc +++ b/src/type/access_control_type.cc @@ -65,14 +65,6 @@ std::string AccessControl::FriendlyName(const SymbolTable& symbols) const { return out.str(); } -uint64_t AccessControl::MinBufferBindingSize(MemoryLayout mem_layout) const { - return subtype_->MinBufferBindingSize(mem_layout); -} - -uint64_t AccessControl::BaseAlignment(MemoryLayout mem_layout) const { - return subtype_->BaseAlignment(mem_layout); -} - AccessControl* AccessControl::Clone(CloneContext* ctx) const { // Clone arguments outside of create() call to have deterministic ordering auto* ty = ctx->Clone(type()); diff --git a/src/type/access_control_type.h b/src/type/access_control_type.h index 7dce8bd1e6..7002ea432e 100644 --- a/src/type/access_control_type.h +++ b/src/type/access_control_type.h @@ -54,16 +54,6 @@ class AccessControl : public Castable { /// declared in WGSL. std::string FriendlyName(const SymbolTable& symbols) const override; - /// @param mem_layout type of memory layout to use in calculation. - /// @returns minimum size required for this type, in bytes. - /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; - - /// @param mem_layout type of memory layout to use in calculation. - /// @returns base alignment for the type, in bytes. - /// 0 for non-host shareable types. - uint64_t BaseAlignment(MemoryLayout mem_layout) const override; - /// Clones this type and all transitive types using the `CloneContext` `ctx`. /// @param ctx the clone context /// @return the newly cloned type diff --git a/src/type/access_control_type_test.cc b/src/type/access_control_type_test.cc index 22563457eb..e390400103 100644 --- a/src/type/access_control_type_test.cc +++ b/src/type/access_control_type_test.cc @@ -95,70 +95,6 @@ TEST_F(AccessControlTest, FriendlyNameReadWrite) { EXPECT_EQ(at.FriendlyName(Symbols()), "[[access(read_write)]] i32"); } -TEST_F(AccessControlTest, MinBufferBindingSizeU32) { - U32 u32; - AccessControl at{ast::AccessControl::kReadOnly, &u32}; - EXPECT_EQ(4u, at.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - -TEST_F(AccessControlTest, MinBufferBindingSizeArray) { - U32 u32; - Array array(&u32, 4, ast::DecorationList{create(4)}); - AccessControl at{ast::AccessControl::kReadOnly, &array}; - EXPECT_EQ(16u, at.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - -TEST_F(AccessControlTest, MinBufferBindingSizeRuntimeArray) { - U32 u32; - Array array(&u32, 0, ast::DecorationList{create(4)}); - AccessControl at{ast::AccessControl::kReadOnly, &array}; - EXPECT_EQ(4u, at.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - -TEST_F(AccessControlTest, MinBufferBindingSizeStruct) { - auto* str = create( - ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}), - Member("bar", ty.u32(), {MemberOffset(4)})}, - ast::DecorationList{}); - - auto* struct_type = ty.struct_("struct_type", str); - AccessControl at{ast::AccessControl::kReadOnly, struct_type}; - EXPECT_EQ(16u, at.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(8u, at.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); -} - -TEST_F(AccessControlTest, BaseAlignmentU32) { - U32 u32; - AccessControl at{ast::AccessControl::kReadOnly, &u32}; - EXPECT_EQ(4u, at.BaseAlignment(MemoryLayout::kUniformBuffer)); -} - -TEST_F(AccessControlTest, BaseAlignmentArray) { - U32 u32; - Array array(&u32, 4, ast::DecorationList{create(4)}); - AccessControl at{ast::AccessControl::kReadOnly, &array}; - EXPECT_EQ(16u, at.BaseAlignment(MemoryLayout::kUniformBuffer)); -} - -TEST_F(AccessControlTest, BaseAlignmentRuntimeArray) { - U32 u32; - Array array(&u32, 0, ast::DecorationList{create(4)}); - AccessControl at{ast::AccessControl::kReadOnly, &array}; - EXPECT_EQ(16u, at.BaseAlignment(MemoryLayout::kUniformBuffer)); -} - -TEST_F(AccessControlTest, BaseAlignmentStruct) { - auto* str = create( - ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}), - Member("bar", ty.u32(), {MemberOffset(4)})}, - ast::DecorationList{}); - auto* struct_type = ty.struct_("struct_type", str); - - AccessControl at{ast::AccessControl::kReadOnly, struct_type}; - EXPECT_EQ(16u, at.BaseAlignment(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(4u, at.BaseAlignment(MemoryLayout::kStorageBuffer)); -} - } // namespace } // namespace type } // namespace tint diff --git a/src/type/alias_type.cc b/src/type/alias_type.cc index f5e2d2a18c..b155cc2d5b 100644 --- a/src/type/alias_type.cc +++ b/src/type/alias_type.cc @@ -38,14 +38,6 @@ std::string Alias::FriendlyName(const SymbolTable& symbols) const { return symbols.NameFor(symbol_); } -uint64_t Alias::MinBufferBindingSize(MemoryLayout mem_layout) const { - return subtype_->MinBufferBindingSize(mem_layout); -} - -uint64_t Alias::BaseAlignment(MemoryLayout mem_layout) const { - return subtype_->BaseAlignment(mem_layout); -} - Alias* Alias::Clone(CloneContext* ctx) const { // Clone arguments outside of create() call to have deterministic ordering auto sym = ctx->Clone(symbol()); diff --git a/src/type/alias_type.h b/src/type/alias_type.h index 53619503f2..ede6218d7e 100644 --- a/src/type/alias_type.h +++ b/src/type/alias_type.h @@ -47,16 +47,6 @@ class Alias : public Castable { /// declared in WGSL. std::string FriendlyName(const SymbolTable& symbols) const override; - /// @param mem_layout type of memory layout to use in calculation. - /// @returns minimum size required for this type, in bytes. - /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; - - /// @param mem_layout type of memory layout to use in calculation. - /// @returns base alignment for the type, in bytes. - /// 0 for non-host shareable types. - uint64_t BaseAlignment(MemoryLayout mem_layout) const override; - /// Clones this type and all transitive types using the `CloneContext` `ctx`. /// @param ctx the clone context /// @return the newly cloned type diff --git a/src/type/alias_type_test.cc b/src/type/alias_type_test.cc index 278ea6afa7..741d04aca2 100644 --- a/src/type/alias_type_test.cc +++ b/src/type/alias_type_test.cc @@ -137,76 +137,6 @@ TEST_F(AliasTest, UnwrapAll_PointerAccessControl) { EXPECT_EQ(a.UnwrapAll(), ty.u32()); } -TEST_F(AliasTest, MinBufferBindingSizeU32) { - auto* alias = ty.alias("alias", ty.u32()); - EXPECT_EQ(4u, alias->MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - -TEST_F(AliasTest, MinBufferBindingSizeArray) { - Array array(ty.u32(), 4, - ast::DecorationList{ - create(4), - }); - auto* alias = ty.alias("alias", &array); - EXPECT_EQ(16u, alias->MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - -TEST_F(AliasTest, MinBufferBindingSizeRuntimeArray) { - Array array(ty.u32(), 0, - ast::DecorationList{ - create(4), - }); - auto* alias = ty.alias("alias", &array); - EXPECT_EQ(4u, alias->MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - -TEST_F(AliasTest, MinBufferBindingSizeStruct) { - auto* str = create( - ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}), - Member("bar", ty.u32(), {MemberOffset(4)})}, - ast::DecorationList{}); - auto* struct_type = ty.struct_("struct_type", str); - auto* alias = ty.alias("alias", struct_type); - - EXPECT_EQ(16u, alias->MinBufferBindingSize(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(8u, alias->MinBufferBindingSize(MemoryLayout::kStorageBuffer)); -} - -TEST_F(AliasTest, BaseAlignmentU32) { - auto* alias = ty.alias("alias", ty.u32()); - EXPECT_EQ(4u, alias->BaseAlignment(MemoryLayout::kUniformBuffer)); -} - -TEST_F(AliasTest, BaseAlignmentArray) { - Array array(ty.u32(), 4, - ast::DecorationList{ - create(4), - }); - auto* alias = ty.alias("alias", &array); - EXPECT_EQ(16u, alias->BaseAlignment(MemoryLayout::kUniformBuffer)); -} - -TEST_F(AliasTest, BaseAlignmentRuntimeArray) { - Array array(ty.u32(), 0, - ast::DecorationList{ - create(4), - }); - auto* alias = ty.alias("alias", &array); - EXPECT_EQ(16u, alias->BaseAlignment(MemoryLayout::kUniformBuffer)); -} - -TEST_F(AliasTest, BaseAlignmentStruct) { - auto* str = create( - ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}), - Member("bar", ty.u32(), {MemberOffset(4)})}, - ast::DecorationList{}); - auto* struct_type = ty.struct_("struct_type", str); - auto* alias = ty.alias("alias", struct_type); - - EXPECT_EQ(16u, alias->BaseAlignment(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(4u, alias->BaseAlignment(MemoryLayout::kStorageBuffer)); -} - TEST_F(AliasTest, UnwrapAliasIfNeeded) { auto* alias1 = ty.alias("alias1", ty.f32()); auto* alias2 = ty.alias("alias2", alias1); diff --git a/src/type/array_type.cc b/src/type/array_type.cc index 29ead9b004..46fe3edab2 100644 --- a/src/type/array_type.cc +++ b/src/type/array_type.cc @@ -30,71 +30,28 @@ Array::Array(Array&&) = default; Array::~Array() = default; -uint64_t Array::MinBufferBindingSize(MemoryLayout mem_layout) const { - if (!has_array_stride()) { - // Arrays in buffers are required to have a stride. - return 0; - } - - if (IsRuntimeArray()) { - // WebGPU spec 10.1.2: - // If the last field of the corresponding structure defined in the shader - // has an unbounded array type, then the value of minBufferBindingSize must - // be greater than or equal to the byte offset of that field plus the stride - // of the unbounded array - return array_stride(); - } else { - // Not including the padding for the last element - return (size_ - 1) * array_stride() + - subtype_->MinBufferBindingSize(mem_layout); - } -} - -uint64_t Array::BaseAlignment(MemoryLayout mem_layout) const { - if (mem_layout == MemoryLayout::kUniformBuffer) { - float aligment = 16; // for a vec4 - float unaligned = static_cast(subtype_->BaseAlignment(mem_layout)); - return static_cast(aligment * std::ceil(unaligned / aligment)); - } else if (mem_layout == MemoryLayout::kStorageBuffer) { - return subtype_->BaseAlignment(mem_layout); - } - return 0; -} - -uint32_t Array::array_stride() const { - for (auto* deco : decos_) { - if (auto* stride = deco->As()) { - return stride->stride(); - } - } - return 0; -} - -bool Array::has_array_stride() const { - for (auto* deco : decos_) { - if (deco->Is()) { - return true; - } - } - return false; -} - std::string Array::type_name() const { assert(subtype_); std::string type_name = "__array" + subtype_->type_name(); - if (!IsRuntimeArray()) + if (!IsRuntimeArray()) { type_name += "_" + std::to_string(size_); - if (has_array_stride()) - type_name += "_stride_" + std::to_string(array_stride()); + } + for (auto* deco : decos_) { + if (auto* stride = deco->As()) { + type_name += "_stride_" + std::to_string(stride->stride()); + } + } return type_name; } std::string Array::FriendlyName(const SymbolTable& symbols) const { std::ostringstream out; - if (has_array_stride()) { - out << "[[stride(" << array_stride() << ")]] "; + for (auto* deco : decos_) { + if (auto* stride = deco->As()) { + out << "[[stride(" << stride->stride() << ")]] "; + } } out << "array<" << subtype_->FriendlyName(symbols); if (!IsRuntimeArray()) { diff --git a/src/type/array_type.h b/src/type/array_type.h index c0f74aa74d..73456bc4fa 100644 --- a/src/type/array_type.h +++ b/src/type/array_type.h @@ -40,24 +40,9 @@ class Array : public Castable { /// i.e. the size is determined at runtime bool IsRuntimeArray() const { return size_ == 0; } - /// @param mem_layout type of memory layout to use in calculation. - /// @returns minimum size required for this type, in bytes. - /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; - - /// @param mem_layout type of memory layout to use in calculation. - /// @returns base alignment for the type, in bytes. - /// 0 for non-host shareable types. - uint64_t BaseAlignment(MemoryLayout mem_layout) const override; - /// @returns the array decorations const ast::DecorationList& decorations() const { return decos_; } - /// @returns the array stride or 0 if none set. - uint32_t array_stride() const; - /// @returns true if the array has a stride set - bool has_array_stride() const; - /// @returns the array type Type* type() const { return subtype_; } /// @returns the array size. Size is 0 for a runtime array diff --git a/src/type/array_type_test.cc b/src/type/array_type_test.cc index 30bf850bc3..062adddc1d 100644 --- a/src/type/array_type_test.cc +++ b/src/type/array_type_test.cc @@ -94,38 +94,6 @@ TEST_F(ArrayTest, TypeName_WithStride) { EXPECT_EQ(arr.type_name(), "__array__i32_3_stride_16"); } -TEST_F(ArrayTest, MinBufferBindingSizeNoStride) { - U32 u32; - Array arr(&u32, 4, ast::DecorationList{}); - EXPECT_EQ(0u, arr.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - -TEST_F(ArrayTest, MinBufferBindingSizeArray) { - U32 u32; - Array arr(&u32, 4, ast::DecorationList{create(4)}); - EXPECT_EQ(16u, arr.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - -TEST_F(ArrayTest, MinBufferBindingSizeRuntimeArray) { - U32 u32; - Array arr(&u32, 0, ast::DecorationList{create(4)}); - EXPECT_EQ(4u, arr.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - -TEST_F(ArrayTest, BaseAlignmentArray) { - U32 u32; - Array arr(&u32, 4, ast::DecorationList{create(4)}); - EXPECT_EQ(16u, arr.BaseAlignment(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(4u, arr.BaseAlignment(MemoryLayout::kStorageBuffer)); -} - -TEST_F(ArrayTest, BaseAlignmentRuntimeArray) { - U32 u32; - Array arr(&u32, 0, ast::DecorationList{create(4)}); - EXPECT_EQ(16u, arr.BaseAlignment(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(4u, arr.BaseAlignment(MemoryLayout::kStorageBuffer)); -} - } // namespace } // namespace type } // namespace tint diff --git a/src/type/bool_type_test.cc b/src/type/bool_type_test.cc index 3a9fd2c52b..7751c53de4 100644 --- a/src/type/bool_type_test.cc +++ b/src/type/bool_type_test.cc @@ -50,11 +50,6 @@ TEST_F(BoolTest, FriendlyName) { EXPECT_EQ(b.FriendlyName(Symbols()), "bool"); } -TEST_F(BoolTest, MinBufferBindingSize) { - Bool b; - EXPECT_EQ(0u, b.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - } // namespace } // namespace type } // namespace tint diff --git a/src/type/depth_texture_type_test.cc b/src/type/depth_texture_type_test.cc index ebb20da807..62cb05b04b 100644 --- a/src/type/depth_texture_type_test.cc +++ b/src/type/depth_texture_type_test.cc @@ -67,11 +67,6 @@ TEST_F(DepthTextureTest, FriendlyName) { EXPECT_EQ(d.FriendlyName(Symbols()), "texture_depth_cube"); } -TEST_F(DepthTextureTest, MinBufferBindingSize) { - DepthTexture d(TextureDimension::kCube); - EXPECT_EQ(0u, d.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - } // namespace } // namespace type } // namespace tint diff --git a/src/type/f32_type.cc b/src/type/f32_type.cc index 8bd02ab085..5e7e2a521a 100644 --- a/src/type/f32_type.cc +++ b/src/type/f32_type.cc @@ -35,14 +35,6 @@ std::string F32::FriendlyName(const SymbolTable&) const { return "f32"; } -uint64_t F32::MinBufferBindingSize(MemoryLayout) const { - return 4; -} - -uint64_t F32::BaseAlignment(MemoryLayout) const { - return 4; -} - F32* F32::Clone(CloneContext* ctx) const { return ctx->dst->create(); } diff --git a/src/type/f32_type.h b/src/type/f32_type.h index 50e990d179..156ddd121c 100644 --- a/src/type/f32_type.h +++ b/src/type/f32_type.h @@ -39,16 +39,6 @@ class F32 : public Castable { /// declared in WGSL. std::string FriendlyName(const SymbolTable& symbols) const override; - /// @param mem_layout type of memory layout to use in calculation. - /// @returns minimum size required for this type, in bytes. - /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; - - /// @param mem_layout type of memory layout to use in calculation. - /// @returns base alignment for the type, in bytes. - /// 0 for non-host shareable types. - uint64_t BaseAlignment(MemoryLayout mem_layout) const override; - /// Clones this type and all transitive types using the `CloneContext` `ctx`. /// @param ctx the clone context /// @return the newly cloned type diff --git a/src/type/f32_type_test.cc b/src/type/f32_type_test.cc index d02d1b90bd..2985597599 100644 --- a/src/type/f32_type_test.cc +++ b/src/type/f32_type_test.cc @@ -50,16 +50,6 @@ TEST_F(F32Test, FriendlyName) { EXPECT_EQ(f.FriendlyName(Symbols()), "f32"); } -TEST_F(F32Test, MinBufferBindingSize) { - F32 f; - EXPECT_EQ(4u, f.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - -TEST_F(F32Test, BaseAlignment) { - F32 f; - EXPECT_EQ(4u, f.BaseAlignment(MemoryLayout::kUniformBuffer)); -} - } // namespace } // namespace type } // namespace tint diff --git a/src/type/i32_type.cc b/src/type/i32_type.cc index 78a7a7d1e9..780738510a 100644 --- a/src/type/i32_type.cc +++ b/src/type/i32_type.cc @@ -35,14 +35,6 @@ std::string I32::FriendlyName(const SymbolTable&) const { return "i32"; } -uint64_t I32::MinBufferBindingSize(MemoryLayout) const { - return 4; -} - -uint64_t I32::BaseAlignment(MemoryLayout) const { - return 4; -} - I32* I32::Clone(CloneContext* ctx) const { return ctx->dst->create(); } diff --git a/src/type/i32_type.h b/src/type/i32_type.h index e688a0c8f0..a376974884 100644 --- a/src/type/i32_type.h +++ b/src/type/i32_type.h @@ -39,16 +39,6 @@ class I32 : public Castable { /// declared in WGSL. std::string FriendlyName(const SymbolTable& symbols) const override; - /// @param mem_layout type of memory layout to use in calculation. - /// @returns minimum size required for this type, in bytes. - /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; - - /// @param mem_layout type of memory layout to use in calculation. - /// @returns base alignment for the type, in bytes. - /// 0 for non-host shareable types. - uint64_t BaseAlignment(MemoryLayout mem_layout) const override; - /// Clones this type and all transitive types using the `CloneContext` `ctx`. /// @param ctx the clone context /// @return the newly cloned type diff --git a/src/type/i32_type_test.cc b/src/type/i32_type_test.cc index 788ea586f2..38aca922d9 100644 --- a/src/type/i32_type_test.cc +++ b/src/type/i32_type_test.cc @@ -50,16 +50,6 @@ TEST_F(I32Test, FriendlyName) { EXPECT_EQ(i.FriendlyName(Symbols()), "i32"); } -TEST_F(I32Test, MinBufferBindingSize) { - I32 i; - EXPECT_EQ(4u, i.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - -TEST_F(I32Test, BaseAlignment) { - I32 i; - EXPECT_EQ(4u, i.BaseAlignment(MemoryLayout::kUniformBuffer)); -} - } // namespace } // namespace type } // namespace tint diff --git a/src/type/matrix_type.cc b/src/type/matrix_type.cc index d98c669a65..02e225bf44 100644 --- a/src/type/matrix_type.cc +++ b/src/type/matrix_type.cc @@ -45,18 +45,6 @@ std::string Matrix::FriendlyName(const SymbolTable& symbols) const { return out.str(); } -uint64_t Matrix::MinBufferBindingSize(MemoryLayout mem_layout) const { - Vector vec(subtype_, rows_); - return (columns_ - 1) * vec.BaseAlignment(mem_layout) + - vec.MinBufferBindingSize(mem_layout); -} - -uint64_t Matrix::BaseAlignment(MemoryLayout mem_layout) const { - Vector vec(subtype_, rows_); - Array arr(&vec, columns_, ast::DecorationList{}); - return arr.BaseAlignment(mem_layout); -} - Matrix* Matrix::Clone(CloneContext* ctx) const { // Clone arguments outside of create() call to have deterministic ordering auto* ty = ctx->Clone(type()); diff --git a/src/type/matrix_type.h b/src/type/matrix_type.h index f76d578985..59c878c54f 100644 --- a/src/type/matrix_type.h +++ b/src/type/matrix_type.h @@ -49,16 +49,6 @@ class Matrix : public Castable { /// declared in WGSL. std::string FriendlyName(const SymbolTable& symbols) const override; - /// @param mem_layout type of memory layout to use in calculation. - /// @returns minimum size required for this type, in bytes. - /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; - - /// @param mem_layout type of memory layout to use in calculation. - /// @returns base alignment for the type, in bytes. - /// 0 for non-host shareable types. - uint64_t BaseAlignment(MemoryLayout mem_layout) const override; - /// Clones this type and all transitive types using the `CloneContext` `ctx`. /// @param ctx the clone context /// @return the newly cloned type diff --git a/src/type/matrix_type_test.cc b/src/type/matrix_type_test.cc index 73a7d0f37f..09dc8f01ca 100644 --- a/src/type/matrix_type_test.cc +++ b/src/type/matrix_type_test.cc @@ -60,62 +60,6 @@ TEST_F(MatrixTest, FriendlyName) { EXPECT_EQ(m.FriendlyName(Symbols()), "mat2x3"); } -TEST_F(MatrixTest, MinBufferBindingSize4x2) { - I32 i32; - Matrix m{&i32, 4, 2}; - EXPECT_EQ(32u, m.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(32u, m.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); -} - -TEST_F(MatrixTest, MinBufferBindingSize3x2) { - I32 i32; - Matrix m{&i32, 3, 2}; - EXPECT_EQ(28u, m.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(28u, m.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); -} - -TEST_F(MatrixTest, MinBufferBindingSize2x3) { - I32 i32; - Matrix m{&i32, 2, 3}; - EXPECT_EQ(24u, m.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(24u, m.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); -} - -TEST_F(MatrixTest, MinBufferBindingSize2x2) { - I32 i32; - Matrix m{&i32, 2, 2}; - EXPECT_EQ(16u, m.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(16u, m.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); -} - -TEST_F(MatrixTest, BaseAlignment4x2) { - I32 i32; - Matrix m{&i32, 4, 2}; - EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kStorageBuffer)); -} - -TEST_F(MatrixTest, BaseAlignment3x2) { - I32 i32; - Matrix m{&i32, 3, 2}; - EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kStorageBuffer)); -} - -TEST_F(MatrixTest, BaseAlignment2x3) { - I32 i32; - Matrix m{&i32, 2, 3}; - EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(8u, m.BaseAlignment(MemoryLayout::kStorageBuffer)); -} - -TEST_F(MatrixTest, BaseAlignment2x2) { - I32 i32; - Matrix m{&i32, 2, 2}; - EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(8u, m.BaseAlignment(MemoryLayout::kStorageBuffer)); -} - } // namespace } // namespace type } // namespace tint diff --git a/src/type/multisampled_texture_type_test.cc b/src/type/multisampled_texture_type_test.cc index caeb3fae0c..d0f025e0fd 100644 --- a/src/type/multisampled_texture_type_test.cc +++ b/src/type/multisampled_texture_type_test.cc @@ -78,12 +78,6 @@ TEST_F(MultisampledTextureTest, FriendlyName) { EXPECT_EQ(s.FriendlyName(Symbols()), "texture_multisampled_3d"); } -TEST_F(MultisampledTextureTest, MinBufferBindingSize) { - F32 f32; - MultisampledTexture s(TextureDimension::k3d, &f32); - EXPECT_EQ(0u, s.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - } // namespace } // namespace type } // namespace tint diff --git a/src/type/sampled_texture_type_test.cc b/src/type/sampled_texture_type_test.cc index 5034fb8100..2587c2efa0 100644 --- a/src/type/sampled_texture_type_test.cc +++ b/src/type/sampled_texture_type_test.cc @@ -76,12 +76,6 @@ TEST_F(SampledTextureTest, FriendlyName) { EXPECT_EQ(s.FriendlyName(Symbols()), "texture_3d"); } -TEST_F(SampledTextureTest, MinBufferBindingSize) { - F32 f32; - SampledTexture s(TextureDimension::kCube, &f32); - EXPECT_EQ(0u, s.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - } // namespace } // namespace type } // namespace tint diff --git a/src/type/sampler_type_test.cc b/src/type/sampler_type_test.cc index 1912857cc2..a3ea06d39e 100644 --- a/src/type/sampler_type_test.cc +++ b/src/type/sampler_type_test.cc @@ -71,11 +71,6 @@ TEST_F(SamplerTest, FriendlyNameComparisonSampler) { EXPECT_EQ(s.FriendlyName(Symbols()), "sampler_comparison"); } -TEST_F(SamplerTest, MinBufferBindingSize) { - Sampler s{SamplerKind::kSampler}; - EXPECT_EQ(0u, s.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - } // namespace } // namespace type } // namespace tint diff --git a/src/type/storage_texture_type_test.cc b/src/type/storage_texture_type_test.cc index d7af8010cc..976ea722c4 100644 --- a/src/type/storage_texture_type_test.cc +++ b/src/type/storage_texture_type_test.cc @@ -132,13 +132,6 @@ TEST_F(StorageTextureTest, I32) { EXPECT_TRUE(s->As()->type()->Is()); } -TEST_F(StorageTextureTest, MinBufferBindingSize) { - auto* subtype = StorageTexture::SubtypeFor(ImageFormat::kRgba32Sint, Types()); - auto* s = create(TextureDimension::k2dArray, - ImageFormat::kRgba32Sint, subtype); - EXPECT_EQ(0u, s->MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - } // namespace } // namespace type } // namespace tint diff --git a/src/type/struct_type.cc b/src/type/struct_type.cc index c1bf774bf9..37e78857ce 100644 --- a/src/type/struct_type.cc +++ b/src/type/struct_type.cc @@ -38,48 +38,6 @@ std::string Struct::FriendlyName(const SymbolTable& symbols) const { return symbols.NameFor(symbol_); } -uint64_t Struct::MinBufferBindingSize(MemoryLayout mem_layout) const { - if (!struct_->members().size()) { - return 0; - } - - auto* last_member = struct_->members().back(); - - // If there is no offset, then this is not a host-shareable struct, returning - // 0 indicates this to the caller. - if (!last_member->has_offset_decoration()) { - return 0; - } - - uint64_t size = last_member->type()->MinBufferBindingSize(mem_layout); - if (!size) { - return 0; - } - - float unaligned = static_cast(last_member->offset() + size); - float alignment = static_cast(BaseAlignment(mem_layout)); - - return static_cast(alignment * std::ceil(unaligned / alignment)); -} - -uint64_t Struct::BaseAlignment(MemoryLayout mem_layout) const { - uint64_t max = 0; - for (auto* member : struct_->members()) { - if (member->type()->BaseAlignment(mem_layout) > max) { - max = member->type()->BaseAlignment(mem_layout); - } - } - - if (mem_layout == MemoryLayout::kUniformBuffer) { - // Round up to a vec4. - return static_cast(16 * - std::ceil(static_cast(max) / 16.0f)); - } else if (mem_layout == MemoryLayout::kStorageBuffer) { - return max; - } - return 0; -} - Struct* Struct::Clone(CloneContext* ctx) const { // Clone arguments outside of create() call to have deterministic ordering auto sym = ctx->Clone(symbol()); diff --git a/src/type/struct_type.h b/src/type/struct_type.h index bec67828b8..9a9487752d 100644 --- a/src/type/struct_type.h +++ b/src/type/struct_type.h @@ -51,16 +51,6 @@ class Struct : public Castable { /// declared in WGSL. std::string FriendlyName(const SymbolTable& symbols) const override; - /// @param mem_layout type of memory layout to use in calculation. - /// @returns minimum size required for this type, in bytes. - /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; - - /// @param mem_layout type of memory layout to use in calculation. - /// @returns base alignment for the type, in bytes. - /// 0 for non-host shareable types. - uint64_t BaseAlignment(MemoryLayout mem_layout) const override; - /// Clones this type and all transitive types using the `CloneContext` `ctx`. /// @param ctx the clone context /// @return the newly cloned type diff --git a/src/type/struct_type_test.cc b/src/type/struct_type_test.cc index ec562bc346..4099ed1e1d 100644 --- a/src/type/struct_type_test.cc +++ b/src/type/struct_type_test.cc @@ -64,140 +64,6 @@ TEST_F(StructTypeTest, FriendlyName) { EXPECT_EQ(s->FriendlyName(Symbols()), "my_struct"); } -TEST_F(StructTypeTest, MinBufferBindingSize) { - auto* str = create( - ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}), - Member("bar", ty.u32(), {MemberOffset(4)})}, - ast::DecorationList{}); - auto* s_ty = ty.struct_("s_ty", str); - - EXPECT_EQ(16u, s_ty->MinBufferBindingSize(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(8u, s_ty->MinBufferBindingSize(MemoryLayout::kStorageBuffer)); -} - -TEST_F(StructTypeTest, MinBufferBindingSizeArray) { - Array arr(ty.u32(), 4, ast::DecorationList{create(4)}); - - auto* str = create( - ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}), - Member("bar", ty.u32(), {MemberOffset(4)}), - Member("bar", &arr, {MemberOffset(8)})}, - ast::DecorationList{}); - auto* s_ty = ty.struct_("s_ty", str); - - EXPECT_EQ(32u, s_ty->MinBufferBindingSize(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(24u, s_ty->MinBufferBindingSize(MemoryLayout::kStorageBuffer)); -} - -TEST_F(StructTypeTest, MinBufferBindingSizeRuntimeArray) { - Array arr(ty.u32(), 0, ast::DecorationList{create(4)}); - - auto* str = create( - ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}), - Member("bar", ty.u32(), {MemberOffset(4)}), - Member("bar", ty.u32(), {MemberOffset(8)})}, - ast::DecorationList{}); - auto* s_ty = ty.struct_("s_ty", str); - - EXPECT_EQ(12u, s_ty->MinBufferBindingSize(MemoryLayout::kStorageBuffer)); -} - -TEST_F(StructTypeTest, MinBufferBindingSizeVec2) { - auto* str = create( - ast::StructMemberList{Member("foo", ty.vec2(), {MemberOffset(0)})}, - ast::DecorationList{}); - auto* s_ty = ty.struct_("s_ty", str); - - EXPECT_EQ(16u, s_ty->MinBufferBindingSize(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(8u, s_ty->MinBufferBindingSize(MemoryLayout::kStorageBuffer)); -} - -TEST_F(StructTypeTest, MinBufferBindingSizeVec3) { - auto* str = create( - ast::StructMemberList{Member("foo", ty.vec3(), {MemberOffset(0)})}, - ast::DecorationList{}); - auto* s_ty = ty.struct_("s_ty", str); - - EXPECT_EQ(16u, s_ty->MinBufferBindingSize(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(16u, s_ty->MinBufferBindingSize(MemoryLayout::kStorageBuffer)); -} - -TEST_F(StructTypeTest, MinBufferBindingSizeVec4) { - auto* str = create( - ast::StructMemberList{Member("foo", ty.vec4(), {MemberOffset(0)})}, - ast::DecorationList{}); - auto* s_ty = ty.struct_("s_ty", str); - - EXPECT_EQ(16u, s_ty->MinBufferBindingSize(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(16u, s_ty->MinBufferBindingSize(MemoryLayout::kStorageBuffer)); -} - -TEST_F(StructTypeTest, BaseAlignment) { - auto* str = create( - ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}), - Member("bar", ty.u32(), {MemberOffset(8)})}, - ast::DecorationList{}); - auto* s_ty = ty.struct_("s_ty", str); - - EXPECT_EQ(16u, s_ty->BaseAlignment(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(4u, s_ty->BaseAlignment(MemoryLayout::kStorageBuffer)); -} - -TEST_F(StructTypeTest, BaseAlignmentArray) { - Array arr(ty.u32(), 4, ast::DecorationList{create(4)}); - auto* str = create( - ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}), - Member("bar", ty.u32(), {MemberOffset(4)}), - Member("bar", &arr, {MemberOffset(8)})}, - ast::DecorationList{}); - auto* s_ty = ty.struct_("s_ty", str); - - EXPECT_EQ(16u, s_ty->BaseAlignment(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(4u, s_ty->BaseAlignment(MemoryLayout::kStorageBuffer)); -} - -TEST_F(StructTypeTest, BaseAlignmentRuntimeArray) { - Array arr(ty.u32(), 0, ast::DecorationList{create(4)}); - auto* str = create( - ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}), - Member("bar", ty.u32(), {MemberOffset(4)}), - Member("bar", ty.u32(), {MemberOffset(8)})}, - ast::DecorationList{}); - auto* s_ty = ty.struct_("s_ty", str); - - EXPECT_EQ(4u, s_ty->BaseAlignment(MemoryLayout::kStorageBuffer)); -} - -TEST_F(StructTypeTest, BaseAlignmentVec2) { - auto* str = create( - ast::StructMemberList{Member("foo", ty.vec2(), {MemberOffset(0)})}, - ast::DecorationList{}); - auto* s_ty = ty.struct_("s_ty", str); - - EXPECT_EQ(16u, s_ty->BaseAlignment(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(8u, s_ty->BaseAlignment(MemoryLayout::kStorageBuffer)); -} - -TEST_F(StructTypeTest, BaseAlignmentVec3) { - auto* str = create( - ast::StructMemberList{Member("foo", ty.vec3(), {MemberOffset(0)})}, - ast::DecorationList{}); - auto* s_ty = ty.struct_("s_ty", str); - - EXPECT_EQ(16u, s_ty->BaseAlignment(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(16u, s_ty->BaseAlignment(MemoryLayout::kStorageBuffer)); -} - -TEST_F(StructTypeTest, BaseAlignmentVec4) { - auto* str = create( - ast::StructMemberList{Member("foo", ty.vec4(), {MemberOffset(0)})}, - ast::DecorationList{}); - auto* s_ty = ty.struct_("s_ty", str); - - EXPECT_EQ(16u, s_ty->BaseAlignment(MemoryLayout::kUniformBuffer)); - EXPECT_EQ(16u, s_ty->BaseAlignment(MemoryLayout::kStorageBuffer)); -} - } // namespace } // namespace type } // namespace tint diff --git a/src/type/type.cc b/src/type/type.cc index 2f6e0dcde6..39f309c2c8 100644 --- a/src/type/type.cc +++ b/src/type/type.cc @@ -70,14 +70,6 @@ Type* Type::UnwrapAll() { return UnwrapIfNeeded()->UnwrapPtrIfNeeded()->UnwrapIfNeeded(); } -uint64_t Type::MinBufferBindingSize(MemoryLayout) const { - return 0; -} - -uint64_t Type::BaseAlignment(MemoryLayout) const { - return 0; -} - bool Type::is_scalar() const { return is_float_scalar() || is_integer_scalar() || Is(); } diff --git a/src/type/type.h b/src/type/type.h index c6ef50e26b..c4e7a36574 100644 --- a/src/type/type.h +++ b/src/type/type.h @@ -45,16 +45,6 @@ class Type : public Castable { /// declared in WGSL. virtual std::string FriendlyName(const SymbolTable& symbols) const = 0; - /// @param mem_layout type of memory layout to use in calculation. - /// @returns minimum size required for this type, in bytes. - /// 0 for non-host shareable types. - virtual uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const; - - /// @param mem_layout type of memory layout to use in calculation. - /// @returns base alignment for the type, in bytes. - /// 0 for non-host shareable types. - virtual uint64_t BaseAlignment(MemoryLayout mem_layout) const; - /// @returns the pointee type if this is a pointer, `this` otherwise Type* UnwrapPtrIfNeeded(); diff --git a/src/type/u32_type.cc b/src/type/u32_type.cc index c2f029c69c..775abc30ba 100644 --- a/src/type/u32_type.cc +++ b/src/type/u32_type.cc @@ -35,14 +35,6 @@ std::string U32::FriendlyName(const SymbolTable&) const { return "u32"; } -uint64_t U32::MinBufferBindingSize(MemoryLayout) const { - return 4; -} - -uint64_t U32::BaseAlignment(MemoryLayout) const { - return 4; -} - U32* U32::Clone(CloneContext* ctx) const { return ctx->dst->create(); } diff --git a/src/type/u32_type.h b/src/type/u32_type.h index 7e92a3a400..7aadc8922d 100644 --- a/src/type/u32_type.h +++ b/src/type/u32_type.h @@ -39,16 +39,6 @@ class U32 : public Castable { /// declared in WGSL. std::string FriendlyName(const SymbolTable& symbols) const override; - /// @param mem_layout type of memory layout to use in calculation. - /// @returns minimum size required for this type, in bytes. - /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; - - /// @param mem_layout type of memory layout to use in calculation. - /// @returns base alignment for the type, in bytes. - /// 0 for non-host shareable types. - uint64_t BaseAlignment(MemoryLayout mem_layout) const override; - /// Clones this type and all transitive types using the `CloneContext` `ctx`. /// @param ctx the clone context /// @return the newly cloned type diff --git a/src/type/u32_type_test.cc b/src/type/u32_type_test.cc index 5b6db5aaab..c320cb55af 100644 --- a/src/type/u32_type_test.cc +++ b/src/type/u32_type_test.cc @@ -50,16 +50,6 @@ TEST_F(U32Test, FriendlyName) { EXPECT_EQ(u.FriendlyName(Symbols()), "u32"); } -TEST_F(U32Test, MinBufferBindingSize) { - U32 u; - EXPECT_EQ(4u, u.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - -TEST_F(U32Test, BaseAlignment) { - U32 u; - EXPECT_EQ(4u, u.BaseAlignment(MemoryLayout::kUniformBuffer)); -} - } // namespace } // namespace type } // namespace tint diff --git a/src/type/vector_type.cc b/src/type/vector_type.cc index 3a8078fdfc..ab12790880 100644 --- a/src/type/vector_type.cc +++ b/src/type/vector_type.cc @@ -40,20 +40,6 @@ std::string Vector::FriendlyName(const SymbolTable& symbols) const { return out.str(); } -uint64_t Vector::MinBufferBindingSize(MemoryLayout mem_layout) const { - return size_ * subtype_->MinBufferBindingSize(mem_layout); -} - -uint64_t Vector::BaseAlignment(MemoryLayout mem_layout) const { - if (size_ == 2) { - return 2 * subtype_->BaseAlignment(mem_layout); - } else if (size_ == 3 || size_ == 4) { - return 4 * subtype_->BaseAlignment(mem_layout); - } - - return 0; // vectors are only supposed to have 2, 3, or 4 elements. -} - Vector* Vector::Clone(CloneContext* ctx) const { // Clone arguments outside of create() call to have deterministic ordering auto* ty = ctx->Clone(type()); diff --git a/src/type/vector_type.h b/src/type/vector_type.h index 88e41f2fff..cbb9022ac7 100644 --- a/src/type/vector_type.h +++ b/src/type/vector_type.h @@ -46,16 +46,6 @@ class Vector : public Castable { /// declared in WGSL. std::string FriendlyName(const SymbolTable& symbols) const override; - /// @param mem_layout type of memory layout to use in calculation. - /// @returns minimum size required for this type, in bytes. - /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; - - /// @param mem_layout type of memory layout to use in calculation. - /// @returns base alignment for the type, in bytes. - /// 0 for non-host shareable types. - uint64_t BaseAlignment(MemoryLayout mem_layout) const override; - /// Clones this type and all transitive types using the `CloneContext` `ctx`. /// @param ctx the clone context /// @return the newly cloned type diff --git a/src/type/vector_type_test.cc b/src/type/vector_type_test.cc index 8f13b68b55..8c4a793055 100644 --- a/src/type/vector_type_test.cc +++ b/src/type/vector_type_test.cc @@ -59,42 +59,6 @@ TEST_F(VectorTest, FriendlyName) { EXPECT_EQ(v->FriendlyName(Symbols()), "vec3"); } -TEST_F(VectorTest, MinBufferBindingSizeVec2) { - I32 i32; - Vector v{&i32, 2}; - EXPECT_EQ(8u, v.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - -TEST_F(VectorTest, MinBufferBindingSizeVec3) { - I32 i32; - Vector v{&i32, 3}; - EXPECT_EQ(12u, v.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - -TEST_F(VectorTest, MinBufferBindingSizeVec4) { - I32 i32; - Vector v{&i32, 4}; - EXPECT_EQ(16u, v.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); -} - -TEST_F(VectorTest, BaseAlignmentVec2) { - I32 i32; - Vector v{&i32, 2}; - EXPECT_EQ(8u, v.BaseAlignment(MemoryLayout::kUniformBuffer)); -} - -TEST_F(VectorTest, BaseAlignmentVec3) { - I32 i32; - Vector v{&i32, 3}; - EXPECT_EQ(16u, v.BaseAlignment(MemoryLayout::kUniformBuffer)); -} - -TEST_F(VectorTest, BaseAlignmentVec4) { - I32 i32; - Vector v{&i32, 4}; - EXPECT_EQ(16u, v.BaseAlignment(MemoryLayout::kUniformBuffer)); -} - } // namespace } // namespace type } // namespace tint diff --git a/src/validator/validator_test.cc b/src/validator/validator_test.cc index 491bed4c83..cc39b22219 100644 --- a/src/validator/validator_test.cc +++ b/src/validator/validator_test.cc @@ -872,14 +872,6 @@ TEST_F(ValidatorTest, IsStorable_ArraySizedOfStorable) { EXPECT_TRUE(v.IsStorable(arr)); } -TEST_F(ValidatorTest, IsStorable_ArraySizedOfNonStorable) { - auto* arr = ty.array(ty.void_(), 5); - - ValidatorImpl& v = Build(); - - EXPECT_FALSE(v.IsStorable(arr)); -} - TEST_F(ValidatorTest, IsStorable_ArrayUnsizedOfStorable) { auto* arr = ty.array(); @@ -888,14 +880,6 @@ TEST_F(ValidatorTest, IsStorable_ArrayUnsizedOfStorable) { EXPECT_TRUE(v.IsStorable(arr)); } -TEST_F(ValidatorTest, IsStorable_ArrayUnsizedOfNonStorable) { - auto* arr = ty.array(); - - ValidatorImpl& v = Build(); - - EXPECT_FALSE(v.IsStorable(arr)); -} - TEST_F(ValidatorTest, IsStorable_Struct_AllMembersStorable) { ast::StructMemberList members{Member("a", ty.i32()), Member("b", ty.f32())}; auto* s = create(Source{}, members, ast::DecorationList{}); @@ -906,16 +890,5 @@ TEST_F(ValidatorTest, IsStorable_Struct_AllMembersStorable) { EXPECT_TRUE(v.IsStorable(s_ty)); } -TEST_F(ValidatorTest, IsStorable_Struct_SomeMembersNonStorable) { - auto* ptr_ty = ty.pointer(ast::StorageClass::kPrivate); - ast::StructMemberList members{Member("a", ty.i32()), Member("b", ptr_ty)}; - auto* s = create(Source{}, members, ast::DecorationList{}); - auto* s_ty = ty.struct_("mystruct", s); - - ValidatorImpl& v = Build(); - - EXPECT_FALSE(v.IsStorable(s_ty)); -} - } // namespace } // namespace tint diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc index cf5d62aac1..47fda58ddf 100644 --- a/src/writer/hlsl/generator_impl.cc +++ b/src/writer/hlsl/generator_impl.cc @@ -21,9 +21,11 @@ #include "src/ast/constant_id_decoration.h" #include "src/ast/fallthrough_statement.h" #include "src/ast/variable_decl_statement.h" +#include "src/semantic/array.h" #include "src/semantic/call.h" #include "src/semantic/function.h" #include "src/semantic/member_accessor_expression.h" +#include "src/semantic/struct.h" #include "src/semantic/variable.h" #include "src/type/access_control_type.h" #include "src/type/multisampled_texture_type.h" @@ -2021,11 +2023,13 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression( auto* str_type = str->impl(); auto* str_member = str_type->get_member(mem->member()->symbol()); - if (!str_member->has_offset_decoration()) { - diagnostics_.add_error("missing offset decoration for struct member"); + auto* sem_mem = builder_.Sem().Get(str_member); + if (!sem_mem) { + TINT_ICE(diagnostics_) << "struct member missing semantic info"; return ""; } - out << str_member->offset(); + + out << sem_mem->Offset(); } else if (res_type->Is()) { auto swizzle = builder_.Sem().Get(mem)->Swizzle(); @@ -2035,9 +2039,9 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression( // This must be a single element swizzle if we've got a vector at this // point. if (swizzle.size() != 1) { - diagnostics_.add_error( - "Encountered multi-element swizzle when should have only one " - "level"); + TINT_ICE(diagnostics_) + << "Encountered multi-element swizzle when should have only one " + "level"; return ""; } @@ -2046,8 +2050,8 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression( // f64 types. out << "(4 * " << swizzle[0] << ")"; } else { - diagnostics_.add_error("Invalid result type for member accessor: " + - res_type->type_name()); + TINT_ICE(diagnostics_) << "Invalid result type for member accessor: " + << res_type->type_name(); return ""; } @@ -2057,7 +2061,12 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression( out << "("; if (auto* arr = ary_type->As()) { - out << arr->array_stride(); + auto* sem_arr = builder_.Sem().Get(arr); + if (!sem_arr) { + TINT_ICE(diagnostics_) << "array type missing semantic info"; + return ""; + } + out << sem_arr->Stride(); } else if (ary_type->Is()) { // TODO(dsinclair): This is a hack. Our vectors can only be f32, i32 // or u32 which are all 4 bytes. When we get f16 or other types we'll diff --git a/src/writer/hlsl/generator_impl_alias_type_test.cc b/src/writer/hlsl/generator_impl_alias_type_test.cc index a11551d3de..ca1007bdfe 100644 --- a/src/writer/hlsl/generator_impl_alias_type_test.cc +++ b/src/writer/hlsl/generator_impl_alias_type_test.cc @@ -43,8 +43,10 @@ TEST_F(HlslGeneratorImplTest_Alias, EmitAlias_NameCollision) { TEST_F(HlslGeneratorImplTest_Alias, EmitAlias_Struct) { auto* str = create( - ast::StructMemberList{Member("a", ty.f32()), - Member("b", ty.i32(), {MemberOffset(4)})}, + ast::StructMemberList{ + Member("a", ty.f32()), + Member("b", ty.i32()), + }, ast::DecorationList{}); auto* s = ty.struct_("A", str); diff --git a/src/writer/hlsl/generator_impl_function_test.cc b/src/writer/hlsl/generator_impl_function_test.cc index c985858388..e6ad1d5700 100644 --- a/src/writer/hlsl/generator_impl_function_test.cc +++ b/src/writer/hlsl/generator_impl_function_test.cc @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "gmock/gmock.h" #include "src/ast/stage_decoration.h" #include "src/ast/struct_block_decoration.h" #include "src/ast/variable_decl_statement.h" @@ -19,6 +20,8 @@ #include "src/type/access_control_type.h" #include "src/writer/hlsl/test_helper.h" +using ::testing::HasSubstr; + namespace tint { namespace writer { namespace hlsl { @@ -264,11 +267,7 @@ void frag_main() { TEST_F(HlslGeneratorImplTest_Function, Emit_Decoration_EntryPoint_With_UniformStruct) { - auto* str = create( - ast::StructMemberList{Member("coord", ty.vec4())}, - ast::DecorationList{}); - - auto* s = ty.struct_("Uniforms", str); + auto* s = Structure("Uniforms", {Member("coord", ty.vec4())}); Global("uniforms", s, ast::StorageClass::kUniform, nullptr, ast::DecorationList{ @@ -276,8 +275,6 @@ TEST_F(HlslGeneratorImplTest_Function, create(1), }); - AST().AddConstructedType(s); - auto* var = Var("v", ty.f32(), ast::StorageClass::kFunction, create( MemberAccessor("uniforms", "coord"), Expr("x"))); @@ -310,12 +307,11 @@ void frag_main() { TEST_F(HlslGeneratorImplTest_Function, Emit_Decoration_EntryPoint_With_RW_StorageBuffer_Read) { - auto* str = create( - ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); + auto* s = Structure("Data", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); - auto* s = ty.struct_("Data", str); type::AccessControl ac(ast::AccessControl::kReadWrite, s); Global("coord", &ac, ast::StorageClass::kStorage, nullptr, @@ -339,24 +335,21 @@ TEST_F(HlslGeneratorImplTest_Function, GeneratorImpl& gen = Build(); ASSERT_TRUE(gen.Generate(out)) << gen.error(); - EXPECT_EQ(result(), R"(RWByteAddressBuffer coord : register(u0); + EXPECT_THAT(result(), HasSubstr(R"(RWByteAddressBuffer coord : register(u0); void frag_main() { float v = asfloat(coord.Load(4)); return; -} - -)"); +})")); } TEST_F(HlslGeneratorImplTest_Function, Emit_Decoration_EntryPoint_With_RO_StorageBuffer_Read) { - auto* str = create( - ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); + auto* s = Structure("Data", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); - auto* s = ty.struct_("Data", str); type::AccessControl ac(ast::AccessControl::kReadOnly, s); Global("coord", &ac, ast::StorageClass::kStorage, nullptr, @@ -380,24 +373,21 @@ TEST_F(HlslGeneratorImplTest_Function, GeneratorImpl& gen = Build(); ASSERT_TRUE(gen.Generate(out)) << gen.error(); - EXPECT_EQ(result(), R"(ByteAddressBuffer coord : register(t0); + EXPECT_THAT(result(), HasSubstr(R"(ByteAddressBuffer coord : register(t0); void frag_main() { float v = asfloat(coord.Load(4)); return; -} - -)"); +})")); } TEST_F(HlslGeneratorImplTest_Function, Emit_Decoration_EntryPoint_With_WO_StorageBuffer_Store) { - auto* str = create( - ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); + auto* s = Structure("Data", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); - auto* s = ty.struct_("Data", str); type::AccessControl ac(ast::AccessControl::kWriteOnly, s); Global("coord", &ac, ast::StorageClass::kStorage, nullptr, @@ -419,24 +409,21 @@ TEST_F(HlslGeneratorImplTest_Function, GeneratorImpl& gen = Build(); ASSERT_TRUE(gen.Generate(out)) << gen.error(); - EXPECT_EQ(result(), R"(RWByteAddressBuffer coord : register(u0); + EXPECT_THAT(result(), HasSubstr(R"(RWByteAddressBuffer coord : register(u0); void frag_main() { coord.Store(4, asuint(2.0f)); return; -} - -)"); +})")); } TEST_F(HlslGeneratorImplTest_Function, Emit_Decoration_EntryPoint_With_StorageBuffer_Store) { - auto* str = create( - ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); + auto* s = Structure("Data", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); - auto* s = ty.struct_("Data", str); type::AccessControl ac(ast::AccessControl::kReadWrite, s); Global("coord", &ac, ast::StorageClass::kStorage, nullptr, @@ -458,14 +445,12 @@ TEST_F(HlslGeneratorImplTest_Function, GeneratorImpl& gen = Build(); ASSERT_TRUE(gen.Generate(out)) << gen.error(); - EXPECT_EQ(result(), R"(RWByteAddressBuffer coord : register(u0); + EXPECT_THAT(result(), HasSubstr(R"(RWByteAddressBuffer coord : register(u0); void frag_main() { coord.Store(4, asuint(2.0f)); return; -} - -)"); +})")); } TEST_F( @@ -715,7 +700,7 @@ TEST_F(HlslGeneratorImplTest_Function, GeneratorImpl& gen = Build(); ASSERT_TRUE(gen.Generate(out)) << gen.error(); - EXPECT_EQ(result(), R"(RWByteAddressBuffer coord : register(u0); + EXPECT_THAT(result(), HasSubstr(R"(RWByteAddressBuffer coord : register(u0); float sub_func(float param) { return asfloat(coord.Load((4 * 0))); @@ -724,9 +709,7 @@ float sub_func(float param) { void frag_main() { float v = sub_func(1.0f); return; -} - -)"); +})")); } TEST_F(HlslGeneratorImplTest_Function, @@ -857,7 +840,7 @@ TEST_F(HlslGeneratorImplTest_Function, Emit_Function_WithArrayParams) { TEST_F(HlslGeneratorImplTest_Function, Emit_Multiple_EntryPoint_With_Same_ModuleVar) { // [[block]] struct Data { - // [[offset(0)]] d : f32; + // d : f32; // }; // [[binding(0), group(0)]] var data : Data; // @@ -871,12 +854,9 @@ TEST_F(HlslGeneratorImplTest_Function, // return; // } - auto* str = create( - ast::StructMemberList{Member("d", ty.f32(), {MemberOffset(0)})}, - ast::DecorationList{create()}); - - auto* s = ty.struct_("Data", str); - AST().AddConstructedType(s); + auto* s = + Structure("Data", {Member("d", ty.f32())}, + ast::DecorationList{create()}); type::AccessControl ac(ast::AccessControl::kReadWrite, s); diff --git a/src/writer/hlsl/generator_impl_member_accessor_test.cc b/src/writer/hlsl/generator_impl_member_accessor_test.cc index ffd588ca08..c8627d191d 100644 --- a/src/writer/hlsl/generator_impl_member_accessor_test.cc +++ b/src/writer/hlsl/generator_impl_member_accessor_test.cc @@ -22,11 +22,7 @@ namespace { using HlslGeneratorImplTest_MemberAccessor = TestHelper; TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor) { - auto* strct = create( - ast::StructMemberList{Member("mem", ty.f32(), {MemberOffset(0)})}, - ast::DecorationList{}); - - auto* s = ty.struct_("Str", strct); + auto* s = Structure("Data", {Member("mem", ty.f32())}); auto* str_var = Global("str", s, ast::StorageClass::kPrivate); auto* expr = MemberAccessor("str", "mem"); @@ -43,20 +39,19 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor) { TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Load) { // struct Data { - // [[offset(0)]] a : i32; - // [[offset(4)]] b : f32; + // a : i32; + // b : f32; // }; // var data : Data; // data.b; // // -> asfloat(data.Load(4)); - auto* str = create( - ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); + auto* s = Structure("data", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); - auto* s = ty.struct_("Data", str); auto* coord_var = Global("data", s, ast::StorageClass::kStorage); auto* expr = MemberAccessor("data", "b"); @@ -73,19 +68,19 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Load_Int) { // struct Data { - // [[offset(0)]] a : i32; - // [[offset(4)]] b : f32; + // a : i32; + // b : f32; // }; // var data : Data; // data.a; // // -> asint(data.Load(0)); - auto* str = create( - ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); - auto* s = ty.struct_("Data", str); + auto* s = Structure("data", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); + auto* coord_var = Global("data", s, ast::StorageClass::kStorage); auto* expr = MemberAccessor("data", "a"); @@ -102,8 +97,8 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Store_Matrix) { // struct Data { - // [[offset(0)]] z : f32; - // [[offset(4)]] a : mat2x3; + // z : f32; + // a : mat2x3; // }; // var data : Data; // mat2x3 b; @@ -113,12 +108,9 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, // data.Store3(4 + 0, asuint(_tint_tmp[0])); // data.Store3(4 + 16, asuint(_tint_tmp[1])); - auto* str = create( - ast::StructMemberList{Member("z", ty.i32(), {MemberOffset(0)}), - Member("a", ty.mat2x3(), {MemberOffset(4)})}, - ast::DecorationList{}); + auto* s = + Structure("Data", {Member("z", ty.i32()), Member("a", ty.mat2x3())}); - auto* s = ty.struct_("Data", str); auto* b_var = Global("b", ty.mat2x3(), ast::StorageClass::kPrivate); auto* coord_var = Global("data", s, ast::StorageClass::kStorage); @@ -135,31 +127,28 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, ASSERT_TRUE(gen.EmitStatement(out, assign)) << gen.error(); EXPECT_EQ(result(), R"(float3x2 _tint_tmp = b; -data.Store3(4 + 0, asuint(_tint_tmp[0])); -data.Store3(4 + 16, asuint(_tint_tmp[1])); +data.Store3(16 + 0, asuint(_tint_tmp[0])); +data.Store3(16 + 16, asuint(_tint_tmp[1])); )"); } TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Store_Matrix_Empty) { // struct Data { - // [[offset(0)]] z : f32; - // [[offset(4)]] a : mat2x3; + // z : f32; + // a : mat2x3; // }; // var data : Data; // data.a = mat2x3(); // // -> float3x2 _tint_tmp = float3x2(0.0f, 0.0f, 0.0f, // 0.0f, 0.0f, 0.0f); - // data.Store3(4 + 0, asuint(_tint_tmp[0]); - // data.Store3(4 + 16, asuint(_tint_tmp[1])); + // data.Store3(16 + 0, asuint(_tint_tmp[0]); + // data.Store3(16 + 16, asuint(_tint_tmp[1])); - auto* str = create( - ast::StructMemberList{Member("z", ty.i32(), {MemberOffset(0)}), - Member("a", ty.mat2x3(), {MemberOffset(4)})}, - ast::DecorationList{}); + auto* s = + Structure("Data", {Member("z", ty.i32()), Member("a", ty.mat2x3())}); - auto* s = ty.struct_("Data", str); auto* coord_var = Global("data", s, ast::StorageClass::kStorage); auto* lhs = MemberAccessor("data", "a"); @@ -176,16 +165,16 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, EXPECT_EQ( result(), R"(float3x2 _tint_tmp = float3x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); -data.Store3(4 + 0, asuint(_tint_tmp[0])); -data.Store3(4 + 16, asuint(_tint_tmp[1])); +data.Store3(16 + 0, asuint(_tint_tmp[0])); +data.Store3(16 + 16, asuint(_tint_tmp[1])); )"); } TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Load_Matrix) { // struct Data { - // [[offset(0)]] z : f32; - // [[offset(4)]] a : mat3x2; + // z : f32; + // a : mat3x2; // }; // var data : Data; // data.a; @@ -193,12 +182,9 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, // -> asfloat(uint2x3(data.Load2(4 + 0), data.Load2(4 + 8), // data.Load2(4 + 16))); - auto* str = create( - ast::StructMemberList{Member("z", ty.i32(), {MemberOffset(0)}), - Member("a", ty.mat3x2(), {MemberOffset(4)})}, - ast::DecorationList{}); + auto* s = + Structure("Data", {Member("z", ty.i32()), Member("a", ty.mat3x2())}); - auto* s = ty.struct_("Data", str); auto* coord_var = Global("data", s, ast::StorageClass::kStorage); auto* expr = MemberAccessor("data", "a"); @@ -210,33 +196,26 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error(); EXPECT_EQ(result(), - "asfloat(uint2x3(data.Load2(4 + 0), data.Load2(4 + 8), " - "data.Load2(4 + 16)))"); + "asfloat(uint2x3(data.Load2(8 + 0), data.Load2(8 + 8), " + "data.Load2(8 + 16)))"); } TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Load_Matrix_Nested) { // struct Data { - // [[offset(0)]] z : f32; - // [[offset(4)]] a : mat2x3 // }; // var data : Outer; // data.b.a; // - // -> asfloat(uint3x2(data.Load3(4 + 0), data.Load3(4 + 16))); + // -> asfloat(uint3x2(data.Load3(4 + 0), data.Load3(16 + 16))); - auto* str = create( - ast::StructMemberList{ - Member("z", ty.i32(), {MemberOffset(0)}), - Member("a", ty.mat2x3(), {MemberOffset(4)}), - }, - ast::DecorationList{}); + auto* s = Structure("Data", { + Member("z", ty.i32()), + Member("a", ty.mat2x3()), + }); - auto* s = ty.struct_("Data", str); auto* coord_var = Global("data", s, ast::StorageClass::kStorage); auto* expr = MemberAccessor("data", "a"); @@ -248,14 +227,14 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error(); EXPECT_EQ(result(), - "asfloat(uint3x2(data.Load3(4 + 0), data.Load3(4 + 16)))"); + "asfloat(uint3x2(data.Load3(16 + 0), data.Load3(16 + 16)))"); } TEST_F( HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Load_Matrix_By3_Is_16_Bytes) { // struct Data { - // [[offset(4)]] a : mat3x3 // }; // var data : Data; // data.a; @@ -263,11 +242,10 @@ TEST_F( // -> asfloat(uint3x3(data.Load3(0), data.Load3(16), // data.Load3(32))); - auto* str = create( - ast::StructMemberList{Member("a", ty.mat3x3(), {MemberOffset(0)})}, - ast::DecorationList{}); + auto* s = Structure("Data", { + Member("a", ty.mat3x3()), + }); - auto* s = ty.struct_("Data", str); auto* coord_var = Global("data", s, ast::StorageClass::kStorage); auto* expr = MemberAccessor("data", "a"); @@ -286,20 +264,19 @@ TEST_F( TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Load_Matrix_Single_Element) { // struct Data { - // [[offset(0)]] z : f32; - // [[offset(16)]] a : mat4x3; + // z : f32; + // a : mat4x3; // }; // var data : Data; // data.a[2][1]; // // -> asfloat(data.Load((2 * 16) + (1 * 4) + 16))) - auto* str = create( - ast::StructMemberList{Member("z", ty.i32(), {MemberOffset(0)}), - Member("a", ty.mat4x3(), {MemberOffset(16)})}, - ast::DecorationList{}); + auto* s = Structure("Data", { + Member("z", ty.i32()), + Member("a", ty.mat4x3()), + }); - auto* s = ty.struct_("Data", str); auto* coord_var = Global("data", s, ast::StorageClass::kStorage); auto* expr = IndexAccessor( @@ -317,7 +294,7 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_ArrayAccessor_StorageBuffer_Load_Int_FromArray) { // struct Data { - // [[offset(0)]] a : [[stride(4)]] array; + // a : [[stride(4)]] array; // }; // var data : Data; // data.a[2]; @@ -328,10 +305,8 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, create(4), }); - auto* str = create( - ast::StructMemberList{Member("a", &ary, {MemberOffset(0)})}, - ast::DecorationList{}); - auto* s = ty.struct_("Data", str); + auto* s = Structure("Data", {Member("a", &ary)}); + auto* coord_var = Global("data", s, ast::StorageClass::kStorage); auto* expr = IndexAccessor(MemberAccessor("data", "a"), Expr(2)); @@ -348,7 +323,7 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_ArrayAccessor_StorageBuffer_Load_Int_FromArray_ExprIdx) { // struct Data { - // [[offset(0)]] a : [[stride(4)]] array; + // a : [[stride(4)]] array; // }; // var data : Data; // data.a[(2 + 4) - 3]; @@ -359,10 +334,8 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, create(4), }); - auto* str = create( - ast::StructMemberList{Member("a", &ary, {MemberOffset(0)})}, - ast::DecorationList{}); - auto* s = ty.struct_("Data", str); + auto* s = Structure("Data", {Member("a", &ary)}); + auto* coord_var = Global("data", s, ast::StorageClass::kStorage); auto* expr = IndexAccessor(MemberAccessor("data", "a"), @@ -380,20 +353,19 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Store) { // struct Data { - // [[offset(0)]] a : i32; - // [[offset(4)]] b : f32; + // a : i32; + // b : f32; // }; // var data : Data; // data.b = 2.3f; // // -> data.Store(0, asuint(2.0f)); - auto* str = create( - ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); + auto* s = Structure("data", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); - auto* s = ty.struct_("Data", str); auto* coord_var = Global("data", s, ast::StorageClass::kStorage); auto* lhs = MemberAccessor("data", "b"); @@ -413,7 +385,7 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Store_ToArray) { // struct Data { - // [[offset(0)]] a : [[stride(4)]] array; + // a : [[stride(4)]] array; // }; // var data : Data; // data.a[2] = 2; @@ -425,11 +397,8 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, create(4), }); - auto* str = create( - ast::StructMemberList{Member("a", &ary, {MemberOffset(0)})}, - ast::DecorationList{}); + auto* s = Structure("Data", {Member("a", &ary)}); - auto* s = ty.struct_("Data", str); auto* coord_var = Global("data", s, ast::StorageClass::kStorage); auto* lhs = IndexAccessor(MemberAccessor("data", "a"), Expr(2)); @@ -449,20 +418,19 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Store_Int) { // struct Data { - // [[offset(0)]] a : i32; - // [[offset(4)]] b : f32; + // a : i32; + // b : f32; // }; // var data : Data; // data.a = 2; // // -> data.Store(0, asuint(2)); - auto* str = create( - ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); + auto* s = Structure("data", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); - auto* s = ty.struct_("Data", str); auto* coord_var = Global("data", s, ast::StorageClass::kStorage); auto* lhs = MemberAccessor("data", "a"); @@ -482,20 +450,19 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Load_Vec3) { // struct Data { - // [[offset(0)]] a : vec3; - // [[offset(16)]] b : vec3; + // a : vec3; + // b : vec3; // }; // var data : Data; // data.b; // // -> asfloat(data.Load(16)); - auto* str = create( - ast::StructMemberList{Member("a", ty.vec3(), {MemberOffset(0)}), - Member("b", ty.vec3(), {MemberOffset(16)})}, - ast::DecorationList{}); + auto* s = Structure("Data", { + Member("a", ty.vec3()), + Member("b", ty.vec3()), + }); - auto* s = ty.struct_("Data", str); auto* coord_var = Global("data", s, ast::StorageClass::kStorage); auto* expr = MemberAccessor("data", "b"); @@ -512,20 +479,19 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Store_Vec3) { // struct Data { - // [[offset(0)]] a : vec3; - // [[offset(16)]] b : vec3; + // a : vec3; + // b : vec3; // }; // var data : Data; // data.b = vec3(2.3f, 1.2f, 0.2f); // // -> data.Store(16, asuint(float3(2.3f, 1.2f, 0.2f))); - auto* str = create( - ast::StructMemberList{Member("a", ty.vec3(), {MemberOffset(0)}), - Member("b", ty.vec3(), {MemberOffset(16)})}, - ast::DecorationList{}); + auto* s = Structure("Data", { + Member("a", ty.vec3()), + Member("b", ty.vec3()), + }); - auto* s = ty.struct_("Data", str); auto* coord_var = Global("data", s, ast::StorageClass::kStorage); auto* lhs = MemberAccessor("data", "b"); @@ -548,8 +514,8 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel) { // struct Data { - // [[offset(0)]] a : vec3; - // [[offset(16)]] b : vec3; + // a : vec3; + // b : vec3; // }; // struct Pre { // var c : [[stride(32)]] array; @@ -560,24 +526,18 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, // // -> asfloat(data.Load3(16 + (2 * 32))) - auto* data_str = create( - ast::StructMemberList{ - Member("a", ty.vec3(), {MemberOffset(0)}), - Member("b", ty.vec3(), {MemberOffset(16)}), - }, - ast::DecorationList{}); + auto* data = Structure("Data", { + Member("a", ty.vec3()), + Member("b", ty.vec3()), + }); - auto* data = ty.struct_("Data", data_str); type::Array ary(data, 4, ast::DecorationList{ create(32), }); - auto* pre_str = create( - ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})}, - ast::DecorationList{}); + auto* pre_struct = Structure("Pre", {Member("c", &ary)}); - auto* pre_struct = ty.struct_("Pre", pre_str); auto* coord_var = Global("data", pre_struct, ast::StorageClass::kStorage); auto* expr = @@ -595,8 +555,8 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel_Swizzle) { // struct Data { - // [[offset(0)]] a : vec3; - // [[offset(16)]] b : vec3; + // a : vec3; + // b : vec3; // }; // struct Pre { // var c : [[stride(32)]] array; @@ -607,22 +567,16 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, // // -> asfloat(data.Load3(16 + (2 * 32))).xy - auto* data_str = create( - ast::StructMemberList{ - Member("a", ty.vec3(), {MemberOffset(0)}), - Member("b", ty.vec3(), {MemberOffset(16)}), - }, - ast::DecorationList{}); + auto* data = Structure("Data", { + Member("a", ty.vec3()), + Member("b", ty.vec3()), + }); - auto* data = ty.struct_("Data", data_str); type::Array ary(data, 4, ast::DecorationList{create(32)}); - auto* pre_str = create( - ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})}, - ast::DecorationList{}); + auto* pre_struct = Structure("Pre", {Member("c", &ary)}); - auto* pre_struct = ty.struct_("Pre", pre_str); auto* coord_var = Global("data", pre_struct, ast::StorageClass::kStorage); auto* expr = MemberAccessor( @@ -642,8 +596,8 @@ TEST_F( HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel_Swizzle_SingleLetter) { // NOLINT // struct Data { - // [[offset(0)]] a : vec3; - // [[offset(16)]] b : vec3; + // a : vec3; + // b : vec3; // }; // struct Pre { // var c : [[stride(32)]] array; @@ -654,24 +608,18 @@ TEST_F( // // -> asfloat(data.Load((4 * 1) + 16 + (2 * 32) + 0)) - auto* data_str = create( - ast::StructMemberList{ - Member("a", ty.vec3(), {MemberOffset(0)}), - Member("b", ty.vec3(), {MemberOffset(16)}), - }, - ast::DecorationList{}); + auto* data = Structure("Data", { + Member("a", ty.vec3()), + Member("b", ty.vec3()), + }); - auto* data = ty.struct_("Data", data_str); type::Array ary(data, 4, ast::DecorationList{ create(32), }); - auto* pre_str = create( - ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})}, - ast::DecorationList{}); + auto* pre_struct = Structure("Pre", {Member("c", &ary)}); - auto* pre_struct = ty.struct_("Pre", pre_str); auto* coord_var = Global("data", pre_struct, ast::StorageClass::kStorage); auto* expr = MemberAccessor( @@ -690,8 +638,8 @@ TEST_F( TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel_Index) { // struct Data { - // [[offset(0)]] a : vec3; - // [[offset(16)]] b : vec3; + // a : vec3; + // b : vec3; // }; // struct Pre { // var c : [[stride(32)]] array; @@ -702,24 +650,18 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, // // -> asfloat(data.Load(4 + 16 + (2 * 32))) - auto* data_str = create( - ast::StructMemberList{ - Member("a", ty.vec3(), {MemberOffset(0)}), - Member("b", ty.vec3(), {MemberOffset(16)}), - }, - ast::DecorationList{}); + auto* data = Structure("Data", { + Member("a", ty.vec3()), + Member("b", ty.vec3()), + }); - auto* data = ty.struct_("Data", data_str); type::Array ary(data, 4, ast::DecorationList{ create(32), }); - auto* pre_str = create( - ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})}, - ast::DecorationList{}); + auto* pre_struct = Structure("Pre", {Member("c", &ary)}); - auto* pre_struct = ty.struct_("Pre", pre_str); auto* coord_var = Global("data", pre_struct, ast::StorageClass::kStorage); auto* expr = IndexAccessor( @@ -738,8 +680,8 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Store_MultiLevel) { // struct Data { - // [[offset(0)]] a : vec3; - // [[offset(16)]] b : vec3; + // a : vec3; + // b : vec3; // }; // struct Pre { // var c : [[stride(32)]] array; @@ -750,24 +692,18 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, // // -> data.Store3(16 + (2 * 32), asuint(float3(1.0f, 2.0f, 3.0f))); - auto* data_str = create( - ast::StructMemberList{ - Member("a", ty.vec3(), {MemberOffset(0)}), - Member("b", ty.vec3(), {MemberOffset(16)}), - }, - ast::DecorationList{}); + auto* data = Structure("Data", { + Member("a", ty.vec3()), + Member("b", ty.vec3()), + }); - auto* data = ty.struct_("Data", data_str); type::Array ary(data, 4, ast::DecorationList{ create(32), }); - auto* pre_str = create( - ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})}, - ast::DecorationList{}); + auto* pre_struct = Structure("Pre", {Member("c", &ary)}); - auto* pre_struct = ty.struct_("Pre", pre_str); auto* coord_var = Global("data", pre_struct, ast::StorageClass::kStorage); auto* lhs = @@ -791,8 +727,8 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor_StorageBuffer_Store_Swizzle_SingleLetter) { // struct Data { - // [[offset(0)]] a : vec3; - // [[offset(16)]] b : vec3; + // a : vec3; + // b : vec3; // }; // struct Pre { // var c : [[stride(32)]] array; @@ -803,24 +739,18 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, // // -> data.Store((4 * 1) + 16 + (2 * 32) + 0, asuint(1.0f)); - auto* data_str = create( - ast::StructMemberList{ - Member("a", ty.vec3(), {MemberOffset(0)}), - Member("b", ty.vec3(), {MemberOffset(16)}), - }, - ast::DecorationList{}); + auto* data = Structure("Data", { + Member("a", ty.vec3()), + Member("b", ty.vec3()), + }); - auto* data = ty.struct_("Data", data_str); type::Array ary(data, 4, ast::DecorationList{ create(32), }); - auto* pre_str = create( - ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})}, - ast::DecorationList{}); + auto* pre_struct = Structure("Pre", {Member("c", &ary)}); - auto* pre_struct = ty.struct_("Pre", pre_str); auto* coord_var = Global("data", pre_struct, ast::StorageClass::kStorage); auto* lhs = MemberAccessor( diff --git a/src/writer/hlsl/generator_impl_type_test.cc b/src/writer/hlsl/generator_impl_type_test.cc index 23a9ef795f..5353b5c4bf 100644 --- a/src/writer/hlsl/generator_impl_type_test.cc +++ b/src/writer/hlsl/generator_impl_type_test.cc @@ -167,12 +167,10 @@ TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Pointer) { } TEST_F(HlslGeneratorImplTest_Type, EmitType_StructDecl) { - auto* str = create( - ast::StructMemberList{Member("a", ty.i32()), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); - - auto* s = ty.struct_("S", str); + auto* s = Structure("S", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); GeneratorImpl& gen = Build(); @@ -185,12 +183,10 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_StructDecl) { } TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct) { - auto* str = create( - ast::StructMemberList{Member("a", ty.i32()), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); - - auto* s = ty.struct_("S", str); + auto* s = Structure("S", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); GeneratorImpl& gen = Build(); @@ -198,35 +194,35 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct) { EXPECT_EQ(result(), "S"); } +/// TODO(bclayton): Enable this, fix it, add tests for vector, matrix, array and +/// nested structures. TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Struct_InjectPadding) { - auto* str = create( - ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(4)}), - Member("b", ty.f32(), {MemberOffset(32)}), - Member("c", ty.f32(), {MemberOffset(128)})}, - ast::DecorationList{}); - - auto* s = ty.struct_("S", str); + auto* s = Structure( + "S", { + Member("a", ty.i32(), {MemberSize(32)}), + Member("b", ty.f32()), + Member("c", ty.f32(), {MemberAlign(128), MemberSize(128)}), + }); GeneratorImpl& gen = Build(); ASSERT_TRUE(gen.EmitType(out, s, "")) << gen.error(); - EXPECT_EQ(result(), R"(struct { - int8_t pad_0[4]; + EXPECT_EQ(gen.result(), R"(struct S { int a; - int8_t pad_1[24]; + int8_t pad_0[28]; float b; - int8_t pad_2[92]; + int8_t pad_1[92]; float c; -})"); + int8_t pad_2[124]; +}; +)"); } TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct_NameCollision) { - auto* str = - create(ast::StructMemberList{Member("double", ty.i32()), - Member("float", ty.f32())}, - ast::DecorationList{}); - - auto* s = ty.struct_("S", str); + auto* s = Structure("S", { + Member("double", ty.i32()), + Member("float", ty.f32()), + }); GeneratorImpl& gen = Build(); @@ -240,15 +236,12 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct_NameCollision) { // TODO(dsinclair): How to translate [[block]] TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Struct_WithDecoration) { - ast::DecorationList decos; - decos.push_back(create()); - - auto* str = create( - ast::StructMemberList{Member("a", ty.i32()), - Member("b", ty.f32(), {MemberOffset(4)})}, - decos); - - auto* s = ty.struct_("S", str); + auto* s = Structure("S", + { + Member("a", ty.i32()), + Member("b", ty.f32()), + }, + {create()}); GeneratorImpl& gen = Build(); diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc index 00f9ffb4eb..29b1e51519 100644 --- a/src/writer/msl/generator_impl.cc +++ b/src/writer/msl/generator_impl.cc @@ -25,12 +25,12 @@ #include "src/ast/float_literal.h" #include "src/ast/module.h" #include "src/ast/sint_literal.h" -#include "src/ast/struct_member_offset_decoration.h" #include "src/ast/uint_literal.h" #include "src/ast/variable_decl_statement.h" #include "src/semantic/call.h" #include "src/semantic/function.h" #include "src/semantic/member_accessor_expression.h" +#include "src/semantic/struct.h" #include "src/semantic/variable.h" #include "src/type/access_control_type.h" #include "src/type/alias_type.h" @@ -68,14 +68,6 @@ bool last_is_break_or_fallthrough(const ast::BlockStatement* stmts) { stmts->last()->Is(); } -uint32_t adjust_for_alignment(uint32_t count, uint32_t alignment) { - const auto spill = count % alignment; - if (spill == 0) { - return count; - } - return count + alignment - spill; -} - } // namespace GeneratorImpl::GeneratorImpl(const Program* program) @@ -140,89 +132,6 @@ bool GeneratorImpl::Generate() { return true; } -uint32_t GeneratorImpl::calculate_largest_alignment(type::Struct* type) { - auto* stct = type->As()->impl(); - uint32_t largest_alignment = 0; - for (auto* mem : stct->members()) { - auto align = calculate_alignment_size(mem->type()); - if (align == 0) { - return 0; - } - if (!mem->type()->Is()) { - largest_alignment = std::max(largest_alignment, align); - } else { - largest_alignment = std::max( - largest_alignment, - calculate_largest_alignment(mem->type()->As())); - } - } - return largest_alignment; -} - -uint32_t GeneratorImpl::calculate_alignment_size(type::Type* type) { - if (auto* alias = type->As()) { - return calculate_alignment_size(alias->type()); - } - if (auto* ary = type->As()) { - // TODO(dsinclair): Handle array stride and adjust for alignment. - uint32_t type_size = calculate_alignment_size(ary->type()); - return ary->size() * type_size; - } - if (type->Is()) { - return 1; - } - if (type->Is()) { - return 0; - } - if (type->Is() || type->Is() || type->Is()) { - return 4; - } - if (auto* mat = type->As()) { - // TODO(dsinclair): Handle MatrixStride - // https://github.com/gpuweb/gpuweb/issues/773 - uint32_t type_size = calculate_alignment_size(mat->type()); - return mat->rows() * mat->columns() * type_size; - } - if (auto* stct_ty = type->As()) { - auto* stct = stct_ty->impl(); - uint32_t count = 0; - uint32_t largest_alignment = 0; - // Offset decorations in WGSL must be in increasing order. - for (auto* mem : stct->members()) { - for (auto* deco : mem->decorations()) { - if (auto* offset = deco->As()) { - count = offset->offset(); - } - } - auto align = calculate_alignment_size(mem->type()); - if (align == 0) { - return 0; - } - if (auto* str = mem->type()->As()) { - largest_alignment = - std::max(largest_alignment, calculate_largest_alignment(str)); - } else { - largest_alignment = std::max(largest_alignment, align); - } - - // Round up to the alignment size - count = adjust_for_alignment(count, align); - count += align; - } - // Round struct up to largest align size - count = adjust_for_alignment(count, largest_alignment); - return count; - } - if (auto* vec = type->As()) { - uint32_t type_size = calculate_alignment_size(vec->type()); - if (vec->size() == 2) { - return 2 * type_size; - } - return 4 * type_size; - } - return 0; -} - bool GeneratorImpl::EmitConstructedType(const type::Type* ty) { make_indent(); @@ -2075,52 +1984,76 @@ bool GeneratorImpl::EmitStructType(const type::Struct* str) { out_ << "struct " << program_->Symbols().NameFor(str->symbol()) << " {" << std::endl; + uint32_t pad_count = 0; + auto add_padding = [&](uint32_t size) { + out_ << "int8_t pad_" << pad_count << "[" << size << "];" << std::endl; + pad_count++; + }; + increment_indent(); uint32_t current_offset = 0; - uint32_t pad_count = 0; for (auto* mem : str->impl()->members()) { std::string attributes; make_indent(); + + auto* sem_mem = program_->Sem().Get(mem); + if (!sem_mem) { + TINT_ICE(diagnostics_) << "struct member missing semantic info"; + return false; + } + + auto const offset = sem_mem->Offset(); + if (offset != current_offset) { + add_padding(offset - current_offset); + make_indent(); + } + for (auto* deco : mem->decorations()) { - if (auto* o = deco->As()) { - uint32_t offset = o->offset(); - if (offset != current_offset) { - out_ << "int8_t pad_" << pad_count << "[" << (offset - current_offset) - << "];" << std::endl; - pad_count++; - make_indent(); - } - current_offset = offset; - } else if (auto* loc = deco->As()) { + if (auto* loc = deco->As()) { attributes = " [[user(locn" + std::to_string(loc->value()) + ")]]"; - } else { - diagnostics_.add_error("unsupported member decoration: " + - program_->str(deco)); - return false; } } if (!EmitType(mem->type(), program_->Symbols().NameFor(mem->symbol()))) { return false; } - auto size = calculate_alignment_size(mem->type()); - if (size == 0) { - diagnostics_.add_error("unable to calculate byte size for: " + - mem->type()->type_name()); - return false; - } - current_offset += size; + + auto* ty = mem->type()->UnwrapAliasIfNeeded(); // Array member name will be output with the type - if (!mem->type()->Is()) { + if (!ty->Is()) { out_ << " " << program_->Symbols().NameFor(mem->symbol()); } out_ << attributes; out_ << ";" << std::endl; + + if (ty->is_scalar()) { + current_offset = offset + 4; + } else if (ty->Is()) { + /// Structure will already contain padding matching the WGSL size + current_offset = offset + sem_mem->Size(); + } else { + /// TODO(bclayton): Implement for vector, matrix, array and nested + /// structures. + TINT_UNREACHABLE(diagnostics_) + << "Unhandled type " << ty->TypeInfo().name; + return false; + } } + + auto* sem_str = program_->Sem().Get(str); + if (!sem_str) { + TINT_ICE(diagnostics_) << "struct missing semantic info"; + return false; + } + if (sem_str->Size() != current_offset) { + make_indent(); + add_padding(sem_str->Size() - current_offset); + } + decrement_indent(); make_indent(); diff --git a/src/writer/msl/generator_impl.h b/src/writer/msl/generator_impl.h index 07c90e9b70..b5989b0905 100644 --- a/src/writer/msl/generator_impl.h +++ b/src/writer/msl/generator_impl.h @@ -60,16 +60,6 @@ class GeneratorImpl : public TextGenerator { /// @returns true on successful generation; false otherwise bool Generate(); - /// Calculates the alignment size of the given `type`. This returns 0 - /// for pointers as the size is unknown. - /// @param type the type to calculate the alignment size for - /// @returns the number of bytes used to align `type` or 0 on error - uint32_t calculate_alignment_size(type::Type* type); - /// Calculates the largest alignment seen within a struct - /// @param type the struct to calculate - /// @returns the largest alignment value - uint32_t calculate_largest_alignment(type::Struct* type); - /// Handles generating a constructed /// @param ty the constructed type to generate /// @returns true if the constructed type was emitted diff --git a/src/writer/msl/generator_impl_alias_type_test.cc b/src/writer/msl/generator_impl_alias_type_test.cc index d663ba924d..b5ec3dfe7f 100644 --- a/src/writer/msl/generator_impl_alias_type_test.cc +++ b/src/writer/msl/generator_impl_alias_type_test.cc @@ -32,12 +32,10 @@ TEST_F(MslGeneratorImplTest, EmitConstructedType_F32) { } TEST_F(MslGeneratorImplTest, EmitConstructedType_Struct) { - auto* str = create( - ast::StructMemberList{Member("a", ty.f32()), - Member("b", ty.i32(), {MemberOffset(4)})}, - ast::DecorationList{}); - - auto* s = ty.struct_("a", str); + auto* s = Structure("a", { + Member("a", ty.f32()), + Member("b", ty.i32()), + }); GeneratorImpl& gen = Build(); @@ -50,12 +48,11 @@ TEST_F(MslGeneratorImplTest, EmitConstructedType_Struct) { } TEST_F(MslGeneratorImplTest, EmitConstructedType_AliasStructIdent) { - auto* str = create( - ast::StructMemberList{Member("a", ty.f32()), - Member("b", ty.i32(), {MemberOffset(4)})}, - ast::DecorationList{}); + auto* s = Structure("b", { + Member("a", ty.f32()), + Member("b", ty.i32()), + }); - auto* s = ty.struct_("b", str); auto* alias = ty.alias("a", s); GeneratorImpl& gen = Build(); diff --git a/src/writer/msl/generator_impl_function_test.cc b/src/writer/msl/generator_impl_function_test.cc index 543d0a8f65..8f4de92bf5 100644 --- a/src/writer/msl/generator_impl_function_test.cc +++ b/src/writer/msl/generator_impl_function_test.cc @@ -243,17 +243,15 @@ fragment void frag_main(constant float4& coord [[buffer(0)]]) { )"); } -TEST_F(MslGeneratorImplTest, Emit_Decoration_EntryPoint_With_RW_StorageBuffer) { - auto* str = create( - ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); +TEST_F(MslGeneratorImplTest, + Emit_FunctionDecoration_EntryPoint_With_RW_StorageBuffer) { + auto* s = Structure("Data", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); - auto* s = ty.struct_("Data", str); type::AccessControl ac(ast::AccessControl::kReadWrite, s); - AST().AddConstructedType(s); - Global("coord", &ac, ast::StorageClass::kStorage, nullptr, ast::DecorationList{create(0), create(1)}); @@ -289,15 +287,14 @@ fragment void frag_main(device Data& coord [[buffer(0)]]) { )"); } -TEST_F(MslGeneratorImplTest, Emit_Decoration_EntryPoint_With_RO_StorageBuffer) { - auto* str = create( - ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); +TEST_F(MslGeneratorImplTest, + Emit_FunctionDecoration_EntryPoint_With_RO_StorageBuffer) { + auto* s = Structure("Data", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); - auto* s = ty.struct_("Data", str); type::AccessControl ac(ast::AccessControl::kReadOnly, s); - AST().AddConstructedType(s); Global("coord", &ac, ast::StorageClass::kStorage, nullptr, ast::DecorationList{create(0), @@ -555,15 +552,13 @@ fragment void frag_main(constant float4& coord [[buffer(0)]]) { } TEST_F(MslGeneratorImplTest, - Emit_Decoration_Called_By_EntryPoint_With_RW_StorageBuffer) { - auto* str = create( - ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); + Emit_FunctionDecoration_Called_By_EntryPoint_With_RW_StorageBuffer) { + auto* s = Structure("Data", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); - auto* s = ty.struct_("Data", str); type::AccessControl ac(ast::AccessControl::kReadWrite, s); - AST().AddConstructedType(s); Global("coord", &ac, ast::StorageClass::kStorage, nullptr, ast::DecorationList{create(0), @@ -613,15 +608,13 @@ fragment void frag_main(device Data& coord [[buffer(0)]]) { } TEST_F(MslGeneratorImplTest, - Emit_Decoration_Called_By_EntryPoint_With_RO_StorageBuffer) { - auto* str = create( - ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); + Emit_FunctionDecoration_Called_By_EntryPoint_With_RO_StorageBuffer) { + auto* s = Structure("Data", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); - auto* s = ty.struct_("Data", str); type::AccessControl ac(ast::AccessControl::kReadOnly, s); - AST().AddConstructedType(s); Global("coord", &ac, ast::StorageClass::kStorage, nullptr, ast::DecorationList{create(0), @@ -746,7 +739,7 @@ using namespace metal; TEST_F(MslGeneratorImplTest, Emit_Function_Multiple_EntryPoint_With_Same_ModuleVar) { // [[block]] struct Data { - // [[offset(0)]] d : f32; + // d : f32; // }; // [[binding(0), group(0)]] var data : Data; // @@ -760,21 +753,15 @@ TEST_F(MslGeneratorImplTest, // return; // } - ast::DecorationList s_decos; - s_decos.push_back(create()); + auto* s = Structure("Data", {Member("d", ty.f32())}, + {create()}); - auto* str = create( - ast::StructMemberList{Member("d", ty.f32(), {MemberOffset(0)})}, s_decos); - - auto* s = ty.struct_("Data", str); type::AccessControl ac(ast::AccessControl::kReadWrite, s); Global("data", &ac, ast::StorageClass::kStorage, nullptr, ast::DecorationList{create(0), create(0)}); - AST().AddConstructedType(s); - { auto* var = Var("v", ty.f32(), ast::StorageClass::kFunction, MemberAccessor("data", "d")); diff --git a/src/writer/msl/generator_impl_test.cc b/src/writer/msl/generator_impl_test.cc index 9c6dfade7e..1fa7776416 100644 --- a/src/writer/msl/generator_impl_test.cc +++ b/src/writer/msl/generator_impl_test.cc @@ -78,178 +78,6 @@ INSTANTIATE_TEST_SUITE_P( MslBuiltinData{ast::Builtin::kSampleMaskOut, "sample_mask"})); -TEST_F(MslGeneratorImplTest, calculate_alignment_size_alias) { - auto* alias = ty.alias("a", ty.f32()); - - GeneratorImpl& gen = Build(); - - EXPECT_EQ(4u, gen.calculate_alignment_size(alias)); -} - -TEST_F(MslGeneratorImplTest, calculate_alignment_size_array) { - auto* array = ty.array(); - - GeneratorImpl& gen = Build(); - - EXPECT_EQ(4u * 4u, gen.calculate_alignment_size(array)); -} - -TEST_F(MslGeneratorImplTest, calculate_alignment_size_bool) { - auto* bool_ = ty.bool_(); - - GeneratorImpl& gen = Build(); - - EXPECT_EQ(1u, gen.calculate_alignment_size(bool_)); -} - -TEST_F(MslGeneratorImplTest, calculate_alignment_size_f32) { - auto* f32 = ty.f32(); - - GeneratorImpl& gen = Build(); - - EXPECT_EQ(4u, gen.calculate_alignment_size(f32)); -} - -TEST_F(MslGeneratorImplTest, calculate_alignment_size_i32) { - auto* i32 = ty.i32(); - - GeneratorImpl& gen = Build(); - - EXPECT_EQ(4u, gen.calculate_alignment_size(i32)); -} - -TEST_F(MslGeneratorImplTest, calculate_alignment_size_matrix) { - auto* mat3x2 = ty.mat3x2(); - - GeneratorImpl& gen = Build(); - - EXPECT_EQ(4u * 3u * 2u, gen.calculate_alignment_size(mat3x2)); -} - -TEST_F(MslGeneratorImplTest, calculate_alignment_size_pointer) { - type::Pointer ptr(ty.bool_(), ast::StorageClass::kPrivate); - - GeneratorImpl& gen = Build(); - - EXPECT_EQ(0u, gen.calculate_alignment_size(&ptr)); -} - -TEST_F(MslGeneratorImplTest, calculate_alignment_size_struct) { - auto* str = create( - ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(4)}), - Member("b", ty.f32(), {MemberOffset(32)}), - Member("c", ty.f32(), {MemberOffset(128)})}, - ast::DecorationList{}); - - auto* s = ty.struct_("S", str); - - GeneratorImpl& gen = Build(); - - EXPECT_EQ(132u, gen.calculate_alignment_size(s)); -} - -TEST_F(MslGeneratorImplTest, calculate_alignment_size_struct_of_struct) { - auto* inner_str = create( - ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}), - Member("b", ty.vec3(), {MemberOffset(16)}), - Member("c", ty.f32(), {MemberOffset(32)})}, - ast::DecorationList{}); - - auto* inner_s = ty.struct_("Inner", inner_str); - - auto* outer_str = create( - ast::StructMemberList{Member("d", ty.f32(), {MemberOffset(0)}), - Member("e", inner_s, {MemberOffset(32)}), - Member("f", ty.f32(), {MemberOffset(64)})}, - ast::DecorationList{}); - - auto* outer_s = ty.struct_("Outer", outer_str); - - GeneratorImpl& gen = Build(); - - EXPECT_EQ(80u, gen.calculate_alignment_size(outer_s)); -} - -TEST_F(MslGeneratorImplTest, calculate_alignment_size_u32) { - auto* u32 = ty.u32(); - - GeneratorImpl& gen = Build(); - - EXPECT_EQ(4u, gen.calculate_alignment_size(u32)); -} - -struct MslVectorSizeData { - uint32_t elements; - uint32_t byte_size; -}; -inline std::ostream& operator<<(std::ostream& out, MslVectorSizeData data) { - out << data.elements; - return out; -} -using MslVectorSizeBoolTest = TestParamHelper; -TEST_P(MslVectorSizeBoolTest, calculate) { - auto param = GetParam(); - - type::Vector vec(ty.bool_(), param.elements); - - GeneratorImpl& gen = Build(); - - EXPECT_EQ(param.byte_size, gen.calculate_alignment_size(&vec)); -} -INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest, - MslVectorSizeBoolTest, - testing::Values(MslVectorSizeData{2u, 2u}, - MslVectorSizeData{3u, 4u}, - MslVectorSizeData{4u, 4u})); - -using MslVectorSizeI32Test = TestParamHelper; -TEST_P(MslVectorSizeI32Test, calculate) { - auto param = GetParam(); - - type::Vector vec(ty.i32(), param.elements); - - GeneratorImpl& gen = Build(); - - EXPECT_EQ(param.byte_size, gen.calculate_alignment_size(&vec)); -} -INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest, - MslVectorSizeI32Test, - testing::Values(MslVectorSizeData{2u, 8u}, - MslVectorSizeData{3u, 16u}, - MslVectorSizeData{4u, 16u})); - -using MslVectorSizeU32Test = TestParamHelper; -TEST_P(MslVectorSizeU32Test, calculate) { - auto param = GetParam(); - - type::Vector vec(ty.u32(), param.elements); - - GeneratorImpl& gen = Build(); - - EXPECT_EQ(param.byte_size, gen.calculate_alignment_size(&vec)); -} -INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest, - MslVectorSizeU32Test, - testing::Values(MslVectorSizeData{2u, 8u}, - MslVectorSizeData{3u, 16u}, - MslVectorSizeData{4u, 16u})); - -using MslVectorSizeF32Test = TestParamHelper; -TEST_P(MslVectorSizeF32Test, calculate) { - auto param = GetParam(); - - type::Vector vec(ty.f32(), param.elements); - - GeneratorImpl& gen = Build(); - - EXPECT_EQ(param.byte_size, gen.calculate_alignment_size(&vec)); -} -INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest, - MslVectorSizeF32Test, - testing::Values(MslVectorSizeData{2u, 8u}, - MslVectorSizeData{3u, 16u}, - MslVectorSizeData{4u, 16u})); - } // namespace } // namespace msl } // namespace writer diff --git a/src/writer/msl/generator_impl_type_test.cc b/src/writer/msl/generator_impl_type_test.cc index 1e99815764..ebd0f72d0c 100644 --- a/src/writer/msl/generator_impl_type_test.cc +++ b/src/writer/msl/generator_impl_type_test.cc @@ -143,12 +143,10 @@ TEST_F(MslGeneratorImplTest, DISABLED_EmitType_Pointer) { } TEST_F(MslGeneratorImplTest, EmitType_Struct) { - auto* str = create( - ast::StructMemberList{Member("a", ty.i32()), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); - - auto* s = ty.struct_("S", str); + auto* s = Structure("S", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); GeneratorImpl& gen = Build(); @@ -157,12 +155,10 @@ TEST_F(MslGeneratorImplTest, EmitType_Struct) { } TEST_F(MslGeneratorImplTest, EmitType_StructDecl) { - auto* str = create( - ast::StructMemberList{Member("a", ty.i32()), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); - - auto* s = ty.struct_("S", str); + auto* s = Structure("S", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); GeneratorImpl& gen = Build(); @@ -174,41 +170,37 @@ TEST_F(MslGeneratorImplTest, EmitType_StructDecl) { )"); } +/// TODO(bclayton): Add tests for vector, matrix, array and nested structures. TEST_F(MslGeneratorImplTest, EmitType_Struct_InjectPadding) { - auto* str = create( - ast::StructMemberList{ - Member("a", ty.i32(), {MemberOffset(4)}), - Member("b", ty.f32(), {MemberOffset(32)}), - Member("c", ty.f32(), {MemberOffset(128)}), - }, - ast::DecorationList{}); - - auto* s = ty.struct_("S", str); + auto* s = Structure( + "S", { + Member("a", ty.i32(), {MemberSize(32)}), + Member("b", ty.f32()), + Member("c", ty.f32(), {MemberAlign(128), MemberSize(128)}), + }); GeneratorImpl& gen = Build(); ASSERT_TRUE(gen.EmitStructType(s)) << gen.error(); EXPECT_EQ(gen.result(), R"(struct S { - int8_t pad_0[4]; int a; - int8_t pad_1[24]; + int8_t pad_0[28]; float b; - int8_t pad_2[92]; + int8_t pad_1[92]; float c; + int8_t pad_2[124]; }; )"); } // TODO(dsinclair): How to translate [[block]] TEST_F(MslGeneratorImplTest, DISABLED_EmitType_Struct_WithDecoration) { - ast::DecorationList decos; - decos.push_back(create()); - auto* str = create( - ast::StructMemberList{Member("a", ty.i32()), - Member("b", ty.f32(), {MemberOffset(4)})}, - decos); - - auto* s = ty.struct_("S", str); + auto* s = Structure("S", + { + Member("a", ty.i32()), + Member("b", ty.f32()), + }, + {create()}); GeneratorImpl& gen = Build(); diff --git a/src/writer/msl/generator_impl_variable_decl_statement_test.cc b/src/writer/msl/generator_impl_variable_decl_statement_test.cc index 095aa9dfd6..e67fcdb8d0 100644 --- a/src/writer/msl/generator_impl_variable_decl_statement_test.cc +++ b/src/writer/msl/generator_impl_variable_decl_statement_test.cc @@ -64,12 +64,11 @@ TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Array) { } TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Struct) { - auto* str = create( - ast::StructMemberList{Member("a", ty.f32()), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); + auto* s = Structure("S", { + Member("a", ty.f32()), + Member("b", ty.f32()), + }); - auto* s = ty.struct_("S", str); auto* var = Var("a", s, ast::StorageClass::kNone); auto* stmt = create(var); WrapInFunction(stmt); diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc index 4a066fcf51..71054c8bd8 100644 --- a/src/writer/spirv/builder.cc +++ b/src/writer/spirv/builder.cc @@ -22,8 +22,11 @@ #include "src/ast/constant_id_decoration.h" #include "src/ast/fallthrough_statement.h" #include "src/ast/null_literal.h" +#include "src/semantic/array.h" #include "src/semantic/call.h" #include "src/semantic/function.h" +#include "src/semantic/intrinsic.h" +#include "src/semantic/struct.h" #include "src/semantic/variable.h" #include "src/type/depth_texture_type.h" #include "src/type/multisampled_texture_type.h" @@ -2961,11 +2964,14 @@ bool Builder::GenerateArrayType(type::Array* ary, const Operand& result) { {result, Operand::Int(elem_type), Operand::Int(len_id)}); } - if (ary->has_array_stride()) { - push_annot(spv::Op::OpDecorate, - {Operand::Int(result_id), Operand::Int(SpvDecorationArrayStride), - Operand::Int(ary->array_stride())}); + auto* sem_arr = builder_.Sem().Get(ary); + if (!sem_arr) { + error_ = "array type missing semantic info"; + return false; } + push_annot(spv::Op::OpDecorate, + {Operand::Int(result_id), Operand::Int(SpvDecorationArrayStride), + Operand::Int(sem_arr->Stride())}); return true; } @@ -3054,38 +3060,39 @@ uint32_t Builder::GenerateStructMember(uint32_t struct_id, {Operand::Int(struct_id), Operand::Int(idx), Operand::String(builder_.Symbols().NameFor(member->symbol()))}); - bool has_layout = false; - for (auto* deco : member->decorations()) { - if (auto* offset = deco->As()) { - push_annot( - spv::Op::OpMemberDecorate, - {Operand::Int(struct_id), Operand::Int(idx), - Operand::Int(SpvDecorationOffset), Operand::Int(offset->offset())}); - has_layout = true; - } else { - error_ = "unknown struct member decoration"; - return 0; - } + // Note: This will generate layout annotations for *all* structs, whether or + // not they are used in host-shareable variables. This is officially ok in + // SPIR-V 1.0 through 1.3. If / when we migrate to using SPIR-V 1.4 we'll have + // to only generate the layout info for structs used for certain storage + // classes. + + auto* sem_member = builder_.Sem().Get(member); + if (!sem_member) { + error_ = "Struct member has no semantic information"; + return 0; } - if (has_layout) { - // Infer and emit matrix layout. - auto* matrix_type = GetNestedMatrixType(member->type()); - if (matrix_type) { - push_annot(spv::Op::OpMemberDecorate, - {Operand::Int(struct_id), Operand::Int(idx), - Operand::Int(SpvDecorationColMajor)}); - if (!matrix_type->type()->Is()) { - error_ = "matrix scalar element type must be f32"; - return 0; - } - const auto scalar_elem_size = 4; - const auto effective_row_count = (matrix_type->rows() == 2) ? 2 : 4; - push_annot(spv::Op::OpMemberDecorate, - {Operand::Int(struct_id), Operand::Int(idx), - Operand::Int(SpvDecorationMatrixStride), - Operand::Int(effective_row_count * scalar_elem_size)}); + push_annot( + spv::Op::OpMemberDecorate, + {Operand::Int(struct_id), Operand::Int(idx), + Operand::Int(SpvDecorationOffset), Operand::Int(sem_member->Offset())}); + + // Infer and emit matrix layout. + auto* matrix_type = GetNestedMatrixType(member->type()); + if (matrix_type) { + push_annot(spv::Op::OpMemberDecorate, + {Operand::Int(struct_id), Operand::Int(idx), + Operand::Int(SpvDecorationColMajor)}); + if (!matrix_type->type()->Is()) { + error_ = "matrix scalar element type must be f32"; + return 0; } + const auto scalar_elem_size = 4; + const auto effective_row_count = (matrix_type->rows() == 2) ? 2 : 4; + push_annot(spv::Op::OpMemberDecorate, + {Operand::Int(struct_id), Operand::Int(idx), + Operand::Int(SpvDecorationMatrixStride), + Operand::Int(effective_row_count * scalar_elem_size)}); } return GenerateTypeIfNeeded(member->type()); diff --git a/src/writer/spirv/builder_accessor_expression_test.cc b/src/writer/spirv/builder_accessor_expression_test.cc index e69f08de4c..78ec1ad316 100644 --- a/src/writer/spirv/builder_accessor_expression_test.cc +++ b/src/writer/spirv/builder_accessor_expression_test.cc @@ -135,12 +135,12 @@ TEST_F(BuilderTest, ArrayAccessor_Dynamic) { } TEST_F(BuilderTest, ArrayAccessor_MultiLevel) { - type::Array ary4(ty.vec3(), 4, ast::DecorationList{}); + auto* ary4 = ty.array(ty.vec3(), 4); // ary = array, 4> // ary[3][2]; - auto* var = Global("ary", &ary4, ast::StorageClass::kFunction); + auto* var = Global("ary", ary4, ast::StorageClass::kFunction); auto* expr = IndexAccessor(IndexAccessor("ary", 3), 2); WrapInFunction(expr); @@ -173,12 +173,12 @@ TEST_F(BuilderTest, ArrayAccessor_MultiLevel) { } TEST_F(BuilderTest, Accessor_ArrayWithSwizzle) { - type::Array ary4(ty.vec3(), 4, ast::DecorationList{}); + auto* ary4 = ty.array(ty.vec3(), 4); // var a : array, 4>; // a[2].xy; - auto* var = Global("ary", &ary4, ast::StorageClass::kFunction); + auto* var = Global("ary", ary4, ast::StorageClass::kFunction); auto* expr = MemberAccessor(IndexAccessor("ary", 2), "xy"); WrapInFunction(expr); @@ -219,12 +219,12 @@ TEST_F(BuilderTest, MemberAccessor) { // var ident : my_struct // ident.b - auto* s = create( - ast::StructMemberList{Member("a", ty.f32()), Member("b", ty.f32())}, - ast::DecorationList{}); + auto* s = Structure("my_struct", { + Member("a", ty.f32()), + Member("b", ty.f32()), + }); - auto* s_type = ty.struct_("my_struct", s); - auto* var = Global("ident", s_type, ast::StorageClass::kFunction); + auto* var = Global("ident", s, ast::StorageClass::kFunction); auto* expr = MemberAccessor("ident", "b"); WrapInFunction(expr); @@ -262,15 +262,12 @@ TEST_F(BuilderTest, MemberAccessor_Nested) { // // var ident : my_struct // ident.inner.a - auto* inner_struct = ty.struct_( - "Inner", create(ast::StructMemberList{Member("a", ty.f32()), - Member("b", ty.f32())}, - ast::DecorationList{})); + auto* inner_struct = Structure("Inner", { + Member("a", ty.f32()), + Member("b", ty.f32()), + }); - auto* s_type = ty.struct_( - "my_struct", - create(ast::StructMemberList{Member("inner", inner_struct)}, - ast::DecorationList{})); + auto* s_type = Structure("my_struct", {Member("inner", inner_struct)}); auto* var = Global("ident", s_type, ast::StorageClass::kFunction); auto* expr = MemberAccessor(MemberAccessor("ident", "inner"), "a"); @@ -311,16 +308,13 @@ TEST_F(BuilderTest, MemberAccessor_Nested_WithAlias) { // // var ident : my_struct // ident.inner.a - auto* inner_struct = ty.struct_( - "Inner", create(ast::StructMemberList{Member("a", ty.f32()), - Member("b", ty.f32())}, - ast::DecorationList{})); + auto* inner_struct = Structure("Inner", { + Member("a", ty.f32()), + Member("b", ty.f32()), + }); auto* alias = ty.alias("Inner", inner_struct); - auto* s_type = ty.struct_( - "Outer", - create(ast::StructMemberList{Member("inner", alias)}, - ast::DecorationList{})); + auto* s_type = Structure("Outer", {Member("inner", alias)}); auto* var = Global("ident", s_type, ast::StorageClass::kFunction); auto* expr = MemberAccessor(MemberAccessor("ident", "inner"), "a"); @@ -360,15 +354,12 @@ TEST_F(BuilderTest, MemberAccessor_Nested_Assignment_LHS) { // // var ident : my_struct // ident.inner.a = 2.0f; - auto* inner_struct = ty.struct_( - "Inner", create(ast::StructMemberList{Member("a", ty.f32()), - Member("b", ty.f32())}, - ast::DecorationList{})); + auto* inner_struct = Structure("Inner", { + Member("a", ty.f32()), + Member("b", ty.f32()), + }); - auto* s_type = ty.struct_( - "my_struct", - create(ast::StructMemberList{Member("inner", inner_struct)}, - ast::DecorationList{})); + auto* s_type = Structure("my_struct", {Member("inner", inner_struct)}); auto* var = Global("ident", s_type, ast::StorageClass::kFunction); auto* expr = create( @@ -412,15 +403,12 @@ TEST_F(BuilderTest, MemberAccessor_Nested_Assignment_RHS) { // var ident : my_struct // var store : f32 = ident.inner.a - auto* inner_struct = ty.struct_( - "Inner", create(ast::StructMemberList{Member("a", ty.f32()), - Member("b", ty.f32())}, - ast::DecorationList{})); + auto* inner_struct = Structure("Inner", { + Member("a", ty.f32()), + Member("b", ty.f32()), + }); - auto* s_type = ty.struct_( - "my_struct", - create(ast::StructMemberList{Member("inner", inner_struct)}, - ast::DecorationList{})); + auto* s_type = Structure("my_struct", {Member("inner", inner_struct)}); auto* var = Global("ident", s_type, ast::StorageClass::kFunction); auto* store = Global("store", ty.f32(), ast::StorageClass::kFunction); @@ -625,21 +613,14 @@ TEST_F(BuilderTest, Accessor_Mixed_ArrayAndMember) { // var index : array // index[0].foo[2].bar.baz.yx - auto* s = - create(ast::StructMemberList{Member("baz", ty.vec3())}, - ast::DecorationList{}); - auto* c_type = ty.struct_("C", s); + auto* c_type = Structure("C", {Member("baz", ty.vec3())}); - s = create(ast::StructMemberList{Member("bar", c_type)}, - ast::DecorationList{}); - auto* b_type = ty.struct_("B", s); - type::Array b_ary_type(b_type, 3, ast::DecorationList{}); - s = create(ast::StructMemberList{Member("foo", &b_ary_type)}, - ast::DecorationList{}); - auto* a_type = ty.struct_("A", s); + auto* b_type = Structure("B", {Member("bar", c_type)}); + auto* b_ary_type = ty.array(b_type, 3); + auto* a_type = Structure("A", {Member("foo", b_ary_type)}); - type::Array a_ary_type(a_type, 2, ast::DecorationList{}); - auto* var = Global("index", &a_ary_type, ast::StorageClass::kFunction); + auto* a_ary_type = ty.array(a_type, 2); + auto* var = Global("index", a_ary_type, ast::StorageClass::kFunction); auto* expr = MemberAccessor( MemberAccessor( MemberAccessor( @@ -693,12 +674,12 @@ TEST_F(BuilderTest, Accessor_Array_Of_Vec) { // vec2(0.5, -0.5)); // pos[1] - type::Array arr(ty.vec2(), 3, ast::DecorationList{}); + auto* arr = ty.array(ty.vec2(), 3); auto* var = - GlobalConst("pos", &arr, - Construct(&arr, vec2(0.0f, 0.5f), - vec2(-0.5f, -0.5f), vec2(0.5f, -0.5f))); + GlobalConst("pos", arr, + Construct(arr, vec2(0.0f, 0.5f), vec2(-0.5f, -0.5f), + vec2(0.5f, -0.5f))); auto* expr = IndexAccessor("pos", 1u); WrapInFunction(expr); diff --git a/src/writer/spirv/builder_assign_test.cc b/src/writer/spirv/builder_assign_test.cc index 45d37698ab..c3dcf99974 100644 --- a/src/writer/spirv/builder_assign_test.cc +++ b/src/writer/spirv/builder_assign_test.cc @@ -176,12 +176,12 @@ TEST_F(BuilderTest, Assign_StructMember) { // var ident : my_struct // ident.b = 4.0; - auto* s = create( - ast::StructMemberList{Member("a", ty.f32()), Member("b", ty.f32())}, - ast::DecorationList{}); + auto* s = Structure("my_struct", { + Member("a", ty.f32()), + Member("b", ty.f32()), + }); - auto* s_type = ty.struct_("my_struct", s); - auto* v = Global("ident", s_type, ast::StorageClass::kFunction); + auto* v = Global("ident", s, ast::StorageClass::kFunction); auto* assign = create(MemberAccessor("ident", "b"), Expr(4.f)); diff --git a/src/writer/spirv/builder_constructor_expression_test.cc b/src/writer/spirv/builder_constructor_expression_test.cc index 4525ba4ef6..6a11cb0854 100644 --- a/src/writer/spirv/builder_constructor_expression_test.cc +++ b/src/writer/spirv/builder_constructor_expression_test.cc @@ -974,15 +974,12 @@ TEST_F(SpvBuilderConstructorTest, Type_Array_2_Vec3) { } TEST_F(SpvBuilderConstructorTest, Type_Struct) { - auto* s = create( - ast::StructMemberList{ - Member("a", ty.f32()), - Member("b", ty.vec3()), - }, - ast::DecorationList{}); - auto* s_type = ty.struct_("my_struct", s); + auto* s = Structure("my_struct", { + Member("a", ty.f32()), + Member("b", ty.vec3()), + }); - auto* t = Construct(s_type, 2.0f, vec3(2.0f, 2.0f, 2.0f)); + auto* t = Construct(s, 2.0f, vec3(2.0f, 2.0f, 2.0f)); WrapInFunction(t); spirv::Builder& b = Build(); @@ -1127,13 +1124,8 @@ TEST_F(SpvBuilderConstructorTest, Type_ZeroInit_Array) { } TEST_F(SpvBuilderConstructorTest, Type_ZeroInit_Struct) { - auto* s = create( - ast::StructMemberList{ - Member("a", ty.f32()), - }, - ast::DecorationList{}); - auto* s_type = ty.struct_("my_struct", s); - auto* t = Construct(s_type); + auto* s = Structure("my_struct", {Member("a", ty.f32())}); + auto* t = Construct(s); WrapInFunction(t); spirv::Builder& b = Build(); @@ -1564,14 +1556,12 @@ TEST_F(SpvBuilderConstructorTest, IsConstructorConst_BitCastScalars) { } TEST_F(SpvBuilderConstructorTest, IsConstructorConst_Struct) { - auto* s = create( - ast::StructMemberList{ - Member("a", ty.f32()), - Member("b", ty.vec3()), - }, - ast::DecorationList{}); - auto* s_type = ty.struct_("my_struct", s); - auto* t = Construct(s_type, 2.f, vec3(2.f, 2.f, 2.f)); + auto* s = Structure("my_struct", { + Member("a", ty.f32()), + Member("b", ty.vec3()), + }); + + auto* t = Construct(s, 2.f, vec3(2.f, 2.f, 2.f)); WrapInFunction(t); spirv::Builder& b = Build(); @@ -1582,15 +1572,12 @@ TEST_F(SpvBuilderConstructorTest, IsConstructorConst_Struct) { TEST_F(SpvBuilderConstructorTest, IsConstructorConst_Struct_WithIdentSubExpression) { - auto* s = create( - ast::StructMemberList{ - Member("a", ty.f32()), - Member("b", ty.vec3()), - }, - ast::DecorationList{}); + auto* s = Structure("my_struct", { + Member("a", ty.f32()), + Member("b", ty.vec3()), + }); - auto* s_type = ty.struct_("my_struct", s); - auto* t = Construct(s_type, 2.f, "a", 2.f); + auto* t = Construct(s, 2.f, "a", 2.f); WrapInFunction(t); Global("a", ty.f32(), ast::StorageClass::kPrivate); diff --git a/src/writer/spirv/builder_function_test.cc b/src/writer/spirv/builder_function_test.cc index feecfacce8..392a05ed98 100644 --- a/src/writer/spirv/builder_function_test.cc +++ b/src/writer/spirv/builder_function_test.cc @@ -192,7 +192,7 @@ TEST_F(BuilderTest, FunctionType_DeDuplicate) { // https://crbug.com/tint/297 TEST_F(BuilderTest, Emit_Multiple_EntryPoint_With_Same_ModuleVar) { // [[block]] struct Data { - // [[offset(0)]] d : f32; + // d : f32; // }; // [[binding(0), group(0)]] var data : Data; // @@ -206,13 +206,9 @@ TEST_F(BuilderTest, Emit_Multiple_EntryPoint_With_Same_ModuleVar) { // return; // } - ast::DecorationList s_decos; - s_decos.push_back(create()); + auto* s = Structure("Data", {Member("d", ty.f32())}, + {create()}); - auto* str = create( - ast::StructMemberList{Member("d", ty.f32(), {MemberOffset(0)})}, s_decos); - - auto* s = ty.struct_("Data", str); type::AccessControl ac(ast::AccessControl::kReadWrite, s); Global("data", &ac, ast::StorageClass::kStorage, nullptr, @@ -221,8 +217,6 @@ TEST_F(BuilderTest, Emit_Multiple_EntryPoint_With_Same_ModuleVar) { create(0), }); - AST().AddConstructedType(s); - { auto* var = Var("v", ty.f32(), ast::StorageClass::kFunction, MemberAccessor("data", "d")); diff --git a/src/writer/spirv/builder_global_variable_test.cc b/src/writer/spirv/builder_global_variable_test.cc index 6a37928003..bc782e1fb2 100644 --- a/src/writer/spirv/builder_global_variable_test.cc +++ b/src/writer/spirv/builder_global_variable_test.cc @@ -383,10 +383,10 @@ TEST_F(BuilderTest, GlobalVar_DeclReadOnly) { // }; // var b : [[access(read)]] A - auto* A = ty.struct_( - "A", create(ast::StructMemberList{Member("a", ty.i32()), - Member("b", ty.i32())}, - ast::DecorationList{})); + auto* A = Structure("A", { + Member("a", ty.i32()), + Member("b", ty.i32()), + }); auto* ac = create(ast::AccessControl::kReadOnly, A); auto* var = Global("b", ac, ast::StorageClass::kStorage); @@ -395,7 +395,9 @@ TEST_F(BuilderTest, GlobalVar_DeclReadOnly) { EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 NonWritable + EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 Offset 0 +OpMemberDecorate %3 0 NonWritable +OpMemberDecorate %3 1 Offset 4 OpMemberDecorate %3 1 NonWritable )"); EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A" @@ -417,9 +419,7 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasDeclReadOnly) { // type B = A; // var b : [[access(read)]] B - auto* A = ty.struct_( - "A", create(ast::StructMemberList{Member("a", ty.i32())}, - ast::DecorationList{})); + auto* A = Structure("A", {Member("a", ty.i32())}); auto* B = ty.alias("B", A); auto* ac = create(ast::AccessControl::kReadOnly, B); auto* var = Global("b", ac, ast::StorageClass::kStorage); @@ -428,7 +428,8 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasDeclReadOnly) { EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 NonWritable + EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 Offset 0 +OpMemberDecorate %3 0 NonWritable )"); EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A" OpMemberName %3 0 "a" @@ -448,9 +449,7 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasAssignReadOnly) { // type B = [[access(read)]] A; // var b : B - auto* A = ty.struct_( - "A", create(ast::StructMemberList{Member("a", ty.i32())}, - ast::DecorationList{})); + auto* A = Structure("A", {Member("a", ty.i32())}); auto* ac = create(ast::AccessControl::kReadOnly, A); auto* B = ty.alias("B", ac); auto* var = Global("b", B, ast::StorageClass::kStorage); @@ -459,7 +458,8 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasAssignReadOnly) { EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 NonWritable + EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 Offset 0 +OpMemberDecorate %3 0 NonWritable )"); EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A" OpMemberName %3 0 "a" @@ -479,9 +479,7 @@ TEST_F(BuilderTest, GlobalVar_TwoVarDeclReadOnly) { // var b : [[access(read)]] A // var c : [[access(read_write)]] A - auto* A = ty.struct_( - "A", create(ast::StructMemberList{Member("a", ty.i32())}, - ast::DecorationList{})); + auto* A = Structure("A", {Member("a", ty.i32())}); type::AccessControl read{ast::AccessControl::kReadOnly, A}; type::AccessControl rw{ast::AccessControl::kReadWrite, A}; @@ -494,7 +492,9 @@ TEST_F(BuilderTest, GlobalVar_TwoVarDeclReadOnly) { EXPECT_TRUE(b.GenerateGlobalVariable(var_c)) << b.error(); EXPECT_EQ(DumpInstructions(b.annots()), - R"(OpMemberDecorate %3 0 NonWritable + R"(OpMemberDecorate %3 0 Offset 0 +OpMemberDecorate %3 0 NonWritable +OpMemberDecorate %7 0 Offset 0 )"); EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A" OpMemberName %3 0 "a" @@ -665,6 +665,7 @@ OpExecutionMode %11 LocalSize 1 1 1 OpName %1 "mask_in" OpName %6 "mask_out" OpName %11 "main" +OpDecorate %3 ArrayStride 4 OpDecorate %1 BuiltIn SampleMask OpDecorate %6 BuiltIn SampleMask %4 = OpTypeInt 32 0 diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc index 3d1510d11c..4fe131e438 100644 --- a/src/writer/spirv/builder_intrinsic_test.cc +++ b/src/writer/spirv/builder_intrinsic_test.cc @@ -1377,13 +1377,9 @@ OpFunctionEnd } TEST_F(IntrinsicBuilderTest, Call_ArrayLength) { - auto* s = create( - ast::StructMemberList{Member(0, "a", ty.array(4))}, - ast::DecorationList{ - create(), - }); - auto* s_type = ty.struct_("my_struct", s); - Global("b", s_type, ast::StorageClass::kStorage, nullptr, + auto* s = Structure("my_struct", {Member(0, "a", ty.array(4))}, + {create()}); + Global("b", s, ast::StorageClass::kStorage, nullptr, ast::DecorationList{ create(1), create(2), @@ -1426,15 +1422,14 @@ TEST_F(IntrinsicBuilderTest, Call_ArrayLength) { } TEST_F(IntrinsicBuilderTest, Call_ArrayLength_OtherMembersInStruct) { - auto* s = create( - ast::StructMemberList{Member(0, "z", ty.f32()), - Member(4, "a", ty.array(4))}, - ast::DecorationList{ - create(), - }); + auto* s = Structure("my_struct", + { + Member(0, "z", ty.f32()), + Member(4, "a", ty.array(4)), + }, + {create()}); - auto* s_type = ty.struct_("my_struct", s); - Global("b", s_type, ast::StorageClass::kStorage, nullptr, + Global("b", s, ast::StorageClass::kStorage, nullptr, ast::DecorationList{ create(1), create(2), diff --git a/src/writer/spirv/builder_type_test.cc b/src/writer/spirv/builder_type_test.cc index cb8a1d1cd0..5e990a1a4c 100644 --- a/src/writer/spirv/builder_type_test.cc +++ b/src/writer/spirv/builder_type_test.cc @@ -58,11 +58,11 @@ TEST_F(BuilderTest_Type, ReturnsGeneratedAlias) { } TEST_F(BuilderTest_Type, GenerateRuntimeArray) { - type::Array ary(ty.i32(), 0, ast::DecorationList{}); + auto* ary = ty.array(ty.i32(), 0); spirv::Builder& b = Build(); - auto id = b.GenerateTypeIfNeeded(&ary); + auto id = b.GenerateTypeIfNeeded(ary); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(1u, id); @@ -72,12 +72,12 @@ TEST_F(BuilderTest_Type, GenerateRuntimeArray) { } TEST_F(BuilderTest_Type, ReturnsGeneratedRuntimeArray) { - type::Array ary(ty.i32(), 0, ast::DecorationList{}); + auto* ary = ty.array(ty.i32(), 0); spirv::Builder& b = Build(); - EXPECT_EQ(b.GenerateTypeIfNeeded(&ary), 1u); - EXPECT_EQ(b.GenerateTypeIfNeeded(&ary), 1u); + EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u); + EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1 @@ -86,11 +86,11 @@ TEST_F(BuilderTest_Type, ReturnsGeneratedRuntimeArray) { } TEST_F(BuilderTest_Type, GenerateArray) { - type::Array ary(ty.i32(), 4, ast::DecorationList{}); + auto* ary = ty.array(ty.i32(), 4); spirv::Builder& b = Build(); - auto id = b.GenerateTypeIfNeeded(&ary); + auto id = b.GenerateTypeIfNeeded(ary); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(1u, id); @@ -102,14 +102,11 @@ TEST_F(BuilderTest_Type, GenerateArray) { } TEST_F(BuilderTest_Type, GenerateArray_WithStride) { - type::Array ary(ty.i32(), 4, - ast::DecorationList{ - create(16u), - }); + auto* ary = ty.array(ty.i32(), 4, 16u); spirv::Builder& b = Build(); - auto id = b.GenerateTypeIfNeeded(&ary); + auto id = b.GenerateTypeIfNeeded(ary); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(1u, id); @@ -124,12 +121,12 @@ TEST_F(BuilderTest_Type, GenerateArray_WithStride) { } TEST_F(BuilderTest_Type, ReturnsGeneratedArray) { - type::Array ary(ty.i32(), 4, ast::DecorationList{}); + auto* ary = ty.array(ty.i32(), 4); spirv::Builder& b = Build(); - EXPECT_EQ(b.GenerateTypeIfNeeded(&ary), 1u); - EXPECT_EQ(b.GenerateTypeIfNeeded(&ary), 1u); + EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u); + EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1 @@ -277,12 +274,11 @@ TEST_F(BuilderTest_Type, ReturnsGeneratedPtr) { } TEST_F(BuilderTest_Type, GenerateStruct_Empty) { - auto* s = create(ast::StructMemberList{}, ast::DecorationList{}); - auto* s_type = ty.struct_("S", s); + auto* s = Structure("S", {}); spirv::Builder& b = Build(); - auto id = b.GenerateTypeIfNeeded(s_type); + auto id = b.GenerateTypeIfNeeded(s); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(id, 1u); @@ -294,13 +290,11 @@ TEST_F(BuilderTest_Type, GenerateStruct_Empty) { } TEST_F(BuilderTest_Type, GenerateStruct) { - auto* s = create(ast::StructMemberList{Member("a", ty.f32())}, - ast::DecorationList{}); - auto* s_type = ty.struct_("my_struct", s); + auto* s = Structure("my_struct", {Member("a", ty.f32())}); spirv::Builder& b = Build(); - auto id = b.GenerateTypeIfNeeded(s_type); + auto id = b.GenerateTypeIfNeeded(s); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(id, 1u); @@ -313,16 +307,12 @@ OpMemberName %1 0 "a" } TEST_F(BuilderTest_Type, GenerateStruct_Decorated) { - ast::DecorationList struct_decos; - struct_decos.push_back(create()); - - auto* s = create(ast::StructMemberList{Member("a", ty.f32())}, - struct_decos); - auto* s_type = ty.struct_("my_struct", s); + auto* s = Structure("my_struct", {Member("a", ty.f32())}, + {create()}); spirv::Builder& b = Build(); - auto id = b.GenerateTypeIfNeeded(s_type); + auto id = b.GenerateTypeIfNeeded(s); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(id, 1u); @@ -333,19 +323,19 @@ TEST_F(BuilderTest_Type, GenerateStruct_Decorated) { OpMemberName %1 0 "a" )"); EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %1 Block +OpMemberDecorate %1 0 Offset 0 )"); } TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers) { - auto* s = create( - ast::StructMemberList{Member("a", ty.f32(), {MemberOffset(0)}), - Member("b", ty.f32(), {MemberOffset(8)})}, - ast::DecorationList{}); - auto* s_type = ty.struct_("S", s); + auto* s = Structure("S", { + Member("a", ty.f32()), + Member("b", ty.f32(), {MemberAlign(8)}), + }); spirv::Builder& b = Build(); - auto id = b.GenerateTypeIfNeeded(s_type); + auto id = b.GenerateTypeIfNeeded(s); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(id, 1u); @@ -362,16 +352,15 @@ OpMemberDecorate %1 1 Offset 8 } TEST_F(BuilderTest_Type, GenerateStruct_NonLayout_Matrix) { - auto* s = - create(ast::StructMemberList{Member("a", ty.mat2x2()), - Member("b", ty.mat2x3()), - Member("c", ty.mat4x4())}, - ast::DecorationList{}); - auto* s_type = ty.struct_("S", s); + auto* s = Structure("S", { + Member("a", ty.mat2x2()), + Member("b", ty.mat2x3()), + Member("c", ty.mat4x4()), + }); spirv::Builder& b = Build(); - auto id = b.GenerateTypeIfNeeded(s_type); + auto id = b.GenerateTypeIfNeeded(s); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(id, 1u); @@ -389,21 +378,29 @@ OpMemberName %1 0 "a" OpMemberName %1 1 "b" OpMemberName %1 2 "c" )"); - EXPECT_EQ(DumpInstructions(b.annots()), ""); + EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %1 0 Offset 0 +OpMemberDecorate %1 0 ColMajor +OpMemberDecorate %1 0 MatrixStride 8 +OpMemberDecorate %1 1 Offset 16 +OpMemberDecorate %1 1 ColMajor +OpMemberDecorate %1 1 MatrixStride 16 +OpMemberDecorate %1 2 Offset 48 +OpMemberDecorate %1 2 ColMajor +OpMemberDecorate %1 2 MatrixStride 16 +)"); } TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers_LayoutMatrix) { // We have to infer layout for matrix when it also has an offset. - auto* s = create( - ast::StructMemberList{Member("a", ty.mat2x2(), {MemberOffset(0)}), - Member("b", ty.mat2x3(), {MemberOffset(16)}), - Member("c", ty.mat4x4(), {MemberOffset(48)})}, - ast::DecorationList{}); - auto* s_type = ty.struct_("S", s); + auto* s = Structure("S", { + Member("a", ty.mat2x2()), + Member("b", ty.mat2x3()), + Member("c", ty.mat4x4()), + }); spirv::Builder& b = Build(); - auto id = b.GenerateTypeIfNeeded(s_type); + auto id = b.GenerateTypeIfNeeded(s); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(id, 1u); @@ -437,26 +434,19 @@ TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers_LayoutArraysOfMatrix) { // We have to infer layout for matrix when it also has an offset. // The decoration goes on the struct member, even if the matrix is buried // in levels of arrays. - type::Array arr_mat2x2(ty.mat2x2(), 1, - ast::DecorationList{}); // Singly nested array + auto* arr_mat2x2 = ty.array(ty.mat2x2(), 1); // Singly nested array + auto* arr_arr_mat2x3 = ty.array(ty.mat2x3(), 1); // Doubly nested array + auto* rtarr_mat4x4 = ty.array(ty.mat4x4(), 0); // Runtime array - type::Array arr_mat2x3(ty.mat2x3(), 1, ast::DecorationList{}); - type::Array arr_arr_mat2x3(ty.mat2x3(), 1, - ast::DecorationList{}); // Doubly nested array - - type::Array rtarr_mat4x4(ty.mat4x4(), 0, - ast::DecorationList{}); // Runtime array - - auto* s = create( - ast::StructMemberList{Member("a", &arr_mat2x2, {MemberOffset(0)}), - Member("b", &arr_arr_mat2x3, {MemberOffset(16)}), - Member("c", &rtarr_mat4x4, {MemberOffset(48)})}, - ast::DecorationList{}); - auto* s_type = ty.struct_("S", s); + auto* s = Structure("S", { + Member("a", arr_mat2x2), + Member("b", arr_arr_mat2x3), + Member("c", rtarr_mat4x4), + }); spirv::Builder& b = Build(); - auto id = b.GenerateTypeIfNeeded(s_type); + auto id = b.GenerateTypeIfNeeded(s); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(id, 1u); @@ -482,12 +472,15 @@ OpMemberName %1 2 "c" EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %1 0 Offset 0 OpMemberDecorate %1 0 ColMajor OpMemberDecorate %1 0 MatrixStride 8 +OpDecorate %2 ArrayStride 16 OpMemberDecorate %1 1 Offset 16 OpMemberDecorate %1 1 ColMajor OpMemberDecorate %1 1 MatrixStride 16 +OpDecorate %8 ArrayStride 32 OpMemberDecorate %1 2 Offset 48 OpMemberDecorate %1 2 ColMajor OpMemberDecorate %1 2 MatrixStride 16 +OpDecorate %11 ArrayStride 64 )"); } diff --git a/src/writer/wgsl/generator_impl.cc b/src/writer/wgsl/generator_impl.cc index 7ea7a8f4d4..304f720b81 100644 --- a/src/writer/wgsl/generator_impl.cc +++ b/src/writer/wgsl/generator_impl.cc @@ -24,7 +24,9 @@ #include "src/ast/sint_literal.h" #include "src/ast/stage_decoration.h" #include "src/ast/stride_decoration.h" +#include "src/ast/struct_member_align_decoration.h" #include "src/ast/struct_member_offset_decoration.h" +#include "src/ast/struct_member_size_decoration.h" #include "src/ast/uint_literal.h" #include "src/ast/variable_decl_statement.h" #include "src/ast/workgroup_decoration.h" @@ -582,6 +584,10 @@ bool GeneratorImpl::EmitDecorations(const ast::DecorationList& decos) { out_ << "constant_id(" << constant->value() << ")"; } else if (auto* offset = deco->As()) { out_ << "offset(" << offset->offset() << ")"; + } else if (auto* size = deco->As()) { + out_ << "[[size(" << size->size() << ")]]" << std::endl; + } else if (auto* align = deco->As()) { + out_ << "[[align(" << align->align() << ")]]" << std::endl; } else { diagnostics_.add_error("unknown variable decoration"); return false; diff --git a/src/writer/wgsl/generator_impl_alias_type_test.cc b/src/writer/wgsl/generator_impl_alias_type_test.cc index 1155aa3220..a1ecefa400 100644 --- a/src/writer/wgsl/generator_impl_alias_type_test.cc +++ b/src/writer/wgsl/generator_impl_alias_type_test.cc @@ -31,12 +31,11 @@ TEST_F(WgslGeneratorImplTest, EmitAlias_F32) { } TEST_F(WgslGeneratorImplTest, EmitConstructedType_Struct) { - auto* str = create( - ast::StructMemberList{Member("a", ty.f32()), - Member("b", ty.i32(), {MemberOffset(4)})}, - ast::DecorationList{}); + auto* s = Structure("A", { + Member("a", ty.f32()), + Member("b", ty.i32()), + }); - auto* s = ty.struct_("A", str); auto* alias = ty.alias("B", s); GeneratorImpl& gen = Build(); @@ -45,7 +44,6 @@ TEST_F(WgslGeneratorImplTest, EmitConstructedType_Struct) { ASSERT_TRUE(gen.EmitConstructedType(alias)) << gen.error(); EXPECT_EQ(gen.result(), R"(struct A { a : f32; - [[offset(4)]] b : i32; }; type B = A; @@ -53,12 +51,11 @@ type B = A; } TEST_F(WgslGeneratorImplTest, EmitAlias_ToStruct) { - auto* str = create( - ast::StructMemberList{Member("a", ty.f32()), - Member("b", ty.i32(), {MemberOffset(4)})}, - ast::DecorationList{}); + auto* s = Structure("A", { + Member("a", ty.f32()), + Member("b", ty.i32()), + }); - auto* s = ty.struct_("A", str); auto* alias = ty.alias("B", s); GeneratorImpl& gen = Build(); diff --git a/src/writer/wgsl/generator_impl_function_test.cc b/src/writer/wgsl/generator_impl_function_test.cc index 63cc1f2f97..f79d0d6d99 100644 --- a/src/writer/wgsl/generator_impl_function_test.cc +++ b/src/writer/wgsl/generator_impl_function_test.cc @@ -171,7 +171,7 @@ TEST_F(WgslGeneratorImplTest, Emit_Function_EntryPoint_Parameters) { TEST_F(WgslGeneratorImplTest, Emit_Function_Multiple_EntryPoint_With_Same_ModuleVar) { // [[block]] struct Data { - // [[offset(0)]] d : f32; + // d : f32; // }; // [[binding(0), group(0)]] var data : Data; // @@ -185,15 +185,10 @@ TEST_F(WgslGeneratorImplTest, // return; // } - ast::DecorationList s_decos; - s_decos.push_back(create()); + auto* s = Structure("Data", {Member("d", ty.f32())}, + {create()}); - auto* str = create( - ast::StructMemberList{Member("d", ty.f32(), {MemberOffset(0)})}, s_decos); - - auto* s = ty.struct_("Data", str); type::AccessControl ac(ast::AccessControl::kReadWrite, s); - AST().AddConstructedType(s); Global("data", &ac, ast::StorageClass::kStorage, nullptr, ast::DecorationList{ @@ -236,7 +231,6 @@ TEST_F(WgslGeneratorImplTest, ASSERT_TRUE(gen.Generate(nullptr)) << gen.error(); EXPECT_EQ(gen.result(), R"([[block]] struct Data { - [[offset(0)]] d : f32; }; diff --git a/src/writer/wgsl/generator_impl_type_test.cc b/src/writer/wgsl/generator_impl_type_test.cc index b3e555336e..3d4d2ca7df 100644 --- a/src/writer/wgsl/generator_impl_type_test.cc +++ b/src/writer/wgsl/generator_impl_type_test.cc @@ -45,13 +45,8 @@ TEST_F(WgslGeneratorImplTest, EmitType_Array) { } TEST_F(WgslGeneratorImplTest, EmitType_AccessControl_Read) { - auto* block_deco = create(); - ast::DecorationList decos; - decos.push_back(block_deco); - - auto* str = - create(ast::StructMemberList{Member("a", ty.i32())}, decos); - auto* s = ty.struct_("S", str); + auto* s = Structure("S", {Member("a", ty.i32())}, + {create()}); type::AccessControl a(ast::AccessControl::kReadOnly, s); @@ -62,13 +57,8 @@ TEST_F(WgslGeneratorImplTest, EmitType_AccessControl_Read) { } TEST_F(WgslGeneratorImplTest, EmitType_AccessControl_ReadWrite) { - auto* block_deco = create(); - ast::DecorationList decos; - decos.push_back(block_deco); - - auto* str = - create(ast::StructMemberList{Member("a", ty.i32())}, decos); - auto* s = ty.struct_("S", str); + auto* s = Structure("S", {Member("a", ty.i32())}, + {create()}); type::AccessControl a(ast::AccessControl::kReadWrite, s); @@ -158,12 +148,11 @@ TEST_F(WgslGeneratorImplTest, EmitType_Pointer) { } TEST_F(WgslGeneratorImplTest, EmitType_Struct) { - auto* str = create( - ast::StructMemberList{Member("a", ty.i32()), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); + auto* s = Structure("S", { + Member("a", ty.i32()), + Member("b", ty.f32()), + }); - auto* s = ty.struct_("S", str); GeneratorImpl& gen = Build(); ASSERT_TRUE(gen.EmitType(s)) << gen.error(); @@ -171,12 +160,11 @@ TEST_F(WgslGeneratorImplTest, EmitType_Struct) { } TEST_F(WgslGeneratorImplTest, EmitType_StructDecl) { - auto* str = create( - ast::StructMemberList{Member("a", ty.i32()), - Member("b", ty.f32(), {MemberOffset(4)})}, - ast::DecorationList{}); + auto* s = Structure("S", { + Member("a", ty.i32()), + Member("b", ty.f32(), {MemberOffset(4)}), + }); - auto* s = ty.struct_("S", str); GeneratorImpl& gen = Build(); ASSERT_TRUE(gen.EmitStructType(s)) << gen.error(); @@ -192,12 +180,13 @@ TEST_F(WgslGeneratorImplTest, EmitType_Struct_WithDecoration) { ast::DecorationList decos; decos.push_back(create()); - auto* str = create( - ast::StructMemberList{Member("a", ty.i32()), - Member("b", ty.f32(), {MemberOffset(4)})}, - decos); + auto* s = Structure("S", + { + Member("a", ty.i32()), + Member("b", ty.f32(), {MemberOffset(4)}), + }, + decos); - auto* s = ty.struct_("S", str); GeneratorImpl& gen = Build(); ASSERT_TRUE(gen.EmitStructType(s)) << gen.error(); diff --git a/test/BUILD.gn b/test/BUILD.gn index 775d5f4bb3..a3eb062edd 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -145,7 +145,9 @@ source_set("tint_unittests_core_src") { "../src/ast/sint_literal_test.cc", "../src/ast/stage_decoration_test.cc", "../src/ast/stride_decoration_test.cc", + "../src/ast/struct_member_align_decoration_test.cc", "../src/ast/struct_member_offset_decoration_test.cc", + "../src/ast/struct_member_size_decoration_test.cc", "../src/ast/struct_member_test.cc", "../src/ast/struct_test.cc", "../src/ast/switch_statement_test.cc", @@ -168,9 +170,11 @@ source_set("tint_unittests_core_src") { "../src/program_builder_test.cc", "../src/program_test.cc", "../src/resolver/intrinsic_test.cc", - "../src/resolver/resolver_test.cc", + "../src/resolver/is_storeable_test.cc", "../src/resolver/resolver_test_helper.cc", "../src/resolver/resolver_test_helper.h", + "../src/resolver/resolver_test.cc", + "../src/resolver/struct_layout_test.cc", "../src/resolver/validation_test.cc", "../src/scope_stack_test.cc", "../src/semantic/sem_intrinsic_test.cc", diff --git a/test/compute_boids.wgsl b/test/compute_boids.wgsl index 86617a68d7..0289b76110 100644 --- a/test/compute_boids.wgsl +++ b/test/compute_boids.wgsl @@ -38,22 +38,22 @@ fn frag_main() -> void { // compute shader [[block]] struct Particle { - [[offset(0)]] pos : vec2; - [[offset(8)]] vel : vec2; + pos : vec2; + vel : vec2; }; [[block]] struct SimParams { - [[offset(0)]] deltaT : f32; - [[offset(4)]] rule1Distance : f32; - [[offset(8)]] rule2Distance : f32; - [[offset(12)]] rule3Distance : f32; - [[offset(16)]] rule1Scale : f32; - [[offset(20)]] rule2Scale : f32; - [[offset(24)]] rule3Scale : f32; + deltaT : f32; + rule1Distance : f32; + rule2Distance : f32; + rule3Distance : f32; + rule1Scale : f32; + rule2Scale : f32; + rule3Scale : f32; }; [[block]] struct Particles { - [[offset(0)]] particles : [[stride(16)]] array; + particles : array; }; [[binding(0), group(0)]] var params : [[access(read)]] SimParams; diff --git a/test/cube.wgsl b/test/cube.wgsl index 908756c3d2..aef1be2896 100644 --- a/test/cube.wgsl +++ b/test/cube.wgsl @@ -14,7 +14,7 @@ // Vertex shader [[block]] struct Uniforms { - [[offset(0)]] modelViewProjectionMatrix : mat4x4; + modelViewProjectionMatrix : mat4x4; }; [[binding(0), group(0)]] var uniforms : [[access(read)]] Uniforms;