// Copyright 2017 the V8 project authors. All rights reserved.
|
// Use of this source code is governed by a BSD-style license that can be
|
// found in the LICENSE file.
|
|
#include "src/snapshot/builtin-serializer.h"
|
|
#include "src/interpreter/interpreter.h"
|
#include "src/objects-inl.h"
|
#include "src/snapshot/startup-serializer.h"
|
|
namespace v8 {
|
namespace internal {
|
|
using interpreter::Bytecode;
|
using interpreter::Bytecodes;
|
using interpreter::OperandScale;
|
|
BuiltinSerializer::BuiltinSerializer(Isolate* isolate,
|
StartupSerializer* startup_serializer)
|
: Serializer(isolate), startup_serializer_(startup_serializer) {}
|
|
BuiltinSerializer::~BuiltinSerializer() {
|
OutputStatistics("BuiltinSerializer");
|
}
|
|
void BuiltinSerializer::SerializeBuiltinsAndHandlers() {
|
// Serialize builtins.
|
|
STATIC_ASSERT(0 == BSU::kFirstBuiltinIndex);
|
|
for (int i = 0; i < BSU::kNumberOfBuiltins; i++) {
|
SetBuiltinOffset(i, sink_.Position());
|
SerializeBuiltin(isolate()->builtins()->builtin(i));
|
}
|
|
// Serialize bytecode handlers.
|
|
STATIC_ASSERT(BSU::kNumberOfBuiltins == BSU::kFirstHandlerIndex);
|
|
BSU::ForEachBytecode([=](Bytecode bytecode, OperandScale operand_scale) {
|
SetHandlerOffset(bytecode, operand_scale, sink_.Position());
|
if (!Bytecodes::BytecodeHasHandler(bytecode, operand_scale)) return;
|
|
SerializeHandler(
|
isolate()->interpreter()->GetBytecodeHandler(bytecode, operand_scale));
|
});
|
|
STATIC_ASSERT(BSU::kFirstHandlerIndex + BSU::kNumberOfHandlers ==
|
BSU::kNumberOfCodeObjects);
|
|
// The DeserializeLazy handlers are serialized by the StartupSerializer
|
// during strong root iteration.
|
|
DCHECK(isolate()->heap()->deserialize_lazy_handler()->IsCode());
|
DCHECK(isolate()->heap()->deserialize_lazy_handler_wide()->IsCode());
|
DCHECK(isolate()->heap()->deserialize_lazy_handler_extra_wide()->IsCode());
|
|
// Pad with kNop since GetInt() might read too far.
|
Pad();
|
|
// Append the offset table. During deserialization, the offset table is
|
// extracted by BuiltinSnapshotData.
|
const byte* data = reinterpret_cast<const byte*>(&code_offsets_[0]);
|
int data_length = static_cast<int>(sizeof(code_offsets_));
|
sink_.PutRaw(data, data_length, "BuiltinOffsets");
|
}
|
|
void BuiltinSerializer::VisitRootPointers(Root root, const char* description,
|
Object** start, Object** end) {
|
UNREACHABLE(); // We iterate manually in SerializeBuiltins.
|
}
|
|
void BuiltinSerializer::SerializeBuiltin(Code* code) {
|
DCHECK_GE(code->builtin_index(), 0);
|
|
// All builtins are serialized unconditionally when the respective builtin is
|
// reached while iterating the builtins list. A builtin seen at any other
|
// time (e.g. startup snapshot creation, or while iterating a builtin code
|
// object during builtin serialization) is serialized by reference - see
|
// BuiltinSerializer::SerializeObject below.
|
ObjectSerializer object_serializer(this, code, &sink_, kPlain,
|
kStartOfObject);
|
object_serializer.Serialize();
|
}
|
|
void BuiltinSerializer::SerializeHandler(Code* code) {
|
DCHECK(ObjectIsBytecodeHandler(code));
|
ObjectSerializer object_serializer(this, code, &sink_, kPlain,
|
kStartOfObject);
|
object_serializer.Serialize();
|
}
|
|
void BuiltinSerializer::SerializeObject(HeapObject* o, HowToCode how_to_code,
|
WhereToPoint where_to_point, int skip) {
|
DCHECK(!o->IsSmi());
|
|
// Roots can simply be serialized as root references.
|
int root_index = root_index_map()->Lookup(o);
|
if (root_index != RootIndexMap::kInvalidRootIndex) {
|
DCHECK(startup_serializer_->root_has_been_serialized(root_index));
|
PutRoot(root_index, o, how_to_code, where_to_point, skip);
|
return;
|
}
|
|
// Builtins are serialized using a dedicated bytecode. We only reach this
|
// point if encountering a Builtin e.g. while iterating the body of another
|
// builtin.
|
if (SerializeBuiltinReference(o, how_to_code, where_to_point, skip)) return;
|
|
// Embedded objects are serialized as part of the partial snapshot cache.
|
// Currently we expect to see:
|
// * Code: Jump targets.
|
// * ByteArrays: Relocation infos.
|
// * FixedArrays: Handler tables.
|
// * Strings: CSA_ASSERTs in debug builds, various other string constants.
|
// * HeapNumbers: Embedded constants.
|
// TODO(6624): Jump targets should never trigger content serialization, it
|
// should always result in a reference instead. Reloc infos and handler
|
// tables should not end up in the partial snapshot cache.
|
|
FlushSkip(skip);
|
|
int cache_index = startup_serializer_->PartialSnapshotCacheIndex(o);
|
sink_.Put(kPartialSnapshotCache + how_to_code + where_to_point,
|
"PartialSnapshotCache");
|
sink_.PutInt(cache_index, "partial_snapshot_cache_index");
|
}
|
|
void BuiltinSerializer::SetBuiltinOffset(int builtin_id, uint32_t offset) {
|
DCHECK(Builtins::IsBuiltinId(builtin_id));
|
DCHECK(BSU::IsBuiltinIndex(builtin_id));
|
code_offsets_[builtin_id] = offset;
|
}
|
|
void BuiltinSerializer::SetHandlerOffset(Bytecode bytecode,
|
OperandScale operand_scale,
|
uint32_t offset) {
|
const int index = BSU::BytecodeToIndex(bytecode, operand_scale);
|
DCHECK(BSU::IsHandlerIndex(index));
|
code_offsets_[index] = offset;
|
}
|
|
} // namespace internal
|
} // namespace v8
|