blob: 893c79c05e6358d5aea44612c785a018f41b18fb [file] [log] [blame]
// 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, 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