Implement Default Struct Layout
Implements https://github.com/gpuweb/gpuweb/pull/1447
SPIR-V Reader is still TODO, but continues to function as the offset
decoration is still supported.
Bug: tint:626
Bug: tint:629
Change-Id: Id574eb3a5c6729559382812de37b23f0c68fd406
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/43640
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/samples/main.cc b/samples/main.cc
index 6a5d80e..eab18f4 100644
--- a/samples/main.cc
+++ b/samples/main.cc
@@ -750,8 +750,6 @@
<< "]:" << 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 b5398bd..baaf5ae 100644
--- a/src/BUILD.gn
+++ b/src/BUILD.gn
@@ -317,8 +317,12 @@
"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 @@
"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 @@
"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 0ba3a57..006c202 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -132,8 +132,12 @@
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 @@
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 @@
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 @@
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 @@
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 39095fd..4e3fea6 100644
--- a/src/ast/module_clone_test.cc
+++ b/src/ast/module_clone_test.cc
@@ -28,9 +28,8 @@
// 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<u32>;
};
@@ -38,7 +37,7 @@
const c1 : bool = true;
type t0 = [[stride(16)]] array<vec4<f32>>;
-type t1 = [[stride(32)]] array<vec4<f32>>;
+type t1 = array<vec4<f32>>;
var<uniform> g0 : u32 = 20u;
var<out> 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 0000000..03a353c
--- /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<StructMemberAlignDecoration>(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 0000000..e408e32
--- /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 <stddef.h>
+
+#include "src/ast/decoration.h"
+
+namespace tint {
+namespace ast {
+
+/// A struct member align decoration
+class StructMemberAlignDecoration
+ : public Castable<StructMemberAlignDecoration, Decoration> {
+ 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 0000000..fc6e5d3
--- /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<StructMemberAlignDecoration>(2);
+ EXPECT_EQ(2u, d->align());
+}
+
+TEST_F(StructMemberAlignDecorationTest, Is) {
+ auto* d = create<StructMemberAlignDecoration>(2);
+ EXPECT_TRUE(d->Is<StructMemberAlignDecoration>());
+}
+
+} // namespace
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/struct_member_offset_decoration.h b/src/ast/struct_member_offset_decoration.h
index d793e75..3ab806d 100644
--- a/src/ast/struct_member_offset_decoration.h
+++ b/src/ast/struct_member_offset_decoration.h
@@ -21,6 +21,8 @@
namespace ast {
/// A struct member offset decoration
+// [DEPRECATED] - Replaced with StructMemberAlignDecoration and
+// StructMemberSizeDecoration
class StructMemberOffsetDecoration
: public Castable<StructMemberOffsetDecoration, Decoration> {
public:
diff --git a/src/ast/struct_member_size_decoration.cc b/src/ast/struct_member_size_decoration.cc
new file mode 100644
index 0000000..54e536a
--- /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<StructMemberSizeDecoration>(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 0000000..f99fd50
--- /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 <stddef.h>
+
+#include "src/ast/decoration.h"
+
+namespace tint {
+namespace ast {
+
+/// A struct member size decoration
+class StructMemberSizeDecoration
+ : public Castable<StructMemberSizeDecoration, Decoration> {
+ 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 0000000..61427b7
--- /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<StructMemberSizeDecoration>(2);
+ EXPECT_EQ(2u, d->size());
+}
+
+TEST_F(StructMemberOffsetDecorationTest, Is) {
+ auto* d = create<StructMemberSizeDecoration>(2);
+ EXPECT_TRUE(d->Is<StructMemberSizeDecoration>());
+}
+
+} // namespace
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/struct_member_test.cc b/src/ast/struct_member_test.cc
index 17c4425..c760c85 100644
--- a/src/ast/struct_member_test.cc
+++ b/src/ast/struct_member_test.cc
@@ -22,11 +22,11 @@
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<StructMemberOffsetDecoration>());
+ EXPECT_TRUE(st->decorations()[0]->Is<StructMemberSizeDecoration>());
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 @@
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 b049940..b2e96f5 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 @@
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 @@
continue;
}
- if (!decl->type()->UnwrapIfNeeded()->Is<type::Struct>()) {
+ auto* str = decl->type()->UnwrapIfNeeded()->As<type::Struct>();
+ 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 @@
: 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 d1c047a..ec91efb 100644
--- a/src/inspector/inspector.h
+++ b/src/inspector/inspector.h
@@ -112,8 +112,8 @@
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 5e7b95e..a48af7a 100644
--- a/src/inspector/inspector_test.cc
+++ b/src/inspector/inspector_test.cc
@@ -190,22 +190,15 @@
/// 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<std::tuple<type::Type*, uint32_t>> members_info,
- bool is_block) {
+ type::Struct* MakeStructType(const std::string& name,
+ std::vector<type::Type*> 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 @@
}
auto* str = create<ast::Struct>(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<type::Struct*, type::AccessControl*> MakeUniformBufferTypes(
const std::string& name,
- std::vector<std::tuple<type::Type*, uint32_t>> members_info) {
- auto* struct_type = MakeStructType(name, members_info, true);
+ std::vector<type::Type*> member_types) {
+ auto* struct_type = MakeStructType(name, member_types, true);
auto* access_type =
create<type::AccessControl>(ast::AccessControl::kReadOnly, struct_type);
return {struct_type, std::move(access_type)};
@@ -235,15 +229,14 @@
/// 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<type::Struct*, type::AccessControl*> MakeStorageBufferTypes(
const std::string& name,
- std::vector<std::tuple<type::Type*, uint32_t>> members_info) {
- auto* struct_type = MakeStructType(name, members_info, false);
+ std::vector<type::Type*> member_types) {
+ auto* struct_type = MakeStructType(name, member_types, false);
auto* access_type = create<type::AccessControl>(
ast::AccessControl::kReadWrite, struct_type);
return {struct_type, std::move(access_type)};
@@ -251,16 +244,14 @@
/// 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<type::Struct*, type::AccessControl*>
- MakeReadOnlyStorageBufferTypes(
- const std::string& name,
- std::vector<std::tuple<type::Type*, uint32_t>> members_info) {
- auto* struct_type = MakeStructType(name, members_info, false);
+ MakeReadOnlyStorageBufferTypes(const std::string& name,
+ std::vector<type::Type*> member_types) {
+ auto* struct_type = MakeStructType(name, member_types, false);
auto* access_type =
create<type::AccessControl>(ast::AccessControl::kReadOnly, struct_type);
return {struct_type, std::move(access_type)};
@@ -1404,21 +1395,21 @@
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 @@
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, MissingBlockDeco) {
ast::DecorationList decos;
auto* str = create<ast::Struct>(
- 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 941f93e..39832a2 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 @@
return create<ast::StructMemberOffsetDecoration>(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<ast::StructMemberSizeDecoration>(source, val);
+ }
+
+ /// Creates a ast::StructMemberSizeDecoration
+ /// @param val the size value
+ /// @returns the size decoration pointer
+ ast::StructMemberSizeDecoration* MemberSize(uint32_t val) {
+ return create<ast::StructMemberSizeDecoration>(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<ast::StructMemberAlignDecoration>(source, val);
+ }
+
+ /// Creates a ast::StructMemberAlignDecoration
+ /// @param val the align value
+ /// @returns the align decoration pointer
+ ast::StructMemberAlignDecoration* MemberAlign(uint32_t val) {
+ return create<ast::StructMemberAlignDecoration>(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 @@
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<ast::Struct>(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<ast::Struct>(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<ast::StructMember>(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<ast::StructMember>(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<ast::StructMember>(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 e4d2fba..ab21b4d 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 @@
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<type::U32>());
@@ -365,8 +364,10 @@
auto* arr_type = type->As<type::Array>();
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<ast::StrideDecoration>());
+ ASSERT_EQ(stride->As<ast::StrideDecoration>()->stride(), 64u);
EXPECT_TRUE(p->error().empty());
}
@@ -413,8 +414,7 @@
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<type::U32>());
@@ -499,8 +499,12 @@
EXPECT_TRUE(type->Is<type::Array>());
auto* arr_type = type->As<type::Array>();
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<ast::StrideDecoration>());
+ ASSERT_EQ(stride->As<ast::StrideDecoration>()->stride(), 8u);
+
EXPECT_TRUE(p->error().empty());
}
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index b025f44..34fc690 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -104,7 +104,9 @@
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 @@
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 @@
});
}
+ 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<ast::StructMemberSizeDecoration>(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<ast::StructMemberAlignDecoration>(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 3fae7f6..00cd58f 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 @@
" ^\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 08a7a50..de3d4716 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_Struct_WithStride) {
- auto p =
- parser("struct A { [[offset(0)]] data: [[stride(4)]] array<f32>; };");
+ auto p = parser("struct A { data: [[stride(4)]] array<f32>; };");
p->expect_global_decl();
ASSERT_FALSE(p->has_error()) << p->error();
@@ -194,12 +193,15 @@
const auto* ty = str->impl()->members()[0]->type();
ASSERT_TRUE(ty->Is<type::Array>());
const auto* arr = ty->As<type::Array>();
- 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<ast::StrideDecoration>());
+ ASSERT_EQ(stride->As<ast::StrideDecoration>()->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 c7f588b..16bcf4b 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 @@
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 23a3dd0..869baa2 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 @@
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 533a5bb..107c358 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_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 @@
ASSERT_EQ(decos.value.size(), 1u);
auto* deco = decos.value[0]->As<ast::Decoration>();
ASSERT_NE(deco, nullptr);
- EXPECT_TRUE(deco->Is<ast::StructMemberOffsetDecoration>());
+ EXPECT_TRUE(deco->Is<ast::StructMemberSizeDecoration>());
}
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 911c3be..68ce72a 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 @@
auto p = parser("a : i32;");
auto& builder = p->builder();
- auto* i32 = builder.create<type::I32>();
+ auto* i32 = builder.ty.i32();
auto decos = p->decoration_list();
EXPECT_FALSE(decos.errored);
@@ -45,11 +45,11 @@
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<type::I32>();
+ auto* i32 = builder.ty.i32();
auto decos = p->decoration_list();
EXPECT_FALSE(decos.errored);
@@ -75,12 +75,99 @@
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<type::I32>();
+ 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<ast::StructMemberAlignDecoration>());
+ EXPECT_EQ(
+ m->decorations()[0]->As<ast::StructMemberAlignDecoration>()->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<ast::StructMemberSizeDecoration>());
+ EXPECT_EQ(m->decorations()[0]->As<ast::StructMemberSizeDecoration>()->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<ast::StructMemberSizeDecoration>());
+ EXPECT_EQ(m->decorations()[0]->As<ast::StructMemberSizeDecoration>()->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 @@
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<ast::StructMemberOffsetDecoration>());
+ EXPECT_TRUE(m->decorations()[0]->Is<ast::StructMemberSizeDecoration>());
+ EXPECT_EQ(m->decorations()[0]->As<ast::StructMemberSizeDecoration>()->size(),
+ 2u);
+ EXPECT_TRUE(m->decorations()[1]->Is<ast::StructMemberAlignDecoration>());
EXPECT_EQ(
- m->decorations()[0]->As<ast::StructMemberOffsetDecoration>()->offset(),
- 2u);
- EXPECT_TRUE(m->decorations()[1]->Is<ast::StructMemberOffsetDecoration>());
- EXPECT_EQ(
- m->decorations()[1]->As<ast::StructMemberOffsetDecoration>()->offset(),
- 4u);
+ m->decorations()[1]->As<ast::StructMemberAlignDecoration>()->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 @@
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 @@
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 ab4d9f2..995c411 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 @@
ASSERT_FALSE(a->IsRuntimeArray());
ASSERT_EQ(a->size(), 5u);
ASSERT_TRUE(a->type()->Is<type::F32>());
- ASSERT_FALSE(a->has_array_stride());
+ EXPECT_EQ(a->decorations().size(), 0u);
}
TEST_F(ParserImplTest, TypeDecl_Array_Stride) {
@@ -360,8 +360,11 @@
ASSERT_FALSE(a->IsRuntimeArray());
ASSERT_EQ(a->size(), 5u);
ASSERT_TRUE(a->type()->Is<type::F32>());
- 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<ast::StrideDecoration>());
+ ASSERT_EQ(stride->As<ast::StrideDecoration>()->stride(), 16u);
}
TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Stride) {
@@ -376,8 +379,11 @@
auto* a = t->As<type::Array>();
ASSERT_TRUE(a->IsRuntimeArray());
ASSERT_TRUE(a->type()->Is<type::F32>());
- 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<ast::StrideDecoration>());
+ ASSERT_EQ(stride->As<ast::StrideDecoration>()->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 0000000..e11d539
--- /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<int>();
+ auto* vec3_i32 = ty.vec3<int>();
+ auto* vec4_i32 = ty.vec4<int>();
+ auto* vec2_u32 = ty.vec2<unsigned>();
+ auto* vec3_u32 = ty.vec3<unsigned>();
+ auto* vec4_u32 = ty.vec4<unsigned>();
+ auto* vec2_f32 = ty.vec2<float>();
+ auto* vec3_f32 = ty.vec3<float>();
+ auto* vec4_f32 = ty.vec4<float>();
+
+ 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<float>();
+ auto* mat2x3 = ty.mat2x3<float>();
+ auto* mat2x4 = ty.mat2x4<float>();
+ auto* mat3x2 = ty.mat3x2<float>();
+ auto* mat3x3 = ty.mat3x3<float>();
+ auto* mat3x4 = ty.mat3x4<float>();
+ auto* mat4x2 = ty.mat4x2<float>();
+ auto* mat4x3 = ty.mat4x3<float>();
+ auto* mat4x4 = ty.mat4x4<float>();
+
+ 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<int>(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<int>();
+
+ EXPECT_TRUE(r()->IsStorable(arr));
+}
+
+TEST_F(ResolverIsStorableTest, Struct_AllMembersStorable) {
+ ast::StructMemberList members{Member("a", ty.i32()), Member("b", ty.f32())};
+ auto* s = create<ast::Struct>(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<int>(ast::StorageClass::kPrivate);
+ ast::StructMemberList members{Member("a", ty.i32()), Member("b", ptr_ty)};
+ auto* s = create<ast::Struct>(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 24ea098..ddec567 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 @@
T old_value_;
};
+/// Rounds `value` to the next multiple of `alignment`
+/// Assumes `alignment` is positive.
+template <typename T>
+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 <typename T>
+bool IsPowerOfTwo(T value) {
+ return (value & (value - 1)) == 0;
+}
+
} // namespace
Resolver::Resolver(ProgramBuilder* builder)
@@ -98,7 +115,47 @@
return result;
}
+bool Resolver::IsStorable(type::Type* type) {
+ if (type == nullptr) {
+ return false;
+ }
+ if (type->is_scalar() || type->Is<type::Vector>() ||
+ type->Is<type::Matrix>()) {
+ return true;
+ }
+ if (type::Array* array_type = type->As<type::Array>()) {
+ return IsStorable(array_type->type());
+ }
+ if (type::Struct* struct_type = type->As<type::Struct>()) {
+ for (const auto* member : struct_type->impl()->members()) {
+ if (!IsStorable(member->type())) {
+ return false;
+ }
+ }
+ return true;
+ }
+ if (type::Alias* alias_type = type->As<type::Alias>()) {
+ return IsStorable(alias_type->type());
+ }
+ return false;
+}
+
bool Resolver::ResolveInternal() {
+ for (auto* ty : builder_->Types()) {
+ if (auto* str = ty->As<type::Struct>()) {
+ if (!Structure(str)) {
+ return false;
+ }
+ continue;
+ }
+ if (auto* arr = ty->As<type::Array>()) {
+ if (!Array(arr)) {
+ return false;
+ }
+ continue;
+ }
+ }
+
for (auto* var : builder_->AST().GlobalVariables()) {
variable_stack_.set_global(var->symbol(), CreateVariableInfo(var));
@@ -962,6 +1019,204 @@
}
}
+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<type::Vector>()) {
+ 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<type::Matrix>()) {
+ 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<type::Struct>()) {
+ if (auto* sem = Structure(s)) {
+ align = sem->Align();
+ size = sem->Size();
+ return true;
+ }
+ return false;
+ } else if (auto* arr = ty->As<type::Array>()) {
+ 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<uint32_t>(arr->size(), 1) * stride;
+ auto* sem = builder_->create<semantic::Array>(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<ast::StrideDecoration>()) {
+ 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<ast::StructMemberOffsetDecoration>()) {
+ // [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<ast::StructMemberAlignDecoration>()) {
+ 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<ast::StructMemberSizeDecoration>()) {
+ 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<semantic::StructMember>(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<semantic::Struct>(str, std::move(sem_members),
+ struct_align, struct_size);
+ builder_->Sem().Add(str, sem);
+ return sem;
+}
+
template <typename F>
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 b2e5bae..485bff8 100644
--- a/src/resolver/resolver.h
+++ b/src/resolver/resolver.h
@@ -42,8 +42,12 @@
class Variable;
} // namespace ast
namespace semantic {
+class Array;
class Statement;
} // namespace semantic
+namespace type {
+class Struct;
+} // namespace type
namespace resolver {
@@ -63,6 +67,10 @@
/// @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 @@
/// @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 @@
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 0000000..53dd37b
--- /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<i32, 3>()),
+ Member("b", ty.array<f32, 5>()),
+ Member("c", ty.array<f32, 1>()),
+ });
+
+ 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<i32, 3>(/*stride*/ 8)),
+ Member("b", ty.array<f32, 5>(/*stride*/ 16)),
+ Member("c", ty.array<f32, 1>(/*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<f32>()),
+ });
+
+ 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<f32>(/*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<i32, 2>(/*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<i32>()),
+ Member("b", ty.vec3<i32>()),
+ Member("c", ty.vec4<i32>()),
+ }); // 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<i32>()),
+ Member("b", ty.vec3<i32>()),
+ Member("c", ty.vec4<i32>()),
+ });
+
+ 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<i32>()),
+ Member("b", ty.mat2x3<i32>()),
+ Member("c", ty.mat2x4<i32>()),
+ Member("d", ty.mat3x2<i32>()),
+ Member("e", ty.mat3x3<i32>()),
+ Member("f", ty.mat3x4<i32>()),
+ Member("g", ty.mat4x2<i32>()),
+ Member("h", ty.mat4x3<i32>()),
+ Member("i", ty.mat4x4<i32>()),
+ });
+
+ 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<i32>()),
+ });
+ 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 87c111e..f3d3cbd 100644
--- a/src/resolver/validation_test.cc
+++ b/src/resolver/validation_test.cc
@@ -556,6 +556,38 @@
"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 0000000..c620d49
--- /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 <stdint.h>
+
+#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<Array, Node> {
+ 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 954585c..8ea4dc1 100644
--- a/src/semantic/call.h
+++ b/src/semantic/call.h
@@ -29,9 +29,9 @@
/// @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 6e478ff..ec14cee 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 @@
/// @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 <typename AST, typename SEM = SemanticNodeTypeFor<AST>>
- const SEM* Get(const AST* ast_node) const {
- auto it = ast_to_sem_.find(ast_node);
- if (it == ast_to_sem_.end()) {
+ template <typename AST_OR_TYPE,
+ typename SEM = SemanticNodeTypeFor<AST_OR_TYPE>>
+ 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<SEM>();
}
- /// 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 <typename AST>
- void Add(const AST* ast_node, const SemanticNodeTypeFor<AST>* sem_node) {
+ template <typename AST_OR_TYPE>
+ void Add(const AST_OR_TYPE* node,
+ const SemanticNodeTypeFor<AST_OR_TYPE>* 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 @@
/// @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<const ast::Node*, const semantic::Node*> ast_to_sem_;
+ std::unordered_map<const CastableBase*, const semantic::Node*> map;
};
} // namespace semantic
diff --git a/src/semantic/sem_array.cc b/src/semantic/sem_array.cc
new file mode 100644
index 0000000..c54a3fc
--- /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 0000000..0d3b971
--- /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 0000000..3d99655
--- /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 <stdint.h>
+
+#include <vector>
+
+#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<StructMember*>;
+
+/// Struct holds the semantic information for structures.
+class Struct : public Castable<Struct, Node> {
+ 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<StructMember, Node> {
+ 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 6b1c331..d0b48d8 100644
--- a/src/semantic/type_mappings.h
+++ b/src/semantic/type_mappings.h
@@ -19,44 +19,52 @@
// 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 <typename AST>
+/// AST or type node `AST_OR_TYPE`.
+template <typename AST_OR_TYPE>
using SemanticNodeTypeFor = typename std::remove_pointer<decltype(
- TypeMappings()(std::declval<AST*>()))>::type;
+ TypeMappings()(std::declval<AST_OR_TYPE*>()))>::type;
} // namespace semantic
} // namespace tint
diff --git a/src/transform/first_index_offset.cc b/src/transform/first_index_offset.cc
index 61ab725..6acabe0 100644
--- a/src/transform/first_index_offset.cc
+++ b/src/transform/first_index_offset.cc
@@ -154,26 +154,20 @@
ast::Variable* FirstIndexOffset::State::AddUniformBuffer() {
auto* u32_type = dst->create<type::U32>();
- ast::StructMemberList members;
uint32_t offset = 0;
+ ast::StructMemberList members;
if (has_vertex_index) {
- ast::DecorationList member_dec;
- member_dec.push_back(
- dst->create<ast::StructMemberOffsetDecoration>(Source{}, offset));
members.push_back(dst->create<ast::StructMember>(
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<ast::StructMemberOffsetDecoration>(Source{}, offset));
members.push_back(dst->create<ast::StructMember>(
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 f2d04ec..dd187d2 100644
--- a/src/transform/first_index_offset.h
+++ b/src/transform/first_index_offset.h
@@ -46,8 +46,8 @@
/// 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<in> tint_first_index_offset_vert_idx : u32;
/// [[binding(N), group(M)]] var<uniform> tint_first_index_data :
diff --git a/src/transform/first_index_offset_test.cc b/src/transform/first_index_offset_test.cc
index 4618915..3370cf4 100644
--- a/src/transform/first_index_offset_test.cc
+++ b/src/transform/first_index_offset_test.cc
@@ -98,7 +98,6 @@
[[block]]
struct TintFirstIndexOffsetData {
- [[offset(0)]]
tint_first_vertex_index : u32;
};
@@ -147,7 +146,6 @@
[[block]]
struct TintFirstIndexOffsetData {
- [[offset(0)]]
tint_first_instance_index : u32;
};
@@ -199,9 +197,7 @@
[[block]]
struct TintFirstIndexOffsetData {
- [[offset(0)]]
tint_first_vertex_index : u32;
- [[offset(4)]]
tint_first_instance_index : u32;
};
@@ -255,7 +251,6 @@
[[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 57340f1..a24f68b 100644
--- a/src/transform/hlsl_test.cc
+++ b/src/transform/hlsl_test.cc
@@ -96,7 +96,6 @@
auto* src = R"(
[[block]]
struct Uniforms {
- [[offset(0)]]
transform : mat2x2<f32>;
};
@@ -121,7 +120,6 @@
auto* expect = R"(
[[block]]
struct Uniforms {
- [[offset(0)]]
transform : mat2x2<f32>;
};
diff --git a/src/transform/msl.h b/src/transform/msl.h
index 7b5e16c..38a2cd1 100644
--- a/src/transform/msl.h
+++ b/src/transform/msl.h
@@ -34,6 +34,7 @@
/// @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 b9d6525..b82a4d5 100644
--- a/src/transform/vertex_pulling.cc
+++ b/src/transform/vertex_pulling.cc
@@ -230,13 +230,9 @@
// Creating the struct type
ast::StructMemberList members;
- ast::DecorationList member_dec;
- member_dec.push_back(
- ctx.dst->create<ast::StructMemberOffsetDecoration>(Source{}, 0u));
-
members.push_back(ctx.dst->create<ast::StructMember>(
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<ast::StructBlockDecoration>(Source{}));
diff --git a/src/transform/vertex_pulling_test.cc b/src/transform/vertex_pulling_test.cc
index 6c80e16..b4e1080 100644
--- a/src/transform/vertex_pulling_test.cc
+++ b/src/transform/vertex_pulling_test.cc
@@ -83,7 +83,6 @@
auto* expect = R"(
[[block]]
struct TintVertexData {
- [[offset(0)]]
_tint_vertex_data : [[stride(4)]] array<u32>;
};
@@ -120,7 +119,6 @@
[[block]]
struct TintVertexData {
- [[offset(0)]]
_tint_vertex_data : [[stride(4)]] array<u32>;
};
@@ -163,7 +161,6 @@
[[block]]
struct TintVertexData {
- [[offset(0)]]
_tint_vertex_data : [[stride(4)]] array<u32>;
};
@@ -206,7 +203,6 @@
[[block]]
struct TintVertexData {
- [[offset(0)]]
_tint_vertex_data : [[stride(4)]] array<u32>;
};
@@ -254,7 +250,6 @@
[[block]]
struct TintVertexData {
- [[offset(0)]]
_tint_vertex_data : [[stride(4)]] array<u32>;
};
@@ -316,7 +311,6 @@
[[block]]
struct TintVertexData {
- [[offset(0)]]
_tint_vertex_data : [[stride(4)]] array<u32>;
};
@@ -371,7 +365,6 @@
[[block]]
struct TintVertexData {
- [[offset(0)]]
_tint_vertex_data : [[stride(4)]] array<u32>;
};
diff --git a/src/type/access_control_type.cc b/src/type/access_control_type.cc
index 43c7924..4ecefe3 100644
--- a/src/type/access_control_type.cc
+++ b/src/type/access_control_type.cc
@@ -65,14 +65,6 @@
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 7dce8bd..7002ea4 100644
--- a/src/type/access_control_type.h
+++ b/src/type/access_control_type.h
@@ -54,16 +54,6 @@
/// 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 2256345..e390400 100644
--- a/src/type/access_control_type_test.cc
+++ b/src/type/access_control_type_test.cc
@@ -95,70 +95,6 @@
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<ast::StrideDecoration>(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<ast::StrideDecoration>(4)});
- AccessControl at{ast::AccessControl::kReadOnly, &array};
- EXPECT_EQ(4u, at.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
-}
-
-TEST_F(AccessControlTest, MinBufferBindingSizeStruct) {
- auto* str = create<ast::Struct>(
- 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<ast::StrideDecoration>(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<ast::StrideDecoration>(4)});
- AccessControl at{ast::AccessControl::kReadOnly, &array};
- EXPECT_EQ(16u, at.BaseAlignment(MemoryLayout::kUniformBuffer));
-}
-
-TEST_F(AccessControlTest, BaseAlignmentStruct) {
- auto* str = create<ast::Struct>(
- 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 f5e2d2a..b155cc2 100644
--- a/src/type/alias_type.cc
+++ b/src/type/alias_type.cc
@@ -38,14 +38,6 @@
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 5361950..ede6218 100644
--- a/src/type/alias_type.h
+++ b/src/type/alias_type.h
@@ -47,16 +47,6 @@
/// 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 278ea6a..741d04a 100644
--- a/src/type/alias_type_test.cc
+++ b/src/type/alias_type_test.cc
@@ -137,76 +137,6 @@
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<ast::StrideDecoration>(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<ast::StrideDecoration>(4),
- });
- auto* alias = ty.alias("alias", &array);
- EXPECT_EQ(4u, alias->MinBufferBindingSize(MemoryLayout::kUniformBuffer));
-}
-
-TEST_F(AliasTest, MinBufferBindingSizeStruct) {
- auto* str = create<ast::Struct>(
- 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<ast::StrideDecoration>(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<ast::StrideDecoration>(4),
- });
- auto* alias = ty.alias("alias", &array);
- EXPECT_EQ(16u, alias->BaseAlignment(MemoryLayout::kUniformBuffer));
-}
-
-TEST_F(AliasTest, BaseAlignmentStruct) {
- auto* str = create<ast::Struct>(
- 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 29ead9b..46fe3ed 100644
--- a/src/type/array_type.cc
+++ b/src/type/array_type.cc
@@ -30,71 +30,28 @@
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<float>(subtype_->BaseAlignment(mem_layout));
- return static_cast<uint64_t>(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<ast::StrideDecoration>()) {
- return stride->stride();
- }
- }
- return 0;
-}
-
-bool Array::has_array_stride() const {
- for (auto* deco : decos_) {
- if (deco->Is<ast::StrideDecoration>()) {
- 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<ast::StrideDecoration>()) {
+ 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<ast::StrideDecoration>()) {
+ 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 c0f74aa..73456bc 100644
--- a/src/type/array_type.h
+++ b/src/type/array_type.h
@@ -40,24 +40,9 @@
/// 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 30bf850..062addd 100644
--- a/src/type/array_type_test.cc
+++ b/src/type/array_type_test.cc
@@ -94,38 +94,6 @@
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<ast::StrideDecoration>(4)});
- EXPECT_EQ(16u, arr.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
-}
-
-TEST_F(ArrayTest, MinBufferBindingSizeRuntimeArray) {
- U32 u32;
- Array arr(&u32, 0, ast::DecorationList{create<ast::StrideDecoration>(4)});
- EXPECT_EQ(4u, arr.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
-}
-
-TEST_F(ArrayTest, BaseAlignmentArray) {
- U32 u32;
- Array arr(&u32, 4, ast::DecorationList{create<ast::StrideDecoration>(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<ast::StrideDecoration>(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 3a9fd2c..7751c53 100644
--- a/src/type/bool_type_test.cc
+++ b/src/type/bool_type_test.cc
@@ -50,11 +50,6 @@
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 ebb20da..62cb05b 100644
--- a/src/type/depth_texture_type_test.cc
+++ b/src/type/depth_texture_type_test.cc
@@ -67,11 +67,6 @@
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 8bd02ab..5e7e2a5 100644
--- a/src/type/f32_type.cc
+++ b/src/type/f32_type.cc
@@ -35,14 +35,6 @@
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<F32>();
}
diff --git a/src/type/f32_type.h b/src/type/f32_type.h
index 50e990d..156ddd1 100644
--- a/src/type/f32_type.h
+++ b/src/type/f32_type.h
@@ -39,16 +39,6 @@
/// 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 d02d1b9..2985597 100644
--- a/src/type/f32_type_test.cc
+++ b/src/type/f32_type_test.cc
@@ -50,16 +50,6 @@
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 78a7a7d..7807385 100644
--- a/src/type/i32_type.cc
+++ b/src/type/i32_type.cc
@@ -35,14 +35,6 @@
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<I32>();
}
diff --git a/src/type/i32_type.h b/src/type/i32_type.h
index e688a0c..a376974 100644
--- a/src/type/i32_type.h
+++ b/src/type/i32_type.h
@@ -39,16 +39,6 @@
/// 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 788ea58..38aca92 100644
--- a/src/type/i32_type_test.cc
+++ b/src/type/i32_type_test.cc
@@ -50,16 +50,6 @@
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 d98c669..02e225b 100644
--- a/src/type/matrix_type.cc
+++ b/src/type/matrix_type.cc
@@ -45,18 +45,6 @@
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 f76d578..59c878c 100644
--- a/src/type/matrix_type.h
+++ b/src/type/matrix_type.h
@@ -49,16 +49,6 @@
/// 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 73a7d0f..09dc8f0 100644
--- a/src/type/matrix_type_test.cc
+++ b/src/type/matrix_type_test.cc
@@ -60,62 +60,6 @@
EXPECT_EQ(m.FriendlyName(Symbols()), "mat2x3<i32>");
}
-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 caeb3fa..d0f025e 100644
--- a/src/type/multisampled_texture_type_test.cc
+++ b/src/type/multisampled_texture_type_test.cc
@@ -78,12 +78,6 @@
EXPECT_EQ(s.FriendlyName(Symbols()), "texture_multisampled_3d<f32>");
}
-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 5034fb8..2587c2e 100644
--- a/src/type/sampled_texture_type_test.cc
+++ b/src/type/sampled_texture_type_test.cc
@@ -76,12 +76,6 @@
EXPECT_EQ(s.FriendlyName(Symbols()), "texture_3d<f32>");
}
-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 1912857..a3ea06d 100644
--- a/src/type/sampler_type_test.cc
+++ b/src/type/sampler_type_test.cc
@@ -71,11 +71,6 @@
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 d7af801..976ea72 100644
--- a/src/type/storage_texture_type_test.cc
+++ b/src/type/storage_texture_type_test.cc
@@ -132,13 +132,6 @@
EXPECT_TRUE(s->As<StorageTexture>()->type()->Is<I32>());
}
-TEST_F(StorageTextureTest, MinBufferBindingSize) {
- auto* subtype = StorageTexture::SubtypeFor(ImageFormat::kRgba32Sint, Types());
- auto* s = create<StorageTexture>(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 c1bf774..37e7885 100644
--- a/src/type/struct_type.cc
+++ b/src/type/struct_type.cc
@@ -38,48 +38,6 @@
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<float>(last_member->offset() + size);
- float alignment = static_cast<float>(BaseAlignment(mem_layout));
-
- return static_cast<uint64_t>(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<uint64_t>(16 *
- std::ceil(static_cast<float>(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 bec6782..9a94877 100644
--- a/src/type/struct_type.h
+++ b/src/type/struct_type.h
@@ -51,16 +51,6 @@
/// 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 ec562bc..4099ed1 100644
--- a/src/type/struct_type_test.cc
+++ b/src/type/struct_type_test.cc
@@ -64,140 +64,6 @@
EXPECT_EQ(s->FriendlyName(Symbols()), "my_struct");
}
-TEST_F(StructTypeTest, MinBufferBindingSize) {
- auto* str = create<ast::Struct>(
- 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<ast::StrideDecoration>(4)});
-
- auto* str = create<ast::Struct>(
- 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<ast::StrideDecoration>(4)});
-
- auto* str = create<ast::Struct>(
- 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::Struct>(
- ast::StructMemberList{Member("foo", ty.vec2<u32>(), {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::Struct>(
- ast::StructMemberList{Member("foo", ty.vec3<u32>(), {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::Struct>(
- ast::StructMemberList{Member("foo", ty.vec4<u32>(), {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::Struct>(
- 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<ast::StrideDecoration>(4)});
- auto* str = create<ast::Struct>(
- 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<ast::StrideDecoration>(4)});
- auto* str = create<ast::Struct>(
- 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::Struct>(
- ast::StructMemberList{Member("foo", ty.vec2<u32>(), {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::Struct>(
- ast::StructMemberList{Member("foo", ty.vec3<u32>(), {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::Struct>(
- ast::StructMemberList{Member("foo", ty.vec4<u32>(), {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 2f6e0dc..39f309c 100644
--- a/src/type/type.cc
+++ b/src/type/type.cc
@@ -70,14 +70,6 @@
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<Bool>();
}
diff --git a/src/type/type.h b/src/type/type.h
index c6ef50e..c4e7a36 100644
--- a/src/type/type.h
+++ b/src/type/type.h
@@ -45,16 +45,6 @@
/// 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 c2f029c..775abc3 100644
--- a/src/type/u32_type.cc
+++ b/src/type/u32_type.cc
@@ -35,14 +35,6 @@
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<U32>();
}
diff --git a/src/type/u32_type.h b/src/type/u32_type.h
index 7e92a3a..7aadc89 100644
--- a/src/type/u32_type.h
+++ b/src/type/u32_type.h
@@ -39,16 +39,6 @@
/// 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 5b6db5a..c320cb5 100644
--- a/src/type/u32_type_test.cc
+++ b/src/type/u32_type_test.cc
@@ -50,16 +50,6 @@
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 3a8078f..ab12790 100644
--- a/src/type/vector_type.cc
+++ b/src/type/vector_type.cc
@@ -40,20 +40,6 @@
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 88e41f2..cbb9022 100644
--- a/src/type/vector_type.h
+++ b/src/type/vector_type.h
@@ -46,16 +46,6 @@
/// 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 8f13b68..8c4a793 100644
--- a/src/type/vector_type_test.cc
+++ b/src/type/vector_type_test.cc
@@ -59,42 +59,6 @@
EXPECT_EQ(v->FriendlyName(Symbols()), "vec3<f32>");
}
-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 491bed4..cc39b22 100644
--- a/src/validator/validator_test.cc
+++ b/src/validator/validator_test.cc
@@ -872,14 +872,6 @@
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<int>();
@@ -888,14 +880,6 @@
EXPECT_TRUE(v.IsStorable(arr));
}
-TEST_F(ValidatorTest, IsStorable_ArrayUnsizedOfNonStorable) {
- auto* arr = ty.array<void>();
-
- 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<ast::Struct>(Source{}, members, ast::DecorationList{});
@@ -906,16 +890,5 @@
EXPECT_TRUE(v.IsStorable(s_ty));
}
-TEST_F(ValidatorTest, IsStorable_Struct_SomeMembersNonStorable) {
- auto* ptr_ty = ty.pointer<int>(ast::StorageClass::kPrivate);
- ast::StructMemberList members{Member("a", ty.i32()), Member("b", ptr_ty)};
- auto* s = create<ast::Struct>(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 cf5d62a..47fda58 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 @@
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<type::Vector>()) {
auto swizzle = builder_.Sem().Get(mem)->Swizzle();
@@ -2035,9 +2039,9 @@
// 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 @@
// 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 @@
out << "(";
if (auto* arr = ary_type->As<type::Array>()) {
- 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<type::Vector>()) {
// 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 a11551d..ca1007b 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_Struct) {
auto* str = create<ast::Struct>(
- 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 c985858..e6ad1d5 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 @@
TEST_F(HlslGeneratorImplTest_Function,
Emit_Decoration_EntryPoint_With_UniformStruct) {
- auto* str = create<ast::Struct>(
- ast::StructMemberList{Member("coord", ty.vec4<f32>())},
- ast::DecorationList{});
-
- auto* s = ty.struct_("Uniforms", str);
+ auto* s = Structure("Uniforms", {Member("coord", ty.vec4<f32>())});
Global("uniforms", s, ast::StorageClass::kUniform, nullptr,
ast::DecorationList{
@@ -276,8 +275,6 @@
create<ast::GroupDecoration>(1),
});
- AST().AddConstructedType(s);
-
auto* var = Var("v", ty.f32(), ast::StorageClass::kFunction,
create<ast::MemberAccessorExpression>(
MemberAccessor("uniforms", "coord"), Expr("x")));
@@ -310,12 +307,11 @@
TEST_F(HlslGeneratorImplTest_Function,
Emit_Decoration_EntryPoint_With_RW_StorageBuffer_Read) {
- auto* str = create<ast::Struct>(
- 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 @@
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::Struct>(
- 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 @@
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::Struct>(
- 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 @@
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::Struct>(
- 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 @@
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 @@
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 @@
void frag_main() {
float v = sub_func(1.0f);
return;
-}
-
-)");
+})"));
}
TEST_F(HlslGeneratorImplTest_Function,
@@ -857,7 +840,7 @@
TEST_F(HlslGeneratorImplTest_Function,
Emit_Multiple_EntryPoint_With_Same_ModuleVar) {
// [[block]] struct Data {
- // [[offset(0)]] d : f32;
+ // d : f32;
// };
// [[binding(0), group(0)]] var<storage> data : Data;
//
@@ -871,12 +854,9 @@
// return;
// }
- auto* str = create<ast::Struct>(
- ast::StructMemberList{Member("d", ty.f32(), {MemberOffset(0)})},
- ast::DecorationList{create<ast::StructBlockDecoration>()});
-
- auto* s = ty.struct_("Data", str);
- AST().AddConstructedType(s);
+ auto* s =
+ Structure("Data", {Member("d", ty.f32())},
+ ast::DecorationList{create<ast::StructBlockDecoration>()});
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 ffd588c..c8627d1 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 @@
using HlslGeneratorImplTest_MemberAccessor = TestHelper;
TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor) {
- auto* strct = create<ast::Struct>(
- 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_StorageBuffer_Load) {
// struct Data {
- // [[offset(0)]] a : i32;
- // [[offset(4)]] b : f32;
+ // a : i32;
+ // b : f32;
// };
// var<storage> data : Data;
// data.b;
//
// -> asfloat(data.Load(4));
- auto* str = create<ast::Struct>(
- 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,
EmitExpression_MemberAccessor_StorageBuffer_Load_Int) {
// struct Data {
- // [[offset(0)]] a : i32;
- // [[offset(4)]] b : f32;
+ // a : i32;
+ // b : f32;
// };
// var<storage> data : Data;
// data.a;
//
// -> asint(data.Load(0));
- auto* str = create<ast::Struct>(
- 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,
EmitExpression_MemberAccessor_StorageBuffer_Store_Matrix) {
// struct Data {
- // [[offset(0)]] z : f32;
- // [[offset(4)]] a : mat2x3<f32>;
+ // z : f32;
+ // a : mat2x3<f32>;
// };
// var<storage> data : Data;
// mat2x3<f32> b;
@@ -113,12 +108,9 @@
// data.Store3(4 + 0, asuint(_tint_tmp[0]));
// data.Store3(4 + 16, asuint(_tint_tmp[1]));
- auto* str = create<ast::Struct>(
- ast::StructMemberList{Member("z", ty.i32(), {MemberOffset(0)}),
- Member("a", ty.mat2x3<f32>(), {MemberOffset(4)})},
- ast::DecorationList{});
+ auto* s =
+ Structure("Data", {Member("z", ty.i32()), Member("a", ty.mat2x3<f32>())});
- auto* s = ty.struct_("Data", str);
auto* b_var = Global("b", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
@@ -135,31 +127,28 @@
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<f32>;
+ // z : f32;
+ // a : mat2x3<f32>;
// };
// var<storage> data : Data;
// data.a = mat2x3<f32>();
//
// -> 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::Struct>(
- ast::StructMemberList{Member("z", ty.i32(), {MemberOffset(0)}),
- Member("a", ty.mat2x3<f32>(), {MemberOffset(4)})},
- ast::DecorationList{});
+ auto* s =
+ Structure("Data", {Member("z", ty.i32()), Member("a", ty.mat2x3<f32>())});
- auto* s = ty.struct_("Data", str);
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
auto* lhs = MemberAccessor("data", "a");
@@ -176,16 +165,16 @@
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<f32>;
+ // z : f32;
+ // a : mat3x2<f32>;
// };
// var<storage> data : Data;
// data.a;
@@ -193,12 +182,9 @@
// -> asfloat(uint2x3(data.Load2(4 + 0), data.Load2(4 + 8),
// data.Load2(4 + 16)));
- auto* str = create<ast::Struct>(
- ast::StructMemberList{Member("z", ty.i32(), {MemberOffset(0)}),
- Member("a", ty.mat3x2<f32>(), {MemberOffset(4)})},
- ast::DecorationList{});
+ auto* s =
+ Structure("Data", {Member("z", ty.i32()), Member("a", ty.mat3x2<f32>())});
- auto* s = ty.struct_("Data", str);
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
auto* expr = MemberAccessor("data", "a");
@@ -210,33 +196,26 @@
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<f32;
- // };
- // struct Outer {
- // [[offset(0)]] c : f32;
- // [[offset(4)]] b : Data;
+ // z : f32;
+ // a : mat2x3<f32>
// };
// var<storage> 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::Struct>(
- ast::StructMemberList{
- Member("z", ty.i32(), {MemberOffset(0)}),
- Member("a", ty.mat2x3<f32>(), {MemberOffset(4)}),
- },
- ast::DecorationList{});
+ auto* s = Structure("Data", {
+ Member("z", ty.i32()),
+ Member("a", ty.mat2x3<f32>()),
+ });
- auto* s = ty.struct_("Data", str);
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
auto* expr = MemberAccessor("data", "a");
@@ -248,14 +227,14 @@
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<f32;
+ // a : mat3x3<f32>
// };
// var<storage> data : Data;
// data.a;
@@ -263,11 +242,10 @@
// -> asfloat(uint3x3(data.Load3(0), data.Load3(16),
// data.Load3(32)));
- auto* str = create<ast::Struct>(
- ast::StructMemberList{Member("a", ty.mat3x3<f32>(), {MemberOffset(0)})},
- ast::DecorationList{});
+ auto* s = Structure("Data", {
+ Member("a", ty.mat3x3<f32>()),
+ });
- 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(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load_Matrix_Single_Element) {
// struct Data {
- // [[offset(0)]] z : f32;
- // [[offset(16)]] a : mat4x3<f32>;
+ // z : f32;
+ // a : mat4x3<f32>;
// };
// var<storage> data : Data;
// data.a[2][1];
//
// -> asfloat(data.Load((2 * 16) + (1 * 4) + 16)))
- auto* str = create<ast::Struct>(
- ast::StructMemberList{Member("z", ty.i32(), {MemberOffset(0)}),
- Member("a", ty.mat4x3<f32>(), {MemberOffset(16)})},
- ast::DecorationList{});
+ auto* s = Structure("Data", {
+ Member("z", ty.i32()),
+ Member("a", ty.mat4x3<f32>()),
+ });
- 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,
EmitExpression_ArrayAccessor_StorageBuffer_Load_Int_FromArray) {
// struct Data {
- // [[offset(0)]] a : [[stride(4)]] array<i32, 5>;
+ // a : [[stride(4)]] array<i32, 5>;
// };
// var<storage> data : Data;
// data.a[2];
@@ -328,10 +305,8 @@
create<ast::StrideDecoration>(4),
});
- auto* str = create<ast::Struct>(
- 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,
EmitExpression_ArrayAccessor_StorageBuffer_Load_Int_FromArray_ExprIdx) {
// struct Data {
- // [[offset(0)]] a : [[stride(4)]] array<i32, 5>;
+ // a : [[stride(4)]] array<i32, 5>;
// };
// var<storage> data : Data;
// data.a[(2 + 4) - 3];
@@ -359,10 +334,8 @@
create<ast::StrideDecoration>(4),
});
- auto* str = create<ast::Struct>(
- 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,
EmitExpression_MemberAccessor_StorageBuffer_Store) {
// struct Data {
- // [[offset(0)]] a : i32;
- // [[offset(4)]] b : f32;
+ // a : i32;
+ // b : f32;
// };
// var<storage> data : Data;
// data.b = 2.3f;
//
// -> data.Store(0, asuint(2.0f));
- auto* str = create<ast::Struct>(
- 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,
EmitExpression_MemberAccessor_StorageBuffer_Store_ToArray) {
// struct Data {
- // [[offset(0)]] a : [[stride(4)]] array<i32, 5>;
+ // a : [[stride(4)]] array<i32, 5>;
// };
// var<storage> data : Data;
// data.a[2] = 2;
@@ -425,11 +397,8 @@
create<ast::StrideDecoration>(4),
});
- auto* str = create<ast::Struct>(
- 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,
EmitExpression_MemberAccessor_StorageBuffer_Store_Int) {
// struct Data {
- // [[offset(0)]] a : i32;
- // [[offset(4)]] b : f32;
+ // a : i32;
+ // b : f32;
// };
// var<storage> data : Data;
// data.a = 2;
//
// -> data.Store(0, asuint(2));
- auto* str = create<ast::Struct>(
- 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,
EmitExpression_MemberAccessor_StorageBuffer_Load_Vec3) {
// struct Data {
- // [[offset(0)]] a : vec3<i32>;
- // [[offset(16)]] b : vec3<f32>;
+ // a : vec3<i32>;
+ // b : vec3<f32>;
// };
// var<storage> data : Data;
// data.b;
//
// -> asfloat(data.Load(16));
- auto* str = create<ast::Struct>(
- ast::StructMemberList{Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
- Member("b", ty.vec3<f32>(), {MemberOffset(16)})},
- ast::DecorationList{});
+ auto* s = Structure("Data", {
+ Member("a", ty.vec3<i32>()),
+ Member("b", ty.vec3<f32>()),
+ });
- 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,
EmitExpression_MemberAccessor_StorageBuffer_Store_Vec3) {
// struct Data {
- // [[offset(0)]] a : vec3<i32>;
- // [[offset(16)]] b : vec3<f32>;
+ // a : vec3<i32>;
+ // b : vec3<f32>;
// };
// var<storage> data : Data;
// data.b = vec3<f32>(2.3f, 1.2f, 0.2f);
//
// -> data.Store(16, asuint(float3(2.3f, 1.2f, 0.2f)));
- auto* str = create<ast::Struct>(
- ast::StructMemberList{Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
- Member("b", ty.vec3<f32>(), {MemberOffset(16)})},
- ast::DecorationList{});
+ auto* s = Structure("Data", {
+ Member("a", ty.vec3<i32>()),
+ Member("b", ty.vec3<f32>()),
+ });
- 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,
EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel) {
// struct Data {
- // [[offset(0)]] a : vec3<i32>;
- // [[offset(16)]] b : vec3<f32>;
+ // a : vec3<i32>;
+ // b : vec3<f32>;
// };
// struct Pre {
// var c : [[stride(32)]] array<Data, 4>;
@@ -560,24 +526,18 @@
//
// -> asfloat(data.Load3(16 + (2 * 32)))
- auto* data_str = create<ast::Struct>(
- ast::StructMemberList{
- Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
- Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
- },
- ast::DecorationList{});
+ auto* data = Structure("Data", {
+ Member("a", ty.vec3<i32>()),
+ Member("b", ty.vec3<f32>()),
+ });
- auto* data = ty.struct_("Data", data_str);
type::Array ary(data, 4,
ast::DecorationList{
create<ast::StrideDecoration>(32),
});
- auto* pre_str = create<ast::Struct>(
- 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,
EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel_Swizzle) {
// struct Data {
- // [[offset(0)]] a : vec3<i32>;
- // [[offset(16)]] b : vec3<f32>;
+ // a : vec3<i32>;
+ // b : vec3<f32>;
// };
// struct Pre {
// var c : [[stride(32)]] array<Data, 4>;
@@ -607,22 +567,16 @@
//
// -> asfloat(data.Load3(16 + (2 * 32))).xy
- auto* data_str = create<ast::Struct>(
- ast::StructMemberList{
- Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
- Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
- },
- ast::DecorationList{});
+ auto* data = Structure("Data", {
+ Member("a", ty.vec3<i32>()),
+ Member("b", ty.vec3<f32>()),
+ });
- auto* data = ty.struct_("Data", data_str);
type::Array ary(data, 4,
ast::DecorationList{create<ast::StrideDecoration>(32)});
- auto* pre_str = create<ast::Struct>(
- 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 @@
HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel_Swizzle_SingleLetter) { // NOLINT
// struct Data {
- // [[offset(0)]] a : vec3<i32>;
- // [[offset(16)]] b : vec3<f32>;
+ // a : vec3<i32>;
+ // b : vec3<f32>;
// };
// struct Pre {
// var c : [[stride(32)]] array<Data, 4>;
@@ -654,24 +608,18 @@
//
// -> asfloat(data.Load((4 * 1) + 16 + (2 * 32) + 0))
- auto* data_str = create<ast::Struct>(
- ast::StructMemberList{
- Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
- Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
- },
- ast::DecorationList{});
+ auto* data = Structure("Data", {
+ Member("a", ty.vec3<i32>()),
+ Member("b", ty.vec3<f32>()),
+ });
- auto* data = ty.struct_("Data", data_str);
type::Array ary(data, 4,
ast::DecorationList{
create<ast::StrideDecoration>(32),
});
- auto* pre_str = create<ast::Struct>(
- 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(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel_Index) {
// struct Data {
- // [[offset(0)]] a : vec3<i32>;
- // [[offset(16)]] b : vec3<f32>;
+ // a : vec3<i32>;
+ // b : vec3<f32>;
// };
// struct Pre {
// var c : [[stride(32)]] array<Data, 4>;
@@ -702,24 +650,18 @@
//
// -> asfloat(data.Load(4 + 16 + (2 * 32)))
- auto* data_str = create<ast::Struct>(
- ast::StructMemberList{
- Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
- Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
- },
- ast::DecorationList{});
+ auto* data = Structure("Data", {
+ Member("a", ty.vec3<i32>()),
+ Member("b", ty.vec3<f32>()),
+ });
- auto* data = ty.struct_("Data", data_str);
type::Array ary(data, 4,
ast::DecorationList{
create<ast::StrideDecoration>(32),
});
- auto* pre_str = create<ast::Struct>(
- 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,
EmitExpression_MemberAccessor_StorageBuffer_Store_MultiLevel) {
// struct Data {
- // [[offset(0)]] a : vec3<i32>;
- // [[offset(16)]] b : vec3<f32>;
+ // a : vec3<i32>;
+ // b : vec3<f32>;
// };
// struct Pre {
// var c : [[stride(32)]] array<Data, 4>;
@@ -750,24 +692,18 @@
//
// -> data.Store3(16 + (2 * 32), asuint(float3(1.0f, 2.0f, 3.0f)));
- auto* data_str = create<ast::Struct>(
- ast::StructMemberList{
- Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
- Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
- },
- ast::DecorationList{});
+ auto* data = Structure("Data", {
+ Member("a", ty.vec3<i32>()),
+ Member("b", ty.vec3<f32>()),
+ });
- auto* data = ty.struct_("Data", data_str);
type::Array ary(data, 4,
ast::DecorationList{
create<ast::StrideDecoration>(32),
});
- auto* pre_str = create<ast::Struct>(
- 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,
EmitExpression_MemberAccessor_StorageBuffer_Store_Swizzle_SingleLetter) {
// struct Data {
- // [[offset(0)]] a : vec3<i32>;
- // [[offset(16)]] b : vec3<f32>;
+ // a : vec3<i32>;
+ // b : vec3<f32>;
// };
// struct Pre {
// var c : [[stride(32)]] array<Data, 4>;
@@ -803,24 +739,18 @@
//
// -> data.Store((4 * 1) + 16 + (2 * 32) + 0, asuint(1.0f));
- auto* data_str = create<ast::Struct>(
- ast::StructMemberList{
- Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
- Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
- },
- ast::DecorationList{});
+ auto* data = Structure("Data", {
+ Member("a", ty.vec3<i32>()),
+ Member("b", ty.vec3<f32>()),
+ });
- auto* data = ty.struct_("Data", data_str);
type::Array ary(data, 4,
ast::DecorationList{
create<ast::StrideDecoration>(32),
});
- auto* pre_str = create<ast::Struct>(
- 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 23a9ef7..5353b5c 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, EmitType_StructDecl) {
- auto* str = create<ast::Struct>(
- 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_Struct) {
- auto* str = create<ast::Struct>(
- 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 @@
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::Struct>(
- 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::Struct>(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 @@
// TODO(dsinclair): How to translate [[block]]
TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Struct_WithDecoration) {
- ast::DecorationList decos;
- decos.push_back(create<ast::StructBlockDecoration>());
-
- auto* str = create<ast::Struct>(
- 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<ast::StructBlockDecoration>()});
GeneratorImpl& gen = Build();
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index 00f9ffb..29b1e51 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 @@
stmts->last()->Is<ast::FallthroughStatement>();
}
-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 @@
return true;
}
-uint32_t GeneratorImpl::calculate_largest_alignment(type::Struct* type) {
- auto* stct = type->As<type::Struct>()->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<type::Struct>()) {
- largest_alignment = std::max(largest_alignment, align);
- } else {
- largest_alignment = std::max(
- largest_alignment,
- calculate_largest_alignment(mem->type()->As<type::Struct>()));
- }
- }
- return largest_alignment;
-}
-
-uint32_t GeneratorImpl::calculate_alignment_size(type::Type* type) {
- if (auto* alias = type->As<type::Alias>()) {
- return calculate_alignment_size(alias->type());
- }
- if (auto* ary = type->As<type::Array>()) {
- // 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<type::Bool>()) {
- return 1;
- }
- if (type->Is<type::Pointer>()) {
- return 0;
- }
- if (type->Is<type::F32>() || type->Is<type::I32>() || type->Is<type::U32>()) {
- return 4;
- }
- if (auto* mat = type->As<type::Matrix>()) {
- // 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<type::Struct>()) {
- 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<ast::StructMemberOffsetDecoration>()) {
- count = offset->offset();
- }
- }
- auto align = calculate_alignment_size(mem->type());
- if (align == 0) {
- return 0;
- }
- if (auto* str = mem->type()->As<type::Struct>()) {
- 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<type::Vector>()) {
- 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 @@
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<ast::StructMemberOffsetDecoration>()) {
- 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<ast::LocationDecoration>()) {
+ if (auto* loc = deco->As<ast::LocationDecoration>()) {
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<type::Array>()) {
+ if (!ty->Is<type::Array>()) {
out_ << " " << program_->Symbols().NameFor(mem->symbol());
}
out_ << attributes;
out_ << ";" << std::endl;
+
+ if (ty->is_scalar()) {
+ current_offset = offset + 4;
+ } else if (ty->Is<type::Struct>()) {
+ /// 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 07c90e9..b5989b0 100644
--- a/src/writer/msl/generator_impl.h
+++ b/src/writer/msl/generator_impl.h
@@ -60,16 +60,6 @@
/// @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 d663ba9..b5ec3df 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_Struct) {
- auto* str = create<ast::Struct>(
- 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_AliasStructIdent) {
- auto* str = create<ast::Struct>(
- 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 543d0a8..8f4de92 100644
--- a/src/writer/msl/generator_impl_function_test.cc
+++ b/src/writer/msl/generator_impl_function_test.cc
@@ -243,17 +243,15 @@
)");
}
-TEST_F(MslGeneratorImplTest, Emit_Decoration_EntryPoint_With_RW_StorageBuffer) {
- auto* str = create<ast::Struct>(
- 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<ast::BindingDecoration>(0),
create<ast::GroupDecoration>(1)});
@@ -289,15 +287,14 @@
)");
}
-TEST_F(MslGeneratorImplTest, Emit_Decoration_EntryPoint_With_RO_StorageBuffer) {
- auto* str = create<ast::Struct>(
- 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<ast::BindingDecoration>(0),
@@ -555,15 +552,13 @@
}
TEST_F(MslGeneratorImplTest,
- Emit_Decoration_Called_By_EntryPoint_With_RW_StorageBuffer) {
- auto* str = create<ast::Struct>(
- 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<ast::BindingDecoration>(0),
@@ -613,15 +608,13 @@
}
TEST_F(MslGeneratorImplTest,
- Emit_Decoration_Called_By_EntryPoint_With_RO_StorageBuffer) {
- auto* str = create<ast::Struct>(
- 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<ast::BindingDecoration>(0),
@@ -746,7 +739,7 @@
TEST_F(MslGeneratorImplTest,
Emit_Function_Multiple_EntryPoint_With_Same_ModuleVar) {
// [[block]] struct Data {
- // [[offset(0)]] d : f32;
+ // d : f32;
// };
// [[binding(0), group(0)]] var<storage> data : Data;
//
@@ -760,21 +753,15 @@
// return;
// }
- ast::DecorationList s_decos;
- s_decos.push_back(create<ast::StructBlockDecoration>());
+ auto* s = Structure("Data", {Member("d", ty.f32())},
+ {create<ast::StructBlockDecoration>()});
- auto* str = create<ast::Struct>(
- 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<ast::BindingDecoration>(0),
create<ast::GroupDecoration>(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 9c6dfad..1fa7776 100644
--- a/src/writer/msl/generator_impl_test.cc
+++ b/src/writer/msl/generator_impl_test.cc
@@ -78,178 +78,6 @@
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<f32, 4>();
-
- 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<f32>();
-
- 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::Struct>(
- 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::Struct>(
- ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}),
- Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
- Member("c", ty.f32(), {MemberOffset(32)})},
- ast::DecorationList{});
-
- auto* inner_s = ty.struct_("Inner", inner_str);
-
- auto* outer_str = create<ast::Struct>(
- 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<MslVectorSizeData>;
-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<MslVectorSizeData>;
-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<MslVectorSizeData>;
-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<MslVectorSizeData>;
-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 1e99815..ebd0f72 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, EmitType_Struct) {
- auto* str = create<ast::Struct>(
- 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_StructDecl) {
- auto* str = create<ast::Struct>(
- 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 @@
)");
}
+/// TODO(bclayton): Add tests for vector, matrix, array and nested structures.
TEST_F(MslGeneratorImplTest, EmitType_Struct_InjectPadding) {
- auto* str = create<ast::Struct>(
- 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<ast::StructBlockDecoration>());
- auto* str = create<ast::Struct>(
- 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<ast::StructBlockDecoration>()});
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 095aa9d..e67fcdb 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_Struct) {
- auto* str = create<ast::Struct>(
- 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<ast::VariableDeclStatement>(var);
WrapInFunction(stmt);
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 4a066fc..71054c8 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 @@
{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 @@
{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<ast::StructMemberOffsetDecoration>()) {
- 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<type::F32>()) {
- 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<type::F32>()) {
+ 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 e69f08d..78ec1ad 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_MultiLevel) {
- type::Array ary4(ty.vec3<f32>(), 4, ast::DecorationList{});
+ auto* ary4 = ty.array(ty.vec3<f32>(), 4);
// ary = array<vec3<f32>, 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, Accessor_ArrayWithSwizzle) {
- type::Array ary4(ty.vec3<f32>(), 4, ast::DecorationList{});
+ auto* ary4 = ty.array(ty.vec3<f32>(), 4);
// var a : array<vec3<f32>, 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 @@
// var ident : my_struct
// ident.b
- auto* s = create<ast::Struct>(
- 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 @@
//
// var ident : my_struct
// ident.inner.a
- auto* inner_struct = ty.struct_(
- "Inner", create<ast::Struct>(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::Struct>(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 @@
//
// var ident : my_struct
// ident.inner.a
- auto* inner_struct = ty.struct_(
- "Inner", create<ast::Struct>(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::Struct>(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 @@
//
// var ident : my_struct
// ident.inner.a = 2.0f;
- auto* inner_struct = ty.struct_(
- "Inner", create<ast::Struct>(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::Struct>(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<ast::AssignmentStatement>(
@@ -412,15 +403,12 @@
// var ident : my_struct
// var store : f32 = ident.inner.a
- auto* inner_struct = ty.struct_(
- "Inner", create<ast::Struct>(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::Struct>(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 @@
// var index : array<A, 2>
// index[0].foo[2].bar.baz.yx
- auto* s =
- create<ast::Struct>(ast::StructMemberList{Member("baz", ty.vec3<f32>())},
- ast::DecorationList{});
- auto* c_type = ty.struct_("C", s);
+ auto* c_type = Structure("C", {Member("baz", ty.vec3<f32>())});
- s = create<ast::Struct>(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::Struct>(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 @@
// vec2<f32>(0.5, -0.5));
// pos[1]
- type::Array arr(ty.vec2<f32>(), 3, ast::DecorationList{});
+ auto* arr = ty.array(ty.vec2<f32>(), 3);
auto* var =
- GlobalConst("pos", &arr,
- Construct(&arr, vec2<f32>(0.0f, 0.5f),
- vec2<f32>(-0.5f, -0.5f), vec2<f32>(0.5f, -0.5f)));
+ GlobalConst("pos", arr,
+ Construct(arr, vec2<f32>(0.0f, 0.5f), vec2<f32>(-0.5f, -0.5f),
+ vec2<f32>(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 45d3769..c3dcf99 100644
--- a/src/writer/spirv/builder_assign_test.cc
+++ b/src/writer/spirv/builder_assign_test.cc
@@ -176,12 +176,12 @@
// var ident : my_struct
// ident.b = 4.0;
- auto* s = create<ast::Struct>(
- 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<ast::AssignmentStatement>(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 4525ba4..6a11cb0 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_Struct) {
- auto* s = create<ast::Struct>(
- ast::StructMemberList{
- Member("a", ty.f32()),
- Member("b", ty.vec3<f32>()),
- },
- ast::DecorationList{});
- auto* s_type = ty.struct_("my_struct", s);
+ auto* s = Structure("my_struct", {
+ Member("a", ty.f32()),
+ Member("b", ty.vec3<f32>()),
+ });
- auto* t = Construct(s_type, 2.0f, vec3<f32>(2.0f, 2.0f, 2.0f));
+ auto* t = Construct(s, 2.0f, vec3<f32>(2.0f, 2.0f, 2.0f));
WrapInFunction(t);
spirv::Builder& b = Build();
@@ -1127,13 +1124,8 @@
}
TEST_F(SpvBuilderConstructorTest, Type_ZeroInit_Struct) {
- auto* s = create<ast::Struct>(
- 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_Struct) {
- auto* s = create<ast::Struct>(
- ast::StructMemberList{
- Member("a", ty.f32()),
- Member("b", ty.vec3<f32>()),
- },
- ast::DecorationList{});
- auto* s_type = ty.struct_("my_struct", s);
- auto* t = Construct(s_type, 2.f, vec3<f32>(2.f, 2.f, 2.f));
+ auto* s = Structure("my_struct", {
+ Member("a", ty.f32()),
+ Member("b", ty.vec3<f32>()),
+ });
+
+ auto* t = Construct(s, 2.f, vec3<f32>(2.f, 2.f, 2.f));
WrapInFunction(t);
spirv::Builder& b = Build();
@@ -1582,15 +1572,12 @@
TEST_F(SpvBuilderConstructorTest,
IsConstructorConst_Struct_WithIdentSubExpression) {
- auto* s = create<ast::Struct>(
- ast::StructMemberList{
- Member("a", ty.f32()),
- Member("b", ty.vec3<f32>()),
- },
- ast::DecorationList{});
+ auto* s = Structure("my_struct", {
+ Member("a", ty.f32()),
+ Member("b", ty.vec3<f32>()),
+ });
- 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 feecfac..392a05e 100644
--- a/src/writer/spirv/builder_function_test.cc
+++ b/src/writer/spirv/builder_function_test.cc
@@ -192,7 +192,7 @@
// 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<storage> data : Data;
//
@@ -206,13 +206,9 @@
// return;
// }
- ast::DecorationList s_decos;
- s_decos.push_back(create<ast::StructBlockDecoration>());
+ auto* s = Structure("Data", {Member("d", ty.f32())},
+ {create<ast::StructBlockDecoration>()});
- auto* str = create<ast::Struct>(
- 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 @@
create<ast::GroupDecoration>(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 6a37928..bc782e1 100644
--- a/src/writer/spirv/builder_global_variable_test.cc
+++ b/src/writer/spirv/builder_global_variable_test.cc
@@ -383,10 +383,10 @@
// };
// var b : [[access(read)]] A
- auto* A = ty.struct_(
- "A", create<ast::Struct>(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<type::AccessControl>(ast::AccessControl::kReadOnly, A);
auto* var = Global("b", ac, ast::StorageClass::kStorage);
@@ -395,7 +395,9 @@
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 @@
// type B = A;
// var b : [[access(read)]] B
- auto* A = ty.struct_(
- "A", create<ast::Struct>(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<type::AccessControl>(ast::AccessControl::kReadOnly, B);
auto* var = Global("b", ac, ast::StorageClass::kStorage);
@@ -428,7 +428,8 @@
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 @@
// type B = [[access(read)]] A;
// var b : B
- auto* A = ty.struct_(
- "A", create<ast::Struct>(ast::StructMemberList{Member("a", ty.i32())},
- ast::DecorationList{}));
+ auto* A = Structure("A", {Member("a", ty.i32())});
auto* ac = create<type::AccessControl>(ast::AccessControl::kReadOnly, A);
auto* B = ty.alias("B", ac);
auto* var = Global("b", B, ast::StorageClass::kStorage);
@@ -459,7 +458,8 @@
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 @@
// var b : [[access(read)]] A
// var c : [[access(read_write)]] A
- auto* A = ty.struct_(
- "A", create<ast::Struct>(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 @@
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 @@
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 3d1510d..4fe131e 100644
--- a/src/writer/spirv/builder_intrinsic_test.cc
+++ b/src/writer/spirv/builder_intrinsic_test.cc
@@ -1377,13 +1377,9 @@
}
TEST_F(IntrinsicBuilderTest, Call_ArrayLength) {
- auto* s = create<ast::Struct>(
- ast::StructMemberList{Member(0, "a", ty.array<f32>(4))},
- ast::DecorationList{
- create<ast::StructBlockDecoration>(),
- });
- 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<f32>(4))},
+ {create<ast::StructBlockDecoration>()});
+ Global("b", s, ast::StorageClass::kStorage, nullptr,
ast::DecorationList{
create<ast::BindingDecoration>(1),
create<ast::GroupDecoration>(2),
@@ -1426,15 +1422,14 @@
}
TEST_F(IntrinsicBuilderTest, Call_ArrayLength_OtherMembersInStruct) {
- auto* s = create<ast::Struct>(
- ast::StructMemberList{Member(0, "z", ty.f32()),
- Member(4, "a", ty.array<f32>(4))},
- ast::DecorationList{
- create<ast::StructBlockDecoration>(),
- });
+ auto* s = Structure("my_struct",
+ {
+ Member(0, "z", ty.f32()),
+ Member(4, "a", ty.array<f32>(4)),
+ },
+ {create<ast::StructBlockDecoration>()});
- 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<ast::BindingDecoration>(1),
create<ast::GroupDecoration>(2),
diff --git a/src/writer/spirv/builder_type_test.cc b/src/writer/spirv/builder_type_test.cc
index cb8a1d1..5e990a1 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, 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, 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, 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_WithStride) {
- type::Array ary(ty.i32(), 4,
- ast::DecorationList{
- create<ast::StrideDecoration>(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, 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, GenerateStruct_Empty) {
- auto* s = create<ast::Struct>(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) {
- auto* s = create<ast::Struct>(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 @@
}
TEST_F(BuilderTest_Type, GenerateStruct_Decorated) {
- ast::DecorationList struct_decos;
- struct_decos.push_back(create<ast::StructBlockDecoration>());
-
- auto* s = create<ast::Struct>(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<ast::StructBlockDecoration>()});
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 @@
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::Struct>(
- 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 @@
}
TEST_F(BuilderTest_Type, GenerateStruct_NonLayout_Matrix) {
- auto* s =
- create<ast::Struct>(ast::StructMemberList{Member("a", ty.mat2x2<f32>()),
- Member("b", ty.mat2x3<f32>()),
- Member("c", ty.mat4x4<f32>())},
- ast::DecorationList{});
- auto* s_type = ty.struct_("S", s);
+ auto* s = Structure("S", {
+ Member("a", ty.mat2x2<f32>()),
+ Member("b", ty.mat2x3<f32>()),
+ Member("c", ty.mat4x4<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);
@@ -389,21 +378,29 @@
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::Struct>(
- ast::StructMemberList{Member("a", ty.mat2x2<f32>(), {MemberOffset(0)}),
- Member("b", ty.mat2x3<f32>(), {MemberOffset(16)}),
- Member("c", ty.mat4x4<f32>(), {MemberOffset(48)})},
- ast::DecorationList{});
- auto* s_type = ty.struct_("S", s);
+ auto* s = Structure("S", {
+ Member("a", ty.mat2x2<f32>()),
+ Member("b", ty.mat2x3<f32>()),
+ Member("c", ty.mat4x4<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);
@@ -437,26 +434,19 @@
// 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<f32>(), 1,
- ast::DecorationList{}); // Singly nested array
+ auto* arr_mat2x2 = ty.array(ty.mat2x2<f32>(), 1); // Singly nested array
+ auto* arr_arr_mat2x3 = ty.array(ty.mat2x3<f32>(), 1); // Doubly nested array
+ auto* rtarr_mat4x4 = ty.array(ty.mat4x4<f32>(), 0); // Runtime array
- type::Array arr_mat2x3(ty.mat2x3<f32>(), 1, ast::DecorationList{});
- type::Array arr_arr_mat2x3(ty.mat2x3<f32>(), 1,
- ast::DecorationList{}); // Doubly nested array
-
- type::Array rtarr_mat4x4(ty.mat4x4<f32>(), 0,
- ast::DecorationList{}); // Runtime array
-
- auto* s = create<ast::Struct>(
- 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 @@
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 7ea7a8f..304f720 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 @@
out_ << "constant_id(" << constant->value() << ")";
} else if (auto* offset = deco->As<ast::StructMemberOffsetDecoration>()) {
out_ << "offset(" << offset->offset() << ")";
+ } else if (auto* size = deco->As<ast::StructMemberSizeDecoration>()) {
+ out_ << "[[size(" << size->size() << ")]]" << std::endl;
+ } else if (auto* align = deco->As<ast::StructMemberAlignDecoration>()) {
+ 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 1155aa3..a1ecefa 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, EmitConstructedType_Struct) {
- auto* str = create<ast::Struct>(
- 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 @@
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 @@
}
TEST_F(WgslGeneratorImplTest, EmitAlias_ToStruct) {
- auto* str = create<ast::Struct>(
- 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 63cc1f2..f79d0d6 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_Multiple_EntryPoint_With_Same_ModuleVar) {
// [[block]] struct Data {
- // [[offset(0)]] d : f32;
+ // d : f32;
// };
// [[binding(0), group(0)]] var<storage> data : Data;
//
@@ -185,15 +185,10 @@
// return;
// }
- ast::DecorationList s_decos;
- s_decos.push_back(create<ast::StructBlockDecoration>());
+ auto* s = Structure("Data", {Member("d", ty.f32())},
+ {create<ast::StructBlockDecoration>()});
- auto* str = create<ast::Struct>(
- 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 @@
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 b3e5553..3d4d2ca 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_AccessControl_Read) {
- auto* block_deco = create<ast::StructBlockDecoration>();
- ast::DecorationList decos;
- decos.push_back(block_deco);
-
- auto* str =
- create<ast::Struct>(ast::StructMemberList{Member("a", ty.i32())}, decos);
- auto* s = ty.struct_("S", str);
+ auto* s = Structure("S", {Member("a", ty.i32())},
+ {create<ast::StructBlockDecoration>()});
type::AccessControl a(ast::AccessControl::kReadOnly, s);
@@ -62,13 +57,8 @@
}
TEST_F(WgslGeneratorImplTest, EmitType_AccessControl_ReadWrite) {
- auto* block_deco = create<ast::StructBlockDecoration>();
- ast::DecorationList decos;
- decos.push_back(block_deco);
-
- auto* str =
- create<ast::Struct>(ast::StructMemberList{Member("a", ty.i32())}, decos);
- auto* s = ty.struct_("S", str);
+ auto* s = Structure("S", {Member("a", ty.i32())},
+ {create<ast::StructBlockDecoration>()});
type::AccessControl a(ast::AccessControl::kReadWrite, s);
@@ -158,12 +148,11 @@
}
TEST_F(WgslGeneratorImplTest, EmitType_Struct) {
- auto* str = create<ast::Struct>(
- 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_StructDecl) {
- auto* str = create<ast::Struct>(
- 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 @@
ast::DecorationList decos;
decos.push_back(create<ast::StructBlockDecoration>());
- auto* str = create<ast::Struct>(
- 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 775d5f4..a3eb062 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -145,7 +145,9 @@
"../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 @@
"../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 86617a6..0289b76 100644
--- a/test/compute_boids.wgsl
+++ b/test/compute_boids.wgsl
@@ -38,22 +38,22 @@
// compute shader
[[block]] struct Particle {
- [[offset(0)]] pos : vec2<f32>;
- [[offset(8)]] vel : vec2<f32>;
+ pos : vec2<f32>;
+ vel : vec2<f32>;
};
[[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<Particle, 5>;
+ particles : array<Particle, 5>;
};
[[binding(0), group(0)]] var<uniform> params : [[access(read)]] SimParams;
diff --git a/test/cube.wgsl b/test/cube.wgsl
index 908756c..aef1be2 100644
--- a/test/cube.wgsl
+++ b/test/cube.wgsl
@@ -14,7 +14,7 @@
// Vertex shader
[[block]] struct Uniforms {
- [[offset(0)]] modelViewProjectionMatrix : mat4x4<f32>;
+ modelViewProjectionMatrix : mat4x4<f32>;
};
[[binding(0), group(0)]] var<uniform> uniforms : [[access(read)]] Uniforms;