1 // Copyright 2017 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/snapshot/builtin-serializer.h"
6
7 #include "src/interpreter/interpreter.h"
8 #include "src/objects-inl.h"
9 #include "src/snapshot/startup-serializer.h"
10
11 namespace v8 {
12 namespace internal {
13
14 using interpreter::Bytecode;
15 using interpreter::Bytecodes;
16 using interpreter::OperandScale;
17
BuiltinSerializer(Isolate * isolate,StartupSerializer * startup_serializer)18 BuiltinSerializer::BuiltinSerializer(Isolate* isolate,
19 StartupSerializer* startup_serializer)
20 : Serializer(isolate), startup_serializer_(startup_serializer) {}
21
~BuiltinSerializer()22 BuiltinSerializer::~BuiltinSerializer() {
23 OutputStatistics("BuiltinSerializer");
24 }
25
SerializeBuiltinsAndHandlers()26 void BuiltinSerializer::SerializeBuiltinsAndHandlers() {
27 // Serialize builtins.
28
29 STATIC_ASSERT(0 == BSU::kFirstBuiltinIndex);
30
31 for (int i = 0; i < BSU::kNumberOfBuiltins; i++) {
32 SetBuiltinOffset(i, sink_.Position());
33 SerializeBuiltin(isolate()->builtins()->builtin(i));
34 }
35
36 // Serialize bytecode handlers.
37
38 STATIC_ASSERT(BSU::kNumberOfBuiltins == BSU::kFirstHandlerIndex);
39
40 BSU::ForEachBytecode([=](Bytecode bytecode, OperandScale operand_scale) {
41 SetHandlerOffset(bytecode, operand_scale, sink_.Position());
42 if (!Bytecodes::BytecodeHasHandler(bytecode, operand_scale)) return;
43
44 SerializeHandler(
45 isolate()->interpreter()->GetBytecodeHandler(bytecode, operand_scale));
46 });
47
48 STATIC_ASSERT(BSU::kFirstHandlerIndex + BSU::kNumberOfHandlers ==
49 BSU::kNumberOfCodeObjects);
50
51 // The DeserializeLazy handlers are serialized by the StartupSerializer
52 // during strong root iteration.
53
54 DCHECK(isolate()->heap()->deserialize_lazy_handler()->IsCode());
55 DCHECK(isolate()->heap()->deserialize_lazy_handler_wide()->IsCode());
56 DCHECK(isolate()->heap()->deserialize_lazy_handler_extra_wide()->IsCode());
57
58 // Pad with kNop since GetInt() might read too far.
59 Pad();
60
61 // Append the offset table. During deserialization, the offset table is
62 // extracted by BuiltinSnapshotData.
63 const byte* data = reinterpret_cast<const byte*>(&code_offsets_[0]);
64 int data_length = static_cast<int>(sizeof(code_offsets_));
65 sink_.PutRaw(data, data_length, "BuiltinOffsets");
66 }
67
VisitRootPointers(Root root,const char * description,Object ** start,Object ** end)68 void BuiltinSerializer::VisitRootPointers(Root root, const char* description,
69 Object** start, Object** end) {
70 UNREACHABLE(); // We iterate manually in SerializeBuiltins.
71 }
72
SerializeBuiltin(Code * code)73 void BuiltinSerializer::SerializeBuiltin(Code* code) {
74 DCHECK_GE(code->builtin_index(), 0);
75
76 // All builtins are serialized unconditionally when the respective builtin is
77 // reached while iterating the builtins list. A builtin seen at any other
78 // time (e.g. startup snapshot creation, or while iterating a builtin code
79 // object during builtin serialization) is serialized by reference - see
80 // BuiltinSerializer::SerializeObject below.
81 ObjectSerializer object_serializer(this, code, &sink_, kPlain,
82 kStartOfObject);
83 object_serializer.Serialize();
84 }
85
SerializeHandler(Code * code)86 void BuiltinSerializer::SerializeHandler(Code* code) {
87 DCHECK(ObjectIsBytecodeHandler(code));
88 ObjectSerializer object_serializer(this, code, &sink_, kPlain,
89 kStartOfObject);
90 object_serializer.Serialize();
91 }
92
SerializeObject(HeapObject * o,HowToCode how_to_code,WhereToPoint where_to_point,int skip)93 void BuiltinSerializer::SerializeObject(HeapObject* o, HowToCode how_to_code,
94 WhereToPoint where_to_point, int skip) {
95 DCHECK(!o->IsSmi());
96
97 // Roots can simply be serialized as root references.
98 int root_index = root_index_map()->Lookup(o);
99 if (root_index != RootIndexMap::kInvalidRootIndex) {
100 DCHECK(startup_serializer_->root_has_been_serialized(root_index));
101 PutRoot(root_index, o, how_to_code, where_to_point, skip);
102 return;
103 }
104
105 // Builtins are serialized using a dedicated bytecode. We only reach this
106 // point if encountering a Builtin e.g. while iterating the body of another
107 // builtin.
108 if (SerializeBuiltinReference(o, how_to_code, where_to_point, skip)) return;
109
110 // Embedded objects are serialized as part of the partial snapshot cache.
111 // Currently we expect to see:
112 // * Code: Jump targets.
113 // * ByteArrays: Relocation infos.
114 // * FixedArrays: Handler tables.
115 // * Strings: CSA_ASSERTs in debug builds, various other string constants.
116 // * HeapNumbers: Embedded constants.
117 // TODO(6624): Jump targets should never trigger content serialization, it
118 // should always result in a reference instead. Reloc infos and handler
119 // tables should not end up in the partial snapshot cache.
120
121 FlushSkip(skip);
122
123 int cache_index = startup_serializer_->PartialSnapshotCacheIndex(o);
124 sink_.Put(kPartialSnapshotCache + how_to_code + where_to_point,
125 "PartialSnapshotCache");
126 sink_.PutInt(cache_index, "partial_snapshot_cache_index");
127 }
128
SetBuiltinOffset(int builtin_id,uint32_t offset)129 void BuiltinSerializer::SetBuiltinOffset(int builtin_id, uint32_t offset) {
130 DCHECK(Builtins::IsBuiltinId(builtin_id));
131 DCHECK(BSU::IsBuiltinIndex(builtin_id));
132 code_offsets_[builtin_id] = offset;
133 }
134
SetHandlerOffset(Bytecode bytecode,OperandScale operand_scale,uint32_t offset)135 void BuiltinSerializer::SetHandlerOffset(Bytecode bytecode,
136 OperandScale operand_scale,
137 uint32_t offset) {
138 const int index = BSU::BytecodeToIndex(bytecode, operand_scale);
139 DCHECK(BSU::IsHandlerIndex(index));
140 code_offsets_[index] = offset;
141 }
142
143 } // namespace internal
144 } // namespace v8
145