| // 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_ != nullptr) { |
| // Lazy deserialization. |
| DCHECK_NULL(handler_allocations_); |
| return handler_allocation_; |
| } else { |
| // Eager deserialization. |
| DCHECK_NULL(handler_allocation_); |
| DCHECK_NOT_NULL(handler_allocations_); |
| int index = HandlerAllocationIndex(code_object_id); |
| DCHECK_NOT_NULL(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, nullptr, nullptr}); |
| } |
| |
| 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, nullptr, nullptr}); |
| } |
| |
| // 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, nullptr, nullptr}); |
| }); |
| |
| 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 |