Reland "[builtins] Remove off-heap builtins from the snapshot"
This is a reland of f1b1ec70a6
Original change's description:
> [builtins] Remove off-heap builtins from the snapshot
>
> This CL is the final major step towards shipping off-heap-safe builtins
> embedded into the binary.
>
> Prior to snapshot serialization, we now:
> * create the embedded blob containing off-heap instruction streams,
> * use that to generate embedded.cc (containing embedded binary data),
> * replace off-heap-safe builtins with trampolines,
> * and serialize those into the final snapshot.
>
> The new RelocInfo::OFF_HEAP_TARGET kind is used to fix up trampoline
> targets on deserialization.
>
> Bug: v8:6666
> Change-Id: Ib07aea9e3bd7ecdec42291c1388b3a7453ea96ce
> Reviewed-on: https://chromium-review.googlesource.com/950775
> Commit-Queue: Jakob Gruber <jgruber@chromium.org>
> Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
> Reviewed-by: Yang Guo <yangguo@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#51960}
TBR=yangguo@chromium.org,mstarzinger@chromium.org
Cq-Include-Trybots: luci.v8.try:v8_mac64_dbg,v8_mac64_rel
Bug: v8:6666
Change-Id: Id9954af3c8195754ff3658c4603858904fcf88c4
Reviewed-on: https://chromium-review.googlesource.com/964481
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52006}
This commit is contained in:
parent
6031412e0a
commit
fd70917d52
@ -73,7 +73,8 @@ Address RelocInfo::target_address() {
|
||||
|
||||
Address RelocInfo::target_address_address() {
|
||||
DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) || IsWasmCall(rmode_) ||
|
||||
rmode_ == EMBEDDED_OBJECT || rmode_ == EXTERNAL_REFERENCE);
|
||||
IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) ||
|
||||
IsOffHeapTarget(rmode_));
|
||||
if (Assembler::IsMovW(Memory::int32_at(pc_))) {
|
||||
return reinterpret_cast<Address>(pc_);
|
||||
} else {
|
||||
@ -151,6 +152,11 @@ void RelocInfo::set_target_runtime_entry(Address target,
|
||||
set_target_address(target, write_barrier_mode, icache_flush_mode);
|
||||
}
|
||||
|
||||
Address RelocInfo::target_off_heap_target() {
|
||||
DCHECK(IsOffHeapTarget(rmode_));
|
||||
return Assembler::target_address_at(pc_, constant_pool_);
|
||||
}
|
||||
|
||||
void RelocInfo::WipeOut() {
|
||||
DCHECK(IsEmbeddedObject(rmode_) || IsCodeTarget(rmode_) ||
|
||||
IsRuntimeEntry(rmode_) || IsExternalReference(rmode_) ||
|
||||
@ -175,6 +181,8 @@ void RelocInfo::Visit(ObjectVisitor* visitor) {
|
||||
visitor->VisitInternalReference(host(), this);
|
||||
} else if (RelocInfo::IsRuntimeEntry(mode)) {
|
||||
visitor->VisitRuntimeEntry(host(), this);
|
||||
} else if (RelocInfo::IsOffHeapTarget(mode)) {
|
||||
visitor->VisitOffHeapTarget(host(), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1717,7 +1717,7 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin,
|
||||
|
||||
void MacroAssembler::JumpToInstructionStream(Address entry) {
|
||||
mov(kOffHeapTrampolineRegister,
|
||||
Operand(reinterpret_cast<int32_t>(entry), RelocInfo::NONE));
|
||||
Operand(reinterpret_cast<int32_t>(entry), RelocInfo::OFF_HEAP_TARGET));
|
||||
Jump(kOffHeapTrampolineRegister);
|
||||
}
|
||||
|
||||
|
@ -613,7 +613,8 @@ Address RelocInfo::target_address() {
|
||||
|
||||
Address RelocInfo::target_address_address() {
|
||||
DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) || IsWasmCall(rmode_) ||
|
||||
rmode_ == EMBEDDED_OBJECT || rmode_ == EXTERNAL_REFERENCE);
|
||||
IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) ||
|
||||
IsOffHeapTarget(rmode_));
|
||||
return Assembler::target_pointer_address_at(pc_);
|
||||
}
|
||||
|
||||
@ -682,6 +683,11 @@ void RelocInfo::set_target_runtime_entry(Address target,
|
||||
}
|
||||
}
|
||||
|
||||
Address RelocInfo::target_off_heap_target() {
|
||||
DCHECK(IsOffHeapTarget(rmode_));
|
||||
return Assembler::target_address_at(pc_, constant_pool_);
|
||||
}
|
||||
|
||||
void RelocInfo::WipeOut() {
|
||||
DCHECK(IsEmbeddedObject(rmode_) || IsCodeTarget(rmode_) ||
|
||||
IsRuntimeEntry(rmode_) || IsExternalReference(rmode_) ||
|
||||
@ -706,6 +712,8 @@ void RelocInfo::Visit(ObjectVisitor* visitor) {
|
||||
visitor->VisitInternalReference(host(), this);
|
||||
} else if (RelocInfo::IsRuntimeEntry(mode)) {
|
||||
visitor->VisitRuntimeEntry(host(), this);
|
||||
} else if (RelocInfo::IsOffHeapTarget(mode)) {
|
||||
visitor->VisitOffHeapTarget(host(), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1760,7 +1760,8 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin,
|
||||
}
|
||||
|
||||
void MacroAssembler::JumpToInstructionStream(Address entry) {
|
||||
Mov(kOffHeapTrampolineRegister, reinterpret_cast<uint64_t>(entry));
|
||||
Mov(kOffHeapTrampolineRegister,
|
||||
Operand(reinterpret_cast<uint64_t>(entry), RelocInfo::OFF_HEAP_TARGET));
|
||||
Br(kOffHeapTrampolineRegister);
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "src/code-stubs.h"
|
||||
#include "src/deoptimizer.h"
|
||||
#include "src/disassembler.h"
|
||||
#include "src/instruction-stream.h"
|
||||
#include "src/isolate.h"
|
||||
#include "src/ostreams.h"
|
||||
#include "src/simulator.h" // For flushing instruction cache.
|
||||
@ -520,6 +521,8 @@ const char* RelocInfo::RelocModeName(RelocInfo::Mode rmode) {
|
||||
return "internal reference";
|
||||
case INTERNAL_REFERENCE_ENCODED:
|
||||
return "encoded internal reference";
|
||||
case OFF_HEAP_TARGET:
|
||||
return "off heap target";
|
||||
case DEOPT_SCRIPT_OFFSET:
|
||||
return "deopt script offset";
|
||||
case DEOPT_INLINING_ID:
|
||||
@ -621,6 +624,12 @@ void RelocInfo::Verify(Isolate* isolate) {
|
||||
CHECK(target <= code->instruction_end());
|
||||
break;
|
||||
}
|
||||
case OFF_HEAP_TARGET: {
|
||||
Address addr = target_off_heap_target();
|
||||
CHECK_NOT_NULL(addr);
|
||||
CHECK_NOT_NULL(InstructionStream::TryLookupCode(isolate, addr));
|
||||
break;
|
||||
}
|
||||
case RUNTIME_ENTRY:
|
||||
case COMMENT:
|
||||
case EXTERNAL_REFERENCE:
|
||||
|
@ -379,6 +379,9 @@ class RelocInfo {
|
||||
// Encoded internal reference, used only on MIPS, MIPS64 and PPC.
|
||||
INTERNAL_REFERENCE_ENCODED,
|
||||
|
||||
// An off-heap instruction stream target. See http://goo.gl/Z2HUiM.
|
||||
OFF_HEAP_TARGET,
|
||||
|
||||
// Marks constant and veneer pools. Only used on ARM and ARM64.
|
||||
// They use a custom noncompact encoding.
|
||||
CONST_POOL,
|
||||
@ -455,6 +458,9 @@ class RelocInfo {
|
||||
static inline bool IsInternalReferenceEncoded(Mode mode) {
|
||||
return mode == INTERNAL_REFERENCE_ENCODED;
|
||||
}
|
||||
static inline bool IsOffHeapTarget(Mode mode) {
|
||||
return mode == OFF_HEAP_TARGET;
|
||||
}
|
||||
static inline bool IsNone(Mode mode) { return mode == NONE; }
|
||||
static inline bool IsWasmContextReference(Mode mode) {
|
||||
return mode == WASM_CONTEXT_REFERENCE;
|
||||
@ -529,6 +535,7 @@ class RelocInfo {
|
||||
Address target,
|
||||
WriteBarrierMode write_barrier_mode = UPDATE_WRITE_BARRIER,
|
||||
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED));
|
||||
INLINE(Address target_off_heap_target());
|
||||
INLINE(Cell* target_cell());
|
||||
INLINE(Handle<Cell> target_cell_handle());
|
||||
INLINE(void set_target_cell(
|
||||
|
@ -202,8 +202,7 @@ bool Builtins::IsBuiltin(Code* code) {
|
||||
// static
|
||||
bool Builtins::IsOffHeapBuiltin(Code* code) {
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
return FLAG_stress_off_heap_code &&
|
||||
Builtins::IsBuiltinId(code->builtin_index()) &&
|
||||
return Builtins::IsBuiltinId(code->builtin_index()) &&
|
||||
Builtins::IsOffHeapSafe(code->builtin_index());
|
||||
#else
|
||||
return false;
|
||||
@ -213,6 +212,12 @@ bool Builtins::IsOffHeapBuiltin(Code* code) {
|
||||
// static
|
||||
bool Builtins::IsLazy(int index) {
|
||||
DCHECK(IsBuiltinId(index));
|
||||
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
// We don't want to lazy-deserialize off-heap builtins.
|
||||
if (Builtins::IsOffHeapSafe(index)) return false;
|
||||
#endif
|
||||
|
||||
// There are a couple of reasons that builtins can require eager-loading,
|
||||
// i.e. deserialization at isolate creation instead of on-demand. For
|
||||
// instance:
|
||||
@ -644,7 +649,7 @@ bool Builtins::IsIsolateIndependent(int index) {
|
||||
|
||||
// static
|
||||
bool Builtins::IsOffHeapSafe(int index) {
|
||||
#ifndef V8_EMBEDDED_BUILTINS
|
||||
#if !defined(V8_EMBEDDED_BUILTINS) || !defined(V8_USE_SNAPSHOT)
|
||||
return false;
|
||||
#else
|
||||
DCHECK(IsBuiltinId(index));
|
||||
@ -664,6 +669,32 @@ bool Builtins::IsTooShortForOffHeapTrampoline(int index) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
// static
|
||||
Handle<Code> Builtins::GenerateOffHeapTrampolineFor(Isolate* isolate,
|
||||
Address off_heap_entry) {
|
||||
DCHECK(isolate->serializer_enabled());
|
||||
DCHECK_NOT_NULL(isolate->embedded_blob());
|
||||
DCHECK_NE(0, isolate->embedded_blob_size());
|
||||
|
||||
constexpr size_t buffer_size = 256; // Enough to fit the single jmp.
|
||||
byte buffer[buffer_size]; // NOLINT(runtime/arrays)
|
||||
|
||||
// Generate replacement code that simply tail-calls the off-heap code.
|
||||
MacroAssembler masm(isolate, buffer, buffer_size, CodeObjectRequired::kYes);
|
||||
DCHECK(!masm.has_frame());
|
||||
{
|
||||
FrameScope scope(&masm, StackFrame::NONE);
|
||||
masm.JumpToInstructionStream(off_heap_entry);
|
||||
}
|
||||
|
||||
CodeDesc desc;
|
||||
masm.GetCode(isolate, &desc);
|
||||
|
||||
return isolate->factory()->NewCode(desc, Code::BUILTIN, masm.CodeObject());
|
||||
}
|
||||
#endif // V8_EMBEDDED_BUILTINS
|
||||
|
||||
// static
|
||||
Builtins::Kind Builtins::KindOf(int index) {
|
||||
DCHECK(IsBuiltinId(index));
|
||||
|
@ -72,7 +72,7 @@ class Builtins {
|
||||
Handle<Code> NewFunctionContext(ScopeType scope_type);
|
||||
Handle<Code> JSConstructStubGeneric();
|
||||
|
||||
// Used by BuiltinDeserializer.
|
||||
// Used by BuiltinDeserializer and CreateOffHeapTrampolines in isolate.cc.
|
||||
void set_builtin(int index, HeapObject* builtin);
|
||||
|
||||
Code* builtin(int index) {
|
||||
@ -160,6 +160,14 @@ class Builtins {
|
||||
private:
|
||||
Builtins();
|
||||
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
// Creates a trampoline code object that jumps to the given off-heap entry.
|
||||
// The result should not be used directly, but only from the related Factory
|
||||
// function.
|
||||
static Handle<Code> GenerateOffHeapTrampolineFor(Isolate* isolate,
|
||||
Address off_heap_entry);
|
||||
#endif
|
||||
|
||||
static void Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode);
|
||||
|
||||
@ -198,6 +206,7 @@ class Builtins {
|
||||
Object* builtins_[builtin_count];
|
||||
bool initialized_;
|
||||
|
||||
friend class Factory; // For GenerateOffHeapTrampolineFor.
|
||||
friend class Isolate;
|
||||
friend class SetupIsolateDelegate;
|
||||
|
||||
|
@ -1856,6 +1856,36 @@ Handle<Code> Factory::NewCodeForDeserialization(uint32_t size) {
|
||||
Code);
|
||||
}
|
||||
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
Handle<Code> Factory::NewOffHeapTrampolineFor(Handle<Code> code,
|
||||
Address off_heap_entry) {
|
||||
DCHECK(isolate()->serializer_enabled());
|
||||
DCHECK_NOT_NULL(isolate()->embedded_blob());
|
||||
DCHECK_NE(0, isolate()->embedded_blob_size());
|
||||
DCHECK(Builtins::IsOffHeapBuiltin(*code));
|
||||
|
||||
Handle<Code> result =
|
||||
Builtins::GenerateOffHeapTrampolineFor(isolate(), off_heap_entry);
|
||||
|
||||
// The trampoline code object must inherit specific flags from the original
|
||||
// builtin (e.g. the safepoint-table offset). We set them manually here.
|
||||
|
||||
const int stack_slots = code->has_safepoint_info() ? code->stack_slots() : 0;
|
||||
result->initialize_flags(code->kind(), code->has_unwinding_info(),
|
||||
code->is_turbofanned(), stack_slots);
|
||||
result->set_builtin_index(code->builtin_index());
|
||||
result->set_has_tagged_params(code->has_tagged_params());
|
||||
result->set_handler_table_offset(code->handler_table_offset());
|
||||
result->code_data_container()->set_kind_specific_flags(
|
||||
code->code_data_container()->kind_specific_flags());
|
||||
if (code->has_safepoint_info()) {
|
||||
result->set_safepoint_table_offset(code->safepoint_table_offset());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
Handle<Code> Factory::CopyCode(Handle<Code> code) {
|
||||
Handle<CodeDataContainer> data_container =
|
||||
NewCodeDataContainer(code->code_data_container()->kind_specific_flags());
|
||||
|
@ -716,6 +716,13 @@ class V8_EXPORT_PRIVATE Factory final {
|
||||
// given {size} argument specifies the size of the entire code object.
|
||||
Handle<Code> NewCodeForDeserialization(uint32_t size);
|
||||
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
// Allocates a new code object and initializes it as the trampoline to the
|
||||
// given off-heap entry point.
|
||||
Handle<Code> NewOffHeapTrampolineFor(Handle<Code> code,
|
||||
Address off_heap_entry);
|
||||
#endif
|
||||
|
||||
Handle<Code> CopyCode(Handle<Code> code);
|
||||
|
||||
Handle<BytecodeArray> CopyBytecodeArray(Handle<BytecodeArray>);
|
||||
|
@ -6830,9 +6830,7 @@ bool Heap::GcSafeCodeContains(HeapObject* code, Address addr) {
|
||||
Map* map = GcSafeMapOfCodeSpaceObject(code);
|
||||
DCHECK(map == code->GetHeap()->code_map());
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
if (FLAG_stress_off_heap_code) {
|
||||
if (InstructionStream::TryLookupCode(isolate(), addr) == code) return true;
|
||||
}
|
||||
if (InstructionStream::TryLookupCode(isolate(), addr) == code) return true;
|
||||
#endif
|
||||
Address start = code->address();
|
||||
Address end = code->address() + code->SizeFromMap(map);
|
||||
@ -6841,10 +6839,8 @@ bool Heap::GcSafeCodeContains(HeapObject* code, Address addr) {
|
||||
|
||||
Code* Heap::GcSafeFindCodeForInnerPointer(Address inner_pointer) {
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
if (FLAG_stress_off_heap_code) {
|
||||
Code* code = InstructionStream::TryLookupCode(isolate(), inner_pointer);
|
||||
if (code != nullptr) return code;
|
||||
}
|
||||
Code* code = InstructionStream::TryLookupCode(isolate(), inner_pointer);
|
||||
if (code != nullptr) return code;
|
||||
#endif
|
||||
|
||||
// Check if the inner pointer points into a large object chunk.
|
||||
|
@ -72,7 +72,8 @@ Address RelocInfo::target_address() {
|
||||
|
||||
Address RelocInfo::target_address_address() {
|
||||
DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) || IsWasmCall(rmode_) ||
|
||||
rmode_ == EMBEDDED_OBJECT || rmode_ == EXTERNAL_REFERENCE);
|
||||
IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) ||
|
||||
IsOffHeapTarget(rmode_));
|
||||
return reinterpret_cast<Address>(pc_);
|
||||
}
|
||||
|
||||
@ -144,6 +145,11 @@ void RelocInfo::set_target_runtime_entry(Address target,
|
||||
}
|
||||
}
|
||||
|
||||
Address RelocInfo::target_off_heap_target() {
|
||||
DCHECK(IsOffHeapTarget(rmode_));
|
||||
return Memory::Address_at(pc_);
|
||||
}
|
||||
|
||||
void RelocInfo::WipeOut() {
|
||||
if (IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) ||
|
||||
IsInternalReference(rmode_)) {
|
||||
@ -171,6 +177,8 @@ void RelocInfo::Visit(ObjectVisitor* visitor) {
|
||||
visitor->VisitInternalReference(host(), this);
|
||||
} else if (IsRuntimeEntry(mode)) {
|
||||
visitor->VisitRuntimeEntry(host(), this);
|
||||
} else if (RelocInfo::IsOffHeapTarget(mode)) {
|
||||
visitor->VisitOffHeapTarget(host(), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -848,7 +848,7 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& ext,
|
||||
}
|
||||
|
||||
void MacroAssembler::JumpToInstructionStream(Address entry) {
|
||||
mov(kOffHeapTrampolineRegister, Immediate(entry, RelocInfo::NONE));
|
||||
mov(kOffHeapTrampolineRegister, Immediate(entry, RelocInfo::OFF_HEAP_TARGET));
|
||||
jmp(kOffHeapTrampolineRegister);
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,6 @@ bool InstructionStream::PcIsOffHeap(Isolate* isolate, Address pc) {
|
||||
// static
|
||||
Code* InstructionStream::TryLookupCode(Isolate* isolate, Address address) {
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
DCHECK(FLAG_stress_off_heap_code);
|
||||
|
||||
if (!PcIsOffHeap(isolate, address)) return nullptr;
|
||||
|
||||
EmbeddedData d = EmbeddedData::FromBlob(isolate->embedded_blob(),
|
||||
@ -52,5 +50,37 @@ Code* InstructionStream::TryLookupCode(Isolate* isolate, Address address) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
// static
|
||||
void InstructionStream::CreateOffHeapInstructionStream(Isolate* isolate,
|
||||
uint8_t** data,
|
||||
uint32_t* size) {
|
||||
EmbeddedData d = EmbeddedData::FromIsolate(isolate);
|
||||
|
||||
const uint32_t page_size = static_cast<uint32_t>(AllocatePageSize());
|
||||
const uint32_t allocated_size = RoundUp(d.size(), page_size);
|
||||
|
||||
uint8_t* allocated_bytes = static_cast<uint8_t*>(
|
||||
AllocatePages(GetRandomMmapAddr(), allocated_size, page_size,
|
||||
PageAllocator::kReadWrite));
|
||||
CHECK_NOT_NULL(allocated_bytes);
|
||||
|
||||
std::memcpy(allocated_bytes, d.data(), d.size());
|
||||
CHECK(SetPermissions(allocated_bytes, allocated_size,
|
||||
PageAllocator::kReadExecute));
|
||||
|
||||
*data = allocated_bytes;
|
||||
*size = allocated_size;
|
||||
|
||||
d.Dispose();
|
||||
}
|
||||
|
||||
// static
|
||||
void InstructionStream::FreeOffHeapInstructionStream(uint8_t* data,
|
||||
uint32_t size) {
|
||||
CHECK(FreePages(data, size));
|
||||
}
|
||||
#endif // V8_EMBEDDED_BUILTINS
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -23,6 +23,16 @@ class InstructionStream final : public AllStatic {
|
||||
|
||||
// Returns the corresponding Code object if it exists, and nullptr otherwise.
|
||||
static Code* TryLookupCode(Isolate* isolate, Address address);
|
||||
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
// During snapshot creation, we first create an executable off-heap area
|
||||
// containing all off-heap code. The area is guaranteed to be contiguous.
|
||||
// Note that this only applies when building the snapshot, e.g. for
|
||||
// mksnapshot. Otherwise, off-heap code is embedded directly into the binary.
|
||||
static void CreateOffHeapInstructionStream(Isolate* isolate, uint8_t** data,
|
||||
uint32_t* size);
|
||||
static void FreeOffHeapInstructionStream(uint8_t* data, uint32_t size);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
129
src/isolate.cc
129
src/isolate.cc
@ -70,11 +70,8 @@ base::Atomic32 ThreadId::highest_thread_id_ = 0;
|
||||
extern const uint8_t* DefaultEmbeddedBlob();
|
||||
extern uint32_t DefaultEmbeddedBlobSize();
|
||||
|
||||
const uint8_t* Isolate::embedded_blob() const { return DefaultEmbeddedBlob(); }
|
||||
|
||||
uint32_t Isolate::embedded_blob_size() const {
|
||||
return DefaultEmbeddedBlobSize();
|
||||
}
|
||||
const uint8_t* Isolate::embedded_blob() const { return embedded_blob_; }
|
||||
uint32_t Isolate::embedded_blob_size() const { return embedded_blob_size_; }
|
||||
#endif
|
||||
|
||||
int ThreadId::AllocateThreadId() {
|
||||
@ -2651,6 +2648,14 @@ void Isolate::Deinit() {
|
||||
heap_.TearDown();
|
||||
logger_->TearDown();
|
||||
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
if (DefaultEmbeddedBlob() == nullptr && embedded_blob() != nullptr) {
|
||||
// We own the embedded blob. Free it.
|
||||
uint8_t* data = const_cast<uint8_t*>(embedded_blob_);
|
||||
InstructionStream::FreeOffHeapInstructionStream(data, embedded_blob_size_);
|
||||
}
|
||||
#endif
|
||||
|
||||
delete interpreter_;
|
||||
interpreter_ = nullptr;
|
||||
|
||||
@ -2818,70 +2823,14 @@ void PrintBuiltinSizes(Isolate* isolate) {
|
||||
}
|
||||
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
void ChangeToOffHeapTrampoline(Isolate* isolate, Handle<Code> code,
|
||||
const uint8_t* entry) {
|
||||
DCHECK(Builtins::IsOffHeapSafe(code->builtin_index()));
|
||||
HandleScope scope(isolate);
|
||||
|
||||
constexpr size_t buffer_size = 256; // Enough to fit the single jmp.
|
||||
byte buffer[buffer_size]; // NOLINT(runtime/arrays)
|
||||
|
||||
// Generate replacement code that simply tail-calls the off-heap code.
|
||||
MacroAssembler masm(isolate, buffer, buffer_size, CodeObjectRequired::kYes);
|
||||
DCHECK(!masm.has_frame());
|
||||
{
|
||||
FrameScope scope(&masm, StackFrame::NONE);
|
||||
masm.JumpToInstructionStream(
|
||||
reinterpret_cast<Address>(const_cast<uint8_t*>(entry)));
|
||||
}
|
||||
|
||||
CodeDesc desc;
|
||||
masm.GetCode(isolate, &desc);
|
||||
|
||||
// Hack in an empty reloc info to satisfy the GC.
|
||||
DCHECK_EQ(0, desc.reloc_size);
|
||||
Handle<ByteArray> reloc_info =
|
||||
isolate->factory()->NewByteArray(desc.reloc_size, TENURED);
|
||||
code->set_relocation_info(*reloc_info);
|
||||
|
||||
// Overwrites the original code.
|
||||
CHECK_LE(desc.instr_size, code->instruction_size());
|
||||
CHECK_IMPLIES(code->has_safepoint_info(),
|
||||
desc.instr_size <= code->safepoint_table_offset());
|
||||
code->CopyFrom(desc);
|
||||
|
||||
// TODO(jgruber): CopyFrom isn't intended to overwrite existing code, and
|
||||
// doesn't update things like instruction_size. The result is a code object in
|
||||
// which the first instructions are overwritten while the rest remain intact
|
||||
// (but are never executed). That's fine for our current purposes, just
|
||||
// manually zero the trailing part.
|
||||
|
||||
int code_instruction_size = code->instruction_size();
|
||||
DCHECK_LE(desc.instr_size, code_instruction_size);
|
||||
byte* trailing_instruction_start =
|
||||
code->instruction_start() + desc.instr_size;
|
||||
if (code->has_safepoint_info()) {
|
||||
CHECK_LE(code->safepoint_table_offset(), code->instruction_size());
|
||||
code_instruction_size = code->safepoint_table_offset();
|
||||
CHECK_LE(desc.instr_size, code_instruction_size);
|
||||
}
|
||||
size_t trailing_instruction_size = code_instruction_size - desc.instr_size;
|
||||
std::memset(trailing_instruction_start, 0, trailing_instruction_size);
|
||||
}
|
||||
|
||||
void CreateOnHeapTrampolines(Isolate* isolate) {
|
||||
DCHECK(FLAG_stress_off_heap_code);
|
||||
DCHECK(!isolate->serializer_enabled());
|
||||
void CreateOffHeapTrampolines(Isolate* isolate) {
|
||||
DCHECK(isolate->serializer_enabled());
|
||||
DCHECK_NOT_NULL(isolate->embedded_blob());
|
||||
DCHECK_NE(0, isolate->embedded_blob_size());
|
||||
|
||||
HandleScope scope(isolate);
|
||||
Builtins* builtins = isolate->builtins();
|
||||
|
||||
// Lazy deserialization would defeat our off-heap stress test (we'd
|
||||
// deserialize later without moving off-heap), so force eager
|
||||
// deserialization.
|
||||
Snapshot::EnsureAllBuiltinsAreDeserialized(isolate);
|
||||
|
||||
EmbeddedData d = EmbeddedData::FromBlob(isolate->embedded_blob(),
|
||||
isolate->embedded_blob_size());
|
||||
|
||||
@ -2890,22 +2839,49 @@ void CreateOnHeapTrampolines(Isolate* isolate) {
|
||||
if (!Builtins::IsOffHeapSafe(i)) continue;
|
||||
|
||||
const uint8_t* instruction_start = d.InstructionStartOfBuiltin(i);
|
||||
Handle<Code> trampoline = isolate->factory()->NewOffHeapTrampolineFor(
|
||||
builtins->builtin_handle(i), const_cast<Address>(instruction_start));
|
||||
|
||||
// TODO(jgruber,v8:6666): Create fresh trampolines instead of rewriting
|
||||
// existing ones. This could happen prior to serialization or
|
||||
// post-deserialization.
|
||||
Handle<Code> code(builtins->builtin(i));
|
||||
ChangeToOffHeapTrampoline(isolate, code, instruction_start);
|
||||
// Note that references to the old, on-heap code objects may still exist on
|
||||
// the heap. This is fine for the sake of serialization, as serialization
|
||||
// will replace all of them with a builtin reference which is later
|
||||
// deserialized to point to the object within the builtins table.
|
||||
//
|
||||
// From this point onwards, some builtin code objects may be unreachable and
|
||||
// thus collected by the GC.
|
||||
builtins->set_builtin(i, *trampoline);
|
||||
|
||||
if (isolate->logger()->is_logging_code_events() ||
|
||||
isolate->is_profiling()) {
|
||||
isolate->logger()->LogCodeObject(*code);
|
||||
isolate->logger()->LogCodeObject(*trampoline);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // V8_EMBEDDED_BUILTINS
|
||||
} // namespace
|
||||
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
void Isolate::PrepareEmbeddedBlobForSerialization() {
|
||||
// When preparing the embedded blob, ensure it doesn't exist yet.
|
||||
DCHECK_NULL(embedded_blob());
|
||||
DCHECK_NULL(DefaultEmbeddedBlob());
|
||||
DCHECK(serializer_enabled());
|
||||
|
||||
// The isolate takes ownership of this pointer into an executable mmap'd
|
||||
// area. We muck around with const-casts because the standard use-case in
|
||||
// shipping builds is for embedded_blob_ to point into a read-only
|
||||
// .text-embedded section.
|
||||
uint8_t* data;
|
||||
uint32_t size;
|
||||
InstructionStream::CreateOffHeapInstructionStream(this, &data, &size);
|
||||
|
||||
embedded_blob_ = const_cast<const uint8_t*>(data);
|
||||
embedded_blob_size_ = size;
|
||||
|
||||
CreateOffHeapTrampolines(this);
|
||||
}
|
||||
#endif // V8_EMBEDDED_BUILTINS
|
||||
|
||||
bool Isolate::Init(StartupDeserializer* des) {
|
||||
TRACE_ISOLATE(init);
|
||||
|
||||
@ -2959,6 +2935,11 @@ bool Isolate::Init(StartupDeserializer* des) {
|
||||
compiler_dispatcher_ =
|
||||
new CompilerDispatcher(this, V8::GetCurrentPlatform(), FLAG_stack_size);
|
||||
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
embedded_blob_ = DefaultEmbeddedBlob();
|
||||
embedded_blob_size_ = DefaultEmbeddedBlobSize();
|
||||
#endif
|
||||
|
||||
// Enable logging before setting up the heap
|
||||
logger_->SetUp(this);
|
||||
|
||||
@ -3066,14 +3047,6 @@ bool Isolate::Init(StartupDeserializer* des) {
|
||||
|
||||
if (FLAG_print_builtin_size) PrintBuiltinSizes(this);
|
||||
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
if (FLAG_stress_off_heap_code && !serializer_enabled() &&
|
||||
embedded_blob() != nullptr) {
|
||||
// Create the on-heap trampolines that jump into embedded code.
|
||||
CreateOnHeapTrampolines(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Finish initialization of ThreadLocal after deserialization is done.
|
||||
clear_pending_exception();
|
||||
clear_pending_message();
|
||||
|
@ -1249,6 +1249,11 @@ class Isolate {
|
||||
}
|
||||
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
// Called only prior to serialization.
|
||||
// This function copies off-heap-safe builtins off the heap, creates off-heap
|
||||
// trampolines, and sets up this isolate's embedded blob.
|
||||
void PrepareEmbeddedBlobForSerialization();
|
||||
|
||||
BuiltinsConstantsTableBuilder* builtins_constants_table_builder() const {
|
||||
return builtins_constants_table_builder_;
|
||||
}
|
||||
@ -1630,6 +1635,9 @@ class Isolate {
|
||||
// Used during builtins compilation to build the builtins constants table,
|
||||
// which is stored on the root list prior to serialization.
|
||||
BuiltinsConstantsTableBuilder* builtins_constants_table_builder_ = nullptr;
|
||||
|
||||
const uint8_t* embedded_blob_ = nullptr;
|
||||
uint32_t embedded_blob_size_ = 0;
|
||||
#endif
|
||||
|
||||
v8::ArrayBuffer::Allocator* array_buffer_allocator_;
|
||||
|
@ -82,7 +82,8 @@ Address RelocInfo::target_address() {
|
||||
|
||||
Address RelocInfo::target_address_address() {
|
||||
DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) || IsWasmCall(rmode_) ||
|
||||
rmode_ == EMBEDDED_OBJECT || rmode_ == EXTERNAL_REFERENCE);
|
||||
IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) ||
|
||||
IsOffHeapTarget(rmode_));
|
||||
// Read the address of the word containing the target_address in an
|
||||
// instruction stream.
|
||||
// The only architecture-independent user of this function is the serializer.
|
||||
@ -254,6 +255,11 @@ void RelocInfo::set_target_runtime_entry(Address target,
|
||||
set_target_address(target, write_barrier_mode, icache_flush_mode);
|
||||
}
|
||||
|
||||
Address RelocInfo::target_off_heap_target() {
|
||||
DCHECK(IsOffHeapTarget(rmode_));
|
||||
return Assembler::target_address_at(pc_, constant_pool_);
|
||||
}
|
||||
|
||||
void RelocInfo::WipeOut() {
|
||||
DCHECK(IsEmbeddedObject(rmode_) || IsCodeTarget(rmode_) ||
|
||||
IsRuntimeEntry(rmode_) || IsExternalReference(rmode_) ||
|
||||
@ -281,6 +287,8 @@ void RelocInfo::Visit(ObjectVisitor* visitor) {
|
||||
visitor->VisitInternalReference(host(), this);
|
||||
} else if (RelocInfo::IsRuntimeEntry(mode)) {
|
||||
visitor->VisitRuntimeEntry(host(), this);
|
||||
} else if (RelocInfo::IsOffHeapTarget(mode)) {
|
||||
visitor->VisitOffHeapTarget(host(), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4412,7 +4412,7 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin,
|
||||
|
||||
void MacroAssembler::JumpToInstructionStream(Address entry) {
|
||||
li(kOffHeapTrampolineRegister,
|
||||
Operand(reinterpret_cast<int32_t>(entry), RelocInfo::NONE));
|
||||
Operand(reinterpret_cast<int32_t>(entry), RelocInfo::OFF_HEAP_TARGET));
|
||||
Jump(kOffHeapTrampolineRegister);
|
||||
}
|
||||
|
||||
|
@ -80,10 +80,9 @@ Address RelocInfo::target_address() {
|
||||
}
|
||||
|
||||
Address RelocInfo::target_address_address() {
|
||||
DCHECK(IsCodeTarget(rmode_) ||
|
||||
IsRuntimeEntry(rmode_) ||
|
||||
rmode_ == EMBEDDED_OBJECT ||
|
||||
rmode_ == EXTERNAL_REFERENCE);
|
||||
DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) || IsWasmCall(rmode_) ||
|
||||
IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) ||
|
||||
IsOffHeapTarget(rmode_));
|
||||
// Read the address of the word containing the target_address in an
|
||||
// instruction stream.
|
||||
// The only architecture-independent user of this function is the serializer.
|
||||
@ -222,6 +221,11 @@ void RelocInfo::set_target_runtime_entry(Address target,
|
||||
set_target_address(target, write_barrier_mode, icache_flush_mode);
|
||||
}
|
||||
|
||||
Address RelocInfo::target_off_heap_target() {
|
||||
DCHECK(IsOffHeapTarget(rmode_));
|
||||
return Assembler::target_address_at(pc_, constant_pool_);
|
||||
}
|
||||
|
||||
void RelocInfo::WipeOut() {
|
||||
DCHECK(IsEmbeddedObject(rmode_) || IsCodeTarget(rmode_) ||
|
||||
IsRuntimeEntry(rmode_) || IsExternalReference(rmode_) ||
|
||||
@ -249,6 +253,8 @@ void RelocInfo::Visit(ObjectVisitor* visitor) {
|
||||
visitor->VisitInternalReference(host(), this);
|
||||
} else if (RelocInfo::IsRuntimeEntry(mode)) {
|
||||
visitor->VisitRuntimeEntry(host(), this);
|
||||
} else if (RelocInfo::IsOffHeapTarget(mode)) {
|
||||
visitor->VisitOffHeapTarget(host(), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4734,7 +4734,7 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin,
|
||||
|
||||
void MacroAssembler::JumpToInstructionStream(Address entry) {
|
||||
li(kOffHeapTrampolineRegister,
|
||||
Operand(reinterpret_cast<uint64_t>(entry), RelocInfo::NONE));
|
||||
Operand(reinterpret_cast<uint64_t>(entry), RelocInfo::OFF_HEAP_TARGET));
|
||||
Jump(kOffHeapTrampolineRegister);
|
||||
}
|
||||
|
||||
|
@ -414,6 +414,7 @@ class Code::BodyDescriptor final : public BodyDescriptorBase {
|
||||
RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
|
||||
RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
|
||||
RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED) |
|
||||
RelocInfo::ModeMask(RelocInfo::OFF_HEAP_TARGET) |
|
||||
RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
|
||||
|
||||
// GC does not visit data/code in the header and in the body directly.
|
||||
|
@ -13972,6 +13972,7 @@ SafepointEntry Code::GetSafepointEntry(Address pc) {
|
||||
int Code::OffHeapInstructionSize() {
|
||||
DCHECK(Builtins::IsOffHeapBuiltin(this));
|
||||
Isolate* isolate = GetIsolate();
|
||||
if (isolate->embedded_blob() == nullptr) return instruction_size();
|
||||
EmbeddedData d = EmbeddedData::FromBlob(isolate->embedded_blob(),
|
||||
isolate->embedded_blob_size());
|
||||
return d.InstructionSizeOfBuiltin(builtin_index());
|
||||
@ -13980,6 +13981,7 @@ int Code::OffHeapInstructionSize() {
|
||||
Address Code::OffHeapInstructionStart() {
|
||||
DCHECK(Builtins::IsOffHeapBuiltin(this));
|
||||
Isolate* isolate = GetIsolate();
|
||||
if (isolate->embedded_blob() == nullptr) return instruction_start();
|
||||
EmbeddedData d = EmbeddedData::FromBlob(isolate->embedded_blob(),
|
||||
isolate->embedded_blob_size());
|
||||
return reinterpret_cast<Address>(
|
||||
@ -13989,6 +13991,7 @@ Address Code::OffHeapInstructionStart() {
|
||||
Address Code::OffHeapInstructionEnd() {
|
||||
DCHECK(Builtins::IsOffHeapBuiltin(this));
|
||||
Isolate* isolate = GetIsolate();
|
||||
if (isolate->embedded_blob() == nullptr) return instruction_end();
|
||||
EmbeddedData d = EmbeddedData::FromBlob(isolate->embedded_blob(),
|
||||
isolate->embedded_blob_size());
|
||||
return reinterpret_cast<Address>(
|
||||
@ -14140,6 +14143,7 @@ bool Code::IsProcessIndependent() {
|
||||
all_real_modes_mask & ~RelocInfo::ModeMask(RelocInfo::COMMENT) &
|
||||
~RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) &
|
||||
~RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED) &
|
||||
~RelocInfo::ModeMask(RelocInfo::OFF_HEAP_TARGET) &
|
||||
~RelocInfo::ModeMask(RelocInfo::CONST_POOL) &
|
||||
~RelocInfo::ModeMask(RelocInfo::VENEER_POOL);
|
||||
STATIC_ASSERT(RelocInfo::LAST_REAL_RELOC_MODE == RelocInfo::VENEER_POOL);
|
||||
|
@ -91,7 +91,8 @@ Address RelocInfo::target_address() {
|
||||
|
||||
Address RelocInfo::target_address_address() {
|
||||
DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) || IsWasmCall(rmode_) ||
|
||||
rmode_ == EMBEDDED_OBJECT || rmode_ == EXTERNAL_REFERENCE);
|
||||
IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) ||
|
||||
IsOffHeapTarget(rmode_));
|
||||
|
||||
if (FLAG_enable_embedded_constant_pool &&
|
||||
Assembler::IsConstantPoolLoadStart(pc_)) {
|
||||
@ -206,6 +207,11 @@ void RelocInfo::set_target_runtime_entry(Address target,
|
||||
set_target_address(target, write_barrier_mode, icache_flush_mode);
|
||||
}
|
||||
|
||||
Address RelocInfo::target_off_heap_target() {
|
||||
DCHECK(IsOffHeapTarget(rmode_));
|
||||
return Assembler::target_address_at(pc_, constant_pool_);
|
||||
}
|
||||
|
||||
void RelocInfo::WipeOut() {
|
||||
DCHECK(IsEmbeddedObject(rmode_) || IsCodeTarget(rmode_) ||
|
||||
IsRuntimeEntry(rmode_) || IsExternalReference(rmode_) ||
|
||||
@ -237,6 +243,8 @@ void RelocInfo::Visit(ObjectVisitor* visitor) {
|
||||
visitor->VisitInternalReference(host(), this);
|
||||
} else if (IsRuntimeEntry(mode)) {
|
||||
visitor->VisitRuntimeEntry(host(), this);
|
||||
} else if (RelocInfo::IsOffHeapTarget(mode)) {
|
||||
visitor->VisitOffHeapTarget(host(), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1617,7 +1617,7 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin,
|
||||
|
||||
void MacroAssembler::JumpToInstructionStream(Address entry) {
|
||||
mov(kOffHeapTrampolineRegister,
|
||||
Operand(reinterpret_cast<intptr_t>(entry), RelocInfo::NONE));
|
||||
Operand(reinterpret_cast<intptr_t>(entry), RelocInfo::OFF_HEAP_TARGET));
|
||||
Jump(kOffHeapTrampolineRegister);
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,8 @@ Address RelocInfo::target_address() {
|
||||
|
||||
Address RelocInfo::target_address_address() {
|
||||
DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) || IsWasmCall(rmode_) ||
|
||||
rmode_ == EMBEDDED_OBJECT || rmode_ == EXTERNAL_REFERENCE);
|
||||
IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) ||
|
||||
IsOffHeapTarget(rmode_));
|
||||
|
||||
// Read the address of the word containing the target_address in an
|
||||
// instruction stream.
|
||||
@ -179,6 +180,11 @@ Address RelocInfo::target_runtime_entry(Assembler* origin) {
|
||||
return target_address();
|
||||
}
|
||||
|
||||
Address RelocInfo::target_off_heap_target() {
|
||||
DCHECK(IsOffHeapTarget(rmode_));
|
||||
return Assembler::target_address_at(pc_, constant_pool_);
|
||||
}
|
||||
|
||||
void RelocInfo::set_target_runtime_entry(Address target,
|
||||
WriteBarrierMode write_barrier_mode,
|
||||
ICacheFlushMode icache_flush_mode) {
|
||||
@ -217,6 +223,8 @@ void RelocInfo::Visit(ObjectVisitor* visitor) {
|
||||
visitor->VisitInternalReference(host(), this);
|
||||
} else if (IsRuntimeEntry(mode)) {
|
||||
visitor->VisitRuntimeEntry(host(), this);
|
||||
} else if (RelocInfo::IsOffHeapTarget(mode)) {
|
||||
visitor->VisitOffHeapTarget(host(), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1524,7 +1524,8 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin,
|
||||
}
|
||||
|
||||
void MacroAssembler::JumpToInstructionStream(Address entry) {
|
||||
mov(kOffHeapTrampolineRegister, Operand(reinterpret_cast<intptr_t>(entry)));
|
||||
mov(kOffHeapTrampolineRegister,
|
||||
Operand(reinterpret_cast<intptr_t>(entry), RelocInfo::OFF_HEAP_TARGET));
|
||||
Jump(kOffHeapTrampolineRegister);
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "src/objects/string.h"
|
||||
#include "src/snapshot/builtin-deserializer-allocator.h"
|
||||
#include "src/snapshot/natives.h"
|
||||
#include "src/snapshot/snapshot.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -491,6 +492,30 @@ bool Deserializer<AllocatorT>::ReadData(Object** current, Object** limit,
|
||||
break;
|
||||
}
|
||||
|
||||
case kOffHeapTarget: {
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
int skip = source_.GetInt();
|
||||
int builtin_index = source_.GetInt();
|
||||
DCHECK(Builtins::IsBuiltinId(builtin_index));
|
||||
|
||||
current = reinterpret_cast<Object**>(
|
||||
reinterpret_cast<Address>(current) + skip);
|
||||
|
||||
CHECK_NOT_NULL(isolate->embedded_blob());
|
||||
EmbeddedData d = EmbeddedData::FromBlob(isolate->embedded_blob(),
|
||||
isolate->embedded_blob_size());
|
||||
const uint8_t* address = d.InstructionStartOfBuiltin(builtin_index);
|
||||
Object* o = reinterpret_cast<Object*>(const_cast<uint8_t*>(address));
|
||||
UnalignedCopy(current, &o);
|
||||
CHECK_NOT_NULL(o);
|
||||
|
||||
current++;
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case kNop:
|
||||
break;
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "src/snapshot/snapshot.h"
|
||||
#include "src/snapshot/startup-serializer.h"
|
||||
|
||||
namespace {
|
||||
class SnapshotWriter {
|
||||
public:
|
||||
SnapshotWriter()
|
||||
@ -150,28 +151,22 @@ class SnapshotWriter {
|
||||
}
|
||||
|
||||
static void WriteEmbeddedFileData(FILE* fp, const i::EmbeddedData* blob) {
|
||||
// Note: On some platforms (observed on mac64), inserting labels into the
|
||||
// .byte stream causes the compiler to reorder symbols, invalidating stored
|
||||
// offsets.
|
||||
// We either need to avoid doing so, or stop relying on our own offset table
|
||||
// and directly reference symbols instead. But there is another complication
|
||||
// there since the chrome build process on mac verifies the order of symbols
|
||||
// present in the binary.
|
||||
// For now, the straight-forward solution seems to be to just emit a pure
|
||||
// .byte stream.
|
||||
fprintf(fp, "V8_EMBEDDED_TEXT_HEADER(v8_embedded_blob_)\n");
|
||||
WriteBinaryContentsAsByteDirective(fp, blob->data(),
|
||||
i::EmbeddedData::RawDataOffset());
|
||||
WriteBuiltins(fp, blob);
|
||||
WriteBinaryContentsAsByteDirective(fp, blob->data(), blob->size());
|
||||
fprintf(fp, "extern \"C\" const uint8_t v8_embedded_blob_[];\n");
|
||||
fprintf(fp, "static const uint32_t v8_embedded_blob_size_ = %d;\n\n",
|
||||
blob->size());
|
||||
}
|
||||
|
||||
static void WriteBuiltins(FILE* fp, const i::EmbeddedData* blob) {
|
||||
for (int i = 0; i < i::Builtins::builtin_count; i++) {
|
||||
if (!blob->ContainsBuiltin(i)) continue;
|
||||
|
||||
fprintf(fp, "__asm__(V8_ASM_LABEL(\"Builtins_%s\"));\n",
|
||||
i::Builtins::name(i));
|
||||
WriteBinaryContentsAsByteDirective(
|
||||
fp, blob->InstructionStartOfBuiltin(i),
|
||||
blob->PaddedInstructionSizeOfBuiltin(i));
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
|
||||
static void WriteBinaryContentsAsByteDirective(FILE* fp, const uint8_t* data,
|
||||
uint32_t size) {
|
||||
static const int kTextWidth = 80;
|
||||
@ -305,6 +300,56 @@ v8::StartupData CreateSnapshotDataBlob(v8::SnapshotCreator* snapshot_creator,
|
||||
return result;
|
||||
}
|
||||
|
||||
v8::StartupData WarmUpSnapshotDataBlob(v8::SnapshotCreator* snapshot_creator,
|
||||
const char* warmup_source) {
|
||||
CHECK_NOT_NULL(warmup_source);
|
||||
// Use following steps to create a warmed up snapshot blob from a cold one:
|
||||
// - Create a new isolate from the cold snapshot.
|
||||
// - Create a new context to run the warmup script. This will trigger
|
||||
// compilation of executed functions.
|
||||
// - Create a new context. This context will be unpolluted.
|
||||
// - Serialize the isolate and the second context into a new snapshot blob.
|
||||
v8::StartupData result = {nullptr, 0};
|
||||
v8::base::ElapsedTimer timer;
|
||||
timer.Start();
|
||||
{
|
||||
v8::Isolate* isolate = snapshot_creator->GetIsolate();
|
||||
{
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
||||
if (!RunExtraCode(isolate, context, warmup_source, "<warm-up>")) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
{
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
isolate->ContextDisposedNotification(false);
|
||||
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
||||
snapshot_creator->SetDefaultContext(context);
|
||||
}
|
||||
result = snapshot_creator->CreateBlob(
|
||||
v8::SnapshotCreator::FunctionCodeHandling::kKeep);
|
||||
}
|
||||
|
||||
if (i::FLAG_profile_deserialization) {
|
||||
i::PrintF("Warming up snapshot took %0.3f ms\n",
|
||||
timer.Elapsed().InMillisecondsF());
|
||||
}
|
||||
timer.Stop();
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
void WriteEmbeddedFile(v8::SnapshotCreator* creator, SnapshotWriter* writer) {
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(creator->GetIsolate());
|
||||
isolate->PrepareEmbeddedBlobForSerialization();
|
||||
i::EmbeddedData embedded_blob = i::EmbeddedData::FromBlob(
|
||||
isolate->embedded_blob(), isolate->embedded_blob_size());
|
||||
writer->WriteEmbedded(&embedded_blob);
|
||||
}
|
||||
#endif // V8_EMBEDDED_BUILTINS
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// Make mksnapshot runs predictable to create reproducible snapshots.
|
||||
i::FLAG_predictable = true;
|
||||
@ -333,31 +378,33 @@ int main(int argc, char** argv) {
|
||||
if (i::FLAG_embedded_src) writer.SetEmbeddedFile(i::FLAG_embedded_src);
|
||||
#endif
|
||||
|
||||
std::unique_ptr<char> embed_script(
|
||||
GetExtraCode(argc >= 2 ? argv[1] : nullptr, "embedding"));
|
||||
std::unique_ptr<char> warmup_script(
|
||||
GetExtraCode(argc >= 3 ? argv[2] : nullptr, "warm up"));
|
||||
|
||||
v8::StartupData blob;
|
||||
{
|
||||
char* embed_script =
|
||||
GetExtraCode(argc >= 2 ? argv[1] : nullptr, "embedding");
|
||||
v8::SnapshotCreator snapshot_creator;
|
||||
blob = CreateSnapshotDataBlob(&snapshot_creator, embed_script);
|
||||
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
i::Isolate* isolate =
|
||||
reinterpret_cast<i::Isolate*>(snapshot_creator.GetIsolate());
|
||||
i::EmbeddedData embedded_blob = i::Snapshot::CreateEmbeddedBlob(isolate);
|
||||
writer.WriteEmbedded(&embedded_blob);
|
||||
delete[] embedded_blob.data();
|
||||
// This process is a bit tricky since we might go on to make a second
|
||||
// snapshot if a warmup script is passed. In that case, create the first
|
||||
// snapshot without off-heap trampolines and only move code off-heap for
|
||||
// the warmed-up snapshot.
|
||||
if (!warmup_script) WriteEmbeddedFile(&snapshot_creator, &writer);
|
||||
#endif
|
||||
|
||||
delete[] embed_script;
|
||||
blob = CreateSnapshotDataBlob(&snapshot_creator, embed_script.get());
|
||||
}
|
||||
|
||||
char* warmup_script =
|
||||
GetExtraCode(argc >= 3 ? argv[2] : nullptr, "warm up");
|
||||
if (warmup_script) {
|
||||
CHECK(blob.raw_size > 0 && blob.data != nullptr);
|
||||
v8::StartupData cold = blob;
|
||||
blob = v8::V8::WarmUpSnapshotDataBlob(cold, warmup_script);
|
||||
v8::SnapshotCreator snapshot_creator(nullptr, &cold);
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
WriteEmbeddedFile(&snapshot_creator, &writer);
|
||||
#endif
|
||||
blob = WarmUpSnapshotDataBlob(&snapshot_creator, warmup_script.get());
|
||||
delete[] cold.data;
|
||||
delete[] warmup_script;
|
||||
}
|
||||
|
||||
CHECK(blob.data);
|
||||
|
@ -132,7 +132,6 @@ class SerializerDeserializer : public RootVisitor {
|
||||
V(0x7c) \
|
||||
V(0x7d) \
|
||||
V(0x7e) \
|
||||
V(0x7f) \
|
||||
V(0xf0) \
|
||||
V(0xf1) \
|
||||
V(0xf2) \
|
||||
@ -240,6 +239,9 @@ class SerializerDeserializer : public RootVisitor {
|
||||
// Used to encode external referenced provided through the API.
|
||||
static const int kApiReference = 0x37;
|
||||
|
||||
// Encodes an off-heap instruction stream target.
|
||||
static const int kOffHeapTarget = 0x7f;
|
||||
|
||||
// 8 hot (recently seen or back-referenced) objects with optional skip.
|
||||
static const int kNumberOfHotObjects = 8;
|
||||
STATIC_ASSERT(kNumberOfHotObjects == HotObjectsList::kSize);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "src/objects/map.h"
|
||||
#include "src/snapshot/builtin-serializer-allocator.h"
|
||||
#include "src/snapshot/natives.h"
|
||||
#include "src/snapshot/snapshot.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -830,6 +831,29 @@ void Serializer<AllocatorT>::ObjectSerializer::VisitRuntimeEntry(
|
||||
bytes_processed_so_far_ += rinfo->target_address_size();
|
||||
}
|
||||
|
||||
template <class AllocatorT>
|
||||
void Serializer<AllocatorT>::ObjectSerializer::VisitOffHeapTarget(
|
||||
Code* host, RelocInfo* rinfo) {
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
{
|
||||
STATIC_ASSERT(EmbeddedData::kTableSize == Builtins::builtin_count);
|
||||
CHECK(Builtins::IsOffHeapBuiltin(host));
|
||||
Address addr = rinfo->target_off_heap_target();
|
||||
CHECK_NOT_NULL(addr);
|
||||
CHECK_NOT_NULL(
|
||||
InstructionStream::TryLookupCode(serializer_->isolate(), addr));
|
||||
}
|
||||
|
||||
int skip = SkipTo(rinfo->target_address_address());
|
||||
sink_->Put(kOffHeapTarget, "OffHeapTarget");
|
||||
sink_->PutInt(skip, "SkipB4OffHeapTarget");
|
||||
sink_->PutInt(host->builtin_index(), "builtin index");
|
||||
bytes_processed_so_far_ += kPointerSize;
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class AllocatorT>
|
||||
void Serializer<AllocatorT>::ObjectSerializer::VisitCodeTarget(
|
||||
Code* host, RelocInfo* rinfo) {
|
||||
|
@ -305,6 +305,7 @@ class Serializer<AllocatorT>::ObjectSerializer : public ObjectVisitor {
|
||||
void VisitInternalReference(Code* host, RelocInfo* rinfo) override;
|
||||
void VisitCodeTarget(Code* host, RelocInfo* target) override;
|
||||
void VisitRuntimeEntry(Code* host, RelocInfo* reloc) override;
|
||||
void VisitOffHeapTarget(Code* host, RelocInfo* target) override;
|
||||
|
||||
private:
|
||||
void SerializePrologue(AllocationSpace space, int size, Map* map);
|
||||
|
@ -330,9 +330,6 @@ bool BuiltinAliasesOffHeapTrampolineRegister(Isolate* isolate, Code* code) {
|
||||
EmbeddedData EmbeddedData::FromIsolate(Isolate* isolate) {
|
||||
Builtins* builtins = isolate->builtins();
|
||||
|
||||
// Builtins must be deserialized to be copied off-heap.
|
||||
Snapshot::EnsureAllBuiltinsAreDeserialized(isolate);
|
||||
|
||||
// Store instruction stream lengths and offsets.
|
||||
std::vector<uint32_t> lengths(kTableSize);
|
||||
std::vector<uint32_t> offsets(kTableSize);
|
||||
@ -343,6 +340,8 @@ EmbeddedData EmbeddedData::FromIsolate(Isolate* isolate) {
|
||||
Code* code = builtins->builtin(i);
|
||||
|
||||
if (Builtins::IsOffHeapSafe(i)) {
|
||||
DCHECK(!Builtins::IsLazy(i));
|
||||
|
||||
// Sanity-check that the given builtin is process-independent and does not
|
||||
// use the trampoline register in its calling convention.
|
||||
if (!code->IsProcessIndependent()) {
|
||||
@ -395,6 +394,8 @@ EmbeddedData EmbeddedData::FromIsolate(Isolate* isolate) {
|
||||
}
|
||||
|
||||
EmbeddedData EmbeddedData::FromBlob(const uint8_t* data, uint32_t size) {
|
||||
DCHECK_NOT_NULL(data);
|
||||
DCHECK_LT(0, size);
|
||||
return {data, size};
|
||||
}
|
||||
|
||||
@ -412,12 +413,6 @@ uint32_t EmbeddedData::InstructionSizeOfBuiltin(int i) const {
|
||||
const uint32_t* lengths = Lengths();
|
||||
return lengths[i];
|
||||
}
|
||||
|
||||
// static
|
||||
EmbeddedData Snapshot::CreateEmbeddedBlob(Isolate* isolate) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
return EmbeddedData::FromIsolate(isolate);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t Snapshot::ExtractNumContexts(const v8::StartupData* data) {
|
||||
|
@ -88,6 +88,8 @@ class EmbeddedData final {
|
||||
const uint8_t* data() const { return data_; }
|
||||
uint32_t size() const { return size_; }
|
||||
|
||||
void Dispose() { delete[] data_; }
|
||||
|
||||
const uint8_t* InstructionStartOfBuiltin(int i) const;
|
||||
uint32_t InstructionSizeOfBuiltin(int i) const;
|
||||
|
||||
@ -177,10 +179,6 @@ class Snapshot : public AllStatic {
|
||||
const std::vector<SnapshotData*>& context_snapshots,
|
||||
bool can_be_rehashed);
|
||||
|
||||
#ifdef V8_EMBEDDED_BUILTINS
|
||||
static EmbeddedData CreateEmbeddedBlob(Isolate* isolate);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool SnapshotIsValid(const v8::StartupData* snapshot_blob);
|
||||
#endif // DEBUG
|
||||
|
@ -119,6 +119,9 @@ class ObjectVisitor BASE_EMBEDDED {
|
||||
|
||||
// Visits an (encoded) internal reference.
|
||||
virtual void VisitInternalReference(Code* host, RelocInfo* rinfo) {}
|
||||
|
||||
// Visits an off-heap target in the instruction stream.
|
||||
virtual void VisitOffHeapTarget(Code* host, RelocInfo* rinfo) {}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -303,7 +303,8 @@ Address RelocInfo::target_address() {
|
||||
|
||||
Address RelocInfo::target_address_address() {
|
||||
DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) || IsWasmCall(rmode_) ||
|
||||
rmode_ == EMBEDDED_OBJECT || rmode_ == EXTERNAL_REFERENCE);
|
||||
IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) ||
|
||||
IsOffHeapTarget(rmode_));
|
||||
return reinterpret_cast<Address>(pc_);
|
||||
}
|
||||
|
||||
@ -383,6 +384,11 @@ void RelocInfo::set_target_runtime_entry(Address target,
|
||||
}
|
||||
}
|
||||
|
||||
Address RelocInfo::target_off_heap_target() {
|
||||
DCHECK(IsOffHeapTarget(rmode_));
|
||||
return Memory::Address_at(pc_);
|
||||
}
|
||||
|
||||
void RelocInfo::WipeOut() {
|
||||
if (IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) ||
|
||||
IsInternalReference(rmode_)) {
|
||||
@ -410,6 +416,8 @@ void RelocInfo::Visit(ObjectVisitor* visitor) {
|
||||
visitor->VisitInternalReference(host(), this);
|
||||
} else if (RelocInfo::IsRuntimeEntry(mode)) {
|
||||
visitor->VisitRuntimeEntry(host(), this);
|
||||
} else if (RelocInfo::IsOffHeapTarget(mode)) {
|
||||
visitor->VisitOffHeapTarget(host(), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1378,7 +1378,7 @@ void MacroAssembler::Jump(Handle<Code> code_object, RelocInfo::Mode rmode) {
|
||||
}
|
||||
|
||||
void MacroAssembler::JumpToInstructionStream(Address entry) {
|
||||
Move(kOffHeapTrampolineRegister, entry, RelocInfo::NONE);
|
||||
Move(kOffHeapTrampolineRegister, entry, RelocInfo::OFF_HEAP_TARGET);
|
||||
jmp(kOffHeapTrampolineRegister);
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,7 @@ UNINITIALIZED_TEST(VerifyBuiltinsIsolateIndependence) {
|
||||
all_real_modes_mask & ~RelocInfo::ModeMask(RelocInfo::COMMENT) &
|
||||
~RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) &
|
||||
~RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED) &
|
||||
~RelocInfo::ModeMask(RelocInfo::OFF_HEAP_TARGET) &
|
||||
~RelocInfo::ModeMask(RelocInfo::CONST_POOL) &
|
||||
~RelocInfo::ModeMask(RelocInfo::VENEER_POOL);
|
||||
STATIC_ASSERT(RelocInfo::LAST_REAL_RELOC_MODE == RelocInfo::VENEER_POOL);
|
||||
|
@ -3324,26 +3324,5 @@ TEST(SerializationMemoryStats) {
|
||||
delete[] blob.data;
|
||||
}
|
||||
|
||||
TEST(BuiltinsHaveBuiltinIdForLazyDeserialization) {
|
||||
CcTest::InitializeVM();
|
||||
Isolate* isolate = CcTest::i_isolate();
|
||||
i::HandleScope scope(isolate);
|
||||
|
||||
CHECK(Builtins::IsLazy(Builtins::kRegExpPrototypeExec));
|
||||
CHECK_EQ(Builtins::kRegExpPrototypeExec,
|
||||
isolate->regexp_exec_function()
|
||||
->shared()
|
||||
->lazy_deserialization_builtin_id());
|
||||
CHECK(Builtins::IsLazy(Builtins::kAsyncIteratorValueUnwrap));
|
||||
CHECK_EQ(Builtins::kAsyncIteratorValueUnwrap,
|
||||
isolate->async_iterator_value_unwrap_shared_fun()
|
||||
->lazy_deserialization_builtin_id());
|
||||
|
||||
CHECK(!Builtins::IsLazy(Builtins::kIllegal));
|
||||
CHECK(!isolate->opaque_reference_function()
|
||||
->shared()
|
||||
->HasLazyDeserializationBuiltinId());
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
Loading…
Reference in New Issue
Block a user