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