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>
This commit is contained in:
Ben Clayton 2021-03-15 10:43:11 +00:00 committed by Commit Bot service account
parent 717fbbf183
commit d614dd5d12
107 changed files with 2401 additions and 2038 deletions

View File

@ -750,8 +750,6 @@ int main(int argc, const char** argv) {
<< "]:" << std::endl;
std::cout << "\t\t resource_type = "
<< ResourceTypeToString(binding.resource_type) << std::endl;
std::cout << "\t\t min_buffer_binding_size = "
<< binding.min_buffer_binding_size << std::endl;
std::cout << "\t\t dim = " << TextureDimensionToString(binding.dim)
<< std::endl;
std::cout << "\t\t sampled_kind = "

View File

@ -317,8 +317,12 @@ source_set("libtint_core_src") {
"ast/struct_block_decoration.h",
"ast/struct_member.cc",
"ast/struct_member.h",
"ast/struct_member_align_decoration.cc",
"ast/struct_member_align_decoration.h",
"ast/struct_member_offset_decoration.cc",
"ast/struct_member_offset_decoration.h",
"ast/struct_member_size_decoration.cc",
"ast/struct_member_size_decoration.h",
"ast/switch_statement.cc",
"ast/switch_statement.h",
"ast/type_constructor_expression.cc",
@ -367,11 +371,14 @@ source_set("libtint_core_src") {
"resolver/resolver.cc",
"resolver/resolver.h",
"scope_stack.h",
"semantic/array.h",
"semantic/call.h",
"semantic/call_target.h",
"semantic/expression.h",
"semantic/info.h",
"semantic/intrinsic.h",
"semantic/node.h",
"semantic/sem_array.cc",
"semantic/sem_call.cc",
"semantic/sem_call_target.cc",
"semantic/sem_expression.cc",
@ -381,6 +388,7 @@ source_set("libtint_core_src") {
"semantic/sem_member_accessor_expression.cc",
"semantic/sem_node.cc",
"semantic/sem_statement.cc",
"semantic/sem_struct.cc",
"semantic/sem_variable.cc",
"semantic/type_mappings.h",
"source.cc",

View File

@ -132,8 +132,12 @@ set(TINT_LIB_SRCS
ast/struct_block_decoration.h
ast/struct_member.cc
ast/struct_member.h
ast/struct_member_align_decoration.cc
ast/struct_member_align_decoration.h
ast/struct_member_offset_decoration.cc
ast/struct_member_offset_decoration.h
ast/struct_member_size_decoration.cc
ast/struct_member_size_decoration.h
ast/switch_statement.cc
ast/switch_statement.h
ast/type_constructor_expression.cc
@ -182,11 +186,14 @@ set(TINT_LIB_SRCS
resolver/resolver.cc
resolver/resolver.h
scope_stack.h
semantic/array.h
semantic/call.h
semantic/call_target.h
semantic/expression.h
semantic/info.h
semantic/intrinsic.h
semantic/node.h
semantic/sem_array.cc
semantic/sem_call.cc
semantic/sem_call_target.cc
semantic/sem_expression.cc
@ -196,6 +203,7 @@ set(TINT_LIB_SRCS
semantic/sem_intrinsic.cc
semantic/sem_node.cc
semantic/sem_statement.cc
semantic/sem_struct.cc
semantic/sem_variable.cc
semantic/type_mappings.h
source.cc
@ -436,7 +444,9 @@ if(${TINT_BUILD_TESTS})
ast/sint_literal_test.cc
ast/stage_decoration_test.cc
ast/stride_decoration_test.cc
ast/struct_member_align_decoration_test.cc
ast/struct_member_offset_decoration_test.cc
ast/struct_member_size_decoration_test.cc
ast/struct_member_test.cc
ast/struct_test.cc
ast/switch_statement_test.cc
@ -458,9 +468,11 @@ if(${TINT_BUILD_TESTS})
intrinsic_table_test.cc
program_test.cc
resolver/intrinsic_test.cc
resolver/is_storeable_test.cc
resolver/resolver_test_helper.cc
resolver/resolver_test_helper.h
resolver/resolver_test.cc
resolver/struct_layout_test.cc
resolver/validation_test.cc
scope_stack_test.cc
semantic/sem_intrinsic_test.cc

View File

@ -28,9 +28,8 @@ TEST(ModuleCloneTest, Clone) {
// See also fuzzers/tint_ast_clone_fuzzer.cc for further coverage of cloning.
Source::File file("test.wgsl", R"([[block]]
struct S {
[[offset(0)]]
[[size(4)]]
m0 : u32;
[[offset(4)]]
m1 : array<u32>;
};
@ -38,7 +37,7 @@ const c0 : i32 = 10;
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;

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -21,6 +21,8 @@ namespace tint {
namespace ast {
/// A struct member offset decoration
// [DEPRECATED] - Replaced with StructMemberAlignDecoration and
// StructMemberSizeDecoration
class StructMemberOffsetDecoration
: public Castable<StructMemberOffsetDecoration, Decoration> {
public:

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -22,11 +22,11 @@ namespace {
using StructMemberTest = TestHelper;
TEST_F(StructMemberTest, Creation) {
auto* st = Member("a", ty.i32(), {MemberOffset(4)});
auto* st = Member("a", ty.i32(), {MemberSize(4)});
EXPECT_EQ(st->symbol(), Symbol(1));
EXPECT_EQ(st->type(), ty.i32());
EXPECT_EQ(st->decorations().size(), 1u);
EXPECT_TRUE(st->decorations()[0]->Is<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 @@ TEST_F(StructMemberTest, Assert_NullDecoration) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.Member("a", b.ty.i32(), {b.MemberOffset(4), nullptr});
b.Member("a", b.ty.i32(), {b.MemberSize(4), nullptr});
},
"internal compiler error");
}
TEST_F(StructMemberTest, ToStr) {
auto* st = Member("a", ty.i32(), {MemberOffset(4)});
EXPECT_EQ(str(st), "StructMember{[[ offset 4 ]] a: __i32}\n");
auto* st = Member("a", ty.i32(), {MemberSize(4)});
EXPECT_EQ(str(st), "StructMember{[[ size 4 ]] a: __i32}\n");
}
TEST_F(StructMemberTest, ToStrNoDecorations) {

View File

@ -23,6 +23,7 @@
#include "src/ast/sint_literal.h"
#include "src/ast/uint_literal.h"
#include "src/semantic/function.h"
#include "src/semantic/struct.h"
#include "src/semantic/variable.h"
#include "src/type/access_control_type.h"
#include "src/type/array_type.h"
@ -379,12 +380,18 @@ std::vector<ResourceBinding> Inspector::GetUniformBufferResourceBindings(
continue;
}
auto* sem = program_->Sem().Get(str);
if (!sem) {
error_ = "Missing semantic information for structure " +
program_->Symbols().NameFor(str->symbol());
continue;
}
ResourceBinding entry;
entry.resource_type = ResourceBinding::ResourceType::kUniformBuffer;
entry.bind_group = binding_info.group->value();
entry.binding = binding_info.binding->value();
entry.min_buffer_binding_size =
decl->type()->MinBufferBindingSize(type::MemoryLayout::kUniformBuffer);
entry.size = sem->Size();
result.push_back(entry);
}
@ -541,7 +548,15 @@ std::vector<ResourceBinding> Inspector::GetStorageBufferResourceBindingsImpl(
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 @@ std::vector<ResourceBinding> Inspector::GetStorageBufferResourceBindingsImpl(
: ResourceBinding::ResourceType::kStorageBuffer;
entry.bind_group = binding_info.group->value();
entry.binding = binding_info.binding->value();
entry.min_buffer_binding_size =
decl->type()->MinBufferBindingSize(type::MemoryLayout::kStorageBuffer);
entry.size = sem->Size();
result.push_back(entry);
}

View File

@ -112,8 +112,8 @@ struct ResourceBinding {
uint32_t bind_group;
/// Identifier to identify this binding within the bind group
uint32_t binding;
/// Minimum size required for this binding, in bytes, if defined.
uint64_t min_buffer_binding_size;
/// Size for this binding, in bytes, if defined.
uint64_t size;
/// Dimensionality of this binding, if defined.
TextureDimension dim;
/// Kind of data being sampled, if defined.

View File

@ -190,22 +190,15 @@ class InspectorHelper : public ProgramBuilder {
/// Generates a struct type
/// @param name name for the type
/// @param members_info a vector of {type, offset} where each entry is the
/// type and offset of a member of the struct
/// @param member_types a vector of member types
/// @param is_block whether or not to decorate as a Block
/// @returns a struct type
type::Struct* MakeStructType(
const std::string& name,
std::vector<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 @@ class InspectorHelper : public ProgramBuilder {
}
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 @@ class InspectorHelper : public ProgramBuilder {
/// Generates types appropriate for using in a storage buffer
/// @param name name for the type
/// @param members_info a vector of {type, offset} where each entry is the
/// type and offset of a member of the struct
/// @param member_types a vector of member types
/// @returns a tuple {struct type, access control type}, where the struct has
/// the layout for a storage buffer, and the control type wraps the
/// struct.
std::tuple<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 @@ class InspectorHelper : public ProgramBuilder {
/// Generates types appropriate for using in a read-only storage buffer
/// @param name name for the type
/// @param members_info a vector of {type, offset} where each entry is the
/// type and offset of a member of the struct
/// @param member_types a vector of member types
/// @returns a tuple {struct type, access control type}, where the struct has
/// the layout for a read-only storage buffer, and the control type
/// wraps the struct.
std::tuple<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 @@ TEST_F(InspectorGetResourceBindingsTest, Simple) {
type::Struct* ub_struct_type;
type::AccessControl* ub_control_type;
std::tie(ub_struct_type, ub_control_type) =
MakeUniformBufferTypes("ub_type", {{ty.i32(), 0}});
MakeUniformBufferTypes("ub_type", {ty.i32()});
AddUniformBuffer("ub_var", ub_control_type, 0, 0);
MakeStructVariableReferenceBodyFunction("ub_func", "ub_var", {{0, ty.i32()}});
type::Struct* sb_struct_type;
type::AccessControl* sb_control_type;
std::tie(sb_struct_type, sb_control_type) =
MakeStorageBufferTypes("sb_type", {{ty.i32(), 0}});
MakeStorageBufferTypes("sb_type", {ty.i32()});
AddStorageBuffer("sb_var", sb_control_type, 1, 0);
MakeStructVariableReferenceBodyFunction("sb_func", "sb_var", {{0, ty.i32()}});
type::Struct* ro_struct_type;
type::AccessControl* ro_control_type;
std::tie(ro_struct_type, ro_control_type) =
MakeReadOnlyStorageBufferTypes("ro_type", {{ty.i32(), 0}});
MakeReadOnlyStorageBufferTypes("ro_type", {ty.i32()});
AddStorageBuffer("ro_var", ro_control_type, 1, 1);
MakeStructVariableReferenceBodyFunction("ro_func", "ro_var", {{0, ty.i32()}});
@ -1499,7 +1490,7 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, NonEntryPointFunc) {
type::Struct* foo_struct_type;
type::AccessControl* foo_control_type;
std::tie(foo_struct_type, foo_control_type) =
MakeUniformBufferTypes("foo_type", {{ty.i32(), 0}});
MakeUniformBufferTypes("foo_type", {ty.i32()});
AddUniformBuffer("foo_ub", foo_control_type, 0, 0);
MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", {{0, ty.i32()}});
@ -1520,8 +1511,7 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, NonEntryPointFunc) {
TEST_F(InspectorGetUniformBufferResourceBindingsTest, MissingBlockDeco) {
ast::DecorationList decos;
auto* str = create<ast::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 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, Simple) {
type::Struct* foo_struct_type;
type::AccessControl* foo_control_type;
std::tie(foo_struct_type, foo_control_type) =
MakeUniformBufferTypes("foo_type", {{ty.i32(), 0}});
MakeUniformBufferTypes("foo_type", {ty.i32()});
AddUniformBuffer("foo_ub", foo_control_type, 0, 0);
MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", {{0, ty.i32()}});
@ -1567,14 +1557,14 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, Simple) {
result[0].resource_type);
EXPECT_EQ(0u, result[0].bind_group);
EXPECT_EQ(0u, result[0].binding);
EXPECT_EQ(16u, result[0].min_buffer_binding_size);
EXPECT_EQ(4u, result[0].size);
}
TEST_F(InspectorGetUniformBufferResourceBindingsTest, MultipleMembers) {
type::Struct* foo_struct_type;
type::AccessControl* foo_control_type;
std::tie(foo_struct_type, foo_control_type) = MakeUniformBufferTypes(
"foo_type", {{ty.i32(), 0}, {ty.u32(), 4}, {ty.f32(), 8}});
std::tie(foo_struct_type, foo_control_type) =
MakeUniformBufferTypes("foo_type", {ty.i32(), ty.u32(), ty.f32()});
AddUniformBuffer("foo_ub", foo_control_type, 0, 0);
MakeStructVariableReferenceBodyFunction(
@ -1596,14 +1586,14 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, MultipleMembers) {
result[0].resource_type);
EXPECT_EQ(0u, result[0].bind_group);
EXPECT_EQ(0u, result[0].binding);
EXPECT_EQ(16u, result[0].min_buffer_binding_size);
EXPECT_EQ(12u, result[0].size);
}
TEST_F(InspectorGetUniformBufferResourceBindingsTest, MultipleUniformBuffers) {
type::Struct* ub_struct_type;
type::AccessControl* ub_control_type;
std::tie(ub_struct_type, ub_control_type) = MakeUniformBufferTypes(
"ub_type", {{ty.i32(), 0}, {ty.u32(), 4}, {ty.f32(), 8}});
std::tie(ub_struct_type, ub_control_type) =
MakeUniformBufferTypes("ub_type", {ty.i32(), ty.u32(), ty.f32()});
AddUniformBuffer("ub_foo", ub_control_type, 0, 0);
AddUniformBuffer("ub_bar", ub_control_type, 0, 1);
AddUniformBuffer("ub_baz", ub_control_type, 2, 0);
@ -1639,26 +1629,29 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, MultipleUniformBuffers) {
result[0].resource_type);
EXPECT_EQ(0u, result[0].bind_group);
EXPECT_EQ(0u, result[0].binding);
EXPECT_EQ(16u, result[0].min_buffer_binding_size);
EXPECT_EQ(12u, result[0].size);
EXPECT_EQ(ResourceBinding::ResourceType::kUniformBuffer,
result[1].resource_type);
EXPECT_EQ(0u, result[1].bind_group);
EXPECT_EQ(1u, result[1].binding);
EXPECT_EQ(16u, result[1].min_buffer_binding_size);
EXPECT_EQ(12u, result[1].size);
EXPECT_EQ(ResourceBinding::ResourceType::kUniformBuffer,
result[2].resource_type);
EXPECT_EQ(2u, result[2].bind_group);
EXPECT_EQ(0u, result[2].binding);
EXPECT_EQ(16u, result[2].min_buffer_binding_size);
EXPECT_EQ(12u, result[2].size);
}
TEST_F(InspectorGetUniformBufferResourceBindingsTest, ContainingArray) {
type::Struct* foo_struct_type;
type::AccessControl* foo_control_type;
std::tie(foo_struct_type, foo_control_type) = MakeUniformBufferTypes(
"foo_type", {{ty.i32(), 0}, {u32_array_type(4), 4}});
// TODO(bclayton) - This is not a legal structure layout for uniform buffer
// usage. Once crbug.com/tint/628 is implemented, this will fail validation
// and will need to be fixed.
std::tie(foo_struct_type, foo_control_type) =
MakeUniformBufferTypes("foo_type", {ty.i32(), u32_array_type(4)});
AddUniformBuffer("foo_ub", foo_control_type, 0, 0);
MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", {{0, ty.i32()}});
@ -1679,14 +1672,14 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, ContainingArray) {
result[0].resource_type);
EXPECT_EQ(0u, result[0].bind_group);
EXPECT_EQ(0u, result[0].binding);
EXPECT_EQ(32u, result[0].min_buffer_binding_size);
EXPECT_EQ(20u, result[0].size);
}
TEST_F(InspectorGetStorageBufferResourceBindingsTest, Simple) {
type::Struct* foo_struct_type;
type::AccessControl* foo_control_type;
std::tie(foo_struct_type, foo_control_type) =
MakeStorageBufferTypes("foo_type", {{ty.i32(), 0}});
MakeStorageBufferTypes("foo_type", {ty.i32()});
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
@ -1707,14 +1700,14 @@ TEST_F(InspectorGetStorageBufferResourceBindingsTest, Simple) {
result[0].resource_type);
EXPECT_EQ(0u, result[0].bind_group);
EXPECT_EQ(0u, result[0].binding);
EXPECT_EQ(4u, result[0].min_buffer_binding_size);
EXPECT_EQ(4u, result[0].size);
}
TEST_F(InspectorGetStorageBufferResourceBindingsTest, MultipleMembers) {
type::Struct* foo_struct_type;
type::AccessControl* foo_control_type;
std::tie(foo_struct_type, foo_control_type) = MakeStorageBufferTypes(
"foo_type", {{ty.i32(), 0}, {ty.u32(), 4}, {ty.f32(), 8}});
std::tie(foo_struct_type, foo_control_type) =
MakeStorageBufferTypes("foo_type", {ty.i32(), ty.u32(), ty.f32()});
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
MakeStructVariableReferenceBodyFunction(
@ -1736,14 +1729,14 @@ TEST_F(InspectorGetStorageBufferResourceBindingsTest, MultipleMembers) {
result[0].resource_type);
EXPECT_EQ(0u, result[0].bind_group);
EXPECT_EQ(0u, result[0].binding);
EXPECT_EQ(12u, result[0].min_buffer_binding_size);
EXPECT_EQ(12u, result[0].size);
}
TEST_F(InspectorGetStorageBufferResourceBindingsTest, MultipleStorageBuffers) {
type::Struct* sb_struct_type;
type::AccessControl* sb_control_type;
std::tie(sb_struct_type, sb_control_type) = MakeStorageBufferTypes(
"sb_type", {{ty.i32(), 0}, {ty.u32(), 4}, {ty.f32(), 8}});
std::tie(sb_struct_type, sb_control_type) =
MakeStorageBufferTypes("sb_type", {ty.i32(), ty.u32(), ty.f32()});
AddStorageBuffer("sb_foo", sb_control_type, 0, 0);
AddStorageBuffer("sb_bar", sb_control_type, 0, 1);
AddStorageBuffer("sb_baz", sb_control_type, 2, 0);
@ -1782,26 +1775,26 @@ TEST_F(InspectorGetStorageBufferResourceBindingsTest, MultipleStorageBuffers) {
result[0].resource_type);
EXPECT_EQ(0u, result[0].bind_group);
EXPECT_EQ(0u, result[0].binding);
EXPECT_EQ(12u, result[0].min_buffer_binding_size);
EXPECT_EQ(12u, result[0].size);
EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer,
result[1].resource_type);
EXPECT_EQ(0u, result[1].bind_group);
EXPECT_EQ(1u, result[1].binding);
EXPECT_EQ(12u, result[1].min_buffer_binding_size);
EXPECT_EQ(12u, result[1].size);
EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer,
result[2].resource_type);
EXPECT_EQ(2u, result[2].bind_group);
EXPECT_EQ(0u, result[2].binding);
EXPECT_EQ(12u, result[2].min_buffer_binding_size);
EXPECT_EQ(12u, result[2].size);
}
TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingArray) {
type::Struct* foo_struct_type;
type::AccessControl* foo_control_type;
std::tie(foo_struct_type, foo_control_type) = MakeStorageBufferTypes(
"foo_type", {{ty.i32(), 0}, {u32_array_type(4), 4}});
std::tie(foo_struct_type, foo_control_type) =
MakeStorageBufferTypes("foo_type", {ty.i32(), u32_array_type(4)});
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
@ -1822,14 +1815,14 @@ TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingArray) {
result[0].resource_type);
EXPECT_EQ(0u, result[0].bind_group);
EXPECT_EQ(0u, result[0].binding);
EXPECT_EQ(20u, result[0].min_buffer_binding_size);
EXPECT_EQ(20u, result[0].size);
}
TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingRuntimeArray) {
type::Struct* foo_struct_type;
type::AccessControl* foo_control_type;
std::tie(foo_struct_type, foo_control_type) = MakeStorageBufferTypes(
"foo_type", {{ty.i32(), 0}, {u32_array_type(0), 4}});
std::tie(foo_struct_type, foo_control_type) =
MakeStorageBufferTypes("foo_type", {ty.i32(), u32_array_type(0)});
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
@ -1850,14 +1843,14 @@ TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingRuntimeArray) {
result[0].resource_type);
EXPECT_EQ(0u, result[0].bind_group);
EXPECT_EQ(0u, result[0].binding);
EXPECT_EQ(8u, result[0].min_buffer_binding_size);
EXPECT_EQ(8u, result[0].size);
}
TEST_F(InspectorGetStorageBufferResourceBindingsTest, SkipReadOnly) {
type::Struct* foo_struct_type;
type::AccessControl* foo_control_type;
std::tie(foo_struct_type, foo_control_type) =
MakeReadOnlyStorageBufferTypes("foo_type", {{ty.i32(), 0}});
MakeReadOnlyStorageBufferTypes("foo_type", {ty.i32()});
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
@ -1879,7 +1872,7 @@ TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, Simple) {
type::Struct* foo_struct_type;
type::AccessControl* foo_control_type;
std::tie(foo_struct_type, foo_control_type) =
MakeReadOnlyStorageBufferTypes("foo_type", {{ty.i32(), 0}});
MakeReadOnlyStorageBufferTypes("foo_type", {ty.i32()});
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
@ -1900,15 +1893,15 @@ TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, Simple) {
result[0].resource_type);
EXPECT_EQ(0u, result[0].bind_group);
EXPECT_EQ(0u, result[0].binding);
EXPECT_EQ(4u, result[0].min_buffer_binding_size);
EXPECT_EQ(4u, result[0].size);
}
TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest,
MultipleStorageBuffers) {
type::Struct* sb_struct_type;
type::AccessControl* sb_control_type;
std::tie(sb_struct_type, sb_control_type) = MakeReadOnlyStorageBufferTypes(
"sb_type", {{ty.i32(), 0}, {ty.u32(), 4}, {ty.f32(), 8}});
std::tie(sb_struct_type, sb_control_type) =
MakeReadOnlyStorageBufferTypes("sb_type", {ty.i32(), ty.u32(), ty.f32()});
AddStorageBuffer("sb_foo", sb_control_type, 0, 0);
AddStorageBuffer("sb_bar", sb_control_type, 0, 1);
AddStorageBuffer("sb_baz", sb_control_type, 2, 0);
@ -1947,26 +1940,26 @@ TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest,
result[0].resource_type);
EXPECT_EQ(0u, result[0].bind_group);
EXPECT_EQ(0u, result[0].binding);
EXPECT_EQ(12u, result[0].min_buffer_binding_size);
EXPECT_EQ(12u, result[0].size);
EXPECT_EQ(ResourceBinding::ResourceType::kReadOnlyStorageBuffer,
result[1].resource_type);
EXPECT_EQ(0u, result[1].bind_group);
EXPECT_EQ(1u, result[1].binding);
EXPECT_EQ(12u, result[1].min_buffer_binding_size);
EXPECT_EQ(12u, result[1].size);
EXPECT_EQ(ResourceBinding::ResourceType::kReadOnlyStorageBuffer,
result[2].resource_type);
EXPECT_EQ(2u, result[2].bind_group);
EXPECT_EQ(0u, result[2].binding);
EXPECT_EQ(12u, result[2].min_buffer_binding_size);
EXPECT_EQ(12u, result[2].size);
}
TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, ContainingArray) {
type::Struct* foo_struct_type;
type::AccessControl* foo_control_type;
std::tie(foo_struct_type, foo_control_type) = MakeReadOnlyStorageBufferTypes(
"foo_type", {{ty.i32(), 0}, {u32_array_type(4), 4}});
std::tie(foo_struct_type, foo_control_type) =
MakeReadOnlyStorageBufferTypes("foo_type", {ty.i32(), u32_array_type(4)});
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
@ -1987,15 +1980,15 @@ TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, ContainingArray) {
result[0].resource_type);
EXPECT_EQ(0u, result[0].bind_group);
EXPECT_EQ(0u, result[0].binding);
EXPECT_EQ(20u, result[0].min_buffer_binding_size);
EXPECT_EQ(20u, result[0].size);
}
TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest,
ContainingRuntimeArray) {
type::Struct* foo_struct_type;
type::AccessControl* foo_control_type;
std::tie(foo_struct_type, foo_control_type) = MakeReadOnlyStorageBufferTypes(
"foo_type", {{ty.i32(), 0}, {u32_array_type(0), 4}});
std::tie(foo_struct_type, foo_control_type) =
MakeReadOnlyStorageBufferTypes("foo_type", {ty.i32(), u32_array_type(0)});
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
@ -2016,14 +2009,14 @@ TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest,
result[0].resource_type);
EXPECT_EQ(0u, result[0].bind_group);
EXPECT_EQ(0u, result[0].binding);
EXPECT_EQ(8u, result[0].min_buffer_binding_size);
EXPECT_EQ(8u, result[0].size);
}
TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, SkipNonReadOnly) {
type::Struct* foo_struct_type;
type::AccessControl* foo_control_type;
std::tie(foo_struct_type, foo_control_type) =
MakeStorageBufferTypes("foo_type", {{ty.i32(), 0}});
MakeStorageBufferTypes("foo_type", {ty.i32()});
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});

View File

@ -31,7 +31,9 @@
#include "src/ast/scalar_constructor_expression.h"
#include "src/ast/sint_literal.h"
#include "src/ast/stride_decoration.h"
#include "src/ast/struct_member_align_decoration.h"
#include "src/ast/struct_member_offset_decoration.h"
#include "src/ast/struct_member_size_decoration.h"
#include "src/ast/type_constructor_expression.h"
#include "src/ast/uint_literal.h"
#include "src/ast/variable_decl_statement.h"
@ -955,6 +957,36 @@ class ProgramBuilder {
return create<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 @@ class ProgramBuilder {
return func;
}
/// Creates a ast::Struct and type::Struct, registering the type::Struct with
/// the AST().ConstructedTypes().
/// @param source the source information
/// @param name the struct name
/// @param members the struct members
/// @param decorations the optional struct decorations
/// @returns the struct type
type::Struct* Structure(const Source& source,
const std::string& name,
ast::StructMemberList members,
ast::DecorationList decorations = {}) {
auto* impl =
create<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

View File

@ -331,8 +331,7 @@ TEST_F(SpvParserTest, ConvertType_RuntimeArray) {
EXPECT_TRUE(arr_type->IsRuntimeArray());
ASSERT_NE(arr_type, nullptr);
EXPECT_EQ(arr_type->size(), 0u);
EXPECT_EQ(arr_type->array_stride(), 0u);
EXPECT_FALSE(arr_type->has_array_stride());
EXPECT_EQ(arr_type->decorations().size(), 0u);
auto* elem_type = arr_type->type();
ASSERT_NE(elem_type, nullptr);
EXPECT_TRUE(elem_type->Is<type::U32>());
@ -365,8 +364,10 @@ TEST_F(SpvParserTest, ConvertType_RuntimeArray_ArrayStride_Valid) {
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 @@ TEST_F(SpvParserTest, ConvertType_Array) {
EXPECT_FALSE(arr_type->IsRuntimeArray());
ASSERT_NE(arr_type, nullptr);
EXPECT_EQ(arr_type->size(), 42u);
EXPECT_EQ(arr_type->array_stride(), 0u);
EXPECT_FALSE(arr_type->has_array_stride());
EXPECT_EQ(arr_type->decorations().size(), 0u);
auto* elem_type = arr_type->type();
ASSERT_NE(elem_type, nullptr);
EXPECT_TRUE(elem_type->Is<type::U32>());
@ -499,8 +499,12 @@ TEST_F(SpvParserTest, ConvertType_ArrayStride_Valid) {
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());
}

View File

@ -104,7 +104,9 @@ const char kBuiltinDecoration[] = "builtin";
const char kConstantIdDecoration[] = "constant_id";
const char kGroupDecoration[] = "group";
const char kLocationDecoration[] = "location";
const char kOffsetDecoration[] = "offset";
const char kOffsetDecoration[] = "offset"; // DEPRECATED
const char kSizeDecoration[] = "size";
const char kAlignDecoration[] = "align";
const char kSetDecoration[] = "set";
const char kStageDecoration[] = "stage";
const char kStrideDecoration[] = "stride";
@ -115,11 +117,12 @@ bool is_decoration(Token t) {
return false;
auto s = t.to_str();
return s == kAccessDecoration || s == kBindingDecoration ||
s == kBlockDecoration || s == kBuiltinDecoration ||
s == kConstantIdDecoration || s == kLocationDecoration ||
return s == kAccessDecoration || s == kAlignDecoration ||
s == kBindingDecoration || s == kBlockDecoration ||
s == kBuiltinDecoration || s == kConstantIdDecoration ||
s == kGroupDecoration || s == kLocationDecoration ||
s == kOffsetDecoration || s == kSetDecoration ||
s == kGroupDecoration || s == kStageDecoration ||
s == kSizeDecoration || s == kStageDecoration ||
s == kStrideDecoration || s == kWorkgroupSizeDecoration;
}
@ -2919,6 +2922,28 @@ Maybe<ast::Decoration*> ParserImpl::decoration() {
});
}
if (s == kSizeDecoration) {
const char* use = "size decoration";
return expect_paren_block(use, [&]() -> Result {
auto val = expect_positive_sint(use);
if (val.errored)
return Failure::kErrored;
return create<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 {

View File

@ -704,35 +704,64 @@ TEST_F(ParserImplErrorTest, GlobalDeclStructMemberMissingSemicolon) {
" ^\n");
}
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberOffsetMissingLParen) {
EXPECT("struct S { [[offset 1)]] i : i32; };",
"test.wgsl:1:21 error: expected '(' for offset decoration\n"
"struct S { [[offset 1)]] i : i32; };\n"
" ^\n");
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberAlignMissingLParen) {
EXPECT("struct S { [[align 1)]] i : i32; };",
"test.wgsl:1:20 error: expected '(' for align decoration\n"
"struct S { [[align 1)]] i : i32; };\n"
" ^\n");
}
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberOffsetMissingRParen) {
EXPECT("struct S { [[offset(1]] i : i32; };",
"test.wgsl:1:22 error: expected ')' for offset decoration\n"
"struct S { [[offset(1]] i : i32; };\n"
" ^^\n");
}
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberOffsetInvaldValue) {
EXPECT("struct S { [[offset(x)]] i : i32; };",
"test.wgsl:1:21 error: expected signed integer literal for offset "
"decoration\n"
"struct S { [[offset(x)]] i : i32; };\n"
" ^\n");
}
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberOffsetNegativeValue) {
EXPECT("struct S { [[offset(-2)]] i : i32; };",
"test.wgsl:1:21 error: offset decoration must be positive\n"
"struct S { [[offset(-2)]] i : i32; };\n"
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberAlignMissingRParen) {
EXPECT("struct S { [[align(1]] i : i32; };",
"test.wgsl:1:21 error: expected ')' for align decoration\n"
"struct S { [[align(1]] i : i32; };\n"
" ^^\n");
}
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberAlignInvaldValue) {
EXPECT("struct S { [[align(x)]] i : i32; };",
"test.wgsl:1:20 error: expected signed integer literal for align "
"decoration\n"
"struct S { [[align(x)]] i : i32; };\n"
" ^\n");
}
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberAlignNegativeValue) {
EXPECT("struct S { [[align(-2)]] i : i32; };",
"test.wgsl:1:20 error: align decoration must be positive\n"
"struct S { [[align(-2)]] i : i32; };\n"
" ^^\n");
}
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberSizeMissingLParen) {
EXPECT("struct S { [[size 1)]] i : i32; };",
"test.wgsl:1:19 error: expected '(' for size decoration\n"
"struct S { [[size 1)]] i : i32; };\n"
" ^\n");
}
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberSizeMissingRParen) {
EXPECT("struct S { [[size(1]] i : i32; };",
"test.wgsl:1:20 error: expected ')' for size decoration\n"
"struct S { [[size(1]] i : i32; };\n"
" ^^\n");
}
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberSizeInvaldValue) {
EXPECT("struct S { [[size(x)]] i : i32; };",
"test.wgsl:1:19 error: expected signed integer literal for size "
"decoration\n"
"struct S { [[size(x)]] i : i32; };\n"
" ^\n");
}
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberSizeNegativeValue) {
EXPECT("struct S { [[size(-2)]] i : i32; };",
"test.wgsl:1:19 error: size decoration must be positive\n"
"struct S { [[size(-2)]] i : i32; };\n"
" ^^\n");
}
TEST_F(ParserImplErrorTest, GlobalDeclTypeAliasMissingIdentifier) {
EXPECT("type 1 = f32;",
"test.wgsl:1:6 error: expected identifier for type alias\n"

View File

@ -173,8 +173,7 @@ TEST_F(ParserImplTest, GlobalDecl_ParsesStruct) {
}
TEST_F(ParserImplTest, GlobalDecl_Struct_WithStride) {
auto p =
parser("struct A { [[offset(0)]] data: [[stride(4)]] array<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 @@ TEST_F(ParserImplTest, GlobalDecl_Struct_WithStride) {
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();

View File

@ -44,16 +44,28 @@ TEST_F(ParserImplTest, StructBodyDecl_ParsesEmpty) {
ASSERT_EQ(m.value.size(), 0u);
}
TEST_F(ParserImplTest, StructBodyDecl_InvalidMember) {
TEST_F(ParserImplTest, StructBodyDecl_InvalidAlign) {
auto p = parser(R"(
{
[[offset(nan)]] a : i32;
[[align(nan)]] a : i32;
})");
auto m = p->expect_struct_body_decl();
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(m.errored);
EXPECT_EQ(p->error(),
"3:12: expected signed integer literal for offset decoration");
"3:11: expected signed integer literal for align decoration");
}
TEST_F(ParserImplTest, StructBodyDecl_InvalidSize) {
auto p = parser(R"(
{
[[size(nan)]] a : i32;
})");
auto m = p->expect_struct_body_decl();
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(m.errored);
EXPECT_EQ(p->error(),
"3:10: expected signed integer literal for size decoration");
}
TEST_F(ParserImplTest, StructBodyDecl_MissingClosingBracket) {

View File

@ -24,7 +24,7 @@ TEST_F(ParserImplTest, StructDecl_Parses) {
auto p = parser(R"(
struct S {
a : i32;
[[offset(4)]] b : f32;
b : f32;
})");
auto decos = p->decoration_list();
EXPECT_FALSE(decos.errored);

View File

@ -39,7 +39,7 @@ TEST_F(ParserImplTest, DecorationDecl_EmptyBlock) {
}
TEST_F(ParserImplTest, DecorationDecl_Single) {
auto p = parser("[[offset(4)]]");
auto p = parser("[[size(4)]]");
auto decos = p->decoration_list();
EXPECT_FALSE(p->has_error());
EXPECT_FALSE(decos.errored);
@ -47,26 +47,35 @@ TEST_F(ParserImplTest, DecorationDecl_Single) {
ASSERT_EQ(decos.value.size(), 1u);
auto* deco = decos.value[0]->As<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

View File

@ -23,7 +23,7 @@ TEST_F(ParserImplTest, StructMember_Parses) {
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 @@ TEST_F(ParserImplTest, StructMember_Parses) {
ASSERT_EQ(m->source().range.end.column, 2u);
}
TEST_F(ParserImplTest, StructMember_ParsesWithDecoration) {
TEST_F(ParserImplTest, StructMember_ParsesWithOffsetDecoration_DEPRECATED) {
auto p = parser("[[offset(2)]] a : i32;");
auto& builder = p->builder();
auto* i32 = builder.create<type::I32>();
auto* i32 = builder.ty.i32();
auto decos = p->decoration_list();
EXPECT_FALSE(decos.errored);
@ -75,12 +75,99 @@ TEST_F(ParserImplTest, StructMember_ParsesWithDecoration) {
ASSERT_EQ(m->source().range.end.column, 16u);
}
TEST_F(ParserImplTest, StructMember_ParsesWithMultipleDecorations) {
auto p = parser(R"([[offset(2)]]
[[offset(4)]] a : i32;)");
TEST_F(ParserImplTest, StructMember_ParsesWithAlignDecoration) {
auto p = parser("[[align(2)]] a : i32;");
auto& builder = p->builder();
auto* i32 = builder.create<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 @@ TEST_F(ParserImplTest, StructMember_ParsesWithMultipleDecorations) {
EXPECT_EQ(m->symbol(), builder.Symbols().Get("a"));
EXPECT_EQ(m->type(), i32);
EXPECT_EQ(m->decorations().size(), 2u);
EXPECT_TRUE(m->decorations()[0]->Is<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 @@ TEST_F(ParserImplTest, StructMember_InvalidDecoration) {
ASSERT_TRUE(p->has_error());
EXPECT_EQ(p->error(),
"1:10: expected signed integer literal for offset decoration");
"1:8: expected signed integer literal for size decoration");
}
TEST_F(ParserImplTest, StructMember_InvalidVariable) {
auto p = parser("[[offset(4)]] a : B;");
auto p = parser("[[size(4)]] a : B;");
auto decos = p->decoration_list();
EXPECT_FALSE(decos.errored);
EXPECT_TRUE(decos.matched);
@ -135,7 +220,7 @@ TEST_F(ParserImplTest, StructMember_InvalidVariable) {
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(m.errored);
ASSERT_EQ(m.value, nullptr);
EXPECT_EQ(p->error(), "1:19: unknown constructed type 'B'");
EXPECT_EQ(p->error(), "1:17: unknown constructed type 'B'");
}
TEST_F(ParserImplTest, StructMember_MissingSemicolon) {

View File

@ -344,7 +344,7 @@ TEST_F(ParserImplTest, TypeDecl_Array) {
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 @@ TEST_F(ParserImplTest, TypeDecl_Array_Stride) {
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 @@ TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Stride) {
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) {

View File

@ -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

View File

@ -30,11 +30,14 @@
#include "src/ast/switch_statement.h"
#include "src/ast/unary_op_expression.h"
#include "src/ast/variable_decl_statement.h"
#include "src/semantic/array.h"
#include "src/semantic/call.h"
#include "src/semantic/function.h"
#include "src/semantic/member_accessor_expression.h"
#include "src/semantic/statement.h"
#include "src/semantic/struct.h"
#include "src/semantic/variable.h"
#include "src/type/access_control_type.h"
namespace tint {
namespace resolver {
@ -59,6 +62,20 @@ class ScopedAssignment {
T old_value_;
};
/// Rounds `value` to the next multiple of `alignment`
/// Assumes `alignment` is positive.
template <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 @@ bool Resolver::Resolve() {
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 @@ void Resolver::CreateSemanticNodes() const {
}
}
bool Resolver::DefaultAlignAndSize(type::Type* ty,
uint32_t& align,
uint32_t& size) {
static constexpr uint32_t vector_size[] = {
/* padding */ 0,
/* padding */ 0,
/*vec2*/ 8,
/*vec3*/ 12,
/*vec4*/ 16,
};
static constexpr uint32_t vector_align[] = {
/* padding */ 0,
/* padding */ 0,
/*vec2*/ 8,
/*vec3*/ 16,
/*vec4*/ 16,
};
ty = ty->UnwrapAliasIfNeeded();
if (ty->is_scalar()) {
// Note: Also captures booleans, but these are not host-sharable.
align = 4;
size = 4;
return true;
} else if (auto* vec = ty->As<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_);

View File

@ -42,8 +42,12 @@ class UnaryOpExpression;
class Variable;
} // namespace ast
namespace semantic {
class Array;
class Statement;
} // namespace semantic
namespace type {
class Struct;
} // namespace type
namespace resolver {
@ -63,6 +67,10 @@ class Resolver {
/// @returns true if the resolver was successful
bool Resolve();
/// @param type the given type
/// @returns true if the given type is storable.
static bool IsStorable(type::Type* type);
private:
/// Structure holding semantic information about a variable.
/// Used to build the semantic::Variable nodes at the end of resolving.
@ -141,41 +149,6 @@ class Resolver {
/// @returns true on success, false on error
bool ResolveInternal();
/// Resolves functions
/// @param funcs the functions to check
/// @returns true on success, false on error
bool Functions(const ast::FunctionList& funcs);
/// Resolves a function. Requires all dependency
/// (callee) functions to have DetermineFunction() called on them first.
/// @param func the function to check
/// @returns true on success, false on error
bool Function(ast::Function* func);
/// Resolves a block statement
/// @param stmt the block statement
/// @returns true if determination was successful
bool BlockStatement(const ast::BlockStatement* stmt);
/// Resolves the list of statements
/// @param stmts the statements to resolve
/// @returns true on success, false on error
bool Statements(const ast::StatementList& stmts);
/// Resolves a statement
/// @param stmt the statement to check
/// @returns true on success, false on error
bool Statement(ast::Statement* stmt);
/// Resolves an expression list
/// @param list the expression list to check
/// @returns true on success, false on error
bool Expressions(const ast::ExpressionList& list);
/// Resolves an expression
/// @param expr the expression to check
/// @returns true on success, false on error
bool Expression(ast::Expression* expr);
/// Resolves the storage class for variables. This assumes that it is only
/// called for things in function scope, not module scope.
/// @param stmt the statement to check
/// @returns false on error
bool VariableStorageClass(ast::Statement* stmt);
/// Creates the nodes and adds them to the semantic::Info mappings of the
/// ProgramBuilder.
void CreateSemanticNodes() const;
@ -195,20 +168,43 @@ class Resolver {
void set_referenced_from_function_if_needed(VariableInfo* var, bool local);
bool ArrayAccessor(ast::ArrayAccessorExpression* expr);
bool Binary(ast::BinaryExpression* expr);
bool Bitcast(ast::BitcastExpression* expr);
bool Call(ast::CallExpression* expr);
bool CaseStatement(ast::CaseStatement* stmt);
bool Constructor(ast::ConstructorExpression* expr);
bool Identifier(ast::IdentifierExpression* expr);
bool IfStatement(ast::IfStatement* stmt);
bool IntrinsicCall(ast::CallExpression* call,
semantic::IntrinsicType intrinsic_type);
bool MemberAccessor(ast::MemberAccessorExpression* expr);
bool UnaryOp(ast::UnaryOpExpression* expr);
// AST and Type traversal methods
// Each return true on success, false on failure.
bool ArrayAccessor(ast::ArrayAccessorExpression*);
bool Binary(ast::BinaryExpression*);
bool Bitcast(ast::BitcastExpression*);
bool BlockStatement(const ast::BlockStatement*);
bool Call(ast::CallExpression*);
bool CaseStatement(ast::CaseStatement*);
bool Constructor(ast::ConstructorExpression*);
bool Expression(ast::Expression*);
bool Expressions(const ast::ExpressionList&);
bool Function(ast::Function*);
bool Functions(const ast::FunctionList&);
bool Identifier(ast::IdentifierExpression*);
bool IfStatement(ast::IfStatement*);
bool IntrinsicCall(ast::CallExpression*, semantic::IntrinsicType);
bool MemberAccessor(ast::MemberAccessorExpression*);
bool Statement(ast::Statement*);
bool Statements(const ast::StatementList&);
bool UnaryOp(ast::UnaryOpExpression*);
bool VariableDeclStatement(const ast::VariableDeclStatement*);
bool VariableStorageClass(ast::Statement*);
bool VariableDeclStatement(const ast::VariableDeclStatement* stmt);
/// @returns the semantic information for the array `arr`, building it if it
/// hasn't been constructed already. If an error is raised, nullptr is
/// returned.
const semantic::Array* Array(type::Array*);
/// @returns the semantic information for the structure `str`, building it if
/// it hasn't been constructed already. If an error is raised, nullptr is
/// returned.
const semantic::Struct* Structure(type::Struct* str);
/// @param align the output default alignment in bytes for the type `ty`
/// @param size the output default size in bytes for the type `ty`
/// @returns true on success, false on error
bool DefaultAlignAndSize(type::Type* ty, uint32_t& align, uint32_t& size);
VariableInfo* CreateVariableInfo(ast::Variable*);

View File

@ -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

View File

@ -556,6 +556,38 @@ TEST_F(ResolverValidationTest, Stmt_BreakNotInLoopOrSwitch) {
"12:34 error: break statement must be in a loop or switch case");
}
TEST_F(ResolverValidationTest, NonPOTStructMemberAlignDecoration) {
Structure("S", {
Member("a", ty.f32(), {MemberAlign(Source{{12, 34}}, 3)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
"12:34 error: align value must be a positive, power-of-two integer");
}
TEST_F(ResolverValidationTest, ZeroStructMemberAlignDecoration) {
Structure("S", {
Member("a", ty.f32(), {MemberAlign(Source{{12, 34}}, 0)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
"12:34 error: align value must be a positive, power-of-two integer");
}
TEST_F(ResolverValidationTest, ZeroStructMemberSizeDecoration) {
Structure("S", {
Member("a", ty.f32(), {MemberSize(Source{{12, 34}}, 0)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: size must be at least as big as the type's size (4)");
}
} // namespace
} // namespace resolver
} // namespace tint

69
src/semantic/array.h Normal file
View File

@ -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_

View File

@ -29,9 +29,9 @@ class Call : public Castable<Call, Expression> {
/// @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;

View File

@ -23,12 +23,6 @@
#include "src/semantic/type_mappings.h"
namespace tint {
// Forward declarations
namespace ast {
class Node;
} // namespace ast
namespace semantic {
/// Info holds all the resolved semantic information for a Program.
@ -48,26 +42,29 @@ class Info {
/// @return this Program
Info& operator=(Info&& rhs);
/// Get looks up the semantic information for the AST node `ast_node`.
/// @param ast_node the AST node
/// Get looks up the semantic information for the AST or type node `node`.
/// @param node the AST or type node
/// @returns a pointer to the semantic node if found, otherwise nullptr
template <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 @@ class Info {
/// @return the Info that wraps `inner`
static Info Wrap(const Info& inner) {
Info out;
out.ast_to_sem_ = inner.ast_to_sem_;
out.map = inner.map;
return out;
}
private:
std::unordered_map<const ast::Node*, const semantic::Node*> ast_to_sem_;
std::unordered_map<const CastableBase*, const semantic::Node*> map;
};
} // namespace semantic

26
src/semantic/sem_array.cc Normal file
View File

@ -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

View File

@ -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

112
src/semantic/struct.h Normal file
View File

@ -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_

View File

@ -19,44 +19,52 @@ namespace tint {
// Forward declarations
namespace ast {
class CallExpression;
class Expression;
class Function;
class MemberAccessorExpression;
class StructMember;
class Variable;
} // namespace ast
namespace type {
class Array;
class Struct;
} // namespace type
namespace semantic {
// Forward declarations
class Array;
class Call;
class Expression;
class Function;
class MemberAccessorExpression;
class Struct;
class StructMember;
class Variable;
/// TypeMappings is a struct that holds dummy `operator()` methods that's used
/// by SemanticNodeTypeFor to map AST node types to their corresponding semantic
/// node types.
/// The standard operator overload resolving rules will be used to infer the
/// return type based on the argument type.
/// by SemanticNodeTypeFor to map AST / type node types to their corresponding
/// semantic node types. The standard operator overload resolving rules will be
/// used to infer the return type based on the argument type.
struct TypeMappings {
//! @cond Doxygen_Suppress
semantic::Expression* operator()(ast::Expression*);
semantic::Function* operator()(ast::Function*);
semantic::Variable* operator()(ast::Variable*);
semantic::Call* operator()(ast::CallExpression*);
semantic::MemberAccessorExpression* operator()(
ast::MemberAccessorExpression*);
Array* operator()(type::Array*);
Call* operator()(ast::CallExpression*);
Expression* operator()(ast::Expression*);
Function* operator()(ast::Function*);
MemberAccessorExpression* operator()(ast::MemberAccessorExpression*);
Struct* operator()(type::Struct*);
StructMember* operator()(ast::StructMember*);
Variable* operator()(ast::Variable*);
//! @endcond
};
/// SemanticNodeTypeFor resolves to the appropriate semantic::Node type for the
/// AST node type `AST`.
template <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

View File

@ -154,26 +154,20 @@ Transform::Output FirstIndexOffset::Run(const Program* in) {
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;
}

View File

@ -46,8 +46,8 @@ namespace transform {
/// After:
/// [[block]]
/// struct TintFirstIndexOffsetData {
/// [[offset(0)]] tint_first_vertex_index : u32;
/// [[offset(4)]] tint_first_instance_index : u32;
/// tint_first_vertex_index : u32;
/// tint_first_instance_index : u32;
/// };
/// [[builtin(vertex_index)]] var<in> tint_first_index_offset_vert_idx : u32;
/// [[binding(N), group(M)]] var<uniform> tint_first_index_data :

View File

@ -98,7 +98,6 @@ fn entry() -> void {
[[block]]
struct TintFirstIndexOffsetData {
[[offset(0)]]
tint_first_vertex_index : u32;
};
@ -147,7 +146,6 @@ fn entry() -> void {
[[block]]
struct TintFirstIndexOffsetData {
[[offset(0)]]
tint_first_instance_index : u32;
};
@ -199,9 +197,7 @@ fn entry() -> void {
[[block]]
struct TintFirstIndexOffsetData {
[[offset(0)]]
tint_first_vertex_index : u32;
[[offset(4)]]
tint_first_instance_index : u32;
};
@ -255,7 +251,6 @@ fn entry() -> void {
[[block]]
struct TintFirstIndexOffsetData {
[[offset(0)]]
tint_first_vertex_index : u32;
};

View File

@ -96,7 +96,6 @@ TEST_F(HlslTest, PromoteArrayInitializerToConstVar_Bug406) {
auto* src = R"(
[[block]]
struct Uniforms {
[[offset(0)]]
transform : mat2x2<f32>;
};
@ -121,7 +120,6 @@ fn main() -> void {
auto* expect = R"(
[[block]]
struct Uniforms {
[[offset(0)]]
transform : mat2x2<f32>;
};

View File

@ -34,6 +34,7 @@ class Msl : public Transform {
/// @returns the transformation result
Output Run(const Program* program) override;
private:
/// Hoist location-decorated entry point parameters out to struct members.
void HandleEntryPointIOTypes(CloneContext& ctx) const;
};

View File

@ -230,13 +230,9 @@ void VertexPulling::State::AddVertexStorageBuffers() {
// Creating the struct type
ast::StructMemberList members;
ast::DecorationList member_dec;
member_dec.push_back(
ctx.dst->create<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{}));

View File

@ -83,7 +83,6 @@ fn main() -> void {}
auto* expect = R"(
[[block]]
struct TintVertexData {
[[offset(0)]]
_tint_vertex_data : [[stride(4)]] array<u32>;
};
@ -120,7 +119,6 @@ fn main() -> void {}
[[block]]
struct TintVertexData {
[[offset(0)]]
_tint_vertex_data : [[stride(4)]] array<u32>;
};
@ -163,7 +161,6 @@ fn main() -> void {}
[[block]]
struct TintVertexData {
[[offset(0)]]
_tint_vertex_data : [[stride(4)]] array<u32>;
};
@ -206,7 +203,6 @@ fn main() -> void {}
[[block]]
struct TintVertexData {
[[offset(0)]]
_tint_vertex_data : [[stride(4)]] array<u32>;
};
@ -254,7 +250,6 @@ fn main() -> void {}
[[block]]
struct TintVertexData {
[[offset(0)]]
_tint_vertex_data : [[stride(4)]] array<u32>;
};
@ -316,7 +311,6 @@ fn main() -> void {}
[[block]]
struct TintVertexData {
[[offset(0)]]
_tint_vertex_data : [[stride(4)]] array<u32>;
};
@ -371,7 +365,6 @@ fn main() -> void {}
[[block]]
struct TintVertexData {
[[offset(0)]]
_tint_vertex_data : [[stride(4)]] array<u32>;
};

View File

@ -65,14 +65,6 @@ std::string AccessControl::FriendlyName(const SymbolTable& symbols) const {
return out.str();
}
uint64_t AccessControl::MinBufferBindingSize(MemoryLayout mem_layout) const {
return subtype_->MinBufferBindingSize(mem_layout);
}
uint64_t AccessControl::BaseAlignment(MemoryLayout mem_layout) const {
return subtype_->BaseAlignment(mem_layout);
}
AccessControl* AccessControl::Clone(CloneContext* ctx) const {
// Clone arguments outside of create() call to have deterministic ordering
auto* ty = ctx->Clone(type());

View File

@ -54,16 +54,6 @@ class AccessControl : public Castable<AccessControl, Type> {
/// 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

View File

@ -95,70 +95,6 @@ TEST_F(AccessControlTest, FriendlyNameReadWrite) {
EXPECT_EQ(at.FriendlyName(Symbols()), "[[access(read_write)]] i32");
}
TEST_F(AccessControlTest, MinBufferBindingSizeU32) {
U32 u32;
AccessControl at{ast::AccessControl::kReadOnly, &u32};
EXPECT_EQ(4u, at.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
}
TEST_F(AccessControlTest, MinBufferBindingSizeArray) {
U32 u32;
Array array(&u32, 4, ast::DecorationList{create<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

View File

@ -38,14 +38,6 @@ std::string Alias::FriendlyName(const SymbolTable& symbols) const {
return symbols.NameFor(symbol_);
}
uint64_t Alias::MinBufferBindingSize(MemoryLayout mem_layout) const {
return subtype_->MinBufferBindingSize(mem_layout);
}
uint64_t Alias::BaseAlignment(MemoryLayout mem_layout) const {
return subtype_->BaseAlignment(mem_layout);
}
Alias* Alias::Clone(CloneContext* ctx) const {
// Clone arguments outside of create() call to have deterministic ordering
auto sym = ctx->Clone(symbol());

View File

@ -47,16 +47,6 @@ class Alias : public Castable<Alias, Type> {
/// 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

View File

@ -137,76 +137,6 @@ TEST_F(AliasTest, UnwrapAll_PointerAccessControl) {
EXPECT_EQ(a.UnwrapAll(), ty.u32());
}
TEST_F(AliasTest, MinBufferBindingSizeU32) {
auto* alias = ty.alias("alias", ty.u32());
EXPECT_EQ(4u, alias->MinBufferBindingSize(MemoryLayout::kUniformBuffer));
}
TEST_F(AliasTest, MinBufferBindingSizeArray) {
Array array(ty.u32(), 4,
ast::DecorationList{
create<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);

View File

@ -30,71 +30,28 @@ Array::Array(Array&&) = default;
Array::~Array() = default;
uint64_t Array::MinBufferBindingSize(MemoryLayout mem_layout) const {
if (!has_array_stride()) {
// Arrays in buffers are required to have a stride.
return 0;
}
if (IsRuntimeArray()) {
// WebGPU spec 10.1.2:
// If the last field of the corresponding structure defined in the shader
// has an unbounded array type, then the value of minBufferBindingSize must
// be greater than or equal to the byte offset of that field plus the stride
// of the unbounded array
return array_stride();
} else {
// Not including the padding for the last element
return (size_ - 1) * array_stride() +
subtype_->MinBufferBindingSize(mem_layout);
}
}
uint64_t Array::BaseAlignment(MemoryLayout mem_layout) const {
if (mem_layout == MemoryLayout::kUniformBuffer) {
float aligment = 16; // for a vec4
float unaligned = static_cast<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()) {

View File

@ -40,24 +40,9 @@ class Array : public Castable<Array, Type> {
/// 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

View File

@ -94,38 +94,6 @@ TEST_F(ArrayTest, TypeName_WithStride) {
EXPECT_EQ(arr.type_name(), "__array__i32_3_stride_16");
}
TEST_F(ArrayTest, MinBufferBindingSizeNoStride) {
U32 u32;
Array arr(&u32, 4, ast::DecorationList{});
EXPECT_EQ(0u, arr.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
}
TEST_F(ArrayTest, MinBufferBindingSizeArray) {
U32 u32;
Array arr(&u32, 4, ast::DecorationList{create<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

View File

@ -50,11 +50,6 @@ TEST_F(BoolTest, FriendlyName) {
EXPECT_EQ(b.FriendlyName(Symbols()), "bool");
}
TEST_F(BoolTest, MinBufferBindingSize) {
Bool b;
EXPECT_EQ(0u, b.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
}
} // namespace
} // namespace type
} // namespace tint

View File

@ -67,11 +67,6 @@ TEST_F(DepthTextureTest, FriendlyName) {
EXPECT_EQ(d.FriendlyName(Symbols()), "texture_depth_cube");
}
TEST_F(DepthTextureTest, MinBufferBindingSize) {
DepthTexture d(TextureDimension::kCube);
EXPECT_EQ(0u, d.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
}
} // namespace
} // namespace type
} // namespace tint

View File

@ -35,14 +35,6 @@ std::string F32::FriendlyName(const SymbolTable&) const {
return "f32";
}
uint64_t F32::MinBufferBindingSize(MemoryLayout) const {
return 4;
}
uint64_t F32::BaseAlignment(MemoryLayout) const {
return 4;
}
F32* F32::Clone(CloneContext* ctx) const {
return ctx->dst->create<F32>();
}

View File

@ -39,16 +39,6 @@ class F32 : public Castable<F32, Type> {
/// 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

View File

@ -50,16 +50,6 @@ TEST_F(F32Test, FriendlyName) {
EXPECT_EQ(f.FriendlyName(Symbols()), "f32");
}
TEST_F(F32Test, MinBufferBindingSize) {
F32 f;
EXPECT_EQ(4u, f.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
}
TEST_F(F32Test, BaseAlignment) {
F32 f;
EXPECT_EQ(4u, f.BaseAlignment(MemoryLayout::kUniformBuffer));
}
} // namespace
} // namespace type
} // namespace tint

View File

@ -35,14 +35,6 @@ std::string I32::FriendlyName(const SymbolTable&) const {
return "i32";
}
uint64_t I32::MinBufferBindingSize(MemoryLayout) const {
return 4;
}
uint64_t I32::BaseAlignment(MemoryLayout) const {
return 4;
}
I32* I32::Clone(CloneContext* ctx) const {
return ctx->dst->create<I32>();
}

View File

@ -39,16 +39,6 @@ class I32 : public Castable<I32, Type> {
/// 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

View File

@ -50,16 +50,6 @@ TEST_F(I32Test, FriendlyName) {
EXPECT_EQ(i.FriendlyName(Symbols()), "i32");
}
TEST_F(I32Test, MinBufferBindingSize) {
I32 i;
EXPECT_EQ(4u, i.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
}
TEST_F(I32Test, BaseAlignment) {
I32 i;
EXPECT_EQ(4u, i.BaseAlignment(MemoryLayout::kUniformBuffer));
}
} // namespace
} // namespace type
} // namespace tint

View File

@ -45,18 +45,6 @@ std::string Matrix::FriendlyName(const SymbolTable& symbols) const {
return out.str();
}
uint64_t Matrix::MinBufferBindingSize(MemoryLayout mem_layout) const {
Vector vec(subtype_, rows_);
return (columns_ - 1) * vec.BaseAlignment(mem_layout) +
vec.MinBufferBindingSize(mem_layout);
}
uint64_t Matrix::BaseAlignment(MemoryLayout mem_layout) const {
Vector vec(subtype_, rows_);
Array arr(&vec, columns_, ast::DecorationList{});
return arr.BaseAlignment(mem_layout);
}
Matrix* Matrix::Clone(CloneContext* ctx) const {
// Clone arguments outside of create() call to have deterministic ordering
auto* ty = ctx->Clone(type());

View File

@ -49,16 +49,6 @@ class Matrix : public Castable<Matrix, Type> {
/// 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

View File

@ -60,62 +60,6 @@ TEST_F(MatrixTest, FriendlyName) {
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

View File

@ -78,12 +78,6 @@ TEST_F(MultisampledTextureTest, FriendlyName) {
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

View File

@ -76,12 +76,6 @@ TEST_F(SampledTextureTest, FriendlyName) {
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

View File

@ -71,11 +71,6 @@ TEST_F(SamplerTest, FriendlyNameComparisonSampler) {
EXPECT_EQ(s.FriendlyName(Symbols()), "sampler_comparison");
}
TEST_F(SamplerTest, MinBufferBindingSize) {
Sampler s{SamplerKind::kSampler};
EXPECT_EQ(0u, s.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
}
} // namespace
} // namespace type
} // namespace tint

View File

@ -132,13 +132,6 @@ TEST_F(StorageTextureTest, I32) {
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

View File

@ -38,48 +38,6 @@ std::string Struct::FriendlyName(const SymbolTable& symbols) const {
return symbols.NameFor(symbol_);
}
uint64_t Struct::MinBufferBindingSize(MemoryLayout mem_layout) const {
if (!struct_->members().size()) {
return 0;
}
auto* last_member = struct_->members().back();
// If there is no offset, then this is not a host-shareable struct, returning
// 0 indicates this to the caller.
if (!last_member->has_offset_decoration()) {
return 0;
}
uint64_t size = last_member->type()->MinBufferBindingSize(mem_layout);
if (!size) {
return 0;
}
float unaligned = static_cast<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());

View File

@ -51,16 +51,6 @@ class Struct : public Castable<Struct, Type> {
/// 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

View File

@ -64,140 +64,6 @@ TEST_F(StructTypeTest, FriendlyName) {
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

View File

@ -70,14 +70,6 @@ Type* Type::UnwrapAll() {
return UnwrapIfNeeded()->UnwrapPtrIfNeeded()->UnwrapIfNeeded();
}
uint64_t Type::MinBufferBindingSize(MemoryLayout) const {
return 0;
}
uint64_t Type::BaseAlignment(MemoryLayout) const {
return 0;
}
bool Type::is_scalar() const {
return is_float_scalar() || is_integer_scalar() || Is<Bool>();
}

View File

@ -45,16 +45,6 @@ class Type : public Castable<Type, Cloneable> {
/// 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();

View File

@ -35,14 +35,6 @@ std::string U32::FriendlyName(const SymbolTable&) const {
return "u32";
}
uint64_t U32::MinBufferBindingSize(MemoryLayout) const {
return 4;
}
uint64_t U32::BaseAlignment(MemoryLayout) const {
return 4;
}
U32* U32::Clone(CloneContext* ctx) const {
return ctx->dst->create<U32>();
}

View File

@ -39,16 +39,6 @@ class U32 : public Castable<U32, Type> {
/// 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

View File

@ -50,16 +50,6 @@ TEST_F(U32Test, FriendlyName) {
EXPECT_EQ(u.FriendlyName(Symbols()), "u32");
}
TEST_F(U32Test, MinBufferBindingSize) {
U32 u;
EXPECT_EQ(4u, u.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
}
TEST_F(U32Test, BaseAlignment) {
U32 u;
EXPECT_EQ(4u, u.BaseAlignment(MemoryLayout::kUniformBuffer));
}
} // namespace
} // namespace type
} // namespace tint

View File

@ -40,20 +40,6 @@ std::string Vector::FriendlyName(const SymbolTable& symbols) const {
return out.str();
}
uint64_t Vector::MinBufferBindingSize(MemoryLayout mem_layout) const {
return size_ * subtype_->MinBufferBindingSize(mem_layout);
}
uint64_t Vector::BaseAlignment(MemoryLayout mem_layout) const {
if (size_ == 2) {
return 2 * subtype_->BaseAlignment(mem_layout);
} else if (size_ == 3 || size_ == 4) {
return 4 * subtype_->BaseAlignment(mem_layout);
}
return 0; // vectors are only supposed to have 2, 3, or 4 elements.
}
Vector* Vector::Clone(CloneContext* ctx) const {
// Clone arguments outside of create() call to have deterministic ordering
auto* ty = ctx->Clone(type());

View File

@ -46,16 +46,6 @@ class Vector : public Castable<Vector, Type> {
/// 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

View File

@ -59,42 +59,6 @@ TEST_F(VectorTest, FriendlyName) {
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

View File

@ -872,14 +872,6 @@ TEST_F(ValidatorTest, IsStorable_ArraySizedOfStorable) {
EXPECT_TRUE(v.IsStorable(arr));
}
TEST_F(ValidatorTest, IsStorable_ArraySizedOfNonStorable) {
auto* arr = ty.array(ty.void_(), 5);
ValidatorImpl& v = Build();
EXPECT_FALSE(v.IsStorable(arr));
}
TEST_F(ValidatorTest, IsStorable_ArrayUnsizedOfStorable) {
auto* arr = ty.array<int>();
@ -888,14 +880,6 @@ TEST_F(ValidatorTest, IsStorable_ArrayUnsizedOfStorable) {
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 @@ TEST_F(ValidatorTest, IsStorable_Struct_AllMembersStorable) {
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

View File

@ -21,9 +21,11 @@
#include "src/ast/constant_id_decoration.h"
#include "src/ast/fallthrough_statement.h"
#include "src/ast/variable_decl_statement.h"
#include "src/semantic/array.h"
#include "src/semantic/call.h"
#include "src/semantic/function.h"
#include "src/semantic/member_accessor_expression.h"
#include "src/semantic/struct.h"
#include "src/semantic/variable.h"
#include "src/type/access_control_type.h"
#include "src/type/multisampled_texture_type.h"
@ -2021,11 +2023,13 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
auto* str_type = str->impl();
auto* str_member = str_type->get_member(mem->member()->symbol());
if (!str_member->has_offset_decoration()) {
diagnostics_.add_error("missing offset decoration for struct member");
auto* sem_mem = builder_.Sem().Get(str_member);
if (!sem_mem) {
TINT_ICE(diagnostics_) << "struct member missing semantic info";
return "";
}
out << str_member->offset();
out << sem_mem->Offset();
} else if (res_type->Is<type::Vector>()) {
auto swizzle = builder_.Sem().Get(mem)->Swizzle();
@ -2035,9 +2039,9 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
// This must be a single element swizzle if we've got a vector at this
// point.
if (swizzle.size() != 1) {
diagnostics_.add_error(
"Encountered multi-element swizzle when should have only one "
"level");
TINT_ICE(diagnostics_)
<< "Encountered multi-element swizzle when should have only one "
"level";
return "";
}
@ -2046,8 +2050,8 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
// f64 types.
out << "(4 * " << swizzle[0] << ")";
} else {
diagnostics_.add_error("Invalid result type for member accessor: " +
res_type->type_name());
TINT_ICE(diagnostics_) << "Invalid result type for member accessor: "
<< res_type->type_name();
return "";
}
@ -2057,7 +2061,12 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
out << "(";
if (auto* arr = ary_type->As<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

View File

@ -43,8 +43,10 @@ TEST_F(HlslGeneratorImplTest_Alias, EmitAlias_NameCollision) {
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);

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gmock/gmock.h"
#include "src/ast/stage_decoration.h"
#include "src/ast/struct_block_decoration.h"
#include "src/ast/variable_decl_statement.h"
@ -19,6 +20,8 @@
#include "src/type/access_control_type.h"
#include "src/writer/hlsl/test_helper.h"
using ::testing::HasSubstr;
namespace tint {
namespace writer {
namespace hlsl {
@ -264,11 +267,7 @@ void frag_main() {
TEST_F(HlslGeneratorImplTest_Function,
Emit_Decoration_EntryPoint_With_UniformStruct) {
auto* str = create<ast::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 @@ TEST_F(HlslGeneratorImplTest_Function,
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 @@ void frag_main() {
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 @@ TEST_F(HlslGeneratorImplTest_Function,
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.Generate(out)) << gen.error();
EXPECT_EQ(result(), R"(RWByteAddressBuffer coord : register(u0);
EXPECT_THAT(result(), HasSubstr(R"(RWByteAddressBuffer coord : register(u0);
void frag_main() {
float v = asfloat(coord.Load(4));
return;
}
)");
})"));
}
TEST_F(HlslGeneratorImplTest_Function,
Emit_Decoration_EntryPoint_With_RO_StorageBuffer_Read) {
auto* str = create<ast::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 @@ TEST_F(HlslGeneratorImplTest_Function,
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.Generate(out)) << gen.error();
EXPECT_EQ(result(), R"(ByteAddressBuffer coord : register(t0);
EXPECT_THAT(result(), HasSubstr(R"(ByteAddressBuffer coord : register(t0);
void frag_main() {
float v = asfloat(coord.Load(4));
return;
}
)");
})"));
}
TEST_F(HlslGeneratorImplTest_Function,
Emit_Decoration_EntryPoint_With_WO_StorageBuffer_Store) {
auto* str = create<ast::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 @@ TEST_F(HlslGeneratorImplTest_Function,
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.Generate(out)) << gen.error();
EXPECT_EQ(result(), R"(RWByteAddressBuffer coord : register(u0);
EXPECT_THAT(result(), HasSubstr(R"(RWByteAddressBuffer coord : register(u0);
void frag_main() {
coord.Store(4, asuint(2.0f));
return;
}
)");
})"));
}
TEST_F(HlslGeneratorImplTest_Function,
Emit_Decoration_EntryPoint_With_StorageBuffer_Store) {
auto* str = create<ast::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 @@ TEST_F(HlslGeneratorImplTest_Function,
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.Generate(out)) << gen.error();
EXPECT_EQ(result(), R"(RWByteAddressBuffer coord : register(u0);
EXPECT_THAT(result(), HasSubstr(R"(RWByteAddressBuffer coord : register(u0);
void frag_main() {
coord.Store(4, asuint(2.0f));
return;
}
)");
})"));
}
TEST_F(
@ -715,7 +700,7 @@ TEST_F(HlslGeneratorImplTest_Function,
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.Generate(out)) << gen.error();
EXPECT_EQ(result(), R"(RWByteAddressBuffer coord : register(u0);
EXPECT_THAT(result(), HasSubstr(R"(RWByteAddressBuffer coord : register(u0);
float sub_func(float param) {
return asfloat(coord.Load((4 * 0)));
@ -724,9 +709,7 @@ float sub_func(float param) {
void frag_main() {
float v = sub_func(1.0f);
return;
}
)");
})"));
}
TEST_F(HlslGeneratorImplTest_Function,
@ -857,7 +840,7 @@ TEST_F(HlslGeneratorImplTest_Function, Emit_Function_WithArrayParams) {
TEST_F(HlslGeneratorImplTest_Function,
Emit_Multiple_EntryPoint_With_Same_ModuleVar) {
// [[block]] struct Data {
// [[offset(0)]] d : f32;
// d : f32;
// };
// [[binding(0), group(0)]] var<storage> data : Data;
//
@ -871,12 +854,9 @@ TEST_F(HlslGeneratorImplTest_Function,
// 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);

View File

@ -22,11 +22,7 @@ namespace {
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) {
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,
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,
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 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
// 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 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
ASSERT_TRUE(gen.EmitStatement(out, assign)) << gen.error();
EXPECT_EQ(result(), R"(float3x2 _tint_tmp = b;
data.Store3(4 + 0, asuint(_tint_tmp[0]));
data.Store3(4 + 16, asuint(_tint_tmp[1]));
data.Store3(16 + 0, asuint(_tint_tmp[0]));
data.Store3(16 + 16, asuint(_tint_tmp[1]));
)");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Store_Matrix_Empty) {
// struct Data {
// [[offset(0)]] z : f32;
// [[offset(4)]] a : mat2x3<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 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
EXPECT_EQ(
result(),
R"(float3x2 _tint_tmp = float3x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
data.Store3(4 + 0, asuint(_tint_tmp[0]));
data.Store3(4 + 16, asuint(_tint_tmp[1]));
data.Store3(16 + 0, asuint(_tint_tmp[0]));
data.Store3(16 + 16, asuint(_tint_tmp[1]));
)");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load_Matrix) {
// struct Data {
// [[offset(0)]] z : f32;
// [[offset(4)]] a : mat3x2<f32>;
// z : f32;
// a : mat3x2<f32>;
// };
// var<storage> data : Data;
// data.a;
@ -193,12 +182,9 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
// -> asfloat(uint2x3(data.Load2(4 + 0), data.Load2(4 + 8),
// data.Load2(4 + 16)));
auto* str = create<ast::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 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
EXPECT_EQ(result(),
"asfloat(uint2x3(data.Load2(4 + 0), data.Load2(4 + 8), "
"data.Load2(4 + 16)))");
"asfloat(uint2x3(data.Load2(8 + 0), data.Load2(8 + 8), "
"data.Load2(8 + 16)))");
}
TEST_F(HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load_Matrix_Nested) {
// struct Data {
// [[offset(0)]] z : f32;
// [[offset(4)]] a : mat2x3<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 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
EXPECT_EQ(result(),
"asfloat(uint3x2(data.Load3(4 + 0), data.Load3(4 + 16)))");
"asfloat(uint3x2(data.Load3(16 + 0), data.Load3(16 + 16)))");
}
TEST_F(
HlslGeneratorImplTest_MemberAccessor,
EmitExpression_MemberAccessor_StorageBuffer_Load_Matrix_By3_Is_16_Bytes) {
// struct Data {
// [[offset(4)]] a : mat3x3<f32;
// a : mat3x3<f32>
// };
// var<storage> data : Data;
// data.a;
@ -263,11 +242,10 @@ TEST_F(
// -> asfloat(uint3x3(data.Load3(0), data.Load3(16),
// data.Load3(32)));
auto* str = create<ast::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(
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,
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 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
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,
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 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
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,
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,
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 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
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,
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,
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,
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,
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 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
//
// -> 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,
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 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
//
// -> 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 @@ TEST_F(
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 @@ TEST_F(
//
// -> 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(
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 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
//
// -> 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,
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 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
//
// -> 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,
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 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
//
// -> 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(

View File

@ -167,12 +167,10 @@ TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Pointer) {
}
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_StructDecl) {
}
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 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct) {
EXPECT_EQ(result(), "S");
}
/// TODO(bclayton): Enable this, fix it, add tests for vector, matrix, array and
/// nested structures.
TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Struct_InjectPadding) {
auto* str = create<ast::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 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct_NameCollision) {
// TODO(dsinclair): How to translate [[block]]
TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Struct_WithDecoration) {
ast::DecorationList decos;
decos.push_back(create<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();

View File

@ -25,12 +25,12 @@
#include "src/ast/float_literal.h"
#include "src/ast/module.h"
#include "src/ast/sint_literal.h"
#include "src/ast/struct_member_offset_decoration.h"
#include "src/ast/uint_literal.h"
#include "src/ast/variable_decl_statement.h"
#include "src/semantic/call.h"
#include "src/semantic/function.h"
#include "src/semantic/member_accessor_expression.h"
#include "src/semantic/struct.h"
#include "src/semantic/variable.h"
#include "src/type/access_control_type.h"
#include "src/type/alias_type.h"
@ -68,14 +68,6 @@ bool last_is_break_or_fallthrough(const ast::BlockStatement* stmts) {
stmts->last()->Is<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 @@ bool GeneratorImpl::Generate() {
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 @@ bool GeneratorImpl::EmitStructType(const type::Struct* str) {
out_ << "struct " << program_->Symbols().NameFor(str->symbol()) << " {"
<< std::endl;
uint32_t pad_count = 0;
auto add_padding = [&](uint32_t size) {
out_ << "int8_t pad_" << pad_count << "[" << size << "];" << std::endl;
pad_count++;
};
increment_indent();
uint32_t current_offset = 0;
uint32_t pad_count = 0;
for (auto* mem : str->impl()->members()) {
std::string attributes;
make_indent();
auto* sem_mem = program_->Sem().Get(mem);
if (!sem_mem) {
TINT_ICE(diagnostics_) << "struct member missing semantic info";
return false;
}
auto const offset = sem_mem->Offset();
if (offset != current_offset) {
add_padding(offset - current_offset);
make_indent();
}
for (auto* deco : mem->decorations()) {
if (auto* o = deco->As<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();

View File

@ -60,16 +60,6 @@ class GeneratorImpl : public TextGenerator {
/// @returns true on successful generation; false otherwise
bool Generate();
/// Calculates the alignment size of the given `type`. This returns 0
/// for pointers as the size is unknown.
/// @param type the type to calculate the alignment size for
/// @returns the number of bytes used to align `type` or 0 on error
uint32_t calculate_alignment_size(type::Type* type);
/// Calculates the largest alignment seen within a struct
/// @param type the struct to calculate
/// @returns the largest alignment value
uint32_t calculate_largest_alignment(type::Struct* type);
/// Handles generating a constructed
/// @param ty the constructed type to generate
/// @returns true if the constructed type was emitted

View File

@ -32,12 +32,10 @@ TEST_F(MslGeneratorImplTest, EmitConstructedType_F32) {
}
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_Struct) {
}
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();

View File

@ -243,17 +243,15 @@ fragment void frag_main(constant float4& coord [[buffer(0)]]) {
)");
}
TEST_F(MslGeneratorImplTest, Emit_Decoration_EntryPoint_With_RW_StorageBuffer) {
auto* str = create<ast::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 @@ fragment void frag_main(device Data& coord [[buffer(0)]]) {
)");
}
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 @@ fragment void frag_main(constant float4& coord [[buffer(0)]]) {
}
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 @@ fragment void frag_main(device Data& coord [[buffer(0)]]) {
}
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 @@ using namespace metal;
TEST_F(MslGeneratorImplTest,
Emit_Function_Multiple_EntryPoint_With_Same_ModuleVar) {
// [[block]] struct Data {
// [[offset(0)]] d : f32;
// d : f32;
// };
// [[binding(0), group(0)]] var<storage> data : Data;
//
@ -760,21 +753,15 @@ TEST_F(MslGeneratorImplTest,
// 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"));

View File

@ -78,178 +78,6 @@ INSTANTIATE_TEST_SUITE_P(
MslBuiltinData{ast::Builtin::kSampleMaskOut,
"sample_mask"}));
TEST_F(MslGeneratorImplTest, calculate_alignment_size_alias) {
auto* alias = ty.alias("a", ty.f32());
GeneratorImpl& gen = Build();
EXPECT_EQ(4u, gen.calculate_alignment_size(alias));
}
TEST_F(MslGeneratorImplTest, calculate_alignment_size_array) {
auto* array = ty.array<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

View File

@ -143,12 +143,10 @@ TEST_F(MslGeneratorImplTest, DISABLED_EmitType_Pointer) {
}
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_Struct) {
}
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 @@ TEST_F(MslGeneratorImplTest, EmitType_StructDecl) {
)");
}
/// TODO(bclayton): Add tests for vector, matrix, array and nested structures.
TEST_F(MslGeneratorImplTest, EmitType_Struct_InjectPadding) {
auto* str = create<ast::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();

View File

@ -64,12 +64,11 @@ TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Array) {
}
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);

View File

@ -22,8 +22,11 @@
#include "src/ast/constant_id_decoration.h"
#include "src/ast/fallthrough_statement.h"
#include "src/ast/null_literal.h"
#include "src/semantic/array.h"
#include "src/semantic/call.h"
#include "src/semantic/function.h"
#include "src/semantic/intrinsic.h"
#include "src/semantic/struct.h"
#include "src/semantic/variable.h"
#include "src/type/depth_texture_type.h"
#include "src/type/multisampled_texture_type.h"
@ -2961,11 +2964,14 @@ bool Builder::GenerateArrayType(type::Array* ary, const Operand& result) {
{result, Operand::Int(elem_type), Operand::Int(len_id)});
}
if (ary->has_array_stride()) {
push_annot(spv::Op::OpDecorate,
{Operand::Int(result_id), Operand::Int(SpvDecorationArrayStride),
Operand::Int(ary->array_stride())});
auto* sem_arr = builder_.Sem().Get(ary);
if (!sem_arr) {
error_ = "array type missing semantic info";
return false;
}
push_annot(spv::Op::OpDecorate,
{Operand::Int(result_id), Operand::Int(SpvDecorationArrayStride),
Operand::Int(sem_arr->Stride())});
return true;
}
@ -3054,38 +3060,39 @@ uint32_t Builder::GenerateStructMember(uint32_t struct_id,
{Operand::Int(struct_id), Operand::Int(idx),
Operand::String(builder_.Symbols().NameFor(member->symbol()))});
bool has_layout = false;
for (auto* deco : member->decorations()) {
if (auto* offset = deco->As<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());

View File

@ -135,12 +135,12 @@ TEST_F(BuilderTest, ArrayAccessor_Dynamic) {
}
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, ArrayAccessor_MultiLevel) {
}
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 @@ TEST_F(BuilderTest, MemberAccessor) {
// 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 @@ TEST_F(BuilderTest, MemberAccessor_Nested) {
//
// 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 @@ TEST_F(BuilderTest, MemberAccessor_Nested_WithAlias) {
//
// 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 @@ TEST_F(BuilderTest, MemberAccessor_Nested_Assignment_LHS) {
//
// 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 @@ TEST_F(BuilderTest, MemberAccessor_Nested_Assignment_RHS) {
// var ident : my_struct
// var store : f32 = ident.inner.a
auto* inner_struct = ty.struct_(
"Inner", create<ast::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 @@ TEST_F(BuilderTest, Accessor_Mixed_ArrayAndMember) {
// 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 @@ TEST_F(BuilderTest, Accessor_Array_Of_Vec) {
// 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);

View File

@ -176,12 +176,12 @@ TEST_F(BuilderTest, Assign_StructMember) {
// 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));

View File

@ -974,15 +974,12 @@ TEST_F(SpvBuilderConstructorTest, Type_Array_2_Vec3) {
}
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_Array) {
}
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_BitCastScalars) {
}
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) {
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);

View File

@ -192,7 +192,7 @@ TEST_F(BuilderTest, FunctionType_DeDuplicate) {
// https://crbug.com/tint/297
TEST_F(BuilderTest, Emit_Multiple_EntryPoint_With_Same_ModuleVar) {
// [[block]] struct Data {
// [[offset(0)]] d : f32;
// d : f32;
// };
// [[binding(0), group(0)]] var<storage> data : Data;
//
@ -206,13 +206,9 @@ TEST_F(BuilderTest, Emit_Multiple_EntryPoint_With_Same_ModuleVar) {
// return;
// }
ast::DecorationList s_decos;
s_decos.push_back(create<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 @@ TEST_F(BuilderTest, Emit_Multiple_EntryPoint_With_Same_ModuleVar) {
create<ast::GroupDecoration>(0),
});
AST().AddConstructedType(s);
{
auto* var = Var("v", ty.f32(), ast::StorageClass::kFunction,
MemberAccessor("data", "d"));

View File

@ -383,10 +383,10 @@ TEST_F(BuilderTest, GlobalVar_DeclReadOnly) {
// };
// 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 @@ TEST_F(BuilderTest, GlobalVar_DeclReadOnly) {
EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 NonWritable
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 Offset 0
OpMemberDecorate %3 0 NonWritable
OpMemberDecorate %3 1 Offset 4
OpMemberDecorate %3 1 NonWritable
)");
EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
@ -417,9 +419,7 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasDeclReadOnly) {
// type B = A;
// var b : [[access(read)]] B
auto* A = ty.struct_(
"A", create<ast::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 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasDeclReadOnly) {
EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 NonWritable
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 Offset 0
OpMemberDecorate %3 0 NonWritable
)");
EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
OpMemberName %3 0 "a"
@ -448,9 +449,7 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasAssignReadOnly) {
// type B = [[access(read)]] A;
// var b : B
auto* A = ty.struct_(
"A", create<ast::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 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasAssignReadOnly) {
EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 NonWritable
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 Offset 0
OpMemberDecorate %3 0 NonWritable
)");
EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
OpMemberName %3 0 "a"
@ -479,9 +479,7 @@ TEST_F(BuilderTest, GlobalVar_TwoVarDeclReadOnly) {
// var b : [[access(read)]] A
// var c : [[access(read_write)]] A
auto* A = ty.struct_(
"A", create<ast::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 @@ TEST_F(BuilderTest, GlobalVar_TwoVarDeclReadOnly) {
EXPECT_TRUE(b.GenerateGlobalVariable(var_c)) << b.error();
EXPECT_EQ(DumpInstructions(b.annots()),
R"(OpMemberDecorate %3 0 NonWritable
R"(OpMemberDecorate %3 0 Offset 0
OpMemberDecorate %3 0 NonWritable
OpMemberDecorate %7 0 Offset 0
)");
EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
OpMemberName %3 0 "a"
@ -665,6 +665,7 @@ OpExecutionMode %11 LocalSize 1 1 1
OpName %1 "mask_in"
OpName %6 "mask_out"
OpName %11 "main"
OpDecorate %3 ArrayStride 4
OpDecorate %1 BuiltIn SampleMask
OpDecorate %6 BuiltIn SampleMask
%4 = OpTypeInt 32 0

View File

@ -1377,13 +1377,9 @@ OpFunctionEnd
}
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) {
}
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),

View File

@ -58,11 +58,11 @@ TEST_F(BuilderTest_Type, ReturnsGeneratedAlias) {
}
TEST_F(BuilderTest_Type, GenerateRuntimeArray) {
type::Array ary(ty.i32(), 0, ast::DecorationList{});
auto* ary = ty.array(ty.i32(), 0);
spirv::Builder& b = Build();
auto id = b.GenerateTypeIfNeeded(&ary);
auto id = b.GenerateTypeIfNeeded(ary);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(1u, id);
@ -72,12 +72,12 @@ TEST_F(BuilderTest_Type, GenerateRuntimeArray) {
}
TEST_F(BuilderTest_Type, ReturnsGeneratedRuntimeArray) {
type::Array ary(ty.i32(), 0, ast::DecorationList{});
auto* ary = ty.array(ty.i32(), 0);
spirv::Builder& b = Build();
EXPECT_EQ(b.GenerateTypeIfNeeded(&ary), 1u);
EXPECT_EQ(b.GenerateTypeIfNeeded(&ary), 1u);
EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u);
EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
@ -86,11 +86,11 @@ TEST_F(BuilderTest_Type, ReturnsGeneratedRuntimeArray) {
}
TEST_F(BuilderTest_Type, GenerateArray) {
type::Array ary(ty.i32(), 4, ast::DecorationList{});
auto* ary = ty.array(ty.i32(), 4);
spirv::Builder& b = Build();
auto id = b.GenerateTypeIfNeeded(&ary);
auto id = b.GenerateTypeIfNeeded(ary);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(1u, id);
@ -102,14 +102,11 @@ TEST_F(BuilderTest_Type, GenerateArray) {
}
TEST_F(BuilderTest_Type, GenerateArray_WithStride) {
type::Array ary(ty.i32(), 4,
ast::DecorationList{
create<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, GenerateArray_WithStride) {
}
TEST_F(BuilderTest_Type, ReturnsGeneratedArray) {
type::Array ary(ty.i32(), 4, ast::DecorationList{});
auto* ary = ty.array(ty.i32(), 4);
spirv::Builder& b = Build();
EXPECT_EQ(b.GenerateTypeIfNeeded(&ary), 1u);
EXPECT_EQ(b.GenerateTypeIfNeeded(&ary), 1u);
EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u);
EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
@ -277,12 +274,11 @@ TEST_F(BuilderTest_Type, ReturnsGeneratedPtr) {
}
TEST_F(BuilderTest_Type, GenerateStruct_Empty) {
auto* s = create<ast::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_Empty) {
}
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 @@ OpMemberName %1 0 "a"
}
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 @@ TEST_F(BuilderTest_Type, GenerateStruct_Decorated) {
OpMemberName %1 0 "a"
)");
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %1 Block
OpMemberDecorate %1 0 Offset 0
)");
}
TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers) {
auto* s = create<ast::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 @@ OpMemberDecorate %1 1 Offset 8
}
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 0 "a"
OpMemberName %1 1 "b"
OpMemberName %1 2 "c"
)");
EXPECT_EQ(DumpInstructions(b.annots()), "");
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %1 0 Offset 0
OpMemberDecorate %1 0 ColMajor
OpMemberDecorate %1 0 MatrixStride 8
OpMemberDecorate %1 1 Offset 16
OpMemberDecorate %1 1 ColMajor
OpMemberDecorate %1 1 MatrixStride 16
OpMemberDecorate %1 2 Offset 48
OpMemberDecorate %1 2 ColMajor
OpMemberDecorate %1 2 MatrixStride 16
)");
}
TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers_LayoutMatrix) {
// We have to infer layout for matrix when it also has an offset.
auto* s = create<ast::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 @@ TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers_LayoutArraysOfMatrix) {
// We have to infer layout for matrix when it also has an offset.
// The decoration goes on the struct member, even if the matrix is buried
// in levels of arrays.
type::Array arr_mat2x2(ty.mat2x2<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 @@ OpMemberName %1 2 "c"
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %1 0 Offset 0
OpMemberDecorate %1 0 ColMajor
OpMemberDecorate %1 0 MatrixStride 8
OpDecorate %2 ArrayStride 16
OpMemberDecorate %1 1 Offset 16
OpMemberDecorate %1 1 ColMajor
OpMemberDecorate %1 1 MatrixStride 16
OpDecorate %8 ArrayStride 32
OpMemberDecorate %1 2 Offset 48
OpMemberDecorate %1 2 ColMajor
OpMemberDecorate %1 2 MatrixStride 16
OpDecorate %11 ArrayStride 64
)");
}

Some files were not shown because too many files have changed in this diff Show More