blob: 4e3d546fa0a010fb6f95209d3f27f6469b5a8aad [file] [log] [blame]
// Copyright 2017 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/snapshot/builtin-deserializer-allocator.h"
#include "src/heap/heap-inl.h"
#include "src/interpreter/interpreter.h"
#include "src/snapshot/builtin-deserializer.h"
#include "src/snapshot/deserializer.h"
namespace v8 {
namespace internal {
using interpreter::Bytecodes;
using interpreter::Interpreter;
BuiltinDeserializerAllocator::BuiltinDeserializerAllocator(
Deserializer<BuiltinDeserializerAllocator>* deserializer)
: deserializer_(deserializer) {}
BuiltinDeserializerAllocator::~BuiltinDeserializerAllocator() {
delete handler_allocations_;
}
namespace {
int HandlerAllocationIndex(int code_object_id) {
return code_object_id - BuiltinSnapshotUtils::kFirstHandlerIndex;
}
} // namespace
Address BuiltinDeserializerAllocator::Allocate(AllocationSpace space,
int size) {
const int code_object_id = deserializer()->CurrentCodeObjectId();
DCHECK_NE(BuiltinDeserializer::kNoCodeObjectId, code_object_id);
DCHECK_EQ(CODE_SPACE, space);
DCHECK_EQ(deserializer()->ExtractCodeObjectSize(code_object_id), size);
#ifdef DEBUG
RegisterCodeObjectAllocation(code_object_id);
#endif
if (BSU::IsBuiltinIndex(code_object_id)) {
Object* obj = isolate()->builtins()->builtin(code_object_id);
DCHECK(Internals::HasHeapObjectTag(obj));
return HeapObject::cast(obj)->address();
} else if (BSU::IsHandlerIndex(code_object_id)) {
if (handler_allocation_ != kNullAddress) {
// Lazy deserialization.
DCHECK_NULL(handler_allocations_);
return handler_allocation_;
} else {
// Eager deserialization.
DCHECK_EQ(kNullAddress, handler_allocation_);
DCHECK_NOT_NULL(handler_allocations_);
int index = HandlerAllocationIndex(code_object_id);
DCHECK_NE(kNullAddress, handler_allocations_->at(index));
return handler_allocations_->at(index);
}
}
UNREACHABLE();
}
Heap::Reservation
BuiltinDeserializerAllocator::CreateReservationsForEagerBuiltinsAndHandlers() {
Heap::Reservation result;
// Reservations for builtins.
// DeserializeLazy is always the first builtin reservation (to simplify logic
// in InitializeBuiltinsTable).
{
DCHECK(!Builtins::IsLazy(Builtins::kDeserializeLazy));
uint32_t builtin_size =
deserializer()->ExtractCodeObjectSize(Builtins::kDeserializeLazy);
DCHECK_LE(builtin_size, MemoryAllocator::PageAreaSize(CODE_SPACE));
result.push_back({builtin_size, kNullAddress, kNullAddress});
}
for (int i = 0; i < BSU::kNumberOfBuiltins; i++) {
if (i == Builtins::kDeserializeLazy) continue;
// Skip lazy builtins. These will be replaced by the DeserializeLazy code
// object in InitializeFromReservations and thus require no reserved space.
if (deserializer()->IsLazyDeserializationEnabled() && Builtins::IsLazy(i)) {
continue;
}
uint32_t builtin_size = deserializer()->ExtractCodeObjectSize(i);
DCHECK_LE(builtin_size, MemoryAllocator::PageAreaSize(CODE_SPACE));
result.push_back({builtin_size, kNullAddress, kNullAddress});
}
// Reservations for bytecode handlers.
BSU::ForEachBytecode(
[=, &result](Bytecode bytecode, OperandScale operand_scale) {
if (!Bytecodes::BytecodeHasHandler(bytecode, operand_scale)) {
// Bytecodes without a handler don't require a reservation.
return;
} else if (FLAG_lazy_handler_deserialization &&
deserializer()->IsLazyDeserializationEnabled() &&
Bytecodes::IsLazy(bytecode)) {
// Skip lazy handlers. These will be replaced by the DeserializeLazy
// code object in InitializeFromReservations and thus require no
// reserved space.
return;
}
const int index = BSU::BytecodeToIndex(bytecode, operand_scale);
uint32_t handler_size = deserializer()->ExtractCodeObjectSize(index);
DCHECK_LE(handler_size, MemoryAllocator::PageAreaSize(CODE_SPACE));
result.push_back({handler_size, kNullAddress, kNullAddress});
});
return result;
}
void BuiltinDeserializerAllocator::InitializeBuiltinFromReservation(
const Heap::Chunk& chunk, int builtin_id) {
DCHECK_EQ(deserializer()->ExtractCodeObjectSize(builtin_id), chunk.size);
DCHECK_EQ(chunk.size, chunk.end - chunk.start);
SkipList::Update(chunk.start, chunk.size);
isolate()->builtins()->set_builtin(builtin_id,
HeapObject::FromAddress(chunk.start));
#ifdef DEBUG
RegisterCodeObjectReservation(builtin_id);
#endif
}
void BuiltinDeserializerAllocator::InitializeHandlerFromReservation(
const Heap::Chunk& chunk, interpreter::Bytecode bytecode,
interpreter::OperandScale operand_scale) {
DCHECK_EQ(deserializer()->ExtractCodeObjectSize(
BSU::BytecodeToIndex(bytecode, operand_scale)),
chunk.size);
DCHECK_EQ(chunk.size, chunk.end - chunk.start);
SkipList::Update(chunk.start, chunk.size);
DCHECK_NOT_NULL(handler_allocations_);
const int index =
HandlerAllocationIndex(BSU::BytecodeToIndex(bytecode, operand_scale));
handler_allocations_->at(index) = chunk.start;
#ifdef DEBUG
RegisterCodeObjectReservation(BSU::BytecodeToIndex(bytecode, operand_scale));
#endif
}
void BuiltinDeserializerAllocator::InitializeFromReservations(
const Heap::Reservation& reservation) {
DCHECK(!AllowHeapAllocation::IsAllowed());
// Initialize the builtins table.
Builtins* builtins = isolate()->builtins();
int reservation_index = 0;
// Other builtins can be replaced by DeserializeLazy so it may not be lazy.
// It always occupies the first reservation slot.
{
DCHECK(!Builtins::IsLazy(Builtins::kDeserializeLazy));
InitializeBuiltinFromReservation(reservation[reservation_index],
Builtins::kDeserializeLazy);
reservation_index++;
}
Code* deserialize_lazy = builtins->builtin(Builtins::kDeserializeLazy);
for (int i = 0; i < BSU::kNumberOfBuiltins; i++) {
if (i == Builtins::kDeserializeLazy) continue;
if (deserializer()->IsLazyDeserializationEnabled() && Builtins::IsLazy(i)) {
builtins->set_builtin(i, deserialize_lazy);
} else {
InitializeBuiltinFromReservation(reservation[reservation_index], i);
reservation_index++;
}
}
// Initialize interpreter bytecode handler reservations.
DCHECK_NULL(handler_allocations_);
handler_allocations_ = new std::vector<Address>(BSU::kNumberOfHandlers);
BSU::ForEachBytecode(
[=, &reservation_index](Bytecode bytecode, OperandScale operand_scale) {
if (!Bytecodes::BytecodeHasHandler(bytecode, operand_scale)) {
// Bytecodes without a handler don't have a reservation.
return;
} else if (FLAG_lazy_handler_deserialization &&
deserializer()->IsLazyDeserializationEnabled() &&
Bytecodes::IsLazy(bytecode)) {
// Likewise, bytecodes with lazy handlers don't either.
return;
}
InitializeHandlerFromReservation(reservation[reservation_index],
bytecode, operand_scale);
reservation_index++;
});
DCHECK_EQ(reservation.size(), reservation_index);
}
void BuiltinDeserializerAllocator::ReserveAndInitializeBuiltinsTableForBuiltin(
int builtin_id) {
DCHECK(AllowHeapAllocation::IsAllowed());
DCHECK(isolate()->builtins()->is_initialized());
DCHECK(Builtins::IsBuiltinId(builtin_id));
DCHECK_NE(Builtins::kDeserializeLazy, builtin_id);
DCHECK_EQ(Builtins::kDeserializeLazy,
isolate()->builtins()->builtin(builtin_id)->builtin_index());
const uint32_t builtin_size =
deserializer()->ExtractCodeObjectSize(builtin_id);
DCHECK_LE(builtin_size, MemoryAllocator::PageAreaSize(CODE_SPACE));
Handle<HeapObject> o =
isolate()->factory()->NewCodeForDeserialization(builtin_size);
// Note: After this point and until deserialization finishes, heap allocation
// is disallowed. We currently can't safely assert this since we'd need to
// pass the DisallowHeapAllocation scope out of this function.
// Write the allocated filler object into the builtins table. It will be
// returned by our custom Allocate method below once needed.
isolate()->builtins()->set_builtin(builtin_id, *o);
#ifdef DEBUG
RegisterCodeObjectReservation(builtin_id);
#endif
}
void BuiltinDeserializerAllocator::ReserveForHandler(
Bytecode bytecode, OperandScale operand_scale) {
DCHECK(AllowHeapAllocation::IsAllowed());
DCHECK(isolate()->interpreter()->IsDispatchTableInitialized());
const int code_object_id = BSU::BytecodeToIndex(bytecode, operand_scale);
const uint32_t handler_size =
deserializer()->ExtractCodeObjectSize(code_object_id);
DCHECK_LE(handler_size, MemoryAllocator::PageAreaSize(CODE_SPACE));
handler_allocation_ =
isolate()->factory()->NewCodeForDeserialization(handler_size)->address();
// Note: After this point and until deserialization finishes, heap allocation
// is disallowed. We currently can't safely assert this since we'd need to
// pass the DisallowHeapAllocation scope out of this function.
#ifdef DEBUG
RegisterCodeObjectReservation(code_object_id);
#endif
}
#ifdef DEBUG
void BuiltinDeserializerAllocator::RegisterCodeObjectReservation(
int code_object_id) {
const auto result = unused_reservations_.emplace(code_object_id);
CHECK(result.second); // False, iff builtin_id was already present in set.
}
void BuiltinDeserializerAllocator::RegisterCodeObjectAllocation(
int code_object_id) {
const size_t removed_elems = unused_reservations_.erase(code_object_id);
CHECK_EQ(removed_elems, 1);
}
bool BuiltinDeserializerAllocator::ReservationsAreFullyUsed() const {
// Not 100% precise but should be good enough.
return unused_reservations_.empty();
}
#endif // DEBUG
Isolate* BuiltinDeserializerAllocator::isolate() const {
return deserializer()->isolate();
}
BuiltinDeserializer* BuiltinDeserializerAllocator::deserializer() const {
return static_cast<BuiltinDeserializer*>(deserializer_);
}
} // namespace internal
} // namespace v8