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:
jgruber 2018-03-16 16:41:19 +01:00 committed by Commit Bot
parent 6031412e0a
commit fd70917d52
39 changed files with 430 additions and 176 deletions

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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:

View File

@ -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(

View File

@ -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));

View File

@ -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;

View File

@ -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());

View File

@ -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>);

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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_;

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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.

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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