blob: a4632bf1962891255c3be61bac14ab1a793ca6c2 [file] [log] [blame]
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#if !V8_ENABLE_WEBASSEMBLY
#error This header should only be included if WebAssembly is enabled.
#endif // !V8_ENABLE_WEBASSEMBLY
#ifndef V8_WASM_STRUCT_TYPES_H_
#define V8_WASM_STRUCT_TYPES_H_
#include "src/base/iterator.h"
#include "src/base/macros.h"
#include "src/common/globals.h"
#include "src/wasm/value-type.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
namespace wasm {
class StructType : public ZoneObject {
public:
StructType(uint32_t field_count, uint32_t* field_offsets,
const ValueType* reps, const bool* mutabilities)
: field_count_(field_count),
field_offsets_(field_offsets),
reps_(reps),
mutabilities_(mutabilities) {}
uint32_t field_count() const { return field_count_; }
ValueType field(uint32_t index) const {
DCHECK_LT(index, field_count_);
return reps_[index];
}
bool mutability(uint32_t index) const {
DCHECK_LT(index, field_count_);
return mutabilities_[index];
}
// Iteration support.
base::iterator_range<const ValueType*> fields() const {
return {reps_, reps_ + field_count_};
}
base::iterator_range<const bool*> mutabilities() const {
return {mutabilities_, mutabilities_ + field_count_};
}
bool operator==(const StructType& other) const {
if (this == &other) return true;
if (field_count() != other.field_count()) return false;
return std::equal(fields().begin(), fields().end(),
other.fields().begin()) &&
std::equal(mutabilities().begin(), mutabilities().end(),
other.mutabilities().begin());
}
bool operator!=(const StructType& other) const { return !(*this == other); }
// Returns the offset of this field in the runtime representation of the
// object, from the start of the object fields (disregarding the object
// header).
uint32_t field_offset(uint32_t index) const {
DCHECK_LT(index, field_count());
if (index == 0) return 0;
DCHECK(offsets_initialized_);
return field_offsets_[index - 1];
}
uint32_t total_fields_size() const {
return field_count() == 0 ? 0 : field_offsets_[field_count() - 1];
}
uint32_t Align(uint32_t offset, uint32_t alignment) {
return RoundUp(offset, std::min(alignment, uint32_t{kTaggedSize}));
}
void InitializeOffsets() {
if (field_count() == 0) return;
DCHECK(!offsets_initialized_);
uint32_t offset = field(0).value_kind_size();
// Optimization: we track the last gap that was introduced by alignment,
// and place any sufficiently-small fields in it.
// It's important that the algorithm that assigns offsets to fields is
// subtyping-safe, i.e. two lists of fields with a common prefix must
// always compute the same offsets for the fields in this common prefix.
uint32_t gap_position = 0;
uint32_t gap_size = 0;
for (uint32_t i = 1; i < field_count(); i++) {
uint32_t field_size = field(i).value_kind_size();
if (field_size <= gap_size) {
uint32_t aligned_gap = Align(gap_position, field_size);
uint32_t gap_before = aligned_gap - gap_position;
uint32_t aligned_gap_size = gap_size - gap_before;
if (field_size <= aligned_gap_size) {
field_offsets_[i - 1] = aligned_gap;
uint32_t gap_after = aligned_gap_size - field_size;
if (gap_before > gap_after) {
// Keep old {gap_position}.
gap_size = gap_before;
} else {
gap_position = aligned_gap + field_size;
gap_size = gap_after;
}
continue; // Successfully placed the field in the gap.
}
}
uint32_t old_offset = offset;
offset = Align(offset, field_size);
uint32_t gap = offset - old_offset;
if (gap > gap_size) {
gap_size = gap;
gap_position = old_offset;
}
field_offsets_[i - 1] = offset;
offset += field_size;
}
offset = RoundUp(offset, kTaggedSize);
field_offsets_[field_count() - 1] = offset;
#if DEBUG
offsets_initialized_ = true;
#endif
}
// For incrementally building StructTypes.
class Builder {
public:
enum ComputeOffsets : bool {
kComputeOffsets = true,
kUseProvidedOffsets = false
};
Builder(Zone* zone, uint32_t field_count)
: zone_(zone),
field_count_(field_count),
cursor_(0),
field_offsets_(zone_->AllocateArray<uint32_t>(field_count_)),
buffer_(
zone->AllocateArray<ValueType>(static_cast<int>(field_count))),
mutabilities_(
zone->AllocateArray<bool>(static_cast<int>(field_count))) {}
void AddField(ValueType type, bool mutability, uint32_t offset = 0) {
DCHECK_LT(cursor_, field_count_);
if (cursor_ > 0) {
field_offsets_[cursor_ - 1] = offset;
} else {
DCHECK_EQ(0, offset); // First field always has offset 0.
}
mutabilities_[cursor_] = mutability;
buffer_[cursor_++] = type;
}
void set_total_fields_size(uint32_t size) {
if (field_count_ == 0) {
DCHECK_EQ(0, size);
return;
}
field_offsets_[field_count_ - 1] = size;
}
StructType* Build(ComputeOffsets compute_offsets = kComputeOffsets) {
DCHECK_EQ(cursor_, field_count_);
StructType* result = zone_->New<StructType>(field_count_, field_offsets_,
buffer_, mutabilities_);
if (compute_offsets == kComputeOffsets) {
result->InitializeOffsets();
} else {
#if DEBUG
bool offsets_specified = true;
for (uint32_t i = 0; i < field_count_; i++) {
if (field_offsets_[i] == 0) {
offsets_specified = false;
break;
}
}
result->offsets_initialized_ = offsets_specified;
#endif
}
return result;
}
private:
Zone* const zone_;
const uint32_t field_count_;
uint32_t cursor_;
uint32_t* field_offsets_;
ValueType* const buffer_;
bool* const mutabilities_;
};
static const size_t kMaxFieldOffset =
(kV8MaxWasmStructFields - 1) * kMaxValueTypeSize;
private:
const uint32_t field_count_;
#if DEBUG
bool offsets_initialized_ = false;
#endif
uint32_t* const field_offsets_;
const ValueType* const reps_;
const bool* const mutabilities_;
};
inline std::ostream& operator<<(std::ostream& out, StructType type) {
out << "[";
for (ValueType field : type.fields()) {
out << field.name() << ", ";
}
out << "]";
return out;
}
// Support base::hash<StructType>.
inline size_t hash_value(const StructType& type) {
return base::Hasher{}
.Add(type.field_count())
.AddRange(type.fields())
.AddRange(type.mutabilities())
.hash();
}
class ArrayType : public ZoneObject {
public:
constexpr explicit ArrayType(ValueType rep, bool mutability)
: rep_(rep), mutability_(mutability) {}
ValueType element_type() const { return rep_; }
bool mutability() const { return mutability_; }
bool operator==(const ArrayType& other) const {
return rep_ == other.rep_ && mutability_ == other.mutability_;
}
bool operator!=(const ArrayType& other) const {
return rep_ != other.rep_ || mutability_ != other.mutability_;
}
static const intptr_t kRepOffset;
private:
const ValueType rep_;
const bool mutability_;
};
inline constexpr intptr_t ArrayType::kRepOffset = offsetof(ArrayType, rep_);
// Support base::hash<ArrayType>.
inline size_t hash_value(const ArrayType& type) {
return base::Hasher::Combine(type.element_type(), type.mutability());
}
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_WASM_STRUCT_TYPES_H_