blob: e3038b4eff9c680875a7f1118e237341469206fb [file] [log] [blame]
// Copyright 2022 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "src/tint/lang/core/type/struct.h"
#include <cmath>
#include <iomanip>
#include <string>
#include <string_view>
#include <utility>
#include "src/tint/lang/core/type/manager.h"
#include "src/tint/utils/math/hash.h"
#include "src/tint/utils/symbol/symbol_table.h"
#include "src/tint/utils/text/string_stream.h"
#include "src/tint/utils/text/styled_text.h"
#include "src/tint/utils/text/text_style.h"
TINT_INSTANTIATE_TYPEINFO(tint::core::type::Struct);
TINT_INSTANTIATE_TYPEINFO(tint::core::type::StructMember);
namespace tint::core::type {
namespace {
Flags FlagsFrom(VectorRef<const StructMember*> members) {
Flags flags{
Flag::kConstructable,
Flag::kCreationFixedFootprint,
Flag::kFixedFootprint,
};
for (auto* member : members) {
if (!member->Type()->IsConstructible()) {
flags.Remove(Flag::kConstructable);
}
if (!member->Type()->HasFixedFootprint()) {
flags.Remove(Flag::kFixedFootprint);
}
if (!member->Type()->HasCreationFixedFootprint()) {
flags.Remove(Flag::kCreationFixedFootprint);
}
}
return flags;
}
} // namespace
Struct::Struct(Symbol name)
: Base(Hash(tint::TypeCode::Of<Struct>().bits, name), type::Flags{}),
name_(name),
members_{},
align_(0),
size_(0),
size_no_padding_(0) {}
Struct::Struct(Symbol name,
VectorRef<const StructMember*> members,
uint32_t align,
uint32_t size,
uint32_t size_no_padding)
: Base(Hash(tint::TypeCode::Of<Struct>().bits, name), FlagsFrom(members)),
name_(name),
members_(std::move(members)),
align_(align),
size_(size),
size_no_padding_(size_no_padding) {}
Struct::~Struct() = default;
bool Struct::Equals(const UniqueNode& other) const {
if (auto* o = other.As<Struct>()) {
return o->name_ == name_;
}
return false;
}
const StructMember* Struct::FindMember(Symbol name) const {
for (auto* member : members_) {
if (member->Name() == name) {
return member;
}
}
return nullptr;
}
uint32_t Struct::Align() const {
return align_;
}
uint32_t Struct::Size() const {
return size_;
}
std::string Struct::FriendlyName() const {
return name_.Name();
}
StyledText Struct::Layout() const {
static constexpr auto Code = style::Code + style::NoQuote;
static constexpr auto Comment = style::Comment + style::NoQuote;
static constexpr auto Keyword = style::Keyword + style::NoQuote;
static constexpr auto Type = style::Type + style::NoQuote;
static constexpr auto Variable = style::Variable + style::NoQuote;
static constexpr auto Plain = style::Plain;
StyledText out;
auto member_name_of = [&](const StructMember* sm) { return sm->Name().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 pad = [](int n) { return std::string(static_cast<size_t>(n), ' '); };
auto print_struct_begin_line = [&](size_t align, size_t size, std::string struct_name) {
out << Comment("/* ", pad(offset_w), "align(", std::setw(align_w), align,
") size(", std::setw(size_w), size, ") */")
<< Keyword(" struct ") << Type(struct_name) << Code(" {") << Plain("\n");
};
auto print_struct_end_line = [&] {
out << Comment("/* ", pad(offset_w + size_w + align_w), "*/")
<< Code(" };");
};
auto print_member_line = [&](size_t offset, size_t align, size_t size, StyledText s) {
out << Comment("/* offset(", std::setw(offset_w), offset, ") align(", std::setw(align_w),
align, ") size(", std::setw(size_w), size, ") */ ")
<< s << Plain("\n");
};
print_struct_begin_line(Align(), Size(), UnwrapRef()->FriendlyName());
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,
StyledText{}
<< Code(" ")
<< Comment("// -- implicit field alignment padding --"));
}
}
// Output member
std::string member_name = member_name_of(m);
print_member_line(m->Offset(), m->Align(), m->Size(),
StyledText{} << Code(" ") << Variable(member_name) << Code(" : ")
<< Type(m->Type()->UnwrapRef()->FriendlyName())
<< Code(","));
}
// 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,
StyledText{} << Code(" ") << Comment("// -- implicit struct size padding --"));
}
print_struct_end_line();
return out;
}
TypeAndCount Struct::Elements(const Type* type_if_invalid /* = nullptr */,
uint32_t /* count_if_invalid = 0 */) const {
return {type_if_invalid, static_cast<uint32_t>(members_.Length())};
}
const Type* Struct::Element(uint32_t index) const {
return index < members_.Length() ? members_[index]->Type() : nullptr;
}
Struct* Struct::Clone(CloneContext& ctx) const {
auto sym = ctx.dst.st->Register(name_.Name());
tint::Vector<const StructMember*, 4> members;
for (const auto& mem : members_) {
members.Push(mem->Clone(ctx));
}
return ctx.dst.mgr->Get<Struct>(sym, members, align_, size_, size_no_padding_);
}
StructMember::StructMember(Symbol name,
const core::type::Type* type,
uint32_t index,
uint32_t offset,
uint32_t align,
uint32_t size,
const StructMemberAttributes& attributes)
: name_(name),
type_(type),
index_(index),
offset_(offset),
align_(align),
size_(size),
attributes_(attributes) {}
StructMember::~StructMember() = default;
StructMember* StructMember::Clone(CloneContext& ctx) const {
auto sym = ctx.dst.st->Register(name_.Name());
auto* ty = type_->Clone(ctx);
return ctx.dst.mgr->Get<StructMember>(sym, ty, index_, offset_, align_, size_, attributes_);
}
} // namespace tint::core::type