blob: 4229607c75a2173664ea038bedc0c42a6f4777c3 [file] [log] [blame]
Ben Murdochda12d292016-06-02 14:46:10 +01001// Copyright 2016 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/code-serializer.h"
6
7#include "src/code-stubs.h"
8#include "src/log.h"
9#include "src/macro-assembler.h"
Ben Murdochda12d292016-06-02 14:46:10 +010010#include "src/snapshot/deserializer.h"
11#include "src/version.h"
12
13namespace v8 {
14namespace internal {
15
16ScriptData* CodeSerializer::Serialize(Isolate* isolate,
17 Handle<SharedFunctionInfo> info,
18 Handle<String> source) {
19 base::ElapsedTimer timer;
20 if (FLAG_profile_deserialization) timer.Start();
21 if (FLAG_trace_serializer) {
22 PrintF("[Serializing from");
23 Object* script = info->script();
24 if (script->IsScript()) Script::cast(script)->name()->ShortPrint();
25 PrintF("]\n");
26 }
27
28 // Serialize code object.
Ben Murdoch61f157c2016-09-16 13:49:30 +010029 CodeSerializer cs(isolate, *source);
Ben Murdochda12d292016-06-02 14:46:10 +010030 DisallowHeapAllocation no_gc;
31 Object** location = Handle<Object>::cast(info).location();
32 cs.VisitPointer(location);
33 cs.SerializeDeferredObjects();
34 cs.Pad();
35
Ben Murdoch61f157c2016-09-16 13:49:30 +010036 SerializedCodeData data(cs.sink()->data(), &cs);
Ben Murdochda12d292016-06-02 14:46:10 +010037 ScriptData* script_data = data.GetScriptData();
38
39 if (FLAG_profile_deserialization) {
40 double ms = timer.Elapsed().InMillisecondsF();
41 int length = script_data->length();
42 PrintF("[Serializing to %d bytes took %0.3f ms]\n", length, ms);
43 }
44
45 return script_data;
46}
47
48void CodeSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
49 WhereToPoint where_to_point, int skip) {
Ben Murdoch61f157c2016-09-16 13:49:30 +010050 if (SerializeHotObject(obj, how_to_code, where_to_point, skip)) return;
51
Ben Murdochda12d292016-06-02 14:46:10 +010052 int root_index = root_index_map_.Lookup(obj);
53 if (root_index != RootIndexMap::kInvalidRootIndex) {
54 PutRoot(root_index, obj, how_to_code, where_to_point, skip);
55 return;
56 }
57
Ben Murdoch61f157c2016-09-16 13:49:30 +010058 if (SerializeBackReference(obj, how_to_code, where_to_point, skip)) return;
Ben Murdochda12d292016-06-02 14:46:10 +010059
60 FlushSkip(skip);
61
62 if (obj->IsCode()) {
63 Code* code_object = Code::cast(obj);
64 switch (code_object->kind()) {
65 case Code::OPTIMIZED_FUNCTION: // No optimized code compiled yet.
66 case Code::HANDLER: // No handlers patched in yet.
67 case Code::REGEXP: // No regexp literals initialized yet.
68 case Code::NUMBER_OF_KINDS: // Pseudo enum value.
69 case Code::BYTECODE_HANDLER: // No direct references to handlers.
70 CHECK(false);
71 case Code::BUILTIN:
72 SerializeBuiltin(code_object->builtin_index(), how_to_code,
73 where_to_point);
74 return;
75 case Code::STUB:
Ben Murdochda12d292016-06-02 14:46:10 +010076#define IC_KIND_CASE(KIND) case Code::KIND:
77 IC_KIND_LIST(IC_KIND_CASE)
78#undef IC_KIND_CASE
Ben Murdochc5610432016-08-08 18:44:38 +010079 SerializeCodeStub(code_object, how_to_code, where_to_point);
Ben Murdochda12d292016-06-02 14:46:10 +010080 return;
81 case Code::FUNCTION:
82 DCHECK(code_object->has_reloc_info_for_serialization());
83 SerializeGeneric(code_object, how_to_code, where_to_point);
84 return;
85 case Code::WASM_FUNCTION:
86 case Code::WASM_TO_JS_FUNCTION:
87 case Code::JS_TO_WASM_FUNCTION:
88 UNREACHABLE();
89 }
90 UNREACHABLE();
91 }
92
93 // Past this point we should not see any (context-specific) maps anymore.
94 CHECK(!obj->IsMap());
95 // There should be no references to the global object embedded.
96 CHECK(!obj->IsJSGlobalProxy() && !obj->IsJSGlobalObject());
97 // There should be no hash table embedded. They would require rehashing.
98 CHECK(!obj->IsHashTable());
99 // We expect no instantiated function objects or contexts.
100 CHECK(!obj->IsJSFunction() && !obj->IsContext());
101
102 SerializeGeneric(obj, how_to_code, where_to_point);
103}
104
105void CodeSerializer::SerializeGeneric(HeapObject* heap_object,
106 HowToCode how_to_code,
107 WhereToPoint where_to_point) {
108 // Object has not yet been serialized. Serialize it here.
Ben Murdoch61f157c2016-09-16 13:49:30 +0100109 ObjectSerializer serializer(this, heap_object, &sink_, how_to_code,
Ben Murdochda12d292016-06-02 14:46:10 +0100110 where_to_point);
111 serializer.Serialize();
112}
113
114void CodeSerializer::SerializeBuiltin(int builtin_index, HowToCode how_to_code,
115 WhereToPoint where_to_point) {
116 DCHECK((how_to_code == kPlain && where_to_point == kStartOfObject) ||
117 (how_to_code == kPlain && where_to_point == kInnerPointer) ||
118 (how_to_code == kFromCode && where_to_point == kInnerPointer));
119 DCHECK_LT(builtin_index, Builtins::builtin_count);
120 DCHECK_LE(0, builtin_index);
121
122 if (FLAG_trace_serializer) {
123 PrintF(" Encoding builtin: %s\n",
124 isolate()->builtins()->name(builtin_index));
125 }
126
Ben Murdoch61f157c2016-09-16 13:49:30 +0100127 sink_.Put(kBuiltin + how_to_code + where_to_point, "Builtin");
128 sink_.PutInt(builtin_index, "builtin_index");
Ben Murdochda12d292016-06-02 14:46:10 +0100129}
130
Ben Murdochc5610432016-08-08 18:44:38 +0100131void CodeSerializer::SerializeCodeStub(Code* code_stub, HowToCode how_to_code,
Ben Murdochda12d292016-06-02 14:46:10 +0100132 WhereToPoint where_to_point) {
Ben Murdochc5610432016-08-08 18:44:38 +0100133 // We only arrive here if we have not encountered this code stub before.
134 DCHECK(!reference_map()->Lookup(code_stub).is_valid());
135 uint32_t stub_key = code_stub->stub_key();
Ben Murdochda12d292016-06-02 14:46:10 +0100136 DCHECK(CodeStub::MajorKeyFromKey(stub_key) != CodeStub::NoCache);
137 DCHECK(!CodeStub::GetCode(isolate(), stub_key).is_null());
Ben Murdochda12d292016-06-02 14:46:10 +0100138 stub_keys_.Add(stub_key);
Ben Murdochc5610432016-08-08 18:44:38 +0100139
140 SerializerReference reference =
141 reference_map()->AddAttachedReference(code_stub);
142 if (FLAG_trace_serializer) {
143 PrintF(" Encoding code stub %s as attached reference %d\n",
144 CodeStub::MajorName(CodeStub::MajorKeyFromKey(stub_key)),
145 reference.attached_reference_index());
146 }
147 PutAttachedReference(reference, how_to_code, where_to_point);
Ben Murdochda12d292016-06-02 14:46:10 +0100148}
149
150MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
151 Isolate* isolate, ScriptData* cached_data, Handle<String> source) {
152 base::ElapsedTimer timer;
153 if (FLAG_profile_deserialization) timer.Start();
154
155 HandleScope scope(isolate);
156
157 base::SmartPointer<SerializedCodeData> scd(
158 SerializedCodeData::FromCachedData(isolate, cached_data, *source));
159 if (scd.is_empty()) {
160 if (FLAG_profile_deserialization) PrintF("[Cached code failed check]\n");
161 DCHECK(cached_data->rejected());
162 return MaybeHandle<SharedFunctionInfo>();
163 }
164
Ben Murdochda12d292016-06-02 14:46:10 +0100165 Deserializer deserializer(scd.get());
Ben Murdochc5610432016-08-08 18:44:38 +0100166 deserializer.AddAttachedObject(source);
167 Vector<const uint32_t> code_stub_keys = scd->CodeStubKeys();
168 for (int i = 0; i < code_stub_keys.length(); i++) {
169 deserializer.AddAttachedObject(
170 CodeStub::GetCode(isolate, code_stub_keys[i]).ToHandleChecked());
171 }
Ben Murdochda12d292016-06-02 14:46:10 +0100172
173 // Deserialize.
174 Handle<SharedFunctionInfo> result;
175 if (!deserializer.DeserializeCode(isolate).ToHandle(&result)) {
176 // Deserializing may fail if the reservations cannot be fulfilled.
177 if (FLAG_profile_deserialization) PrintF("[Deserializing failed]\n");
178 return MaybeHandle<SharedFunctionInfo>();
179 }
180
181 if (FLAG_profile_deserialization) {
182 double ms = timer.Elapsed().InMillisecondsF();
183 int length = cached_data->length();
184 PrintF("[Deserializing from %d bytes took %0.3f ms]\n", length, ms);
185 }
186 result->set_deserialized(true);
187
Ben Murdoch61f157c2016-09-16 13:49:30 +0100188 if (isolate->logger()->is_logging_code_events() || isolate->is_profiling()) {
Ben Murdochda12d292016-06-02 14:46:10 +0100189 String* name = isolate->heap()->empty_string();
190 if (result->script()->IsScript()) {
191 Script* script = Script::cast(result->script());
192 if (script->name()->IsString()) name = String::cast(script->name());
193 }
Ben Murdoch61f157c2016-09-16 13:49:30 +0100194 PROFILE(isolate, CodeCreateEvent(CodeEventListener::SCRIPT_TAG,
195 result->abstract_code(), *result, name));
Ben Murdochda12d292016-06-02 14:46:10 +0100196 }
197 return scope.CloseAndEscape(result);
198}
199
200class Checksum {
201 public:
202 explicit Checksum(Vector<const byte> payload) {
203#ifdef MEMORY_SANITIZER
204 // Computing the checksum includes padding bytes for objects like strings.
205 // Mark every object as initialized in the code serializer.
206 MSAN_MEMORY_IS_INITIALIZED(payload.start(), payload.length());
207#endif // MEMORY_SANITIZER
208 // Fletcher's checksum. Modified to reduce 64-bit sums to 32-bit.
209 uintptr_t a = 1;
210 uintptr_t b = 0;
211 const uintptr_t* cur = reinterpret_cast<const uintptr_t*>(payload.start());
212 DCHECK(IsAligned(payload.length(), kIntptrSize));
213 const uintptr_t* end = cur + payload.length() / kIntptrSize;
214 while (cur < end) {
215 // Unsigned overflow expected and intended.
216 a += *cur++;
217 b += a;
218 }
219#if V8_HOST_ARCH_64_BIT
220 a ^= a >> 32;
221 b ^= b >> 32;
222#endif // V8_HOST_ARCH_64_BIT
223 a_ = static_cast<uint32_t>(a);
224 b_ = static_cast<uint32_t>(b);
225 }
226
227 bool Check(uint32_t a, uint32_t b) const { return a == a_ && b == b_; }
228
229 uint32_t a() const { return a_; }
230 uint32_t b() const { return b_; }
231
232 private:
233 uint32_t a_;
234 uint32_t b_;
235
236 DISALLOW_COPY_AND_ASSIGN(Checksum);
237};
238
Ben Murdoch61f157c2016-09-16 13:49:30 +0100239SerializedCodeData::SerializedCodeData(const List<byte>* payload,
240 const CodeSerializer* cs) {
Ben Murdochda12d292016-06-02 14:46:10 +0100241 DisallowHeapAllocation no_gc;
Ben Murdoch61f157c2016-09-16 13:49:30 +0100242 const List<uint32_t>* stub_keys = cs->stub_keys();
Ben Murdochda12d292016-06-02 14:46:10 +0100243
244 List<Reservation> reservations;
Ben Murdoch61f157c2016-09-16 13:49:30 +0100245 cs->EncodeReservations(&reservations);
Ben Murdochda12d292016-06-02 14:46:10 +0100246
247 // Calculate sizes.
248 int reservation_size = reservations.length() * kInt32Size;
249 int num_stub_keys = stub_keys->length();
250 int stub_keys_size = stub_keys->length() * kInt32Size;
251 int payload_offset = kHeaderSize + reservation_size + stub_keys_size;
252 int padded_payload_offset = POINTER_SIZE_ALIGN(payload_offset);
Ben Murdoch61f157c2016-09-16 13:49:30 +0100253 int size = padded_payload_offset + payload->length();
Ben Murdochda12d292016-06-02 14:46:10 +0100254
255 // Allocate backing store and create result data.
256 AllocateData(size);
257
258 // Set header values.
Ben Murdoch61f157c2016-09-16 13:49:30 +0100259 SetMagicNumber(cs->isolate());
Ben Murdochda12d292016-06-02 14:46:10 +0100260 SetHeaderValue(kVersionHashOffset, Version::Hash());
Ben Murdoch61f157c2016-09-16 13:49:30 +0100261 SetHeaderValue(kSourceHashOffset, SourceHash(cs->source()));
Ben Murdochda12d292016-06-02 14:46:10 +0100262 SetHeaderValue(kCpuFeaturesOffset,
263 static_cast<uint32_t>(CpuFeatures::SupportedFeatures()));
264 SetHeaderValue(kFlagHashOffset, FlagList::Hash());
265 SetHeaderValue(kNumReservationsOffset, reservations.length());
266 SetHeaderValue(kNumCodeStubKeysOffset, num_stub_keys);
Ben Murdoch61f157c2016-09-16 13:49:30 +0100267 SetHeaderValue(kPayloadLengthOffset, payload->length());
Ben Murdochda12d292016-06-02 14:46:10 +0100268
Ben Murdoch61f157c2016-09-16 13:49:30 +0100269 Checksum checksum(payload->ToConstVector());
Ben Murdochda12d292016-06-02 14:46:10 +0100270 SetHeaderValue(kChecksum1Offset, checksum.a());
271 SetHeaderValue(kChecksum2Offset, checksum.b());
272
273 // Copy reservation chunk sizes.
274 CopyBytes(data_ + kHeaderSize, reinterpret_cast<byte*>(reservations.begin()),
275 reservation_size);
276
277 // Copy code stub keys.
278 CopyBytes(data_ + kHeaderSize + reservation_size,
279 reinterpret_cast<byte*>(stub_keys->begin()), stub_keys_size);
280
281 memset(data_ + payload_offset, 0, padded_payload_offset - payload_offset);
282
283 // Copy serialized data.
Ben Murdoch61f157c2016-09-16 13:49:30 +0100284 CopyBytes(data_ + padded_payload_offset, payload->begin(),
285 static_cast<size_t>(payload->length()));
Ben Murdochda12d292016-06-02 14:46:10 +0100286}
287
288SerializedCodeData::SanityCheckResult SerializedCodeData::SanityCheck(
289 Isolate* isolate, String* source) const {
290 uint32_t magic_number = GetMagicNumber();
291 if (magic_number != ComputeMagicNumber(isolate)) return MAGIC_NUMBER_MISMATCH;
292 uint32_t version_hash = GetHeaderValue(kVersionHashOffset);
293 uint32_t source_hash = GetHeaderValue(kSourceHashOffset);
294 uint32_t cpu_features = GetHeaderValue(kCpuFeaturesOffset);
295 uint32_t flags_hash = GetHeaderValue(kFlagHashOffset);
296 uint32_t c1 = GetHeaderValue(kChecksum1Offset);
297 uint32_t c2 = GetHeaderValue(kChecksum2Offset);
298 if (version_hash != Version::Hash()) return VERSION_MISMATCH;
299 if (source_hash != SourceHash(source)) return SOURCE_MISMATCH;
300 if (cpu_features != static_cast<uint32_t>(CpuFeatures::SupportedFeatures())) {
301 return CPU_FEATURES_MISMATCH;
302 }
303 if (flags_hash != FlagList::Hash()) return FLAGS_MISMATCH;
304 if (!Checksum(Payload()).Check(c1, c2)) return CHECKSUM_MISMATCH;
305 return CHECK_SUCCESS;
306}
307
308uint32_t SerializedCodeData::SourceHash(String* source) const {
309 return source->length();
310}
311
312// Return ScriptData object and relinquish ownership over it to the caller.
313ScriptData* SerializedCodeData::GetScriptData() {
314 DCHECK(owns_data_);
315 ScriptData* result = new ScriptData(data_, size_);
316 result->AcquireDataOwnership();
317 owns_data_ = false;
318 data_ = NULL;
319 return result;
320}
321
322Vector<const SerializedData::Reservation> SerializedCodeData::Reservations()
323 const {
324 return Vector<const Reservation>(
325 reinterpret_cast<const Reservation*>(data_ + kHeaderSize),
326 GetHeaderValue(kNumReservationsOffset));
327}
328
329Vector<const byte> SerializedCodeData::Payload() const {
330 int reservations_size = GetHeaderValue(kNumReservationsOffset) * kInt32Size;
331 int code_stubs_size = GetHeaderValue(kNumCodeStubKeysOffset) * kInt32Size;
332 int payload_offset = kHeaderSize + reservations_size + code_stubs_size;
333 int padded_payload_offset = POINTER_SIZE_ALIGN(payload_offset);
334 const byte* payload = data_ + padded_payload_offset;
335 DCHECK(IsAligned(reinterpret_cast<intptr_t>(payload), kPointerAlignment));
336 int length = GetHeaderValue(kPayloadLengthOffset);
337 DCHECK_EQ(data_ + size_, payload + length);
338 return Vector<const byte>(payload, length);
339}
340
341Vector<const uint32_t> SerializedCodeData::CodeStubKeys() const {
342 int reservations_size = GetHeaderValue(kNumReservationsOffset) * kInt32Size;
343 const byte* start = data_ + kHeaderSize + reservations_size;
344 return Vector<const uint32_t>(reinterpret_cast<const uint32_t*>(start),
345 GetHeaderValue(kNumCodeStubKeysOffset));
346}
347
348SerializedCodeData::SerializedCodeData(ScriptData* data)
349 : SerializedData(const_cast<byte*>(data->data()), data->length()) {}
350
351SerializedCodeData* SerializedCodeData::FromCachedData(Isolate* isolate,
352 ScriptData* cached_data,
353 String* source) {
354 DisallowHeapAllocation no_gc;
355 SerializedCodeData* scd = new SerializedCodeData(cached_data);
356 SanityCheckResult r = scd->SanityCheck(isolate, source);
357 if (r == CHECK_SUCCESS) return scd;
358 cached_data->Reject();
359 source->GetIsolate()->counters()->code_cache_reject_reason()->AddSample(r);
360 delete scd;
361 return NULL;
362}
363
364} // namespace internal
365} // namespace v8