Split sem/struct into sem and type.
This CL moves the Base entries from sem/struct into type. The class names are maintained as Base over the move and will be renamed in a followup. Bug: tint:1718 Change-Id: I07bed013167c58fc83f0069c781404d8197966ee Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/113425 Reviewed-by: Ben Clayton <bclayton@google.com> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
d8a084585f
commit
f53b2b5b2e
|
@ -573,6 +573,7 @@ libtint_source_set("libtint_core_all_src") {
|
|||
"type/sampler.h",
|
||||
"type/short_name.h",
|
||||
"type/storage_texture.h",
|
||||
"type/struct.h",
|
||||
"type/texture.h",
|
||||
"type/type.h",
|
||||
"type/type_manager.h",
|
||||
|
@ -738,6 +739,8 @@ libtint_source_set("libtint_type_src") {
|
|||
"type/short_name.h",
|
||||
"type/storage_texture.cc",
|
||||
"type/storage_texture.h",
|
||||
"type/struct.cc",
|
||||
"type/struct.h",
|
||||
"type/texture.cc",
|
||||
"type/texture.h",
|
||||
"type/type.cc",
|
||||
|
@ -1228,6 +1231,7 @@ if (tint_build_unittests) {
|
|||
"type/sampler_test.cc",
|
||||
"type/short_name_test.cc",
|
||||
"type/storage_texture_test.cc",
|
||||
"type/struct_test.cc",
|
||||
"type/texture_test.cc",
|
||||
"type/type_manager_test.cc",
|
||||
"type/type_test.cc",
|
||||
|
|
|
@ -335,6 +335,7 @@ list(APPEND TINT_LIB_SRCS
|
|||
sem/sampler_texture_pair.h
|
||||
sem/statement.cc
|
||||
sem/struct.cc
|
||||
sem/struct.h
|
||||
sem/switch_statement.cc
|
||||
sem/switch_statement.h
|
||||
sem/type_initializer.cc
|
||||
|
@ -493,6 +494,8 @@ list(APPEND TINT_LIB_SRCS
|
|||
type/sampler.h
|
||||
type/storage_texture.cc
|
||||
type/storage_texture.h
|
||||
type/struct.cc
|
||||
type/struct.h
|
||||
type/texture.cc
|
||||
type/texture.h
|
||||
type/type.cc
|
||||
|
@ -946,6 +949,7 @@ if(TINT_BUILD_TESTS)
|
|||
type/sampled_texture_test.cc
|
||||
type/sampler_test.cc
|
||||
type/storage_texture_test.cc
|
||||
type/struct_test.cc
|
||||
type/texture_test.cc
|
||||
type/type_test.cc
|
||||
type/type_manager_test.cc
|
||||
|
|
|
@ -497,7 +497,7 @@ class ProgramBuilder {
|
|||
/// @returns the de-aliased array count pointer
|
||||
template <typename T, typename... ARGS>
|
||||
traits::EnableIf<traits::IsTypeOrDerived<T, type::ArrayCount> ||
|
||||
traits::IsTypeOrDerived<T, sem::StructMemberBase>,
|
||||
traits::IsTypeOrDerived<T, type::StructMemberBase>,
|
||||
T>*
|
||||
create(ARGS&&... args) {
|
||||
AssertNotMoved();
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "src/tint/type/f32.h"
|
||||
#include "src/tint/type/i32.h"
|
||||
#include "src/tint/type/matrix.h"
|
||||
#include "src/tint/type/struct.h"
|
||||
#include "src/tint/type/u32.h"
|
||||
#include "src/tint/type/vector.h"
|
||||
#include "src/tint/utils/bitcast.h"
|
||||
|
@ -415,7 +416,7 @@ struct Composite : ImplConstant {
|
|||
utils::Vector<const sem::Constant*, 4> conv_els;
|
||||
conv_els.Reserve(elements.Length());
|
||||
std::function<const type::Type*(size_t idx)> target_el_ty;
|
||||
if (auto* str = target_ty->As<sem::Struct>()) {
|
||||
if (auto* str = target_ty->As<type::StructBase>()) {
|
||||
if (str->Members().Length() != elements.Length()) {
|
||||
TINT_ICE(Resolver, builder.Diagnostics())
|
||||
<< "const-eval conversion of structure has mismatched element counts";
|
||||
|
@ -493,7 +494,7 @@ const ImplConstant* ZeroValue(ProgramBuilder& builder, const type::Type* type) {
|
|||
}
|
||||
return nullptr;
|
||||
},
|
||||
[&](const sem::Struct* s) -> const ImplConstant* {
|
||||
[&](const type::StructBase* s) -> const ImplConstant* {
|
||||
utils::Hashmap<const type::Type*, const ImplConstant*, 8> zero_by_type;
|
||||
utils::Vector<const sem::Constant*, 4> zeros;
|
||||
zeros.Reserve(s->Members().Length());
|
||||
|
@ -1448,7 +1449,7 @@ ConstEval::Result ConstEval::Index(const sem::Expression* obj_expr,
|
|||
}
|
||||
|
||||
ConstEval::Result ConstEval::MemberAccess(const sem::Expression* obj_expr,
|
||||
const sem::StructMember* member) {
|
||||
const type::StructMemberBase* member) {
|
||||
auto obj_val = obj_expr->ConstantValue();
|
||||
if (!obj_val) {
|
||||
return nullptr;
|
||||
|
|
|
@ -33,8 +33,10 @@ class LiteralExpression;
|
|||
namespace tint::sem {
|
||||
class Constant;
|
||||
class Expression;
|
||||
class StructMember;
|
||||
} // namespace tint::sem
|
||||
namespace tint::type {
|
||||
class StructMemberBase;
|
||||
} // namespace tint::type
|
||||
|
||||
namespace tint::resolver {
|
||||
|
||||
|
@ -92,7 +94,7 @@ class ConstEval {
|
|||
/// @param obj the object being accessed
|
||||
/// @param member the member
|
||||
/// @return the result of the member access, or null if the value cannot be calculated
|
||||
Result MemberAccess(const sem::Expression* obj, const sem::StructMember* member);
|
||||
Result MemberAccess(const sem::Expression* obj, const type::StructMemberBase* member);
|
||||
|
||||
/// @param ty the result type
|
||||
/// @param vector the vector being swizzled
|
||||
|
|
|
@ -996,13 +996,13 @@ sem::Function* Resolver::Function(const ast::Function* decl) {
|
|||
if (auto* str = p_ty->As<sem::Struct>()) {
|
||||
switch (decl->PipelineStage()) {
|
||||
case ast::PipelineStage::kVertex:
|
||||
str->AddUsage(sem::PipelineStageUsage::kVertexInput);
|
||||
str->AddUsage(type::PipelineStageUsage::kVertexInput);
|
||||
break;
|
||||
case ast::PipelineStage::kFragment:
|
||||
str->AddUsage(sem::PipelineStageUsage::kFragmentInput);
|
||||
str->AddUsage(type::PipelineStageUsage::kFragmentInput);
|
||||
break;
|
||||
case ast::PipelineStage::kCompute:
|
||||
str->AddUsage(sem::PipelineStageUsage::kComputeInput);
|
||||
str->AddUsage(type::PipelineStageUsage::kComputeInput);
|
||||
break;
|
||||
case ast::PipelineStage::kNone:
|
||||
break;
|
||||
|
@ -1048,13 +1048,13 @@ sem::Function* Resolver::Function(const ast::Function* decl) {
|
|||
|
||||
switch (decl->PipelineStage()) {
|
||||
case ast::PipelineStage::kVertex:
|
||||
str->AddUsage(sem::PipelineStageUsage::kVertexOutput);
|
||||
str->AddUsage(type::PipelineStageUsage::kVertexOutput);
|
||||
break;
|
||||
case ast::PipelineStage::kFragment:
|
||||
str->AddUsage(sem::PipelineStageUsage::kFragmentOutput);
|
||||
str->AddUsage(type::PipelineStageUsage::kFragmentOutput);
|
||||
break;
|
||||
case ast::PipelineStage::kCompute:
|
||||
str->AddUsage(sem::PipelineStageUsage::kComputeOutput);
|
||||
str->AddUsage(type::PipelineStageUsage::kComputeOutput);
|
||||
break;
|
||||
case ast::PipelineStage::kNone:
|
||||
break;
|
||||
|
|
|
@ -76,7 +76,7 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderParam) {
|
|||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->PipelineStageUses(),
|
||||
UnorderedElementsAre(sem::PipelineStageUsage::kVertexInput));
|
||||
UnorderedElementsAre(type::PipelineStageUsage::kVertexInput));
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderReturnType) {
|
||||
|
@ -92,7 +92,7 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderReturnType) {
|
|||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->PipelineStageUses(),
|
||||
UnorderedElementsAre(sem::PipelineStageUsage::kVertexOutput));
|
||||
UnorderedElementsAre(type::PipelineStageUsage::kVertexOutput));
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsFragmentShaderParam) {
|
||||
|
@ -106,7 +106,7 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsFragmentShaderParam) {
|
|||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->PipelineStageUses(),
|
||||
UnorderedElementsAre(sem::PipelineStageUsage::kFragmentInput));
|
||||
UnorderedElementsAre(type::PipelineStageUsage::kFragmentInput));
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsFragmentShaderReturnType) {
|
||||
|
@ -120,7 +120,7 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsFragmentShaderReturnType) {
|
|||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->PipelineStageUses(),
|
||||
UnorderedElementsAre(sem::PipelineStageUsage::kFragmentOutput));
|
||||
UnorderedElementsAre(type::PipelineStageUsage::kFragmentOutput));
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsComputeShaderParam) {
|
||||
|
@ -136,7 +136,7 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsComputeShaderParam) {
|
|||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->PipelineStageUses(),
|
||||
UnorderedElementsAre(sem::PipelineStageUsage::kComputeInput));
|
||||
UnorderedElementsAre(type::PipelineStageUsage::kComputeInput));
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedMultipleStages) {
|
||||
|
@ -155,8 +155,8 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedMultipleStages) {
|
|||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->PipelineStageUses(),
|
||||
UnorderedElementsAre(sem::PipelineStageUsage::kVertexOutput,
|
||||
sem::PipelineStageUsage::kFragmentInput));
|
||||
UnorderedElementsAre(type::PipelineStageUsage::kVertexOutput,
|
||||
type::PipelineStageUsage::kFragmentInput));
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderParamViaAlias) {
|
||||
|
@ -171,7 +171,7 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderParamViaAlias) {
|
|||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->PipelineStageUses(),
|
||||
UnorderedElementsAre(sem::PipelineStageUsage::kFragmentInput));
|
||||
UnorderedElementsAre(type::PipelineStageUsage::kFragmentInput));
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderParamLocationSet) {
|
||||
|
@ -201,7 +201,7 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderReturnTypeViaAlias) {
|
|||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->PipelineStageUses(),
|
||||
UnorderedElementsAre(sem::PipelineStageUsage::kFragmentOutput));
|
||||
UnorderedElementsAre(type::PipelineStageUsage::kFragmentOutput));
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderReturnTypeLocationSet) {
|
||||
|
|
|
@ -23,7 +23,6 @@ namespace tint::ast {
|
|||
class MemberAccessorExpression;
|
||||
} // namespace tint::ast
|
||||
namespace tint::sem {
|
||||
class Struct;
|
||||
class StructMember;
|
||||
} // namespace tint::sem
|
||||
|
||||
|
|
|
@ -14,44 +14,12 @@
|
|||
|
||||
#include "src/tint/sem/struct.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "src/tint/ast/struct_member.h"
|
||||
#include "src/tint/symbol_table.h"
|
||||
#include "src/tint/utils/hash.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::StructBase);
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::Struct);
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::StructMemberBase);
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::StructMember);
|
||||
|
||||
namespace tint::sem {
|
||||
namespace {
|
||||
|
||||
type::TypeFlags FlagsFrom(utils::VectorRef<const StructMemberBase*> members) {
|
||||
type::TypeFlags flags{
|
||||
type::TypeFlag::kConstructable,
|
||||
type::TypeFlag::kCreationFixedFootprint,
|
||||
type::TypeFlag::kFixedFootprint,
|
||||
};
|
||||
for (auto* member : members) {
|
||||
if (!member->Type()->IsConstructible()) {
|
||||
flags.Remove(type::TypeFlag::kConstructable);
|
||||
}
|
||||
if (!member->Type()->HasFixedFootprint()) {
|
||||
flags.Remove(type::TypeFlag::kFixedFootprint);
|
||||
}
|
||||
if (!member->Type()->HasCreationFixedFootprint()) {
|
||||
flags.Remove(type::TypeFlag::kCreationFixedFootprint);
|
||||
}
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Struct::Struct(const ast::Struct* declaration,
|
||||
tint::Source source,
|
||||
|
@ -64,122 +32,6 @@ Struct::Struct(const ast::Struct* declaration,
|
|||
|
||||
Struct::~Struct() = default;
|
||||
|
||||
StructBase::StructBase(tint::Source source,
|
||||
Symbol name,
|
||||
utils::VectorRef<const StructMemberBase*> members,
|
||||
uint32_t align,
|
||||
uint32_t size,
|
||||
uint32_t size_no_padding)
|
||||
: Base(FlagsFrom(members)),
|
||||
source_(source),
|
||||
name_(name),
|
||||
members_(std::move(members)),
|
||||
align_(align),
|
||||
size_(size),
|
||||
size_no_padding_(size_no_padding) {}
|
||||
|
||||
StructBase::~StructBase() = default;
|
||||
|
||||
size_t StructBase::Hash() const {
|
||||
return utils::Hash(TypeInfo::Of<Struct>().full_hashcode, name_);
|
||||
}
|
||||
|
||||
bool StructBase::Equals(const type::Type& other) const {
|
||||
if (auto* o = other.As<Struct>()) {
|
||||
return o->name_ == name_;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const StructMemberBase* StructBase::FindMember(Symbol name) const {
|
||||
for (auto* member : members_) {
|
||||
if (member->Name() == name) {
|
||||
return member;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t StructBase::Align() const {
|
||||
return align_;
|
||||
}
|
||||
|
||||
uint32_t StructBase::Size() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
std::string StructBase::FriendlyName(const SymbolTable& symbols) const {
|
||||
return symbols.NameFor(name_);
|
||||
}
|
||||
|
||||
std::string StructBase::Layout(const tint::SymbolTable& symbols) const {
|
||||
std::stringstream ss;
|
||||
|
||||
auto member_name_of = [&](const sem::StructMemberBase* sm) {
|
||||
return symbols.NameFor(sm->Name());
|
||||
};
|
||||
|
||||
if (Members().IsEmpty()) {
|
||||
return {};
|
||||
}
|
||||
const auto* const last_member = Members().Back();
|
||||
const uint32_t last_member_struct_padding_offset = last_member->Offset() + last_member->Size();
|
||||
|
||||
// Compute max widths to align output
|
||||
const auto offset_w = static_cast<int>(::log10(last_member_struct_padding_offset)) + 1;
|
||||
const auto size_w = static_cast<int>(::log10(Size())) + 1;
|
||||
const auto align_w = static_cast<int>(::log10(Align())) + 1;
|
||||
|
||||
auto print_struct_begin_line = [&](size_t align, size_t size, std::string struct_name) {
|
||||
ss << "/* " << std::setw(offset_w) << " "
|
||||
<< "align(" << std::setw(align_w) << align << ") size(" << std::setw(size_w) << size
|
||||
<< ") */ struct " << struct_name << " {\n";
|
||||
};
|
||||
|
||||
auto print_struct_end_line = [&]() {
|
||||
ss << "/* " << std::setw(offset_w + size_w + align_w) << " "
|
||||
<< "*/ };";
|
||||
};
|
||||
|
||||
auto print_member_line = [&](size_t offset, size_t align, size_t size, std::string s) {
|
||||
ss << "/* offset(" << std::setw(offset_w) << offset << ") align(" << std::setw(align_w)
|
||||
<< align << ") size(" << std::setw(size_w) << size << ") */ " << s << ";\n";
|
||||
};
|
||||
|
||||
print_struct_begin_line(Align(), Size(), UnwrapRef()->FriendlyName(symbols));
|
||||
|
||||
for (size_t i = 0; i < Members().Length(); ++i) {
|
||||
auto* const m = Members()[i];
|
||||
|
||||
// Output field alignment padding, if any
|
||||
auto* const prev_member = (i == 0) ? nullptr : Members()[i - 1];
|
||||
if (prev_member) {
|
||||
uint32_t padding = m->Offset() - (prev_member->Offset() + prev_member->Size());
|
||||
if (padding > 0) {
|
||||
size_t padding_offset = m->Offset() - padding;
|
||||
print_member_line(padding_offset, 1, padding,
|
||||
"// -- implicit field alignment padding --");
|
||||
}
|
||||
}
|
||||
|
||||
// Output member
|
||||
std::string member_name = member_name_of(m);
|
||||
print_member_line(m->Offset(), m->Align(), m->Size(),
|
||||
member_name + " : " + m->Type()->UnwrapRef()->FriendlyName(symbols));
|
||||
}
|
||||
|
||||
// Output struct size padding, if any
|
||||
uint32_t struct_padding = Size() - last_member_struct_padding_offset;
|
||||
if (struct_padding > 0) {
|
||||
print_member_line(last_member_struct_padding_offset, 1, struct_padding,
|
||||
"// -- implicit struct size padding --");
|
||||
}
|
||||
|
||||
print_struct_end_line();
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
StructMember::StructMember(const ast::StructMember* declaration,
|
||||
tint::Source source,
|
||||
Symbol name,
|
||||
|
@ -193,23 +45,4 @@ StructMember::StructMember(const ast::StructMember* declaration,
|
|||
|
||||
StructMember::~StructMember() = default;
|
||||
|
||||
StructMemberBase::StructMemberBase(tint::Source source,
|
||||
Symbol name,
|
||||
const type::Type* type,
|
||||
uint32_t index,
|
||||
uint32_t offset,
|
||||
uint32_t align,
|
||||
uint32_t size,
|
||||
std::optional<uint32_t> location)
|
||||
: source_(source),
|
||||
name_(name),
|
||||
type_(type),
|
||||
index_(index),
|
||||
offset_(offset),
|
||||
align_(align),
|
||||
size_(size),
|
||||
location_(location) {}
|
||||
|
||||
StructMemberBase::~StructMemberBase() = default;
|
||||
|
||||
} // namespace tint::sem
|
||||
|
|
|
@ -15,16 +15,12 @@
|
|||
#ifndef SRC_TINT_SEM_STRUCT_H_
|
||||
#define SRC_TINT_SEM_STRUCT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "src/tint/ast/address_space.h"
|
||||
#include "src/tint/ast/struct.h"
|
||||
#include "src/tint/sem/node.h"
|
||||
#include "src/tint/symbol.h"
|
||||
#include "src/tint/type/struct.h"
|
||||
#include "src/tint/type/type.h"
|
||||
#include "src/tint/utils/vector.h"
|
||||
|
||||
|
@ -34,145 +30,15 @@ class StructMember;
|
|||
} // namespace tint::ast
|
||||
namespace tint::sem {
|
||||
class StructMember;
|
||||
class StructMemberBase;
|
||||
} // namespace tint::sem
|
||||
namespace tint::type {
|
||||
class StructMemberBase;
|
||||
} // namespace tint::type
|
||||
|
||||
namespace tint::sem {
|
||||
|
||||
/// Metadata to capture how a structure is used in a shader module.
|
||||
enum class PipelineStageUsage {
|
||||
kVertexInput,
|
||||
kVertexOutput,
|
||||
kFragmentInput,
|
||||
kFragmentOutput,
|
||||
kComputeInput,
|
||||
kComputeOutput,
|
||||
};
|
||||
|
||||
/// StructBase holds the semantic information for structures.
|
||||
class StructBase : public Castable<StructBase, type::Type> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param source the source of the structure
|
||||
/// @param name the name of the structure
|
||||
/// @param members the structure members
|
||||
/// @param align the byte alignment of the structure
|
||||
/// @param size the byte size of the structure
|
||||
/// @param size_no_padding size of the members without the end of structure
|
||||
/// alignment padding
|
||||
StructBase(tint::Source source,
|
||||
Symbol name,
|
||||
utils::VectorRef<const StructMemberBase*> members,
|
||||
uint32_t align,
|
||||
uint32_t size,
|
||||
uint32_t size_no_padding);
|
||||
|
||||
/// Destructor
|
||||
~StructBase() override;
|
||||
|
||||
/// @returns a hash of the type.
|
||||
size_t Hash() const override;
|
||||
|
||||
/// @param other the other type to compare against
|
||||
/// @returns true if the this type is equal to the given type
|
||||
bool Equals(const Type& other) const override;
|
||||
|
||||
/// @returns the source of the structure
|
||||
tint::Source Source() const { return source_; }
|
||||
|
||||
/// @returns the name of the structure
|
||||
Symbol Name() const { return name_; }
|
||||
|
||||
/// @returns the members of the structure
|
||||
utils::VectorRef<const StructMemberBase*> Members() const { return members_; }
|
||||
|
||||
/// @param name the member name to look for
|
||||
/// @returns the member with the given name, or nullptr if it was not found.
|
||||
const StructMemberBase* FindMember(Symbol name) const;
|
||||
|
||||
/// @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)`
|
||||
/// attribute.
|
||||
uint32_t Align() const override;
|
||||
|
||||
/// @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)`
|
||||
/// attribute.
|
||||
uint32_t Size() const override;
|
||||
|
||||
/// @returns the byte size of the members without the end of structure
|
||||
/// alignment padding
|
||||
uint32_t SizeNoPadding() const { return size_no_padding_; }
|
||||
|
||||
/// Adds the AddressSpace usage to the structure.
|
||||
/// @param usage the storage usage
|
||||
void AddUsage(ast::AddressSpace usage) { address_space_usage_.emplace(usage); }
|
||||
|
||||
/// @returns the set of address space uses of this structure
|
||||
const std::unordered_set<ast::AddressSpace>& AddressSpaceUsage() const {
|
||||
return address_space_usage_;
|
||||
}
|
||||
|
||||
/// @param usage the ast::AddressSpace usage type to query
|
||||
/// @returns true iff this structure has been used as the given address space
|
||||
bool UsedAs(ast::AddressSpace usage) const { return address_space_usage_.count(usage) > 0; }
|
||||
|
||||
/// @returns true iff this structure has been used by address space that's
|
||||
/// host-shareable.
|
||||
bool IsHostShareable() const {
|
||||
for (auto sc : address_space_usage_) {
|
||||
if (ast::IsHostShareable(sc)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Adds the pipeline stage usage to the structure.
|
||||
/// @param usage the storage usage
|
||||
void AddUsage(PipelineStageUsage usage) { pipeline_stage_uses_.emplace(usage); }
|
||||
|
||||
/// @returns the set of entry point uses of this structure
|
||||
const std::unordered_set<PipelineStageUsage>& PipelineStageUses() const {
|
||||
return pipeline_stage_uses_;
|
||||
}
|
||||
|
||||
/// @param symbols the program's symbol table
|
||||
/// @returns the name for this type that closely resembles how it would be
|
||||
/// declared in WGSL.
|
||||
std::string FriendlyName(const SymbolTable& symbols) const override;
|
||||
|
||||
/// @param symbols the program's symbol table
|
||||
/// @returns a multiline string that describes the layout of this struct,
|
||||
/// including size and alignment information.
|
||||
std::string Layout(const tint::SymbolTable& symbols) const;
|
||||
|
||||
/// @param concrete the conversion-rank ordered concrete versions of this abstract structure.
|
||||
void SetConcreteTypes(utils::VectorRef<const StructBase*> concrete) {
|
||||
concrete_types_ = concrete;
|
||||
}
|
||||
|
||||
/// @returns the conversion-rank ordered concrete versions of this abstract structure, or an
|
||||
/// empty vector if this structure is not abstract.
|
||||
/// @note only structures returned by builtins may be abstract (e.g. modf, frexp)
|
||||
utils::VectorRef<const StructBase*> ConcreteTypes() const { return concrete_types_; }
|
||||
|
||||
private:
|
||||
const tint::Source source_;
|
||||
const Symbol name_;
|
||||
const utils::Vector<const StructMemberBase*, 4> members_;
|
||||
const uint32_t align_;
|
||||
const uint32_t size_;
|
||||
const uint32_t size_no_padding_;
|
||||
std::unordered_set<ast::AddressSpace> address_space_usage_;
|
||||
std::unordered_set<PipelineStageUsage> pipeline_stage_uses_;
|
||||
utils::Vector<const StructBase*, 2> concrete_types_;
|
||||
};
|
||||
|
||||
/// Struct holds the semantic information for structures.
|
||||
class Struct final : public Castable<Struct, StructBase> {
|
||||
class Struct final : public Castable<Struct, type::StructBase> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST structure declaration
|
||||
|
@ -206,75 +72,8 @@ class Struct final : public Castable<Struct, StructBase> {
|
|||
ast::Struct const* const declaration_;
|
||||
};
|
||||
|
||||
/// StructMemberBase holds the semantic information for structure members.
|
||||
class StructMemberBase : public Castable<StructMemberBase, type::Node> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param source the source of the struct member
|
||||
/// @param name the name of the structure member
|
||||
/// @param type the type of the member
|
||||
/// @param index the index of the member in the structure
|
||||
/// @param offset the byte offset from the base of the structure
|
||||
/// @param align the byte alignment of the member
|
||||
/// @param size the byte size of the member
|
||||
/// @param location the location attribute, if present
|
||||
StructMemberBase(tint::Source source,
|
||||
Symbol name,
|
||||
const type::Type* type,
|
||||
uint32_t index,
|
||||
uint32_t offset,
|
||||
uint32_t align,
|
||||
uint32_t size,
|
||||
std::optional<uint32_t> location);
|
||||
|
||||
/// Destructor
|
||||
~StructMemberBase() override;
|
||||
|
||||
/// @returns the source the struct member
|
||||
const tint::Source& Source() const { return source_; }
|
||||
|
||||
/// @returns the name of the structure member
|
||||
Symbol Name() const { return name_; }
|
||||
|
||||
/// Sets the owning structure to `s`
|
||||
/// @param s the new structure owner
|
||||
void SetStruct(const sem::StructBase* s) { struct_ = s; }
|
||||
|
||||
/// @returns the structure that owns this member
|
||||
const sem::StructBase* Struct() const { return struct_; }
|
||||
|
||||
/// @returns the type of the member
|
||||
const type::Type* Type() const { return type_; }
|
||||
|
||||
/// @returns the member index
|
||||
uint32_t Index() const { return index_; }
|
||||
|
||||
/// @returns byte offset from base of structure
|
||||
uint32_t Offset() const { return offset_; }
|
||||
|
||||
/// @returns the alignment of the member in bytes
|
||||
uint32_t Align() const { return align_; }
|
||||
|
||||
/// @returns byte size
|
||||
uint32_t Size() const { return size_; }
|
||||
|
||||
/// @returns the location, if set
|
||||
std::optional<uint32_t> Location() const { return location_; }
|
||||
|
||||
private:
|
||||
const tint::Source source_;
|
||||
const Symbol name_;
|
||||
const sem::StructBase* struct_;
|
||||
const type::Type* type_;
|
||||
const uint32_t index_;
|
||||
const uint32_t offset_;
|
||||
const uint32_t align_;
|
||||
const uint32_t size_;
|
||||
const std::optional<uint32_t> location_;
|
||||
};
|
||||
|
||||
/// StructMember holds the semantic information for structure members.
|
||||
class StructMember final : public Castable<StructMember, StructMemberBase> {
|
||||
class StructMember final : public Castable<StructMember, type::StructMemberBase> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST declaration node
|
||||
|
|
|
@ -20,9 +20,9 @@ namespace tint::sem {
|
|||
namespace {
|
||||
|
||||
using namespace tint::number_suffixes; // NOLINT
|
||||
using StructTest = TestHelper;
|
||||
using SemStructTest = TestHelper;
|
||||
|
||||
TEST_F(StructTest, Creation) {
|
||||
TEST_F(SemStructTest, Creation) {
|
||||
auto name = Sym("S");
|
||||
auto* impl = create<ast::Struct>(name, utils::Empty, utils::Empty);
|
||||
auto* ptr = impl;
|
||||
|
@ -34,7 +34,7 @@ TEST_F(StructTest, Creation) {
|
|||
EXPECT_EQ(s->SizeNoPadding(), 16u);
|
||||
}
|
||||
|
||||
TEST_F(StructTest, Hash) {
|
||||
TEST_F(SemStructTest, Hash) {
|
||||
auto* a_impl = create<ast::Struct>(Sym("a"), utils::Empty, utils::Empty);
|
||||
auto* a = create<sem::Struct>(a_impl, a_impl->source, a_impl->name, utils::Empty,
|
||||
4u /* align */, 4u /* size */, 4u /* size_no_padding */);
|
||||
|
@ -45,7 +45,7 @@ TEST_F(StructTest, Hash) {
|
|||
EXPECT_NE(a->Hash(), b->Hash());
|
||||
}
|
||||
|
||||
TEST_F(StructTest, Equals) {
|
||||
TEST_F(SemStructTest, Equals) {
|
||||
auto* a_impl = create<ast::Struct>(Sym("a"), utils::Empty, utils::Empty);
|
||||
auto* a = create<sem::Struct>(a_impl, a_impl->source, a_impl->name, utils::Empty,
|
||||
4u /* align */, 4u /* size */, 4u /* size_no_padding */);
|
||||
|
@ -58,7 +58,7 @@ TEST_F(StructTest, Equals) {
|
|||
EXPECT_FALSE(a->Equals(type::Void{}));
|
||||
}
|
||||
|
||||
TEST_F(StructTest, FriendlyName) {
|
||||
TEST_F(SemStructTest, FriendlyName) {
|
||||
auto name = Sym("my_struct");
|
||||
auto* impl = create<ast::Struct>(name, utils::Empty, utils::Empty);
|
||||
auto* s = create<sem::Struct>(impl, impl->source, impl->name, utils::Empty, 4u /* align */,
|
||||
|
@ -66,162 +66,5 @@ TEST_F(StructTest, FriendlyName) {
|
|||
EXPECT_EQ(s->FriendlyName(Symbols()), "my_struct");
|
||||
}
|
||||
|
||||
TEST_F(StructTest, Layout) {
|
||||
auto* inner_st = //
|
||||
Structure("Inner", utils::Vector{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.u32()),
|
||||
Member("c", ty.f32()),
|
||||
Member("d", ty.vec3<f32>()),
|
||||
Member("e", ty.mat4x2<f32>()),
|
||||
});
|
||||
|
||||
auto* outer_st = Structure("Outer", utils::Vector{
|
||||
Member("inner", ty.type_name("Inner")),
|
||||
Member("a", ty.i32()),
|
||||
});
|
||||
|
||||
auto p = Build();
|
||||
ASSERT_TRUE(p.IsValid()) << p.Diagnostics().str();
|
||||
|
||||
auto* sem_inner_st = p.Sem().Get(inner_st);
|
||||
auto* sem_outer_st = p.Sem().Get(outer_st);
|
||||
|
||||
EXPECT_EQ(sem_inner_st->Layout(p.Symbols()),
|
||||
R"(/* align(16) size(64) */ struct Inner {
|
||||
/* offset( 0) align( 4) size( 4) */ a : i32;
|
||||
/* offset( 4) align( 4) size( 4) */ b : u32;
|
||||
/* offset( 8) align( 4) size( 4) */ c : f32;
|
||||
/* offset(12) align( 1) size( 4) */ // -- implicit field alignment padding --;
|
||||
/* offset(16) align(16) size(12) */ d : vec3<f32>;
|
||||
/* offset(28) align( 1) size( 4) */ // -- implicit field alignment padding --;
|
||||
/* offset(32) align( 8) size(32) */ e : mat4x2<f32>;
|
||||
/* */ };)");
|
||||
|
||||
EXPECT_EQ(sem_outer_st->Layout(p.Symbols()),
|
||||
R"(/* align(16) size(80) */ struct Outer {
|
||||
/* offset( 0) align(16) size(64) */ inner : Inner;
|
||||
/* offset(64) align( 4) size( 4) */ a : i32;
|
||||
/* offset(68) align( 1) size(12) */ // -- implicit struct size padding --;
|
||||
/* */ };)");
|
||||
}
|
||||
|
||||
TEST_F(StructTest, Location) {
|
||||
auto* st = Structure("st", utils::Vector{
|
||||
Member("a", ty.i32(), utils::Vector{Location(1_u)}),
|
||||
Member("b", ty.u32()),
|
||||
});
|
||||
|
||||
auto p = Build();
|
||||
ASSERT_TRUE(p.IsValid()) << p.Diagnostics().str();
|
||||
|
||||
auto* sem = p.Sem().Get(st);
|
||||
ASSERT_EQ(2u, sem->Members().Length());
|
||||
|
||||
EXPECT_TRUE(sem->Members()[0]->Location().has_value());
|
||||
EXPECT_EQ(sem->Members()[0]->Location().value(), 1u);
|
||||
|
||||
EXPECT_FALSE(sem->Members()[1]->Location().has_value());
|
||||
}
|
||||
|
||||
TEST_F(StructTest, IsConstructable) {
|
||||
auto* inner = //
|
||||
Structure("Inner", utils::Vector{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.u32()),
|
||||
Member("c", ty.f32()),
|
||||
Member("d", ty.vec3<f32>()),
|
||||
Member("e", ty.mat4x2<f32>()),
|
||||
});
|
||||
|
||||
auto* outer = Structure("Outer", utils::Vector{
|
||||
Member("inner", ty.type_name("Inner")),
|
||||
Member("a", ty.i32()),
|
||||
});
|
||||
|
||||
auto* outer_runtime_sized_array =
|
||||
Structure("OuterRuntimeSizedArray", utils::Vector{
|
||||
Member("inner", ty.type_name("Inner")),
|
||||
Member("a", ty.i32()),
|
||||
Member("runtime_sized_array", ty.array<i32>()),
|
||||
});
|
||||
auto p = Build();
|
||||
ASSERT_TRUE(p.IsValid()) << p.Diagnostics().str();
|
||||
|
||||
auto* sem_inner = p.Sem().Get(inner);
|
||||
auto* sem_outer = p.Sem().Get(outer);
|
||||
auto* sem_outer_runtime_sized_array = p.Sem().Get(outer_runtime_sized_array);
|
||||
|
||||
EXPECT_TRUE(sem_inner->IsConstructible());
|
||||
EXPECT_TRUE(sem_outer->IsConstructible());
|
||||
EXPECT_FALSE(sem_outer_runtime_sized_array->IsConstructible());
|
||||
}
|
||||
|
||||
TEST_F(StructTest, HasCreationFixedFootprint) {
|
||||
auto* inner = //
|
||||
Structure("Inner", utils::Vector{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.u32()),
|
||||
Member("c", ty.f32()),
|
||||
Member("d", ty.vec3<f32>()),
|
||||
Member("e", ty.mat4x2<f32>()),
|
||||
Member("f", ty.array<f32, 32>()),
|
||||
});
|
||||
|
||||
auto* outer = Structure("Outer", utils::Vector{
|
||||
Member("inner", ty.type_name("Inner")),
|
||||
});
|
||||
|
||||
auto* outer_with_runtime_sized_array =
|
||||
Structure("OuterRuntimeSizedArray", utils::Vector{
|
||||
Member("inner", ty.type_name("Inner")),
|
||||
Member("runtime_sized_array", ty.array<i32>()),
|
||||
});
|
||||
|
||||
auto p = Build();
|
||||
ASSERT_TRUE(p.IsValid()) << p.Diagnostics().str();
|
||||
|
||||
auto* sem_inner = p.Sem().Get(inner);
|
||||
auto* sem_outer = p.Sem().Get(outer);
|
||||
auto* sem_outer_with_runtime_sized_array = p.Sem().Get(outer_with_runtime_sized_array);
|
||||
|
||||
EXPECT_TRUE(sem_inner->HasCreationFixedFootprint());
|
||||
EXPECT_TRUE(sem_outer->HasCreationFixedFootprint());
|
||||
EXPECT_FALSE(sem_outer_with_runtime_sized_array->HasCreationFixedFootprint());
|
||||
}
|
||||
|
||||
TEST_F(StructTest, HasFixedFootprint) {
|
||||
auto* inner = //
|
||||
Structure("Inner", utils::Vector{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.u32()),
|
||||
Member("c", ty.f32()),
|
||||
Member("d", ty.vec3<f32>()),
|
||||
Member("e", ty.mat4x2<f32>()),
|
||||
Member("f", ty.array<f32, 32>()),
|
||||
});
|
||||
|
||||
auto* outer = Structure("Outer", utils::Vector{
|
||||
Member("inner", ty.type_name("Inner")),
|
||||
});
|
||||
|
||||
auto* outer_with_runtime_sized_array =
|
||||
Structure("OuterRuntimeSizedArray", utils::Vector{
|
||||
Member("inner", ty.type_name("Inner")),
|
||||
Member("runtime_sized_array", ty.array<i32>()),
|
||||
});
|
||||
|
||||
auto p = Build();
|
||||
ASSERT_TRUE(p.IsValid()) << p.Diagnostics().str();
|
||||
|
||||
auto* sem_inner = p.Sem().Get(inner);
|
||||
auto* sem_outer = p.Sem().Get(outer);
|
||||
auto* sem_outer_with_runtime_sized_array = p.Sem().Get(outer_with_runtime_sized_array);
|
||||
|
||||
EXPECT_TRUE(sem_inner->HasFixedFootprint());
|
||||
EXPECT_TRUE(sem_outer->HasFixedFootprint());
|
||||
EXPECT_FALSE(sem_outer_with_runtime_sized_array->HasFixedFootprint());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint::sem
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
// Copyright 2022 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/tint/type/struct.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "src/tint/symbol_table.h"
|
||||
#include "src/tint/utils/hash.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::type::StructBase);
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::type::StructMemberBase);
|
||||
|
||||
namespace tint::type {
|
||||
namespace {
|
||||
|
||||
type::TypeFlags FlagsFrom(utils::VectorRef<const StructMemberBase*> members) {
|
||||
type::TypeFlags flags{
|
||||
type::TypeFlag::kConstructable,
|
||||
type::TypeFlag::kCreationFixedFootprint,
|
||||
type::TypeFlag::kFixedFootprint,
|
||||
};
|
||||
for (auto* member : members) {
|
||||
if (!member->Type()->IsConstructible()) {
|
||||
flags.Remove(type::TypeFlag::kConstructable);
|
||||
}
|
||||
if (!member->Type()->HasFixedFootprint()) {
|
||||
flags.Remove(type::TypeFlag::kFixedFootprint);
|
||||
}
|
||||
if (!member->Type()->HasCreationFixedFootprint()) {
|
||||
flags.Remove(type::TypeFlag::kCreationFixedFootprint);
|
||||
}
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
StructBase::StructBase(tint::Source source,
|
||||
Symbol name,
|
||||
utils::VectorRef<const StructMemberBase*> members,
|
||||
uint32_t align,
|
||||
uint32_t size,
|
||||
uint32_t size_no_padding)
|
||||
: Base(FlagsFrom(members)),
|
||||
source_(source),
|
||||
name_(name),
|
||||
members_(std::move(members)),
|
||||
align_(align),
|
||||
size_(size),
|
||||
size_no_padding_(size_no_padding) {}
|
||||
|
||||
StructBase::~StructBase() = default;
|
||||
|
||||
size_t StructBase::Hash() const {
|
||||
return utils::Hash(TypeInfo::Of<StructBase>().full_hashcode, name_);
|
||||
}
|
||||
|
||||
bool StructBase::Equals(const type::Type& other) const {
|
||||
if (auto* o = other.As<StructBase>()) {
|
||||
return o->name_ == name_;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const StructMemberBase* StructBase::FindMember(Symbol name) const {
|
||||
for (auto* member : members_) {
|
||||
if (member->Name() == name) {
|
||||
return member;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t StructBase::Align() const {
|
||||
return align_;
|
||||
}
|
||||
|
||||
uint32_t StructBase::Size() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
std::string StructBase::FriendlyName(const SymbolTable& symbols) const {
|
||||
return symbols.NameFor(name_);
|
||||
}
|
||||
|
||||
std::string StructBase::Layout(const tint::SymbolTable& symbols) const {
|
||||
std::stringstream ss;
|
||||
|
||||
auto member_name_of = [&](const type::StructMemberBase* sm) {
|
||||
return symbols.NameFor(sm->Name());
|
||||
};
|
||||
|
||||
if (Members().IsEmpty()) {
|
||||
return {};
|
||||
}
|
||||
const auto* const last_member = Members().Back();
|
||||
const uint32_t last_member_struct_padding_offset = last_member->Offset() + last_member->Size();
|
||||
|
||||
// Compute max widths to align output
|
||||
const auto offset_w = static_cast<int>(::log10(last_member_struct_padding_offset)) + 1;
|
||||
const auto size_w = static_cast<int>(::log10(Size())) + 1;
|
||||
const auto align_w = static_cast<int>(::log10(Align())) + 1;
|
||||
|
||||
auto print_struct_begin_line = [&](size_t align, size_t size, std::string struct_name) {
|
||||
ss << "/* " << std::setw(offset_w) << " "
|
||||
<< "align(" << std::setw(align_w) << align << ") size(" << std::setw(size_w) << size
|
||||
<< ") */ struct " << struct_name << " {\n";
|
||||
};
|
||||
|
||||
auto print_struct_end_line = [&]() {
|
||||
ss << "/* " << std::setw(offset_w + size_w + align_w) << " "
|
||||
<< "*/ };";
|
||||
};
|
||||
|
||||
auto print_member_line = [&](size_t offset, size_t align, size_t size, std::string s) {
|
||||
ss << "/* offset(" << std::setw(offset_w) << offset << ") align(" << std::setw(align_w)
|
||||
<< align << ") size(" << std::setw(size_w) << size << ") */ " << s << ";\n";
|
||||
};
|
||||
|
||||
print_struct_begin_line(Align(), Size(), UnwrapRef()->FriendlyName(symbols));
|
||||
|
||||
for (size_t i = 0; i < Members().Length(); ++i) {
|
||||
auto* const m = Members()[i];
|
||||
|
||||
// Output field alignment padding, if any
|
||||
auto* const prev_member = (i == 0) ? nullptr : Members()[i - 1];
|
||||
if (prev_member) {
|
||||
uint32_t padding = m->Offset() - (prev_member->Offset() + prev_member->Size());
|
||||
if (padding > 0) {
|
||||
size_t padding_offset = m->Offset() - padding;
|
||||
print_member_line(padding_offset, 1, padding,
|
||||
"// -- implicit field alignment padding --");
|
||||
}
|
||||
}
|
||||
|
||||
// Output member
|
||||
std::string member_name = member_name_of(m);
|
||||
print_member_line(m->Offset(), m->Align(), m->Size(),
|
||||
member_name + " : " + m->Type()->UnwrapRef()->FriendlyName(symbols));
|
||||
}
|
||||
|
||||
// Output struct size padding, if any
|
||||
uint32_t struct_padding = Size() - last_member_struct_padding_offset;
|
||||
if (struct_padding > 0) {
|
||||
print_member_line(last_member_struct_padding_offset, 1, struct_padding,
|
||||
"// -- implicit struct size padding --");
|
||||
}
|
||||
|
||||
print_struct_end_line();
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
StructMemberBase::StructMemberBase(tint::Source source,
|
||||
Symbol name,
|
||||
const type::Type* type,
|
||||
uint32_t index,
|
||||
uint32_t offset,
|
||||
uint32_t align,
|
||||
uint32_t size,
|
||||
std::optional<uint32_t> location)
|
||||
: source_(source),
|
||||
name_(name),
|
||||
type_(type),
|
||||
index_(index),
|
||||
offset_(offset),
|
||||
align_(align),
|
||||
size_(size),
|
||||
location_(location) {}
|
||||
|
||||
StructMemberBase::~StructMemberBase() = default;
|
||||
|
||||
} // namespace tint::type
|
|
@ -0,0 +1,238 @@
|
|||
// Copyright 2022 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_TINT_TYPE_STRUCT_H_
|
||||
#define SRC_TINT_TYPE_STRUCT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "src/tint/ast/address_space.h"
|
||||
#include "src/tint/symbol.h"
|
||||
#include "src/tint/type/node.h"
|
||||
#include "src/tint/type/type.h"
|
||||
#include "src/tint/utils/vector.h"
|
||||
|
||||
// Forward declarations
|
||||
namespace tint::type {
|
||||
class StructMemberBase;
|
||||
} // namespace tint::type
|
||||
|
||||
namespace tint::type {
|
||||
|
||||
/// Metadata to capture how a structure is used in a shader module.
|
||||
enum class PipelineStageUsage {
|
||||
kVertexInput,
|
||||
kVertexOutput,
|
||||
kFragmentInput,
|
||||
kFragmentOutput,
|
||||
kComputeInput,
|
||||
kComputeOutput,
|
||||
};
|
||||
|
||||
/// StructBase holds the Type information for structures.
|
||||
class StructBase : public Castable<StructBase, type::Type> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param source the source of the structure
|
||||
/// @param name the name of the structure
|
||||
/// @param members the structure members
|
||||
/// @param align the byte alignment of the structure
|
||||
/// @param size the byte size of the structure
|
||||
/// @param size_no_padding size of the members without the end of structure
|
||||
/// alignment padding
|
||||
StructBase(tint::Source source,
|
||||
Symbol name,
|
||||
utils::VectorRef<const StructMemberBase*> members,
|
||||
uint32_t align,
|
||||
uint32_t size,
|
||||
uint32_t size_no_padding);
|
||||
|
||||
/// Destructor
|
||||
~StructBase() override;
|
||||
|
||||
/// @returns a hash of the type.
|
||||
size_t Hash() const override;
|
||||
|
||||
/// @param other the other type to compare against
|
||||
/// @returns true if the this type is equal to the given type
|
||||
bool Equals(const Type& other) const override;
|
||||
|
||||
/// @returns the source of the structure
|
||||
tint::Source Source() const { return source_; }
|
||||
|
||||
/// @returns the name of the structure
|
||||
Symbol Name() const { return name_; }
|
||||
|
||||
/// @returns the members of the structure
|
||||
utils::VectorRef<const StructMemberBase*> Members() const { return members_; }
|
||||
|
||||
/// @param name the member name to look for
|
||||
/// @returns the member with the given name, or nullptr if it was not found.
|
||||
const StructMemberBase* FindMember(Symbol name) const;
|
||||
|
||||
/// @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)`
|
||||
/// attribute.
|
||||
uint32_t Align() const override;
|
||||
|
||||
/// @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)`
|
||||
/// attribute.
|
||||
uint32_t Size() const override;
|
||||
|
||||
/// @returns the byte size of the members without the end of structure
|
||||
/// alignment padding
|
||||
uint32_t SizeNoPadding() const { return size_no_padding_; }
|
||||
|
||||
/// Adds the AddressSpace usage to the structure.
|
||||
/// @param usage the storage usage
|
||||
void AddUsage(ast::AddressSpace usage) { address_space_usage_.emplace(usage); }
|
||||
|
||||
/// @returns the set of address space uses of this structure
|
||||
const std::unordered_set<ast::AddressSpace>& AddressSpaceUsage() const {
|
||||
return address_space_usage_;
|
||||
}
|
||||
|
||||
/// @param usage the ast::AddressSpace usage type to query
|
||||
/// @returns true iff this structure has been used as the given address space
|
||||
bool UsedAs(ast::AddressSpace usage) const { return address_space_usage_.count(usage) > 0; }
|
||||
|
||||
/// @returns true iff this structure has been used by address space that's
|
||||
/// host-shareable.
|
||||
bool IsHostShareable() const {
|
||||
for (auto sc : address_space_usage_) {
|
||||
if (ast::IsHostShareable(sc)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Adds the pipeline stage usage to the structure.
|
||||
/// @param usage the storage usage
|
||||
void AddUsage(PipelineStageUsage usage) { pipeline_stage_uses_.emplace(usage); }
|
||||
|
||||
/// @returns the set of entry point uses of this structure
|
||||
const std::unordered_set<PipelineStageUsage>& PipelineStageUses() const {
|
||||
return pipeline_stage_uses_;
|
||||
}
|
||||
|
||||
/// @param symbols the program's symbol table
|
||||
/// @returns the name for this type that closely resembles how it would be
|
||||
/// declared in WGSL.
|
||||
std::string FriendlyName(const SymbolTable& symbols) const override;
|
||||
|
||||
/// @param symbols the program's symbol table
|
||||
/// @returns a multiline string that describes the layout of this struct,
|
||||
/// including size and alignment information.
|
||||
std::string Layout(const tint::SymbolTable& symbols) const;
|
||||
|
||||
/// @param concrete the conversion-rank ordered concrete versions of this abstract structure.
|
||||
void SetConcreteTypes(utils::VectorRef<const StructBase*> concrete) {
|
||||
concrete_types_ = concrete;
|
||||
}
|
||||
|
||||
/// @returns the conversion-rank ordered concrete versions of this abstract structure, or an
|
||||
/// empty vector if this structure is not abstract.
|
||||
/// @note only structures returned by builtins may be abstract (e.g. modf, frexp)
|
||||
utils::VectorRef<const StructBase*> ConcreteTypes() const { return concrete_types_; }
|
||||
|
||||
private:
|
||||
const tint::Source source_;
|
||||
const Symbol name_;
|
||||
const utils::Vector<const StructMemberBase*, 4> members_;
|
||||
const uint32_t align_;
|
||||
const uint32_t size_;
|
||||
const uint32_t size_no_padding_;
|
||||
std::unordered_set<ast::AddressSpace> address_space_usage_;
|
||||
std::unordered_set<PipelineStageUsage> pipeline_stage_uses_;
|
||||
utils::Vector<const StructBase*, 2> concrete_types_;
|
||||
};
|
||||
|
||||
/// StructMemberBase holds the type information for structure members.
|
||||
class StructMemberBase : public Castable<StructMemberBase, type::Node> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param source the source of the struct member
|
||||
/// @param name the name of the structure member
|
||||
/// @param type the type of the member
|
||||
/// @param index the index of the member in the structure
|
||||
/// @param offset the byte offset from the base of the structure
|
||||
/// @param align the byte alignment of the member
|
||||
/// @param size the byte size of the member
|
||||
/// @param location the location attribute, if present
|
||||
StructMemberBase(tint::Source source,
|
||||
Symbol name,
|
||||
const type::Type* type,
|
||||
uint32_t index,
|
||||
uint32_t offset,
|
||||
uint32_t align,
|
||||
uint32_t size,
|
||||
std::optional<uint32_t> location);
|
||||
|
||||
/// Destructor
|
||||
~StructMemberBase() override;
|
||||
|
||||
/// @returns the source the struct member
|
||||
const tint::Source& Source() const { return source_; }
|
||||
|
||||
/// @returns the name of the structure member
|
||||
Symbol Name() const { return name_; }
|
||||
|
||||
/// Sets the owning structure to `s`
|
||||
/// @param s the new structure owner
|
||||
void SetStruct(const type::StructBase* s) { struct_ = s; }
|
||||
|
||||
/// @returns the structure that owns this member
|
||||
const type::StructBase* Struct() const { return struct_; }
|
||||
|
||||
/// @returns the type of the member
|
||||
const type::Type* Type() const { return type_; }
|
||||
|
||||
/// @returns the member index
|
||||
uint32_t Index() const { return index_; }
|
||||
|
||||
/// @returns byte offset from base of structure
|
||||
uint32_t Offset() const { return offset_; }
|
||||
|
||||
/// @returns the alignment of the member in bytes
|
||||
uint32_t Align() const { return align_; }
|
||||
|
||||
/// @returns byte size
|
||||
uint32_t Size() const { return size_; }
|
||||
|
||||
/// @returns the location, if set
|
||||
std::optional<uint32_t> Location() const { return location_; }
|
||||
|
||||
private:
|
||||
const tint::Source source_;
|
||||
const Symbol name_;
|
||||
const type::StructBase* struct_;
|
||||
const type::Type* type_;
|
||||
const uint32_t index_;
|
||||
const uint32_t offset_;
|
||||
const uint32_t align_;
|
||||
const uint32_t size_;
|
||||
const std::optional<uint32_t> location_;
|
||||
};
|
||||
|
||||
} // namespace tint::type
|
||||
|
||||
#endif // SRC_TINT_TYPE_STRUCT_H_
|
|
@ -0,0 +1,219 @@
|
|||
// Copyright 2022 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/tint/type/struct.h"
|
||||
#include "src/tint/type/test_helper.h"
|
||||
#include "src/tint/type/texture.h"
|
||||
|
||||
namespace tint::type {
|
||||
namespace {
|
||||
|
||||
using namespace tint::number_suffixes; // NOLINT
|
||||
using TypeStructTest = TestHelper;
|
||||
|
||||
TEST_F(TypeStructTest, Creation) {
|
||||
auto name = Sym("S");
|
||||
auto* s = create<type::StructBase>(Source{}, name, utils::Empty, 4u /* align */, 8u /* size */,
|
||||
16u /* size_no_padding */);
|
||||
EXPECT_EQ(s->Align(), 4u);
|
||||
EXPECT_EQ(s->Size(), 8u);
|
||||
EXPECT_EQ(s->SizeNoPadding(), 16u);
|
||||
}
|
||||
|
||||
TEST_F(TypeStructTest, Hash) {
|
||||
auto* a = create<type::StructBase>(Source{}, Sym("a"), utils::Empty, 4u /* align */,
|
||||
4u /* size */, 4u /* size_no_padding */);
|
||||
auto* b = create<type::StructBase>(Source{}, Sym("b"), utils::Empty, 4u /* align */,
|
||||
4u /* size */, 4u /* size_no_padding */);
|
||||
|
||||
EXPECT_NE(a->Hash(), b->Hash());
|
||||
}
|
||||
|
||||
TEST_F(TypeStructTest, Equals) {
|
||||
auto* a = create<type::StructBase>(Source{}, Sym("a"), utils::Empty, 4u /* align */,
|
||||
4u /* size */, 4u /* size_no_padding */);
|
||||
auto* b = create<type::StructBase>(Source{}, Sym("b"), utils::Empty, 4u /* align */,
|
||||
4u /* size */, 4u /* size_no_padding */);
|
||||
|
||||
EXPECT_TRUE(a->Equals(*a));
|
||||
EXPECT_FALSE(a->Equals(*b));
|
||||
EXPECT_FALSE(a->Equals(type::Void{}));
|
||||
}
|
||||
|
||||
TEST_F(TypeStructTest, FriendlyName) {
|
||||
auto name = Sym("my_struct");
|
||||
auto* s = create<type::StructBase>(Source{}, name, utils::Empty, 4u /* align */, 4u /* size */,
|
||||
4u /* size_no_padding */);
|
||||
EXPECT_EQ(s->FriendlyName(Symbols()), "my_struct");
|
||||
}
|
||||
|
||||
TEST_F(TypeStructTest, Layout) {
|
||||
auto* inner_st = //
|
||||
Structure("Inner", utils::Vector{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.u32()),
|
||||
Member("c", ty.f32()),
|
||||
Member("d", ty.vec3<f32>()),
|
||||
Member("e", ty.mat4x2<f32>()),
|
||||
});
|
||||
|
||||
auto* outer_st = Structure("Outer", utils::Vector{
|
||||
Member("inner", ty.type_name("Inner")),
|
||||
Member("a", ty.i32()),
|
||||
});
|
||||
|
||||
auto p = Build();
|
||||
ASSERT_TRUE(p.IsValid()) << p.Diagnostics().str();
|
||||
|
||||
auto* sem_inner_st = p.Sem().Get(inner_st);
|
||||
auto* sem_outer_st = p.Sem().Get(outer_st);
|
||||
|
||||
EXPECT_EQ(sem_inner_st->Layout(p.Symbols()),
|
||||
R"(/* align(16) size(64) */ struct Inner {
|
||||
/* offset( 0) align( 4) size( 4) */ a : i32;
|
||||
/* offset( 4) align( 4) size( 4) */ b : u32;
|
||||
/* offset( 8) align( 4) size( 4) */ c : f32;
|
||||
/* offset(12) align( 1) size( 4) */ // -- implicit field alignment padding --;
|
||||
/* offset(16) align(16) size(12) */ d : vec3<f32>;
|
||||
/* offset(28) align( 1) size( 4) */ // -- implicit field alignment padding --;
|
||||
/* offset(32) align( 8) size(32) */ e : mat4x2<f32>;
|
||||
/* */ };)");
|
||||
|
||||
EXPECT_EQ(sem_outer_st->Layout(p.Symbols()),
|
||||
R"(/* align(16) size(80) */ struct Outer {
|
||||
/* offset( 0) align(16) size(64) */ inner : Inner;
|
||||
/* offset(64) align( 4) size( 4) */ a : i32;
|
||||
/* offset(68) align( 1) size(12) */ // -- implicit struct size padding --;
|
||||
/* */ };)");
|
||||
}
|
||||
|
||||
TEST_F(TypeStructTest, Location) {
|
||||
auto* st = Structure("st", utils::Vector{
|
||||
Member("a", ty.i32(), utils::Vector{Location(1_u)}),
|
||||
Member("b", ty.u32()),
|
||||
});
|
||||
|
||||
auto p = Build();
|
||||
ASSERT_TRUE(p.IsValid()) << p.Diagnostics().str();
|
||||
|
||||
auto* sem = p.Sem().Get(st);
|
||||
ASSERT_EQ(2u, sem->Members().Length());
|
||||
|
||||
EXPECT_TRUE(sem->Members()[0]->Location().has_value());
|
||||
EXPECT_EQ(sem->Members()[0]->Location().value(), 1u);
|
||||
|
||||
EXPECT_FALSE(sem->Members()[1]->Location().has_value());
|
||||
}
|
||||
|
||||
TEST_F(TypeStructTest, IsConstructable) {
|
||||
auto* inner = //
|
||||
Structure("Inner", utils::Vector{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.u32()),
|
||||
Member("c", ty.f32()),
|
||||
Member("d", ty.vec3<f32>()),
|
||||
Member("e", ty.mat4x2<f32>()),
|
||||
});
|
||||
|
||||
auto* outer = Structure("Outer", utils::Vector{
|
||||
Member("inner", ty.type_name("Inner")),
|
||||
Member("a", ty.i32()),
|
||||
});
|
||||
|
||||
auto* outer_runtime_sized_array =
|
||||
Structure("OuterRuntimeSizedArray", utils::Vector{
|
||||
Member("inner", ty.type_name("Inner")),
|
||||
Member("a", ty.i32()),
|
||||
Member("runtime_sized_array", ty.array<i32>()),
|
||||
});
|
||||
auto p = Build();
|
||||
ASSERT_TRUE(p.IsValid()) << p.Diagnostics().str();
|
||||
|
||||
auto* sem_inner = p.Sem().Get(inner);
|
||||
auto* sem_outer = p.Sem().Get(outer);
|
||||
auto* sem_outer_runtime_sized_array = p.Sem().Get(outer_runtime_sized_array);
|
||||
|
||||
EXPECT_TRUE(sem_inner->IsConstructible());
|
||||
EXPECT_TRUE(sem_outer->IsConstructible());
|
||||
EXPECT_FALSE(sem_outer_runtime_sized_array->IsConstructible());
|
||||
}
|
||||
|
||||
TEST_F(TypeStructTest, HasCreationFixedFootprint) {
|
||||
auto* inner = //
|
||||
Structure("Inner", utils::Vector{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.u32()),
|
||||
Member("c", ty.f32()),
|
||||
Member("d", ty.vec3<f32>()),
|
||||
Member("e", ty.mat4x2<f32>()),
|
||||
Member("f", ty.array<f32, 32>()),
|
||||
});
|
||||
|
||||
auto* outer = Structure("Outer", utils::Vector{
|
||||
Member("inner", ty.type_name("Inner")),
|
||||
});
|
||||
|
||||
auto* outer_with_runtime_sized_array =
|
||||
Structure("OuterRuntimeSizedArray", utils::Vector{
|
||||
Member("inner", ty.type_name("Inner")),
|
||||
Member("runtime_sized_array", ty.array<i32>()),
|
||||
});
|
||||
|
||||
auto p = Build();
|
||||
ASSERT_TRUE(p.IsValid()) << p.Diagnostics().str();
|
||||
|
||||
auto* sem_inner = p.Sem().Get(inner);
|
||||
auto* sem_outer = p.Sem().Get(outer);
|
||||
auto* sem_outer_with_runtime_sized_array = p.Sem().Get(outer_with_runtime_sized_array);
|
||||
|
||||
EXPECT_TRUE(sem_inner->HasCreationFixedFootprint());
|
||||
EXPECT_TRUE(sem_outer->HasCreationFixedFootprint());
|
||||
EXPECT_FALSE(sem_outer_with_runtime_sized_array->HasCreationFixedFootprint());
|
||||
}
|
||||
|
||||
TEST_F(TypeStructTest, HasFixedFootprint) {
|
||||
auto* inner = //
|
||||
Structure("Inner", utils::Vector{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.u32()),
|
||||
Member("c", ty.f32()),
|
||||
Member("d", ty.vec3<f32>()),
|
||||
Member("e", ty.mat4x2<f32>()),
|
||||
Member("f", ty.array<f32, 32>()),
|
||||
});
|
||||
|
||||
auto* outer = Structure("Outer", utils::Vector{
|
||||
Member("inner", ty.type_name("Inner")),
|
||||
});
|
||||
|
||||
auto* outer_with_runtime_sized_array =
|
||||
Structure("OuterRuntimeSizedArray", utils::Vector{
|
||||
Member("inner", ty.type_name("Inner")),
|
||||
Member("runtime_sized_array", ty.array<i32>()),
|
||||
});
|
||||
|
||||
auto p = Build();
|
||||
ASSERT_TRUE(p.IsValid()) << p.Diagnostics().str();
|
||||
|
||||
auto* sem_inner = p.Sem().Get(inner);
|
||||
auto* sem_outer = p.Sem().Get(outer);
|
||||
auto* sem_outer_with_runtime_sized_array = p.Sem().Get(outer_with_runtime_sized_array);
|
||||
|
||||
EXPECT_TRUE(sem_inner->HasFixedFootprint());
|
||||
EXPECT_TRUE(sem_outer->HasFixedFootprint());
|
||||
EXPECT_FALSE(sem_outer_with_runtime_sized_array->HasFixedFootprint());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint::type
|
|
@ -182,7 +182,7 @@ bool Type::HoldsAbstract() const {
|
|||
[&](const type::Vector* v) { return v->type()->HoldsAbstract(); },
|
||||
[&](const type::Matrix* m) { return m->type()->HoldsAbstract(); },
|
||||
[&](const sem::Array* a) { return a->ElemType()->HoldsAbstract(); },
|
||||
[&](const sem::Struct* s) {
|
||||
[&](const type::StructBase* s) {
|
||||
for (auto* m : s->Members()) {
|
||||
if (m->Type()->HoldsAbstract()) {
|
||||
return true;
|
||||
|
@ -240,7 +240,7 @@ uint32_t Type::ConversionRank(const Type* from, const Type* to) {
|
|||
}
|
||||
return kNoConversion;
|
||||
},
|
||||
[&](const sem::Struct* from_str) {
|
||||
[&](const type::StructBase* from_str) {
|
||||
auto concrete_tys = from_str->ConcreteTypes();
|
||||
for (size_t i = 0; i < concrete_tys.Length(); i++) {
|
||||
if (concrete_tys[i] == to) {
|
||||
|
|
|
@ -91,7 +91,7 @@ class TypeManager final {
|
|||
/// pointer is returned.
|
||||
template <typename TYPE,
|
||||
typename _ = std::enable_if<traits::IsTypeOrDerived<TYPE, type::ArrayCount> ||
|
||||
traits::IsTypeOrDerived<TYPE, sem::StructMemberBase>>,
|
||||
traits::IsTypeOrDerived<TYPE, type::StructMemberBase>>,
|
||||
typename... ARGS>
|
||||
TYPE* GetNode(ARGS&&... args) {
|
||||
return nodes_.Get<TYPE>(std::forward<ARGS>(args)...);
|
||||
|
@ -119,8 +119,8 @@ struct hash<tint::type::Node> {
|
|||
size_t operator()(const tint::type::Node& type) const {
|
||||
if (const auto* ac = type.As<tint::type::ArrayCount>()) {
|
||||
return ac->Hash();
|
||||
} else if (type.Is<tint::sem::StructMemberBase>()) {
|
||||
return tint::TypeInfo::Of<tint::sem::StructMemberBase>().full_hashcode;
|
||||
} else if (type.Is<tint::type::StructMemberBase>()) {
|
||||
return tint::TypeInfo::Of<tint::type::StructMemberBase>().full_hashcode;
|
||||
}
|
||||
TINT_ASSERT(Type, false && "Unreachable");
|
||||
return 0;
|
||||
|
@ -139,7 +139,7 @@ struct equal_to<tint::type::Node> {
|
|||
return ac->Equals(*bc);
|
||||
}
|
||||
return false;
|
||||
} else if (a.Is<tint::sem::StructMemberBase>()) {
|
||||
} else if (a.Is<tint::type::StructMemberBase>()) {
|
||||
return &a == &b;
|
||||
}
|
||||
TINT_ASSERT(Type, false && "Unreachable");
|
||||
|
|
|
@ -45,12 +45,10 @@ struct TypeTest : public TestHelper {
|
|||
const type::Matrix* mat4x3_af = create<type::Matrix>(vec3_af, 4u);
|
||||
const type::Reference* ref_u32 =
|
||||
create<type::Reference>(u32, ast::AddressSpace::kPrivate, ast::Access::kReadWrite);
|
||||
const sem::Struct* str_f32 = create<sem::Struct>(nullptr,
|
||||
Source{},
|
||||
const type::StructBase* str_f32 = create<type::StructBase>(Source{},
|
||||
Sym("str_f32"),
|
||||
utils::Vector{
|
||||
create<sem::StructMember>(
|
||||
/* declaration */ nullptr,
|
||||
create<type::StructMemberBase>(
|
||||
/* source */ Source{},
|
||||
/* name */ Sym("x"),
|
||||
/* type */ f32,
|
||||
|
@ -63,12 +61,10 @@ struct TypeTest : public TestHelper {
|
|||
/* align*/ 4u,
|
||||
/* size*/ 4u,
|
||||
/* size_no_padding*/ 4u);
|
||||
const sem::Struct* str_f16 = create<sem::Struct>(nullptr,
|
||||
Source{},
|
||||
const type::StructBase* str_f16 = create<type::StructBase>(Source{},
|
||||
Sym("str_f16"),
|
||||
utils::Vector{
|
||||
create<sem::StructMember>(
|
||||
/* declaration */ nullptr,
|
||||
create<type::StructMemberBase>(
|
||||
/* source */ Source{},
|
||||
/* name */ Sym("x"),
|
||||
/* type */ f16,
|
||||
|
@ -81,12 +77,10 @@ struct TypeTest : public TestHelper {
|
|||
/* align*/ 4u,
|
||||
/* size*/ 4u,
|
||||
/* size_no_padding*/ 4u);
|
||||
sem::Struct* str_af = create<sem::Struct>(nullptr,
|
||||
Source{},
|
||||
type::StructBase* str_af = create<type::StructBase>(Source{},
|
||||
Sym("str_af"),
|
||||
utils::Vector{
|
||||
create<sem::StructMember>(
|
||||
/* declaration */ nullptr,
|
||||
create<type::StructMemberBase>(
|
||||
/* source */ Source{},
|
||||
/* name */ Sym("x"),
|
||||
/* type */ af,
|
||||
|
|
|
@ -4155,16 +4155,16 @@ bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) {
|
|||
}
|
||||
|
||||
auto loc = mem->Location().value();
|
||||
if (pipeline_stage_uses.count(sem::PipelineStageUsage::kVertexInput)) {
|
||||
if (pipeline_stage_uses.count(type::PipelineStageUsage::kVertexInput)) {
|
||||
post += " : TEXCOORD" + std::to_string(loc);
|
||||
} else if (pipeline_stage_uses.count(
|
||||
sem::PipelineStageUsage::kVertexOutput)) {
|
||||
type::PipelineStageUsage::kVertexOutput)) {
|
||||
post += " : TEXCOORD" + std::to_string(loc);
|
||||
} else if (pipeline_stage_uses.count(
|
||||
sem::PipelineStageUsage::kFragmentInput)) {
|
||||
type::PipelineStageUsage::kFragmentInput)) {
|
||||
post += " : TEXCOORD" + std::to_string(loc);
|
||||
} else if (pipeline_stage_uses.count(
|
||||
sem::PipelineStageUsage::kFragmentOutput)) {
|
||||
type::PipelineStageUsage::kFragmentOutput)) {
|
||||
post += " : SV_Target" + std::to_string(loc);
|
||||
} else {
|
||||
TINT_ICE(Writer, diagnostics_) << "invalid use of location attribute";
|
||||
|
|
|
@ -2841,16 +2841,16 @@ bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) {
|
|||
}
|
||||
|
||||
uint32_t loc = mem->Location().value();
|
||||
if (pipeline_stage_uses.count(sem::PipelineStageUsage::kVertexInput)) {
|
||||
if (pipeline_stage_uses.count(type::PipelineStageUsage::kVertexInput)) {
|
||||
out << " [[attribute(" + std::to_string(loc) + ")]]";
|
||||
} else if (pipeline_stage_uses.count(
|
||||
sem::PipelineStageUsage::kVertexOutput)) {
|
||||
type::PipelineStageUsage::kVertexOutput)) {
|
||||
out << " [[user(locn" + std::to_string(loc) + ")]]";
|
||||
} else if (pipeline_stage_uses.count(
|
||||
sem::PipelineStageUsage::kFragmentInput)) {
|
||||
type::PipelineStageUsage::kFragmentInput)) {
|
||||
out << " [[user(locn" + std::to_string(loc) + ")]]";
|
||||
} else if (pipeline_stage_uses.count(
|
||||
sem::PipelineStageUsage::kFragmentOutput)) {
|
||||
type::PipelineStageUsage::kFragmentOutput)) {
|
||||
out << " [[color(" + std::to_string(loc) + ")]]";
|
||||
} else {
|
||||
TINT_ICE(Writer, diagnostics_) << "invalid use of location decoration";
|
||||
|
|
Loading…
Reference in New Issue