[ext-code-space] Support external code space in serializers
... by updating the checks for not_mapped_symbol root and using proper cage base values depending on the host object type. Bug: v8:11880 Change-Id: I28908cbb5b1023addaee248028661d480734e29c Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3222760 Commit-Queue: Igor Sheludko <ishell@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/main@{#77435}
This commit is contained in:
parent
761f892898
commit
c0cf4f838d
@ -1890,6 +1890,7 @@ filegroup(
|
||||
"src/snapshot/serializer-deserializer.h",
|
||||
"src/snapshot/serializer.cc",
|
||||
"src/snapshot/serializer.h",
|
||||
"src/snapshot/serializer-inl.h",
|
||||
"src/snapshot/snapshot-compression.cc",
|
||||
"src/snapshot/snapshot-compression.h",
|
||||
"src/snapshot/snapshot-data.cc",
|
||||
|
1
BUILD.gn
1
BUILD.gn
@ -3334,6 +3334,7 @@ v8_header_set("v8_internal_headers") {
|
||||
"src/snapshot/references.h",
|
||||
"src/snapshot/roots-serializer.h",
|
||||
"src/snapshot/serializer-deserializer.h",
|
||||
"src/snapshot/serializer-inl.h",
|
||||
"src/snapshot/serializer.h",
|
||||
"src/snapshot/shared-heap-deserializer.h",
|
||||
"src/snapshot/shared-heap-serializer.h",
|
||||
|
@ -844,9 +844,9 @@ void UncompiledData::InitAfterBytecodeFlush(
|
||||
set_end_position(end_position);
|
||||
}
|
||||
|
||||
HeapObject SharedFunctionInfo::script() const {
|
||||
HeapObject maybe_script = script_or_debug_info(kAcquireLoad);
|
||||
if (maybe_script.IsDebugInfo()) {
|
||||
DEF_GETTER(SharedFunctionInfo, script, HeapObject) {
|
||||
HeapObject maybe_script = script_or_debug_info(cage_base, kAcquireLoad);
|
||||
if (maybe_script.IsDebugInfo(cage_base)) {
|
||||
return DebugInfo::cast(maybe_script).script();
|
||||
}
|
||||
return maybe_script;
|
||||
|
@ -378,7 +378,7 @@ class SharedFunctionInfo
|
||||
// - a DebugInfo which holds the actual script [HasDebugInfo()].
|
||||
DECL_RELEASE_ACQUIRE_ACCESSORS(script_or_debug_info, HeapObject)
|
||||
|
||||
inline HeapObject script() const;
|
||||
DECL_GETTER(script, HeapObject)
|
||||
inline void set_script(HeapObject script);
|
||||
|
||||
// True if the underlying script was parsed and compiled in REPL mode.
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "src/heap/read-only-heap.h"
|
||||
#include "src/objects/objects-inl.h"
|
||||
#include "src/objects/slots.h"
|
||||
#include "src/snapshot/serializer-inl.h"
|
||||
#include "src/snapshot/startup-serializer.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -40,7 +41,7 @@ void ReadOnlySerializer::SerializeObjectImpl(Handle<HeapObject> obj) {
|
||||
// in the root table, so don't try to serialize a reference and rely on the
|
||||
// below CHECK(!did_serialize_not_mapped_symbol_) to make sure it doesn't
|
||||
// serialize twice.
|
||||
if (*obj != ReadOnlyRoots(isolate()).not_mapped_symbol()) {
|
||||
if (!IsNotMappedSymbol(*obj)) {
|
||||
if (SerializeHotObject(obj)) return;
|
||||
if (IsRootAndHasBeenSerialized(*obj) && SerializeRoot(obj)) return;
|
||||
if (SerializeBackReference(obj)) return;
|
||||
@ -52,7 +53,7 @@ void ReadOnlySerializer::SerializeObjectImpl(Handle<HeapObject> obj) {
|
||||
ObjectSerializer object_serializer(this, obj, &sink_);
|
||||
object_serializer.Serialize();
|
||||
#ifdef DEBUG
|
||||
if (*obj == ReadOnlyRoots(isolate()).not_mapped_symbol()) {
|
||||
if (IsNotMappedSymbol(*obj)) {
|
||||
CHECK(!did_serialize_not_mapped_symbol_);
|
||||
did_serialize_not_mapped_symbol_ = true;
|
||||
} else {
|
||||
@ -94,7 +95,7 @@ void ReadOnlySerializer::FinalizeSerialization() {
|
||||
ReadOnlyHeapObjectIterator iterator(isolate()->read_only_heap());
|
||||
for (HeapObject object = iterator.Next(); !object.is_null();
|
||||
object = iterator.Next()) {
|
||||
if (object == ReadOnlyRoots(isolate()).not_mapped_symbol()) {
|
||||
if (IsNotMappedSymbol(object)) {
|
||||
CHECK(did_serialize_not_mapped_symbol_);
|
||||
} else {
|
||||
CHECK_NOT_NULL(serialized_objects_.Find(object));
|
||||
|
32
src/snapshot/serializer-inl.h
Normal file
32
src/snapshot/serializer-inl.h
Normal file
@ -0,0 +1,32 @@
|
||||
// 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/roots/roots-inl.h"
|
||||
#include "src/snapshot/serializer.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
bool Serializer::IsNotMappedSymbol(HeapObject obj) const {
|
||||
Object not_mapped_symbol = ReadOnlyRoots(isolate()).not_mapped_symbol();
|
||||
if (V8_EXTERNAL_CODE_SPACE_BOOL) {
|
||||
// It's possible that a Code 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
|
||||
// Code references anywhere except the CodeDadaContainer objects.
|
||||
// In particular, the Code objects should not appear in serializer's
|
||||
// identity map. This should be possible once the IsolateData::builtins
|
||||
// table is migrated to contain CodeT references.
|
||||
return obj.ptr() == not_mapped_symbol.ptr();
|
||||
}
|
||||
return obj == not_mapped_symbol;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_SNAPSHOT_SERIALIZER_INL_H_
|
@ -19,6 +19,7 @@
|
||||
#include "src/objects/slots-inl.h"
|
||||
#include "src/objects/smi.h"
|
||||
#include "src/snapshot/serializer-deserializer.h"
|
||||
#include "src/snapshot/serializer-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -343,7 +344,7 @@ ExternalReferenceEncoder::Value Serializer::EncodeExternalReference(
|
||||
}
|
||||
|
||||
void Serializer::RegisterObjectIsPending(Handle<HeapObject> obj) {
|
||||
if (*obj == ReadOnlyRoots(isolate()).not_mapped_symbol()) return;
|
||||
if (IsNotMappedSymbol(*obj)) return;
|
||||
|
||||
// Add the given object to the pending objects -> forward refs map.
|
||||
auto find_result = forward_refs_per_pending_object_.FindOrInsert(obj);
|
||||
@ -358,7 +359,7 @@ void Serializer::RegisterObjectIsPending(Handle<HeapObject> obj) {
|
||||
}
|
||||
|
||||
void Serializer::ResolvePendingObject(Handle<HeapObject> obj) {
|
||||
if (*obj == ReadOnlyRoots(isolate()).not_mapped_symbol()) return;
|
||||
if (IsNotMappedSymbol(*obj)) return;
|
||||
|
||||
std::vector<int>* refs;
|
||||
CHECK(forward_refs_per_pending_object_.Delete(obj, &refs));
|
||||
@ -435,7 +436,7 @@ void Serializer::ObjectSerializer::SerializePrologue(SnapshotSpace space,
|
||||
// Make sure the map serialization didn't accidentally recursively serialize
|
||||
// this object.
|
||||
DCHECK_IMPLIES(
|
||||
*object_ != ReadOnlyRoots(isolate()).not_mapped_symbol(),
|
||||
!serializer_->IsNotMappedSymbol(*object_),
|
||||
serializer_->reference_map()->LookupReference(object_) == nullptr);
|
||||
|
||||
// Now that the object is allocated, we can resolve pending references to
|
||||
@ -454,7 +455,7 @@ void Serializer::ObjectSerializer::SerializePrologue(SnapshotSpace space,
|
||||
serializer_->back_refs_.Push(*object_);
|
||||
DCHECK_EQ(serializer_->back_refs_.size(), serializer_->num_back_refs_);
|
||||
#endif
|
||||
if (*object_ != ReadOnlyRoots(isolate()).not_mapped_symbol()) {
|
||||
if (!serializer_->IsNotMappedSymbol(*object_)) {
|
||||
// Only add the object to the map if it's not not_mapped_symbol, else
|
||||
// the reference IdentityMap has issues. We don't expect to have back
|
||||
// references to the not_mapped_symbol anyway, so it's fine.
|
||||
@ -575,7 +576,8 @@ void Serializer::ObjectSerializer::SerializeExternalStringAsSequentialString() {
|
||||
// Instead of serializing this as an external string, we serialize
|
||||
// an imaginary sequential string with the same content.
|
||||
ReadOnlyRoots roots(isolate());
|
||||
DCHECK(object_->IsExternalString());
|
||||
PtrComprCageBase cage_base(isolate());
|
||||
DCHECK(object_->IsExternalString(cage_base));
|
||||
Handle<ExternalString> string = Handle<ExternalString>::cast(object_);
|
||||
int length = string->length();
|
||||
Map map;
|
||||
@ -583,8 +585,8 @@ void Serializer::ObjectSerializer::SerializeExternalStringAsSequentialString() {
|
||||
int allocation_size;
|
||||
const byte* resource;
|
||||
// Find the map and size for the imaginary sequential string.
|
||||
bool internalized = object_->IsInternalizedString();
|
||||
if (object_->IsExternalOneByteString()) {
|
||||
bool internalized = object_->IsInternalizedString(cage_base);
|
||||
if (object_->IsExternalOneByteString(cage_base)) {
|
||||
map = internalized ? roots.one_byte_internalized_string_map()
|
||||
: roots.one_byte_string_map();
|
||||
allocation_size = SeqOneByteString::SizeFor(length);
|
||||
@ -683,32 +685,33 @@ void Serializer::ObjectSerializer::Serialize() {
|
||||
PrintF("\n");
|
||||
}
|
||||
|
||||
if (object_->IsExternalString()) {
|
||||
PtrComprCageBase cage_base(isolate());
|
||||
if (object_->IsExternalString(cage_base)) {
|
||||
SerializeExternalString();
|
||||
return;
|
||||
} else if (!ReadOnlyHeap::Contains(*object_)) {
|
||||
// Only clear padding for strings outside the read-only heap. Read-only heap
|
||||
// should have been cleared elsewhere.
|
||||
if (object_->IsSeqOneByteString()) {
|
||||
if (object_->IsSeqOneByteString(cage_base)) {
|
||||
// Clear padding bytes at the end. Done here to avoid having to do this
|
||||
// at allocation sites in generated code.
|
||||
Handle<SeqOneByteString>::cast(object_)->clear_padding();
|
||||
} else if (object_->IsSeqTwoByteString()) {
|
||||
} else if (object_->IsSeqTwoByteString(cage_base)) {
|
||||
Handle<SeqTwoByteString>::cast(object_)->clear_padding();
|
||||
}
|
||||
}
|
||||
if (object_->IsJSTypedArray()) {
|
||||
if (object_->IsJSTypedArray(cage_base)) {
|
||||
SerializeJSTypedArray();
|
||||
return;
|
||||
} else if (object_->IsJSArrayBuffer()) {
|
||||
} else if (object_->IsJSArrayBuffer(cage_base)) {
|
||||
SerializeJSArrayBuffer();
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't expect fillers.
|
||||
DCHECK(!object_->IsFreeSpaceOrFiller());
|
||||
DCHECK(!object_->IsFreeSpaceOrFiller(cage_base));
|
||||
|
||||
if (object_->IsScript()) {
|
||||
if (object_->IsScript(cage_base)) {
|
||||
// Clear cached line ends.
|
||||
Oddball undefined = ReadOnlyRoots(isolate()).undefined_value();
|
||||
Handle<Script>::cast(object_)->set_line_ends(undefined);
|
||||
@ -827,11 +830,12 @@ void Serializer::ObjectSerializer::VisitPointers(HeapObject host,
|
||||
MaybeObjectSlot start,
|
||||
MaybeObjectSlot end) {
|
||||
HandleScope scope(isolate());
|
||||
PtrComprCageBase cage_base(isolate());
|
||||
DisallowGarbageCollection no_gc;
|
||||
|
||||
MaybeObjectSlot current = start;
|
||||
while (current < end) {
|
||||
while (current < end && (*current)->IsSmi()) {
|
||||
while (current < end && current.load(cage_base).IsSmi()) {
|
||||
++current;
|
||||
}
|
||||
if (current < end) {
|
||||
@ -839,15 +843,15 @@ void Serializer::ObjectSerializer::VisitPointers(HeapObject host,
|
||||
}
|
||||
// TODO(ishell): Revisit this change once we stick to 32-bit compressed
|
||||
// tagged values.
|
||||
while (current < end && (*current)->IsCleared()) {
|
||||
while (current < end && current.load(cage_base).IsCleared()) {
|
||||
sink_->Put(kClearedWeakReference, "ClearedWeakReference");
|
||||
bytes_processed_so_far_ += kTaggedSize;
|
||||
++current;
|
||||
}
|
||||
HeapObject current_contents;
|
||||
HeapObjectReferenceType reference_type;
|
||||
while (current < end &&
|
||||
(*current)->GetHeapObject(¤t_contents, &reference_type)) {
|
||||
while (current < end && current.load(cage_base).GetHeapObject(
|
||||
¤t_contents, &reference_type)) {
|
||||
// Write a weak prefix if we need it. This has to be done before the
|
||||
// potential pending object serialization.
|
||||
if (reference_type == HeapObjectReferenceType::WEAK) {
|
||||
@ -896,8 +900,11 @@ void Serializer::ObjectSerializer::VisitCodePointer(HeapObject host,
|
||||
HandleScope scope(isolate());
|
||||
DisallowGarbageCollection no_gc;
|
||||
|
||||
// TODO(v8:11880): support external code space.
|
||||
PtrComprCageBase code_cage_base = GetPtrComprCageBase(host);
|
||||
#if V8_EXTERNAL_CODE_SPACE
|
||||
PtrComprCageBase code_cage_base(isolate()->code_cage_base());
|
||||
#else
|
||||
PtrComprCageBase code_cage_base(isolate());
|
||||
#endif
|
||||
Object contents = slot.load(code_cage_base);
|
||||
DCHECK(HAS_STRONG_HEAP_OBJECT_TAG(contents.ptr()));
|
||||
DCHECK(contents.IsCode());
|
||||
@ -1106,13 +1113,14 @@ void Serializer::ObjectSerializer::OutputRawData(Address up_to) {
|
||||
__msan_check_mem_is_initialized(
|
||||
reinterpret_cast<void*>(object_start + base), bytes_to_output);
|
||||
#endif // MEMORY_SANITIZER
|
||||
if (object_->IsBytecodeArray()) {
|
||||
PtrComprCageBase cage_base(isolate_);
|
||||
if (object_->IsBytecodeArray(cage_base)) {
|
||||
// The bytecode age field can be changed by GC concurrently.
|
||||
byte field_value = BytecodeArray::kNoAgeBytecodeAge;
|
||||
OutputRawWithCustomField(sink_, object_start, base, bytes_to_output,
|
||||
BytecodeArray::kBytecodeAgeOffset,
|
||||
sizeof(field_value), &field_value);
|
||||
} else if (object_->IsDescriptorArray()) {
|
||||
} else if (object_->IsDescriptorArray(cage_base)) {
|
||||
// The number of marked descriptors field can be changed by GC
|
||||
// concurrently.
|
||||
static byte field_value[2] = {0};
|
||||
@ -1120,7 +1128,8 @@ void Serializer::ObjectSerializer::OutputRawData(Address up_to) {
|
||||
sink_, object_start, base, bytes_to_output,
|
||||
DescriptorArray::kRawNumberOfMarkedDescriptorsOffset,
|
||||
sizeof(field_value), field_value);
|
||||
} else if (V8_EXTERNAL_CODE_SPACE_BOOL && object_->IsCodeDataContainer()) {
|
||||
} else if (V8_EXTERNAL_CODE_SPACE_BOOL &&
|
||||
object_->IsCodeDataContainer(cage_base)) {
|
||||
// The CodeEntryPoint field is just a cached value which will be
|
||||
// recomputed after deserialization, so write zeros to keep the snapshot
|
||||
// deterministic.
|
||||
|
@ -204,6 +204,10 @@ class Serializer : public SerializerDeserializer {
|
||||
Serializer* serializer_;
|
||||
};
|
||||
|
||||
// Compares obj with not_mapped_symbol root. When V8_EXTERNAL_CODE_SPACE is
|
||||
// enabled it compares full pointers.
|
||||
V8_INLINE bool IsNotMappedSymbol(HeapObject obj) const;
|
||||
|
||||
void SerializeDeferredObjects();
|
||||
void SerializeObject(Handle<HeapObject> o);
|
||||
virtual void SerializeObjectImpl(Handle<HeapObject> o) = 0;
|
||||
|
@ -222,6 +222,7 @@ MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
|
||||
void Snapshot::ClearReconstructableDataForSerialization(
|
||||
Isolate* isolate, bool clear_recompilable_data) {
|
||||
// Clear SFIs and JSRegExps.
|
||||
PtrComprCageBase cage_base(isolate);
|
||||
|
||||
if (clear_recompilable_data) {
|
||||
HandleScope scope(isolate);
|
||||
@ -231,16 +232,17 @@ void Snapshot::ClearReconstructableDataForSerialization(
|
||||
DisallowGarbageCollection disallow_gc;
|
||||
i::HeapObjectIterator it(isolate->heap());
|
||||
for (i::HeapObject o = it.Next(); !o.is_null(); o = it.Next()) {
|
||||
if (o.IsSharedFunctionInfo()) {
|
||||
if (o.IsSharedFunctionInfo(cage_base)) {
|
||||
i::SharedFunctionInfo shared = i::SharedFunctionInfo::cast(o);
|
||||
if (shared.script().IsScript() &&
|
||||
Script::cast(shared.script()).type() == Script::TYPE_EXTENSION) {
|
||||
if (shared.script(cage_base).IsScript(cage_base) &&
|
||||
Script::cast(shared.script(cage_base)).type() ==
|
||||
Script::TYPE_EXTENSION) {
|
||||
continue; // Don't clear extensions, they cannot be recompiled.
|
||||
}
|
||||
if (shared.CanDiscardCompiled()) {
|
||||
sfis_to_clear.emplace_back(shared, isolate);
|
||||
}
|
||||
} else if (o.IsJSRegExp()) {
|
||||
} else if (o.IsJSRegExp(cage_base)) {
|
||||
i::JSRegExp regexp = i::JSRegExp::cast(o);
|
||||
if (regexp.HasCompiledCode()) {
|
||||
regexp.DiscardCompiledCodeForSerialization();
|
||||
@ -261,14 +263,15 @@ void Snapshot::ClearReconstructableDataForSerialization(
|
||||
|
||||
i::HeapObjectIterator it(isolate->heap());
|
||||
for (i::HeapObject o = it.Next(); !o.is_null(); o = it.Next()) {
|
||||
if (!o.IsJSFunction()) continue;
|
||||
if (!o.IsJSFunction(cage_base)) continue;
|
||||
|
||||
i::JSFunction fun = i::JSFunction::cast(o);
|
||||
fun.CompleteInobjectSlackTrackingIfActive();
|
||||
|
||||
i::SharedFunctionInfo shared = fun.shared();
|
||||
if (shared.script().IsScript() &&
|
||||
Script::cast(shared.script()).type() == Script::TYPE_EXTENSION) {
|
||||
if (shared.script(cage_base).IsScript(cage_base) &&
|
||||
Script::cast(shared.script(cage_base)).type() ==
|
||||
Script::TYPE_EXTENSION) {
|
||||
continue; // Don't clear extensions, they cannot be recompiled.
|
||||
}
|
||||
|
||||
@ -276,8 +279,8 @@ void Snapshot::ClearReconstructableDataForSerialization(
|
||||
if (fun.CanDiscardCompiled()) {
|
||||
fun.set_code(*BUILTIN_CODE(isolate, CompileLazy));
|
||||
}
|
||||
if (!fun.raw_feedback_cell().value().IsUndefined()) {
|
||||
fun.raw_feedback_cell().set_value(
|
||||
if (!fun.raw_feedback_cell(cage_base).value(cage_base).IsUndefined()) {
|
||||
fun.raw_feedback_cell(cage_base).set_value(
|
||||
i::ReadOnlyRoots(isolate).undefined_value());
|
||||
}
|
||||
#ifdef DEBUG
|
||||
|
@ -134,8 +134,9 @@ bool IsUnexpectedCodeObject(Isolate* isolate, HeapObject obj) {
|
||||
#endif // DEBUG
|
||||
|
||||
void StartupSerializer::SerializeObjectImpl(Handle<HeapObject> obj) {
|
||||
PtrComprCageBase cage_base(isolate());
|
||||
#ifdef DEBUG
|
||||
if (obj->IsJSFunction()) {
|
||||
if (obj->IsJSFunction(cage_base)) {
|
||||
v8::base::OS::PrintError("Reference stack:\n");
|
||||
PrintStack(std::cerr);
|
||||
obj->Print(std::cerr);
|
||||
@ -157,7 +158,7 @@ void StartupSerializer::SerializeObjectImpl(Handle<HeapObject> obj) {
|
||||
use_simulator = true;
|
||||
#endif
|
||||
|
||||
if (use_simulator && obj->IsAccessorInfo()) {
|
||||
if (use_simulator && obj->IsAccessorInfo(cage_base)) {
|
||||
// Wipe external reference redirects in the accessor info.
|
||||
Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(obj);
|
||||
Address original_address =
|
||||
@ -165,17 +166,18 @@ void StartupSerializer::SerializeObjectImpl(Handle<HeapObject> obj) {
|
||||
Foreign::cast(info->js_getter())
|
||||
.set_foreign_address(isolate(), original_address);
|
||||
accessor_infos_.Push(*info);
|
||||
} else if (use_simulator && obj->IsCallHandlerInfo()) {
|
||||
} else if (use_simulator && obj->IsCallHandlerInfo(cage_base)) {
|
||||
Handle<CallHandlerInfo> info = Handle<CallHandlerInfo>::cast(obj);
|
||||
Address original_address =
|
||||
Foreign::cast(info->callback()).foreign_address(isolate());
|
||||
Foreign::cast(info->js_callback())
|
||||
.set_foreign_address(isolate(), original_address);
|
||||
call_handler_infos_.Push(*info);
|
||||
} else if (obj->IsScript() && Handle<Script>::cast(obj)->IsUserJavaScript()) {
|
||||
} else if (obj->IsScript(cage_base) &&
|
||||
Handle<Script>::cast(obj)->IsUserJavaScript()) {
|
||||
Handle<Script>::cast(obj)->set_context_data(
|
||||
ReadOnlyRoots(isolate()).uninitialized_symbol());
|
||||
} else if (obj->IsSharedFunctionInfo()) {
|
||||
} else if (obj->IsSharedFunctionInfo(cage_base)) {
|
||||
// Clear inferred name for native functions.
|
||||
Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(obj);
|
||||
if (!shared->IsSubjectToDebugging() && shared->HasUncompiledData()) {
|
||||
|
Loading…
Reference in New Issue
Block a user