blob: d2b799562383dd87c5210509bff00061436d6573 [file] [log] [blame]
// Copyright 2015 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.
#include "src/interpreter/constant-array-builder.h"
#include <functional>
#include <set>
#include "src/isolate.h"
#include "src/objects-inl.h"
namespace v8 {
namespace internal {
namespace interpreter {
ConstantArrayBuilder::ConstantArraySlice::ConstantArraySlice(
Zone* zone, size_t start_index, size_t capacity, OperandSize operand_size)
: start_index_(start_index),
capacity_(capacity),
reserved_(0),
operand_size_(operand_size),
constants_(zone) {}
void ConstantArrayBuilder::ConstantArraySlice::Reserve() {
DCHECK_GT(available(), 0u);
reserved_++;
DCHECK_LE(reserved_, capacity() - constants_.size());
}
void ConstantArrayBuilder::ConstantArraySlice::Unreserve() {
DCHECK_GT(reserved_, 0u);
reserved_--;
}
size_t ConstantArrayBuilder::ConstantArraySlice::Allocate(
Handle<Object> object) {
DCHECK_GT(available(), 0u);
size_t index = constants_.size();
DCHECK_LT(index, capacity());
constants_.push_back(object);
return index + start_index();
}
Handle<Object> ConstantArrayBuilder::ConstantArraySlice::At(
size_t index) const {
DCHECK_GE(index, start_index());
DCHECK_LT(index, start_index() + size());
return constants_[index - start_index()];
}
void ConstantArrayBuilder::ConstantArraySlice::InsertAt(size_t index,
Handle<Object> object) {
DCHECK_GE(index, start_index());
DCHECK_LT(index, start_index() + size());
constants_[index - start_index()] = object;
}
bool ConstantArrayBuilder::ConstantArraySlice::AllElementsAreUnique() const {
std::set<Object*> elements;
for (auto constant : constants_) {
if (elements.find(*constant) != elements.end()) return false;
elements.insert(*constant);
}
return true;
}
STATIC_CONST_MEMBER_DEFINITION const size_t ConstantArrayBuilder::k8BitCapacity;
STATIC_CONST_MEMBER_DEFINITION const size_t
ConstantArrayBuilder::k16BitCapacity;
STATIC_CONST_MEMBER_DEFINITION const size_t
ConstantArrayBuilder::k32BitCapacity;
ConstantArrayBuilder::ConstantArrayBuilder(Zone* zone,
Handle<Object> the_hole_value)
: constants_map_(16, base::KeyEqualityMatcher<Address>(),
ZoneAllocationPolicy(zone)),
smi_map_(zone),
smi_pairs_(zone),
zone_(zone),
the_hole_value_(the_hole_value) {
idx_slice_[0] =
new (zone) ConstantArraySlice(zone, 0, k8BitCapacity, OperandSize::kByte);
idx_slice_[1] = new (zone) ConstantArraySlice(
zone, k8BitCapacity, k16BitCapacity, OperandSize::kShort);
idx_slice_[2] = new (zone) ConstantArraySlice(
zone, k8BitCapacity + k16BitCapacity, k32BitCapacity, OperandSize::kQuad);
}
size_t ConstantArrayBuilder::size() const {
size_t i = arraysize(idx_slice_);
while (i > 0) {
ConstantArraySlice* slice = idx_slice_[--i];
if (slice->size() > 0) {
return slice->start_index() + slice->size();
}
}
return idx_slice_[0]->size();
}
ConstantArrayBuilder::ConstantArraySlice* ConstantArrayBuilder::IndexToSlice(
size_t index) const {
for (ConstantArraySlice* slice : idx_slice_) {
if (index <= slice->max_index()) {
return slice;
}
}
UNREACHABLE();
return nullptr;
}
Handle<Object> ConstantArrayBuilder::At(size_t index) const {
const ConstantArraySlice* slice = IndexToSlice(index);
if (index < slice->start_index() + slice->size()) {
return slice->At(index);
} else {
DCHECK_LT(index, slice->capacity());
return the_hole_value();
}
}
Handle<FixedArray> ConstantArrayBuilder::ToFixedArray(Isolate* isolate) {
// First insert reserved SMI values.
for (auto reserved_smi : smi_pairs_) {
InsertAllocatedEntry(reserved_smi.second,
handle(reserved_smi.first, isolate));
}
Handle<FixedArray> fixed_array = isolate->factory()->NewFixedArray(
static_cast<int>(size()), PretenureFlag::TENURED);
int array_index = 0;
for (const ConstantArraySlice* slice : idx_slice_) {
if (array_index == fixed_array->length()) {
break;
}
DCHECK(array_index == 0 ||
base::bits::IsPowerOfTwo32(static_cast<uint32_t>(array_index)));
// Different slices might contain the same element due to reservations, but
// all elements within a slice should be unique. If this DCHECK fails, then
// the AST nodes are not being internalized within a CanonicalHandleScope.
DCHECK(slice->AllElementsAreUnique());
// Copy objects from slice into array.
for (size_t i = 0; i < slice->size(); ++i) {
fixed_array->set(array_index++, *slice->At(slice->start_index() + i));
}
// Insert holes where reservations led to unused slots.
size_t padding =
std::min(static_cast<size_t>(fixed_array->length() - array_index),
slice->capacity() - slice->size());
for (size_t i = 0; i < padding; i++) {
fixed_array->set(array_index++, *the_hole_value());
}
}
DCHECK_EQ(array_index, fixed_array->length());
return fixed_array;
}
size_t ConstantArrayBuilder::Insert(Handle<Object> object) {
return constants_map_
.LookupOrInsert(object.address(), ObjectHash(object.address()),
[&]() { return AllocateIndex(object); },
ZoneAllocationPolicy(zone_))
->value;
}
ConstantArrayBuilder::index_t ConstantArrayBuilder::AllocateIndex(
Handle<Object> object) {
for (size_t i = 0; i < arraysize(idx_slice_); ++i) {
if (idx_slice_[i]->available() > 0) {
return static_cast<index_t>(idx_slice_[i]->Allocate(object));
}
}
UNREACHABLE();
return kMaxUInt32;
}
ConstantArrayBuilder::ConstantArraySlice*
ConstantArrayBuilder::OperandSizeToSlice(OperandSize operand_size) const {
ConstantArraySlice* slice = nullptr;
switch (operand_size) {
case OperandSize::kNone:
UNREACHABLE();
break;
case OperandSize::kByte:
slice = idx_slice_[0];
break;
case OperandSize::kShort:
slice = idx_slice_[1];
break;
case OperandSize::kQuad:
slice = idx_slice_[2];
break;
}
DCHECK(slice->operand_size() == operand_size);
return slice;
}
size_t ConstantArrayBuilder::AllocateEntry() {
return AllocateIndex(the_hole_value());
}
void ConstantArrayBuilder::InsertAllocatedEntry(size_t index,
Handle<Object> object) {
DCHECK_EQ(the_hole_value().address(), At(index).address());
ConstantArraySlice* slice = IndexToSlice(index);
slice->InsertAt(index, object);
}
OperandSize ConstantArrayBuilder::CreateReservedEntry() {
for (size_t i = 0; i < arraysize(idx_slice_); ++i) {
if (idx_slice_[i]->available() > 0) {
idx_slice_[i]->Reserve();
return idx_slice_[i]->operand_size();
}
}
UNREACHABLE();
return OperandSize::kNone;
}
ConstantArrayBuilder::index_t ConstantArrayBuilder::AllocateReservedEntry(
Smi* value) {
index_t index = static_cast<index_t>(AllocateEntry());
smi_map_[value] = index;
smi_pairs_.push_back(std::make_pair(value, index));
return index;
}
size_t ConstantArrayBuilder::CommitReservedEntry(OperandSize operand_size,
Smi* value) {
DiscardReservedEntry(operand_size);
size_t index;
auto entry = smi_map_.find(value);
if (entry == smi_map_.end()) {
index = AllocateReservedEntry(value);
} else {
ConstantArraySlice* slice = OperandSizeToSlice(operand_size);
index = entry->second;
if (index > slice->max_index()) {
// The object is already in the constant array, but may have an
// index too big for the reserved operand_size. So, duplicate
// entry with the smaller operand size.
index = AllocateReservedEntry(value);
}
DCHECK_LE(index, slice->max_index());
}
return index;
}
void ConstantArrayBuilder::DiscardReservedEntry(OperandSize operand_size) {
OperandSizeToSlice(operand_size)->Unreserve();
}
} // namespace interpreter
} // namespace internal
} // namespace v8