Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

11
deps/v8/src/snapshot/DEPS vendored Normal file
View File

@ -0,0 +1,11 @@
specific_include_rules = {
"mksnapshot\.cc": [
"+include/libplatform/libplatform.h",
],
"snapshot-compression.cc": [
"+third_party/zlib",
],
"snapshot-utils.cc": [
"+third_party/zlib",
],
}

14
deps/v8/src/snapshot/DIR_METADATA vendored Normal file
View File

@ -0,0 +1,14 @@
# Metadata information for this directory.
#
# For more information on DIR_METADATA files, see:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
#
# For the schema of this file, see Metadata message:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
monorail {
component: "Blink>JavaScript>Runtime"
}
buganizer_public: {
component_id: 1456800
}

4
deps/v8/src/snapshot/OWNERS vendored Normal file
View File

@ -0,0 +1,4 @@
jgruber@chromium.org
leszeks@chromium.org
olivf@chromium.org
verwaest@chromium.org

914
deps/v8/src/snapshot/code-serializer.cc vendored Normal file
View File

@ -0,0 +1,914 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/code-serializer.h"
#include <memory>
#include "src/base/logging.h"
#include "src/base/platform/elapsed-timer.h"
#include "src/base/platform/platform.h"
#include "src/baseline/baseline-batch-compiler.h"
#include "src/codegen/background-merge-task.h"
#include "src/common/globals.h"
#include "src/handles/maybe-handles.h"
#include "src/handles/persistent-handles.h"
#include "src/heap/heap-inl.h"
#include "src/heap/parked-scope.h"
#include "src/logging/counters-scopes.h"
#include "src/logging/log.h"
#include "src/logging/runtime-call-stats-scope.h"
#include "src/objects/objects-inl.h"
#include "src/objects/shared-function-info.h"
#include "src/objects/slots.h"
#include "src/objects/visitors.h"
#include "src/snapshot/object-deserializer.h"
#include "src/snapshot/snapshot-utils.h"
#include "src/snapshot/snapshot.h"
#include "src/utils/version.h"
namespace v8 {
namespace internal {
AlignedCachedData::AlignedCachedData(const uint8_t* data, int length)
: owns_data_(false), rejected_(false), data_(data), length_(length) {
if (!IsAligned(reinterpret_cast<intptr_t>(data), kPointerAlignment)) {
uint8_t* copy = NewArray<uint8_t>(length);
DCHECK(IsAligned(reinterpret_cast<intptr_t>(copy), kPointerAlignment));
CopyBytes(copy, data, length);
data_ = copy;
AcquireDataOwnership();
}
}
CodeSerializer::CodeSerializer(Isolate* isolate, uint32_t source_hash)
: Serializer(isolate, Snapshot::kDefaultSerializerFlags),
source_hash_(source_hash) {}
// static
ScriptCompiler::CachedData* CodeSerializer::Serialize(
Isolate* isolate, Handle<SharedFunctionInfo> info) {
TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8", "V8.SerializeCode");
NestedTimedHistogramScope histogram_timer(
isolate->counters()->compile_serialize());
RCS_SCOPE(isolate, RuntimeCallCounterId::kCompileSerialize);
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.CompileSerialize");
base::ElapsedTimer timer;
if (v8_flags.profile_deserialization) timer.Start();
DirectHandle<Script> script(Cast<Script>(info->script()), isolate);
if (v8_flags.trace_serializer) {
PrintF("[Serializing from");
ShortPrint(script->name());
PrintF("]\n");
}
#if V8_ENABLE_WEBASSEMBLY
// TODO(7110): Enable serialization of Asm modules once the AsmWasmData is
// context independent.
if (script->ContainsAsmModule()) return nullptr;
#endif // V8_ENABLE_WEBASSEMBLY
// Serialize code object.
DirectHandle<String> source(Cast<String>(script->source()), isolate);
DirectHandle<FixedArray> wrapped_arguments;
if (script->is_wrapped()) {
wrapped_arguments =
DirectHandle<FixedArray>(script->wrapped_arguments(), isolate);
}
HandleScope scope(isolate);
CodeSerializer cs(isolate,
SerializedCodeData::SourceHash(source, wrapped_arguments,
script->origin_options()));
DisallowGarbageCollection no_gc;
#ifndef DEBUG
cs.reference_map()->AddAttachedReference(*source);
#endif
AlignedCachedData* cached_data = cs.SerializeSharedFunctionInfo(info);
if (v8_flags.profile_deserialization) {
double ms = timer.Elapsed().InMillisecondsF();
int length = cached_data->length();
PrintF("[Serializing to %d bytes took %0.3f ms]\n", length, ms);
}
ScriptCompiler::CachedData* result =
new ScriptCompiler::CachedData(cached_data->data(), cached_data->length(),
ScriptCompiler::CachedData::BufferOwned);
cached_data->ReleaseDataOwnership();
delete cached_data;
return result;
}
AlignedCachedData* CodeSerializer::SerializeSharedFunctionInfo(
Handle<SharedFunctionInfo> info) {
DisallowGarbageCollection no_gc;
VisitRootPointer(Root::kHandleScope, nullptr,
FullObjectSlot(info.location()));
SerializeDeferredObjects();
Pad();
SerializedCodeData data(sink_.data(), this);
return data.GetScriptData();
}
void CodeSerializer::SerializeObjectImpl(Handle<HeapObject> obj,
SlotType slot_type) {
ReadOnlyRoots roots(isolate());
InstanceType instance_type;
{
DisallowGarbageCollection no_gc;
Tagged<HeapObject> raw = *obj;
if (SerializeHotObject(raw)) return;
if (SerializeRoot(raw)) return;
if (SerializeBackReference(raw)) return;
if (SerializeReadOnlyObjectReference(raw, &sink_)) return;
instance_type = raw->map()->instance_type();
CHECK(!InstanceTypeChecker::IsInstructionStream(instance_type));
}
if (InstanceTypeChecker::IsScript(instance_type)) {
DirectHandle<FixedArray> host_options;
DirectHandle<UnionOf<Smi, Symbol, Undefined>> context_data;
{
DisallowGarbageCollection no_gc;
Tagged<Script> script_obj = Cast<Script>(*obj);
DCHECK_NE(script_obj->compilation_type(), Script::CompilationType::kEval);
// We want to differentiate between undefined and uninitialized_symbol for
// context_data for now. It is hack to allow debugging for scripts that
// are included as a part of custom snapshot. (see
// debug::Script::IsEmbedded())
Tagged<UnionOf<Smi, Symbol, Undefined>> raw_context_data =
script_obj->context_data();
if (raw_context_data != roots.undefined_value() &&
raw_context_data != roots.uninitialized_symbol()) {
script_obj->set_context_data(roots.undefined_value());
}
context_data = direct_handle(raw_context_data, isolate());
// We don't want to serialize host options to avoid serializing
// unnecessary object graph.
host_options =
direct_handle(script_obj->host_defined_options(), isolate());
script_obj->set_host_defined_options(roots.empty_fixed_array());
}
SerializeGeneric(obj, slot_type);
{
DisallowGarbageCollection no_gc;
Tagged<Script> script_obj = Cast<Script>(*obj);
script_obj->set_host_defined_options(*host_options);
script_obj->set_context_data(*context_data);
}
return;
} else if (InstanceTypeChecker::IsSharedFunctionInfo(instance_type)) {
DirectHandle<DebugInfo> debug_info;
CachedTieringDecision cached_tiering_decision;
bool restore_bytecode = false;
{
DisallowGarbageCollection no_gc;
Tagged<SharedFunctionInfo> sfi = Cast<SharedFunctionInfo>(*obj);
DCHECK(!sfi->IsApiFunction());
#if V8_ENABLE_WEBASSEMBLY
// TODO(7110): Enable serializing of Asm modules once the AsmWasmData
// is context independent.
DCHECK(!sfi->HasAsmWasmData());
#endif // V8_ENABLE_WEBASSEMBLY
if (auto maybe_debug_info = sfi->TryGetDebugInfo(isolate())) {
debug_info = direct_handle(maybe_debug_info.value(), isolate());
// Clear debug info.
if (debug_info->HasInstrumentedBytecodeArray()) {
restore_bytecode = true;
sfi->SetActiveBytecodeArray(
debug_info->OriginalBytecodeArray(isolate()), isolate());
}
}
if (v8_flags.profile_guided_optimization) {
cached_tiering_decision = sfi->cached_tiering_decision();
if (cached_tiering_decision > CachedTieringDecision::kEarlySparkplug) {
sfi->set_cached_tiering_decision(
CachedTieringDecision::kEarlySparkplug);
}
}
}
SerializeGeneric(obj, slot_type);
DisallowGarbageCollection no_gc;
Tagged<SharedFunctionInfo> sfi = Cast<SharedFunctionInfo>(*obj);
if (restore_bytecode) {
sfi->SetActiveBytecodeArray(debug_info->DebugBytecodeArray(isolate()),
isolate());
}
if (v8_flags.profile_guided_optimization &&
cached_tiering_decision > CachedTieringDecision::kEarlySparkplug) {
sfi->set_cached_tiering_decision(cached_tiering_decision);
}
return;
} else if (InstanceTypeChecker::IsUncompiledDataWithoutPreparseDataWithJob(
instance_type)) {
Handle<UncompiledDataWithoutPreparseDataWithJob> data =
Cast<UncompiledDataWithoutPreparseDataWithJob>(obj);
Address job = data->job();
data->set_job(kNullAddress);
SerializeGeneric(data, slot_type);
data->set_job(job);
return;
} else if (InstanceTypeChecker::IsUncompiledDataWithPreparseDataAndJob(
instance_type)) {
Handle<UncompiledDataWithPreparseDataAndJob> data =
Cast<UncompiledDataWithPreparseDataAndJob>(obj);
Address job = data->job();
data->set_job(kNullAddress);
SerializeGeneric(data, slot_type);
data->set_job(job);
return;
} else if (InstanceTypeChecker::IsScopeInfo(instance_type)) {
// TODO(ishell): define a dedicated instance type for DependentCode and
// serialize DependentCode objects as an empty_dependent_code instead
// of customizing ScopeInfo serialization.
static_assert(DEPENDENT_CODE_TYPE == WEAK_ARRAY_LIST_TYPE);
Handle<ScopeInfo> scope_info = Cast<ScopeInfo>(obj);
DirectHandle<DependentCode> dependent_code;
bool restore_dependent_code = false;
if (scope_info->SloppyEvalCanExtendVars()) {
// If |scope_info| has a dependent code field, serialize it as an empty
// dependent code in order to avoid accidental serialization of optimized
// code.
Tagged<DependentCode> empty_dependent_code =
DependentCode::empty_dependent_code(ReadOnlyRoots(isolate()));
if (scope_info->dependent_code() != empty_dependent_code) {
dependent_code = direct_handle(scope_info->dependent_code(), isolate());
restore_dependent_code = true;
scope_info->set_dependent_code(empty_dependent_code);
}
}
SerializeGeneric(scope_info, slot_type);
if (restore_dependent_code) {
scope_info->set_dependent_code(*dependent_code);
}
return;
}
// NOTE(mmarchini): If we try to serialize an InterpreterData our process
// will crash since it stores a code object. Instead, we serialize the
// bytecode array stored within the InterpreterData, which is the important
// information. On deserialization we'll create our code objects again, if
// --interpreted-frames-native-stack is on. See v8:9122 for more context
if (V8_UNLIKELY(isolate()->interpreted_frames_native_stack()) &&
IsInterpreterData(*obj)) {
obj = handle(Cast<InterpreterData>(*obj)->bytecode_array(), isolate());
}
// Past this point we should not see any (context-specific) maps anymore.
CHECK(!InstanceTypeChecker::IsMap(instance_type));
// There should be no references to the global object embedded.
CHECK(!InstanceTypeChecker::IsJSGlobalProxy(instance_type) &&
!InstanceTypeChecker::IsJSGlobalObject(instance_type));
// Embedded FixedArrays that need rehashing must support rehashing.
CHECK_IMPLIES(obj->NeedsRehashing(cage_base()),
obj->CanBeRehashed(cage_base()));
// We expect no instantiated function objects or contexts.
CHECK(!InstanceTypeChecker::IsJSFunction(instance_type) &&
!InstanceTypeChecker::IsContext(instance_type));
SerializeGeneric(obj, slot_type);
}
void CodeSerializer::SerializeGeneric(Handle<HeapObject> heap_object,
SlotType slot_type) {
// Object has not yet been serialized. Serialize it here.
ObjectSerializer serializer(this, heap_object, &sink_);
serializer.Serialize(slot_type);
}
namespace {
// NOTE(mmarchini): when v8_flags.interpreted_frames_native_stack is on, we want
// to create duplicates of InterpreterEntryTrampoline for the deserialized
// functions, otherwise we'll call the builtin IET for those functions (which
// is not what a user of this flag wants).
void CreateInterpreterDataForDeserializedCode(
Isolate* isolate, DirectHandle<SharedFunctionInfo> result_sfi,
bool log_code_creation) {
DCHECK_IMPLIES(log_code_creation, isolate->NeedsSourcePositions());
DirectHandle<Script> script(Cast<Script>(result_sfi->script()), isolate);
if (log_code_creation) Script::InitLineEnds(isolate, script);
Tagged<String> name = ReadOnlyRoots(isolate).empty_string();
if (IsString(script->name())) name = Cast<String>(script->name());
DirectHandle<String> name_handle(name, isolate);
SharedFunctionInfo::ScriptIterator iter(isolate, *script);
for (Tagged<SharedFunctionInfo> shared_info = iter.Next();
!shared_info.is_null(); shared_info = iter.Next()) {
IsCompiledScope is_compiled(shared_info, isolate);
if (!is_compiled.is_compiled()) continue;
DCHECK(shared_info->HasBytecodeArray());
DirectHandle<SharedFunctionInfo> sfi(shared_info, isolate);
DirectHandle<BytecodeArray> bytecode(sfi->GetBytecodeArray(isolate),
isolate);
DirectHandle<Code> code =
Builtins::CreateInterpreterEntryTrampolineForProfiling(isolate);
DirectHandle<InterpreterData> interpreter_data =
isolate->factory()->NewInterpreterData(bytecode, code);
if (sfi->HasBaselineCode()) {
sfi->baseline_code(kAcquireLoad)
->set_bytecode_or_interpreter_data(*interpreter_data);
} else {
sfi->set_interpreter_data(isolate, *interpreter_data);
}
if (!log_code_creation) continue;
DirectHandle<AbstractCode> abstract_code = Cast<AbstractCode>(code);
Script::PositionInfo info;
Script::GetPositionInfo(script, sfi->StartPosition(), &info);
int line_num = info.line_start + 1;
int column_num = info.line_end + 1;
PROFILE(isolate,
CodeCreateEvent(LogEventListener::CodeTag::kFunction, abstract_code,
sfi, name_handle, line_num, column_num));
}
}
class StressOffThreadDeserializeThread final : public base::Thread {
public:
explicit StressOffThreadDeserializeThread(Isolate* isolate,
AlignedCachedData* cached_data)
: Thread(
base::Thread::Options("StressOffThreadDeserializeThread", 2 * MB)),
isolate_(isolate),
cached_data_(cached_data) {}
void Run() final {
LocalIsolate local_isolate(isolate_, ThreadKind::kBackground);
UnparkedScope unparked_scope(&local_isolate);
LocalHandleScope handle_scope(&local_isolate);
off_thread_data_ =
CodeSerializer::StartDeserializeOffThread(&local_isolate, cached_data_);
}
MaybeDirectHandle<SharedFunctionInfo> Finalize(
Isolate* isolate, DirectHandle<String> source,
const ScriptDetails& script_details) {
return CodeSerializer::FinishOffThreadDeserialize(
isolate, std::move(off_thread_data_), cached_data_, source,
script_details);
}
private:
Isolate* isolate_;
AlignedCachedData* cached_data_;
CodeSerializer::OffThreadDeserializeData off_thread_data_;
};
void FinalizeDeserialization(Isolate* isolate,
DirectHandle<SharedFunctionInfo> result,
const base::ElapsedTimer& timer,
const ScriptDetails& script_details) {
// Devtools can report time in this function as profiler overhead, since none
// of the following tasks would need to happen normally.
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.FinalizeDeserialization");
const bool log_code_creation = isolate->IsLoggingCodeCreation();
if (V8_UNLIKELY(isolate->interpreted_frames_native_stack())) {
CreateInterpreterDataForDeserializedCode(isolate, result,
log_code_creation);
}
DirectHandle<Script> script(Cast<Script>(result->script()), isolate);
// Reset the script details, including host-defined options.
{
DisallowGarbageCollection no_gc;
SetScriptFieldsFromDetails(isolate, *script, script_details, &no_gc);
}
bool needs_source_positions = isolate->NeedsSourcePositions();
if (!log_code_creation && !needs_source_positions) return;
if (needs_source_positions) {
Script::InitLineEnds(isolate, script);
}
DirectHandle<String> name(IsString(script->name())
? Cast<String>(script->name())
: ReadOnlyRoots(isolate).empty_string(),
isolate);
if (V8_UNLIKELY(v8_flags.log_function_events)) {
LOG(isolate,
FunctionEvent("deserialize", script->id(),
timer.Elapsed().InMillisecondsF(),
result->StartPosition(), result->EndPosition(), *name));
}
SharedFunctionInfo::ScriptIterator iter(isolate, *script);
for (Tagged<SharedFunctionInfo> info = iter.Next(); !info.is_null();
info = iter.Next()) {
if (!info->is_compiled()) continue;
DirectHandle<SharedFunctionInfo> shared_info(info, isolate);
if (needs_source_positions) {
SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, shared_info);
}
Script::PositionInfo pos_info;
Script::GetPositionInfo(script, shared_info->StartPosition(), &pos_info);
int line_num = pos_info.line + 1;
int column_num = pos_info.column + 1;
PROFILE(
isolate,
CodeCreateEvent(
shared_info->is_toplevel() ? LogEventListener::CodeTag::kScript
: LogEventListener::CodeTag::kFunction,
direct_handle(shared_info->abstract_code(isolate), isolate),
shared_info, name, line_num, column_num));
}
}
#ifdef V8_ENABLE_SPARKPLUG
void BaselineBatchCompileIfSparkplugCompiled(Isolate* isolate,
Tagged<Script> script) {
// Here is main thread, we trigger early baseline compilation only in
// concurrent sparkplug and baseline batch compilation mode which consumes
// little main thread execution time.
if (v8_flags.concurrent_sparkplug && v8_flags.baseline_batch_compilation) {
SharedFunctionInfo::ScriptIterator iter(isolate, script);
for (Tagged<SharedFunctionInfo> info = iter.Next(); !info.is_null();
info = iter.Next()) {
if (info->cached_tiering_decision() != CachedTieringDecision::kPending &&
CanCompileWithBaseline(isolate, info)) {
isolate->baseline_batch_compiler()->EnqueueSFI(info);
}
}
}
}
#else
void BaselineBatchCompileIfSparkplugCompiled(Isolate*, Tagged<Script>) {}
#endif // V8_ENABLE_SPARKPLUG
const char* ToString(SerializedCodeSanityCheckResult result) {
switch (result) {
case SerializedCodeSanityCheckResult::kSuccess:
return "success";
case SerializedCodeSanityCheckResult::kMagicNumberMismatch:
return "magic number mismatch";
case SerializedCodeSanityCheckResult::kVersionMismatch:
return "version mismatch";
case SerializedCodeSanityCheckResult::kSourceMismatch:
return "source mismatch";
case SerializedCodeSanityCheckResult::kFlagsMismatch:
return "flags mismatch";
case SerializedCodeSanityCheckResult::kChecksumMismatch:
return "checksum mismatch";
case SerializedCodeSanityCheckResult::kInvalidHeader:
return "invalid header";
case SerializedCodeSanityCheckResult::kLengthMismatch:
return "length mismatch";
case SerializedCodeSanityCheckResult::kReadOnlySnapshotChecksumMismatch:
return "read-only snapshot checksum mismatch";
}
}
} // namespace
MaybeDirectHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
Isolate* isolate, AlignedCachedData* cached_data,
DirectHandle<String> source, const ScriptDetails& script_details,
MaybeDirectHandle<Script> maybe_cached_script) {
if (v8_flags.stress_background_compile) {
StressOffThreadDeserializeThread thread(isolate, cached_data);
CHECK(thread.Start());
thread.Join();
return thread.Finalize(isolate, source, script_details);
// TODO(leszeks): Compare off-thread deserialized data to on-thread.
}
base::ElapsedTimer timer;
if (v8_flags.profile_deserialization || v8_flags.log_function_events) {
timer.Start();
}
HandleScope scope(isolate);
DirectHandle<FixedArray> wrapped_arguments;
if (!script_details.wrapped_arguments.is_null()) {
wrapped_arguments = script_details.wrapped_arguments.ToHandleChecked();
}
SerializedCodeSanityCheckResult sanity_check_result =
SerializedCodeSanityCheckResult::kSuccess;
const SerializedCodeData scd = SerializedCodeData::FromCachedData(
isolate, cached_data,
SerializedCodeData::SourceHash(source, wrapped_arguments,
script_details.origin_options),
&sanity_check_result);
if (sanity_check_result != SerializedCodeSanityCheckResult::kSuccess) {
if (v8_flags.profile_deserialization) {
PrintF("[Cached code failed check: %s]\n", ToString(sanity_check_result));
}
DCHECK(cached_data->rejected());
isolate->counters()->code_cache_reject_reason()->AddSample(
static_cast<int>(sanity_check_result));
return MaybeDirectHandle<SharedFunctionInfo>();
}
// Deserialize.
MaybeDirectHandle<SharedFunctionInfo> maybe_result =
ObjectDeserializer::DeserializeSharedFunctionInfo(isolate, &scd, source);
DirectHandle<SharedFunctionInfo> result;
if (!maybe_result.ToHandle(&result)) {
// Deserializing may fail if the reservations cannot be fulfilled.
if (v8_flags.profile_deserialization) PrintF("[Deserializing failed]\n");
return MaybeDirectHandle<SharedFunctionInfo>();
}
// Check whether the newly deserialized data should be merged into an
// existing Script from the Isolate compilation cache. If so, perform
// the merge in a single-threaded manner since this deserialization was
// single-threaded.
if (DirectHandle<Script> cached_script;
maybe_cached_script.ToHandle(&cached_script)) {
BackgroundMergeTask merge;
merge.SetUpOnMainThread(isolate, cached_script);
CHECK(merge.HasPendingBackgroundWork());
DirectHandle<Script> new_script(Cast<Script>(result->script()), isolate);
merge.BeginMergeInBackground(isolate->AsLocalIsolate(), new_script);
CHECK(merge.HasPendingForegroundWork());
result = merge.CompleteMergeInForeground(isolate, new_script);
}
Tagged<Script> script = Cast<Script>(result->script());
script->set_deserialized(true);
BaselineBatchCompileIfSparkplugCompiled(isolate, script);
if (v8_flags.profile_deserialization) {
double ms = timer.Elapsed().InMillisecondsF();
int length = cached_data->length();
PrintF("[Deserializing from %d bytes took %0.3f ms]\n", length, ms);
}
FinalizeDeserialization(isolate, result, timer, script_details);
return scope.CloseAndEscape(result);
}
DirectHandle<Script> CodeSerializer::OffThreadDeserializeData::GetOnlyScript(
LocalHeap* heap) {
std::unique_ptr<PersistentHandles> previous_persistent_handles =
heap->DetachPersistentHandles();
heap->AttachPersistentHandles(std::move(persistent_handles));
DCHECK_EQ(scripts.size(), 1);
// Make a non-persistent handle to return.
DirectHandle<Script> script = direct_handle(*scripts[0], heap);
DCHECK_EQ(*script, maybe_result.ToHandleChecked()->script());
persistent_handles = heap->DetachPersistentHandles();
if (previous_persistent_handles) {
heap->AttachPersistentHandles(std::move(previous_persistent_handles));
}
return script;
}
CodeSerializer::OffThreadDeserializeData
CodeSerializer::StartDeserializeOffThread(LocalIsolate* local_isolate,
AlignedCachedData* cached_data) {
OffThreadDeserializeData result;
DCHECK(!local_isolate->heap()->HasPersistentHandles());
const SerializedCodeData scd =
SerializedCodeData::FromCachedDataWithoutSource(
local_isolate, cached_data, &result.sanity_check_result);
if (result.sanity_check_result != SerializedCodeSanityCheckResult::kSuccess) {
// Exit early but don't report yet, we'll re-check this when finishing on
// the main thread
DCHECK(cached_data->rejected());
return result;
}
MaybeDirectHandle<SharedFunctionInfo> local_maybe_result =
OffThreadObjectDeserializer::DeserializeSharedFunctionInfo(
local_isolate, &scd, &result.scripts);
result.maybe_result =
local_isolate->heap()->NewPersistentMaybeHandle(local_maybe_result);
result.persistent_handles = local_isolate->heap()->DetachPersistentHandles();
return result;
}
MaybeDirectHandle<SharedFunctionInfo>
CodeSerializer::FinishOffThreadDeserialize(
Isolate* isolate, OffThreadDeserializeData&& data,
AlignedCachedData* cached_data, DirectHandle<String> source,
const ScriptDetails& script_details,
BackgroundMergeTask* background_merge_task) {
base::ElapsedTimer timer;
if (v8_flags.profile_deserialization || v8_flags.log_function_events) {
timer.Start();
}
HandleScope scope(isolate);
DirectHandle<FixedArray> wrapped_arguments;
if (!script_details.wrapped_arguments.is_null()) {
wrapped_arguments = script_details.wrapped_arguments.ToHandleChecked();
}
// Do a source sanity check now that we have the source. It's important for
// FromPartiallySanityCheckedCachedData call that the sanity_check_result
// holds the result of the off-thread sanity check.
SerializedCodeSanityCheckResult sanity_check_result =
data.sanity_check_result;
const SerializedCodeData scd =
SerializedCodeData::FromPartiallySanityCheckedCachedData(
cached_data,
SerializedCodeData::SourceHash(source, wrapped_arguments,
script_details.origin_options),
&sanity_check_result);
if (sanity_check_result != SerializedCodeSanityCheckResult::kSuccess) {
// The only case where the deserialization result could exist despite a
// check failure is on a source mismatch, since we can't test for this
// off-thread.
DCHECK_IMPLIES(!data.maybe_result.is_null(),
sanity_check_result ==
SerializedCodeSanityCheckResult::kSourceMismatch);
// The only kind of sanity check we can't test for off-thread is a source
// mismatch.
DCHECK_IMPLIES(sanity_check_result != data.sanity_check_result,
sanity_check_result ==
SerializedCodeSanityCheckResult::kSourceMismatch);
if (v8_flags.profile_deserialization) {
PrintF("[Cached code failed check: %s]\n", ToString(sanity_check_result));
}
DCHECK(cached_data->rejected());
isolate->counters()->code_cache_reject_reason()->AddSample(
static_cast<int>(sanity_check_result));
return MaybeDirectHandle<SharedFunctionInfo>();
}
Handle<SharedFunctionInfo> result;
if (!data.maybe_result.ToHandle(&result)) {
// Deserializing may fail if the reservations cannot be fulfilled.
if (v8_flags.profile_deserialization) {
PrintF("[Off-thread deserializing failed]\n");
}
return MaybeDirectHandle<SharedFunctionInfo>();
}
// Change the result persistent handle into a regular handle.
DCHECK(data.persistent_handles->Contains(result.location()));
result = handle(*result, isolate);
if (background_merge_task &&
background_merge_task->HasPendingForegroundWork()) {
DCHECK_EQ(data.scripts.size(), 1);
DirectHandle<Script> new_script = data.scripts[0];
result =
background_merge_task->CompleteMergeInForeground(isolate, new_script);
DCHECK(Object::StrictEquals(Cast<Script>(result->script())->source(),
*source));
DCHECK(isolate->factory()->script_list()->Contains(
MakeWeak(result->script())));
} else {
DirectHandle<Script> result_script(Cast<Script>(result->script()), isolate);
// Fix up the source on the script. This should be the only deserialized
// script, and the off-thread deserializer should have set its source to the
// empty string. In debug mode the code cache does contain the original
// source.
DCHECK_EQ(data.scripts.size(), 1);
DCHECK_EQ(*result_script, *data.scripts[0]);
#ifdef DEBUG
if (!Cast<String>(result_script->source())->Equals(*source)) {
isolate->PushStackTraceAndDie(
reinterpret_cast<void*>(result_script->source().ptr()),
reinterpret_cast<void*>(source->ptr()));
}
#else
CHECK_EQ(result_script->source(), ReadOnlyRoots(isolate).empty_string());
#endif
Script::SetSource(isolate, result_script, source);
// Fix up the script list to include the newly deserialized script.
Handle<WeakArrayList> list = isolate->factory()->script_list();
for (Handle<Script> script : data.scripts) {
script->set_deserialized(true);
BaselineBatchCompileIfSparkplugCompiled(isolate, *script);
DCHECK(data.persistent_handles->Contains(script.location()));
list = WeakArrayList::AddToEnd(isolate, list,
MaybeObjectDirectHandle::Weak(script));
}
isolate->heap()->SetRootScriptList(*list);
}
if (v8_flags.profile_deserialization) {
double ms = timer.Elapsed().InMillisecondsF();
int length = cached_data->length();
PrintF("[Finishing off-thread deserialize from %d bytes took %0.3f ms]\n",
length, ms);
}
FinalizeDeserialization(isolate, result, timer, script_details);
DCHECK(!background_merge_task ||
!background_merge_task->HasPendingForegroundWork());
return scope.CloseAndEscape(result);
}
SerializedCodeData::SerializedCodeData(const std::vector<uint8_t>* payload,
const CodeSerializer* cs) {
DisallowGarbageCollection no_gc;
// Calculate sizes.
uint32_t size = kHeaderSize + static_cast<uint32_t>(payload->size());
DCHECK(IsAligned(size, kPointerAlignment));
// Allocate backing store and create result data.
AllocateData(size);
// Zero out pre-payload data. Part of that is only used for padding.
memset(data_, 0, kHeaderSize);
// Set header values.
SetMagicNumber();
SetHeaderValue(kVersionHashOffset, Version::Hash());
SetHeaderValue(kSourceHashOffset, cs->source_hash());
SetHeaderValue(kFlagHashOffset, FlagList::Hash());
SetHeaderValue(kReadOnlySnapshotChecksumOffset,
Snapshot::ExtractReadOnlySnapshotChecksum(
cs->isolate()->snapshot_blob()));
SetHeaderValue(kPayloadLengthOffset, static_cast<uint32_t>(payload->size()));
// Zero out any padding in the header.
memset(data_ + kUnalignedHeaderSize, 0, kHeaderSize - kUnalignedHeaderSize);
// Copy serialized data.
CopyBytes(data_ + kHeaderSize, payload->data(),
static_cast<size_t>(payload->size()));
uint32_t checksum =
v8_flags.verify_snapshot_checksum ? Checksum(ChecksummedContent()) : 0;
SetHeaderValue(kChecksumOffset, checksum);
}
SerializedCodeSanityCheckResult SerializedCodeData::SanityCheck(
uint32_t expected_ro_snapshot_checksum,
uint32_t expected_source_hash) const {
SerializedCodeSanityCheckResult result =
SanityCheckWithoutSource(expected_ro_snapshot_checksum);
if (result != SerializedCodeSanityCheckResult::kSuccess) return result;
return SanityCheckJustSource(expected_source_hash);
}
SerializedCodeSanityCheckResult SerializedCodeData::SanityCheckJustSource(
uint32_t expected_source_hash) const {
uint32_t source_hash = GetHeaderValue(kSourceHashOffset);
if (source_hash != expected_source_hash) {
return SerializedCodeSanityCheckResult::kSourceMismatch;
}
return SerializedCodeSanityCheckResult::kSuccess;
}
SerializedCodeSanityCheckResult SerializedCodeData::SanityCheckWithoutSource(
uint32_t expected_ro_snapshot_checksum) const {
if (size_ < kHeaderSize) {
return SerializedCodeSanityCheckResult::kInvalidHeader;
}
uint32_t magic_number = GetMagicNumber();
if (magic_number != kMagicNumber) {
return SerializedCodeSanityCheckResult::kMagicNumberMismatch;
}
uint32_t version_hash = GetHeaderValue(kVersionHashOffset);
if (version_hash != Version::Hash()) {
return SerializedCodeSanityCheckResult::kVersionMismatch;
}
uint32_t flags_hash = GetHeaderValue(kFlagHashOffset);
if (flags_hash != FlagList::Hash()) {
return SerializedCodeSanityCheckResult::kFlagsMismatch;
}
uint32_t ro_snapshot_checksum =
GetHeaderValue(kReadOnlySnapshotChecksumOffset);
if (ro_snapshot_checksum != expected_ro_snapshot_checksum) {
return SerializedCodeSanityCheckResult::kReadOnlySnapshotChecksumMismatch;
}
uint32_t payload_length = GetHeaderValue(kPayloadLengthOffset);
uint32_t max_payload_length = size_ - kHeaderSize;
if (payload_length > max_payload_length) {
return SerializedCodeSanityCheckResult::kLengthMismatch;
}
if (v8_flags.verify_snapshot_checksum) {
uint32_t checksum = GetHeaderValue(kChecksumOffset);
if (Checksum(ChecksummedContent()) != checksum) {
return SerializedCodeSanityCheckResult::kChecksumMismatch;
}
}
return SerializedCodeSanityCheckResult::kSuccess;
}
uint32_t SerializedCodeData::SourceHash(
DirectHandle<String> source, DirectHandle<FixedArray> wrapped_arguments,
ScriptOriginOptions origin_options) {
using LengthField = base::BitField<uint32_t, 0, 29>;
static_assert(String::kMaxLength <= LengthField::kMax,
"String length must fit into a LengthField");
using HasWrappedArgumentsField = LengthField::Next<bool, 1>;
using IsModuleField = HasWrappedArgumentsField::Next<bool, 1>;
uint32_t hash = 0;
hash = LengthField::update(hash, source->length());
hash = HasWrappedArgumentsField::update(hash, !wrapped_arguments.is_null());
hash = IsModuleField::update(hash, origin_options.IsModule());
return hash;
}
// Return ScriptData object and relinquish ownership over it to the caller.
AlignedCachedData* SerializedCodeData::GetScriptData() {
DCHECK(owns_data_);
AlignedCachedData* result = new AlignedCachedData(data_, size_);
result->AcquireDataOwnership();
owns_data_ = false;
data_ = nullptr;
return result;
}
base::Vector<const uint8_t> SerializedCodeData::Payload() const {
const uint8_t* payload = data_ + kHeaderSize;
DCHECK(IsAligned(reinterpret_cast<intptr_t>(payload), kPointerAlignment));
int length = GetHeaderValue(kPayloadLengthOffset);
DCHECK_EQ(data_ + size_, payload + length);
return base::Vector<const uint8_t>(payload, length);
}
SerializedCodeData::SerializedCodeData(AlignedCachedData* data)
: SerializedData(const_cast<uint8_t*>(data->data()), data->length()) {}
SerializedCodeData SerializedCodeData::FromCachedData(
Isolate* isolate, AlignedCachedData* cached_data,
uint32_t expected_source_hash,
SerializedCodeSanityCheckResult* rejection_result) {
DisallowGarbageCollection no_gc;
SerializedCodeData scd(cached_data);
*rejection_result = scd.SanityCheck(
Snapshot::ExtractReadOnlySnapshotChecksum(isolate->snapshot_blob()),
expected_source_hash);
if (*rejection_result != SerializedCodeSanityCheckResult::kSuccess) {
cached_data->Reject();
return SerializedCodeData(nullptr, 0);
}
return scd;
}
SerializedCodeData SerializedCodeData::FromCachedDataWithoutSource(
LocalIsolate* local_isolate, AlignedCachedData* cached_data,
SerializedCodeSanityCheckResult* rejection_result) {
DisallowGarbageCollection no_gc;
SerializedCodeData scd(cached_data);
*rejection_result =
scd.SanityCheckWithoutSource(Snapshot::ExtractReadOnlySnapshotChecksum(
local_isolate->snapshot_blob()));
if (*rejection_result != SerializedCodeSanityCheckResult::kSuccess) {
cached_data->Reject();
return SerializedCodeData(nullptr, 0);
}
return scd;
}
SerializedCodeData SerializedCodeData::FromPartiallySanityCheckedCachedData(
AlignedCachedData* cached_data, uint32_t expected_source_hash,
SerializedCodeSanityCheckResult* rejection_result) {
DisallowGarbageCollection no_gc;
// The previous call to FromCachedDataWithoutSource may have already rejected
// the cached data, so reuse the previous rejection result if it's not a
// success.
if (*rejection_result != SerializedCodeSanityCheckResult::kSuccess) {
// FromCachedDataWithoutSource doesn't check the source, so there can't be
// a source mismatch.
DCHECK_NE(*rejection_result,
SerializedCodeSanityCheckResult::kSourceMismatch);
cached_data->Reject();
return SerializedCodeData(nullptr, 0);
}
SerializedCodeData scd(cached_data);
*rejection_result = scd.SanityCheckJustSource(expected_source_hash);
if (*rejection_result != SerializedCodeSanityCheckResult::kSuccess) {
// This check only checks the source, so the only possible failure is a
// source mismatch.
DCHECK_EQ(*rejection_result,
SerializedCodeSanityCheckResult::kSourceMismatch);
cached_data->Reject();
return SerializedCodeData(nullptr, 0);
}
return scd;
}
} // namespace internal
} // namespace v8

181
deps/v8/src/snapshot/code-serializer.h vendored Normal file
View File

@ -0,0 +1,181 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_CODE_SERIALIZER_H_
#define V8_SNAPSHOT_CODE_SERIALIZER_H_
#include "src/base/macros.h"
#include "src/codegen/script-details.h"
#include "src/snapshot/serializer.h"
#include "src/snapshot/snapshot-data.h"
namespace v8 {
namespace internal {
class PersistentHandles;
class BackgroundMergeTask;
class V8_EXPORT_PRIVATE AlignedCachedData {
public:
AlignedCachedData(const uint8_t* data, int length);
~AlignedCachedData() {
if (owns_data_) DeleteArray(data_);
}
AlignedCachedData(const AlignedCachedData&) = delete;
AlignedCachedData& operator=(const AlignedCachedData&) = delete;
const uint8_t* data() const { return data_; }
int length() const { return length_; }
bool rejected() const { return rejected_; }
void Reject() { rejected_ = true; }
bool HasDataOwnership() const { return owns_data_; }
void AcquireDataOwnership() {
DCHECK(!owns_data_);
owns_data_ = true;
}
void ReleaseDataOwnership() {
DCHECK(owns_data_);
owns_data_ = false;
}
private:
bool owns_data_ : 1;
bool rejected_ : 1;
const uint8_t* data_;
int length_;
};
typedef v8::ScriptCompiler::CachedData::CompatibilityCheckResult
SerializedCodeSanityCheckResult;
// If this fails, update the static_assert AND the code_cache_reject_reason
// histogram definition.
static_assert(static_cast<int>(SerializedCodeSanityCheckResult::kLast) == 9);
class CodeSerializer : public Serializer {
public:
struct OffThreadDeserializeData {
public:
bool HasResult() const { return !maybe_result.is_null(); }
DirectHandle<Script> GetOnlyScript(LocalHeap* heap);
private:
friend class CodeSerializer;
MaybeIndirectHandle<SharedFunctionInfo> maybe_result;
std::vector<IndirectHandle<Script>> scripts;
std::unique_ptr<PersistentHandles> persistent_handles;
SerializedCodeSanityCheckResult sanity_check_result;
};
CodeSerializer(const CodeSerializer&) = delete;
CodeSerializer& operator=(const CodeSerializer&) = delete;
V8_EXPORT_PRIVATE static ScriptCompiler::CachedData* Serialize(
Isolate* isolate, Handle<SharedFunctionInfo> info);
AlignedCachedData* SerializeSharedFunctionInfo(
Handle<SharedFunctionInfo> info);
V8_WARN_UNUSED_RESULT static MaybeDirectHandle<SharedFunctionInfo>
Deserialize(Isolate* isolate, AlignedCachedData* cached_data,
DirectHandle<String> source, const ScriptDetails& script_details,
MaybeDirectHandle<Script> maybe_cached_script = {});
V8_WARN_UNUSED_RESULT static OffThreadDeserializeData
StartDeserializeOffThread(LocalIsolate* isolate,
AlignedCachedData* cached_data);
V8_WARN_UNUSED_RESULT static MaybeDirectHandle<SharedFunctionInfo>
FinishOffThreadDeserialize(
Isolate* isolate, OffThreadDeserializeData&& data,
AlignedCachedData* cached_data, DirectHandle<String> source,
const ScriptDetails& script_details,
BackgroundMergeTask* background_merge_task = nullptr);
uint32_t source_hash() const { return source_hash_; }
protected:
CodeSerializer(Isolate* isolate, uint32_t source_hash);
~CodeSerializer() override { OutputStatistics("CodeSerializer"); }
void SerializeGeneric(Handle<HeapObject> heap_object, SlotType slot_type);
private:
void SerializeObjectImpl(Handle<HeapObject> o, SlotType slot_type) override;
DISALLOW_GARBAGE_COLLECTION(no_gc_)
uint32_t source_hash_;
};
// Wrapper around ScriptData to provide code-serializer-specific functionality.
class SerializedCodeData : public SerializedData {
public:
// The data header consists of uint32_t-sized entries:
static const uint32_t kVersionHashOffset = kMagicNumberOffset + kUInt32Size;
static const uint32_t kSourceHashOffset = kVersionHashOffset + kUInt32Size;
static const uint32_t kFlagHashOffset = kSourceHashOffset + kUInt32Size;
static const uint32_t kReadOnlySnapshotChecksumOffset =
kFlagHashOffset + kUInt32Size;
static const uint32_t kPayloadLengthOffset =
kReadOnlySnapshotChecksumOffset + kUInt32Size;
static const uint32_t kChecksumOffset = kPayloadLengthOffset + kUInt32Size;
static const uint32_t kUnalignedHeaderSize = kChecksumOffset + kUInt32Size;
static const uint32_t kHeaderSize = POINTER_SIZE_ALIGN(kUnalignedHeaderSize);
// Used when consuming.
static SerializedCodeData FromCachedData(
Isolate* isolate, AlignedCachedData* cached_data,
uint32_t expected_source_hash,
SerializedCodeSanityCheckResult* rejection_result);
// For cached data which is consumed before the source is available (e.g.
// off-thread).
static SerializedCodeData FromCachedDataWithoutSource(
LocalIsolate* local_isolate, AlignedCachedData* cached_data,
SerializedCodeSanityCheckResult* rejection_result);
// For cached data which was previously already sanity checked by
// FromCachedDataWithoutSource. The rejection result from that call should be
// passed into this one.
static SerializedCodeData FromPartiallySanityCheckedCachedData(
AlignedCachedData* cached_data, uint32_t expected_source_hash,
SerializedCodeSanityCheckResult* rejection_result);
// Used when producing.
SerializedCodeData(const std::vector<uint8_t>* payload,
const CodeSerializer* cs);
// Return ScriptData object and relinquish ownership over it to the caller.
AlignedCachedData* GetScriptData();
base::Vector<const uint8_t> Payload() const;
static uint32_t SourceHash(DirectHandle<String> source,
DirectHandle<FixedArray> wrapped_arguments,
ScriptOriginOptions origin_options);
private:
explicit SerializedCodeData(AlignedCachedData* data);
SerializedCodeData(const uint8_t* data, int size)
: SerializedData(const_cast<uint8_t*>(data), size) {}
base::Vector<const uint8_t> ChecksummedContent() const {
return base::Vector<const uint8_t>(data_ + kHeaderSize,
size_ - kHeaderSize);
}
SerializedCodeSanityCheckResult SanityCheck(
uint32_t expected_ro_snapshot_checksum,
uint32_t expected_source_hash) const;
SerializedCodeSanityCheckResult SanityCheckJustSource(
uint32_t expected_source_hash) const;
SerializedCodeSanityCheckResult SanityCheckWithoutSource(
uint32_t expected_ro_snapshot_checksum) const;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_CODE_SERIALIZER_H_

View File

@ -0,0 +1,167 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/context-deserializer.h"
#include "src/api/api-inl.h"
#include "src/base/logging.h"
#include "src/common/assert-scope.h"
#include "src/logging/counters-scopes.h"
#include "src/snapshot/serializer-deserializer.h"
namespace v8 {
namespace internal {
// static
MaybeDirectHandle<Context> ContextDeserializer::DeserializeContext(
Isolate* isolate, const SnapshotData* data, size_t context_index,
bool can_rehash, DirectHandle<JSGlobalProxy> global_proxy,
DeserializeEmbedderFieldsCallback embedder_fields_deserializer) {
TRACE_EVENT0("v8", "V8.DeserializeContext");
RCS_SCOPE(isolate, RuntimeCallCounterId::kDeserializeContext);
base::ElapsedTimer timer;
if (V8_UNLIKELY(v8_flags.profile_deserialization)) timer.Start();
NestedTimedHistogramScope histogram_timer(
isolate->counters()->snapshot_deserialize_context());
ContextDeserializer d(isolate, data, can_rehash);
MaybeDirectHandle<Object> maybe_result =
d.Deserialize(isolate, global_proxy, embedder_fields_deserializer);
if (V8_UNLIKELY(v8_flags.profile_deserialization)) {
// ATTENTION: The Memory.json benchmark greps for this exact output. Do not
// change it without also updating Memory.json.
const int bytes = static_cast<int>(data->RawData().size());
const double ms = timer.Elapsed().InMillisecondsF();
PrintF("[Deserializing context #%zu (%d bytes) took %0.3f ms]\n",
context_index, bytes, ms);
}
DirectHandle<Object> result;
if (!maybe_result.ToHandle(&result)) return {};
return Cast<Context>(result);
}
MaybeDirectHandle<Object> ContextDeserializer::Deserialize(
Isolate* isolate, DirectHandle<JSGlobalProxy> global_proxy,
DeserializeEmbedderFieldsCallback embedder_fields_deserializer) {
// Replace serialized references to the global proxy and its map with the
// given global proxy and its map.
AddAttachedObject(global_proxy);
AddAttachedObject(direct_handle(global_proxy->map(), isolate));
DirectHandle<Object> result;
{
// There's no code deserialized here. If this assert fires then that's
// changed and logging should be added to notify the profiler et al. of
// the new code, which also has to be flushed from instruction cache.
DisallowCodeAllocation no_code_allocation;
result = ReadObject();
DCHECK(IsNativeContext(*result));
DeserializeDeferredObjects();
DeserializeEmbedderFields(Cast<NativeContext>(result),
embedder_fields_deserializer);
DeserializeApiWrapperFields(
embedder_fields_deserializer.api_wrapper_callback);
LogNewMapEvents();
WeakenDescriptorArrays();
}
if (should_rehash()) Rehash();
return result;
}
template <typename T>
class PlainBuffer {
public:
T* data() { return data_.get(); }
void EnsureCapacity(size_t new_capacity) {
if (new_capacity > capacity_) {
data_.reset(new T[new_capacity]);
capacity_ = new_capacity;
}
}
private:
std::unique_ptr<T[]> data_;
size_t capacity_{0};
};
void ContextDeserializer::DeserializeEmbedderFields(
DirectHandle<NativeContext> context,
DeserializeEmbedderFieldsCallback embedder_fields_deserializer) {
if (!source()->HasMore() || source()->Peek() != kEmbedderFieldsData) {
return;
}
// Consume `kEmbedderFieldsData`.
source()->Get();
DisallowGarbageCollection no_gc;
DisallowJavascriptExecution no_js(isolate());
DisallowCompilation no_compile(isolate());
// Buffer is reused across various deserializations. We always copy N bytes
// into the backing and pass that N bytes to the embedder via StartupData.
PlainBuffer<char> buffer;
for (int code = source()->Get(); code != kSynchronize;
code = source()->Get()) {
HandleScope scope(isolate());
DirectHandle<HeapObject> heap_object =
Cast<HeapObject>(GetBackReferencedObject());
const int index = source()->GetUint30();
const int size = source()->GetUint30();
buffer.EnsureCapacity(size);
source()->CopyRaw(buffer.data(), size);
if (IsJSObject(*heap_object)) {
DirectHandle<JSObject> obj = Cast<JSObject>(heap_object);
v8::DeserializeInternalFieldsCallback callback =
embedder_fields_deserializer.js_object_callback;
DCHECK_NOT_NULL(callback.callback);
callback.callback(v8::Utils::ToLocal(obj), index, {buffer.data(), size},
callback.data);
} else {
DCHECK(IsEmbedderDataArray(*heap_object));
v8::DeserializeContextDataCallback callback =
embedder_fields_deserializer.context_callback;
DCHECK_NOT_NULL(callback.callback);
callback.callback(v8::Utils::ToLocal(context), index,
{buffer.data(), size}, callback.data);
}
}
}
void ContextDeserializer::DeserializeApiWrapperFields(
const v8::DeserializeAPIWrapperCallback& api_wrapper_callback) {
if (!source()->HasMore() || source()->Peek() != kApiWrapperFieldsData) {
return;
}
// Consume `kApiWrapperFieldsData`.
source()->Get();
DisallowGarbageCollection no_gc;
DisallowJavascriptExecution no_js(isolate());
DisallowCompilation no_compile(isolate());
// Buffer is reused across various deserializations. We always copy N bytes
// into the backing and pass that N bytes to the embedder via StartupData.
PlainBuffer<char> buffer;
// The block for `kApiWrapperFieldsData` consists of consecutive `kNewObject`
// blocks that are in the end terminated with a `kSynchronize`.
for (int code = source()->Get(); code != kSynchronize;
code = source()->Get()) {
HandleScope scope(isolate());
DirectHandle<JSObject> js_object =
Cast<JSObject>(GetBackReferencedObject());
const int size = source()->GetUint30();
buffer.EnsureCapacity(size);
source()->CopyRaw(buffer.data(), size);
DCHECK_NOT_NULL(api_wrapper_callback.callback);
api_wrapper_callback.callback(v8::Utils::ToLocal(js_object),
{buffer.data(), size},
api_wrapper_callback.data);
}
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,49 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_CONTEXT_DESERIALIZER_H_
#define V8_SNAPSHOT_CONTEXT_DESERIALIZER_H_
#include "src/snapshot/deserializer.h"
#include "src/snapshot/snapshot-data.h"
namespace v8 {
namespace internal {
class Context;
class Isolate;
// Deserializes the context-dependent object graph rooted at a given object.
// The ContextDeserializer is not expected to deserialize any code objects.
class V8_EXPORT_PRIVATE ContextDeserializer final
: public Deserializer<Isolate> {
public:
static MaybeDirectHandle<Context> DeserializeContext(
Isolate* isolate, const SnapshotData* data, size_t context_index,
bool can_rehash, DirectHandle<JSGlobalProxy> global_proxy,
DeserializeEmbedderFieldsCallback embedder_fields_deserializer);
private:
explicit ContextDeserializer(Isolate* isolate, const SnapshotData* data,
bool can_rehash)
: Deserializer(isolate, data->Payload(), data->GetMagicNumber(), false,
can_rehash) {}
// Deserialize a single object and the objects reachable from it.
MaybeDirectHandle<Object> Deserialize(
Isolate* isolate, DirectHandle<JSGlobalProxy> global_proxy,
DeserializeEmbedderFieldsCallback embedder_fields_deserializer);
void DeserializeEmbedderFields(
DirectHandle<NativeContext> context,
DeserializeEmbedderFieldsCallback embedder_fields_deserializer);
void DeserializeApiWrapperFields(
const v8::DeserializeAPIWrapperCallback& api_wrapper_callback);
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_CONTEXT_DESERIALIZER_H_

View File

@ -0,0 +1,412 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/context-serializer.h"
#include "src/api/api-inl.h"
#include "src/execution/microtask-queue.h"
#include "src/heap/combined-heap.h"
#include "src/numbers/math-random.h"
#include "src/objects/embedder-data-array-inl.h"
#include "src/objects/js-objects.h"
#include "src/objects/objects-inl.h"
#include "src/objects/slots.h"
#include "src/snapshot/serializer-deserializer.h"
#include "src/snapshot/startup-serializer.h"
namespace v8 {
namespace internal {
namespace {
// During serialization, puts the native context into a state understood by the
// serializer (e.g. by clearing lists of InstructionStream objects). After
// serialization, the original state is restored.
class V8_NODISCARD SanitizeNativeContextScope final {
public:
SanitizeNativeContextScope(Isolate* isolate,
Tagged<NativeContext> native_context,
bool allow_active_isolate_for_testing,
const DisallowGarbageCollection& no_gc)
: native_context_(native_context), no_gc_(no_gc) {
#ifdef DEBUG
if (!allow_active_isolate_for_testing) {
// Microtasks.
MicrotaskQueue* microtask_queue = native_context_->microtask_queue();
DCHECK_EQ(0, microtask_queue->size());
DCHECK(!microtask_queue->HasMicrotasksSuppressions());
DCHECK_EQ(0, microtask_queue->GetMicrotasksScopeDepth());
DCHECK(microtask_queue->DebugMicrotasksScopeDepthIsZero());
}
#endif
microtask_queue_external_pointer_ =
native_context
->RawExternalPointerField(NativeContext::kMicrotaskQueueOffset,
kNativeContextMicrotaskQueueTag)
.GetAndClearContentForSerialization(no_gc);
}
~SanitizeNativeContextScope() {
// Restore saved fields.
native_context_
->RawExternalPointerField(NativeContext::kMicrotaskQueueOffset,
kNativeContextMicrotaskQueueTag)
.RestoreContentAfterSerialization(microtask_queue_external_pointer_,
no_gc_);
}
private:
Tagged<NativeContext> native_context_;
ExternalPointerSlot::RawContent microtask_queue_external_pointer_;
const DisallowGarbageCollection& no_gc_;
};
} // namespace
ContextSerializer::ContextSerializer(Isolate* isolate,
Snapshot::SerializerFlags flags,
StartupSerializer* startup_serializer,
SerializeEmbedderFieldsCallback callback)
: Serializer(isolate, flags),
startup_serializer_(startup_serializer),
serialize_embedder_fields_(callback),
can_be_rehashed_(true) {
InitializeCodeAddressMap();
}
ContextSerializer::~ContextSerializer() {
OutputStatistics("ContextSerializer");
}
void ContextSerializer::Serialize(Tagged<Context>* o,
const DisallowGarbageCollection& no_gc) {
context_ = *o;
DCHECK(IsNativeContext(context_));
// Upon deserialization, references to the global proxy and its map will be
// replaced.
reference_map()->AddAttachedReference(context_->global_proxy());
reference_map()->AddAttachedReference(context_->global_proxy()->map());
// The bootstrap snapshot has a code-stub context. When serializing the
// context snapshot, it is chained into the weak context list on the isolate
// and it's next context pointer may point to the code-stub context. Clear
// it before serializing, it will get re-added to the context list
// explicitly when it's loaded.
// TODO(v8:10416): These mutations should not observably affect the running
// context.
context_->set(Context::NEXT_CONTEXT_LINK,
ReadOnlyRoots(isolate()).undefined_value());
DCHECK(!IsUndefined(context_->global_object()));
// Reset math random cache to get fresh random numbers.
MathRandom::ResetContext(context_);
SanitizeNativeContextScope sanitize_native_context(
isolate(), context_->native_context(), allow_active_isolate_for_testing(),
no_gc);
VisitRootPointer(Root::kStartupObjectCache, nullptr, FullObjectSlot(o));
SerializeDeferredObjects();
// Add section for embedder-serialized embedder fields.
if (!embedder_fields_sink_.data()->empty()) {
sink_.Put(kEmbedderFieldsData, "embedder fields data");
sink_.Append(embedder_fields_sink_);
sink_.Put(kSynchronize, "Finished with embedder fields data");
}
// Add section for embedder-serializer API wrappers.
if (!api_wrapper_sink_.data()->empty()) {
sink_.Put(kApiWrapperFieldsData, "api wrapper fields data");
sink_.Append(api_wrapper_sink_);
sink_.Put(kSynchronize, "Finished with api wrapper fields data");
}
Pad();
}
v8::StartupData InternalFieldSerializeWrapper(
int index, bool field_is_nullptr,
v8::SerializeInternalFieldsCallback user_callback,
v8::Local<v8::Object> api_obj) {
// If no serializer is provided and the field was empty, we
// serialize it by default to nullptr.
if (user_callback.callback == nullptr && field_is_nullptr) {
return StartupData{nullptr, 0};
}
DCHECK(user_callback.callback);
return user_callback.callback(api_obj, index, user_callback.data);
}
v8::StartupData ContextDataSerializeWrapper(
int index, bool field_is_nullptr,
v8::SerializeContextDataCallback user_callback,
v8::Local<v8::Context> api_obj) {
// For compatibility, we do not require all non-null context pointer
// fields to be serialized by a proper user callback. Instead, if no
// user callback is provided, we serialize it verbatim, which was
// the old behavior before we introduce context data callbacks.
if (user_callback.callback == nullptr) {
return StartupData{nullptr, 0};
}
return user_callback.callback(api_obj, index, user_callback.data);
}
void ContextSerializer::SerializeObjectImpl(Handle<HeapObject> obj,
SlotType slot_type) {
DCHECK(!ObjectIsBytecodeHandler(*obj)); // Only referenced in dispatch table.
if (!allow_active_isolate_for_testing()) {
// When serializing a snapshot intended for real use, we should not end up
// at another native context.
// But in test scenarios there is no way to avoid this. Since we only
// serialize a single context in these cases, and this context does not
// have to be executable, we can simply ignore this.
DCHECK_IMPLIES(IsNativeContext(*obj), *obj == context_);
}
{
DisallowGarbageCollection no_gc;
Tagged<HeapObject> raw = *obj;
if (SerializeHotObject(raw)) return;
if (SerializeRoot(raw)) return;
if (SerializeBackReference(raw)) return;
if (SerializeReadOnlyObjectReference(raw, &sink_)) return;
}
if (startup_serializer_->SerializeUsingSharedHeapObjectCache(&sink_, obj)) {
return;
}
if (ShouldBeInTheStartupObjectCache(*obj)) {
startup_serializer_->SerializeUsingStartupObjectCache(&sink_, obj);
return;
}
// Pointers from the context snapshot to the objects in the startup snapshot
// should go through the root array or through the startup object cache.
// If this is not the case you may have to add something to the root array.
DCHECK(!startup_serializer_->ReferenceMapContains(obj));
// All the internalized strings that the context snapshot needs should be
// either in the root table or in the shared heap object cache.
DCHECK(!IsInternalizedString(*obj));
// Function and object templates are not context specific.
DCHECK(!IsTemplateInfo(*obj));
InstanceType instance_type = obj->map()->instance_type();
if (InstanceTypeChecker::IsFeedbackVector(instance_type)) {
// Clear literal boilerplates and feedback.
Cast<FeedbackVector>(obj)->ClearSlots(isolate());
} else if (InstanceTypeChecker::IsJSObject(instance_type)) {
Handle<JSObject> js_obj = Cast<JSObject>(obj);
int embedder_fields_count = js_obj->GetEmbedderFieldCount();
if (embedder_fields_count > 0) {
DCHECK(!js_obj->NeedsRehashing(cage_base()));
v8::Local<v8::Object> api_obj = v8::Utils::ToLocal(js_obj);
v8::SerializeInternalFieldsCallback user_callback =
serialize_embedder_fields_.js_object_callback;
SerializeObjectWithEmbedderFields(js_obj, embedder_fields_count,
InternalFieldSerializeWrapper,
user_callback, api_obj);
if (IsJSApiWrapperObject(*js_obj)) {
SerializeApiWrapperFields(js_obj);
}
return;
}
if (InstanceTypeChecker::IsJSFunction(instance_type)) {
DisallowGarbageCollection no_gc;
// Unconditionally reset the JSFunction to its SFI's code, since we can't
// serialize optimized code anyway.
Tagged<JSFunction> closure = Cast<JSFunction>(*obj);
if (closure->shared()->HasBytecodeArray()) {
closure->SetInterruptBudget(isolate(), BudgetModification::kReset);
}
closure->ResetIfCodeFlushed(isolate());
if (closure->is_compiled(isolate())) {
if (closure->shared()->HasBaselineCode()) {
closure->shared()->FlushBaselineCode();
}
Tagged<Code> sfi_code = closure->shared()->GetCode(isolate());
if (!sfi_code.SafeEquals(closure->code(isolate()))) {
closure->UpdateCode(sfi_code);
}
}
}
} else if (InstanceTypeChecker::IsEmbedderDataArray(instance_type) &&
!allow_active_isolate_for_testing()) {
DCHECK_EQ(*obj, context_->embedder_data());
Handle<EmbedderDataArray> embedder_data = Cast<EmbedderDataArray>(obj);
int embedder_fields_count = embedder_data->length();
if (embedder_data->length() > 0) {
DirectHandle<Context> context_handle(context_, isolate());
v8::Local<v8::Context> api_obj =
v8::Utils::ToLocal(Cast<NativeContext>(context_handle));
v8::SerializeContextDataCallback user_callback =
serialize_embedder_fields_.context_callback;
SerializeObjectWithEmbedderFields(embedder_data, embedder_fields_count,
ContextDataSerializeWrapper,
user_callback, api_obj);
return;
}
}
CheckRehashability(*obj);
// Object has not yet been serialized. Serialize it here.
ObjectSerializer serializer(this, obj, &sink_);
serializer.Serialize(slot_type);
if (IsJSApiWrapperObject(obj->map())) {
SerializeApiWrapperFields(Cast<JSObject>(obj));
}
}
bool ContextSerializer::ShouldBeInTheStartupObjectCache(Tagged<HeapObject> o) {
// We can't allow scripts to be part of the context snapshot because they
// contain a unique ID, and deserializing several context snapshots containing
// script would cause dupes.
return IsName(o) || IsScript(o) || IsSharedFunctionInfo(o) ||
IsHeapNumber(o) || IsCode(o) || IsInstructionStream(o) ||
IsScopeInfo(o) || IsAccessorInfo(o) || IsTemplateInfo(o) ||
IsClassPositions(o) ||
o->map() == ReadOnlyRoots(isolate()).fixed_cow_array_map();
}
bool ContextSerializer::ShouldBeInTheSharedObjectCache(Tagged<HeapObject> o) {
// v8_flags.shared_string_table may be true during deserialization, so put
// internalized strings into the shared object snapshot.
return IsInternalizedString(o);
}
namespace {
bool DataIsEmpty(const StartupData& data) { return data.raw_size == 0; }
} // anonymous namespace
void ContextSerializer::SerializeApiWrapperFields(
DirectHandle<JSObject> js_object) {
DCHECK(IsJSApiWrapperObject(*js_object));
auto* cpp_heap_pointer =
JSApiWrapper(*js_object)
.GetCppHeapWrappable(isolate(), kAnyCppHeapPointer);
const auto& callback_data = serialize_embedder_fields_.api_wrapper_callback;
if (callback_data.callback == nullptr && cpp_heap_pointer == nullptr) {
// No need to serialize anything as empty handles or handles pointing to
// null objects will be preserved.
return;
}
DCHECK_NOT_NULL(callback_data.callback);
const auto data = callback_data.callback(
v8::Utils::ToLocal(js_object), cpp_heap_pointer, callback_data.data);
if (DataIsEmpty(data)) {
return;
}
const SerializerReference* reference =
reference_map()->LookupReference(*js_object);
DCHECK_NOT_NULL(reference);
DCHECK(reference->is_back_reference());
api_wrapper_sink_.Put(kNewObject, "api wrapper field holder");
api_wrapper_sink_.PutUint30(reference->back_ref_index(), "BackRefIndex");
api_wrapper_sink_.PutUint30(data.raw_size, "api wrapper raw field data size");
api_wrapper_sink_.PutRaw(reinterpret_cast<const uint8_t*>(data.data),
data.raw_size, "api wrapper raw field data");
}
template <typename V8Type, typename UserSerializerWrapper,
typename UserCallback, typename ApiObjectType>
void ContextSerializer::SerializeObjectWithEmbedderFields(
Handle<V8Type> data_holder, int embedder_fields_count,
UserSerializerWrapper wrapper, UserCallback user_callback,
ApiObjectType api_obj) {
DisallowGarbageCollection no_gc;
CHECK_GT(embedder_fields_count, 0);
DisallowJavascriptExecution no_js(isolate());
DisallowCompilation no_compile(isolate());
auto raw_obj = *data_holder;
std::vector<EmbedderDataSlot::RawData> original_embedder_values;
std::vector<StartupData> serialized_data;
std::vector<bool> should_clear_slot;
// 1) Iterate embedder fields. Hold onto the original value of the fields.
// Ignore references to heap objects since these are to be handled by the
// serializer. For aligned pointers, call the serialize callback. Hold
// onto the result.
for (int i = 0; i < embedder_fields_count; i++) {
EmbedderDataSlot slot(raw_obj, i);
original_embedder_values.emplace_back(slot.load_raw(isolate(), no_gc));
Tagged<Object> object = slot.load_tagged();
if (IsHeapObject(object)) {
DCHECK(IsValidHeapObject(isolate()->heap(), Cast<HeapObject>(object)));
serialized_data.push_back({nullptr, 0});
should_clear_slot.push_back(false);
} else {
StartupData data =
wrapper(i, object == Smi::zero(), user_callback, api_obj);
serialized_data.push_back(data);
bool clear_slot =
!DataIsEmpty(data) || slot.MustClearDuringSerialization(no_gc);
should_clear_slot.push_back(clear_slot);
}
}
// 2) Prevent embedder fields that are not V8 objects from ending up in the
// blob. This is done separately to step 1 so as to not interleave with
// embedder callbacks.
for (int i = 0; i < embedder_fields_count; i++) {
if (should_clear_slot[i]) {
EmbedderDataSlot(raw_obj, i).store_raw(isolate(), kNullAddress, no_gc);
}
}
// 3) Serialize the object. References from embedder fields to heap objects or
// smis are serialized regularly.
{
AllowGarbageCollection allow_gc;
ObjectSerializer(this, data_holder, &sink_).Serialize(SlotType::kAnySlot);
// Reload raw pointer.
raw_obj = *data_holder;
}
// 4) Obtain back reference for the serialized object.
const SerializerReference* reference =
reference_map()->LookupReference(raw_obj);
DCHECK_NOT_NULL(reference);
DCHECK(reference->is_back_reference());
// 5) Write data returned by the embedder callbacks into a separate sink,
// headed by the back reference. Restore the original embedder fields.
for (int i = 0; i < embedder_fields_count; i++) {
StartupData data = serialized_data[i];
if (!should_clear_slot[i]) continue;
// Restore original values from cleared fields.
EmbedderDataSlot(raw_obj, i)
.store_raw(isolate(), original_embedder_values[i], no_gc);
if (DataIsEmpty(data)) continue;
embedder_fields_sink_.Put(kNewObject, "embedder field holder");
embedder_fields_sink_.PutUint30(reference->back_ref_index(),
"BackRefIndex");
embedder_fields_sink_.PutUint30(i, "embedder field index");
embedder_fields_sink_.PutUint30(data.raw_size, "embedder fields data size");
embedder_fields_sink_.PutRaw(reinterpret_cast<const uint8_t*>(data.data),
data.raw_size, "embedder fields data");
delete[] data.data;
}
// 6) The content of the separate sink is appended eventually to the default
// sink. The ensures that during deserialization, we call the deserializer
// callback at the end, and can guarantee that the deserialized objects are
// in a consistent state. See ContextSerializer::Serialize.
}
void ContextSerializer::CheckRehashability(Tagged<HeapObject> obj) {
if (!can_be_rehashed_) return;
if (!obj->NeedsRehashing(cage_base())) return;
if (obj->CanBeRehashed(cage_base())) return;
can_be_rehashed_ = false;
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,66 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_CONTEXT_SERIALIZER_H_
#define V8_SNAPSHOT_CONTEXT_SERIALIZER_H_
#include "src/objects/contexts.h"
#include "src/snapshot/serializer.h"
#include "src/snapshot/snapshot-source-sink.h"
namespace v8 {
namespace internal {
class StartupSerializer;
class V8_EXPORT_PRIVATE ContextSerializer : public Serializer {
public:
ContextSerializer(Isolate* isolate, Snapshot::SerializerFlags flags,
StartupSerializer* startup_serializer,
SerializeEmbedderFieldsCallback callback);
~ContextSerializer() override;
ContextSerializer(const ContextSerializer&) = delete;
ContextSerializer& operator=(const ContextSerializer&) = delete;
// Serialize the objects reachable from a single object pointer.
void Serialize(Tagged<Context>* o, const DisallowGarbageCollection& no_gc);
bool can_be_rehashed() const { return can_be_rehashed_; }
private:
void SerializeObjectImpl(Handle<HeapObject> o, SlotType slot_type) override;
bool ShouldBeInTheStartupObjectCache(Tagged<HeapObject> o);
bool ShouldBeInTheSharedObjectCache(Tagged<HeapObject> o);
void CheckRehashability(Tagged<HeapObject> obj);
template <typename V8Type, typename UserSerializerWrapper,
typename UserCallback, typename ApiObjectType>
void SerializeObjectWithEmbedderFields(Handle<V8Type> data_holder,
int embedder_fields_count,
UserSerializerWrapper wrapper,
UserCallback user_callback,
ApiObjectType api_obj);
// For JS API wrapper objects we serialize embedder-controled data for each
// object.
void SerializeApiWrapperFields(DirectHandle<JSObject> js_object);
StartupSerializer* startup_serializer_;
SerializeEmbedderFieldsCallback serialize_embedder_fields_;
// Indicates whether we only serialized hash tables that we can rehash.
// TODO(yangguo): generalize rehashing, and remove this flag.
bool can_be_rehashed_;
Tagged<Context> context_;
// Used to store serialized data for embedder fields.
SnapshotByteSink embedder_fields_sink_;
// Used to store serialized data for API wrappers.
SnapshotByteSink api_wrapper_sink_;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_CONTEXT_SERIALIZER_H_

1680
deps/v8/src/snapshot/deserializer.cc vendored Normal file

File diff suppressed because it is too large Load Diff

398
deps/v8/src/snapshot/deserializer.h vendored Normal file
View File

@ -0,0 +1,398 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_DESERIALIZER_H_
#define V8_SNAPSHOT_DESERIALIZER_H_
#include <utility>
#include <vector>
#include "src/base/macros.h"
#include "src/common/globals.h"
#include "src/execution/local-isolate.h"
#include "src/handles/global-handles.h"
#include "src/objects/allocation-site.h"
#include "src/objects/api-callbacks.h"
#include "src/objects/backing-store.h"
#include "src/objects/code.h"
#include "src/objects/map.h"
#include "src/objects/objects.h"
#include "src/objects/string-table.h"
#include "src/objects/string.h"
#include "src/snapshot/serializer-deserializer.h"
#include "src/snapshot/snapshot-source-sink.h"
namespace v8 {
namespace internal {
class HeapObject;
class Object;
// Used for platforms with embedded constant pools to trigger deserialization
// of objects found in code.
#if defined(V8_TARGET_ARCH_MIPS64) || defined(V8_TARGET_ARCH_S390X) || \
defined(V8_TARGET_ARCH_PPC64) || defined(V8_TARGET_ARCH_RISCV32) || \
defined(V8_TARGET_ARCH_RISCV64) || V8_EMBEDDED_CONSTANT_POOL_BOOL
#define V8_CODE_EMBEDS_OBJECT_POINTER 1
#else
#define V8_CODE_EMBEDS_OBJECT_POINTER 0
#endif
// A Deserializer reads a snapshot and reconstructs the Object graph it defines.
template <typename IsolateT>
class Deserializer : public SerializerDeserializer {
public:
~Deserializer() override;
Deserializer(const Deserializer&) = delete;
Deserializer& operator=(const Deserializer&) = delete;
protected:
// Create a deserializer from a snapshot byte source.
Deserializer(IsolateT* isolate, base::Vector<const uint8_t> payload,
uint32_t magic_number, bool deserializing_user_code,
bool can_rehash);
void DeserializeDeferredObjects();
// Create Log events for newly deserialized objects.
void LogNewObjectEvents();
void LogScriptEvents(Tagged<Script> script);
void LogNewMapEvents();
// Descriptor arrays are deserialized as "strong", so that there is no risk of
// them getting trimmed during a partial deserialization. This method makes
// them "weak" again after deserialization completes.
void WeakenDescriptorArrays();
// This returns the address of an object that has been described in the
// snapshot by object vector index.
Handle<HeapObject> GetBackReferencedObject();
Handle<HeapObject> GetBackReferencedObject(uint32_t index);
// Add an object to back an attached reference. The order to add objects must
// mirror the order they are added in the serializer.
void AddAttachedObject(DirectHandle<HeapObject> attached_object) {
attached_objects_.push_back(attached_object);
}
IsolateT* isolate() const { return isolate_; }
Isolate* main_thread_isolate() const { return isolate_->AsIsolate(); }
SnapshotByteSource* source() { return &source_; }
base::Vector<const DirectHandle<AllocationSite>> new_allocation_sites()
const {
return {new_allocation_sites_.data(), new_allocation_sites_.size()};
}
base::Vector<const DirectHandle<InstructionStream>> new_code_objects() const {
return {new_code_objects_.data(), new_code_objects_.size()};
}
base::Vector<const DirectHandle<Map>> new_maps() const {
return {new_maps_.data(), new_maps_.size()};
}
base::Vector<const DirectHandle<AccessorInfo>> accessor_infos() const {
return {accessor_infos_.data(), accessor_infos_.size()};
}
base::Vector<const DirectHandle<FunctionTemplateInfo>>
function_template_infos() const {
return {function_template_infos_.data(), function_template_infos_.size()};
}
base::Vector<const DirectHandle<Script>> new_scripts() const {
return {new_scripts_.data(), new_scripts_.size()};
}
std::shared_ptr<BackingStore> backing_store(size_t i) {
DCHECK_LT(i, backing_stores_.size());
return backing_stores_[i];
}
bool deserializing_user_code() const { return deserializing_user_code_; }
bool should_rehash() const { return should_rehash_; }
void PushObjectToRehash(DirectHandle<HeapObject> object) {
to_rehash_.push_back(object);
}
void Rehash();
DirectHandle<HeapObject> ReadObject();
private:
// A circular queue of hot objects. This is added to in the same order as in
// Serializer::HotObjectsList, but this stores the objects as a vector of
// existing handles. This allows us to add Handles to the queue without having
// to create new handles. Note that this depends on those Handles staying
// valid as long as the HotObjectsList is alive.
class HotObjectsList {
public:
HotObjectsList() = default;
HotObjectsList(const HotObjectsList&) = delete;
HotObjectsList& operator=(const HotObjectsList&) = delete;
void Add(DirectHandle<HeapObject> object) {
circular_queue_[index_] = object;
index_ = (index_ + 1) & kSizeMask;
}
DirectHandle<HeapObject> Get(int index) {
DCHECK(!circular_queue_[index].is_null());
return circular_queue_[index];
}
private:
static const int kSize = kHotObjectCount;
static const int kSizeMask = kSize - 1;
static_assert(base::bits::IsPowerOfTwo(kSize));
DirectHandle<HeapObject> circular_queue_[kSize];
int index_ = 0;
};
struct ReferenceDescriptor {
HeapObjectReferenceType type;
bool is_indirect_pointer;
bool is_protected_pointer;
};
void VisitRootPointers(Root root, const char* description,
FullObjectSlot start, FullObjectSlot end) override;
void Synchronize(VisitorSynchronization::SyncTag tag) override;
template <typename SlotAccessor>
int WriteHeapPointer(SlotAccessor slot_accessor,
Tagged<HeapObject> heap_object,
ReferenceDescriptor descr,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
template <typename SlotAccessor>
int WriteHeapPointer(SlotAccessor slot_accessor,
DirectHandle<HeapObject> heap_object,
ReferenceDescriptor descr,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
inline int WriteExternalPointer(Tagged<HeapObject> host,
ExternalPointerSlot dest, Address value,
ExternalPointerTag tag);
inline int WriteIndirectPointer(IndirectPointerSlot dest,
Tagged<HeapObject> value);
// Fills in a heap object's data from start to end (exclusive). Start and end
// are slot indices within the object.
void ReadData(Handle<HeapObject> object, int start_slot_index,
int end_slot_index);
// Fills in a contiguous range of full object slots (e.g. root pointers) from
// start to end (exclusive).
void ReadData(FullMaybeObjectSlot start, FullMaybeObjectSlot end);
// Helper for ReadData which reads the given bytecode and fills in some heap
// data into the given slot. May fill in zero or multiple slots, so it returns
// the number of slots filled.
template <typename SlotAccessor>
int ReadSingleBytecodeData(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadNewObject(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadBackref(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadReadOnlyHeapRef(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadRootArray(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadStartupObjectCache(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadSharedHeapObjectCache(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadNewMetaMap(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadExternalReference(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadRawExternalReference(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadAttachedReference(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadRegisterPendingForwardRef(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadResolvePendingForwardRef(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadVariableRawData(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadVariableRepeatRoot(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadOffHeapBackingStore(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadApiReference(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadClearedWeakReference(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadWeakPrefix(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadIndirectPointerPrefix(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadInitializeSelfIndirectPointer(uint8_t data,
SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadAllocateJSDispatchEntry(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadJSDispatchEntry(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadProtectedPointerPrefix(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadRootArrayConstants(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadHotObject(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadFixedRawData(uint8_t data, SlotAccessor slot_accessor);
template <typename SlotAccessor>
int ReadFixedRepeatRoot(uint8_t data, SlotAccessor slot_accessor);
// A helper function for ReadData for reading external references.
inline Address ReadExternalReferenceCase();
// A helper function for reading external pointer tags.
ExternalPointerTag ReadExternalPointerTag();
Handle<HeapObject> ReadObject(SnapshotSpace space);
Handle<HeapObject> ReadMetaMap(SnapshotSpace space);
ReferenceDescriptor GetAndResetNextReferenceDescriptor();
template <typename SlotGetter>
int ReadRepeatedRoot(SlotGetter slot_getter, int repeat_count);
// Special handling for serialized code like hooking up internalized strings.
void PostProcessNewObject(DirectHandle<Map> map, Handle<HeapObject> obj,
SnapshotSpace space);
void PostProcessNewJSReceiver(Tagged<Map> map, DirectHandle<JSReceiver> obj,
InstanceType instance_type,
SnapshotSpace space);
Tagged<HeapObject> Allocate(AllocationType allocation, int size,
AllocationAlignment alignment);
// Cached current isolate.
IsolateT* isolate_;
// Objects from the attached object descriptions in the serialized user code.
DirectHandleVector<HeapObject> attached_objects_;
SnapshotByteSource source_;
uint32_t magic_number_;
HotObjectsList hot_objects_;
DirectHandleVector<Map> new_maps_;
DirectHandleVector<AllocationSite> new_allocation_sites_;
DirectHandleVector<InstructionStream> new_code_objects_;
DirectHandleVector<AccessorInfo> accessor_infos_;
DirectHandleVector<FunctionTemplateInfo> function_template_infos_;
DirectHandleVector<Script> new_scripts_;
std::vector<std::shared_ptr<BackingStore>> backing_stores_;
// Roots vector as those arrays are passed to Heap, see
// WeakenDescriptorArrays().
GlobalHandleVector<DescriptorArray> new_descriptor_arrays_;
// Vector of allocated objects that can be accessed by a backref, by index.
std::vector<IndirectHandle<HeapObject>> back_refs_;
// Vector of already allocated JSDispatchTable entries.
std::vector<JSDispatchHandle> js_dispatch_entries_;
// Unresolved forward references (registered with kRegisterPendingForwardRef)
// are collected in order as (object, field offset) pairs. The subsequent
// forward ref resolution (with kResolvePendingForwardRef) accesses this
// vector by index.
//
// The vector is cleared when there are no more unresolved forward refs.
struct UnresolvedForwardRef {
UnresolvedForwardRef(Handle<HeapObject> object, int offset,
ReferenceDescriptor descr)
: object(object), offset(offset), descr(descr) {}
IndirectHandle<HeapObject> object;
int offset;
ReferenceDescriptor descr;
};
std::vector<UnresolvedForwardRef> unresolved_forward_refs_;
int num_unresolved_forward_refs_ = 0;
const bool deserializing_user_code_;
bool next_reference_is_weak_ = false;
bool next_reference_is_indirect_pointer_ = false;
bool next_reference_is_protected_pointer = false;
// TODO(6593): generalize rehashing, and remove this flag.
const bool should_rehash_;
DirectHandleVector<HeapObject> to_rehash_;
// Do not collect any gc stats during deserialization since objects might
// be in an invalid state
class V8_NODISCARD DisableGCStats {
public:
DisableGCStats() {
original_gc_stats_ = TracingFlags::gc_stats;
TracingFlags::gc_stats = 0;
}
~DisableGCStats() { TracingFlags::gc_stats = original_gc_stats_; }
private:
unsigned int original_gc_stats_;
};
DisableGCStats no_gc_stats_;
int depth_ = 0;
#ifdef DEBUG
uint32_t num_api_references_;
// Record the previous object allocated for DCHECKs.
DirectHandle<HeapObject> previous_allocation_obj_;
int previous_allocation_size_ = 0;
#endif // DEBUG
};
enum class DeserializingUserCodeOption {
kNotDeserializingUserCode,
kIsDeserializingUserCode
};
// Used to insert a deserialized internalized string into the string table.
class StringTableInsertionKey final : public StringTableKey {
public:
explicit StringTableInsertionKey(
Isolate* isolate, DirectHandle<String> string,
DeserializingUserCodeOption deserializing_user_code);
explicit StringTableInsertionKey(
LocalIsolate* isolate, DirectHandle<String> string,
DeserializingUserCodeOption deserializing_user_code);
template <typename IsolateT>
bool IsMatch(IsolateT* isolate, Tagged<String> string);
void PrepareForInsertion(Isolate* isolate) {
// When sharing the string table, all string table lookups during snapshot
// deserialization are hits.
DCHECK(isolate->OwnsStringTables() ||
deserializing_user_code_ ==
DeserializingUserCodeOption::kIsDeserializingUserCode);
}
void PrepareForInsertion(LocalIsolate* isolate) {}
V8_WARN_UNUSED_RESULT DirectHandle<String> GetHandleForInsertion(
Isolate* isolate) {
return string_;
}
private:
DirectHandle<String> string_;
#ifdef DEBUG
DeserializingUserCodeOption deserializing_user_code_;
#endif
DISALLOW_GARBAGE_COLLECTION(no_gc)
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_DESERIALIZER_H_

View File

@ -0,0 +1,64 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_EMBEDDED_EMBEDDED_DATA_INL_H_
#define V8_SNAPSHOT_EMBEDDED_EMBEDDED_DATA_INL_H_
#include "src/snapshot/embedded/embedded-data.h"
// Include the non-inl header before the rest of the headers.
namespace v8 {
namespace internal {
Address EmbeddedData::InstructionStartOf(Builtin builtin) const {
DCHECK(Builtins::IsBuiltinId(builtin));
const struct LayoutDescription& desc = LayoutDescription(builtin);
const uint8_t* result = RawCode() + desc.instruction_offset;
DCHECK_LT(result, code_ + code_size_);
return reinterpret_cast<Address>(result);
}
Address EmbeddedData::InstructionEndOf(Builtin builtin) const {
DCHECK(Builtins::IsBuiltinId(builtin));
const struct LayoutDescription& desc = LayoutDescription(builtin);
const uint8_t* result =
RawCode() + desc.instruction_offset + desc.instruction_length;
DCHECK_LT(result, code_ + code_size_);
return reinterpret_cast<Address>(result);
}
uint32_t EmbeddedData::InstructionSizeOf(Builtin builtin) const {
DCHECK(Builtins::IsBuiltinId(builtin));
const struct LayoutDescription& desc = LayoutDescription(builtin);
return desc.instruction_length;
}
Address EmbeddedData::MetadataStartOf(Builtin builtin) const {
DCHECK(Builtins::IsBuiltinId(builtin));
const struct LayoutDescription& desc = LayoutDescription(builtin);
const uint8_t* result = RawMetadata() + desc.metadata_offset;
DCHECK_LE(desc.metadata_offset, data_size_);
return reinterpret_cast<Address>(result);
}
Address EmbeddedData::InstructionStartOfBytecodeHandlers() const {
return InstructionStartOf(Builtin::kFirstBytecodeHandler);
}
Address EmbeddedData::InstructionEndOfBytecodeHandlers() const {
static_assert(Builtins::kBytecodeHandlersAreSortedLast);
// Note this also includes trailing padding, but that's fine for our purposes.
return reinterpret_cast<Address>(code_ + code_size_);
}
uint32_t EmbeddedData::PaddedInstructionSizeOf(Builtin builtin) const {
uint32_t size = InstructionSizeOf(builtin);
CHECK_NE(size, 0);
return PadAndAlignCode(size);
}
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_EMBEDDED_EMBEDDED_DATA_INL_H_

View File

@ -0,0 +1,477 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/embedded/embedded-data.h"
#include "src/codegen/assembler-inl.h"
#include "src/codegen/callable.h"
#include "src/snapshot/embedded/embedded-data-inl.h"
#include "src/snapshot/snapshot-utils.h"
#include "src/snapshot/sort-builtins.h"
namespace v8 {
namespace internal {
Builtin EmbeddedData::TryLookupCode(Address address) const {
if (!IsInCodeRange(address)) return Builtin::kNoBuiltinId;
// Note: Addresses within the padding section between builtins (i.e. within
// start + size <= address < start + padded_size) are interpreted as belonging
// to the preceding builtin.
uint32_t offset =
static_cast<uint32_t>(address - reinterpret_cast<Address>(RawCode()));
const struct BuiltinLookupEntry* start =
BuiltinLookupEntry(static_cast<ReorderedBuiltinIndex>(0));
const struct BuiltinLookupEntry* end = start + kTableSize;
const struct BuiltinLookupEntry* desc =
std::upper_bound(start, end, offset,
[](uint32_t o, const struct BuiltinLookupEntry& desc) {
return o < desc.end_offset;
});
Builtin builtin = static_cast<Builtin>(desc->builtin_id);
DCHECK_LT(address,
InstructionStartOf(builtin) + PaddedInstructionSizeOf(builtin));
DCHECK_GE(address, InstructionStartOf(builtin));
return builtin;
}
// static
bool OffHeapInstructionStream::PcIsOffHeap(Isolate* isolate, Address pc) {
// Mksnapshot calls this while the embedded blob is not available yet.
if (isolate->embedded_blob_code() == nullptr) return false;
DCHECK_NOT_NULL(Isolate::CurrentEmbeddedBlobCode());
if (EmbeddedData::FromBlob(isolate).IsInCodeRange(pc)) return true;
return isolate->is_short_builtin_calls_enabled() &&
EmbeddedData::FromBlob().IsInCodeRange(pc);
}
// static
bool OffHeapInstructionStream::TryGetAddressForHashing(
Isolate* isolate, Address address, uint32_t* hashable_address) {
// Mksnapshot calls this while the embedded blob is not available yet.
if (isolate->embedded_blob_code() == nullptr) return false;
DCHECK_NOT_NULL(Isolate::CurrentEmbeddedBlobCode());
EmbeddedData d = EmbeddedData::FromBlob(isolate);
if (d.IsInCodeRange(address)) {
*hashable_address = d.AddressForHashing(address);
return true;
}
if (isolate->is_short_builtin_calls_enabled()) {
d = EmbeddedData::FromBlob();
if (d.IsInCodeRange(address)) {
*hashable_address = d.AddressForHashing(address);
return true;
}
}
return false;
}
// static
Builtin OffHeapInstructionStream::TryLookupCode(Isolate* isolate,
Address address) {
// Mksnapshot calls this while the embedded blob is not available yet.
if (isolate->embedded_blob_code() == nullptr) return Builtin::kNoBuiltinId;
DCHECK_NOT_NULL(Isolate::CurrentEmbeddedBlobCode());
Builtin builtin = EmbeddedData::FromBlob(isolate).TryLookupCode(address);
if (isolate->is_short_builtin_calls_enabled() &&
!Builtins::IsBuiltinId(builtin)) {
builtin = EmbeddedData::FromBlob().TryLookupCode(address);
}
#ifdef V8_COMPRESS_POINTERS_IN_SHARED_CAGE
if (V8_SHORT_BUILTIN_CALLS_BOOL && !Builtins::IsBuiltinId(builtin)) {
// When shared pointer compression cage is enabled and it has the embedded
// code blob copy then it could have been used regardless of whether the
// isolate uses it or knows about it or not (see
// InstructionStream::OffHeapInstructionStart()).
// So, this blob has to be checked too.
CodeRange* code_range = IsolateGroup::current()->GetCodeRange();
if (code_range && code_range->embedded_blob_code_copy() != nullptr) {
builtin = EmbeddedData::FromBlob(code_range).TryLookupCode(address);
}
}
#endif
return builtin;
}
// static
void OffHeapInstructionStream::CreateOffHeapOffHeapInstructionStream(
Isolate* isolate, uint8_t** code, uint32_t* code_size, uint8_t** data,
uint32_t* data_size) {
// Create the embedded blob from scratch using the current Isolate's heap.
EmbeddedData d = EmbeddedData::NewFromIsolate(isolate);
// Allocate the backing store that will contain the embedded blob in this
// Isolate. The backing store is on the native heap, *not* on V8's garbage-
// collected heap.
v8::PageAllocator* page_allocator = v8::internal::GetPlatformPageAllocator();
const uint32_t alignment =
static_cast<uint32_t>(page_allocator->AllocatePageSize());
void* const requested_allocation_code_address =
AlignedAddress(isolate->heap()->GetRandomMmapAddr(), alignment);
const uint32_t allocation_code_size = RoundUp(d.code_size(), alignment);
uint8_t* allocated_code_bytes = static_cast<uint8_t*>(AllocatePages(
page_allocator, requested_allocation_code_address, allocation_code_size,
alignment, PageAllocator::kReadWrite));
CHECK_NOT_NULL(allocated_code_bytes);
void* const requested_allocation_data_address =
AlignedAddress(isolate->heap()->GetRandomMmapAddr(), alignment);
const uint32_t allocation_data_size = RoundUp(d.data_size(), alignment);
uint8_t* allocated_data_bytes = static_cast<uint8_t*>(AllocatePages(
page_allocator, requested_allocation_data_address, allocation_data_size,
alignment, PageAllocator::kReadWrite));
CHECK_NOT_NULL(allocated_data_bytes);
// Copy the embedded blob into the newly allocated backing store. Switch
// permissions to read-execute since builtin code is immutable from now on
// and must be executable in case any JS execution is triggered.
//
// Once this backing store is set as the current_embedded_blob, V8 cannot tell
// the difference between a 'real' embedded build (where the blob is embedded
// in the binary) and what we are currently setting up here (where the blob is
// on the native heap).
std::memcpy(allocated_code_bytes, d.code(), d.code_size());
if (v8_flags.experimental_flush_embedded_blob_icache) {
FlushInstructionCache(allocated_code_bytes, d.code_size());
}
CHECK(SetPermissions(page_allocator, allocated_code_bytes,
allocation_code_size, PageAllocator::kReadExecute));
std::memcpy(allocated_data_bytes, d.data(), d.data_size());
CHECK(SetPermissions(page_allocator, allocated_data_bytes,
allocation_data_size, PageAllocator::kRead));
*code = allocated_code_bytes;
*code_size = d.code_size();
*data = allocated_data_bytes;
*data_size = d.data_size();
d.Dispose();
}
// static
void OffHeapInstructionStream::FreeOffHeapOffHeapInstructionStream(
uint8_t* code, uint32_t code_size, uint8_t* data, uint32_t data_size) {
v8::PageAllocator* page_allocator = v8::internal::GetPlatformPageAllocator();
const uint32_t page_size =
static_cast<uint32_t>(page_allocator->AllocatePageSize());
FreePages(page_allocator, code, RoundUp(code_size, page_size));
FreePages(page_allocator, data, RoundUp(data_size, page_size));
}
namespace {
void FinalizeEmbeddedCodeTargets(Isolate* isolate, EmbeddedData* blob) {
static const int kRelocMask =
RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
RelocInfo::ModeMask(RelocInfo::RELATIVE_CODE_TARGET);
static_assert(Builtins::kAllBuiltinsAreIsolateIndependent);
for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast;
++builtin) {
Tagged<Code> code = isolate->builtins()->code(builtin);
RelocIterator on_heap_it(code, kRelocMask);
RelocIterator off_heap_it(blob, code, kRelocMask);
#if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM64) || \
defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_IA32) || \
defined(V8_TARGET_ARCH_S390X) || defined(V8_TARGET_ARCH_RISCV64) || \
defined(V8_TARGET_ARCH_LOONG64) || defined(V8_TARGET_ARCH_RISCV32)
// On these platforms we emit relative builtin-to-builtin
// jumps for isolate independent builtins in the snapshot. This fixes up the
// relative jumps to the right offsets in the snapshot.
// See also: InstructionStream::IsIsolateIndependent.
while (!on_heap_it.done()) {
DCHECK(!off_heap_it.done());
RelocInfo* rinfo = on_heap_it.rinfo();
DCHECK_EQ(rinfo->rmode(), off_heap_it.rinfo()->rmode());
Tagged<Code> target_code =
Code::FromTargetAddress(rinfo->target_address());
CHECK(Builtins::IsIsolateIndependentBuiltin(target_code));
// Do not emit write-barrier for off-heap writes.
off_heap_it.rinfo()->set_off_heap_target_address(
blob->InstructionStartOf(target_code->builtin_id()));
on_heap_it.next();
off_heap_it.next();
}
DCHECK(off_heap_it.done());
#else
// Architectures other than x64 and arm/arm64 do not use pc-relative calls
// and thus must not contain embedded code targets. Instead, we use an
// indirection through the root register.
CHECK(on_heap_it.done());
CHECK(off_heap_it.done());
#endif
}
}
void EnsureRelocatable(Tagged<Code> code) {
if (code->relocation_size() == 0) return;
// On some architectures (arm) the builtin might have a non-empty reloc
// info containing a CONST_POOL entry. These entries don't have to be
// updated when InstructionStream object is relocated, so it's safe to drop
// the reloc info alltogether. If it wasn't the case then we'd have to store
// it in the metadata.
for (RelocIterator it(code); !it.done(); it.next()) {
CHECK_EQ(it.rinfo()->rmode(), RelocInfo::CONST_POOL);
}
}
} // namespace
// static
EmbeddedData EmbeddedData::NewFromIsolate(Isolate* isolate) {
Builtins* builtins = isolate->builtins();
// Store instruction stream lengths and offsets.
std::vector<struct LayoutDescription> layout_descriptions(kTableSize);
std::vector<struct BuiltinLookupEntry> offset_descriptions(kTableSize);
bool saw_unsafe_builtin = false;
uint32_t raw_code_size = 0;
uint32_t raw_data_size = 0;
static_assert(Builtins::kAllBuiltinsAreIsolateIndependent);
std::vector<Builtin> reordered_builtins;
if (v8_flags.reorder_builtins &&
BuiltinsCallGraph::Get()->all_hash_matched()) {
DCHECK(v8_flags.turbo_profiling_input.value());
// TODO(ishell, v8:13938): avoid the binary size overhead for non-mksnapshot
// binaries.
BuiltinsSorter sorter;
std::vector<uint32_t> builtin_sizes;
for (Builtin i = Builtins::kFirst; i <= Builtins::kLast; ++i) {
Tagged<Code> code = builtins->code(i);
uint32_t instruction_size =
static_cast<uint32_t>(code->instruction_size());
uint32_t padding_size = PadAndAlignCode(instruction_size);
builtin_sizes.push_back(padding_size);
}
reordered_builtins = sorter.SortBuiltins(
v8_flags.turbo_profiling_input.value(), builtin_sizes);
CHECK_EQ(reordered_builtins.size(), Builtins::kBuiltinCount);
}
for (ReorderedBuiltinIndex embedded_index = 0;
embedded_index < Builtins::kBuiltinCount; embedded_index++) {
Builtin builtin;
if (reordered_builtins.empty()) {
builtin = static_cast<Builtin>(embedded_index);
} else {
builtin = reordered_builtins[embedded_index];
}
Tagged<Code> code = builtins->code(builtin);
// Sanity-check that the given builtin is isolate-independent.
if (!code->IsIsolateIndependent(isolate)) {
saw_unsafe_builtin = true;
fprintf(stderr, "%s is not isolate-independent.\n",
Builtins::name(builtin));
}
uint32_t instruction_size = static_cast<uint32_t>(code->instruction_size());
DCHECK_EQ(0, raw_code_size % kCodeAlignment);
{
// We use builtin id as index in layout_descriptions.
const int builtin_id = static_cast<int>(builtin);
struct LayoutDescription& layout_desc = layout_descriptions[builtin_id];
layout_desc.instruction_offset = raw_code_size;
layout_desc.instruction_length = instruction_size;
layout_desc.metadata_offset = raw_data_size;
}
// Align the start of each section.
raw_code_size += PadAndAlignCode(instruction_size);
raw_data_size += PadAndAlignData(code->metadata_size());
{
// We use embedded index as index in offset_descriptions.
struct BuiltinLookupEntry& offset_desc =
offset_descriptions[embedded_index];
offset_desc.end_offset = raw_code_size;
offset_desc.builtin_id = static_cast<uint32_t>(builtin);
}
}
CHECK_WITH_MSG(
!saw_unsafe_builtin,
"One or more builtins marked as isolate-independent either contains "
"isolate-dependent code or aliases the off-heap trampoline register. "
"If in doubt, ask jgruber@");
// Allocate space for the code section, value-initialized to 0.
static_assert(RawCodeOffset() == 0);
const uint32_t blob_code_size = RawCodeOffset() + raw_code_size;
uint8_t* const blob_code = new uint8_t[blob_code_size]();
// Allocate space for the data section, value-initialized to 0.
static_assert(
IsAligned(FixedDataSize(), InstructionStream::kMetadataAlignment));
const uint32_t blob_data_size = FixedDataSize() + raw_data_size;
uint8_t* const blob_data = new uint8_t[blob_data_size]();
// Initially zap the entire blob, effectively padding the alignment area
// between two builtins with int3's (on x64/ia32).
ZapCode(reinterpret_cast<Address>(blob_code), blob_code_size);
// Hash relevant parts of the Isolate's heap and store the result.
{
static_assert(IsolateHashSize() == kSizetSize);
const size_t hash = isolate->HashIsolateForEmbeddedBlob();
std::memcpy(blob_data + IsolateHashOffset(), &hash, IsolateHashSize());
}
// Write the layout_descriptions tables.
DCHECK_EQ(LayoutDescriptionTableSize(),
sizeof(layout_descriptions[0]) * layout_descriptions.size());
std::memcpy(blob_data + LayoutDescriptionTableOffset(),
layout_descriptions.data(), LayoutDescriptionTableSize());
// Write the builtin_offset_descriptions tables.
DCHECK_EQ(BuiltinLookupEntryTableSize(),
sizeof(offset_descriptions[0]) * offset_descriptions.size());
std::memcpy(blob_data + BuiltinLookupEntryTableOffset(),
offset_descriptions.data(), BuiltinLookupEntryTableSize());
// .. and the variable-size data section.
uint8_t* const raw_metadata_start = blob_data + RawMetadataOffset();
static_assert(Builtins::kAllBuiltinsAreIsolateIndependent);
for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast;
++builtin) {
Tagged<Code> code = builtins->code(builtin);
uint32_t offset =
layout_descriptions[static_cast<int>(builtin)].metadata_offset;
uint8_t* dst = raw_metadata_start + offset;
DCHECK_LE(RawMetadataOffset() + offset + code->metadata_size(),
blob_data_size);
std::memcpy(dst, reinterpret_cast<uint8_t*>(code->metadata_start()),
code->metadata_size());
}
CHECK_IMPLIES(
kMaxPCRelativeCodeRangeInMB,
static_cast<size_t>(raw_code_size) <= kMaxPCRelativeCodeRangeInMB * MB);
// .. and the variable-size code section.
uint8_t* const raw_code_start = blob_code + RawCodeOffset();
static_assert(Builtins::kAllBuiltinsAreIsolateIndependent);
for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast;
++builtin) {
Tagged<Code> code = builtins->code(builtin);
uint32_t offset =
layout_descriptions[static_cast<int>(builtin)].instruction_offset;
uint8_t* dst = raw_code_start + offset;
DCHECK_LE(RawCodeOffset() + offset + code->instruction_size(),
blob_code_size);
std::memcpy(dst, reinterpret_cast<uint8_t*>(code->instruction_start()),
code->instruction_size());
}
EmbeddedData d(blob_code, blob_code_size, blob_data, blob_data_size);
// Fix up call targets that point to other embedded builtins.
FinalizeEmbeddedCodeTargets(isolate, &d);
// Hash the blob and store the result.
{
static_assert(EmbeddedBlobDataHashSize() == kSizetSize);
const size_t data_hash = d.CreateEmbeddedBlobDataHash();
std::memcpy(blob_data + EmbeddedBlobDataHashOffset(), &data_hash,
EmbeddedBlobDataHashSize());
static_assert(EmbeddedBlobCodeHashSize() == kSizetSize);
const size_t code_hash = d.CreateEmbeddedBlobCodeHash();
std::memcpy(blob_data + EmbeddedBlobCodeHashOffset(), &code_hash,
EmbeddedBlobCodeHashSize());
DCHECK_EQ(data_hash, d.CreateEmbeddedBlobDataHash());
DCHECK_EQ(data_hash, d.EmbeddedBlobDataHash());
DCHECK_EQ(code_hash, d.CreateEmbeddedBlobCodeHash());
DCHECK_EQ(code_hash, d.EmbeddedBlobCodeHash());
}
if (DEBUG_BOOL) {
for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast;
++builtin) {
Tagged<Code> code = builtins->code(builtin);
CHECK_EQ(d.InstructionSizeOf(builtin), code->instruction_size());
}
}
// Ensure that InterpreterEntryTrampolineForProfiling is relocatable.
// See v8_flags.interpreted_frames_native_stack for details.
EnsureRelocatable(
builtins->code(Builtin::kInterpreterEntryTrampolineForProfiling));
if (v8_flags.serialization_statistics) d.PrintStatistics();
return d;
}
size_t EmbeddedData::CreateEmbeddedBlobDataHash() const {
static_assert(EmbeddedBlobDataHashOffset() == 0);
static_assert(EmbeddedBlobCodeHashOffset() == EmbeddedBlobDataHashSize());
static_assert(IsolateHashOffset() ==
EmbeddedBlobCodeHashOffset() + EmbeddedBlobCodeHashSize());
static constexpr uint32_t kFirstHashedDataOffset = IsolateHashOffset();
// Hash the entire data section except the embedded blob hash fields
// themselves.
base::Vector<const uint8_t> payload(data_ + kFirstHashedDataOffset,
data_size_ - kFirstHashedDataOffset);
return Checksum(payload);
}
size_t EmbeddedData::CreateEmbeddedBlobCodeHash() const {
CHECK(v8_flags.text_is_readable);
base::Vector<const uint8_t> payload(code_, code_size_);
return Checksum(payload);
}
Builtin EmbeddedData::GetBuiltinId(ReorderedBuiltinIndex embedded_index) const {
Builtin builtin =
Builtins::FromInt(BuiltinLookupEntry(embedded_index)->builtin_id);
return builtin;
}
void EmbeddedData::PrintStatistics() const {
DCHECK(v8_flags.serialization_statistics);
constexpr int kCount = Builtins::kBuiltinCount;
int sizes[kCount];
static_assert(Builtins::kAllBuiltinsAreIsolateIndependent);
for (int i = 0; i < kCount; i++) {
sizes[i] = InstructionSizeOf(Builtins::FromInt(i));
}
// Sort for percentiles.
std::sort(&sizes[0], &sizes[kCount]);
const int k50th = kCount * 0.5;
const int k75th = kCount * 0.75;
const int k90th = kCount * 0.90;
const int k99th = kCount * 0.99;
PrintF("EmbeddedData:\n");
PrintF(" Total size: %d\n",
static_cast<int>(code_size() + data_size()));
PrintF(" Data size: %d\n", static_cast<int>(data_size()));
PrintF(" Code size: %d\n", static_cast<int>(code_size()));
PrintF(" Instruction size (50th percentile): %d\n", sizes[k50th]);
PrintF(" Instruction size (75th percentile): %d\n", sizes[k75th]);
PrintF(" Instruction size (90th percentile): %d\n", sizes[k90th]);
PrintF(" Instruction size (99th percentile): %d\n", sizes[k99th]);
PrintF("\n");
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,319 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_EMBEDDED_EMBEDDED_DATA_H_
#define V8_SNAPSHOT_EMBEDDED_EMBEDDED_DATA_H_
#include "src/base/macros.h"
#include "src/builtins/builtins.h"
#include "src/common/globals.h"
#include "src/execution/isolate.h"
#include "src/heap/code-range.h"
#include "src/objects/instruction-stream.h"
namespace v8 {
namespace internal {
class InstructionStream;
class Isolate;
using ReorderedBuiltinIndex = uint32_t;
// Wraps an off-heap instruction stream.
// TODO(jgruber,v8:6666): Remove this class.
class OffHeapInstructionStream final : public AllStatic {
public:
// Returns true, iff the given pc points into an off-heap instruction stream.
static bool PcIsOffHeap(Isolate* isolate, Address pc);
// If the address belongs to the embedded code blob, predictably converts it
// to uint32 by calculating offset from the embedded code blob start and
// returns true, and false otherwise.
static bool TryGetAddressForHashing(Isolate* isolate, Address address,
uint32_t* hashable_address);
// Returns the corresponding builtin ID if lookup succeeds, and kNoBuiltinId
// otherwise.
static Builtin TryLookupCode(Isolate* isolate, Address address);
// 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 CreateOffHeapOffHeapInstructionStream(Isolate* isolate,
uint8_t** code,
uint32_t* code_size,
uint8_t** data,
uint32_t* data_size);
static void FreeOffHeapOffHeapInstructionStream(uint8_t* code,
uint32_t code_size,
uint8_t* data,
uint32_t data_size);
};
class EmbeddedData final {
public:
// Create the embedded blob from the given Isolate's heap state.
static EmbeddedData NewFromIsolate(Isolate* isolate);
// Returns the global embedded blob (usually physically located in .text and
// .rodata).
static EmbeddedData FromBlob() {
return EmbeddedData(Isolate::CurrentEmbeddedBlobCode(),
Isolate::CurrentEmbeddedBlobCodeSize(),
Isolate::CurrentEmbeddedBlobData(),
Isolate::CurrentEmbeddedBlobDataSize());
}
// Returns a potentially remapped embedded blob (see also
// MaybeRemapEmbeddedBuiltinsIntoCodeRange).
static EmbeddedData FromBlob(Isolate* isolate) {
return EmbeddedData(
isolate->embedded_blob_code(), isolate->embedded_blob_code_size(),
isolate->embedded_blob_data(), isolate->embedded_blob_data_size());
}
// Returns a potentially remapped embedded blob (see also
// MaybeRemapEmbeddedBuiltinsIntoCodeRange).
static EmbeddedData FromBlob(CodeRange* code_range) {
return EmbeddedData(code_range->embedded_blob_code_copy(),
Isolate::CurrentEmbeddedBlobCodeSize(),
Isolate::CurrentEmbeddedBlobData(),
Isolate::CurrentEmbeddedBlobDataSize());
}
// When short builtin calls optimization is enabled for the Isolate, there
// will be two builtins instruction streams executed: the embedded one and
// the one un-embedded into the per-Isolate code range. In most of the cases,
// the per-Isolate instructions will be used but in some cases (like builtin
// calls from Wasm) the embedded instruction stream could be used. If the
// requested PC belongs to the embedded code blob - it'll be returned, and
// the per-Isolate blob otherwise.
// See http://crbug.com/v8/11527 for details.
static EmbeddedData FromBlobForPc(Isolate* isolate,
Address maybe_builtin_pc) {
EmbeddedData d = EmbeddedData::FromBlob(isolate);
if (d.IsInCodeRange(maybe_builtin_pc)) return d;
if (isolate->is_short_builtin_calls_enabled()) {
EmbeddedData global_d = EmbeddedData::FromBlob();
// If the pc does not belong to the embedded code blob we should be using
// the un-embedded one.
if (global_d.IsInCodeRange(maybe_builtin_pc)) return global_d;
}
#if defined(V8_COMPRESS_POINTERS_IN_SHARED_CAGE) && \
defined(V8_SHORT_BUILTIN_CALLS)
// When shared pointer compression cage is enabled and it has the embedded
// code blob copy then it could have been used regardless of whether the
// isolate uses it or knows about it or not (see
// InstructionStream::OffHeapInstructionStart()).
// So, this blob has to be checked too.
CodeRange* code_range = IsolateGroup::current()->GetCodeRange();
if (code_range && code_range->embedded_blob_code_copy() != nullptr) {
EmbeddedData remapped_d = EmbeddedData::FromBlob(code_range);
// If the pc does not belong to the embedded code blob we should be
// using the un-embedded one.
if (remapped_d.IsInCodeRange(maybe_builtin_pc)) return remapped_d;
}
#endif // defined(V8_COMPRESS_POINTERS_IN_SHARED_CAGE) &&
// defined(V8_SHORT_BUILTIN_CALLS)
return d;
}
const uint8_t* code() const { return code_; }
uint32_t code_size() const { return code_size_; }
const uint8_t* data() const { return data_; }
uint32_t data_size() const { return data_size_; }
bool IsInCodeRange(Address pc) const {
Address start = reinterpret_cast<Address>(code_);
return (start <= pc) && (pc < start + code_size_);
}
void Dispose() {
delete[] code_;
code_ = nullptr;
delete[] data_;
data_ = nullptr;
}
inline Address InstructionStartOf(Builtin builtin) const;
inline Address InstructionEndOf(Builtin builtin) const;
inline uint32_t InstructionSizeOf(Builtin builtin) const;
inline Address InstructionStartOfBytecodeHandlers() const;
inline Address InstructionEndOfBytecodeHandlers() const;
inline Address MetadataStartOf(Builtin builtin) const;
uint32_t AddressForHashing(Address addr) {
DCHECK(IsInCodeRange(addr));
Address start = reinterpret_cast<Address>(code_);
return static_cast<uint32_t>(addr - start);
}
// Padded with kCodeAlignment.
inline uint32_t PaddedInstructionSizeOf(Builtin builtin) const;
size_t CreateEmbeddedBlobDataHash() const;
size_t CreateEmbeddedBlobCodeHash() const;
size_t EmbeddedBlobDataHash() const {
return *reinterpret_cast<const size_t*>(data_ +
EmbeddedBlobDataHashOffset());
}
size_t EmbeddedBlobCodeHash() const {
return *reinterpret_cast<const size_t*>(data_ +
EmbeddedBlobCodeHashOffset());
}
size_t IsolateHash() const {
return *reinterpret_cast<const size_t*>(data_ + IsolateHashOffset());
}
Builtin TryLookupCode(Address address) const;
// Blob layout information for a single instruction stream.
struct LayoutDescription {
// The offset and (unpadded) length of this builtin's instruction area
// from the start of the embedded code section.
uint32_t instruction_offset;
uint32_t instruction_length;
// The offset of this builtin's metadata area from the start of the
// embedded data section.
uint32_t metadata_offset;
};
static_assert(offsetof(LayoutDescription, instruction_offset) ==
0 * kUInt32Size);
static_assert(offsetof(LayoutDescription, instruction_length) ==
1 * kUInt32Size);
static_assert(offsetof(LayoutDescription, metadata_offset) ==
2 * kUInt32Size);
// The embedded code section stores builtins in the so-called
// 'embedded snapshot order' which is usually different from the order
// as defined by the Builtins enum ('builtin id order'), and determined
// through an algorithm based on collected execution profiles. The
// BuiltinLookupEntry struct maps from the 'embedded snapshot order' to
// the 'builtin id order' and additionally keeps a copy of instruction_end for
// each builtin since it is convenient for binary search.
struct BuiltinLookupEntry {
// The end offset (including padding) of builtin, the end_offset field
// should be in ascending order in the array in snapshot, because we will
// use it in TryLookupCode. It should be equal to
// LayoutDescription[builtin_id].instruction_offset +
// PadAndAlignCode(length)
uint32_t end_offset;
// The id of builtin.
uint32_t builtin_id;
};
static_assert(offsetof(BuiltinLookupEntry, end_offset) == 0 * kUInt32Size);
static_assert(offsetof(BuiltinLookupEntry, builtin_id) == 1 * kUInt32Size);
Builtin GetBuiltinId(ReorderedBuiltinIndex embedded_index) const;
// The layout of the blob is as follows:
//
// data:
// [0] hash of the data section
// [1] hash of the code section
// [2] hash of embedded-blob-relevant heap objects
// [3] layout description of builtin 0
// ... layout descriptions (builtin id order)
// [n] builtin lookup table where entries are sorted by offset_end in
// ascending order. (embedded snapshot order)
// [x] metadata section of builtin 0
// ... metadata sections (builtin id order)
//
// code:
// [0] instruction section of builtin 0
// ... instruction sections (embedded snapshot order)
static constexpr uint32_t kTableSize = Builtins::kBuiltinCount;
static constexpr uint32_t EmbeddedBlobDataHashOffset() { return 0; }
static constexpr uint32_t EmbeddedBlobDataHashSize() { return kSizetSize; }
static constexpr uint32_t EmbeddedBlobCodeHashOffset() {
return EmbeddedBlobDataHashOffset() + EmbeddedBlobDataHashSize();
}
static constexpr uint32_t EmbeddedBlobCodeHashSize() { return kSizetSize; }
static constexpr uint32_t IsolateHashOffset() {
return EmbeddedBlobCodeHashOffset() + EmbeddedBlobCodeHashSize();
}
static constexpr uint32_t IsolateHashSize() { return kSizetSize; }
static constexpr uint32_t LayoutDescriptionTableOffset() {
return IsolateHashOffset() + IsolateHashSize();
}
static constexpr uint32_t LayoutDescriptionTableSize() {
return sizeof(struct LayoutDescription) * kTableSize;
}
static constexpr uint32_t BuiltinLookupEntryTableOffset() {
return LayoutDescriptionTableOffset() + LayoutDescriptionTableSize();
}
static constexpr uint32_t BuiltinLookupEntryTableSize() {
return sizeof(struct BuiltinLookupEntry) * kTableSize;
}
static constexpr uint32_t FixedDataSize() {
return BuiltinLookupEntryTableOffset() + BuiltinLookupEntryTableSize();
}
// The variable-size data section starts here.
static constexpr uint32_t RawMetadataOffset() { return FixedDataSize(); }
// Code is in its own dedicated section.
static constexpr uint32_t RawCodeOffset() { return 0; }
private:
EmbeddedData(const uint8_t* code, uint32_t code_size, const uint8_t* data,
uint32_t data_size)
: code_(code), code_size_(code_size), data_(data), data_size_(data_size) {
DCHECK_NOT_NULL(code);
DCHECK_LT(0, code_size);
DCHECK_NOT_NULL(data);
DCHECK_LT(0, data_size);
}
const uint8_t* RawCode() const { return code_ + RawCodeOffset(); }
const LayoutDescription& LayoutDescription(Builtin builtin) const {
const struct LayoutDescription* descs =
reinterpret_cast<const struct LayoutDescription*>(
data_ + LayoutDescriptionTableOffset());
return descs[static_cast<int>(builtin)];
}
const BuiltinLookupEntry* BuiltinLookupEntry(
ReorderedBuiltinIndex index) const {
const struct BuiltinLookupEntry* entries =
reinterpret_cast<const struct BuiltinLookupEntry*>(
data_ + BuiltinLookupEntryTableOffset());
return entries + index;
}
const uint8_t* RawMetadata() const { return data_ + RawMetadataOffset(); }
static constexpr int PadAndAlignCode(int size) {
// Ensure we have at least one byte trailing the actual builtin
// instructions which we can later fill with int3.
return RoundUp<kCodeAlignment>(size + 1);
}
static constexpr int PadAndAlignData(int size) {
// Ensure we have at least one byte trailing the actual builtin
// instructions which we can later fill with int3.
return RoundUp<InstructionStream::kMetadataAlignment>(size);
}
void PrintStatistics() const;
// The code section contains instruction streams. It is guaranteed to have
// execute permissions, and may have read permissions.
const uint8_t* code_;
uint32_t code_size_;
// The data section contains both descriptions of the code section (hashes,
// offsets, sizes) and metadata describing InstructionStream objects (see
// InstructionStream::MetadataStart()). It is guaranteed to have read
// permissions.
const uint8_t* data_;
uint32_t data_size_;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_EMBEDDED_EMBEDDED_DATA_H_

View File

@ -0,0 +1,27 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Used for building without embedded data.
#include <cstdint>
extern "C" const uint8_t v8_Default_embedded_blob_code_[];
extern "C" uint32_t v8_Default_embedded_blob_code_size_;
extern "C" const uint8_t v8_Default_embedded_blob_data_[];
extern "C" uint32_t v8_Default_embedded_blob_data_size_;
const uint8_t v8_Default_embedded_blob_code_[1] = {0};
uint32_t v8_Default_embedded_blob_code_size_ = 0;
const uint8_t v8_Default_embedded_blob_data_[1] = {0};
uint32_t v8_Default_embedded_blob_data_size_ = 0;
#if V8_ENABLE_DRUMBRAKE
#include "src/wasm/interpreter/instruction-handlers.h"
typedef void (*fun_ptr)();
#define V(name) \
extern "C" fun_ptr Builtins_##name; \
fun_ptr Builtins_##name = nullptr;
FOREACH_LOAD_STORE_INSTR_HANDLER(V)
#undef V
#endif // V8_ENABLE_DRUMBRAKE

View File

@ -0,0 +1,53 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_EMBEDDED_EMBEDDED_FILE_WRITER_INTERFACE_H_
#define V8_SNAPSHOT_EMBEDDED_EMBEDDED_FILE_WRITER_INTERFACE_H_
#include <string>
#include "v8config.h" // NOLINT(build/include_directory)
namespace v8 {
namespace internal {
class Builtins;
#if defined(V8_OS_WIN64)
namespace win64_unwindinfo {
class BuiltinUnwindInfo;
}
#endif // V8_OS_WIN64
static constexpr char kDefaultEmbeddedVariant[] = "Default";
struct LabelInfo {
int offset;
std::string name;
};
// Detailed source-code information about builtins can only be obtained by
// registration on the isolate during compilation.
class EmbeddedFileWriterInterface {
public:
// We maintain a database of filenames to synthetic IDs.
virtual int LookupOrAddExternallyCompiledFilename(const char* filename) = 0;
virtual const char* GetExternallyCompiledFilename(int index) const = 0;
virtual int GetExternallyCompiledFilenameCount() const = 0;
// The isolate will call the method below just prior to replacing the
// compiled builtin InstructionStream objects with trampolines.
virtual void PrepareBuiltinSourcePositionMap(Builtins* builtins) = 0;
#if defined(V8_OS_WIN64)
virtual void SetBuiltinUnwindData(
Builtin builtin,
const win64_unwindinfo::BuiltinUnwindInfo& unwinding_info) = 0;
#endif // V8_OS_WIN64
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_EMBEDDED_EMBEDDED_FILE_WRITER_INTERFACE_H_

View File

@ -0,0 +1,299 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/embedded/embedded-file-writer.h"
#include <algorithm>
#include <cinttypes>
#include "src/codegen/source-position-table.h"
#include "src/flags/flags.h" // For ENABLE_CONTROL_FLOW_INTEGRITY_BOOL
#include "src/objects/code-inl.h"
#include "src/snapshot/embedded/embedded-data-inl.h"
namespace v8 {
namespace internal {
namespace {
int WriteDirectiveOrSeparator(PlatformEmbeddedFileWriterBase* w,
int current_line_length,
DataDirective directive) {
int printed_chars;
if (current_line_length == 0) {
printed_chars = w->IndentedDataDirective(directive);
DCHECK_LT(0, printed_chars);
} else {
printed_chars = fprintf(w->fp(), ",");
DCHECK_EQ(1, printed_chars);
}
return current_line_length + printed_chars;
}
int WriteLineEndIfNeeded(PlatformEmbeddedFileWriterBase* w,
int current_line_length, int write_size) {
static const int kTextWidth = 100;
// Check if adding ',0xFF...FF\n"' would force a line wrap. This doesn't use
// the actual size of the string to be written to determine this so it's
// more conservative than strictly needed.
if (current_line_length + strlen(",0x") + write_size * 2 > kTextWidth) {
fprintf(w->fp(), "\n");
return 0;
} else {
return current_line_length;
}
}
} // namespace
void EmbeddedFileWriter::WriteBuiltin(PlatformEmbeddedFileWriterBase* w,
const i::EmbeddedData* blob,
const Builtin builtin) const {
const bool is_default_variant =
std::strcmp(embedded_variant_, kDefaultEmbeddedVariant) == 0;
base::EmbeddedVector<char, kTemporaryStringLength> builtin_symbol;
if (is_default_variant) {
// Create nicer symbol names for the default mode.
base::SNPrintF(builtin_symbol, "Builtins_%s", i::Builtins::name(builtin));
} else {
base::SNPrintF(builtin_symbol, "%s_Builtins_%s", embedded_variant_,
i::Builtins::name(builtin));
}
// Labels created here will show up in backtraces. We check in
// Isolate::SetEmbeddedBlob that the blob layout remains unchanged, i.e.
// that labels do not insert bytes into the middle of the blob byte
// stream.
w->DeclareFunctionBegin(builtin_symbol.begin(),
blob->InstructionSizeOf(builtin));
const int builtin_id = static_cast<int>(builtin);
const std::vector<uint8_t>& current_positions = source_positions_[builtin_id];
// The code below interleaves bytes of assembly code for the builtin
// function with source positions at the appropriate offsets.
base::Vector<const uint8_t> vpos(current_positions.data(),
current_positions.size());
v8::internal::SourcePositionTableIterator positions(
vpos, SourcePositionTableIterator::kExternalOnly);
#ifndef DEBUG
CHECK(positions.done()); // Release builds must not contain debug infos.
#endif
// Some builtins (InterpreterPushArgsThenFastConstructFunction,
// JSConstructStubGeneric) have entry points located in the middle of them, we
// need to store their addresses since they are part of the list of allowed
// return addresses in the deoptimizer.
const std::vector<LabelInfo>& current_labels = label_info_[builtin_id];
auto label = current_labels.begin();
const uint8_t* data =
reinterpret_cast<const uint8_t*>(blob->InstructionStartOf(builtin));
uint32_t size = blob->PaddedInstructionSizeOf(builtin);
uint32_t i = 0;
uint32_t next_source_pos_offset =
static_cast<uint32_t>(positions.done() ? size : positions.code_offset());
uint32_t next_label_offset = static_cast<uint32_t>(
(label == current_labels.end()) ? size : label->offset);
uint32_t next_offset = 0;
while (i < size) {
if (i == next_source_pos_offset) {
// Write source directive.
w->SourceInfo(positions.source_position().ExternalFileId(),
GetExternallyCompiledFilename(
positions.source_position().ExternalFileId()),
positions.source_position().ExternalLine());
positions.Advance();
next_source_pos_offset = static_cast<uint32_t>(
positions.done() ? size : positions.code_offset());
CHECK_GE(next_source_pos_offset, i);
}
if (i == next_label_offset) {
WriteBuiltinLabels(w, label->name);
label++;
next_label_offset = static_cast<uint32_t>(
(label == current_labels.end()) ? size : label->offset);
CHECK_GE(next_label_offset, i);
}
next_offset = std::min(next_source_pos_offset, next_label_offset);
WriteBinaryContentsAsInlineAssembly(w, data + i, next_offset - i);
i = next_offset;
}
w->DeclareFunctionEnd(builtin_symbol.begin());
}
void EmbeddedFileWriter::WriteBuiltinLabels(PlatformEmbeddedFileWriterBase* w,
std::string name) const {
w->DeclareLabel(name.c_str());
}
void EmbeddedFileWriter::WriteCodeSection(PlatformEmbeddedFileWriterBase* w,
const i::EmbeddedData* blob) const {
w->Comment(
"The embedded blob code section starts here. It contains the builtin");
w->Comment("instruction streams.");
w->SectionText();
#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64
// UMA needs an exposed function-type label at the start of the embedded
// code section.
static const char* kCodeStartForProfilerSymbolName =
"v8_code_start_for_profiler_";
static constexpr int kDummyFunctionLength = 1;
static constexpr int kDummyFunctionData = 0xcc;
w->DeclareFunctionBegin(kCodeStartForProfilerSymbolName,
kDummyFunctionLength);
// The label must not be at the same address as the first builtin, insert
// padding bytes.
WriteDirectiveOrSeparator(w, 0, kByte);
w->HexLiteral(kDummyFunctionData);
w->Newline();
w->DeclareFunctionEnd(kCodeStartForProfilerSymbolName);
#endif
w->AlignToCodeAlignment();
w->DeclareSymbolGlobal(EmbeddedBlobCodeSymbol().c_str());
w->DeclareLabelProlog(EmbeddedBlobCodeSymbol().c_str());
w->DeclareLabel(EmbeddedBlobCodeSymbol().c_str());
static_assert(Builtins::kAllBuiltinsAreIsolateIndependent);
for (ReorderedBuiltinIndex embedded_index = 0;
embedded_index < Builtins::kBuiltinCount; embedded_index++) {
Builtin builtin = blob->GetBuiltinId(embedded_index);
WriteBuiltin(w, blob, builtin);
}
w->AlignToPageSizeIfNeeded();
w->DeclareLabelEpilogue();
w->Newline();
}
void EmbeddedFileWriter::WriteFileEpilogue(PlatformEmbeddedFileWriterBase* w,
const i::EmbeddedData* blob) const {
{
base::EmbeddedVector<char, kTemporaryStringLength>
embedded_blob_code_size_symbol;
base::SNPrintF(embedded_blob_code_size_symbol,
"v8_%s_embedded_blob_code_size_", embedded_variant_);
w->Comment("The size of the embedded blob code in bytes.");
w->SectionRoData();
w->AlignToDataAlignment();
w->DeclareUint32(embedded_blob_code_size_symbol.begin(), blob->code_size());
w->Newline();
base::EmbeddedVector<char, kTemporaryStringLength>
embedded_blob_data_size_symbol;
base::SNPrintF(embedded_blob_data_size_symbol,
"v8_%s_embedded_blob_data_size_", embedded_variant_);
w->Comment("The size of the embedded blob data section in bytes.");
w->DeclareUint32(embedded_blob_data_size_symbol.begin(), blob->data_size());
w->Newline();
}
#if defined(V8_OS_WIN64)
{
base::EmbeddedVector<char, kTemporaryStringLength> unwind_info_symbol;
base::SNPrintF(unwind_info_symbol, "%s_Builtins_UnwindInfo",
embedded_variant_);
w->MaybeEmitUnwindData(unwind_info_symbol.begin(),
EmbeddedBlobCodeSymbol().c_str(), blob,
reinterpret_cast<const void*>(&unwind_infos_[0]));
}
#endif // V8_OS_WIN64
w->FileEpilogue();
}
// static
void EmbeddedFileWriter::WriteBinaryContentsAsInlineAssembly(
PlatformEmbeddedFileWriterBase* w, const uint8_t* data, uint32_t size) {
#if V8_OS_ZOS
// HLASM source must end at column 71 (followed by an optional
// line-continuation char on column 72), so write the binary data
// in 32 byte chunks (length 64):
uint32_t chunks = (size + 31) / 32;
uint32_t i, j;
uint32_t offset = 0;
for (i = 0; i < chunks; ++i) {
fprintf(w->fp(), " DC x'");
for (j = 0; offset < size && j < 32; ++j) {
fprintf(w->fp(), "%02x", data[offset++]);
}
fprintf(w->fp(), "'\n");
}
#else
int current_line_length = 0;
uint32_t i = 0;
// Begin by writing out byte chunks.
const DataDirective directive = w->ByteChunkDataDirective();
const int byte_chunk_size = DataDirectiveSize(directive);
for (; i + byte_chunk_size < size; i += byte_chunk_size) {
current_line_length =
WriteDirectiveOrSeparator(w, current_line_length, directive);
current_line_length += w->WriteByteChunk(data + i);
current_line_length =
WriteLineEndIfNeeded(w, current_line_length, byte_chunk_size);
}
if (current_line_length != 0) w->Newline();
current_line_length = 0;
// Write any trailing bytes one-by-one.
for (; i < size; i++) {
current_line_length =
WriteDirectiveOrSeparator(w, current_line_length, kByte);
current_line_length += w->HexLiteral(data[i]);
current_line_length = WriteLineEndIfNeeded(w, current_line_length, 1);
}
if (current_line_length != 0) w->Newline();
#endif // V8_OS_ZOS
}
int EmbeddedFileWriter::LookupOrAddExternallyCompiledFilename(
const char* filename) {
auto result = external_filenames_.find(filename);
if (result != external_filenames_.end()) {
return result->second;
}
int new_id =
ExternalFilenameIndexToId(static_cast<int>(external_filenames_.size()));
external_filenames_.insert(std::make_pair(filename, new_id));
external_filenames_by_index_.push_back(filename);
DCHECK_EQ(external_filenames_by_index_.size(), external_filenames_.size());
return new_id;
}
const char* EmbeddedFileWriter::GetExternallyCompiledFilename(
int fileid) const {
size_t index = static_cast<size_t>(ExternalFilenameIdToIndex(fileid));
DCHECK_GE(index, 0);
DCHECK_LT(index, external_filenames_by_index_.size());
return external_filenames_by_index_[index];
}
int EmbeddedFileWriter::GetExternallyCompiledFilenameCount() const {
return static_cast<int>(external_filenames_.size());
}
void EmbeddedFileWriter::PrepareBuiltinSourcePositionMap(Builtins* builtins) {
for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast;
++builtin) {
// Retrieve the SourcePositionTable and copy it.
Tagged<Code> code = builtins->code(builtin);
if (!code->has_source_position_table()) continue;
Tagged<TrustedByteArray> source_position_table =
code->source_position_table();
std::vector<unsigned char> data(source_position_table->begin(),
source_position_table->end());
source_positions_[static_cast<int>(builtin)] = data;
}
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,213 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_EMBEDDED_EMBEDDED_FILE_WRITER_H_
#define V8_SNAPSHOT_EMBEDDED_EMBEDDED_FILE_WRITER_H_
#include <cinttypes>
#include <cstdio>
#include <cstring>
#include <memory>
#include "src/base/platform/wrappers.h"
#include "src/base/strings.h"
#include "src/common/globals.h"
#include "src/snapshot/embedded/embedded-data.h"
#include "src/snapshot/embedded/embedded-file-writer-interface.h"
#include "src/snapshot/embedded/platform-embedded-file-writer-base.h"
#if defined(V8_OS_WIN64)
#include "src/diagnostics/unwinding-info-win64.h"
#endif // V8_OS_WIN64
namespace v8 {
namespace internal {
// Generates the embedded.S file which is later compiled into the final v8
// binary. Its contents are exported through two symbols:
//
// v8_<variant>_embedded_blob_ (intptr_t):
// a pointer to the start of the embedded blob.
// v8_<variant>_embedded_blob_size_ (uint32_t):
// size of the embedded blob in bytes.
//
// The variant is usually "Default" but can be modified in multisnapshot builds.
class EmbeddedFileWriter : public EmbeddedFileWriterInterface {
public:
int LookupOrAddExternallyCompiledFilename(const char* filename) override;
const char* GetExternallyCompiledFilename(int fileid) const override;
int GetExternallyCompiledFilenameCount() const override;
void PrepareBuiltinSourcePositionMap(Builtins* builtins) override;
#if defined(V8_OS_WIN64)
void SetBuiltinUnwindData(
Builtin builtin,
const win64_unwindinfo::BuiltinUnwindInfo& unwinding_info) override {
DCHECK_LT(static_cast<int>(builtin), Builtins::kBuiltinCount);
unwind_infos_[static_cast<int>(builtin)] = unwinding_info;
}
#endif // V8_OS_WIN64
void SetEmbeddedFile(const char* embedded_src_path) {
embedded_src_path_ = embedded_src_path;
}
void SetEmbeddedVariant(const char* embedded_variant) {
if (embedded_variant == nullptr) return;
embedded_variant_ = embedded_variant;
}
void SetTargetArch(const char* target_arch) { target_arch_ = target_arch; }
void SetTargetOs(const char* target_os) { target_os_ = target_os; }
void WriteEmbedded(const i::EmbeddedData* blob) const {
MaybeWriteEmbeddedFile(blob);
}
private:
void MaybeWriteEmbeddedFile(const i::EmbeddedData* blob) const {
if (embedded_src_path_ == nullptr) return;
FILE* fp = GetFileDescriptorOrDie(embedded_src_path_);
std::unique_ptr<PlatformEmbeddedFileWriterBase> writer =
NewPlatformEmbeddedFileWriter(target_arch_, target_os_);
writer->SetFile(fp);
WriteFilePrologue(writer.get());
WriteExternalFilenames(writer.get());
WriteDataSection(writer.get(), blob);
WriteCodeSection(writer.get(), blob);
WriteFileEpilogue(writer.get(), blob);
base::Fclose(fp);
}
static FILE* GetFileDescriptorOrDie(const char* filename) {
FILE* fp = v8::base::OS::FOpen(filename, "w");
if (fp == nullptr) {
i::PrintF("Unable to open file \"%s\" for writing.\n", filename);
exit(1);
}
return fp;
}
void WriteFilePrologue(PlatformEmbeddedFileWriterBase* w) const {
w->Comment("Autogenerated file. Do not edit.");
w->Newline();
w->FilePrologue();
}
void WriteExternalFilenames(PlatformEmbeddedFileWriterBase* w) const {
#ifndef DEBUG
// Release builds must not contain debug infos.
CHECK_EQ(external_filenames_by_index_.size(), 0);
#endif
w->Comment(
"Source positions in the embedded blob refer to filenames by id.");
w->Comment("Assembly directives here map the id to a filename.");
w->Newline();
// Write external filenames.
int size = static_cast<int>(external_filenames_by_index_.size());
for (int i = 0; i < size; i++) {
w->DeclareExternalFilename(ExternalFilenameIndexToId(i),
external_filenames_by_index_[i]);
}
}
// Fairly arbitrary but should fit all symbol names.
static constexpr int kTemporaryStringLength = 256;
std::string EmbeddedBlobCodeSymbol() const {
base::EmbeddedVector<char, kTemporaryStringLength>
embedded_blob_code_symbol;
base::SNPrintF(embedded_blob_code_symbol, "v8_%s_embedded_blob_code_",
embedded_variant_);
return std::string{embedded_blob_code_symbol.begin()};
}
std::string EmbeddedBlobDataSymbol() const {
base::EmbeddedVector<char, kTemporaryStringLength>
embedded_blob_data_symbol;
base::SNPrintF(embedded_blob_data_symbol, "v8_%s_embedded_blob_data_",
embedded_variant_);
return std::string{embedded_blob_data_symbol.begin()};
}
void WriteDataSection(PlatformEmbeddedFileWriterBase* w,
const i::EmbeddedData* blob) const {
w->Comment("The embedded blob data section starts here.");
w->SectionRoData();
w->AlignToDataAlignment();
w->DeclareSymbolGlobal(EmbeddedBlobDataSymbol().c_str());
w->DeclareLabelProlog(EmbeddedBlobDataSymbol().c_str());
w->DeclareLabel(EmbeddedBlobDataSymbol().c_str());
WriteBinaryContentsAsInlineAssembly(w, blob->data(), blob->data_size());
w->DeclareLabelEpilogue();
w->Newline();
}
void WriteBuiltin(PlatformEmbeddedFileWriterBase* w,
const i::EmbeddedData* blob, const Builtin builtin) const;
void WriteBuiltinLabels(PlatformEmbeddedFileWriterBase* w,
std::string name) const;
void WriteCodeSection(PlatformEmbeddedFileWriterBase* w,
const i::EmbeddedData* blob) const;
void WriteFileEpilogue(PlatformEmbeddedFileWriterBase* w,
const i::EmbeddedData* blob) const;
#if defined(V8_OS_WIN_X64)
void WriteUnwindInfoEntry(PlatformEmbeddedFileWriterBase* w,
uint64_t rva_start, uint64_t rva_end) const;
#endif
static void WriteBinaryContentsAsInlineAssembly(
PlatformEmbeddedFileWriterBase* w, const uint8_t* data, uint32_t size);
// In assembly directives, filename ids need to begin with 1.
static constexpr int kFirstExternalFilenameId = 1;
static int ExternalFilenameIndexToId(int index) {
return kFirstExternalFilenameId + index;
}
static int ExternalFilenameIdToIndex(int id) {
return id - kFirstExternalFilenameId;
}
private:
std::vector<uint8_t> source_positions_[Builtins::kBuiltinCount];
std::vector<LabelInfo> label_info_[Builtins::kBuiltinCount];
#if defined(V8_OS_WIN64)
win64_unwindinfo::BuiltinUnwindInfo unwind_infos_[Builtins::kBuiltinCount];
#endif // V8_OS_WIN64
std::map<const char*, int> external_filenames_;
std::vector<const char*> external_filenames_by_index_;
// The file to generate or nullptr.
const char* embedded_src_path_ = nullptr;
// The variant is only used in multi-snapshot builds and otherwise set to
// "Default".
const char* embedded_variant_ = kDefaultEmbeddedVariant;
// {target_arch} and {target_os} control the generated assembly format. Note
// these may differ from both host- and target-platforms specified through
// e.g. V8_OS_* and V8_TARGET_ARCH_* defines.
const char* target_arch_ = nullptr;
const char* target_os_ = nullptr;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_EMBEDDED_EMBEDDED_FILE_WRITER_H_

View File

@ -0,0 +1,133 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/embedded/platform-embedded-file-writer-aix.h"
#include "src/objects/instruction-stream.h"
namespace v8 {
namespace internal {
#define SYMBOL_PREFIX ""
namespace {
const char* DirectiveAsString(DataDirective directive) {
switch (directive) {
case kByte:
return ".byte";
case kLong:
return ".long";
case kQuad:
return ".llong";
default:
UNREACHABLE();
}
}
} // namespace
void PlatformEmbeddedFileWriterAIX::SectionText() {
fprintf(fp_, ".csect [GL], 6\n");
}
void PlatformEmbeddedFileWriterAIX::SectionRoData() {
fprintf(fp_, ".csect[RO]\n");
}
void PlatformEmbeddedFileWriterAIX::DeclareUint32(const char* name,
uint32_t value) {
DeclareSymbolGlobal(name);
fprintf(fp_, ".align 2\n");
fprintf(fp_, "%s:\n", name);
IndentedDataDirective(kLong);
fprintf(fp_, "%d\n", value);
Newline();
}
void PlatformEmbeddedFileWriterAIX::DeclareSymbolGlobal(const char* name) {
// These symbols are not visible outside of the final binary, this allows for
// reduced binary size, and less work for the dynamic linker.
fprintf(fp_, ".globl %s, hidden\n", name);
}
void PlatformEmbeddedFileWriterAIX::AlignToCodeAlignment() {
#if V8_TARGET_ARCH_X64
// On x64 use 64-bytes code alignment to allow 64-bytes loop header alignment.
static_assert((1 << 6) >= kCodeAlignment);
fprintf(fp_, ".align 6\n");
#elif V8_TARGET_ARCH_PPC64
// 64 byte alignment is needed on ppc64 to make sure p10 prefixed instructions
// don't cross 64-byte boundaries.
static_assert((1 << 6) >= kCodeAlignment);
fprintf(fp_, ".align 6\n");
#else
static_assert((1 << 5) >= kCodeAlignment);
fprintf(fp_, ".align 5\n");
#endif
}
void PlatformEmbeddedFileWriterAIX::AlignToDataAlignment() {
static_assert((1 << 3) >= InstructionStream::kMetadataAlignment);
fprintf(fp_, ".align 3\n");
}
void PlatformEmbeddedFileWriterAIX::Comment(const char* string) {
fprintf(fp_, "// %s\n", string);
}
void PlatformEmbeddedFileWriterAIX::DeclareLabel(const char* name) {
// .global is required on AIX, if the label is used/referenced in another file
// later to be linked.
fprintf(fp_, ".globl %s\n", name);
fprintf(fp_, "%s:\n", name);
}
void PlatformEmbeddedFileWriterAIX::SourceInfo(int fileid, const char* filename,
int line) {
fprintf(fp_, ".xline %d, \"%s\"\n", line, filename);
}
// TODO(mmarchini): investigate emitting size annotations for AIX
void PlatformEmbeddedFileWriterAIX::DeclareFunctionBegin(const char* name,
uint32_t size) {
Newline();
if (ENABLE_CONTROL_FLOW_INTEGRITY_BOOL) {
DeclareSymbolGlobal(name);
}
fprintf(fp_, ".csect %s[DS]\n", name); // function descriptor
fprintf(fp_, "%s:\n", name);
fprintf(fp_, ".llong .%s, 0, 0\n", name);
SectionText();
fprintf(fp_, ".%s:\n", name);
}
void PlatformEmbeddedFileWriterAIX::DeclareFunctionEnd(const char* name) {}
void PlatformEmbeddedFileWriterAIX::FilePrologue() {}
void PlatformEmbeddedFileWriterAIX::DeclareExternalFilename(
int fileid, const char* filename) {
// File name cannot be declared with an identifier on AIX.
// We use the SourceInfo method to emit debug info in
//.xline <line-number> <file-name> format.
}
void PlatformEmbeddedFileWriterAIX::FileEpilogue() {}
int PlatformEmbeddedFileWriterAIX::IndentedDataDirective(
DataDirective directive) {
return fprintf(fp_, " %s ", DirectiveAsString(directive));
}
DataDirective PlatformEmbeddedFileWriterAIX::ByteChunkDataDirective() const {
// PPC uses a fixed 4 byte instruction set, using .long
// to prevent any unnecessary padding.
return kLong;
}
#undef SYMBOL_PREFIX
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,57 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_AIX_H_
#define V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_AIX_H_
#include "src/base/macros.h"
#include "src/snapshot/embedded/platform-embedded-file-writer-base.h"
namespace v8 {
namespace internal {
class PlatformEmbeddedFileWriterAIX : public PlatformEmbeddedFileWriterBase {
public:
PlatformEmbeddedFileWriterAIX(EmbeddedTargetArch target_arch,
EmbeddedTargetOs target_os)
: target_arch_(target_arch), target_os_(target_os) {
USE(target_arch_);
USE(target_os_);
DCHECK_EQ(target_os_, EmbeddedTargetOs::kAIX);
}
void SectionText() override;
void SectionRoData() override;
void AlignToCodeAlignment() override;
void AlignToDataAlignment() override;
void DeclareUint32(const char* name, uint32_t value) override;
void DeclareSymbolGlobal(const char* name) override;
void DeclareLabel(const char* name) override;
void SourceInfo(int fileid, const char* filename, int line) override;
void DeclareFunctionBegin(const char* name, uint32_t size) override;
void DeclareFunctionEnd(const char* name) override;
void Comment(const char* string) override;
void FilePrologue() override;
void DeclareExternalFilename(int fileid, const char* filename) override;
void FileEpilogue() override;
int IndentedDataDirective(DataDirective directive) override;
DataDirective ByteChunkDataDirective() const override;
private:
const EmbeddedTargetArch target_arch_;
const EmbeddedTargetOs target_os_;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_AIX_H_

View File

@ -0,0 +1,205 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/embedded/platform-embedded-file-writer-base.h"
#include <string>
#include "src/common/globals.h"
#include "src/snapshot/embedded/platform-embedded-file-writer-aix.h"
#include "src/snapshot/embedded/platform-embedded-file-writer-generic.h"
#include "src/snapshot/embedded/platform-embedded-file-writer-mac.h"
#include "src/snapshot/embedded/platform-embedded-file-writer-win.h"
#include "src/snapshot/embedded/platform-embedded-file-writer-zos.h"
namespace v8 {
namespace internal {
DataDirective PointerSizeDirective() {
if (kSystemPointerSize == 8) {
return kQuad;
} else {
CHECK_EQ(4, kSystemPointerSize);
return kLong;
}
}
int PlatformEmbeddedFileWriterBase::HexLiteral(uint64_t value) {
return fprintf(fp_, "0x%" PRIx64, value);
}
int DataDirectiveSize(DataDirective directive) {
switch (directive) {
case kByte:
return 1;
case kLong:
return 4;
case kQuad:
return 8;
case kOcta:
return 16;
}
UNREACHABLE();
}
int PlatformEmbeddedFileWriterBase::WriteByteChunk(const uint8_t* data) {
size_t kSize = DataDirectiveSize(ByteChunkDataDirective());
size_t kHalfSize = kSize / 2;
uint64_t high = 0, low = 0;
switch (kSize) {
case 1:
low = *data;
break;
case 4:
low = *reinterpret_cast<const uint32_t*>(data);
break;
case 8:
low = *reinterpret_cast<const uint64_t*>(data);
break;
case 16:
#ifdef V8_TARGET_BIG_ENDIAN
memcpy(&high, data, kHalfSize);
memcpy(&low, data + kHalfSize, kHalfSize);
#else
memcpy(&high, data + kHalfSize, kHalfSize);
memcpy(&low, data, kHalfSize);
#endif // V8_TARGET_BIG_ENDIAN
break;
default:
UNREACHABLE();
}
if (high != 0) {
return fprintf(fp(), "0x%" PRIx64 "%016" PRIx64, high, low);
} else {
return fprintf(fp(), "0x%" PRIx64, low);
}
}
namespace {
EmbeddedTargetArch DefaultEmbeddedTargetArch() {
#if defined(V8_TARGET_ARCH_ARM)
return EmbeddedTargetArch::kArm;
#elif defined(V8_TARGET_ARCH_ARM64)
return EmbeddedTargetArch::kArm64;
#elif defined(V8_TARGET_ARCH_IA32)
return EmbeddedTargetArch::kIA32;
#elif defined(V8_TARGET_ARCH_X64)
return EmbeddedTargetArch::kX64;
#else
return EmbeddedTargetArch::kGeneric;
#endif
}
EmbeddedTargetArch ToEmbeddedTargetArch(const char* s) {
if (s == nullptr) {
return DefaultEmbeddedTargetArch();
}
std::string string(s);
if (string == "arm") {
return EmbeddedTargetArch::kArm;
} else if (string == "arm64") {
return EmbeddedTargetArch::kArm64;
} else if (string == "ia32") {
return EmbeddedTargetArch::kIA32;
} else if (string == "x64") {
return EmbeddedTargetArch::kX64;
} else {
return EmbeddedTargetArch::kGeneric;
}
}
EmbeddedTargetOs DefaultEmbeddedTargetOs() {
#if defined(V8_OS_AIX)
return EmbeddedTargetOs::kAIX;
#elif defined(V8_OS_DARWIN)
return EmbeddedTargetOs::kMac;
#elif defined(V8_OS_WIN)
return EmbeddedTargetOs::kWin;
#elif defined(V8_OS_ZOS)
return EmbeddedTargetOs::kZOS;
#else
return EmbeddedTargetOs::kGeneric;
#endif
}
EmbeddedTargetOs ToEmbeddedTargetOs(const char* s) {
if (s == nullptr) {
return DefaultEmbeddedTargetOs();
}
std::string string(s);
// Python 3.9+ on IBM i returns os400 as sys.platform instead of aix
if (string == "aix" || string == "os400") {
return EmbeddedTargetOs::kAIX;
} else if (string == "chromeos") {
return EmbeddedTargetOs::kChromeOS;
} else if (string == "fuchsia") {
return EmbeddedTargetOs::kFuchsia;
} else if (string == "ios" || string == "mac") {
return EmbeddedTargetOs::kMac;
} else if (string == "win") {
return EmbeddedTargetOs::kWin;
} else if (string == "starboard") {
return EmbeddedTargetOs::kStarboard;
} else if (string == "zos") {
return EmbeddedTargetOs::kZOS;
} else {
return EmbeddedTargetOs::kGeneric;
}
}
} // namespace
std::unique_ptr<PlatformEmbeddedFileWriterBase> NewPlatformEmbeddedFileWriter(
const char* target_arch, const char* target_os) {
auto embedded_target_arch = ToEmbeddedTargetArch(target_arch);
auto embedded_target_os = ToEmbeddedTargetOs(target_os);
if (embedded_target_os == EmbeddedTargetOs::kStarboard) {
// target OS is "Starboard" for all starboard build so we need to
// use host OS macros to decide which writer to use.
// Cobalt also has Windows-based Posix target platform,
// in which case generic writer should be used.
switch (DefaultEmbeddedTargetOs()) {
case EmbeddedTargetOs::kMac:
#if defined(V8_TARGET_OS_WIN)
case EmbeddedTargetOs::kWin:
// V8_TARGET_OS_WIN is used to enable WINDOWS-specific assembly code,
// for windows-hosted non-windows targets, we should still fallback to
// the generic writer.
#endif
embedded_target_os = DefaultEmbeddedTargetOs();
break;
default:
// In the block below, we will use WriterGeneric for other cases.
break;
}
}
if (embedded_target_os == EmbeddedTargetOs::kAIX) {
return std::make_unique<PlatformEmbeddedFileWriterAIX>(embedded_target_arch,
embedded_target_os);
} else if (embedded_target_os == EmbeddedTargetOs::kMac) {
return std::make_unique<PlatformEmbeddedFileWriterMac>(embedded_target_arch,
embedded_target_os);
} else if (embedded_target_os == EmbeddedTargetOs::kWin) {
return std::make_unique<PlatformEmbeddedFileWriterWin>(embedded_target_arch,
embedded_target_os);
} else if (embedded_target_os == EmbeddedTargetOs::kZOS) {
return std::make_unique<PlatformEmbeddedFileWriterZOS>(embedded_target_arch,
embedded_target_os);
} else {
return std::make_unique<PlatformEmbeddedFileWriterGeneric>(
embedded_target_arch, embedded_target_os);
}
UNREACHABLE();
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,123 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_BASE_H_
#define V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_BASE_H_
#include <cinttypes>
#include <cstdio> // For FILE.
#include <memory>
#if V8_ENABLE_DRUMBRAKE
#include <string>
#endif // V8_ENABLE_DRUMBRAKE
namespace v8 {
namespace internal {
class EmbeddedData;
enum DataDirective {
kByte,
kLong,
kQuad,
kOcta,
};
DataDirective PointerSizeDirective();
int DataDirectiveSize(DataDirective directive);
enum class EmbeddedTargetOs {
kAIX,
kChromeOS,
kFuchsia,
kMac,
kWin,
kStarboard,
kZOS,
kGeneric, // Everything not covered above falls in here.
};
enum class EmbeddedTargetArch {
kArm,
kArm64,
kIA32,
kX64,
kGeneric, // Everything not covered above falls in here.
};
// The platform-dependent logic for emitting assembly code for the generated
// embedded.S file.
class PlatformEmbeddedFileWriterBase {
public:
virtual ~PlatformEmbeddedFileWriterBase() = default;
void SetFile(FILE* fp) { fp_ = fp; }
FILE* fp() const { return fp_; }
virtual void SectionText() = 0;
virtual void SectionRoData() = 0;
virtual void AlignToCodeAlignment() = 0;
virtual void AlignToPageSizeIfNeeded() {}
virtual void AlignToDataAlignment() = 0;
virtual void DeclareUint32(const char* name, uint32_t value) = 0;
virtual void DeclareSymbolGlobal(const char* name) = 0;
virtual void DeclareLabel(const char* name) = 0;
virtual void DeclareLabelProlog(const char* name) {}
virtual void DeclareLabelEpilogue() {}
virtual void SourceInfo(int fileid, const char* filename, int line) = 0;
virtual void DeclareFunctionBegin(const char* name, uint32_t size) = 0;
virtual void DeclareFunctionEnd(const char* name) = 0;
// Returns the number of printed characters.
virtual int HexLiteral(uint64_t value);
virtual void Comment(const char* string) = 0;
virtual void Newline() { fprintf(fp_, "\n"); }
virtual void FilePrologue() = 0;
virtual void DeclareExternalFilename(int fileid, const char* filename) = 0;
virtual void FileEpilogue() = 0;
virtual int IndentedDataDirective(DataDirective directive) = 0;
virtual DataDirective ByteChunkDataDirective() const { return kOcta; }
virtual int WriteByteChunk(const uint8_t* data);
// This awkward interface works around the fact that unwind data emission
// is both high-level and platform-dependent. The former implies it should
// live in EmbeddedFileWriter, but code there should be platform-independent.
//
// Emits unwinding data on x64 Windows, and does nothing otherwise.
virtual void MaybeEmitUnwindData(const char* unwind_info_symbol,
const char* embedded_blob_data_symbol,
const EmbeddedData* blob,
const void* unwind_infos) {}
protected:
FILE* fp_ = nullptr;
};
// The factory function. Returns the appropriate platform-specific instance.
std::unique_ptr<PlatformEmbeddedFileWriterBase> NewPlatformEmbeddedFileWriter(
const char* target_arch, const char* target_os);
#if V8_ENABLE_DRUMBRAKE
inline bool IsDrumBrakeInstructionHandler(const char* name) {
std::string builtin_name(name);
return builtin_name.find("Builtins_r2r_") == 0 ||
builtin_name.find("Builtins_r2s_") == 0 ||
builtin_name.find("Builtins_s2r_") == 0 ||
builtin_name.find("Builtins_s2s_") == 0;
}
#endif // V8_ENABLE_DRUMBRAKE
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_BASE_H_

View File

@ -0,0 +1,181 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/embedded/platform-embedded-file-writer-generic.h"
#include <algorithm>
#include <cinttypes>
#include "src/objects/instruction-stream.h"
namespace v8 {
namespace internal {
#define SYMBOL_PREFIX ""
namespace {
const char* DirectiveAsString(DataDirective directive) {
switch (directive) {
case kByte:
return ".byte";
case kLong:
return ".long";
case kQuad:
return ".quad";
case kOcta:
return ".octa";
}
UNREACHABLE();
}
} // namespace
void PlatformEmbeddedFileWriterGeneric::SectionText() {
if (target_os_ == EmbeddedTargetOs::kChromeOS) {
fprintf(fp_, ".section .text.hot.embedded\n");
} else {
fprintf(fp_, ".section .text\n");
}
}
void PlatformEmbeddedFileWriterGeneric::SectionRoData() {
fprintf(fp_, ".section .rodata\n");
}
void PlatformEmbeddedFileWriterGeneric::DeclareUint32(const char* name,
uint32_t value) {
DeclareSymbolGlobal(name);
DeclareLabel(name);
IndentedDataDirective(kLong);
fprintf(fp_, "%d", value);
Newline();
}
void PlatformEmbeddedFileWriterGeneric::DeclareSymbolGlobal(const char* name) {
fprintf(fp_, ".global %s%s\n", SYMBOL_PREFIX, name);
// These symbols are not visible outside of the final binary, this allows for
// reduced binary size, and less work for the dynamic linker.
fprintf(fp_, ".hidden %s\n", name);
}
void PlatformEmbeddedFileWriterGeneric::AlignToCodeAlignment() {
#if (V8_OS_ANDROID || V8_OS_LINUX) && \
(V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM64)
// On these architectures and platforms, we remap the builtins, so need these
// to be aligned on a page boundary.
fprintf(fp_, ".balign 4096\n");
#elif V8_TARGET_ARCH_X64
// On x64 use 64-bytes code alignment to allow 64-bytes loop header alignment.
static_assert(64 >= kCodeAlignment);
fprintf(fp_, ".balign 64\n");
#elif V8_TARGET_ARCH_PPC64
// 64 byte alignment is needed on ppc64 to make sure p10 prefixed instructions
// don't cross 64-byte boundaries.
static_assert(64 >= kCodeAlignment);
fprintf(fp_, ".balign 64\n");
#else
static_assert(32 >= kCodeAlignment);
fprintf(fp_, ".balign 32\n");
#endif
}
void PlatformEmbeddedFileWriterGeneric::AlignToPageSizeIfNeeded() {
#if (V8_OS_ANDROID || V8_OS_LINUX) && \
(V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM64)
// Since the builtins are remapped, need to pad until the next page boundary.
fprintf(fp_, ".balign 4096\n");
#endif
}
void PlatformEmbeddedFileWriterGeneric::AlignToDataAlignment() {
// On Windows ARM64, s390, PPC and possibly more platforms, aligned load
// instructions are used to retrieve v8_Default_embedded_blob_ and/or
// v8_Default_embedded_blob_size_. The generated instructions require the
// load target to be aligned at 8 bytes (2^3).
static_assert(8 >= InstructionStream::kMetadataAlignment);
fprintf(fp_, ".balign 8\n");
}
void PlatformEmbeddedFileWriterGeneric::Comment(const char* string) {
fprintf(fp_, "// %s\n", string);
}
void PlatformEmbeddedFileWriterGeneric::DeclareLabel(const char* name) {
fprintf(fp_, "%s%s:\n", SYMBOL_PREFIX, name);
}
void PlatformEmbeddedFileWriterGeneric::SourceInfo(int fileid,
const char* filename,
int line) {
fprintf(fp_, ".loc %d %d\n", fileid, line);
}
void PlatformEmbeddedFileWriterGeneric::DeclareFunctionBegin(const char* name,
uint32_t size) {
#if V8_ENABLE_DRUMBRAKE
if (IsDrumBrakeInstructionHandler(name)) {
DeclareSymbolGlobal(name);
}
#endif // V8_ENABLE_DRUMBRAKE
DeclareLabel(name);
if (target_arch_ == EmbeddedTargetArch::kArm ||
target_arch_ == EmbeddedTargetArch::kArm64) {
// ELF format binaries on ARM use ".type <function name>, %function"
// to create a DWARF subprogram entry.
fprintf(fp_, ".type %s, %%function\n", name);
} else {
// Other ELF Format binaries use ".type <function name>, @function"
// to create a DWARF subprogram entry.
fprintf(fp_, ".type %s, @function\n", name);
}
fprintf(fp_, ".size %s, %u\n", name, size);
}
void PlatformEmbeddedFileWriterGeneric::DeclareFunctionEnd(const char* name) {}
void PlatformEmbeddedFileWriterGeneric::FilePrologue() {}
void PlatformEmbeddedFileWriterGeneric::DeclareExternalFilename(
int fileid, const char* filename) {
// Replace any Windows style paths (backslashes) with forward
// slashes.
std::string fixed_filename(filename);
std::replace(fixed_filename.begin(), fixed_filename.end(), '\\', '/');
fprintf(fp_, ".file %d \"%s\"\n", fileid, fixed_filename.c_str());
}
void PlatformEmbeddedFileWriterGeneric::FileEpilogue() {
// Omitting this section can imply an executable stack, which is usually
// a linker warning/error. C++ compilers add these automatically, but
// compiling assembly requires the .note.GNU-stack section to be inserted
// manually.
// Additional documentation:
// https://wiki.gentoo.org/wiki/Hardened/GNU_stack_quickstart
fprintf(fp_, ".section .note.GNU-stack,\"\",%%progbits\n");
}
int PlatformEmbeddedFileWriterGeneric::IndentedDataDirective(
DataDirective directive) {
return fprintf(fp_, " %s ", DirectiveAsString(directive));
}
DataDirective PlatformEmbeddedFileWriterGeneric::ByteChunkDataDirective()
const {
#if defined(V8_TARGET_ARCH_MIPS64) || defined(V8_TARGET_ARCH_LOONG64)
// MIPS and LOONG64 uses a fixed 4 byte instruction set, using .long
// to prevent any unnecessary padding.
return kLong;
#else
// Other ISAs just listen to the base
return PlatformEmbeddedFileWriterBase::ByteChunkDataDirective();
#endif
}
#undef SYMBOL_PREFIX
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,59 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_GENERIC_H_
#define V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_GENERIC_H_
#include "src/common/globals.h" // For V8_OS_WIN_X64
#include "src/snapshot/embedded/platform-embedded-file-writer-base.h"
namespace v8 {
namespace internal {
class PlatformEmbeddedFileWriterGeneric
: public PlatformEmbeddedFileWriterBase {
public:
PlatformEmbeddedFileWriterGeneric(EmbeddedTargetArch target_arch,
EmbeddedTargetOs target_os)
: target_arch_(target_arch), target_os_(target_os) {
DCHECK(target_os_ == EmbeddedTargetOs::kChromeOS ||
target_os_ == EmbeddedTargetOs::kFuchsia ||
target_os_ == EmbeddedTargetOs::kGeneric);
}
void SectionText() override;
void SectionRoData() override;
void AlignToCodeAlignment() override;
void AlignToPageSizeIfNeeded() override;
void AlignToDataAlignment() override;
void DeclareUint32(const char* name, uint32_t value) override;
void DeclareSymbolGlobal(const char* name) override;
void DeclareLabel(const char* name) override;
void SourceInfo(int fileid, const char* filename, int line) override;
void DeclareFunctionBegin(const char* name, uint32_t size) override;
void DeclareFunctionEnd(const char* name) override;
void Comment(const char* string) override;
void FilePrologue() override;
void DeclareExternalFilename(int fileid, const char* filename) override;
void FileEpilogue() override;
int IndentedDataDirective(DataDirective directive) override;
DataDirective ByteChunkDataDirective() const override;
private:
const EmbeddedTargetArch target_arch_;
const EmbeddedTargetOs target_os_;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_GENERIC_H_

View File

@ -0,0 +1,126 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/embedded/platform-embedded-file-writer-mac.h"
#include "src/objects/instruction-stream.h"
namespace v8 {
namespace internal {
namespace {
const char* DirectiveAsString(DataDirective directive) {
switch (directive) {
case kByte:
return ".byte";
case kLong:
return ".long";
case kQuad:
return ".quad";
case kOcta:
return ".octa";
}
UNREACHABLE();
}
} // namespace
void PlatformEmbeddedFileWriterMac::SectionText() { fprintf(fp_, ".text\n"); }
void PlatformEmbeddedFileWriterMac::SectionRoData() {
fprintf(fp_, ".const_data\n");
}
void PlatformEmbeddedFileWriterMac::DeclareUint32(const char* name,
uint32_t value) {
DeclareSymbolGlobal(name);
DeclareLabel(name);
IndentedDataDirective(kLong);
fprintf(fp_, "%d", value);
Newline();
}
void PlatformEmbeddedFileWriterMac::DeclareSymbolGlobal(const char* name) {
// TODO(jgruber): Investigate switching to .globl. Using .private_extern
// prevents something along the compilation chain from messing with the
// embedded blob. Using .global here causes embedded blob hash verification
// failures at runtime.
fprintf(fp_, ".private_extern _%s\n", name);
}
void PlatformEmbeddedFileWriterMac::AlignToCodeAlignment() {
#if V8_TARGET_ARCH_X64
// On x64 use 64-bytes code alignment to allow 64-bytes loop header alignment.
static_assert(64 >= kCodeAlignment);
fprintf(fp_, ".balign 64\n");
#elif V8_TARGET_ARCH_PPC64
// 64 byte alignment is needed on ppc64 to make sure p10 prefixed instructions
// don't cross 64-byte boundaries.
static_assert(64 >= kCodeAlignment);
fprintf(fp_, ".balign 64\n");
#elif V8_TARGET_ARCH_ARM64
// ARM64 macOS has a 16kiB page size. Since we want to remap it on the heap,
// needs to be page-aligned.
fprintf(fp_, ".balign 16384\n");
#else
static_assert(32 >= kCodeAlignment);
fprintf(fp_, ".balign 32\n");
#endif
}
void PlatformEmbeddedFileWriterMac::AlignToPageSizeIfNeeded() {
#if V8_TARGET_ARCH_ARM64
// ARM64 macOS has a 16kiB page size. Since we want to remap builtins on the
// heap, make sure that the trailing part of the page doesn't contain anything
// dangerous.
fprintf(fp_, ".balign 16384\n");
#endif
}
void PlatformEmbeddedFileWriterMac::AlignToDataAlignment() {
static_assert(8 >= InstructionStream::kMetadataAlignment);
fprintf(fp_, ".balign 8\n");
}
void PlatformEmbeddedFileWriterMac::Comment(const char* string) {
fprintf(fp_, "// %s\n", string);
}
void PlatformEmbeddedFileWriterMac::DeclareLabel(const char* name) {
fprintf(fp_, "_%s:\n", name);
}
void PlatformEmbeddedFileWriterMac::SourceInfo(int fileid, const char* filename,
int line) {
fprintf(fp_, ".loc %d %d\n", fileid, line);
}
// TODO(mmarchini): investigate emitting size annotations for OS X
void PlatformEmbeddedFileWriterMac::DeclareFunctionBegin(const char* name,
uint32_t size) {
DeclareLabel(name);
// TODO(mvstanton): Investigate the proper incantations to mark the label as
// a function on OSX.
}
void PlatformEmbeddedFileWriterMac::DeclareFunctionEnd(const char* name) {}
void PlatformEmbeddedFileWriterMac::FilePrologue() {}
void PlatformEmbeddedFileWriterMac::DeclareExternalFilename(
int fileid, const char* filename) {
fprintf(fp_, ".file %d \"%s\"\n", fileid, filename);
}
void PlatformEmbeddedFileWriterMac::FileEpilogue() {}
int PlatformEmbeddedFileWriterMac::IndentedDataDirective(
DataDirective directive) {
return fprintf(fp_, " %s ", DirectiveAsString(directive));
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,56 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_MAC_H_
#define V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_MAC_H_
#include "src/base/macros.h"
#include "src/snapshot/embedded/platform-embedded-file-writer-base.h"
namespace v8 {
namespace internal {
class PlatformEmbeddedFileWriterMac : public PlatformEmbeddedFileWriterBase {
public:
PlatformEmbeddedFileWriterMac(EmbeddedTargetArch target_arch,
EmbeddedTargetOs target_os)
: target_arch_(target_arch), target_os_(target_os) {
USE(target_arch_);
USE(target_os_);
DCHECK_EQ(target_os_, EmbeddedTargetOs::kMac);
}
void SectionText() override;
void SectionRoData() override;
void AlignToCodeAlignment() override;
void AlignToPageSizeIfNeeded() override;
void AlignToDataAlignment() override;
void DeclareUint32(const char* name, uint32_t value) override;
void DeclareSymbolGlobal(const char* name) override;
void DeclareLabel(const char* name) override;
void SourceInfo(int fileid, const char* filename, int line) override;
void DeclareFunctionBegin(const char* name, uint32_t size) override;
void DeclareFunctionEnd(const char* name) override;
void Comment(const char* string) override;
void FilePrologue() override;
void DeclareExternalFilename(int fileid, const char* filename) override;
void FileEpilogue() override;
int IndentedDataDirective(DataDirective directive) override;
private:
const EmbeddedTargetArch target_arch_;
const EmbeddedTargetOs target_os_;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_MAC_H_

View File

@ -0,0 +1,727 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/embedded/platform-embedded-file-writer-win.h"
#include <algorithm>
#include "src/common/globals.h" // For V8_OS_WIN64
#if defined(V8_OS_WIN64)
#include "src/builtins/builtins.h"
#include "src/diagnostics/unwinding-info-win64.h"
#include "src/snapshot/embedded/embedded-data-inl.h"
#include "src/snapshot/embedded/embedded-file-writer.h"
#endif // V8_OS_WIN64
// V8_CC_MSVC is true for both MSVC and clang on windows. clang can handle
// __asm__-style inline assembly but MSVC cannot, and thus we need a more
// precise compiler detection that can distinguish between the two. clang on
// windows sets both __clang__ and _MSC_VER, MSVC sets only _MSC_VER.
#if defined(_MSC_VER) && !defined(__clang__)
#define V8_COMPILER_IS_MSVC
#endif
#if defined(V8_COMPILER_IS_MSVC)
#include "src/flags/flags.h"
#endif
namespace v8 {
namespace internal {
// MSVC uses MASM for x86 and x64, while it has a ARMASM for ARM32 and
// ARMASM64 for ARM64. Since ARMASM and ARMASM64 accept a slightly tweaked
// version of ARM assembly language, they are referred to together in Visual
// Studio project files as MARMASM.
//
// ARM assembly language docs:
// http://infocenter.arm.com/help/topic/com.arm.doc.dui0802b/index.html
// Microsoft ARM assembler and assembly language docs:
// https://docs.microsoft.com/en-us/cpp/assembler/arm/arm-assembler-reference
// Name mangling.
// Symbols are prefixed with an underscore on 32-bit architectures.
#if !defined(V8_TARGET_ARCH_X64) && !defined(V8_TARGET_ARCH_ARM64)
#define SYMBOL_PREFIX "_"
#else
#define SYMBOL_PREFIX ""
#endif
// Notes:
//
// Cross-bitness builds are unsupported. It's thus safe to detect bitness
// through compile-time defines.
//
// Cross-compiler builds (e.g. with mixed use of clang / MSVC) are likewise
// unsupported and hence the compiler can also be detected through compile-time
// defines.
namespace {
#if defined(V8_OS_WIN_X64)
void WriteUnwindInfoEntry(PlatformEmbeddedFileWriterWin* w,
const char* unwind_info_symbol,
const char* embedded_blob_data_symbol,
uint64_t rva_start, uint64_t rva_end) {
w->DeclareRvaToSymbol(embedded_blob_data_symbol, rva_start);
w->DeclareRvaToSymbol(embedded_blob_data_symbol, rva_end);
w->DeclareRvaToSymbol(unwind_info_symbol);
}
void EmitUnwindData(PlatformEmbeddedFileWriterWin* w,
const char* unwind_info_symbol,
const char* embedded_blob_data_symbol,
const EmbeddedData* blob,
const win64_unwindinfo::BuiltinUnwindInfo* unwind_infos) {
// Emit an UNWIND_INFO (XDATA) struct, which contains the unwinding
// information that is used for all builtin functions.
DCHECK(win64_unwindinfo::CanEmitUnwindInfoForBuiltins());
w->Comment("xdata for all the code in the embedded blob.");
w->DeclareExternalFunction(CRASH_HANDLER_FUNCTION_NAME_STRING);
w->StartXdataSection();
{
w->DeclareLabel(unwind_info_symbol);
std::vector<uint8_t> xdata =
win64_unwindinfo::GetUnwindInfoForBuiltinFunctions();
DCHECK(!xdata.empty());
w->IndentedDataDirective(kByte);
for (size_t i = 0; i < xdata.size(); i++) {
if (i > 0) fprintf(w->fp(), ",");
w->HexLiteral(xdata[i]);
}
w->Newline();
w->Comment(" ExceptionHandler");
w->DeclareRvaToSymbol(CRASH_HANDLER_FUNCTION_NAME_STRING);
}
w->EndXdataSection();
w->Newline();
// Emit a RUNTIME_FUNCTION (PDATA) entry for each builtin function, as
// documented here:
// https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64.
w->Comment(
"pdata for all the code in the embedded blob (structs of type "
"RUNTIME_FUNCTION).");
w->Comment(" BeginAddress");
w->Comment(" EndAddress");
w->Comment(" UnwindInfoAddress");
w->StartPdataSection();
{
static_assert(Builtins::kAllBuiltinsAreIsolateIndependent);
Address prev_builtin_end_offset = 0;
for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast;
++builtin) {
const int builtin_index = static_cast<int>(builtin);
// Some builtins are leaf functions from the point of view of Win64 stack
// walking: they do not move the stack pointer and do not require a PDATA
// entry because the return address can be retrieved from [rsp].
if (unwind_infos[builtin_index].is_leaf_function()) continue;
uint64_t builtin_start_offset = blob->InstructionStartOf(builtin) -
reinterpret_cast<Address>(blob->code());
uint32_t builtin_size = blob->InstructionSizeOf(builtin);
const std::vector<int>& xdata_desc =
unwind_infos[builtin_index].fp_offsets();
if (xdata_desc.empty()) {
// Some builtins do not have any "push rbp - mov rbp, rsp" instructions
// to start a stack frame. We still emit a PDATA entry as if they had,
// relying on the fact that we can find the previous frame address from
// rbp in most cases. Note that since the function does not really start
// with a 'push rbp' we need to specify the start RVA in the PDATA entry
// a few bytes before the beginning of the function, if it does not
// overlap the end of the previous builtin.
WriteUnwindInfoEntry(
w, unwind_info_symbol, embedded_blob_data_symbol,
std::max(prev_builtin_end_offset,
builtin_start_offset - win64_unwindinfo::kRbpPrefixLength),
builtin_start_offset + builtin_size);
} else {
// Some builtins have one or more "push rbp - mov rbp, rsp" sequences,
// but not necessarily at the beginning of the function. In this case
// we want to yield a PDATA entry for each block of instructions that
// emit an rbp frame. If the function does not start with 'push rbp'
// we also emit a PDATA entry for the initial block of code up to the
// first 'push rbp', like in the case above.
if (xdata_desc[0] > 0) {
WriteUnwindInfoEntry(w, unwind_info_symbol, embedded_blob_data_symbol,
std::max(prev_builtin_end_offset,
builtin_start_offset -
win64_unwindinfo::kRbpPrefixLength),
builtin_start_offset + xdata_desc[0]);
}
for (size_t j = 0; j < xdata_desc.size(); j++) {
int chunk_start = xdata_desc[j];
int chunk_end =
(j < xdata_desc.size() - 1) ? xdata_desc[j + 1] : builtin_size;
WriteUnwindInfoEntry(w, unwind_info_symbol, embedded_blob_data_symbol,
builtin_start_offset + chunk_start,
builtin_start_offset + chunk_end);
}
}
prev_builtin_end_offset = builtin_start_offset + builtin_size;
w->Newline();
}
}
w->EndPdataSection();
w->Newline();
}
#elif defined(V8_OS_WIN_ARM64)
void EmitUnwindData(PlatformEmbeddedFileWriterWin* w,
const char* unwind_info_symbol,
const char* embedded_blob_data_symbol,
const EmbeddedData* blob,
const win64_unwindinfo::BuiltinUnwindInfo* unwind_infos) {
DCHECK(win64_unwindinfo::CanEmitUnwindInfoForBuiltins());
// Fairly arbitrary but should fit all symbol names.
static constexpr int kTemporaryStringLength = 256;
base::EmbeddedVector<char, kTemporaryStringLength> unwind_info_full_symbol;
// Emit a RUNTIME_FUNCTION (PDATA) entry for each builtin function, as
// documented here:
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling.
w->Comment(
"pdata for all the code in the embedded blob (structs of type "
"RUNTIME_FUNCTION).");
w->Comment(" BeginAddress");
w->Comment(" UnwindInfoAddress");
w->StartPdataSection();
std::vector<int> code_chunks;
std::vector<win64_unwindinfo::FrameOffsets> fp_adjustments;
static_assert(Builtins::kAllBuiltinsAreIsolateIndependent);
for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast;
++builtin) {
const int builtin_index = static_cast<int>(builtin);
if (unwind_infos[builtin_index].is_leaf_function()) continue;
uint64_t builtin_start_offset = blob->InstructionStartOf(builtin) -
reinterpret_cast<Address>(blob->code());
uint32_t builtin_size = blob->InstructionSizeOf(builtin);
const std::vector<int>& xdata_desc =
unwind_infos[builtin_index].fp_offsets();
const std::vector<win64_unwindinfo::FrameOffsets>& xdata_fp_adjustments =
unwind_infos[builtin_index].fp_adjustments();
DCHECK_EQ(xdata_desc.size(), xdata_fp_adjustments.size());
for (size_t j = 0; j < xdata_desc.size(); j++) {
int chunk_start = xdata_desc[j];
int chunk_end =
(j < xdata_desc.size() - 1) ? xdata_desc[j + 1] : builtin_size;
int chunk_len = ::RoundUp(chunk_end - chunk_start, kInstrSize);
while (chunk_len > 0) {
int allowed_chunk_len =
std::min(chunk_len, win64_unwindinfo::kMaxFunctionLength);
chunk_len -= win64_unwindinfo::kMaxFunctionLength;
// Record the chunk length and fp_adjustment for emitting UNWIND_INFO
// later.
code_chunks.push_back(allowed_chunk_len);
fp_adjustments.push_back(xdata_fp_adjustments[j]);
base::SNPrintF(unwind_info_full_symbol, "%s_%u", unwind_info_symbol,
code_chunks.size());
w->DeclareRvaToSymbol(embedded_blob_data_symbol,
builtin_start_offset + chunk_start);
w->DeclareRvaToSymbol(unwind_info_full_symbol.begin());
}
}
}
w->EndPdataSection();
w->Newline();
// Emit an UNWIND_INFO (XDATA) structs, which contains the unwinding
// information.
w->DeclareExternalFunction(CRASH_HANDLER_FUNCTION_NAME_STRING);
w->StartXdataSection();
{
for (size_t i = 0; i < code_chunks.size(); i++) {
base::SNPrintF(unwind_info_full_symbol, "%s_%u", unwind_info_symbol,
i + 1);
w->DeclareLabel(unwind_info_full_symbol.begin());
std::vector<uint8_t> xdata =
win64_unwindinfo::GetUnwindInfoForBuiltinFunction(code_chunks[i],
fp_adjustments[i]);
w->IndentedDataDirective(kByte);
for (size_t j = 0; j < xdata.size(); j++) {
if (j > 0) fprintf(w->fp(), ",");
w->HexLiteral(xdata[j]);
}
w->Newline();
w->DeclareRvaToSymbol(CRASH_HANDLER_FUNCTION_NAME_STRING);
}
}
w->EndXdataSection();
w->Newline();
}
#endif // V8_OS_WIN_X64
} // namespace
const char* PlatformEmbeddedFileWriterWin::DirectiveAsString(
DataDirective directive) {
#if defined(V8_COMPILER_IS_MSVC)
if (target_arch_ != EmbeddedTargetArch::kArm64) {
switch (directive) {
case kByte:
return "BYTE";
case kLong:
return "DWORD";
case kQuad:
return "QWORD";
default:
UNREACHABLE();
}
} else {
switch (directive) {
case kByte:
return "DCB";
case kLong:
return "DCDU";
case kQuad:
return "DCQU";
default:
UNREACHABLE();
}
}
#else
switch (directive) {
case kByte:
return ".byte";
case kLong:
return ".long";
case kQuad:
return ".quad";
case kOcta:
return ".octa";
}
UNREACHABLE();
#endif
}
void PlatformEmbeddedFileWriterWin::MaybeEmitUnwindData(
const char* unwind_info_symbol, const char* embedded_blob_data_symbol,
const EmbeddedData* blob, const void* unwind_infos) {
// Windows ARM64 supports cross build which could require unwind info for
// host_os. Ignore this case because it is only used in build time.
#if defined(V8_OS_WIN_ARM64)
if (target_arch_ != EmbeddedTargetArch::kArm64) {
return;
}
#endif // V8_OS_WIN_ARM64
#if defined(V8_OS_WIN64)
if (win64_unwindinfo::CanEmitUnwindInfoForBuiltins()) {
EmitUnwindData(this, unwind_info_symbol, embedded_blob_data_symbol, blob,
reinterpret_cast<const win64_unwindinfo::BuiltinUnwindInfo*>(
unwind_infos));
}
#endif // V8_OS_WIN64
}
// Windows, MSVC
// -----------------------------------------------------------------------------
#if defined(V8_COMPILER_IS_MSVC)
// For x64 MSVC builds we emit assembly in MASM syntax.
// See https://docs.microsoft.com/en-us/cpp/assembler/masm/directives-reference.
// For Arm build, we emit assembly in MARMASM syntax.
// Note that the same mksnapshot has to be used to compile the host and target.
// The AARCH64 ABI requires instructions be 4-byte-aligned and Windows does
// not have a stricter alignment requirement (see the TEXTAREA macro of
// kxarm64.h in the Windows SDK), so code is 4-byte-aligned.
// The data fields in the emitted assembly tend to be accessed with 8-byte
// LDR instructions, so data is 8-byte-aligned.
//
// armasm64's warning A4228 states
// Alignment value exceeds AREA alignment; alignment not guaranteed
// To ensure that ALIGN directives are honored, their values are defined as
// equal to their corresponding AREA's ALIGN attributes.
#define ARM64_DATA_ALIGNMENT_POWER (3)
#define ARM64_DATA_ALIGNMENT (1 << ARM64_DATA_ALIGNMENT_POWER)
#define ARM64_CODE_ALIGNMENT_POWER (2)
#define ARM64_CODE_ALIGNMENT (1 << ARM64_CODE_ALIGNMENT_POWER)
void PlatformEmbeddedFileWriterWin::SectionText() {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " AREA |.text|, CODE, ALIGN=%d, READONLY\n",
ARM64_CODE_ALIGNMENT_POWER);
} else {
fprintf(fp_, ".CODE\n");
}
}
void PlatformEmbeddedFileWriterWin::SectionRoData() {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " AREA |.rodata|, DATA, ALIGN=%d, READONLY\n",
ARM64_DATA_ALIGNMENT_POWER);
} else {
fprintf(fp_, ".CONST\n");
}
}
void PlatformEmbeddedFileWriterWin::DeclareUint32(const char* name,
uint32_t value) {
DeclareSymbolGlobal(name);
fprintf(fp_, "%s%s %s %d\n", SYMBOL_PREFIX, name, DirectiveAsString(kLong),
value);
}
void PlatformEmbeddedFileWriterWin::StartPdataSection() {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " AREA |.pdata|, DATA, ALIGN=%d, READONLY\n",
ARM64_DATA_ALIGNMENT_POWER);
} else {
fprintf(fp_, "OPTION DOTNAME\n");
fprintf(fp_, ".pdata SEGMENT DWORD READ ''\n");
}
}
void PlatformEmbeddedFileWriterWin::EndPdataSection() {
if (target_arch_ != EmbeddedTargetArch::kArm64) {
fprintf(fp_, ".pdata ENDS\n");
}
}
void PlatformEmbeddedFileWriterWin::StartXdataSection() {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " AREA |.xdata|, DATA, ALIGN=%d, READONLY\n",
ARM64_DATA_ALIGNMENT_POWER);
} else {
fprintf(fp_, "OPTION DOTNAME\n");
fprintf(fp_, ".xdata SEGMENT DWORD READ ''\n");
}
}
void PlatformEmbeddedFileWriterWin::EndXdataSection() {
if (target_arch_ != EmbeddedTargetArch::kArm64) {
fprintf(fp_, ".xdata ENDS\n");
}
}
void PlatformEmbeddedFileWriterWin::DeclareExternalFunction(const char* name) {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " EXTERN %s \n", name);
} else {
fprintf(fp_, "EXTERN %s : PROC\n", name);
}
}
void PlatformEmbeddedFileWriterWin::DeclareRvaToSymbol(const char* name,
uint64_t offset) {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
if (offset > 0) {
fprintf(fp_, " DCD %s + %llu\n", name, offset);
} else {
fprintf(fp_, " DCD %s\n", name);
}
// The default relocation entry generated by MSVC armasm64.exe for DCD
// directive is IMAGE_REL_ARM64_ADDR64 which represents relocation for
// 64-bit pointer instead of 32-bit RVA. Append RELOC with
// IMAGE_REL_ARM64_ADDR32NB(2) to generate correct relocation entry for
// 32-bit RVA.
fprintf(fp_, " RELOC 2\n");
} else {
if (offset > 0) {
fprintf(fp_, "DD IMAGEREL %s+%llu\n", name, offset);
} else {
fprintf(fp_, "DD IMAGEREL %s\n", name);
}
}
}
void PlatformEmbeddedFileWriterWin::DeclareSymbolGlobal(const char* name) {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " EXPORT %s%s\n", SYMBOL_PREFIX, name);
} else {
fprintf(fp_, "PUBLIC %s%s\n", SYMBOL_PREFIX, name);
}
}
void PlatformEmbeddedFileWriterWin::AlignToCodeAlignment() {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " ALIGN %d\n", ARM64_CODE_ALIGNMENT);
} else {
// Diverges from other platforms due to compile error
// 'invalid combination with segment alignment'.
fprintf(fp_, "ALIGN 4\n");
}
}
void PlatformEmbeddedFileWriterWin::AlignToDataAlignment() {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " ALIGN %d\n", ARM64_DATA_ALIGNMENT);
} else {
fprintf(fp_, "ALIGN 4\n");
}
}
void PlatformEmbeddedFileWriterWin::Comment(const char* string) {
fprintf(fp_, "; %s\n", string);
}
void PlatformEmbeddedFileWriterWin::DeclareLabel(const char* name) {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, "%s%s\n", SYMBOL_PREFIX, name);
} else {
fprintf(fp_, "%s%s LABEL %s\n", SYMBOL_PREFIX, name,
DirectiveAsString(kByte));
}
}
void PlatformEmbeddedFileWriterWin::SourceInfo(int fileid, const char* filename,
int line) {
// TODO(mvstanton): output source information for MSVC.
// Its syntax is #line <line> "<filename>"
}
// TODO(mmarchini): investigate emitting size annotations for Windows
void PlatformEmbeddedFileWriterWin::DeclareFunctionBegin(const char* name,
uint32_t size) {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, "%s%s FUNCTION\n", SYMBOL_PREFIX, name);
} else {
fprintf(fp_, "%s%s PROC\n", SYMBOL_PREFIX, name);
}
}
void PlatformEmbeddedFileWriterWin::DeclareFunctionEnd(const char* name) {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " ENDFUNC\n");
} else {
fprintf(fp_, "%s%s ENDP\n", SYMBOL_PREFIX, name);
}
}
int PlatformEmbeddedFileWriterWin::HexLiteral(uint64_t value) {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
return fprintf(fp_, "0x%" PRIx64, value);
} else {
return fprintf(fp_, "0%" PRIx64 "h", value);
}
}
void PlatformEmbeddedFileWriterWin::FilePrologue() {
if (target_arch_ != EmbeddedTargetArch::kArm64 &&
target_arch_ != EmbeddedTargetArch::kX64) {
// x86 falls into this case
fprintf(fp_, ".MODEL FLAT\n");
}
}
void PlatformEmbeddedFileWriterWin::DeclareExternalFilename(
int fileid, const char* filename) {}
void PlatformEmbeddedFileWriterWin::FileEpilogue() {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " END\n");
} else {
fprintf(fp_, "END\n");
}
}
int PlatformEmbeddedFileWriterWin::IndentedDataDirective(
DataDirective directive) {
return fprintf(fp_, " %s ", DirectiveAsString(directive));
}
#undef ARM64_DATA_ALIGNMENT_POWER
#undef ARM64_DATA_ALIGNMENT
#undef ARM64_CODE_ALIGNMENT_POWER
#undef ARM64_CODE_ALIGNMENT
// All Windows builds without MSVC.
// -----------------------------------------------------------------------------
#else
// The directives for text section prefix come from the COFF
// (Common Object File Format) standards:
// https://llvm.org/docs/Extensions.html
//
// .text$hot means this section contains hot code.
// x means executable section.
// r means read-only section.
void PlatformEmbeddedFileWriterWin::SectionText() {
fprintf(fp_, ".section .text$hot,\"xr\"\n");
}
void PlatformEmbeddedFileWriterWin::SectionRoData() {
fprintf(fp_, ".section .rdata\n");
}
void PlatformEmbeddedFileWriterWin::DeclareUint32(const char* name,
uint32_t value) {
DeclareSymbolGlobal(name);
DeclareLabel(name);
IndentedDataDirective(kLong);
fprintf(fp_, "%d", value);
Newline();
}
void PlatformEmbeddedFileWriterWin::StartPdataSection() {
fprintf(fp_, ".section .pdata\n");
}
void PlatformEmbeddedFileWriterWin::EndPdataSection() {}
void PlatformEmbeddedFileWriterWin::StartXdataSection() {
fprintf(fp_, ".section .xdata\n");
}
void PlatformEmbeddedFileWriterWin::EndXdataSection() {}
void PlatformEmbeddedFileWriterWin::DeclareExternalFunction(const char* name) {}
void PlatformEmbeddedFileWriterWin::DeclareRvaToSymbol(const char* name,
uint64_t offset) {
if (offset > 0) {
fprintf(fp_, ".rva %s + %" PRIu64 "\n", name, offset);
} else {
fprintf(fp_, ".rva %s\n", name);
}
}
void PlatformEmbeddedFileWriterWin::DeclareSymbolGlobal(const char* name) {
fprintf(fp_, ".global %s%s\n", SYMBOL_PREFIX, name);
}
void PlatformEmbeddedFileWriterWin::AlignToCodeAlignment() {
#if V8_TARGET_ARCH_X64
// On x64 use 64-bytes code alignment to allow 64-bytes loop header alignment.
static_assert(64 >= kCodeAlignment);
fprintf(fp_, ".balign 64\n");
#elif V8_TARGET_ARCH_PPC64
// 64 byte alignment is needed on ppc64 to make sure p10 prefixed instructions
// don't cross 64-byte boundaries.
static_assert(64 >= kCodeAlignment);
fprintf(fp_, ".balign 64\n");
#else
static_assert(32 >= kCodeAlignment);
fprintf(fp_, ".balign 32\n");
#endif
}
void PlatformEmbeddedFileWriterWin::AlignToDataAlignment() {
// On Windows ARM64, s390, PPC and possibly more platforms, aligned load
// instructions are used to retrieve v8_Default_embedded_blob_ and/or
// v8_Default_embedded_blob_size_. The generated instructions require the
// load target to be aligned at 8 bytes (2^3).
fprintf(fp_, ".balign 8\n");
}
void PlatformEmbeddedFileWriterWin::Comment(const char* string) {
fprintf(fp_, "// %s\n", string);
}
void PlatformEmbeddedFileWriterWin::DeclareLabel(const char* name) {
fprintf(fp_, "%s%s:\n", SYMBOL_PREFIX, name);
}
void PlatformEmbeddedFileWriterWin::SourceInfo(int fileid, const char* filename,
int line) {
// BUG(9944): Use .cv_loc to ensure CodeView information is used on
// Windows.
}
// TODO(mmarchini): investigate emitting size annotations for Windows
void PlatformEmbeddedFileWriterWin::DeclareFunctionBegin(const char* name,
uint32_t size) {
DeclareLabel(name);
if (target_arch_ == EmbeddedTargetArch::kArm64
#if V8_ENABLE_DRUMBRAKE
|| IsDrumBrakeInstructionHandler(name)
#endif // V8_ENABLE_DRUMBRAKE
) {
// Windows ARM64 assembly is in GAS syntax, but ".type" is invalid directive
// in PE/COFF for Windows.
DeclareSymbolGlobal(name);
} else {
// The directives for inserting debugging information on Windows come
// from the PE (Portable Executable) and COFF (Common Object File Format)
// standards. Documented here:
// https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format
//
// .scl 2 means StorageClass external.
// .type 32 means Type Representation Function.
fprintf(fp_, ".def %s%s; .scl 2; .type 32; .endef;\n", SYMBOL_PREFIX, name);
}
}
void PlatformEmbeddedFileWriterWin::DeclareFunctionEnd(const char* name) {}
int PlatformEmbeddedFileWriterWin::HexLiteral(uint64_t value) {
return fprintf(fp_, "0x%" PRIx64, value);
}
void PlatformEmbeddedFileWriterWin::FilePrologue() {}
void PlatformEmbeddedFileWriterWin::DeclareExternalFilename(
int fileid, const char* filename) {
// BUG(9944): Use .cv_filename to ensure CodeView information is used on
// Windows.
}
void PlatformEmbeddedFileWriterWin::FileEpilogue() {}
int PlatformEmbeddedFileWriterWin::IndentedDataDirective(
DataDirective directive) {
return fprintf(fp_, " %s ", DirectiveAsString(directive));
}
#endif
DataDirective PlatformEmbeddedFileWriterWin::ByteChunkDataDirective() const {
#if defined(V8_COMPILER_IS_MSVC)
// Windows MASM doesn't have an .octa directive, use QWORDs instead.
// Note: MASM *really* does not like large data streams. It takes over 5
// minutes to assemble the ~350K lines of embedded.S produced when using
// BYTE directives in a debug build. QWORD produces roughly 120KLOC and
// reduces assembly time to ~40 seconds. Still terrible, but much better
// than before. See also: https://crbug.com/v8/8475.
return kQuad;
#else
return PlatformEmbeddedFileWriterBase::ByteChunkDataDirective();
#endif
}
int PlatformEmbeddedFileWriterWin::WriteByteChunk(const uint8_t* data) {
#if defined(V8_COMPILER_IS_MSVC)
DCHECK_EQ(ByteChunkDataDirective(), kQuad);
const uint64_t* quad_ptr = reinterpret_cast<const uint64_t*>(data);
return HexLiteral(*quad_ptr);
#else
return PlatformEmbeddedFileWriterBase::WriteByteChunk(data);
#endif
}
#undef SYMBOL_PREFIX
#undef V8_ASSEMBLER_IS_MASM
#undef V8_ASSEMBLER_IS_MARMASM
#undef V8_COMPILER_IS_MSVC
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,77 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_WIN_H_
#define V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_WIN_H_
#include "src/base/macros.h"
#include "src/snapshot/embedded/platform-embedded-file-writer-base.h"
namespace v8 {
namespace internal {
class PlatformEmbeddedFileWriterWin : public PlatformEmbeddedFileWriterBase {
public:
PlatformEmbeddedFileWriterWin(EmbeddedTargetArch target_arch,
EmbeddedTargetOs target_os)
: target_arch_(target_arch), target_os_(target_os) {
USE(target_os_);
DCHECK_EQ(target_os_, EmbeddedTargetOs::kWin);
}
void SectionText() override;
void SectionRoData() override;
void AlignToCodeAlignment() override;
void AlignToDataAlignment() override;
void DeclareUint32(const char* name, uint32_t value) override;
void DeclareSymbolGlobal(const char* name) override;
void DeclareLabel(const char* name) override;
void SourceInfo(int fileid, const char* filename, int line) override;
void DeclareFunctionBegin(const char* name, uint32_t size) override;
void DeclareFunctionEnd(const char* name) override;
int HexLiteral(uint64_t value) override;
void Comment(const char* string) override;
void FilePrologue() override;
void DeclareExternalFilename(int fileid, const char* filename) override;
void FileEpilogue() override;
int IndentedDataDirective(DataDirective directive) override;
DataDirective ByteChunkDataDirective() const override;
int WriteByteChunk(const uint8_t* data) override;
void StartPdataSection();
void EndPdataSection();
void StartXdataSection();
void EndXdataSection();
void DeclareExternalFunction(const char* name);
// Emits an RVA (address relative to the module load address) specified as an
// offset from a given symbol.
void DeclareRvaToSymbol(const char* name, uint64_t offset = 0);
void MaybeEmitUnwindData(const char* unwind_info_symbol,
const char* embedded_blob_data_symbol,
const EmbeddedData* blob,
const void* unwind_infos) override;
private:
const char* DirectiveAsString(DataDirective directive);
private:
const EmbeddedTargetArch target_arch_;
const EmbeddedTargetOs target_os_;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_WIN_H_

View File

@ -0,0 +1,174 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/embedded/platform-embedded-file-writer-zos.h"
#include <stdarg.h>
#include <string>
namespace v8 {
namespace internal {
// https://www.ibm.com/docs/en/zos/2.1.0?topic=conventions-continuation-lines
// for length of HLASM statements and continuation.
static constexpr int kAsmMaxLineLen = 71;
static constexpr int kAsmContIndentLen = 15;
static constexpr int kAsmContMaxLen = kAsmMaxLineLen - kAsmContIndentLen;
namespace {
int hlasmPrintLine(FILE* fp, const char* fmt, ...) {
int ret;
char buffer[4096];
int offset = 0;
static char indent[kAsmContIndentLen] = "";
va_list ap;
va_start(ap, fmt);
ret = vsnprintf(buffer, sizeof(buffer), fmt, ap);
va_end(ap);
if (!*indent) memset(indent, ' ', sizeof(indent));
if (ret > kAsmMaxLineLen && buffer[kAsmMaxLineLen] != '\n') {
offset += fwrite(buffer + offset, 1, kAsmMaxLineLen, fp);
// Write continuation mark
fwrite("-\n", 1, 2, fp);
ret -= kAsmMaxLineLen;
while (ret > kAsmContMaxLen) {
// indent by kAsmContIndentLen
fwrite(indent, 1, kAsmContIndentLen, fp);
offset += fwrite(buffer + offset, 1, kAsmContMaxLen, fp);
// write continuation mark
fwrite("-\n", 1, 2, fp);
ret -= kAsmContMaxLen;
}
if (ret > 0) {
// indent kAsmContIndentLen blanks
fwrite(indent, 1, kAsmContIndentLen, fp);
offset += fwrite(buffer + offset, 1, ret, fp);
}
} else {
offset += fwrite(buffer + offset, 1, ret, fp);
}
return ret;
}
} // namespace
void PlatformEmbeddedFileWriterZOS::DeclareLabelProlog(const char* name) {
fprintf(fp_,
"&suffix SETA &suffix+1\n"
"CEECWSA LOCTR\n"
"AL&suffix ALIAS C'%s'\n"
"C_WSA64 CATTR DEFLOAD,RMODE(64),PART(AL&suffix)\n"
"AL&suffix XATTR REF(DATA),LINKAGE(XPLINK),SCOPE(EXPORT)\n",
name);
}
void PlatformEmbeddedFileWriterZOS::DeclareLabelEpilogue() {
fprintf(fp_,
"C_WSA64 CATTR PART(PART1)\n"
"LBL&suffix DC AD(AL&suffix)\n");
}
void PlatformEmbeddedFileWriterZOS::DeclareUint32(const char* name,
uint32_t value) {
DeclareSymbolGlobal(name);
fprintf(fp_,
"&suffix SETA &suffix+1\n"
"CEECWSA LOCTR\n"
"AL&suffix ALIAS C'%s'\n"
"C_WSA64 CATTR DEFLOAD,RMODE(64),PART(AL&suffix)\n"
"AL&suffix XATTR REF(DATA),LINKAGE(XPLINK),SCOPE(EXPORT)\n"
" DC F'%d'\n"
"C_WSA64 CATTR PART(PART1)\n"
"LBL&suffix DC AD(AL&suffix)\n",
name, value);
}
void PlatformEmbeddedFileWriterZOS::DeclareSymbolGlobal(const char* name) {
hlasmPrintLine(fp_, "* Global Symbol %s\n", name);
}
void PlatformEmbeddedFileWriterZOS::AlignToCodeAlignment() {
// No code alignment required.
}
void PlatformEmbeddedFileWriterZOS::AlignToDataAlignment() {
// No data alignment required.
}
void PlatformEmbeddedFileWriterZOS::Comment(const char* string) {
hlasmPrintLine(fp_, "* %s\n", string);
}
void PlatformEmbeddedFileWriterZOS::DeclareLabel(const char* name) {
hlasmPrintLine(fp_, "*--------------------------------------------\n");
hlasmPrintLine(fp_, "* Label %s\n", name);
hlasmPrintLine(fp_, "*--------------------------------------------\n");
hlasmPrintLine(fp_, "%s DS 0H\n", name);
}
void PlatformEmbeddedFileWriterZOS::SourceInfo(int fileid, const char* filename,
int line) {
hlasmPrintLine(fp_, "* line %d \"%s\"\n", line, filename);
}
void PlatformEmbeddedFileWriterZOS::DeclareFunctionBegin(const char* name,
uint32_t size) {
hlasmPrintLine(fp_, "*--------------------------------------------\n");
hlasmPrintLine(fp_, "* Builtin %s\n", name);
hlasmPrintLine(fp_, "*--------------------------------------------\n");
hlasmPrintLine(fp_, "%s DS 0H\n", name);
}
void PlatformEmbeddedFileWriterZOS::DeclareFunctionEnd(const char* name) {
// Not used.
}
int PlatformEmbeddedFileWriterZOS::HexLiteral(uint64_t value) {
// The cast is because some platforms define uint64_t as unsigned long long,
// while others (e.g. z/OS) define it as unsigned long.
return fprintf(fp_, "%.16lx", static_cast<unsigned long>(value));
}
void PlatformEmbeddedFileWriterZOS::FilePrologue() {
fprintf(fp_,
"&C SETC 'embed'\n"
" SYSSTATE AMODE64=YES\n"
"&C csect\n"
"&C amode 64\n"
"&C rmode 64\n");
}
void PlatformEmbeddedFileWriterZOS::DeclareExternalFilename(
int fileid, const char* filename) {
// Not used.
}
void PlatformEmbeddedFileWriterZOS::FileEpilogue() { fprintf(fp_, " end\n"); }
int PlatformEmbeddedFileWriterZOS::IndentedDataDirective(
DataDirective directive) {
// Not used.
return 0;
}
DataDirective PlatformEmbeddedFileWriterZOS::ByteChunkDataDirective() const {
return kQuad;
}
int PlatformEmbeddedFileWriterZOS::WriteByteChunk(const uint8_t* data) {
DCHECK_EQ(ByteChunkDataDirective(), kQuad);
const uint64_t* quad_ptr = reinterpret_cast<const uint64_t*>(data);
return HexLiteral(*quad_ptr);
}
void PlatformEmbeddedFileWriterZOS::SectionText() {
// Not used.
}
void PlatformEmbeddedFileWriterZOS::SectionRoData() {
// Not used.
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,62 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_ZOS_H_
#define V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_ZOS_H_
#include "src/base/macros.h"
#include "src/snapshot/embedded/platform-embedded-file-writer-base.h"
namespace v8 {
namespace internal {
class PlatformEmbeddedFileWriterZOS : public PlatformEmbeddedFileWriterBase {
public:
PlatformEmbeddedFileWriterZOS(EmbeddedTargetArch target_arch,
EmbeddedTargetOs target_os)
: target_arch_(target_arch), target_os_(target_os) {
USE(target_arch_);
USE(target_os_);
DCHECK_EQ(target_os_, EmbeddedTargetOs::kZOS);
}
void SectionText() override;
void SectionRoData() override;
void AlignToCodeAlignment() override;
void AlignToDataAlignment() override;
void DeclareUint32(const char* name, uint32_t value) override;
void DeclareLabel(const char* name) override;
void DeclareLabelProlog(const char* name) override;
void DeclareLabelEpilogue() override;
void SourceInfo(int fileid, const char* filename, int line) override;
void DeclareFunctionBegin(const char* name, uint32_t size) override;
void DeclareFunctionEnd(const char* name) override;
int HexLiteral(uint64_t value) override;
void Comment(const char* string) override;
void FilePrologue() override;
void DeclareExternalFilename(int fileid, const char* filename) override;
void FileEpilogue() override;
int IndentedDataDirective(DataDirective directive) override;
DataDirective ByteChunkDataDirective() const override;
int WriteByteChunk(const uint8_t* data) override;
private:
void DeclareSymbolGlobal(const char* name) override;
private:
const EmbeddedTargetArch target_arch_;
const EmbeddedTargetOs target_os_;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_ZOS_H_

330
deps/v8/src/snapshot/mksnapshot.cc vendored Normal file
View File

@ -0,0 +1,330 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <iomanip>
#include "include/libplatform/libplatform.h"
#include "include/v8-initialization.h"
#include "src/base/platform/elapsed-timer.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/wrappers.h"
#include "src/base/vector.h"
#include "src/codegen/cpu-features.h"
#include "src/common/globals.h"
#include "src/flags/flags.h"
#include "src/snapshot/embedded/embedded-file-writer.h"
#include "src/snapshot/snapshot.h"
#include "src/snapshot/static-roots-gen.h"
namespace {
class SnapshotFileWriter {
public:
void SetSnapshotFile(const char* snapshot_cpp_file) {
snapshot_cpp_path_ = snapshot_cpp_file;
}
void SetStartupBlobFile(const char* snapshot_blob_file) {
snapshot_blob_path_ = snapshot_blob_file;
}
void WriteSnapshot(v8::StartupData blob) const {
// TODO(crbug/633159): if we crash before the files have been fully created,
// we end up with a corrupted snapshot file. The build step would succeed,
// but the build target is unusable. Ideally we would write out temporary
// files and only move them to the final destination as last step.
v8::base::Vector<const uint8_t> blob_vector(
reinterpret_cast<const uint8_t*>(blob.data), blob.raw_size);
MaybeWriteSnapshotFile(blob_vector);
MaybeWriteStartupBlob(blob_vector);
}
private:
void MaybeWriteStartupBlob(v8::base::Vector<const uint8_t> blob) const {
if (!snapshot_blob_path_) return;
FILE* fp = GetFileDescriptorOrDie(snapshot_blob_path_);
size_t written = fwrite(blob.begin(), 1, blob.length(), fp);
v8::base::Fclose(fp);
if (written != static_cast<size_t>(blob.length())) {
i::PrintF("Writing snapshot file failed.. Aborting.\n");
remove(snapshot_blob_path_);
exit(1);
}
}
void MaybeWriteSnapshotFile(v8::base::Vector<const uint8_t> blob) const {
if (!snapshot_cpp_path_) return;
FILE* fp = GetFileDescriptorOrDie(snapshot_cpp_path_);
WriteSnapshotFilePrefix(fp);
WriteSnapshotFileData(fp, blob);
WriteSnapshotFileSuffix(fp);
v8::base::Fclose(fp);
}
static void WriteSnapshotFilePrefix(FILE* fp) {
fprintf(fp, "// Autogenerated snapshot file. Do not edit.\n\n");
fprintf(fp, "#include \"src/init/v8.h\"\n");
fprintf(fp, "#include \"src/base/platform/platform.h\"\n\n");
fprintf(fp, "#include \"src/flags/flags.h\"\n");
fprintf(fp, "#include \"src/snapshot/snapshot.h\"\n\n");
fprintf(fp, "namespace v8 {\n");
fprintf(fp, "namespace internal {\n\n");
}
static void WriteSnapshotFileSuffix(FILE* fp) {
fprintf(fp, "const v8::StartupData* Snapshot::DefaultSnapshotBlob() {\n");
fprintf(fp, " return &blob;\n");
fprintf(fp, "}\n");
fprintf(fp, "\n");
fprintf(
fp,
"bool Snapshot::ShouldVerifyChecksum(const v8::StartupData* data) {\n");
fprintf(fp, " return v8_flags.verify_snapshot_checksum;\n");
fprintf(fp, "}\n");
fprintf(fp, "} // namespace internal\n");
fprintf(fp, "} // namespace v8\n");
}
static void WriteSnapshotFileData(FILE* fp,
v8::base::Vector<const uint8_t> blob) {
fprintf(
fp,
"alignas(kPointerAlignment) static const uint8_t blob_data[] = {\n");
WriteBinaryContentsAsCArray(fp, blob);
fprintf(fp, "};\n");
fprintf(fp, "static const int blob_size = %d;\n", blob.length());
fprintf(fp, "static const v8::StartupData blob =\n");
fprintf(fp, "{ (const char*) blob_data, blob_size };\n");
}
static void WriteBinaryContentsAsCArray(
FILE* fp, v8::base::Vector<const uint8_t> blob) {
for (int i = 0; i < blob.length(); i++) {
if ((i & 0x1F) == 0x1F) fprintf(fp, "\n");
if (i > 0) fprintf(fp, ",");
fprintf(fp, "%u", static_cast<unsigned char>(blob.at(i)));
}
fprintf(fp, "\n");
}
static FILE* GetFileDescriptorOrDie(const char* filename) {
FILE* fp = v8::base::OS::FOpen(filename, "wb");
if (fp == nullptr) {
i::PrintF("Unable to open file \"%s\" for writing.\n", filename);
exit(1);
}
return fp;
}
const char* snapshot_cpp_path_ = nullptr;
const char* snapshot_blob_path_ = nullptr;
};
std::unique_ptr<char[]> GetExtraCode(char* filename, const char* description) {
if (filename == nullptr || strlen(filename) == 0) return nullptr;
::printf("Loading script for %s: %s\n", description, filename);
FILE* file = v8::base::OS::FOpen(filename, "rb");
if (file == nullptr) {
fprintf(stderr, "Failed to open '%s': errno %d\n", filename, errno);
exit(1);
}
fseek(file, 0, SEEK_END);
size_t size = ftell(file);
rewind(file);
char* chars = new char[size + 1];
chars[size] = '\0';
for (size_t i = 0; i < size;) {
size_t read = fread(&chars[i], 1, size - i, file);
if (ferror(file)) {
fprintf(stderr, "Failed to read '%s': errno %d\n", filename, errno);
exit(1);
}
i += read;
}
v8::base::Fclose(file);
return std::unique_ptr<char[]>(chars);
}
v8::StartupData CreateSnapshotDataBlob(v8::SnapshotCreator& snapshot_creator,
const char* embedded_source) {
v8::base::ElapsedTimer timer;
timer.Start();
v8::StartupData result = i::CreateSnapshotDataBlobInternal(
v8::SnapshotCreator::FunctionCodeHandling::kClear, embedded_source,
snapshot_creator);
if (i::v8_flags.profile_deserialization) {
i::PrintF("[Creating snapshot took %0.3f ms]\n",
timer.Elapsed().InMillisecondsF());
}
timer.Stop();
return result;
}
v8::StartupData WarmUpSnapshotDataBlob(v8::StartupData cold_snapshot_blob,
const char* warmup_source) {
v8::base::ElapsedTimer timer;
timer.Start();
v8::StartupData result =
i::WarmUpSnapshotDataBlobInternal(cold_snapshot_blob, warmup_source);
if (i::v8_flags.profile_deserialization) {
i::PrintF("Warming up snapshot took %0.3f ms\n",
timer.Elapsed().InMillisecondsF());
}
timer.Stop();
return result;
}
void WriteEmbeddedFile(i::EmbeddedFileWriter* writer) {
i::EmbeddedData embedded_blob = i::EmbeddedData::FromBlob();
writer->WriteEmbedded(&embedded_blob);
}
using CounterMap = std::map<std::string, int>;
CounterMap* counter_map_ = nullptr;
void MaybeSetCounterFunction(v8::Isolate* isolate) {
// If --native-code-counters is on then we enable all counters to make
// sure we generate code to increment them from the snapshot.
//
// Note: For the sake of the mksnapshot, the counter function must only
// return distinct addresses for each counter s.t. the serializer can properly
// distinguish between them. In theory it should be okay to just return an
// incremented int value each time this function is called, but we play it
// safe and return a real distinct memory location tied to every counter name.
if (i::v8_flags.native_code_counters) {
counter_map_ = new CounterMap();
isolate->SetCounterFunction([](const char* name) -> int* {
auto map_entry = counter_map_->find(name);
if (map_entry == counter_map_->end()) {
counter_map_->emplace(name, 0);
}
return &counter_map_->at(name);
});
}
}
} // namespace
int main(int argc, char** argv) {
v8::base::EnsureConsoleOutput();
// Make mksnapshot runs predictable to create reproducible snapshots.
i::v8_flags.predictable = true;
// Disable ICs globally in mksnapshot to avoid problems with Code handlers.
// See https://crbug.com/345280736.
// TODO(jgruber): Re-enable once a better fix is available.
i::v8_flags.use_ic = false;
// Print the usage if an error occurs when parsing the command line
// flags or if the help flag is set.
using HelpOptions = i::FlagList::HelpOptions;
std::string usage = "Usage: " + std::string(argv[0]) +
" [--startup-src=file]" + " [--startup-blob=file]" +
" [--embedded-src=file]" + " [--embedded-variant=label]" +
" [--static-roots-src=file]" + " [--target-arch=arch]" +
" [--target-os=os] [extras]\n\n";
int result = i::FlagList::SetFlagsFromCommandLine(
&argc, argv, true, HelpOptions(HelpOptions::kExit, usage.c_str()));
if (result > 0 || (argc > 3)) {
i::PrintF(stdout, "%s", usage.c_str());
return result;
}
i::CpuFeatures::Probe(true);
v8::V8::InitializeICUDefaultLocation(argv[0]);
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
{
SnapshotFileWriter snapshot_writer;
snapshot_writer.SetSnapshotFile(i::v8_flags.startup_src);
snapshot_writer.SetStartupBlobFile(i::v8_flags.startup_blob);
i::EmbeddedFileWriter embedded_writer;
embedded_writer.SetEmbeddedFile(i::v8_flags.embedded_src);
embedded_writer.SetEmbeddedVariant(i::v8_flags.embedded_variant);
embedded_writer.SetTargetArch(i::v8_flags.target_arch);
embedded_writer.SetTargetOs(i::v8_flags.target_os);
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;
{
v8::Isolate* isolate = v8::Isolate::Allocate();
MaybeSetCounterFunction(isolate);
// The isolate contains data from builtin compilation that needs
// to be written out if builtins are embedded.
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i_isolate->RegisterEmbeddedFileWriter(&embedded_writer);
std::unique_ptr<v8::ArrayBuffer::Allocator> array_buffer_allocator(
v8::ArrayBuffer::Allocator::NewDefaultAllocator());
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = array_buffer_allocator.get();
// Set code range such that relative jumps for builtins to
// builtin calls in the snapshot are possible.
size_t code_range_size_mb =
i::kMaximalCodeRangeSize == 0
? i::kMaxPCRelativeCodeRangeInMB
: std::min(i::kMaximalCodeRangeSize / i::MB,
i::kMaxPCRelativeCodeRangeInMB);
create_params.constraints.set_code_range_size_in_bytes(
code_range_size_mb * i::MB);
{
v8::SnapshotCreator creator(isolate, create_params);
blob = CreateSnapshotDataBlob(creator, embed_script.get());
WriteEmbeddedFile(&embedded_writer);
#if V8_STATIC_ROOTS_GENERATION_BOOL
if (i::v8_flags.static_roots_src) {
i::StaticRootsTableGen::write(i_isolate,
i::v8_flags.static_roots_src);
}
#endif
}
isolate->Dispose();
}
if (warmup_script) {
v8::StartupData cold = blob;
blob = WarmUpSnapshotDataBlob(cold, warmup_script.get());
delete[] cold.data;
}
delete counter_map_;
CHECK(blob.data);
snapshot_writer.WriteSnapshot(blob);
delete[] blob.data;
}
v8::V8::Dispose();
v8::V8::DisposePlatform();
return 0;
}

View File

@ -0,0 +1,137 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/object-deserializer.h"
#include "src/execution/isolate.h"
#include "src/heap/heap-inl.h"
#include "src/heap/local-factory-inl.h"
#include "src/objects/allocation-site-inl.h"
#include "src/objects/objects.h"
#include "src/snapshot/code-serializer.h"
namespace v8 {
namespace internal {
ObjectDeserializer::ObjectDeserializer(Isolate* isolate,
const SerializedCodeData* data)
: Deserializer(isolate, data->Payload(), data->GetMagicNumber(), true,
false) {}
MaybeDirectHandle<SharedFunctionInfo>
ObjectDeserializer::DeserializeSharedFunctionInfo(
Isolate* isolate, const SerializedCodeData* data,
DirectHandle<String> source) {
ObjectDeserializer d(isolate, data);
d.AddAttachedObject(source);
DirectHandle<HeapObject> result;
return d.Deserialize().ToHandle(&result)
? Cast<SharedFunctionInfo>(result)
: MaybeDirectHandle<SharedFunctionInfo>();
}
MaybeDirectHandle<HeapObject> ObjectDeserializer::Deserialize() {
DCHECK(deserializing_user_code());
HandleScope scope(isolate());
DirectHandle<HeapObject> result;
{
result = ReadObject();
DeserializeDeferredObjects();
CHECK(new_code_objects().empty());
LinkAllocationSites();
CHECK(new_maps().empty());
WeakenDescriptorArrays();
}
Rehash();
CommitPostProcessedObjects();
return scope.CloseAndEscape(result);
}
void ObjectDeserializer::CommitPostProcessedObjects() {
for (DirectHandle<Script> script : new_scripts()) {
// Assign a new script id to avoid collision.
script->set_id(isolate()->GetNextScriptId());
LogScriptEvents(*script);
// Add script to list.
Handle<WeakArrayList> list = isolate()->factory()->script_list();
list = WeakArrayList::AddToEnd(isolate(), list,
MaybeObjectDirectHandle::Weak(script));
isolate()->heap()->SetRootScriptList(*list);
}
}
void ObjectDeserializer::LinkAllocationSites() {
DisallowGarbageCollection no_gc;
Heap* heap = isolate()->heap();
// Allocation sites are present in the snapshot, and must be linked into
// a list at deserialization time.
for (DirectHandle<AllocationSite> site : new_allocation_sites()) {
if (!site->HasWeakNext()) continue;
// TODO(mvstanton): consider treating the heap()->allocation_sites_list()
// as a (weak) root. If this root is relocated correctly, this becomes
// unnecessary.
if (heap->allocation_sites_list() == Smi::zero()) {
site->set_weak_next(ReadOnlyRoots(heap).undefined_value());
} else {
site->set_weak_next(heap->allocation_sites_list());
}
heap->set_allocation_sites_list(*site);
}
}
OffThreadObjectDeserializer::OffThreadObjectDeserializer(
LocalIsolate* isolate, const SerializedCodeData* data)
: Deserializer(isolate, data->Payload(), data->GetMagicNumber(), true,
false) {}
MaybeDirectHandle<SharedFunctionInfo>
OffThreadObjectDeserializer::DeserializeSharedFunctionInfo(
LocalIsolate* isolate, const SerializedCodeData* data,
std::vector<IndirectHandle<Script>>* deserialized_scripts) {
OffThreadObjectDeserializer d(isolate, data);
// Attach the empty string as the source.
d.AddAttachedObject(isolate->factory()->empty_string());
DirectHandle<HeapObject> result;
if (!d.Deserialize(deserialized_scripts).ToHandle(&result)) {
return MaybeDirectHandle<SharedFunctionInfo>();
}
return Cast<SharedFunctionInfo>(result);
}
MaybeDirectHandle<HeapObject> OffThreadObjectDeserializer::Deserialize(
std::vector<IndirectHandle<Script>>* deserialized_scripts) {
DCHECK(deserializing_user_code());
LocalHandleScope scope(isolate());
DirectHandle<HeapObject> result;
{
result = ReadObject();
DeserializeDeferredObjects();
CHECK(new_code_objects().empty());
CHECK(new_allocation_sites().empty());
CHECK(new_maps().empty());
WeakenDescriptorArrays();
}
Rehash();
// TODO(leszeks): Figure out a better way of dealing with scripts.
CHECK_EQ(new_scripts().size(), 1);
for (DirectHandle<Script> script : new_scripts()) {
// Assign a new script id to avoid collision.
script->set_id(isolate()->GetNextScriptId());
LogScriptEvents(*script);
deserialized_scripts->push_back(
isolate()->heap()->NewPersistentHandle(script));
}
return scope.CloseAndEscape(result);
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,52 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_OBJECT_DESERIALIZER_H_
#define V8_SNAPSHOT_OBJECT_DESERIALIZER_H_
#include "src/snapshot/deserializer.h"
namespace v8 {
namespace internal {
class SerializedCodeData;
class SharedFunctionInfo;
// Deserializes the object graph rooted at a given object.
class ObjectDeserializer final : public Deserializer<Isolate> {
public:
static MaybeDirectHandle<SharedFunctionInfo> DeserializeSharedFunctionInfo(
Isolate* isolate, const SerializedCodeData* data,
DirectHandle<String> source);
private:
explicit ObjectDeserializer(Isolate* isolate, const SerializedCodeData* data);
// Deserialize an object graph. Fail gracefully.
MaybeDirectHandle<HeapObject> Deserialize();
void LinkAllocationSites();
void CommitPostProcessedObjects();
};
// Deserializes the object graph rooted at a given object.
class OffThreadObjectDeserializer final : public Deserializer<LocalIsolate> {
public:
static MaybeDirectHandle<SharedFunctionInfo> DeserializeSharedFunctionInfo(
LocalIsolate* isolate, const SerializedCodeData* data,
std::vector<IndirectHandle<Script>>* deserialized_scripts);
private:
explicit OffThreadObjectDeserializer(LocalIsolate* isolate,
const SerializedCodeData* data);
// Deserialize an object graph. Fail gracefully.
MaybeDirectHandle<HeapObject> Deserialize(
std::vector<IndirectHandle<Script>>* deserialized_scripts);
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_OBJECT_DESERIALIZER_H_

View File

@ -0,0 +1,349 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/read-only-deserializer.h"
#include "src/handles/handles-inl.h"
#include "src/heap/heap-inl.h"
#include "src/heap/read-only-heap.h"
#include "src/logging/counters-scopes.h"
#include "src/objects/objects-inl.h"
#include "src/objects/slots.h"
#include "src/snapshot/embedded/embedded-data-inl.h"
#include "src/snapshot/read-only-serializer-deserializer.h"
#include "src/snapshot/snapshot-data.h"
namespace v8 {
namespace internal {
class ReadOnlyHeapImageDeserializer final {
public:
static void Deserialize(Isolate* isolate, SnapshotByteSource* source) {
ReadOnlyHeapImageDeserializer{isolate, source}.DeserializeImpl();
}
private:
using Bytecode = ro::Bytecode;
ReadOnlyHeapImageDeserializer(Isolate* isolate, SnapshotByteSource* source)
: source_(source), isolate_(isolate) {}
void DeserializeImpl() {
while (true) {
int bytecode_as_int = source_->Get();
DCHECK_LT(bytecode_as_int, ro::kNumberOfBytecodes);
switch (static_cast<Bytecode>(bytecode_as_int)) {
case Bytecode::kAllocatePage:
AllocatePage(false);
break;
case Bytecode::kAllocatePageAt:
AllocatePage(true);
break;
case Bytecode::kSegment:
DeserializeSegment();
break;
case Bytecode::kRelocateSegment:
UNREACHABLE(); // Handled together with kSegment.
case Bytecode::kReadOnlyRootsTable:
DeserializeReadOnlyRootsTable();
break;
case Bytecode::kFinalizeReadOnlySpace:
ro_space()->FinalizeSpaceForDeserialization();
return;
}
}
}
void AllocatePage(bool fixed_offset) {
CHECK_EQ(V8_STATIC_ROOTS_BOOL, fixed_offset);
size_t expected_page_index = static_cast<size_t>(source_->GetUint30());
size_t actual_page_index = static_cast<size_t>(-1);
size_t area_size_in_bytes = static_cast<size_t>(source_->GetUint30());
if (fixed_offset) {
#ifdef V8_COMPRESS_POINTERS
uint32_t compressed_page_addr = source_->GetUint32();
Address pos = isolate_->cage_base() + compressed_page_addr;
actual_page_index = ro_space()->AllocateNextPageAt(pos);
#else
UNREACHABLE();
#endif // V8_COMPRESS_POINTERS
} else {
actual_page_index = ro_space()->AllocateNextPage();
}
CHECK_EQ(actual_page_index, expected_page_index);
ro_space()->InitializePageForDeserialization(PageAt(actual_page_index),
area_size_in_bytes);
}
void DeserializeSegment() {
uint32_t page_index = source_->GetUint30();
ReadOnlyPageMetadata* page = PageAt(page_index);
// Copy over raw contents.
Address start = page->area_start() + source_->GetUint30();
int size_in_bytes = source_->GetUint30();
CHECK_LE(start + size_in_bytes, page->area_end());
source_->CopyRaw(reinterpret_cast<void*>(start), size_in_bytes);
if (!V8_STATIC_ROOTS_BOOL) {
uint8_t relocate_marker_bytecode = source_->Get();
CHECK_EQ(relocate_marker_bytecode, Bytecode::kRelocateSegment);
int tagged_slots_size_in_bits = size_in_bytes / kTaggedSize;
// The const_cast is unfortunate, but we promise not to mutate data.
uint8_t* data =
const_cast<uint8_t*>(source_->data() + source_->position());
ro::BitSet tagged_slots(data, tagged_slots_size_in_bits);
DecodeTaggedSlots(start, tagged_slots);
source_->Advance(static_cast<int>(tagged_slots.size_in_bytes()));
}
}
Address Decode(ro::EncodedTagged encoded) const {
ReadOnlyPageMetadata* page = PageAt(encoded.page_index);
return page->OffsetToAddress(encoded.offset * kTaggedSize);
}
void DecodeTaggedSlots(Address segment_start,
const ro::BitSet& tagged_slots) {
DCHECK(!V8_STATIC_ROOTS_BOOL);
for (size_t i = 0; i < tagged_slots.size_in_bits(); i++) {
// TODO(jgruber): Depending on sparseness, different iteration methods
// could be more efficient.
if (!tagged_slots.contains(static_cast<int>(i))) continue;
Address slot_addr = segment_start + i * kTaggedSize;
Address obj_addr = Decode(ro::EncodedTagged::FromAddress(slot_addr));
Address obj_ptr = obj_addr + kHeapObjectTag;
Tagged_t* dst = reinterpret_cast<Tagged_t*>(slot_addr);
*dst = COMPRESS_POINTERS_BOOL
? V8HeapCompressionScheme::CompressObject(obj_ptr)
: static_cast<Tagged_t>(obj_ptr);
}
}
ReadOnlyPageMetadata* PageAt(size_t index) const {
DCHECK_LT(index, ro_space()->pages().size());
return ro_space()->pages()[index];
}
void DeserializeReadOnlyRootsTable() {
ReadOnlyRoots roots(isolate_);
if (V8_STATIC_ROOTS_BOOL) {
roots.InitFromStaticRootsTable(isolate_->cage_base());
} else {
for (size_t i = 0; i < ReadOnlyRoots::kEntriesCount; i++) {
uint32_t encoded_as_int = source_->GetUint32();
Address rudolf = Decode(ro::EncodedTagged::FromUint32(encoded_as_int));
roots.read_only_roots_[i] = rudolf + kHeapObjectTag;
}
}
}
ReadOnlySpace* ro_space() const {
return isolate_->read_only_heap()->read_only_space();
}
SnapshotByteSource* const source_;
Isolate* const isolate_;
};
ReadOnlyDeserializer::ReadOnlyDeserializer(Isolate* isolate,
const SnapshotData* data,
bool can_rehash)
: Deserializer(isolate, data->Payload(), data->GetMagicNumber(), false,
can_rehash) {}
void ReadOnlyDeserializer::DeserializeIntoIsolate() {
base::ElapsedTimer timer;
if (V8_UNLIKELY(v8_flags.profile_deserialization)) timer.Start();
NestedTimedHistogramScope histogram_timer(
isolate()->counters()->snapshot_deserialize_rospace());
HandleScope scope(isolate());
ReadOnlyHeapImageDeserializer::Deserialize(isolate(), source());
ReadOnlyHeap* ro_heap = isolate()->read_only_heap();
ro_heap->read_only_space()->RepairFreeSpacesAfterDeserialization();
PostProcessNewObjects();
ReadOnlyRoots roots(isolate());
roots.VerifyNameForProtectorsPages();
#ifdef DEBUG
roots.VerifyTypes();
roots.VerifyNameForProtectors();
#endif
if (should_rehash()) {
isolate()->heap()->InitializeHashSeed();
Rehash();
}
if (V8_UNLIKELY(v8_flags.profile_deserialization)) {
// ATTENTION: The Memory.json benchmark greps for this exact output. Do not
// change it without also updating Memory.json.
const int bytes = source()->length();
const double ms = timer.Elapsed().InMillisecondsF();
PrintF("[Deserializing read-only space (%d bytes) took %0.3f ms]\n", bytes,
ms);
}
}
void NoExternalReferencesCallback() {
// The following check will trigger if a function or object template with
// references to native functions have been deserialized from snapshot, but
// no actual external references were provided when the isolate was created.
FATAL("No external references provided via API");
}
class ObjectPostProcessor final {
public:
explicit ObjectPostProcessor(Isolate* isolate)
: isolate_(isolate), embedded_data_(EmbeddedData::FromBlob(isolate_)) {}
void Finalize() {
#ifdef V8_ENABLE_SANDBOX
std::vector<ReadOnlyArtifacts::ExternalPointerRegistryEntry> registry;
registry.reserve(external_pointer_slots_.size());
for (auto& slot : external_pointer_slots_) {
registry.emplace_back(slot.Relaxed_LoadHandle(), slot.load(isolate_),
slot.exact_tag());
}
isolate_->read_only_artifacts()->set_external_pointer_registry(
std::move(registry));
#endif // V8_ENABLE_SANDBOX
}
#define POST_PROCESS_TYPE_LIST(V) \
V(AccessorInfo) \
V(JSExternalObject) \
V(FunctionTemplateInfo) \
V(Code) \
V(SharedFunctionInfo)
V8_INLINE void PostProcessIfNeeded(Tagged<HeapObject> o,
InstanceType instance_type) {
DCHECK_EQ(o->map(isolate_)->instance_type(), instance_type);
#define V(TYPE) \
if (InstanceTypeChecker::Is##TYPE(instance_type)) { \
return PostProcess##TYPE(Cast<TYPE>(o)); \
}
POST_PROCESS_TYPE_LIST(V)
#undef V
// If we reach here, no postprocessing is needed for this object.
}
#undef POST_PROCESS_TYPE_LIST
private:
Address GetAnyExternalReferenceAt(int index, bool is_api_reference) const {
if (is_api_reference) {
const intptr_t* refs = isolate_->api_external_references();
Address address =
refs == nullptr
? reinterpret_cast<Address>(NoExternalReferencesCallback)
: static_cast<Address>(refs[index]);
DCHECK_NE(address, kNullAddress);
return address;
}
// Note we allow `address` to be kNullAddress since some of our tests
// rely on this (e.g. when testing an incompletely initialized ER table).
return isolate_->external_reference_table_unsafe()->address(index);
}
void DecodeExternalPointerSlot(Tagged<HeapObject> host,
ExternalPointerSlot slot) {
// Constructing no_gc here is not the intended use pattern (instead we
// should pass it along the entire callchain); but there's little point of
// doing that here - all of the code in this file relies on GC being
// disabled, and that's guarded at entry points.
DisallowGarbageCollection no_gc;
auto encoded = ro::EncodedExternalReference::FromUint32(
slot.GetContentAsIndexAfterDeserialization(no_gc));
Address slot_value =
GetAnyExternalReferenceAt(encoded.index, encoded.is_api_reference);
DCHECK(slot.ExactTagIsKnown());
slot.init(isolate_, host, slot_value, slot.exact_tag());
#ifdef V8_ENABLE_SANDBOX
// Register these slots during deserialization s.t. later isolates (which
// share the RO space we are currently deserializing) can properly
// initialize their external pointer table RO space. Note that slot values
// are only fully finalized at the end of deserialization, thus we only
// register the slot itself now and read the handle/value in Finalize.
external_pointer_slots_.emplace_back(slot);
#endif // V8_ENABLE_SANDBOX
}
void PostProcessAccessorInfo(Tagged<AccessorInfo> o) {
DecodeExternalPointerSlot(
o, o->RawExternalPointerField(AccessorInfo::kSetterOffset,
kAccessorInfoSetterTag));
DecodeExternalPointerSlot(o, o->RawExternalPointerField(
AccessorInfo::kMaybeRedirectedGetterOffset,
kAccessorInfoGetterTag));
if (USE_SIMULATOR_BOOL) o->init_getter_redirection(isolate_);
}
void PostProcessJSExternalObject(Tagged<JSExternalObject> o) {
DecodeExternalPointerSlot(
o, o->RawExternalPointerField(JSExternalObject::kValueOffset,
kExternalObjectValueTag));
}
void PostProcessFunctionTemplateInfo(Tagged<FunctionTemplateInfo> o) {
DecodeExternalPointerSlot(
o, o->RawExternalPointerField(
FunctionTemplateInfo::kMaybeRedirectedCallbackOffset,
kFunctionTemplateInfoCallbackTag));
if (USE_SIMULATOR_BOOL) o->init_callback_redirection(isolate_);
}
void PostProcessCode(Tagged<Code> o) {
o->init_self_indirect_pointer(isolate_);
o->wrapper()->set_code(o);
// RO space only contains builtin Code objects which don't have an
// attached InstructionStream.
DCHECK(o->is_builtin());
DCHECK(!o->has_instruction_stream());
o->SetInstructionStartForOffHeapBuiltin(
isolate_,
EmbeddedData::FromBlob(isolate_).InstructionStartOf(o->builtin_id()));
}
void PostProcessSharedFunctionInfo(Tagged<SharedFunctionInfo> o) {
// Reset the id to avoid collisions - it must be unique in this isolate.
o->set_unique_id(isolate_->GetAndIncNextUniqueSfiId());
}
Isolate* const isolate_;
const EmbeddedData embedded_data_;
#ifdef V8_ENABLE_SANDBOX
std::vector<ExternalPointerSlot> external_pointer_slots_;
#endif // V8_ENABLE_SANDBOX
};
void ReadOnlyDeserializer::PostProcessNewObjects() {
// Since we are not deserializing individual objects we need to scan the
// heap and search for objects that need post-processing.
//
// See also Deserializer<IsolateT>::PostProcessNewObject.
PtrComprCageBase cage_base(isolate());
#ifdef V8_COMPRESS_POINTERS
ExternalPointerTable::UnsealReadOnlySegmentScope unseal_scope(
&isolate()->external_pointer_table());
#endif // V8_COMPRESS_POINTERS
ObjectPostProcessor post_processor(isolate());
ReadOnlyHeapObjectIterator it(isolate()->read_only_heap());
for (Tagged<HeapObject> o = it.Next(); !o.is_null(); o = it.Next()) {
const InstanceType instance_type = o->map(cage_base)->instance_type();
if (should_rehash()) {
if (InstanceTypeChecker::IsString(instance_type)) {
Tagged<String> str = Cast<String>(o);
str->set_raw_hash_field(Name::kEmptyHashField);
PushObjectToRehash(direct_handle(str, isolate()));
} else if (o->NeedsRehashing(instance_type)) {
PushObjectToRehash(direct_handle(o, isolate()));
}
}
post_processor.PostProcessIfNeeded(o, instance_type);
}
post_processor.Finalize();
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,30 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_READ_ONLY_DESERIALIZER_H_
#define V8_SNAPSHOT_READ_ONLY_DESERIALIZER_H_
#include "src/snapshot/deserializer.h"
namespace v8 {
namespace internal {
class SnapshotData;
// Deserializes the read-only blob and creates the read-only roots table.
class ReadOnlyDeserializer final : public Deserializer<Isolate> {
public:
ReadOnlyDeserializer(Isolate* isolate, const SnapshotData* data,
bool can_rehash);
void DeserializeIntoIsolate();
private:
void PostProcessNewObjects();
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_READ_ONLY_DESERIALIZER_H_

View File

@ -0,0 +1,152 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_READ_ONLY_SERIALIZER_DESERIALIZER_H_
#define V8_SNAPSHOT_READ_ONLY_SERIALIZER_DESERIALIZER_H_
#include "src/common/globals.h"
namespace v8 {
namespace internal {
namespace ro {
// Common functionality for RO serialization and deserialization.
enum Bytecode {
// kAllocatePage parameters:
// Uint30 page_index
// Uint30 area_size_in_bytes
kAllocatePage,
// kAllocatePageAt parameters:
// Uint30 page_index
// Uint30 area_size_in_bytes
// Uint32 compressed_page_address
kAllocatePageAt,
//
// kSegment parameters:
// Uint30 page_index
// Uint30 offset
// Uint30 size_in_bytes
// ... segment byte stream
kSegment,
//
// kRelocateSegment parameters:
// ... relocation byte stream
kRelocateSegment,
//
// kReadOnlyRootsTable parameters:
// IF_STATIC_ROOTS(... ro roots table slots)
kReadOnlyRootsTable,
//
kFinalizeReadOnlySpace,
};
static constexpr int kNumberOfBytecodes =
static_cast<int>(kFinalizeReadOnlySpace) + 1;
// Like std::vector<bool> but with a known underlying encoding.
class BitSet final {
public:
explicit BitSet(size_t size_in_bits)
: size_in_bits_(size_in_bits),
data_(new uint8_t[size_in_bytes()]()),
owns_data_(true) {}
explicit BitSet(uint8_t* data, size_t size_in_bits)
: size_in_bits_(size_in_bits), data_(data), owns_data_(false) {}
~BitSet() {
if (owns_data_) delete[] data_;
}
bool contains(int i) const {
DCHECK(0 <= i && i < static_cast<int>(size_in_bits_));
return (data_[chunk_index(i)] & bit_mask(i)) != 0;
}
void set(int i) {
DCHECK(0 <= i && i < static_cast<int>(size_in_bits_));
data_[chunk_index(i)] |= bit_mask(i);
}
size_t size_in_bits() const { return size_in_bits_; }
size_t size_in_bytes() const {
return RoundUp<kBitsPerByte>(size_in_bits_) / kBitsPerByte;
}
const uint8_t* data() const { return data_; }
private:
static constexpr int kBitsPerChunk = kUInt8Size * kBitsPerByte;
static constexpr int chunk_index(int i) { return i / kBitsPerChunk; }
static constexpr int bit_index(int i) { return i % kBitsPerChunk; }
static constexpr uint32_t bit_mask(int i) { return 1 << bit_index(i); }
const size_t size_in_bits_;
uint8_t* const data_;
const bool owns_data_;
};
// Tagged slots need relocation after deserialization when V8_STATIC_ROOTS is
// disabled.
//
// Note this encoding works for all remaining build configs, in particular for
// all supported kTaggedSize values.
struct EncodedTagged {
static constexpr int kOffsetBits = kPageSizeBits;
static constexpr int kSize = kUInt32Size;
static constexpr int kPageIndexBits =
kSize * 8 - kOffsetBits; // Determines max number of RO pages.
explicit EncodedTagged(unsigned int page_index, unsigned int offset)
: page_index(page_index), offset(offset) {
DCHECK_LT(page_index, 1UL << kPageIndexBits);
DCHECK_LT(offset, 1UL << kOffsetBits);
}
uint32_t ToUint32() const {
static_assert(kSize == kUInt32Size);
return *reinterpret_cast<const uint32_t*>(this);
}
static EncodedTagged FromUint32(uint32_t v) {
return FromAddress(reinterpret_cast<Address>(&v));
}
static EncodedTagged FromAddress(Address address) {
return *reinterpret_cast<EncodedTagged*>(address);
}
const unsigned int page_index : kPageIndexBits;
const unsigned int offset : kOffsetBits; // Shifted by kTaggedSizeLog2.
};
static_assert(EncodedTagged::kSize == sizeof(EncodedTagged));
struct EncodedExternalReference {
static constexpr int kIsApiReferenceBits = 1;
static constexpr int kIndexBits = 31;
static constexpr int kSize = kUInt32Size;
uint32_t ToUint32() const {
static_assert(kSize == kUInt32Size);
return *reinterpret_cast<const uint32_t*>(this);
}
static EncodedExternalReference FromUint32(uint32_t v) {
return *reinterpret_cast<EncodedExternalReference*>(&v);
}
// This ctor is needed to convert parameter types. We can't use bool/uint32_t
// as underlying member types since that messes with field packing on
// windows.
EncodedExternalReference(bool is_api_reference, uint32_t index)
: is_api_reference(is_api_reference), index(index) {}
int is_api_reference : kIsApiReferenceBits;
int index : kIndexBits;
};
static_assert(EncodedExternalReference::kSize ==
sizeof(EncodedExternalReference));
} // namespace ro
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_READ_ONLY_SERIALIZER_DESERIALIZER_H_

View File

@ -0,0 +1,462 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/read-only-serializer.h"
#include "src/common/globals.h"
#include "src/heap/heap-inl.h"
#include "src/heap/read-only-heap.h"
#include "src/heap/visit-object.h"
#include "src/objects/objects-inl.h"
#include "src/objects/slots.h"
#include "src/snapshot/read-only-serializer-deserializer.h"
namespace v8 {
namespace internal {
namespace {
// Preprocess an object to prepare it for serialization.
class ObjectPreProcessor final {
public:
explicit ObjectPreProcessor(Isolate* isolate)
: isolate_(isolate), extref_encoder_(isolate) {}
#define PRE_PROCESS_TYPE_LIST(V) \
V(AccessorInfo) \
V(JSExternalObject) \
V(FunctionTemplateInfo) \
V(Code)
void PreProcessIfNeeded(Tagged<HeapObject> o) {
const InstanceType itype = o->map(isolate_)->instance_type();
#define V(TYPE) \
if (InstanceTypeChecker::Is##TYPE(itype)) { \
return PreProcess##TYPE(Cast<TYPE>(o)); \
}
PRE_PROCESS_TYPE_LIST(V)
#undef V
// If we reach here, no preprocessing is needed for this object.
}
#undef PRE_PROCESS_TYPE_LIST
private:
void EncodeExternalPointerSlot(ExternalPointerSlot slot) {
Address value = slot.load(isolate_);
EncodeExternalPointerSlot(slot, value);
}
void EncodeExternalPointerSlot(ExternalPointerSlot slot, Address value) {
// Note it's possible that `value != slot.load(...)`, e.g. for
// AccessorInfo::remove_getter_indirection.
ExternalReferenceEncoder::Value encoder_value =
extref_encoder_.Encode(value);
DCHECK_LT(encoder_value.index(),
1UL << ro::EncodedExternalReference::kIndexBits);
ro::EncodedExternalReference encoded{encoder_value.is_from_api(),
encoder_value.index()};
// Constructing no_gc here is not the intended use pattern (instead we
// should pass it along the entire callchain); but there's little point of
// doing that here - all of the code in this file relies on GC being
// disabled, and that's guarded at entry points.
DisallowGarbageCollection no_gc;
slot.ReplaceContentWithIndexForSerialization(no_gc, encoded.ToUint32());
}
void PreProcessAccessorInfo(Tagged<AccessorInfo> o) {
EncodeExternalPointerSlot(
o->RawExternalPointerField(AccessorInfo::kMaybeRedirectedGetterOffset,
kAccessorInfoGetterTag),
o->getter(isolate_)); // Pass the non-redirected value.
EncodeExternalPointerSlot(o->RawExternalPointerField(
AccessorInfo::kSetterOffset, kAccessorInfoSetterTag));
}
void PreProcessJSExternalObject(Tagged<JSExternalObject> o) {
EncodeExternalPointerSlot(
o->RawExternalPointerField(JSExternalObject::kValueOffset,
kExternalObjectValueTag),
reinterpret_cast<Address>(o->value(isolate_)));
}
void PreProcessFunctionTemplateInfo(Tagged<FunctionTemplateInfo> o) {
EncodeExternalPointerSlot(
o->RawExternalPointerField(
FunctionTemplateInfo::kMaybeRedirectedCallbackOffset,
kFunctionTemplateInfoCallbackTag),
o->callback(isolate_)); // Pass the non-redirected value.
}
void PreProcessCode(Tagged<Code> o) {
o->ClearInstructionStartForSerialization(isolate_);
CHECK(!o->has_source_position_table_or_bytecode_offset_table());
CHECK(!o->has_deoptimization_data_or_interpreter_data());
#ifdef V8_ENABLE_LEAPTIERING
CHECK_EQ(o->js_dispatch_handle(), kNullJSDispatchHandle);
#endif
}
Isolate* const isolate_;
ExternalReferenceEncoder extref_encoder_;
};
struct ReadOnlySegmentForSerialization {
ReadOnlySegmentForSerialization(Isolate* isolate,
const ReadOnlyPageMetadata* page,
Address segment_start, size_t segment_size,
ObjectPreProcessor* pre_processor)
: page(page),
segment_start(segment_start),
segment_size(segment_size),
segment_offset(segment_start - page->area_start()),
contents(new uint8_t[segment_size]),
tagged_slots(segment_size / kTaggedSize) {
// .. because tagged_slots records a bit for each slot:
DCHECK(IsAligned(segment_size, kTaggedSize));
// Ensure incoming pointers to this page are representable.
CHECK_LT(isolate->read_only_heap()->read_only_space()->IndexOf(page),
1UL << ro::EncodedTagged::kPageIndexBits);
MemCopy(contents.get(), reinterpret_cast<void*>(segment_start),
segment_size);
PreProcessSegment(pre_processor);
if (!V8_STATIC_ROOTS_BOOL) EncodeTaggedSlots(isolate);
}
void PreProcessSegment(ObjectPreProcessor* pre_processor) {
// Iterate the RO page and the contents copy in lockstep, preprocessing
// objects as we go along.
//
// See also ObjectSerializer::OutputRawData.
DCHECK_GE(segment_start, page->area_start());
const Address segment_end = segment_start + segment_size;
ReadOnlyPageObjectIterator it(page, segment_start);
for (Tagged<HeapObject> o = it.Next(); !o.is_null(); o = it.Next()) {
if (o.address() >= segment_end) break;
size_t o_offset = o.ptr() - segment_start;
Address o_dst = reinterpret_cast<Address>(contents.get()) + o_offset;
pre_processor->PreProcessIfNeeded(
Cast<HeapObject>(Tagged<Object>(o_dst)));
}
}
void EncodeTaggedSlots(Isolate* isolate);
const ReadOnlyPageMetadata* const page;
const Address segment_start;
const size_t segment_size;
const size_t segment_offset;
// The (mutated) off-heap copy of the on-heap segment.
std::unique_ptr<uint8_t[]> contents;
// The relocation table.
ro::BitSet tagged_slots;
friend class EncodeRelocationsVisitor;
};
ro::EncodedTagged Encode(Isolate* isolate, Tagged<HeapObject> o) {
Address o_address = o.address();
MemoryChunkMetadata* chunk = MemoryChunkMetadata::FromAddress(o_address);
ReadOnlySpace* ro_space = isolate->read_only_heap()->read_only_space();
int index = static_cast<int>(ro_space->IndexOf(chunk));
uint32_t offset = static_cast<int>(chunk->Offset(o_address));
DCHECK(IsAligned(offset, kTaggedSize));
return ro::EncodedTagged(index, offset / kTaggedSize);
}
// If relocations are needed, this class
// - encodes all tagged slots s.t. valid pointers can be reconstructed during
// deserialization, and
// - records the location of all tagged slots in a table.
class EncodeRelocationsVisitor final : public ObjectVisitor {
public:
EncodeRelocationsVisitor(Isolate* isolate,
ReadOnlySegmentForSerialization* segment)
: isolate_(isolate), segment_(segment) {
DCHECK(!V8_STATIC_ROOTS_BOOL);
}
void VisitPointers(Tagged<HeapObject> host, ObjectSlot start,
ObjectSlot end) override {
VisitPointers(host, MaybeObjectSlot(start), MaybeObjectSlot(end));
}
void VisitPointers(Tagged<HeapObject> host, MaybeObjectSlot start,
MaybeObjectSlot end) override {
for (MaybeObjectSlot slot = start; slot < end; slot++) {
ProcessSlot(slot);
}
}
void VisitMapPointer(Tagged<HeapObject> host) override {
ProcessSlot(host->RawMaybeWeakField(HeapObject::kMapOffset));
}
// Sanity-checks:
void VisitInstructionStreamPointer(Tagged<Code> host,
InstructionStreamSlot slot) override {
// RO space contains only builtin Code objects.
DCHECK(!host->has_instruction_stream());
}
void VisitCodeTarget(Tagged<InstructionStream>, RelocInfo*) override {
UNREACHABLE();
}
void VisitEmbeddedPointer(Tagged<InstructionStream>, RelocInfo*) override {
UNREACHABLE();
}
void VisitExternalReference(Tagged<InstructionStream>, RelocInfo*) override {
UNREACHABLE();
}
void VisitInternalReference(Tagged<InstructionStream>, RelocInfo*) override {
UNREACHABLE();
}
void VisitOffHeapTarget(Tagged<InstructionStream>, RelocInfo*) override {
UNREACHABLE();
}
void VisitExternalPointer(Tagged<HeapObject>,
ExternalPointerSlot slot) override {
// This slot was encoded in a previous pass, see EncodeExternalPointerSlot.
#ifdef DEBUG
ExternalPointerSlot slot_in_segment{
reinterpret_cast<Address>(segment_->contents.get() +
SegmentOffsetOf(slot)),
slot.exact_tag()};
// Constructing no_gc here is not the intended use pattern (instead we
// should pass it along the entire callchain); but there's little point of
// doing that here - all of the code in this file relies on GC being
// disabled, and that's guarded at entry points.
DisallowGarbageCollection no_gc;
auto encoded = ro::EncodedExternalReference::FromUint32(
slot_in_segment.GetContentAsIndexAfterDeserialization(no_gc));
if (encoded.is_api_reference) {
// Can't validate these since we don't know how many entries
// api_external_references contains.
} else {
CHECK_LT(encoded.index, ExternalReferenceTable::kSize);
}
#endif // DEBUG
}
private:
void ProcessSlot(MaybeObjectSlot slot) {
Tagged<MaybeObject> o = *slot;
if (!o.IsStrongOrWeak()) return; // Smis don't need relocation.
DCHECK(o.IsStrong());
int slot_offset = SegmentOffsetOf(slot);
DCHECK(IsAligned(slot_offset, kTaggedSize));
// Encode:
ro::EncodedTagged encoded = Encode(isolate_, o.GetHeapObject());
memcpy(segment_->contents.get() + slot_offset, &encoded,
ro::EncodedTagged::kSize);
// Record:
segment_->tagged_slots.set(AsSlot(slot_offset));
}
template <class SlotT>
int SegmentOffsetOf(SlotT slot) const {
Address addr = slot.address();
DCHECK_GE(addr, segment_->segment_start);
DCHECK_LT(addr, segment_->segment_start + segment_->segment_size);
return static_cast<int>(addr - segment_->segment_start);
}
static constexpr int AsSlot(int byte_offset) {
return byte_offset / kTaggedSize;
}
Isolate* const isolate_;
ReadOnlySegmentForSerialization* const segment_;
};
void ReadOnlySegmentForSerialization::EncodeTaggedSlots(Isolate* isolate) {
DCHECK(!V8_STATIC_ROOTS_BOOL);
EncodeRelocationsVisitor v(isolate, this);
PtrComprCageBase cage_base(isolate);
DCHECK_GE(segment_start, page->area_start());
const Address segment_end = segment_start + segment_size;
ReadOnlyPageObjectIterator it(page, segment_start,
SkipFreeSpaceOrFiller::kNo);
for (Tagged<HeapObject> o = it.Next(); !o.is_null(); o = it.Next()) {
if (o.address() >= segment_end) break;
VisitObject(isolate, o, &v);
}
}
class ReadOnlyHeapImageSerializer {
public:
struct MemoryRegion {
Address start;
size_t size;
};
static void Serialize(Isolate* isolate, SnapshotByteSink* sink,
const std::vector<MemoryRegion>& unmapped_regions) {
ReadOnlyHeapImageSerializer{isolate, sink}.SerializeImpl(unmapped_regions);
}
private:
using Bytecode = ro::Bytecode;
ReadOnlyHeapImageSerializer(Isolate* isolate, SnapshotByteSink* sink)
: isolate_(isolate), sink_(sink), pre_processor_(isolate) {}
void SerializeImpl(const std::vector<MemoryRegion>& unmapped_regions) {
DCHECK_EQ(sink_->Position(), 0);
ReadOnlySpace* ro_space = isolate_->read_only_heap()->read_only_space();
// Allocate all pages first s.t. the deserializer can easily handle forward
// references (e.g.: an object on page i points at an object on page i+1).
for (const ReadOnlyPageMetadata* page : ro_space->pages()) {
EmitAllocatePage(page, unmapped_regions);
}
// Now write the page contents.
for (const ReadOnlyPageMetadata* page : ro_space->pages()) {
SerializePage(page, unmapped_regions);
}
EmitReadOnlyRootsTable();
sink_->Put(Bytecode::kFinalizeReadOnlySpace, "space end");
}
uint32_t IndexOf(const ReadOnlyPageMetadata* page) {
ReadOnlySpace* ro_space = isolate_->read_only_heap()->read_only_space();
return static_cast<uint32_t>(ro_space->IndexOf(page));
}
void EmitAllocatePage(const ReadOnlyPageMetadata* page,
const std::vector<MemoryRegion>& unmapped_regions) {
if (V8_STATIC_ROOTS_BOOL) {
sink_->Put(Bytecode::kAllocatePageAt, "fixed page begin");
} else {
sink_->Put(Bytecode::kAllocatePage, "page begin");
}
sink_->PutUint30(IndexOf(page), "page index");
sink_->PutUint30(
static_cast<uint32_t>(page->HighWaterMark() - page->area_start()),
"area size in bytes");
if (V8_STATIC_ROOTS_BOOL) {
auto page_addr = page->ChunkAddress();
sink_->PutUint32(V8HeapCompressionScheme::CompressAny(page_addr),
"page start offset");
}
}
void SerializePage(const ReadOnlyPageMetadata* page,
const std::vector<MemoryRegion>& unmapped_regions) {
Address pos = page->area_start();
// If this page contains unmapped regions split it into multiple segments.
for (auto r = unmapped_regions.begin(); r != unmapped_regions.end(); ++r) {
// Regions must be sorted and non-overlapping.
if (r + 1 != unmapped_regions.end()) {
CHECK(r->start < (r + 1)->start);
CHECK(r->start + r->size < (r + 1)->start);
}
if (base::IsInRange(r->start, pos, page->HighWaterMark())) {
size_t segment_size = r->start - pos;
ReadOnlySegmentForSerialization segment(isolate_, page, pos,
segment_size, &pre_processor_);
EmitSegment(&segment);
pos += segment_size + r->size;
}
}
// Pages are shrunk, but memory at the end of the area is still
// uninitialized and we do not want to include it in the snapshot.
size_t segment_size = page->HighWaterMark() - pos;
ReadOnlySegmentForSerialization segment(isolate_, page, pos, segment_size,
&pre_processor_);
EmitSegment(&segment);
}
void EmitSegment(const ReadOnlySegmentForSerialization* segment) {
sink_->Put(Bytecode::kSegment, "segment begin");
sink_->PutUint30(IndexOf(segment->page), "page index");
sink_->PutUint30(static_cast<uint32_t>(segment->segment_offset),
"segment start offset");
sink_->PutUint30(static_cast<uint32_t>(segment->segment_size),
"segment byte size");
sink_->PutRaw(segment->contents.get(),
static_cast<int>(segment->segment_size), "page");
if (!V8_STATIC_ROOTS_BOOL) {
sink_->Put(Bytecode::kRelocateSegment, "relocate segment");
sink_->PutRaw(segment->tagged_slots.data(),
static_cast<int>(segment->tagged_slots.size_in_bytes()),
"tagged_slots");
}
}
void EmitReadOnlyRootsTable() {
sink_->Put(Bytecode::kReadOnlyRootsTable, "read only roots table");
if (!V8_STATIC_ROOTS_BOOL) {
ReadOnlyRoots roots(isolate_);
for (size_t i = 0; i < ReadOnlyRoots::kEntriesCount; i++) {
RootIndex rudi = static_cast<RootIndex>(i);
Tagged<HeapObject> rudolf = Cast<HeapObject>(roots.object_at(rudi));
ro::EncodedTagged encoded = Encode(isolate_, rudolf);
sink_->PutUint32(encoded.ToUint32(), "read only roots entry");
}
}
}
Isolate* const isolate_;
SnapshotByteSink* const sink_;
ObjectPreProcessor pre_processor_;
};
std::vector<ReadOnlyHeapImageSerializer::MemoryRegion> GetUnmappedRegions(
Isolate* isolate) {
#ifdef V8_STATIC_ROOTS
// WasmNull's payload is aligned to the OS page and consists of
// WasmNull::kPayloadSize bytes of unmapped memory. To avoid inflating the
// snapshot size and accessing uninitialized and/or unmapped memory, the
// serializer skips the padding bytes and the payload.
ReadOnlyRoots ro_roots(isolate);
Tagged<WasmNull> wasm_null = ro_roots.wasm_null();
Tagged<HeapObject> wasm_null_padding = ro_roots.wasm_null_padding();
CHECK(IsFreeSpace(wasm_null_padding));
Address wasm_null_padding_start =
wasm_null_padding.address() + FreeSpace::kHeaderSize;
std::vector<ReadOnlyHeapImageSerializer::MemoryRegion> unmapped;
if (wasm_null.address() > wasm_null_padding_start) {
unmapped.push_back({wasm_null_padding_start,
wasm_null.address() - wasm_null_padding_start});
}
unmapped.push_back({wasm_null->payload(), WasmNull::kPayloadSize});
return unmapped;
#else
return {};
#endif // V8_STATIC_ROOTS
}
} // namespace
ReadOnlySerializer::ReadOnlySerializer(Isolate* isolate,
Snapshot::SerializerFlags flags)
: RootsSerializer(isolate, flags, RootIndex::kFirstReadOnlyRoot) {}
ReadOnlySerializer::~ReadOnlySerializer() {
OutputStatistics("ReadOnlySerializer");
}
void ReadOnlySerializer::Serialize() {
DisallowGarbageCollection no_gc;
ReadOnlyHeapImageSerializer::Serialize(isolate(), &sink_,
GetUnmappedRegions(isolate()));
ReadOnlyHeapObjectIterator it(isolate()->read_only_heap());
for (Tagged<HeapObject> o = it.Next(); !o.is_null(); o = it.Next()) {
CheckRehashability(o);
if (v8_flags.serialization_statistics) {
CountAllocation(o->map(), o->Size(), SnapshotSpace::kReadOnlyHeap);
}
}
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,38 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_READ_ONLY_SERIALIZER_H_
#define V8_SNAPSHOT_READ_ONLY_SERIALIZER_H_
#include "src/snapshot/roots-serializer.h"
namespace v8 {
namespace internal {
// TODO(jgruber): Now that this does a memcpy-style serialization, there is no
// longer a fundamental reason to inherit from RootsSerializer. It's still
// convenient though because callers expect parts of the Serializer interface
// (e.g.: rehashability, serialization statistics, blob creation).
// Consider removing this inheritance.
class V8_EXPORT_PRIVATE ReadOnlySerializer : public RootsSerializer {
public:
ReadOnlySerializer(Isolate* isolate, Snapshot::SerializerFlags flags);
~ReadOnlySerializer() override;
// Serializes the entire ReadOnlySpace as well as the ReadOnlyRoots table.
void Serialize();
private:
void SerializeObjectImpl(Handle<HeapObject> o, SlotType slot_type) override {
UNREACHABLE();
}
ReadOnlySerializer(const ReadOnlySerializer&) = delete;
ReadOnlySerializer& operator=(const ReadOnlySerializer&) = delete;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_READ_ONLY_SERIALIZER_H_

149
deps/v8/src/snapshot/references.h vendored Normal file
View File

@ -0,0 +1,149 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_REFERENCES_H_
#define V8_SNAPSHOT_REFERENCES_H_
#include "src/base/bit-field.h"
#include "src/base/hashmap.h"
#include "src/execution/isolate.h"
#include "src/utils/identity-map.h"
namespace v8 {
namespace internal {
// Values must be contiguous and start at 0 since they're directly used as
// array indices.
enum class SnapshotSpace : uint8_t {
kReadOnlyHeap = 0,
kOld = 1,
kCode = 2,
kTrusted = 3,
};
static constexpr int kNumberOfSnapshotSpaces = 4;
class SerializerReference {
private:
enum SpecialValueType {
kBackReference,
kAttachedReference,
kOffHeapBackingStore,
kBuiltinReference,
};
SerializerReference(SpecialValueType type, uint32_t value)
: bit_field_(TypeBits::encode(type) | ValueBits::encode(value)) {}
public:
static SerializerReference BackReference(uint32_t index) {
return SerializerReference(kBackReference, index);
}
static SerializerReference OffHeapBackingStoreReference(uint32_t index) {
return SerializerReference(kOffHeapBackingStore, index);
}
static SerializerReference AttachedReference(uint32_t index) {
return SerializerReference(kAttachedReference, index);
}
static SerializerReference BuiltinReference(uint32_t index) {
return SerializerReference(kBuiltinReference, index);
}
bool is_back_reference() const {
return TypeBits::decode(bit_field_) == kBackReference;
}
uint32_t back_ref_index() const {
DCHECK(is_back_reference());
return ValueBits::decode(bit_field_);
}
bool is_off_heap_backing_store_reference() const {
return TypeBits::decode(bit_field_) == kOffHeapBackingStore;
}
uint32_t off_heap_backing_store_index() const {
DCHECK(is_off_heap_backing_store_reference());
return ValueBits::decode(bit_field_);
}
bool is_attached_reference() const {
return TypeBits::decode(bit_field_) == kAttachedReference;
}
uint32_t attached_reference_index() const {
DCHECK(is_attached_reference());
return ValueBits::decode(bit_field_);
}
bool is_builtin_reference() const {
return TypeBits::decode(bit_field_) == kBuiltinReference;
}
uint32_t builtin_index() const {
DCHECK(is_builtin_reference());
return ValueBits::decode(bit_field_);
}
private:
using TypeBits = base::BitField<SpecialValueType, 0, 2>;
using ValueBits = TypeBits::Next<uint32_t, 32 - TypeBits::kSize>;
uint32_t bit_field_;
friend class SerializerReferenceMap;
};
// SerializerReference has to fit in an IdentityMap value field.
static_assert(sizeof(SerializerReference) <= sizeof(void*));
class SerializerReferenceMap {
public:
explicit SerializerReferenceMap(Isolate* isolate)
: map_(isolate->heap()), attached_reference_index_(0) {}
const SerializerReference* LookupReference(Tagged<HeapObject> object) const {
return map_.Find(object);
}
const SerializerReference* LookupReference(
DirectHandle<HeapObject> object) const {
return map_.Find(object);
}
const SerializerReference* LookupBackingStore(void* backing_store) const {
auto it = backing_store_map_.find(backing_store);
if (it == backing_store_map_.end()) return nullptr;
return &it->second;
}
void Add(Tagged<HeapObject> object, SerializerReference reference) {
DCHECK_NULL(LookupReference(object));
map_.Insert(object, reference);
}
void AddBackingStore(void* backing_store, SerializerReference reference) {
DCHECK(backing_store_map_.find(backing_store) == backing_store_map_.end());
backing_store_map_.emplace(backing_store, reference);
}
SerializerReference AddAttachedReference(Tagged<HeapObject> object) {
SerializerReference reference =
SerializerReference::AttachedReference(attached_reference_index_++);
map_.Insert(object, reference);
return reference;
}
private:
IdentityMap<SerializerReference, base::DefaultAllocationPolicy> map_;
std::unordered_map<void*, SerializerReference> backing_store_map_;
int attached_reference_index_;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_REFERENCES_H_

View File

@ -0,0 +1,68 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/roots-serializer.h"
#include "src/execution/isolate.h"
#include "src/heap/heap.h"
#include "src/objects/slots.h"
namespace v8 {
namespace internal {
RootsSerializer::RootsSerializer(Isolate* isolate,
Snapshot::SerializerFlags flags,
RootIndex first_root_to_be_serialized)
: Serializer(isolate, flags),
first_root_to_be_serialized_(first_root_to_be_serialized),
object_cache_index_map_(isolate->heap()),
can_be_rehashed_(true) {
for (size_t i = 0; i < static_cast<size_t>(first_root_to_be_serialized);
++i) {
root_has_been_serialized_[i] = true;
}
}
int RootsSerializer::SerializeInObjectCache(Handle<HeapObject> heap_object) {
int index;
if (!object_cache_index_map_.LookupOrInsert(*heap_object, &index)) {
// This object is not part of the object cache yet. Add it to the cache so
// we can refer to it via cache index from the delegating snapshot.
SerializeObject(heap_object, SlotType::kAnySlot);
}
return index;
}
void RootsSerializer::Synchronize(VisitorSynchronization::SyncTag tag) {
sink_.Put(kSynchronize, "Synchronize");
}
void RootsSerializer::VisitRootPointers(Root root, const char* description,
FullObjectSlot start,
FullObjectSlot end) {
RootsTable& roots_table = isolate()->roots_table();
if (start ==
roots_table.begin() + static_cast<int>(first_root_to_be_serialized_)) {
// Serializing the root list needs special handling:
// - Only root list elements that have been fully serialized can be
// referenced using kRootArray bytecodes.
for (FullObjectSlot current = start; current < end; ++current) {
SerializeRootObject(current);
size_t root_index = current - roots_table.begin();
root_has_been_serialized_.set(root_index);
}
} else {
Serializer::VisitRootPointers(root, description, start, end);
}
}
void RootsSerializer::CheckRehashability(Tagged<HeapObject> obj) {
if (!can_be_rehashed_) return;
if (!obj->NeedsRehashing(cage_base())) return;
if (obj->CanBeRehashed(cage_base())) return;
can_be_rehashed_ = false;
}
} // namespace internal
} // namespace v8

67
deps/v8/src/snapshot/roots-serializer.h vendored Normal file
View File

@ -0,0 +1,67 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_ROOTS_SERIALIZER_H_
#define V8_SNAPSHOT_ROOTS_SERIALIZER_H_
#include <bitset>
#include "src/objects/visitors.h"
#include "src/snapshot/serializer.h"
namespace v8 {
namespace internal {
class HeapObject;
class Object;
class Isolate;
enum class RootIndex : uint16_t;
// Base class for serializer that iterate over roots. Also maintains a cache
// that can be used to share non-root objects with other serializers.
class RootsSerializer : public Serializer {
public:
// The serializer expects that all roots before |first_root_to_be_serialized|
// are already serialized.
RootsSerializer(Isolate* isolate, Snapshot::SerializerFlags flags,
RootIndex first_root_to_be_serialized);
RootsSerializer(const RootsSerializer&) = delete;
RootsSerializer& operator=(const RootsSerializer&) = delete;
bool can_be_rehashed() const { return can_be_rehashed_; }
bool root_has_been_serialized(RootIndex root_index) const {
return root_has_been_serialized_.test(static_cast<size_t>(root_index));
}
bool IsRootAndHasBeenSerialized(Tagged<HeapObject> obj) const {
RootIndex root_index;
return root_index_map()->Lookup(obj, &root_index) &&
root_has_been_serialized(root_index);
}
protected:
void CheckRehashability(Tagged<HeapObject> obj);
// Serializes |object| if not previously seen and returns its cache index.
int SerializeInObjectCache(Handle<HeapObject> object);
bool object_cache_empty() { return object_cache_index_map_.size() == 0; }
private:
void VisitRootPointers(Root root, const char* description,
FullObjectSlot start, FullObjectSlot end) override;
void Synchronize(VisitorSynchronization::SyncTag tag) override;
const RootIndex first_root_to_be_serialized_;
std::bitset<RootsTable::kEntriesCount> root_has_been_serialized_;
ObjectCacheIndexMap object_cache_index_map_;
// Indicates whether we only serialized hash tables that we can rehash.
// TODO(yangguo): generalize rehashing, and remove this flag.
bool can_be_rehashed_;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_ROOTS_SERIALIZER_H_

View File

@ -0,0 +1,88 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/serializer-deserializer.h"
#include "src/objects/embedder-data-array-inl.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
namespace {
DISABLE_CFI_PERF
void IterateObjectCache(Isolate* isolate, std::vector<Tagged<Object>>* cache,
Root root_id, RootVisitor* visitor) {
for (size_t i = 0;; ++i) {
// Extend the array ready to get a value when deserializing.
if (cache->size() <= i) cache->push_back(Smi::zero());
// During deserialization, the visitor populates the object cache and
// eventually terminates the cache with undefined.
visitor->VisitRootPointer(root_id, nullptr, FullObjectSlot(&cache->at(i)));
// We may see objects in trusted space here (outside of the main pointer
// compression cage), so have to use SafeEquals.
Tagged<Object> undefined = ReadOnlyRoots(isolate).undefined_value();
if (cache->at(i).SafeEquals(undefined)) break;
}
}
} // namespace
// The startup and shared heap object caches are terminated by undefined. We
// visit these caches...
// - during deserialization to populate it.
// - during normal GC to keep its content alive.
// - not during serialization. The context serializer adds to it explicitly.
void SerializerDeserializer::IterateStartupObjectCache(Isolate* isolate,
RootVisitor* visitor) {
IterateObjectCache(isolate, isolate->startup_object_cache(),
Root::kStartupObjectCache, visitor);
}
void SerializerDeserializer::IterateSharedHeapObjectCache(
Isolate* isolate, RootVisitor* visitor) {
IterateObjectCache(isolate, isolate->shared_heap_object_cache(),
Root::kSharedHeapObjectCache, visitor);
}
bool SerializerDeserializer::CanBeDeferred(Tagged<HeapObject> o,
SlotType slot_type) {
// HeapObjects' map slots cannot be deferred as objects are expected to have a
// valid map immediately.
if (slot_type == SlotType::kMapSlot) {
DCHECK(IsMap(o));
return false;
}
// * Internalized strings cannot be deferred as they might be
// converted to thin strings during post processing, at which point forward
// references to the now-thin string will already have been written.
// * JS objects with embedder fields cannot be deferred because the
// serialize/deserialize callbacks need the back reference immediately to
// identify the object.
// * ByteArray cannot be deferred as JSTypedArray needs the base_pointer
// ByteArray immediately if it's on heap.
// * Non-empty EmbdderDataArrays cannot be deferred because the serialize
// and deserialize callbacks need the back reference immediately to
// identify the object.
// TODO(leszeks): Could we defer string serialization if forward references
// were resolved after object post processing?
return !IsInternalizedString(o) &&
!(IsJSObject(o) && Cast<JSObject>(o)->GetEmbedderFieldCount() > 0) &&
!IsByteArray(o) &&
!(IsEmbedderDataArray(o) && Cast<EmbedderDataArray>(o)->length() > 0);
}
void SerializerDeserializer::RestoreExternalReferenceRedirector(
Isolate* isolate, Tagged<AccessorInfo> accessor_info) {
DisallowGarbageCollection no_gc;
accessor_info->init_getter_redirection(isolate);
}
void SerializerDeserializer::RestoreExternalReferenceRedirector(
Isolate* isolate, Tagged<FunctionTemplateInfo> function_template_info) {
DisallowGarbageCollection no_gc;
function_template_info->init_callback_redirection(isolate);
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,318 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_SERIALIZER_DESERIALIZER_H_
#define V8_SNAPSHOT_SERIALIZER_DESERIALIZER_H_
#include "src/objects/visitors.h"
#include "src/snapshot/references.h"
namespace v8 {
namespace internal {
class Isolate;
// The Serializer/Deserializer class is a common superclass for Serializer and
// Deserializer which is used to store common constants and methods used by
// both.
class SerializerDeserializer : public RootVisitor {
public:
static void IterateStartupObjectCache(Isolate* isolate, RootVisitor* visitor);
static void IterateSharedHeapObjectCache(Isolate* isolate,
RootVisitor* visitor);
protected:
enum class SlotType {
kAnySlot,
kMapSlot,
};
static bool CanBeDeferred(Tagged<HeapObject> o, SlotType slot_type);
void RestoreExternalReferenceRedirector(Isolate* isolate,
Tagged<AccessorInfo> accessor_info);
void RestoreExternalReferenceRedirector(
Isolate* isolate, Tagged<FunctionTemplateInfo> function_template_info);
// clang-format off
#define UNUSED_SERIALIZER_BYTE_CODES(V) \
/* Free range 0x22..0x2f */ \
V(0x22) V(0x23) V(0x24) V(0x25) V(0x26) V(0x27) \
V(0x28) V(0x29) V(0x2a) V(0x2b) V(0x2c) V(0x2d) V(0x2e) V(0x2f) \
/* Free range 0x30..0x3f */ \
V(0x30) V(0x31) V(0x32) V(0x33) V(0x34) V(0x35) V(0x36) V(0x37) \
V(0x38) V(0x39) V(0x3a) V(0x3b) V(0x3c) V(0x3d) V(0x3e) V(0x3f) \
/* Free range 0x97..0x9f */ \
V(0x98) V(0x99) V(0x9a) V(0x9b) V(0x9c) V(0x9d) V(0x9e) V(0x9f) \
/* Free range 0xa0..0xaf */ \
V(0xa0) V(0xa1) V(0xa2) V(0xa3) V(0xa4) V(0xa5) V(0xa6) V(0xa7) \
V(0xa8) V(0xa9) V(0xaa) V(0xab) V(0xac) V(0xad) V(0xae) V(0xaf) \
/* Free range 0xb0..0xbf */ \
V(0xb0) V(0xb1) V(0xb2) V(0xb3) V(0xb4) V(0xb5) V(0xb6) V(0xb7) \
V(0xb8) V(0xb9) V(0xba) V(0xbb) V(0xbc) V(0xbd) V(0xbe) V(0xbf) \
/* Free range 0xc0..0xcf */ \
V(0xc0) V(0xc1) V(0xc2) V(0xc3) V(0xc4) V(0xc5) V(0xc6) V(0xc7) \
V(0xc8) V(0xc9) V(0xca) V(0xcb) V(0xcc) V(0xcd) V(0xce) V(0xcf) \
/* Free range 0xd0..0xdf */ \
V(0xd0) V(0xd1) V(0xd2) V(0xd3) V(0xd4) V(0xd5) V(0xd6) V(0xd7) \
V(0xd8) V(0xd9) V(0xda) V(0xdb) V(0xdc) V(0xdd) V(0xde) V(0xdf) \
/* Free range 0xe0..0xef */ \
V(0xe0) V(0xe1) V(0xe2) V(0xe3) V(0xe4) V(0xe5) V(0xe6) V(0xe7) \
V(0xe8) V(0xe9) V(0xea) V(0xeb) V(0xec) V(0xed) V(0xee) V(0xef) \
/* Free range 0xf0..0xff */ \
V(0xf0) V(0xf1) V(0xf2) V(0xf3) V(0xf4) V(0xf5) V(0xf6) V(0xf7) \
V(0xf8) V(0xf9) V(0xfa) V(0xfb) V(0xfc) V(0xfd) V(0xfe) V(0xff)
// clang-format on
// The static assert below will trigger when the number of preallocated spaces
// changed. If that happens, update the kNewObject and kBackref bytecode
// ranges in the comments below.
static_assert(4 == kNumberOfSnapshotSpaces);
// First 32 root array items.
static const int kRootArrayConstantsCount = 0x20;
// 32 common raw data lengths.
static const int kFixedRawDataCount = 0x20;
// 16 repeats lengths.
static const int kFixedRepeatRootCount = 0x10;
// 8 hot (recently seen or back-referenced) objects with optional skip.
static const int kHotObjectCount = 8;
enum Bytecode : uint8_t {
//
// ---------- byte code range 0x00..0x1f ----------
//
// 0x00..0x03 Allocate new object, in specified space.
kNewObject = 0x00,
// Reference to previously allocated object.
kBackref = 0x04,
// Reference to an object in the read only heap.
kReadOnlyHeapRef,
// Object in the startup object cache.
kStartupObjectCache,
// Root array item.
kRootArray,
// Object provided in the attached list.
kAttachedReference,
// Object in the shared heap object cache.
kSharedHeapObjectCache,
// Do nothing, used for padding.
kNop,
// A tag emitted at strategic points in the snapshot to delineate sections.
// If the deserializer does not find these at the expected moments then it
// is an indication that the snapshot and the VM do not fit together.
// Examine the build process for architecture, version or configuration
// mismatches.
kSynchronize,
// Repeats of variable length of a root.
kVariableRepeatRoot,
// Used for embedder-allocated backing stores for TypedArrays.
kOffHeapBackingStore,
kOffHeapResizableBackingStore,
// Used for embedder-provided serialization data for embedder fields.
kEmbedderFieldsData,
// Used for embedder-provided serialziation data for API wrappers.
kApiWrapperFieldsData,
// Raw data of variable length.
kVariableRawData,
// Used to encode external references provided through the API.
kApiReference,
// External reference referenced by id.
kExternalReference,
// Same as three bytecodes above but for serializing sandboxed external
// pointer values.
// TODO(v8:10391): Remove them once all ExternalPointer usages are
// sandbox-ready.
kSandboxedApiReference,
kSandboxedExternalReference,
kSandboxedRawExternalReference,
// In-place weak references.
kClearedWeakReference,
kWeakPrefix,
// Registers the current slot as a "pending" forward reference, to be later
// filled by a corresponding resolution bytecode.
kRegisterPendingForwardRef,
// Resolves an existing "pending" forward reference to point to the current
// object.
kResolvePendingForwardRef,
// Special construction bytecodes for the metamaps. In theory we could
// reuse forward-references for this, but then the forward reference would
// be registered during object map deserialization, before the object is
// allocated, so there wouldn't be a allocated object whose map field we can
// register as the pending field. We could either hack around this, or
// simply introduce this new bytecode.
kNewContextlessMetaMap,
kNewContextfulMetaMap,
// When the sandbox is enabled, a prefix indicating that the following
// object is referenced through an indirect pointer, i.e. through an entry
// in a pointer table.
kIndirectPointerPrefix,
// When the sandbox is enabled, this bytecode instructs the deserializer to
// initialize the "self" indirect pointer of trusted objects, which
// references the object's pointer table entry. As the "self" indirect
// pointer is always the first field after the map word, it is guaranteed
// that it will be deserialized before any inner objects, which may require
// the pointer table entry for back reference to the trusted object.
kInitializeSelfIndirectPointer,
// This bytecode instructs the deserializer to allocate an entry in the
// JSDispatchTable for the host object and store the corresponding dispatch
// handle into the current slot.
kAllocateJSDispatchEntry,
// A back-reference to the already allocated n-th dispatch entry.
kJSDispatchEntry,
// A prefix indicating that the following object is referenced through a
// protected pointer, i.e. a pointer from one trusted object to another.
kProtectedPointerPrefix,
//
// ---------- byte code range 0x40..0x7f ----------
//
// 0x40..0x5f
kRootArrayConstants = 0x40,
// 0x60..0x7f
kFixedRawData = 0x60,
//
// ---------- byte code range 0x80..0x9f ----------
//
// 0x80..0x8f
kFixedRepeatRoot = 0x80,
// 0x90..0x97
kHotObject = 0x90,
};
// Helper class for encoding and decoding a value into and from a bytecode.
//
// The value is encoded by allocating an entire bytecode range, and encoding
// the value as an index in that range, starting at kMinValue; thus the range
// of values
// [kMinValue, kMinValue + 1, ... , kMaxValue]
// is encoded as
// [kBytecode, kBytecode + 1, ... , kBytecode + (N - 1)]
// where N is the number of values, i.e. kMaxValue - kMinValue + 1.
template <Bytecode kBytecode, int kMinValue, int kMaxValue,
typename TValue = int>
struct BytecodeValueEncoder {
static_assert((kBytecode + kMaxValue - kMinValue) <= kMaxUInt8);
static constexpr bool IsEncodable(TValue value) {
return base::IsInRange(static_cast<int>(value), kMinValue, kMaxValue);
}
static constexpr uint8_t Encode(TValue value) {
DCHECK(IsEncodable(value));
return static_cast<uint8_t>(kBytecode + static_cast<int>(value) -
kMinValue);
}
static constexpr TValue Decode(uint8_t bytecode) {
DCHECK(base::IsInRange(bytecode, Encode(static_cast<TValue>(kMinValue)),
Encode(static_cast<TValue>(kMaxValue))));
return static_cast<TValue>(bytecode - kBytecode + kMinValue);
}
};
template <Bytecode bytecode>
using SpaceEncoder =
BytecodeValueEncoder<bytecode, 0, kNumberOfSnapshotSpaces - 1,
SnapshotSpace>;
using NewObject = SpaceEncoder<kNewObject>;
//
// Some other constants.
//
// Sentinel after a new object to indicate that double alignment is needed.
static const int kDoubleAlignmentSentinel = 0;
// Raw data size encoding helpers.
static const int kFirstEncodableFixedRawDataSize = 1;
static const int kLastEncodableFixedRawDataSize =
kFirstEncodableFixedRawDataSize + kFixedRawDataCount - 1;
using FixedRawDataWithSize =
BytecodeValueEncoder<kFixedRawData, kFirstEncodableFixedRawDataSize,
kLastEncodableFixedRawDataSize>;
// Repeat count encoding helpers.
static const int kFirstEncodableRepeatRootCount = 2;
static const int kLastEncodableFixedRepeatRootCount =
kFirstEncodableRepeatRootCount + kFixedRepeatRootCount - 1;
static const int kFirstEncodableVariableRepeatRootCount =
kLastEncodableFixedRepeatRootCount + 1;
using FixedRepeatRootWithCount =
BytecodeValueEncoder<kFixedRepeatRoot, kFirstEncodableRepeatRootCount,
kLastEncodableFixedRepeatRootCount>;
// Encodes/decodes repeat count into a serialized variable repeat count
// value.
struct VariableRepeatRootCount {
static constexpr bool IsEncodable(int repeat_count) {
return repeat_count >= kFirstEncodableVariableRepeatRootCount;
}
static constexpr int Encode(int repeat_count) {
DCHECK(IsEncodable(repeat_count));
return repeat_count - kFirstEncodableVariableRepeatRootCount;
}
static constexpr int Decode(int value) {
return value + kFirstEncodableVariableRepeatRootCount;
}
};
using RootArrayConstant =
BytecodeValueEncoder<kRootArrayConstants, 0, kRootArrayConstantsCount - 1,
RootIndex>;
using HotObject = BytecodeValueEncoder<kHotObject, 0, kHotObjectCount - 1>;
// This backing store reference value represents empty backing stores during
// serialization/deserialization.
static const uint32_t kEmptyBackingStoreRefSentinel = 0;
};
struct SerializeEmbedderFieldsCallback {
explicit SerializeEmbedderFieldsCallback(
v8::SerializeInternalFieldsCallback js_cb =
v8::SerializeInternalFieldsCallback(),
v8::SerializeContextDataCallback context_cb =
v8::SerializeContextDataCallback(),
v8::SerializeAPIWrapperCallback api_wrapper_cb =
v8::SerializeAPIWrapperCallback())
: js_object_callback(js_cb),
context_callback(context_cb),
api_wrapper_callback(api_wrapper_cb) {}
v8::SerializeInternalFieldsCallback js_object_callback;
v8::SerializeContextDataCallback context_callback;
v8::SerializeAPIWrapperCallback api_wrapper_callback;
};
struct DeserializeEmbedderFieldsCallback {
explicit DeserializeEmbedderFieldsCallback(
v8::DeserializeInternalFieldsCallback js_cb =
v8::DeserializeInternalFieldsCallback(),
v8::DeserializeContextDataCallback context_cb =
v8::DeserializeContextDataCallback(),
v8::DeserializeAPIWrapperCallback api_wrapper_cb =
v8::DeserializeAPIWrapperCallback())
: js_object_callback(js_cb),
context_callback(context_cb),
api_wrapper_callback(api_wrapper_cb) {}
v8::DeserializeInternalFieldsCallback js_object_callback;
v8::DeserializeContextDataCallback context_callback;
v8::DeserializeAPIWrapperCallback api_wrapper_callback;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_SERIALIZER_DESERIALIZER_H_

37
deps/v8/src/snapshot/serializer-inl.h vendored Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_SERIALIZER_INL_H_
#define V8_SNAPSHOT_SERIALIZER_INL_H_
#include "src/snapshot/serializer.h"
// Include the non-inl header before the rest of the headers.
#include "src/roots/roots-inl.h"
namespace v8 {
namespace internal {
bool Serializer::IsNotMappedSymbol(Tagged<HeapObject> obj) const {
Tagged<Object> not_mapped_symbol =
ReadOnlyRoots(isolate()).not_mapped_symbol();
if (V8_EXTERNAL_CODE_SPACE_BOOL) {
// It's possible that an InstructionStream object might have the same
// compressed value as the not_mapped_symbol, so we must compare full
// pointers.
// TODO(v8:11880): Avoid the need for this special case by never putting
// InstructionStream references anywhere except the CodeDadaContainer
// objects. In particular, the InstructionStream objects should not appear
// in serializer's identity map. This should be possible once the
// IsolateData::builtins table is migrated to contain Code
// references.
return obj.ptr() == not_mapped_symbol.ptr();
}
return obj == not_mapped_symbol;
}
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_SERIALIZER_INL_H_

1485
deps/v8/src/snapshot/serializer.cc vendored Normal file

File diff suppressed because it is too large Load Diff

536
deps/v8/src/snapshot/serializer.h vendored Normal file
View File

@ -0,0 +1,536 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_SERIALIZER_H_
#define V8_SNAPSHOT_SERIALIZER_H_
#include "src/codegen/external-reference-encoder.h"
#include "src/common/assert-scope.h"
#include "src/execution/isolate.h"
#include "src/handles/global-handles.h"
#include "src/logging/log.h"
#include "src/objects/abstract-code.h"
#include "src/objects/bytecode-array.h"
#include "src/objects/instruction-stream.h"
#include "src/objects/objects.h"
#include "src/snapshot/serializer-deserializer.h"
#include "src/snapshot/snapshot-source-sink.h"
#include "src/snapshot/snapshot.h"
#include "src/utils/identity-map.h"
namespace v8 {
namespace internal {
class CodeAddressMap : public CodeEventLogger {
public:
explicit CodeAddressMap(Isolate* isolate) : CodeEventLogger(isolate) {
CHECK(isolate->logger()->AddListener(this));
}
~CodeAddressMap() override {
CHECK(isolate_->logger()->RemoveListener(this));
}
void CodeMoveEvent(Tagged<InstructionStream> from,
Tagged<InstructionStream> to) override {
address_to_name_map_.Move(from.address(), to.address());
}
void BytecodeMoveEvent(Tagged<BytecodeArray> from,
Tagged<BytecodeArray> to) override {
address_to_name_map_.Move(from.address(), to.address());
}
void CodeDisableOptEvent(DirectHandle<AbstractCode> code,
DirectHandle<SharedFunctionInfo> shared) override {}
const char* Lookup(Address address) {
return address_to_name_map_.Lookup(address);
}
private:
class NameMap {
public:
NameMap() : impl_() {}
NameMap(const NameMap&) = delete;
NameMap& operator=(const NameMap&) = delete;
~NameMap() {
for (base::HashMap::Entry* p = impl_.Start(); p != nullptr;
p = impl_.Next(p)) {
DeleteArray(static_cast<const char*>(p->value));
}
}
void Insert(Address code_address, const char* name, size_t name_size) {
base::HashMap::Entry* entry = FindOrCreateEntry(code_address);
if (entry->value == nullptr) {
entry->value = CopyName(name, name_size);
}
}
const char* Lookup(Address code_address) {
base::HashMap::Entry* entry = FindEntry(code_address);
return (entry != nullptr) ? static_cast<const char*>(entry->value)
: nullptr;
}
void Remove(Address code_address) {
base::HashMap::Entry* entry = FindEntry(code_address);
if (entry != nullptr) {
DeleteArray(static_cast<char*>(entry->value));
RemoveEntry(entry);
}
}
void Move(Address from, Address to) {
if (from == to) return;
base::HashMap::Entry* from_entry = FindEntry(from);
DCHECK_NOT_NULL(from_entry);
void* value = from_entry->value;
RemoveEntry(from_entry);
base::HashMap::Entry* to_entry = FindOrCreateEntry(to);
DCHECK_NULL(to_entry->value);
to_entry->value = value;
}
private:
static char* CopyName(const char* name, size_t name_size) {
char* result = NewArray<char>(name_size + 1);
for (size_t i = 0; i < name_size; ++i) {
char c = name[i];
if (c == '\0') c = ' ';
result[i] = c;
}
result[name_size] = '\0';
return result;
}
base::HashMap::Entry* FindOrCreateEntry(Address code_address) {
return impl_.LookupOrInsert(reinterpret_cast<void*>(code_address),
ComputeAddressHash(code_address));
}
base::HashMap::Entry* FindEntry(Address code_address) {
return impl_.Lookup(reinterpret_cast<void*>(code_address),
ComputeAddressHash(code_address));
}
void RemoveEntry(base::HashMap::Entry* entry) {
impl_.Remove(entry->key, entry->hash);
}
base::HashMap impl_;
};
void LogRecordedBuffer(Tagged<AbstractCode> code,
MaybeDirectHandle<SharedFunctionInfo>,
const char* name, size_t length) override {
DisallowGarbageCollection no_gc;
address_to_name_map_.Insert(code.address(), name, length);
}
#if V8_ENABLE_WEBASSEMBLY
void LogRecordedBuffer(const wasm::WasmCode* code, const char* name,
size_t length) override {
UNREACHABLE();
}
#endif // V8_ENABLE_WEBASSEMBLY
NameMap address_to_name_map_;
};
class ObjectCacheIndexMap {
public:
explicit ObjectCacheIndexMap(Heap* heap) : map_(heap), next_index_(0) {}
ObjectCacheIndexMap(const ObjectCacheIndexMap&) = delete;
ObjectCacheIndexMap& operator=(const ObjectCacheIndexMap&) = delete;
// If |obj| is in the map, immediately return true. Otherwise add it to the
// map and return false. In either case set |*index_out| to the index
// associated with the map.
bool LookupOrInsert(Tagged<HeapObject> obj, int* index_out) {
auto find_result = map_.FindOrInsert(obj);
if (!find_result.already_exists) {
*find_result.entry = next_index_++;
}
*index_out = *find_result.entry;
return find_result.already_exists;
}
bool LookupOrInsert(DirectHandle<HeapObject> obj, int* index_out) {
return LookupOrInsert(*obj, index_out);
}
bool Lookup(Tagged<HeapObject> obj, int* index_out) const {
int* index = map_.Find(obj);
if (index == nullptr) {
return false;
}
*index_out = *index;
return true;
}
DirectHandle<FixedArray> Values(Isolate* isolate);
int size() const { return next_index_; }
private:
IdentityMap<int, base::DefaultAllocationPolicy> map_;
int next_index_;
};
class Serializer : public SerializerDeserializer {
public:
Serializer(Isolate* isolate, Snapshot::SerializerFlags flags);
~Serializer() override { DCHECK_EQ(unresolved_forward_refs_, 0); }
Serializer(const Serializer&) = delete;
Serializer& operator=(const Serializer&) = delete;
const std::vector<uint8_t>* Payload() const { return sink_.data(); }
bool ReferenceMapContains(DirectHandle<HeapObject> o) {
return reference_map()->LookupReference(o) != nullptr;
}
Isolate* isolate() const { return isolate_; }
// The pointer compression cage base value used for decompression of all
// tagged values except references to InstructionStream objects.
PtrComprCageBase cage_base() const {
#if V8_COMPRESS_POINTERS
return cage_base_;
#else
return PtrComprCageBase{};
#endif // V8_COMPRESS_POINTERS
}
int TotalAllocationSize() const;
protected:
using PendingObjectReferences = std::vector<int>*;
class ObjectSerializer;
class V8_NODISCARD RecursionScope {
public:
explicit RecursionScope(Serializer* serializer) : serializer_(serializer) {
serializer_->recursion_depth_++;
}
~RecursionScope() { serializer_->recursion_depth_--; }
bool ExceedsMaximum() const {
return serializer_->recursion_depth_ > kMaxRecursionDepth;
}
int ExceedsMaximumBy() const {
return serializer_->recursion_depth_ - kMaxRecursionDepth;
}
private:
static const int kMaxRecursionDepth = 32;
Serializer* serializer_;
};
// Compares obj with not_mapped_symbol root. When V8_EXTERNAL_CODE_SPACE is
// enabled it compares full pointers.
V8_INLINE bool IsNotMappedSymbol(Tagged<HeapObject> obj) const;
void SerializeDeferredObjects();
void SerializeObject(Handle<HeapObject> o, SlotType slot_type);
virtual void SerializeObjectImpl(Handle<HeapObject> o,
SlotType slot_type) = 0;
virtual bool MustBeDeferred(Tagged<HeapObject> object);
void VisitRootPointers(Root root, const char* description,
FullObjectSlot start, FullObjectSlot end) override;
void SerializeRootObject(FullObjectSlot slot);
void PutRoot(RootIndex root_index);
void PutSmiRoot(FullObjectSlot slot);
void PutBackReference(Tagged<HeapObject> object,
SerializerReference reference);
void PutAttachedReference(SerializerReference reference);
void PutNextChunk(SnapshotSpace space);
void PutRepeatRoot(int repeat_count, RootIndex root_index);
// Emit a marker noting that this slot is a forward reference to the an
// object which has not yet been serialized.
void PutPendingForwardReference(PendingObjectReferences& ref);
// Resolve the given previously registered forward reference to the current
// object.
void ResolvePendingForwardReference(int obj);
// Returns true if the object was successfully serialized as a root.
bool SerializeRoot(Tagged<HeapObject> obj);
// Returns true if the object was successfully serialized as hot object.
bool SerializeHotObject(Tagged<HeapObject> obj);
// Returns true if the object was successfully serialized as back reference.
bool SerializeBackReference(Tagged<HeapObject> obj);
// Returns true if the object was successfully serialized as pending object.
bool SerializePendingObject(Tagged<HeapObject> obj);
// Returns true if the given heap object is a bytecode handler code object.
bool ObjectIsBytecodeHandler(Tagged<HeapObject> obj) const;
ExternalReferenceEncoder::Value EncodeExternalReference(Address addr);
Maybe<ExternalReferenceEncoder::Value> TryEncodeExternalReference(
Address addr) {
return external_reference_encoder_.TryEncode(addr);
}
bool SerializeReadOnlyObjectReference(Tagged<HeapObject> obj,
SnapshotByteSink* sink);
// GetInt reads 4 bytes at once, requiring padding at the end.
// Use padding_offset to specify the space you want to use after padding.
void Pad(int padding_offset = 0);
// We may not need the code address map for logging for every instance
// of the serializer. Initialize it on demand.
void InitializeCodeAddressMap();
Tagged<InstructionStream> CopyCode(Tagged<InstructionStream> istream);
void QueueDeferredObject(Tagged<HeapObject> obj) {
DCHECK_NULL(reference_map_.LookupReference(obj));
deferred_objects_.Push(obj);
}
// Register that the the given object shouldn't be immediately serialized, but
// will be serialized later and any references to it should be pending forward
// references.
void RegisterObjectIsPending(Tagged<HeapObject> obj);
// Resolve the given pending object reference with the current object.
void ResolvePendingObject(Tagged<HeapObject> obj);
void OutputStatistics(const char* name);
void CountAllocation(Tagged<Map> map, int size, SnapshotSpace space);
#ifdef DEBUG
void PushStack(DirectHandle<HeapObject> o) { stack_.Push(*o); }
void PopStack();
void PrintStack();
void PrintStack(std::ostream&);
#endif // DEBUG
SerializerReferenceMap* reference_map() { return &reference_map_; }
const RootIndexMap* root_index_map() const { return &root_index_map_; }
SnapshotByteSink sink_; // Used directly by subclasses.
bool allow_unknown_external_references_for_testing() const {
return (flags_ & Snapshot::kAllowUnknownExternalReferencesForTesting) != 0;
}
bool allow_active_isolate_for_testing() const {
return (flags_ & Snapshot::kAllowActiveIsolateForTesting) != 0;
}
bool reconstruct_read_only_and_shared_object_caches_for_testing() const {
return (flags_ &
Snapshot::kReconstructReadOnlyAndSharedObjectCachesForTesting) != 0;
}
bool deferred_objects_empty() { return deferred_objects_.size() == 0; }
protected:
bool serializer_tracks_serialization_statistics() const {
return serializer_tracks_serialization_statistics_;
}
void set_serializer_tracks_serialization_statistics(bool v) {
serializer_tracks_serialization_statistics_ = v;
}
private:
// A circular queue of hot objects. This is added to in the same order as in
// Deserializer::HotObjectsList, but this stores the objects as an array of
// raw addresses that are considered strong roots. This allows objects to be
// added to the list without having to extend their handle's lifetime.
//
// We should never allow this class to return Handles to objects in the queue,
// as the object in the queue may change if kSize other objects are added to
// the queue during that Handle's lifetime.
class HotObjectsList {
public:
explicit HotObjectsList(Heap* heap);
~HotObjectsList();
HotObjectsList(const HotObjectsList&) = delete;
HotObjectsList& operator=(const HotObjectsList&) = delete;
void Add(Tagged<HeapObject> object) {
circular_queue_[index_] = object.ptr();
index_ = (index_ + 1) & kSizeMask;
}
static const int kNotFound = -1;
int Find(Tagged<HeapObject> object) {
DCHECK(!AllowGarbageCollection::IsAllowed());
for (int i = 0; i < kSize; i++) {
if (circular_queue_[i] == object.ptr()) {
return i;
}
}
return kNotFound;
}
private:
static const int kSize = kHotObjectCount;
static const int kSizeMask = kSize - 1;
static_assert(base::bits::IsPowerOfTwo(kSize));
Heap* heap_;
StrongRootsEntry* strong_roots_entry_;
Address circular_queue_[kSize] = {kNullAddress};
int index_ = 0;
};
// Disallow GC during serialization.
// TODO(leszeks, v8:10815): Remove this constraint.
DISALLOW_GARBAGE_COLLECTION(no_gc_)
Isolate* isolate_;
#if V8_COMPRESS_POINTERS
const PtrComprCageBase cage_base_;
#endif // V8_COMPRESS_POINTERS
HotObjectsList hot_objects_;
SerializerReferenceMap reference_map_;
ExternalReferenceEncoder external_reference_encoder_;
RootIndexMap root_index_map_;
std::unique_ptr<CodeAddressMap> code_address_map_;
std::vector<uint8_t> code_buffer_;
GlobalHandleVector<HeapObject>
deferred_objects_; // To handle stack overflow.
int num_back_refs_ = 0;
// Used to provide deterministic IDs to the serialized dispatch handles.
std::unordered_map<JSDispatchHandle, uint32_t> dispatch_handle_map_;
// Objects which have started being serialized, but haven't yet been allocated
// with the allocator, are considered "pending". References to them don't have
// an allocation to backref to, so instead they are registered as pending
// forward references, which are resolved once the object is allocated.
//
// Forward references are registered in a deterministic order, and can
// therefore be identified by an incrementing integer index, which is
// effectively an index into a vector of the currently registered forward
// refs. The references in this vector might not be resolved in order, so we
// can only clear it (and reset the indices) when there are no unresolved
// forward refs remaining.
int next_forward_ref_id_ = 0;
int unresolved_forward_refs_ = 0;
IdentityMap<PendingObjectReferences, base::DefaultAllocationPolicy>
forward_refs_per_pending_object_;
// Used to keep track of the off-heap backing stores used by TypedArrays/
// ArrayBuffers. Note that the index begins at 1 and not 0, because when a
// TypedArray has an on-heap backing store, the backing_store pointer in the
// corresponding ArrayBuffer will be null, which makes it indistinguishable
// from index 0.
uint32_t seen_backing_stores_index_ = 1;
int recursion_depth_ = 0;
const Snapshot::SerializerFlags flags_;
bool serializer_tracks_serialization_statistics_ = true;
size_t allocation_size_[kNumberOfSnapshotSpaces] = {0};
#ifdef OBJECT_PRINT
// Verbose serialization_statistics output is only enabled conditionally.
#define VERBOSE_SERIALIZATION_STATISTICS
#endif
#ifdef VERBOSE_SERIALIZATION_STATISTICS
static constexpr int kInstanceTypes = LAST_TYPE + 1;
std::unique_ptr<int[]> instance_type_count_[kNumberOfSnapshotSpaces];
std::unique_ptr<size_t[]> instance_type_size_[kNumberOfSnapshotSpaces];
#endif // VERBOSE_SERIALIZATION_STATISTICS
#ifdef DEBUG
GlobalHandleVector<HeapObject> back_refs_;
GlobalHandleVector<HeapObject> stack_;
#endif // DEBUG
};
class Serializer::ObjectSerializer : public ObjectVisitor {
public:
ObjectSerializer(Serializer* serializer, Handle<HeapObject> obj,
SnapshotByteSink* sink)
: isolate_(serializer->isolate()),
serializer_(serializer),
object_(obj),
sink_(sink),
bytes_processed_so_far_(0) {
#ifdef DEBUG
serializer_->PushStack(obj);
#endif // DEBUG
}
~ObjectSerializer() override {
#ifdef DEBUG
serializer_->PopStack();
#endif // DEBUG
}
void Serialize(SlotType slot_type);
void SerializeObject();
void SerializeDeferred();
void VisitPointers(Tagged<HeapObject> host, ObjectSlot start,
ObjectSlot end) override;
void VisitPointers(Tagged<HeapObject> host, MaybeObjectSlot start,
MaybeObjectSlot end) override;
void VisitInstructionStreamPointer(Tagged<Code> host,
InstructionStreamSlot slot) override;
void VisitEmbeddedPointer(Tagged<InstructionStream> host,
RelocInfo* target) override;
void VisitExternalReference(Tagged<InstructionStream> host,
RelocInfo* rinfo) override;
void VisitInternalReference(Tagged<InstructionStream> host,
RelocInfo* rinfo) override;
void VisitCodeTarget(Tagged<InstructionStream> host,
RelocInfo* target) override;
void VisitOffHeapTarget(Tagged<InstructionStream> host,
RelocInfo* target) override;
void VisitExternalPointer(Tagged<HeapObject> host,
ExternalPointerSlot slot) override;
void VisitIndirectPointer(Tagged<HeapObject> host, IndirectPointerSlot slot,
IndirectPointerMode mode) override;
void VisitTrustedPointerTableEntry(Tagged<HeapObject> host,
IndirectPointerSlot slot) override;
void VisitProtectedPointer(Tagged<TrustedObject> host,
ProtectedPointerSlot slot) override;
void VisitProtectedPointer(Tagged<TrustedObject> host,
ProtectedMaybeObjectSlot slot) override;
void VisitCppHeapPointer(Tagged<HeapObject> host,
CppHeapPointerSlot slot) override;
void VisitJSDispatchTableEntry(Tagged<HeapObject> host,
JSDispatchHandle handle) override;
Isolate* isolate() { return isolate_; }
private:
void SerializePrologue(SnapshotSpace space, int size, Tagged<Map> map);
// This function outputs or skips the raw data between the last pointer and
// up to the current position.
void SerializeContent(Tagged<Map> map, int size);
void OutputExternalReference(Address target, int target_size, bool sandboxify,
ExternalPointerTag tag);
void OutputRawData(Address up_to);
uint32_t SerializeBackingStore(void* backing_store, uint32_t byte_length,
Maybe<uint32_t> max_byte_length);
void SerializeJSTypedArray();
void SerializeJSArrayBuffer();
void SerializeExternalString();
void SerializeExternalStringAsSequentialString();
Isolate* isolate_;
Serializer* serializer_;
Handle<HeapObject> object_;
SnapshotByteSink* sink_;
int bytes_processed_so_far_;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_SERIALIZER_H_

View File

@ -0,0 +1,60 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/shared-heap-deserializer.h"
#include "src/heap/heap-inl.h"
namespace v8 {
namespace internal {
void SharedHeapDeserializer::DeserializeIntoIsolate() {
// Don't deserialize into isolates that don't own their string table. If there
// are client Isolates, the shared heap object cache should already be
// populated.
// TODO(372493838): The shared heap object cache can only contain strings.
// Update name to reflect this.
if (!isolate()->OwnsStringTables()) {
DCHECK(!isolate()->shared_heap_object_cache()->empty());
return;
}
DCHECK(isolate()->shared_heap_object_cache()->empty());
HandleScope scope(isolate());
IterateSharedHeapObjectCache(isolate(), this);
DeserializeStringTable();
DeserializeDeferredObjects();
if (should_rehash()) {
// The hash seed has already been initialized in ReadOnlyDeserializer, thus
// there is no need to call `isolate()->heap()->InitializeHashSeed();`.
Rehash();
}
}
void SharedHeapDeserializer::DeserializeStringTable() {
// See SharedHeapSerializer::SerializeStringTable.
DCHECK(isolate()->OwnsStringTables());
// Get the string table size.
const int length = source()->GetUint30();
// .. and the contents.
DirectHandleVector<String> strings(isolate());
strings.reserve(length);
for (int i = 0; i < length; ++i) {
strings.emplace_back(Cast<String>(ReadObject()));
}
StringTable* t = isolate()->string_table();
DCHECK_EQ(t->NumberOfElements(), 0);
t->InsertForIsolateDeserialization(
isolate(), base::VectorOf(strings.data(), strings.size()));
DCHECK_EQ(t->NumberOfElements(), length);
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,35 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_SHARED_HEAP_DESERIALIZER_H_
#define V8_SNAPSHOT_SHARED_HEAP_DESERIALIZER_H_
#include "src/snapshot/deserializer.h"
#include "src/snapshot/snapshot-data.h"
namespace v8 {
namespace internal {
// Initializes objects in the shared isolate that are not already included in
// the startup snapshot.
class SharedHeapDeserializer final : public Deserializer<Isolate> {
public:
explicit SharedHeapDeserializer(Isolate* isolate,
const SnapshotData* shared_heap_data,
bool can_rehash)
: Deserializer(isolate, shared_heap_data->Payload(),
shared_heap_data->GetMagicNumber(), false, can_rehash) {}
// Depending on runtime flags, deserialize shared heap objects into the
// Isolate.
void DeserializeIntoIsolate();
private:
void DeserializeStringTable();
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_SHARED_HEAP_DESERIALIZER_H_

View File

@ -0,0 +1,220 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/shared-heap-serializer.h"
#include "src/heap/read-only-heap.h"
#include "src/objects/objects-inl.h"
#include "src/snapshot/read-only-serializer.h"
namespace v8 {
namespace internal {
// static
bool SharedHeapSerializer::CanBeInSharedOldSpace(Tagged<HeapObject> obj) {
if (ReadOnlyHeap::Contains(obj)) return false;
if (IsString(obj)) {
return IsInternalizedString(obj) ||
String::IsInPlaceInternalizable(Cast<String>(obj));
}
return false;
}
// static
bool SharedHeapSerializer::ShouldBeInSharedHeapObjectCache(
Tagged<HeapObject> obj) {
// To keep the shared heap object cache lean, only include objects that should
// not be duplicated. Currently, that is only internalized strings. In-place
// internalizable strings will still be allocated in the shared heap by the
// deserializer, but do not need to be kept alive forever in the cache.
if (CanBeInSharedOldSpace(obj)) {
if (IsInternalizedString(obj)) return true;
}
return false;
}
SharedHeapSerializer::SharedHeapSerializer(Isolate* isolate,
Snapshot::SerializerFlags flags)
: RootsSerializer(isolate, flags, RootIndex::kFirstStrongRoot)
#ifdef DEBUG
,
serialized_objects_(isolate->heap())
#endif
{
if (ShouldReconstructSharedHeapObjectCacheForTesting()) {
ReconstructSharedHeapObjectCacheForTesting();
}
}
SharedHeapSerializer::~SharedHeapSerializer() {
OutputStatistics("SharedHeapSerializer");
}
void SharedHeapSerializer::FinalizeSerialization() {
// This is called after serialization of the startup and context snapshots
// which entries are added to the shared heap object cache. Terminate the
// cache with an undefined.
Tagged<Object> undefined = ReadOnlyRoots(isolate()).undefined_value();
VisitRootPointer(Root::kSharedHeapObjectCache, nullptr,
FullObjectSlot(&undefined));
// When v8_flags.shared_string_table is true, all internalized and
// internalizable-in-place strings are in the shared heap.
SerializeStringTable(isolate()->string_table());
SerializeDeferredObjects();
Pad();
#ifdef DEBUG
// Check that all serialized object are in shared heap and not RO. RO objects
// should be in the RO snapshot.
IdentityMap<int, base::DefaultAllocationPolicy>::IteratableScope it_scope(
&serialized_objects_);
for (auto it = it_scope.begin(); it != it_scope.end(); ++it) {
Tagged<HeapObject> obj = Cast<HeapObject>(it.key());
CHECK(CanBeInSharedOldSpace(obj));
CHECK(!ReadOnlyHeap::Contains(obj));
}
#endif
}
bool SharedHeapSerializer::SerializeUsingSharedHeapObjectCache(
SnapshotByteSink* sink, Handle<HeapObject> obj) {
if (!ShouldBeInSharedHeapObjectCache(*obj)) return false;
int cache_index = SerializeInObjectCache(obj);
// When testing deserialization of a snapshot from a live Isolate where there
// is also a shared Isolate, the shared object cache needs to be extended
// because the live isolate may have had new internalized strings that were
// not present in the startup snapshot to be serialized.
if (ShouldReconstructSharedHeapObjectCacheForTesting()) {
std::vector<Tagged<Object>>* existing_cache =
isolate()->shared_space_isolate()->shared_heap_object_cache();
const size_t existing_cache_size = existing_cache->size();
// This is strictly < because the existing cache contains the terminating
// undefined value, which the reconstructed cache does not.
DCHECK_LT(base::checked_cast<size_t>(cache_index), existing_cache_size);
if (base::checked_cast<size_t>(cache_index) == existing_cache_size - 1) {
ReadOnlyRoots roots(isolate());
DCHECK(IsUndefined(existing_cache->back(), roots));
existing_cache->back() = *obj;
existing_cache->push_back(roots.undefined_value());
}
}
sink->Put(kSharedHeapObjectCache, "SharedHeapObjectCache");
sink->PutUint30(cache_index, "shared_heap_object_cache_index");
return true;
}
void SharedHeapSerializer::SerializeStringTable(StringTable* string_table) {
// A StringTable is serialized as:
//
// N : int
// string 1
// string 2
// ...
// string N
//
// Notably, the hashmap structure, including empty and deleted elements, is
// not serialized.
sink_.PutUint30(string_table->NumberOfElements(),
"String table number of elements");
// Custom RootVisitor which walks the string table, but only serializes the
// string entries. This is an inline class to be able to access the non-public
// SerializeObject method.
class SharedHeapSerializerStringTableVisitor : public RootVisitor {
public:
explicit SharedHeapSerializerStringTableVisitor(
SharedHeapSerializer* serializer)
: serializer_(serializer) {}
void VisitRootPointers(Root root, const char* description,
FullObjectSlot start, FullObjectSlot end) override {
UNREACHABLE();
}
void VisitRootPointers(Root root, const char* description,
OffHeapObjectSlot start,
OffHeapObjectSlot end) override {
DCHECK_EQ(root, Root::kStringTable);
Isolate* isolate = serializer_->isolate();
for (OffHeapObjectSlot current = start; current < end; ++current) {
Tagged<Object> obj = current.load(isolate);
if (IsHeapObject(obj)) {
DCHECK(IsInternalizedString(obj));
serializer_->SerializeObject(handle(Cast<HeapObject>(obj), isolate),
SlotType::kAnySlot);
}
}
}
private:
SharedHeapSerializer* serializer_;
};
SharedHeapSerializerStringTableVisitor string_table_visitor(this);
isolate()->string_table()->IterateElements(&string_table_visitor);
}
void SharedHeapSerializer::SerializeObjectImpl(Handle<HeapObject> obj,
SlotType slot_type) {
// Objects in the shared heap cannot depend on per-Isolate roots but can
// depend on RO roots since sharing objects requires sharing the RO space.
DCHECK(CanBeInSharedOldSpace(*obj) || ReadOnlyHeap::Contains(*obj));
{
DisallowGarbageCollection no_gc;
Tagged<HeapObject> raw = *obj;
if (SerializeHotObject(raw)) return;
if (IsRootAndHasBeenSerialized(raw) && SerializeRoot(raw)) return;
}
if (SerializeReadOnlyObjectReference(*obj, &sink_)) return;
{
DisallowGarbageCollection no_gc;
Tagged<HeapObject> raw = *obj;
if (SerializeBackReference(raw)) return;
CheckRehashability(raw);
DCHECK(!ReadOnlyHeap::Contains(raw));
}
ObjectSerializer object_serializer(this, obj, &sink_);
object_serializer.Serialize(slot_type);
#ifdef DEBUG
CHECK_NULL(serialized_objects_.Find(obj));
// There's no "IdentitySet", so use an IdentityMap with a value that is
// later ignored.
serialized_objects_.Insert(obj, 0);
#endif
}
bool SharedHeapSerializer::ShouldReconstructSharedHeapObjectCacheForTesting()
const {
// When the live Isolate being serialized is not a client Isolate, there's no
// need to reconstruct the shared heap object cache because it is not actually
// shared.
return reconstruct_read_only_and_shared_object_caches_for_testing() &&
isolate()->has_shared_space();
}
void SharedHeapSerializer::ReconstructSharedHeapObjectCacheForTesting() {
std::vector<Tagged<Object>>* cache =
isolate()->shared_space_isolate()->shared_heap_object_cache();
// Don't reconstruct the final element, which is always undefined and marks
// the end of the cache, since serializing the live Isolate may extend the
// shared object cache.
for (size_t i = 0, size = cache->size(); i < size - 1; i++) {
Handle<HeapObject> obj(Cast<HeapObject>(cache->at(i)), isolate());
DCHECK(ShouldBeInSharedHeapObjectCache(*obj));
int cache_index = SerializeInObjectCache(obj);
USE(cache_index);
DCHECK_EQ(cache_index, i);
}
DCHECK(IsUndefined(cache->back(), isolate()));
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,58 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_SHARED_HEAP_SERIALIZER_H_
#define V8_SNAPSHOT_SHARED_HEAP_SERIALIZER_H_
#include "src/snapshot/roots-serializer.h"
namespace v8 {
namespace internal {
class HeapObject;
// SharedHeapSerializer serializes objects that should be in the shared heap in
// the shared Isolate during startup. Currently the shared heap is only in use
// behind flags (e.g. --shared-string-table). When it is not in use, its
// contents are deserialized into each Isolate.
class V8_EXPORT_PRIVATE SharedHeapSerializer : public RootsSerializer {
public:
SharedHeapSerializer(Isolate* isolate, Snapshot::SerializerFlags flags);
~SharedHeapSerializer() override;
SharedHeapSerializer(const SharedHeapSerializer&) = delete;
SharedHeapSerializer& operator=(const SharedHeapSerializer&) = delete;
// Terminate the shared heap object cache with an undefined value and
// serialize the string table..
void FinalizeSerialization();
// If |obj| can be serialized in the shared heap snapshot then add it to the
// shared heap object cache if not already present and emit a
// SharedHeapObjectCache bytecode into |sink|. Returns whether this was
// successful.
bool SerializeUsingSharedHeapObjectCache(SnapshotByteSink* sink,
Handle<HeapObject> obj);
static bool CanBeInSharedOldSpace(Tagged<HeapObject> obj);
static bool ShouldBeInSharedHeapObjectCache(Tagged<HeapObject> obj);
private:
bool ShouldReconstructSharedHeapObjectCacheForTesting() const;
void ReconstructSharedHeapObjectCacheForTesting();
void SerializeStringTable(StringTable* string_table);
void SerializeObjectImpl(Handle<HeapObject> obj, SlotType slot_type) override;
#ifdef DEBUG
IdentityMap<int, base::DefaultAllocationPolicy> serialized_objects_;
#endif
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_SHARED_HEAP_SERIALIZER_H_

View File

@ -0,0 +1,100 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/snapshot-compression.h"
#include "src/base/platform/elapsed-timer.h"
#include "src/utils/memcopy.h"
#include "src/utils/utils.h"
#include "third_party/zlib/google/compression_utils_portable.h"
namespace v8 {
namespace internal {
uint32_t GetUncompressedSize(const Bytef* compressed_data) {
uint32_t size;
MemCopy(&size, compressed_data, sizeof(size));
return size;
}
SnapshotData SnapshotCompression::Compress(
const SnapshotData* uncompressed_data) {
SnapshotData snapshot_data;
base::ElapsedTimer timer;
if (v8_flags.profile_deserialization) timer.Start();
static_assert(sizeof(Bytef) == 1, "");
const uLongf input_size =
static_cast<uLongf>(uncompressed_data->RawData().size());
uint32_t payload_length =
static_cast<uint32_t>(uncompressed_data->RawData().size());
uLongf compressed_data_size = compressBound(input_size);
// Allocating >= the final amount we will need.
snapshot_data.AllocateData(
static_cast<uint32_t>(sizeof(payload_length) + compressed_data_size));
uint8_t* compressed_data =
const_cast<uint8_t*>(snapshot_data.RawData().begin());
// Since we are doing raw compression (no zlib or gzip headers), we need to
// manually store the uncompressed size.
MemCopy(compressed_data, &payload_length, sizeof(payload_length));
CHECK_EQ(
zlib_internal::CompressHelper(
zlib_internal::ZRAW, compressed_data + sizeof(payload_length),
&compressed_data_size,
reinterpret_cast<const Bytef*>(uncompressed_data->RawData().begin()),
input_size, Z_DEFAULT_COMPRESSION, nullptr, nullptr),
Z_OK);
// Reallocating to exactly the size we need.
snapshot_data.Resize(static_cast<uint32_t>(compressed_data_size) +
sizeof(payload_length));
DCHECK_EQ(payload_length,
GetUncompressedSize(snapshot_data.RawData().begin()));
if (v8_flags.profile_deserialization) {
double ms = timer.Elapsed().InMillisecondsF();
PrintF("[Compressing %d bytes took %0.3f ms]\n", payload_length, ms);
}
return snapshot_data;
}
SnapshotData SnapshotCompression::Decompress(
base::Vector<const uint8_t> compressed_data) {
SnapshotData snapshot_data;
base::ElapsedTimer timer;
if (v8_flags.profile_deserialization) timer.Start();
const Bytef* input_bytef =
reinterpret_cast<const Bytef*>(compressed_data.begin());
// Since we are doing raw compression (no zlib or gzip headers), we need to
// manually retrieve the uncompressed size.
uint32_t uncompressed_payload_length = GetUncompressedSize(input_bytef);
input_bytef += sizeof(uncompressed_payload_length);
snapshot_data.AllocateData(uncompressed_payload_length);
uLongf uncompressed_size = uncompressed_payload_length;
CHECK_EQ(zlib_internal::UncompressHelper(
zlib_internal::ZRAW,
const_cast<Bytef*>(snapshot_data.RawData().begin()),
&uncompressed_size, input_bytef,
static_cast<uLong>(compressed_data.size() -
sizeof(uncompressed_payload_length))),
Z_OK);
if (v8_flags.profile_deserialization) {
double ms = timer.Elapsed().InMillisecondsF();
PrintF("[Decompressing %d bytes took %0.3f ms]\n",
uncompressed_payload_length, ms);
}
return snapshot_data;
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,25 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_SNAPSHOT_COMPRESSION_H_
#define V8_SNAPSHOT_SNAPSHOT_COMPRESSION_H_
#include "src/base/vector.h"
#include "src/snapshot/snapshot-data.h"
namespace v8 {
namespace internal {
class SnapshotCompression : public AllStatic {
public:
V8_EXPORT_PRIVATE static SnapshotData Compress(
const SnapshotData* uncompressed_data);
V8_EXPORT_PRIVATE static SnapshotData Decompress(
base::Vector<const uint8_t> compressed_data);
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_SNAPSHOT_COMPRESSION_H_

53
deps/v8/src/snapshot/snapshot-data.cc vendored Normal file
View File

@ -0,0 +1,53 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/snapshot-data.h"
#include "src/common/assert-scope.h"
#include "src/snapshot/serializer.h"
namespace v8 {
namespace internal {
void SerializedData::AllocateData(uint32_t size) {
DCHECK(!owns_data_);
data_ = NewArray<uint8_t>(size);
size_ = size;
owns_data_ = true;
}
// static
constexpr uint32_t SerializedData::kMagicNumber;
SnapshotData::SnapshotData(const Serializer* serializer) {
DisallowGarbageCollection no_gc;
const std::vector<uint8_t>* payload = serializer->Payload();
// Calculate sizes.
uint32_t size = kHeaderSize + static_cast<uint32_t>(payload->size());
// Allocate backing store and create result data.
AllocateData(size);
// Zero out pre-payload data. Part of that is only used for padding.
memset(data_, 0, kHeaderSize);
// Set header values.
SetMagicNumber();
SetHeaderValue(kPayloadLengthOffset, static_cast<int>(payload->size()));
// Copy serialized data.
CopyBytes(data_ + kHeaderSize, payload->data(),
static_cast<size_t>(payload->size()));
}
base::Vector<const uint8_t> SnapshotData::Payload() const {
const uint8_t* payload = data_ + kHeaderSize;
uint32_t length = GetHeaderValue(kPayloadLengthOffset);
DCHECK_EQ(data_ + size_, payload + length);
return base::Vector<const uint8_t>(payload, length);
}
} // namespace internal
} // namespace v8

107
deps/v8/src/snapshot/snapshot-data.h vendored Normal file
View File

@ -0,0 +1,107 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_SNAPSHOT_DATA_H_
#define V8_SNAPSHOT_SNAPSHOT_DATA_H_
#include "src/base/bit-field.h"
#include "src/base/memory.h"
#include "src/base/vector.h"
#include "src/codegen/external-reference-table.h"
#include "src/utils/memcopy.h"
namespace v8 {
namespace internal {
// Forward declarations.
class Isolate;
class Serializer;
class SerializedData {
public:
SerializedData(uint8_t* data, int size)
: data_(data), size_(size), owns_data_(false) {}
SerializedData() : data_(nullptr), size_(0), owns_data_(false) {}
SerializedData(SerializedData&& other) V8_NOEXCEPT
: data_(other.data_),
size_(other.size_),
owns_data_(other.owns_data_) {
// Ensure |other| will not attempt to destroy our data in destructor.
other.owns_data_ = false;
}
SerializedData(const SerializedData&) = delete;
SerializedData& operator=(const SerializedData&) = delete;
virtual ~SerializedData() {
if (owns_data_) DeleteArray<uint8_t>(data_);
}
uint32_t GetMagicNumber() const { return GetHeaderValue(kMagicNumberOffset); }
using ChunkSizeBits = base::BitField<uint32_t, 0, 31>;
using IsLastChunkBits = base::BitField<bool, 31, 1>;
static constexpr uint32_t kMagicNumberOffset = 0;
static constexpr uint32_t kMagicNumber =
0xC0DE0000 ^ ExternalReferenceTable::kSize;
protected:
void SetHeaderValue(uint32_t offset, uint32_t value) {
base::WriteLittleEndianValue(reinterpret_cast<Address>(data_) + offset,
value);
}
uint32_t GetHeaderValue(uint32_t offset) const {
return base::ReadLittleEndianValue<uint32_t>(
reinterpret_cast<Address>(data_) + offset);
}
void AllocateData(uint32_t size);
void SetMagicNumber() { SetHeaderValue(kMagicNumberOffset, kMagicNumber); }
uint8_t* data_;
uint32_t size_;
bool owns_data_;
};
// Wrapper around reservation sizes and the serialization payload.
class V8_EXPORT_PRIVATE SnapshotData : public SerializedData {
public:
// Used when producing.
explicit SnapshotData(const Serializer* serializer);
// Used when consuming.
explicit SnapshotData(const base::Vector<const uint8_t> snapshot)
: SerializedData(const_cast<uint8_t*>(snapshot.begin()),
snapshot.length()) {}
virtual base::Vector<const uint8_t> Payload() const;
base::Vector<const uint8_t> RawData() const {
return base::Vector<const uint8_t>(data_, size_);
}
protected:
// Empty constructor used by SnapshotCompression so it can manually allocate
// memory.
SnapshotData() : SerializedData() {}
friend class SnapshotCompression;
// Resize used by SnapshotCompression so it can shrink the compressed
// SnapshotData.
void Resize(uint32_t size) { size_ = size; }
// The data header consists of uint32_t-sized entries:
// [0] magic number and (internal) external reference count
// [1] payload length
// ... serialized payload
static const uint32_t kPayloadLengthOffset = kMagicNumberOffset + kUInt32Size;
static const uint32_t kHeaderSize = kPayloadLengthOffset + kUInt32Size;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_SNAPSHOT_DATA_H_

30
deps/v8/src/snapshot/snapshot-empty.cc vendored Normal file
View File

@ -0,0 +1,30 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Used for building without snapshots.
#include "src/snapshot/snapshot.h"
namespace v8 {
namespace internal {
#ifdef V8_USE_EXTERNAL_STARTUP_DATA
// Dummy implementations of Set*FromFile(..) APIs.
//
// These are meant for use with snapshot-external.cc. Should this file
// be compiled with those options we just supply these dummy implementations
// below. This happens when compiling the mksnapshot utility.
void SetNativesFromFile(StartupData* data) { UNREACHABLE(); }
void SetSnapshotFromFile(StartupData* data) { UNREACHABLE(); }
void ReadNatives() {}
void DisposeNatives() {}
#endif // V8_USE_EXTERNAL_STARTUP_DATA
const v8::StartupData* Snapshot::DefaultSnapshotBlob() { return nullptr; }
bool Snapshot::ShouldVerifyChecksum(const v8::StartupData* data) {
return false;
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,61 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Used for building with external snapshots.
#include "src/base/platform/mutex.h"
#include "src/flags/flags.h"
#include "src/init/v8.h" // for V8::Initialize
#include "src/snapshot/snapshot-source-sink.h"
#include "src/snapshot/snapshot.h"
#ifndef V8_USE_EXTERNAL_STARTUP_DATA
#error snapshot-external.cc is used only for the external snapshot build.
#endif // V8_USE_EXTERNAL_STARTUP_DATA
namespace v8 {
namespace internal {
static base::LazyMutex external_startup_data_mutex = LAZY_MUTEX_INITIALIZER;
static v8::StartupData external_startup_blob = {nullptr, 0};
#ifdef V8_TARGET_OS_ANDROID
static bool external_startup_checksum_verified = false;
#endif
void SetSnapshotFromFile(StartupData* snapshot_blob) {
base::MutexGuard lock_guard(external_startup_data_mutex.Pointer());
DCHECK(snapshot_blob);
DCHECK(snapshot_blob->data);
DCHECK_GT(snapshot_blob->raw_size, 0);
DCHECK(!external_startup_blob.data);
DCHECK(Snapshot::SnapshotIsValid(snapshot_blob));
external_startup_blob = *snapshot_blob;
#ifdef V8_TARGET_OS_ANDROID
external_startup_checksum_verified = false;
#endif
}
bool Snapshot::ShouldVerifyChecksum(const v8::StartupData* data) {
#ifdef V8_TARGET_OS_ANDROID
base::MutexGuard lock_guard(external_startup_data_mutex.Pointer());
if (data != &external_startup_blob) {
return v8_flags.verify_snapshot_checksum;
}
// Verify the external snapshot maximally once per process due to the
// additional overhead.
if (external_startup_checksum_verified) return false;
external_startup_checksum_verified = true;
return true;
#else
return v8_flags.verify_snapshot_checksum;
#endif
}
const v8::StartupData* Snapshot::DefaultSnapshotBlob() {
base::MutexGuard lock_guard(external_startup_data_mutex.Pointer());
return &external_startup_blob;
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,55 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/snapshot-source-sink.h"
#include <vector>
#include "src/base/logging.h"
#include "src/handles/handles-inl.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
void SnapshotByteSink::PutN(int number_of_bytes, const uint8_t v,
const char* description) {
data_.insert(data_.end(), number_of_bytes, v);
}
void SnapshotByteSink::PutUint30(uint32_t integer, const char* description) {
CHECK_LT(integer, 1UL << 30);
integer <<= 2;
int bytes = 1;
if (integer > 0xFF) bytes = 2;
if (integer > 0xFFFF) bytes = 3;
if (integer > 0xFFFFFF) bytes = 4;
integer |= (bytes - 1);
Put(static_cast<uint8_t>(integer & 0xFF), "IntPart1");
if (bytes > 1) Put(static_cast<uint8_t>((integer >> 8) & 0xFF), "IntPart2");
if (bytes > 2) Put(static_cast<uint8_t>((integer >> 16) & 0xFF), "IntPart3");
if (bytes > 3) Put(static_cast<uint8_t>((integer >> 24) & 0xFF), "IntPart4");
}
void SnapshotByteSink::PutRaw(const uint8_t* data, int number_of_bytes,
const char* description) {
#ifdef MEMORY_SANITIZER
__msan_check_mem_is_initialized(data, number_of_bytes);
#endif
data_.insert(data_.end(), data, data + number_of_bytes);
}
void SnapshotByteSink::Append(const SnapshotByteSink& other) {
data_.insert(data_.end(), other.data_.begin(), other.data_.end());
}
int SnapshotByteSource::GetBlob(const uint8_t** data) {
int size = GetUint30();
CHECK_LE(position_ + size, length_);
*data = &data_[position_];
Advance(size);
return size;
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,158 @@
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_SNAPSHOT_SOURCE_SINK_H_
#define V8_SNAPSHOT_SNAPSHOT_SOURCE_SINK_H_
#include <utility>
#include <vector>
#include "src/base/atomicops.h"
#include "src/base/logging.h"
#include "src/common/globals.h"
#include "src/utils/utils.h"
namespace v8 {
namespace internal {
/**
* Source to read snapshot and builtins files from.
*
* Note: Memory ownership remains with callee.
*/
class SnapshotByteSource final {
public:
SnapshotByteSource(const char* data, int length)
: data_(reinterpret_cast<const uint8_t*>(data)),
length_(length),
position_(0) {}
explicit SnapshotByteSource(base::Vector<const uint8_t> payload)
: data_(payload.begin()), length_(payload.length()), position_(0) {}
~SnapshotByteSource() = default;
SnapshotByteSource(const SnapshotByteSource&) = delete;
SnapshotByteSource& operator=(const SnapshotByteSource&) = delete;
bool HasMore() { return position_ < length_; }
uint8_t Get() {
DCHECK(position_ < length_);
return data_[position_++];
}
uint8_t Peek() const {
DCHECK(position_ < length_);
return data_[position_];
}
void Advance(int by) { position_ += by; }
void CopyRaw(void* to, int number_of_bytes) {
DCHECK_LE(position_ + number_of_bytes, length_);
memcpy(to, data_ + position_, number_of_bytes);
position_ += number_of_bytes;
}
void CopySlots(Address* dest, int number_of_slots) {
base::AtomicWord* start = reinterpret_cast<base::AtomicWord*>(dest);
base::AtomicWord* end = start + number_of_slots;
for (base::AtomicWord* p = start; p < end;
++p, position_ += sizeof(base::AtomicWord)) {
base::AtomicWord val;
memcpy(&val, data_ + position_, sizeof(base::AtomicWord));
base::Relaxed_Store(p, val);
}
}
#ifdef V8_COMPRESS_POINTERS
void CopySlots(Tagged_t* dest, int number_of_slots) {
AtomicTagged_t* start = reinterpret_cast<AtomicTagged_t*>(dest);
AtomicTagged_t* end = start + number_of_slots;
for (AtomicTagged_t* p = start; p < end;
++p, position_ += sizeof(AtomicTagged_t)) {
AtomicTagged_t val;
memcpy(&val, data_ + position_, sizeof(AtomicTagged_t));
base::Relaxed_Store(p, val);
}
}
#endif
// Decode a uint30 with run-length encoding. Must have been encoded with
// PutUint30.
inline uint32_t GetUint30() {
// This way of decoding variable-length encoded integers does not
// suffer from branch mispredictions.
DCHECK_LT(position_ + 3, length_);
uint32_t answer = data_[position_];
answer |= data_[position_ + 1] << 8;
answer |= data_[position_ + 2] << 16;
answer |= data_[position_ + 3] << 24;
int bytes = (answer & 3) + 1;
Advance(bytes);
uint32_t mask = 0xffffffffu;
mask >>= 32 - (bytes << 3);
answer &= mask;
answer >>= 2;
return answer;
}
uint32_t GetUint32() {
uint32_t integer;
CopyRaw(reinterpret_cast<uint8_t*>(&integer), sizeof(integer));
return integer;
}
// Returns length.
int GetBlob(const uint8_t** data);
int position() const { return position_; }
void set_position(int position) { position_ = position; }
const uint8_t* data() const { return data_; }
int length() const { return length_; }
private:
const uint8_t* data_;
int length_;
int position_;
};
/**
* Sink to write snapshot files to.
*
* Users must implement actual storage or i/o.
*/
class SnapshotByteSink {
public:
SnapshotByteSink() = default;
explicit SnapshotByteSink(int initial_size) : data_(initial_size) {}
~SnapshotByteSink() = default;
void Put(uint8_t b, const char* description) { data_.push_back(b); }
void PutN(int number_of_bytes, const uint8_t v, const char* description);
// Append a uint30 with run-length encoding. Must be decoded with GetUint30.
void PutUint30(uint32_t integer, const char* description);
void PutUint32(uint32_t integer, const char* description) {
PutRaw(reinterpret_cast<uint8_t*>(&integer), sizeof(integer), description);
}
void PutRaw(const uint8_t* data, int number_of_bytes,
const char* description);
void Append(const SnapshotByteSink& other);
int Position() const { return static_cast<int>(data_.size()); }
const std::vector<uint8_t>* data() const { return &data_; }
private:
std::vector<uint8_t> data_;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_SNAPSHOT_SOURCE_SINK_H_

39
deps/v8/src/snapshot/snapshot-utils.cc vendored Normal file
View File

@ -0,0 +1,39 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/snapshot-utils.h"
#include "src/base/sanitizer/msan.h"
#ifdef V8_USE_ZLIB
#include "third_party/zlib/zlib.h"
#endif
namespace v8 {
namespace internal {
uint32_t Checksum(base::Vector<const uint8_t> payload) {
#ifdef MEMORY_SANITIZER
// Computing the checksum includes padding bytes for objects like strings.
// Mark every object as initialized in the code serializer.
MSAN_MEMORY_IS_INITIALIZED(payload.begin(), payload.length());
#endif // MEMORY_SANITIZER
#ifdef V8_USE_ZLIB
// Priming the adler32 call so it can see what CPU features are available.
adler32(0, nullptr, 0);
return static_cast<uint32_t>(adler32(0, payload.begin(), payload.length()));
#else
// Simple Fletcher-32.
uint32_t sum1 = 0, sum2 = 0;
for (auto data : payload) {
sum1 = (sum1 + data) % 65535;
sum2 = (sum2 + sum1) % 65535;
}
return (sum2 << 16 | sum1);
#endif
}
} // namespace internal
} // namespace v8

19
deps/v8/src/snapshot/snapshot-utils.h vendored Normal file
View File

@ -0,0 +1,19 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_SNAPSHOT_UTILS_H_
#define V8_SNAPSHOT_SNAPSHOT_UTILS_H_
#include "src/base/vector.h"
#include "src/common/globals.h"
namespace v8 {
namespace internal {
V8_EXPORT_PRIVATE uint32_t Checksum(base::Vector<const uint8_t> payload);
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_SNAPSHOT_UTILS_H_

1168
deps/v8/src/snapshot/snapshot.cc vendored Normal file

File diff suppressed because it is too large Load Diff

214
deps/v8/src/snapshot/snapshot.h vendored Normal file
View File

@ -0,0 +1,214 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_SNAPSHOT_H_
#define V8_SNAPSHOT_SNAPSHOT_H_
#include <vector>
#include "include/v8-array-buffer.h" // For ArrayBuffer::Allocator.
#include "include/v8-snapshot.h" // For StartupData.
#include "src/common/assert-scope.h"
#include "src/common/globals.h"
#include "src/snapshot/serializer-deserializer.h"
namespace v8 {
namespace internal {
class Context;
class Isolate;
class JSGlobalProxy;
class SafepointScope;
class SnapshotData;
class Snapshot : public AllStatic {
public:
// ---------------- Serialization -------------------------------------------
enum SerializerFlag {
// If set, serializes unknown external references as verbatim data. This
// usually leads to invalid state if the snapshot is deserialized in a
// different isolate or a different process.
// If unset, all external references must be known to the encoder.
kAllowUnknownExternalReferencesForTesting = 1 << 0,
// If set, the serializer enters a more permissive mode which allows
// serialization of a currently active, running isolate. This has multiple
// effects; for example, open handles are allowed, microtasks may exist,
// etc. Note that in this mode, the serializer is allowed to skip
// visitation of certain problematic areas even if they are non-empty. The
// resulting snapshot is not guaranteed to result in a runnable context
// after deserialization.
// If unset, we assert that these previously mentioned areas are empty.
kAllowActiveIsolateForTesting = 1 << 1,
// If set, the ReadOnlySerializer and the SharedHeapSerializer reconstructs
// their respective object caches from the existing ReadOnlyHeap's read-only
// object cache or the existing shared heap's object cache so the same
// mapping is used. This mode is used for testing deserialization of a
// snapshot from a live isolate that's using a shared ReadOnlyHeap or is
// attached to a shared isolate. Otherwise during deserialization the
// indices will mismatch, causing deserialization crashes when e.g. types
// mismatch. If unset, the read-only object cache is populated as read-only
// objects are serialized, and the shared heap object cache is populated as
// shared heap objects are serialized.
kReconstructReadOnlyAndSharedObjectCachesForTesting = 1 << 2,
};
using SerializerFlags = base::Flags<SerializerFlag>;
V8_EXPORT_PRIVATE static constexpr SerializerFlags kDefaultSerializerFlags =
{};
// In preparation for serialization, clear data from the given isolate's heap
// that 1. can be reconstructed and 2. is not suitable for serialization. The
// `clear_recompilable_data` flag controls whether compiled objects are
// cleared from shared function infos and regexp objects.
V8_EXPORT_PRIVATE static void ClearReconstructableDataForSerialization(
Isolate* isolate, bool clear_recompilable_data);
// Serializes the given isolate and contexts. Each context may have an
// associated callback to serialize internal fields. The default context must
// be passed at index 0.
static v8::StartupData Create(
Isolate* isolate, std::vector<Tagged<Context>>* contexts,
const std::vector<SerializeEmbedderFieldsCallback>&
embedder_fields_serializers,
const SafepointScope& safepoint_scope,
const DisallowGarbageCollection& no_gc,
SerializerFlags flags = kDefaultSerializerFlags);
// ---------------- Deserialization -----------------------------------------
// Initialize the Isolate from the internal snapshot. Returns false if no
// snapshot could be found.
static bool Initialize(Isolate* isolate);
// Create a new context using the internal context snapshot.
static MaybeDirectHandle<Context> NewContextFromSnapshot(
Isolate* isolate, DirectHandle<JSGlobalProxy> global_proxy,
size_t context_index,
DeserializeEmbedderFieldsCallback embedder_fields_deserializer);
// ---------------- Testing -------------------------------------------------
// This function is used to stress the snapshot component. It serializes the
// current isolate and context into a snapshot, deserializes the snapshot into
// a new isolate and context, and finally runs VerifyHeap on the fresh
// isolate.
V8_EXPORT_PRIVATE static void SerializeDeserializeAndVerifyForTesting(
Isolate* isolate, DirectHandle<Context> default_context);
// ---------------- Helper methods ------------------------------------------
static bool HasContextSnapshot(Isolate* isolate, size_t index);
static bool EmbedsScript(Isolate* isolate);
V8_EXPORT_PRIVATE static uint32_t GetExpectedChecksum(
const v8::StartupData* data);
V8_EXPORT_PRIVATE static uint32_t CalculateChecksum(
const v8::StartupData* data);
V8_EXPORT_PRIVATE static bool VerifyChecksum(const v8::StartupData* data);
static bool ExtractRehashability(const v8::StartupData* data);
V8_EXPORT_PRIVATE static uint32_t ExtractReadOnlySnapshotChecksum(
const v8::StartupData* data);
static bool VersionIsValid(const v8::StartupData* data);
// To be implemented by the snapshot source.
static const v8::StartupData* DefaultSnapshotBlob();
static bool ShouldVerifyChecksum(const v8::StartupData* data);
#ifdef DEBUG
static bool SnapshotIsValid(const v8::StartupData* snapshot_blob);
#endif // DEBUG
};
// Convenience wrapper around snapshot data blob creation used e.g. by tests.
V8_EXPORT_PRIVATE v8::StartupData CreateSnapshotDataBlobInternal(
v8::SnapshotCreator::FunctionCodeHandling function_code_handling,
const char* embedded_source = nullptr,
Snapshot::SerializerFlags serializer_flags =
Snapshot::kDefaultSerializerFlags);
// Convenience wrapper around snapshot data blob creation used e.g. by
// mksnapshot.
V8_EXPORT_PRIVATE v8::StartupData CreateSnapshotDataBlobInternal(
v8::SnapshotCreator::FunctionCodeHandling function_code_handling,
const char* embedded_source, v8::SnapshotCreator& snapshot_creator,
Snapshot::SerializerFlags serializer_flags =
Snapshot::kDefaultSerializerFlags);
// .. and for inspector-test.cc which needs an extern declaration due to
// restrictive include rules:
V8_EXPORT_PRIVATE v8::StartupData
CreateSnapshotDataBlobInternalForInspectorTest(
v8::SnapshotCreator::FunctionCodeHandling function_code_handling,
const char* embedded_source);
// Convenience wrapper around snapshot data blob warmup used e.g. by tests and
// mksnapshot.
V8_EXPORT_PRIVATE v8::StartupData WarmUpSnapshotDataBlobInternal(
v8::StartupData cold_snapshot_blob, const char* warmup_source);
#ifdef V8_USE_EXTERNAL_STARTUP_DATA
void SetSnapshotFromFile(StartupData* snapshot_blob);
#endif
// The implementation of the API-exposed class SnapshotCreator.
class SnapshotCreatorImpl final {
public:
// This ctor is used for internal usages:
// 1. %ProfileCreateSnapshotDataBlob(): Needs to hook into an existing
// Isolate.
//
// TODO(v8:14490): Refactor 1. to go through the public API and simplify this
// part of the internal snapshot creator.
SnapshotCreatorImpl(Isolate* isolate, const intptr_t* api_external_references,
const StartupData* existing_blob, bool owns_isolate);
explicit SnapshotCreatorImpl(const v8::Isolate::CreateParams& params);
SnapshotCreatorImpl(Isolate* isolate,
const v8::Isolate::CreateParams& params);
~SnapshotCreatorImpl();
Isolate* isolate() const { return isolate_; }
void SetDefaultContext(DirectHandle<NativeContext> context,
SerializeEmbedderFieldsCallback callback);
size_t AddContext(DirectHandle<NativeContext> context,
SerializeEmbedderFieldsCallback callback);
size_t AddData(DirectHandle<NativeContext> context, Address object);
size_t AddData(Address object);
StartupData CreateBlob(
SnapshotCreator::FunctionCodeHandling function_code_handling,
Snapshot::SerializerFlags serializer_flags =
Snapshot::kDefaultSerializerFlags);
static SnapshotCreatorImpl* FromSnapshotCreator(
v8::SnapshotCreator* snapshot_creator);
static constexpr size_t kDefaultContextIndex = 0;
static constexpr size_t kFirstAddtlContextIndex = kDefaultContextIndex + 1;
private:
struct SerializableContext {
SerializableContext() : handle_location(nullptr), callback(nullptr) {}
SerializableContext(Address* handle_location,
SerializeEmbedderFieldsCallback callback)
: handle_location(handle_location), callback(callback) {}
Address* handle_location = nullptr; // A GlobalHandle.
SerializeEmbedderFieldsCallback callback;
};
void InitInternal(const StartupData*);
DirectHandle<NativeContext> context_at(size_t i) const;
bool created() const { return contexts_.size() == 0; }
const bool owns_isolate_;
Isolate* const isolate_;
std::unique_ptr<v8::ArrayBuffer::Allocator> array_buffer_allocator_;
std::vector<SerializableContext> contexts_;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_SNAPSHOT_H_

333
deps/v8/src/snapshot/sort-builtins.cc vendored Normal file
View File

@ -0,0 +1,333 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sort-builtins.h"
#include <algorithm>
#include <fstream>
#include "src/snapshot/embedded/embedded-data-inl.h"
#include "src/snapshot/embedded/embedded-data.h"
namespace v8 {
namespace internal {
Cluster::Cluster(uint32_t density, uint32_t size, Builtin target,
BuiltinsSorter* sorter)
: density_(density), size_(size), sorter_(sorter) {
CHECK(size_);
targets_.push_back(target);
sorter_->builtin_cluster_map_[target] = this;
}
BuiltinsSorter::BuiltinsSorter() {}
BuiltinsSorter::~BuiltinsSorter() {
for (Cluster* cls : clusters_) {
delete cls;
}
}
void Cluster::Merge(Cluster* other) {
for (Builtin builtin : other->targets_) {
targets_.push_back(builtin);
sorter_->builtin_cluster_map_.emplace(builtin, this);
}
density_ = static_cast<uint32_t>(
(time_approximation() + other->time_approximation()) /
(size_ + other->size_));
size_ += other->size_;
other->density_ = 0;
other->size_ = 0;
other->targets_.clear();
}
uint64_t Cluster::time_approximation() {
return static_cast<uint64_t>(size_) * density_;
}
void BuiltinsSorter::InitializeClusters() {
for (uint32_t i = 0; i < static_cast<uint32_t>(builtin_size_.size()); i++) {
Builtin id = Builtins::FromInt(i);
Builtins::Kind kind = Builtins::KindOf(id);
if (kind == Builtins::Kind::ASM || kind == Builtins::Kind::CPP) {
// CHECK there is no data for execution count for non TurboFan compiled
// builtin.
CHECK_EQ(builtin_density_map_[id], 0);
continue;
}
Cluster* cls =
new Cluster(builtin_density_map_[id], builtin_size_[i], id, this);
clusters_.push_back(cls);
builtin_density_order_.push_back(
BuiltinDensitySlot{builtin_density_map_[id], id});
}
std::sort(builtin_density_order_.begin(), builtin_density_order_.end(),
[](const BuiltinDensitySlot& x, const BuiltinDensitySlot& y) {
return x.density_ > y.density_;
});
}
Builtin BuiltinsSorter::FindBestPredecessorOf(Builtin callee) {
Builtin bestPred = Builtin::kNoBuiltinId;
int32_t bestProb = 0;
for (auto caller_it = call_graph_.begin(); caller_it != call_graph_.end();
caller_it++) {
Builtin caller = caller_it->first;
const CallProbabilities& callees_prob = caller_it->second;
if (callees_prob.count(callee) > 0) {
int32_t incoming_prob = callees_prob.at(callee).incoming_;
if (incoming_prob == -1) {
// We dont want to merge any cluster with -1 prob, because it means it's
// either a non TurboFan compiled builtin or its execution count too
// small.
continue;
}
if (bestPred == Builtin::kNoBuiltinId || incoming_prob > bestProb) {
bestPred = caller;
bestProb = incoming_prob;
}
}
if (bestProb < kMinEdgeProbabilityThreshold ||
bestPred == Builtin::kNoBuiltinId)
continue;
Cluster* predCls = builtin_cluster_map_[bestPred];
Cluster* succCls = builtin_cluster_map_[callee];
// Don't merge if the caller and callee are already in same cluster.
if (predCls == succCls) continue;
// Don't merge clusters if the combined size is too big.
if (predCls->size_ + succCls->size_ > kMaxClusterSize) continue;
if (predCls->density_ == 0) {
// Some density of cluster after normalized may be 0, in that case we dont
// merge them.
continue;
}
CHECK(predCls->size_);
uint32_t new_density = static_cast<uint32_t>(
(predCls->time_approximation() + succCls->time_approximation()) /
(predCls->size_ + succCls->size_));
// Don't merge clusters if the new merged density is lower too many times
// than current cluster, to avoid a huge dropping in cluster density, it
// will harm locality of builtins.
if (predCls->density_ / kMaxDensityDecreaseThreshold > new_density)
continue;
}
return bestPred;
}
void BuiltinsSorter::MergeBestPredecessors() {
for (size_t i = 0; i < builtin_density_order_.size(); i++) {
Builtin id = builtin_density_order_[i].builtin_;
Cluster* succ_cluster = builtin_cluster_map_[id];
Builtin bestPred = FindBestPredecessorOf(id);
if (bestPred != Builtin::kNoBuiltinId) {
Cluster* pred_cluster = builtin_cluster_map_[bestPred];
pred_cluster->Merge(succ_cluster);
}
}
}
void BuiltinsSorter::SortClusters() {
std::sort(clusters_.begin(), clusters_.end(),
[](const Cluster* x, const Cluster* y) {
return x->density_ > y->density_;
});
clusters_.erase(
std::remove_if(clusters_.begin(), clusters_.end(),
[](const Cluster* x) { return x->targets_.empty(); }),
clusters_.end());
}
bool AddBuiltinIfNotProcessed(Builtin builtin, std::vector<Builtin>& order,
std::unordered_set<Builtin>& processed_builtins) {
if (processed_builtins.count(builtin) == 0) {
order.push_back(builtin);
processed_builtins.emplace(builtin);
return true;
}
return false;
}
void BuiltinsSorter::ProcessBlockCountLineInfo(
std::istringstream& line_stream,
std::unordered_map<std::string, Builtin>& name2id) {
// Any line starting with kBuiltinCallBlockDensityMarker is a normalized
// execution count of block with call. The format is:
// literal kBuiltinCallBlockDensityMarker , caller , block ,
// normalized_count
std::string token;
std::string caller_name;
CHECK(std::getline(line_stream, caller_name, ','));
Builtin caller_id = name2id[caller_name];
BuiltinsCallGraph* profiler = BuiltinsCallGraph::Get();
char* end = nullptr;
errno = 0;
CHECK(std::getline(line_stream, token, ','));
int32_t block_id = static_cast<int32_t>(strtoul(token.c_str(), &end, 0));
CHECK(errno == 0 && end != token.c_str());
CHECK(std::getline(line_stream, token, ','));
int32_t normalized_count =
static_cast<int32_t>(strtoul(token.c_str(), &end, 0));
CHECK(errno == 0 && end != token.c_str());
CHECK(line_stream.eof());
const BuiltinCallees* block_callees = profiler->GetBuiltinCallees(caller_id);
if (block_callees) {
int32_t outgoing_prob = 0;
int32_t incoming_prob = 0;
int caller_density = 0;
int callee_density = 0;
CHECK(builtin_density_map_.count(caller_id));
caller_density = builtin_density_map_.at(caller_id);
// TODO(v8:13938): Remove the below if check when we just store
// interesting blocks (contain call other builtins) execution count into
// profiling file.
if (block_callees->count(block_id)) {
// If the line of block density make sense (means it contain call to
// other builtins in this block).
for (const auto& callee_id : block_callees->at(block_id)) {
if (caller_density != 0) {
outgoing_prob = normalized_count * 100 / caller_density;
} else {
// If the caller density was normalized as 0 but the block density
// was not, we set caller prob as 100, otherwise it's 0. Because in
// the normalization, we may loss fidelity.
// For example, a caller was executed 8 times, but after
// normalization, it may be 0 time. At that time, if the
// normalized_count of this block (it may be a loop body) is a
// positive number, we could think normalized_count is bigger than the
// execution count of caller, hence we set it as 100, otherwise it's
// smaller than execution count of caller, we could set it as 0.
outgoing_prob = normalized_count ? 100 : 0;
}
if (builtin_density_map_.count(callee_id)) {
callee_density = builtin_density_map_.at(callee_id);
if (callee_density != 0) {
incoming_prob = normalized_count * 100 / callee_density;
} else {
// Same as caller prob when callee density exists but is 0.
incoming_prob = normalized_count ? 100 : 0;
}
} else {
// If callee_density does not exist, it means the callee was not
// compiled by TurboFan or execution count is too small (0 after
// normalization), we couldn't get the callee count, so we set it as
// -1. In that case we could avoid merging this callee builtin into
// any other cluster.
incoming_prob = -1;
}
CallProbability probs = CallProbability(incoming_prob, outgoing_prob);
if (call_graph_.count(caller_id) == 0) {
call_graph_.emplace(caller_id, CallProbabilities());
}
CallProbabilities& call_probs = call_graph_.at(caller_id);
call_probs.emplace(callee_id, probs);
}
}
}
CHECK(line_stream.eof());
}
void BuiltinsSorter::ProcessBuiltinDensityLineInfo(
std::istringstream& line_stream,
std::unordered_map<std::string, Builtin>& name2id) {
// Any line starting with kBuiltinDensityMarker is normalized execution count
// for block 0 of a builtin, we take it as density of this builtin. The format
// is:
// literal kBuiltinDensityMarker , builtin_name , density
std::string token;
std::string builtin_name;
CHECK(std::getline(line_stream, builtin_name, ','));
std::getline(line_stream, token, ',');
CHECK(line_stream.eof());
char* end = nullptr;
errno = 0;
int density = static_cast<int>(strtol(token.c_str(), &end, 0));
CHECK(errno == 0 && end != token.c_str());
Builtin builtin_id = name2id[builtin_name];
builtin_density_map_.emplace(builtin_id, density);
}
void BuiltinsSorter::InitializeCallGraph(const char* profiling_file,
const std::vector<uint32_t>& size) {
std::ifstream file(profiling_file);
CHECK_WITH_MSG(file.good(), "Can't read log file");
std::unordered_map<std::string, Builtin> name2id;
for (Builtin i = Builtins::kFirst; i <= Builtins::kLast; ++i) {
std::string name = Builtins::name(i);
name2id.emplace(name, i);
builtin_size_.push_back(size.at(static_cast<uint32_t>(i)));
}
for (std::string line; std::getline(file, line);) {
std::string token;
std::istringstream line_stream(line);
// We must put lines start with kBuiltinDensityMarker before lines start
// with kBuiltinCallBlockDensityMarker, because we have to density to
// calculate call prob.
if (!std::getline(line_stream, token, ',')) continue;
if (token == kBuiltinCallBlockDensityMarker) {
ProcessBlockCountLineInfo(line_stream, name2id);
} else if (token == kBuiltinDensityMarker) {
ProcessBuiltinDensityLineInfo(line_stream, name2id);
}
}
}
std::vector<Builtin> BuiltinsSorter::SortBuiltins(
const char* profiling_file, const std::vector<uint32_t>& builtin_size) {
InitializeCallGraph(profiling_file, builtin_size);
// Step 1: initialization.
InitializeClusters();
// Step 2: Merge best predecessors.
MergeBestPredecessors();
// Step 3: Sort clusters again.
SortClusters();
std::unordered_set<Builtin> processed_builtins;
std::vector<Builtin> builtin_order;
// For functions in the sorted cluster from step 3.
for (size_t i = 0; i < clusters_.size(); i++) {
Cluster* cls = clusters_.at(i);
for (size_t j = 0; j < cls->targets_.size(); j++) {
Builtin builtin = cls->targets_[j];
CHECK(
AddBuiltinIfNotProcessed(builtin, builtin_order, processed_builtins));
}
}
// For the remaining builtins.
for (Builtin i = Builtins::kFirst; i <= Builtins::kLast; ++i) {
AddBuiltinIfNotProcessed(i, builtin_order, processed_builtins);
}
return builtin_order;
}
} // namespace internal
} // namespace v8

147
deps/v8/src/snapshot/sort-builtins.h vendored Normal file
View File

@ -0,0 +1,147 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_SORT_BUILTINS_H_
#define V8_SNAPSHOT_SORT_BUILTINS_H_
#include <unordered_map>
#include <vector>
#include "src/builtins/builtins.h"
#include "src/diagnostics/basic-block-profiler.h"
// The inputs were the builtin size, call graph and basic block execution count.
// There are 3 steps in this sorting algorithm:
// 1. Initializing cluster and sorting:
// A cluster represents a group of functions. At the beginning, each
// function was in an individual cluster, and we sort these clusters
// by their density (which means how much probabilities this function was
// invoked).
//
// 2. Merge the best predecessor:
// After step 1, we will get lots of clusters which may contain only
// one function. According to this order, we iterate each function
// and merge cluster with some conditions, like:
// 1) The most incoming probability.
// 2) Incoming probability must be bigger than a threshold, like 0.1
// 3) Merged cluster size couldn't be bigger than a threshold, like 1 mb.
// 4) Predecessor cluster density couldn't be bigger N times than the new
// merged cluster, N is 8 now.
//
// 3. Sorting clusters:
// After step 2, we obtain lots of clusters which comprise several functions.
// We will finally sort these clusters by their density.
namespace v8 {
namespace internal {
class Cluster;
struct CallProbability {
CallProbability(int32_t incoming = 0, int32_t outgoing = 0)
: incoming_(incoming), outgoing_(outgoing) {}
// There are a caller and a callee, we assume caller was invoked
// "caller-count" times, it calls callee "call-count" times, the callee was
// invoked "callee-count" times. imcoming_ means the possibity the callee
// calls from caller, it was calculted by call-count / callee-count. If
// callee-count is 0 (may not be compiled by TurboFan or normalized as 0 due
// to too small), we set imcoming_ as -1.
int32_t incoming_;
// outgoing_ means the possibity the caller
// calls to callee, it was calculted by call-count / caller-count. If
// caller-count is 0 (may not be compiled by TurboFan or normalized as 0 due
// to too small), we set outgoing_ as -1. We didn't use outgoing_ as condition
// for reordering builtins yet, but we could try to do some experiments with
// it later for obtaining a better order of builtins.
int32_t outgoing_;
};
// The key is the callee builtin, the value is call probabilities in percent
// (mostly range in 0 ~ 100, except one call happend in a loop block which was
// executed more times than block 0 of this builtin).
using CallProbabilities = std::unordered_map<Builtin, CallProbability>;
// The key is the caller builtin.
using CallGraph = std::unordered_map<Builtin, CallProbabilities>;
// The key is the builtin id, the value is density of builtin (range in 0 ~
// 10000).
using BuiltinDensityMap = std::unordered_map<Builtin, uint32_t>;
// The index is the builtin id, the value is size of builtin (in bytes).
using BuiltinSize = std::vector<uint32_t>;
// The key is the builtin id, the value is the cluster which it was comprised.
using BuiltinClusterMap = std::unordered_map<Builtin, Cluster*>;
class BuiltinsSorter {
const int32_t kMinEdgeProbabilityThreshold = 10;
const uint32_t kMaxClusterSize = 1 * MB;
const uint32_t kMaxDensityDecreaseThreshold = 8;
const std::string kBuiltinCallBlockDensityMarker = "block_count";
const std::string kBuiltinDensityMarker = "builtin_count";
// Pair of denstity of builtin and builtin id.
struct BuiltinDensitySlot {
BuiltinDensitySlot(uint32_t density, Builtin builtin)
: density_(density), builtin_(builtin) {}
uint32_t density_;
Builtin builtin_;
};
public:
BuiltinsSorter();
~BuiltinsSorter();
std::vector<Builtin> SortBuiltins(const char* profiling_file,
const std::vector<uint32_t>& builtin_size);
private:
void InitializeCallGraph(const char* profiling_file,
const std::vector<uint32_t>& size);
void InitializeClusters();
void MergeBestPredecessors();
void SortClusters();
Builtin FindBestPredecessorOf(Builtin callee);
void ProcessBlockCountLineInfo(
std::istringstream& line_stream,
std::unordered_map<std::string, Builtin>& name2id);
void ProcessBuiltinDensityLineInfo(
std::istringstream& line_stream,
std::unordered_map<std::string, Builtin>& name2id);
std::vector<Cluster*> clusters_;
std::vector<BuiltinDensitySlot> builtin_density_order_;
CallGraph call_graph_;
BuiltinDensityMap builtin_density_map_;
BuiltinSize builtin_size_;
BuiltinClusterMap builtin_cluster_map_;
friend class Cluster;
};
class Cluster {
public:
Cluster(uint32_t density, uint32_t size, Builtin target,
BuiltinsSorter* sorter);
void Merge(Cluster* other);
uint64_t time_approximation();
private:
// Max initialized density was normalized as 10000.
uint32_t density_;
// Size of the cluster in bytes.
uint32_t size_;
std::vector<Builtin> targets_;
BuiltinsSorter* sorter_;
friend class BuiltinsSorter;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_SORT_BUILTINS_H_

View File

@ -0,0 +1,120 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/startup-deserializer.h"
#include "src/api/api.h"
#include "src/codegen/flush-instruction-cache.h"
#include "src/execution/v8threads.h"
#include "src/handles/handles-inl.h"
#include "src/heap/paged-spaces-inl.h"
#include "src/logging/counters-scopes.h"
#include "src/logging/log.h"
#include "src/objects/oddball.h"
#include "src/roots/roots-inl.h"
namespace v8 {
namespace internal {
void StartupDeserializer::DeserializeIntoIsolate() {
TRACE_EVENT0("v8", "V8.DeserializeIsolate");
RCS_SCOPE(isolate(), RuntimeCallCounterId::kDeserializeIsolate);
base::ElapsedTimer timer;
if (V8_UNLIKELY(v8_flags.profile_deserialization)) timer.Start();
NestedTimedHistogramScope histogram_timer(
isolate()->counters()->snapshot_deserialize_isolate());
HandleScope scope(isolate());
// No active threads.
DCHECK_NULL(isolate()->thread_manager()->FirstThreadStateInUse());
// No active handles.
DCHECK(isolate()->handle_scope_implementer()->blocks()->empty());
// Startup object cache is not yet populated.
DCHECK(isolate()->startup_object_cache()->empty());
// Builtins are not yet created.
DCHECK(!isolate()->builtins()->is_initialized());
{
DeserializeAndCheckExternalReferenceTable();
isolate()->heap()->IterateSmiRoots(this);
isolate()->heap()->IterateRoots(
this,
base::EnumSet<SkipRoot>{SkipRoot::kUnserializable, SkipRoot::kWeak,
SkipRoot::kTracedHandles});
IterateStartupObjectCache(isolate(), this);
isolate()->heap()->IterateWeakRoots(
this, base::EnumSet<SkipRoot>{SkipRoot::kUnserializable});
DeserializeDeferredObjects();
for (DirectHandle<AccessorInfo> info : accessor_infos()) {
RestoreExternalReferenceRedirector(isolate(), *info);
}
for (DirectHandle<FunctionTemplateInfo> info : function_template_infos()) {
RestoreExternalReferenceRedirector(isolate(), *info);
}
// Flush the instruction cache for the entire code-space. Must happen after
// builtins deserialization.
FlushICache();
}
isolate()->heap()->set_native_contexts_list(
ReadOnlyRoots(isolate()).undefined_value());
// The allocation site list is build during root iteration, but if no sites
// were encountered then it needs to be initialized to undefined.
if (isolate()->heap()->allocation_sites_list() == Smi::zero()) {
isolate()->heap()->set_allocation_sites_list(
ReadOnlyRoots(isolate()).undefined_value());
}
isolate()->heap()->set_dirty_js_finalization_registries_list(
ReadOnlyRoots(isolate()).undefined_value());
isolate()->heap()->set_dirty_js_finalization_registries_list_tail(
ReadOnlyRoots(isolate()).undefined_value());
isolate()->builtins()->MarkInitialized();
LogNewMapEvents();
WeakenDescriptorArrays();
if (should_rehash()) {
// Hash seed was initialized in ReadOnlyDeserializer.
Rehash();
}
if (V8_UNLIKELY(v8_flags.profile_deserialization)) {
// ATTENTION: The Memory.json benchmark greps for this exact output. Do not
// change it without also updating Memory.json.
const int bytes = source()->length();
const double ms = timer.Elapsed().InMillisecondsF();
PrintF("[Deserializing isolate (%d bytes) took %0.3f ms]\n", bytes, ms);
}
}
void StartupDeserializer::DeserializeAndCheckExternalReferenceTable() {
// Verify that any external reference entries that were deduplicated in the
// serializer are also deduplicated in this isolate.
ExternalReferenceTable* table = isolate()->external_reference_table();
while (true) {
uint32_t index = source()->GetUint30();
if (index == ExternalReferenceTable::kSizeIsolateIndependent) break;
uint32_t encoded_index = source()->GetUint30();
CHECK_EQ(table->address(index), table->address(encoded_index));
}
}
void StartupDeserializer::LogNewMapEvents() {
if (v8_flags.log_maps) LOG(isolate(), LogAllMaps());
}
void StartupDeserializer::FlushICache() {
DCHECK(!deserializing_user_code());
// The entire isolate is newly deserialized. Simply flush all code pages.
for (PageMetadata* p : *isolate()->heap()->code_space()) {
FlushInstructionCache(p->area_start(), p->area_end() - p->area_start());
}
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,35 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_STARTUP_DESERIALIZER_H_
#define V8_SNAPSHOT_STARTUP_DESERIALIZER_H_
#include "src/snapshot/deserializer.h"
#include "src/snapshot/snapshot-data.h"
namespace v8 {
namespace internal {
// Initializes an isolate with context-independent data from a given snapshot.
class StartupDeserializer final : public Deserializer<Isolate> {
public:
explicit StartupDeserializer(Isolate* isolate,
const SnapshotData* startup_data,
bool can_rehash)
: Deserializer(isolate, startup_data->Payload(),
startup_data->GetMagicNumber(), false, can_rehash) {}
// Deserialize the snapshot into an empty heap.
void DeserializeIntoIsolate();
private:
void FlushICache();
void LogNewMapEvents();
void DeserializeAndCheckExternalReferenceTable();
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_STARTUP_DESERIALIZER_H_

View File

@ -0,0 +1,243 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/startup-serializer.h"
#include "src/execution/v8threads.h"
#include "src/handles/global-handles-inl.h"
#include "src/heap/heap-inl.h"
#include "src/heap/read-only-heap.h"
#include "src/objects/contexts.h"
#include "src/objects/objects-inl.h"
#include "src/objects/slots.h"
#include "src/snapshot/read-only-serializer.h"
#include "src/snapshot/shared-heap-serializer.h"
namespace v8 {
namespace internal {
namespace {
// The isolate roots may not point at context-specific objects during
// serialization.
class V8_NODISCARD SanitizeIsolateScope final {
public:
SanitizeIsolateScope(Isolate* isolate, bool allow_active_isolate_for_testing,
const DisallowGarbageCollection& no_gc)
: isolate_(isolate),
feedback_vectors_for_profiling_tools_(
isolate->heap()->feedback_vectors_for_profiling_tools()),
detached_contexts_(isolate->heap()->detached_contexts()) {
#ifdef DEBUG
if (!allow_active_isolate_for_testing) {
// These should already be empty when creating a real snapshot.
DCHECK_EQ(feedback_vectors_for_profiling_tools_,
ReadOnlyRoots(isolate).undefined_value());
DCHECK_EQ(detached_contexts_,
ReadOnlyRoots(isolate).empty_weak_array_list());
}
#endif
isolate->SetFeedbackVectorsForProfilingTools(
ReadOnlyRoots(isolate).undefined_value());
isolate->heap()->SetDetachedContexts(
ReadOnlyRoots(isolate).empty_weak_array_list());
}
~SanitizeIsolateScope() {
// Restore saved fields.
isolate_->SetFeedbackVectorsForProfilingTools(
feedback_vectors_for_profiling_tools_);
isolate_->heap()->SetDetachedContexts(detached_contexts_);
}
private:
Isolate* isolate_;
const Tagged<Object> feedback_vectors_for_profiling_tools_;
const Tagged<WeakArrayList> detached_contexts_;
};
} // namespace
StartupSerializer::StartupSerializer(
Isolate* isolate, Snapshot::SerializerFlags flags,
SharedHeapSerializer* shared_heap_serializer)
: RootsSerializer(isolate, flags, RootIndex::kFirstStrongRoot),
shared_heap_serializer_(shared_heap_serializer),
accessor_infos_(isolate->heap()),
function_template_infos_(isolate->heap()) {
InitializeCodeAddressMap();
// This serializes any external reference which don't encode to their own
// index. This is so that the deserializer can verify that any entries that
// were deduplicated during serialization are also deduplicated in the
// deserializing binary.
ExternalReferenceTable* table = isolate->external_reference_table();
for (uint32_t i = 0; i < ExternalReferenceTable::kSizeIsolateIndependent;
++i) {
ExternalReferenceEncoder::Value encoded_reference =
EncodeExternalReference(table->address(i));
if (encoded_reference.index() != i) {
sink_.PutUint30(i, "expected reference index");
sink_.PutUint30(encoded_reference.index(), "actual reference index");
}
}
sink_.PutUint30(ExternalReferenceTable::kSizeIsolateIndependent,
"end of deduplicated reference indices");
}
StartupSerializer::~StartupSerializer() {
for (DirectHandle<AccessorInfo> info : accessor_infos_) {
RestoreExternalReferenceRedirector(isolate(), *info);
}
for (DirectHandle<FunctionTemplateInfo> info : function_template_infos_) {
RestoreExternalReferenceRedirector(isolate(), *info);
}
OutputStatistics("StartupSerializer");
}
void StartupSerializer::SerializeObjectImpl(Handle<HeapObject> obj,
SlotType slot_type) {
PtrComprCageBase cage_base(isolate());
#ifdef DEBUG
if (IsJSFunction(*obj, cage_base)) {
v8::base::OS::PrintError("Reference stack:\n");
PrintStack(std::cerr);
Print(*obj, std::cerr);
FATAL(
"JSFunction should be added through the context snapshot instead of "
"the isolate snapshot");
}
#endif // DEBUG
{
DisallowGarbageCollection no_gc;
Tagged<HeapObject> raw = *obj;
DCHECK(!IsInstructionStream(raw));
if (SerializeHotObject(raw)) return;
if (IsRootAndHasBeenSerialized(raw) && SerializeRoot(raw)) return;
}
if (SerializeReadOnlyObjectReference(*obj, &sink_)) return;
if (SerializeUsingSharedHeapObjectCache(&sink_, obj)) return;
if (SerializeBackReference(*obj)) return;
if (USE_SIMULATOR_BOOL && IsAccessorInfo(*obj, cage_base)) {
// Wipe external reference redirects in the accessor info.
auto info = Cast<AccessorInfo>(obj);
info->remove_getter_redirection(isolate());
accessor_infos_.Push(*info);
} else if (USE_SIMULATOR_BOOL && IsFunctionTemplateInfo(*obj, cage_base)) {
auto info = Cast<FunctionTemplateInfo>(obj);
info->remove_callback_redirection(isolate());
function_template_infos_.Push(*info);
} else if (IsScript(*obj, cage_base) &&
Cast<Script>(obj)->IsUserJavaScript()) {
Cast<Script>(obj)->set_context_data(
ReadOnlyRoots(isolate()).uninitialized_symbol());
} else if (IsSharedFunctionInfo(*obj, cage_base)) {
// Clear inferred name for native functions.
auto shared = Cast<SharedFunctionInfo>(obj);
if (!shared->IsSubjectToDebugging() && shared->HasUncompiledData()) {
shared->uncompiled_data(isolate())->set_inferred_name(
ReadOnlyRoots(isolate()).empty_string());
}
}
CheckRehashability(*obj);
// Object has not yet been serialized. Serialize it here.
DCHECK(!ReadOnlyHeap::Contains(*obj));
ObjectSerializer object_serializer(this, obj, &sink_);
object_serializer.Serialize(slot_type);
}
void StartupSerializer::SerializeWeakReferencesAndDeferred() {
// This comes right after serialization of the context snapshot, where we
// add entries to the startup object cache of the startup snapshot. Add
// one entry with 'undefined' to terminate the startup object cache.
Tagged<Object> undefined = ReadOnlyRoots(isolate()).undefined_value();
VisitRootPointer(Root::kStartupObjectCache, nullptr,
FullObjectSlot(&undefined));
isolate()->heap()->IterateWeakRoots(
this, base::EnumSet<SkipRoot>{SkipRoot::kUnserializable});
SerializeDeferredObjects();
Pad();
}
void StartupSerializer::SerializeStrongReferences(
const DisallowGarbageCollection& no_gc) {
Isolate* isolate = this->isolate();
// No active threads.
CHECK_NULL(isolate->thread_manager()->FirstThreadStateInUse());
SanitizeIsolateScope sanitize_isolate(
isolate, allow_active_isolate_for_testing(), no_gc);
// Visit smi roots and immortal immovables first to make sure they end up in
// the first page.
isolate->heap()->IterateSmiRoots(this);
isolate->heap()->IterateRoots(
this, base::EnumSet<SkipRoot>{SkipRoot::kUnserializable, SkipRoot::kWeak,
SkipRoot::kTracedHandles});
}
SerializedHandleChecker::SerializedHandleChecker(
Isolate* isolate, std::vector<Tagged<Context>>* contexts)
: isolate_(isolate) {
AddToSet(Cast<FixedArray>(isolate->heap()->serialized_objects()));
for (auto const& context : *contexts) {
AddToSet(Cast<FixedArray>(context->serialized_objects()));
}
}
bool StartupSerializer::SerializeUsingSharedHeapObjectCache(
SnapshotByteSink* sink, Handle<HeapObject> obj) {
return shared_heap_serializer_->SerializeUsingSharedHeapObjectCache(sink,
obj);
}
void StartupSerializer::SerializeUsingStartupObjectCache(
SnapshotByteSink* sink, Handle<HeapObject> obj) {
int cache_index = SerializeInObjectCache(obj);
sink->Put(kStartupObjectCache, "StartupObjectCache");
sink->PutUint30(cache_index, "startup_object_cache_index");
}
void StartupSerializer::CheckNoDirtyFinalizationRegistries() {
Isolate* isolate = this->isolate();
CHECK(IsUndefined(isolate->heap()->dirty_js_finalization_registries_list(),
isolate));
CHECK(IsUndefined(
isolate->heap()->dirty_js_finalization_registries_list_tail(), isolate));
}
void SerializedHandleChecker::AddToSet(Tagged<FixedArray> serialized) {
int length = serialized->length();
for (int i = 0; i < length; i++) serialized_.insert(serialized->get(i));
}
void SerializedHandleChecker::VisitRootPointers(Root root,
const char* description,
FullObjectSlot start,
FullObjectSlot end) {
for (FullObjectSlot p = start; p < end; ++p) {
if (serialized_.find(*p) != serialized_.end()) continue;
PrintF("%s handle not serialized: ",
root == Root::kGlobalHandles ? "global" : "eternal");
Print(*p);
PrintF("\n");
ok_ = false;
}
}
bool SerializedHandleChecker::CheckGlobalAndEternalHandles() {
isolate_->global_handles()->IterateAllRoots(this);
isolate_->traced_handles()->Iterate(this);
isolate_->eternal_handles()->IterateAllRoots(this);
return ok_;
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,79 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_STARTUP_SERIALIZER_H_
#define V8_SNAPSHOT_STARTUP_SERIALIZER_H_
#include <unordered_set>
#include "src/handles/global-handles.h"
#include "src/snapshot/roots-serializer.h"
namespace v8 {
namespace internal {
class HeapObject;
class SnapshotByteSink;
class SharedHeapSerializer;
class V8_EXPORT_PRIVATE StartupSerializer : public RootsSerializer {
public:
StartupSerializer(Isolate* isolate, Snapshot::SerializerFlags flags,
SharedHeapSerializer* shared_heap_serializer);
~StartupSerializer() override;
StartupSerializer(const StartupSerializer&) = delete;
StartupSerializer& operator=(const StartupSerializer&) = delete;
// Serialize the current state of the heap. The order is:
// 1) Strong roots
// 2) Builtins and bytecode handlers
// 3) Startup object cache
// 4) Weak references (e.g. the string table)
void SerializeStrongReferences(const DisallowGarbageCollection& no_gc);
void SerializeWeakReferencesAndDeferred();
// If |obj| can be serialized in the shared heap snapshot then add it to the
// shareable object cache if not already present and emits a
// SharedHeapObjectCache bytecode into |sink|. Returns whether this was
// successful.
bool SerializeUsingSharedHeapObjectCache(SnapshotByteSink* sink,
Handle<HeapObject> obj);
// Adds |obj| to the startup object object cache if not already present and
// emits a StartupObjectCache bytecode into |sink|.
void SerializeUsingStartupObjectCache(SnapshotByteSink* sink,
Handle<HeapObject> obj);
// The per-heap dirty FinalizationRegistry list is weak and not serialized. No
// JSFinalizationRegistries should be used during startup.
void CheckNoDirtyFinalizationRegistries();
private:
void SerializeObjectImpl(Handle<HeapObject> o, SlotType slot_type) override;
SharedHeapSerializer* const shared_heap_serializer_;
GlobalHandleVector<AccessorInfo> accessor_infos_;
GlobalHandleVector<FunctionTemplateInfo> function_template_infos_;
};
class SerializedHandleChecker : public RootVisitor {
public:
SerializedHandleChecker(Isolate* isolate,
std::vector<Tagged<Context>>* contexts);
void VisitRootPointers(Root root, const char* description,
FullObjectSlot start, FullObjectSlot end) override;
bool CheckGlobalAndEternalHandles();
private:
void AddToSet(Tagged<FixedArray> serialized);
Isolate* isolate_;
std::unordered_set<Tagged<Object>, Object::Hasher> serialized_;
bool ok_ = true;
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_STARTUP_SERIALIZER_H_

142
deps/v8/src/snapshot/static-roots-gen.cc vendored Normal file
View File

@ -0,0 +1,142 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/static-roots-gen.h"
#include <fstream>
#include "src/common/globals.h"
#include "src/common/ptr-compr-inl.h"
#include "src/execution/isolate.h"
#include "src/objects/instance-type-inl.h"
#include "src/objects/instance-type.h"
#include "src/objects/objects-definitions.h"
#include "src/objects/visitors.h"
#include "src/roots/roots-inl.h"
#include "src/roots/roots.h"
namespace v8 {
namespace internal {
class StaticRootsTableGenImpl {
public:
explicit StaticRootsTableGenImpl(Isolate* isolate) {
// Collect all roots
ReadOnlyRoots ro_roots(isolate);
{
RootIndex pos = RootIndex::kFirstReadOnlyRoot;
#define ADD_ROOT(_, value, CamelName) \
{ \
Tagged_t ptr = V8HeapCompressionScheme::CompressObject( \
ro_roots.unchecked_##value().ptr()); \
sorted_roots_[ptr].push_back(pos); \
camel_names_[RootIndex::k##CamelName] = #CamelName; \
++pos; \
}
READ_ONLY_ROOT_LIST(ADD_ROOT)
#undef ADD_ROOT
}
}
const std::map<Tagged_t, std::list<RootIndex>>& sorted_roots() {
return sorted_roots_;
}
const std::string& camel_name(RootIndex idx) { return camel_names_.at(idx); }
private:
std::map<Tagged_t, std::list<RootIndex>> sorted_roots_;
std::unordered_map<RootIndex, std::string> camel_names_;
};
void StaticRootsTableGen::write(Isolate* isolate, const char* file) {
CHECK_WITH_MSG(!V8_STATIC_ROOTS_BOOL,
"Re-generating the table of roots is only supported in builds "
"with v8_enable_static_roots disabled");
CHECK(V8_STATIC_ROOTS_GENERATION_BOOL);
CHECK(file);
static_assert(static_cast<int>(RootIndex::kFirstReadOnlyRoot) == 0);
std::ofstream out(file, std::ios::binary);
out << "// Copyright 2022 the V8 project authors. All rights reserved.\n"
<< "// Use of this source code is governed by a BSD-style license "
"that can be\n"
<< "// found in the LICENSE file.\n"
<< "\n"
<< "// This file is automatically generated by "
"`tools/dev/gen-static-roots.py`. Do\n// not edit manually.\n"
<< "\n"
<< "#ifndef V8_ROOTS_STATIC_ROOTS_H_\n"
<< "#define V8_ROOTS_STATIC_ROOTS_H_\n"
<< "\n"
<< "#include \"src/common/globals.h\"\n"
<< "\n"
<< "#if V8_STATIC_ROOTS_BOOL\n"
<< "\n"
<< "#include \"src/roots/roots.h\"\n"
<< "\n"
<< "// Disabling Wasm or Intl invalidates the contents of "
"static-roots.h.\n"
<< "// TODO(olivf): To support static roots for multiple build "
"configurations we\n"
<< "// will need to generate target specific versions of "
"this file.\n"
<< "static_assert(V8_ENABLE_WEBASSEMBLY);\n"
<< "static_assert(V8_INTL_SUPPORT);\n"
<< "\n"
<< "namespace v8 {\n"
<< "namespace internal {\n"
<< "\n"
<< "struct StaticReadOnlyRoot {\n";
// Output a symbol for every root. Ordered by ptr to make it easier to see the
// memory layout of the read only page.
const auto size = static_cast<int>(RootIndex::kReadOnlyRootsCount);
StaticRootsTableGenImpl gen(isolate);
for (auto& entry : gen.sorted_roots()) {
Tagged_t ptr = entry.first;
CHECK_LT(ptr, kRegularPageSize);
const std::list<RootIndex>& roots = entry.second;
for (RootIndex root : roots) {
static const char* kPreString = " static constexpr Tagged_t k";
const std::string& name = gen.camel_name(root);
size_t ptr_len = ceil(log2(ptr) / 4.0);
// Full line is: "kPreString|name = 0x.....;"
size_t len = strlen(kPreString) + name.length() + 5 + ptr_len + 1;
out << kPreString << name << " =";
if (len > 80) out << "\n ";
out << " 0x" << std::hex << ptr << std::dec << ";\n";
}
}
out << "\n";
out << " static constexpr Tagged_t kFirstAllocatedRoot = 0x" << std::hex
<< gen.sorted_roots().cbegin()->first << std::dec << ";\n";
out << " static constexpr Tagged_t kLastAllocatedRoot = 0x" << std::hex
<< gen.sorted_roots().crbegin()->first << std::dec << ";\n";
out << "};\n";
// Output in order of roots table
out << "\nstatic constexpr std::array<Tagged_t, " << size
<< "> StaticReadOnlyRootsPointerTable = {\n";
{
#define ENTRY(_1, _2, CamelName) \
out << " StaticReadOnlyRoot::k" << #CamelName << ",\n";
READ_ONLY_ROOT_LIST(ENTRY)
#undef ENTRY
out << "};\n";
}
out << "\n"
<< "} // namespace internal\n"
<< "} // namespace v8\n"
<< "#endif // V8_STATIC_ROOTS_BOOL\n"
<< "#endif // V8_ROOTS_STATIC_ROOTS_H_\n";
}
} // namespace internal
} // namespace v8

21
deps/v8/src/snapshot/static-roots-gen.h vendored Normal file
View File

@ -0,0 +1,21 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SNAPSHOT_STATIC_ROOTS_GEN_H_
#define V8_SNAPSHOT_STATIC_ROOTS_GEN_H_
namespace v8 {
namespace internal {
class Isolate;
class StaticRootsTableGen {
public:
static void write(Isolate* isolate, const char* file);
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_STATIC_ROOTS_GEN_H_