blob: 1a2e077407eaf0bd36c8115451f19e9827eeb205 [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"
10#include "src/profiler/cpu-profiler.h"
11#include "src/snapshot/deserializer.h"
12#include "src/version.h"
13
14namespace v8 {
15namespace internal {
16
17ScriptData* CodeSerializer::Serialize(Isolate* isolate,
18 Handle<SharedFunctionInfo> info,
19 Handle<String> source) {
20 base::ElapsedTimer timer;
21 if (FLAG_profile_deserialization) timer.Start();
22 if (FLAG_trace_serializer) {
23 PrintF("[Serializing from");
24 Object* script = info->script();
25 if (script->IsScript()) Script::cast(script)->name()->ShortPrint();
26 PrintF("]\n");
27 }
28
29 // Serialize code object.
30 SnapshotByteSink sink(info->code()->CodeSize() * 2);
31 CodeSerializer cs(isolate, &sink, *source);
32 DisallowHeapAllocation no_gc;
33 Object** location = Handle<Object>::cast(info).location();
34 cs.VisitPointer(location);
35 cs.SerializeDeferredObjects();
36 cs.Pad();
37
38 SerializedCodeData data(sink.data(), cs);
39 ScriptData* script_data = data.GetScriptData();
40
41 if (FLAG_profile_deserialization) {
42 double ms = timer.Elapsed().InMillisecondsF();
43 int length = script_data->length();
44 PrintF("[Serializing to %d bytes took %0.3f ms]\n", length, ms);
45 }
46
47 return script_data;
48}
49
50void CodeSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
51 WhereToPoint where_to_point, int skip) {
52 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
58 if (SerializeKnownObject(obj, how_to_code, where_to_point, skip)) return;
59
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.
109 ObjectSerializer serializer(this, heap_object, sink_, how_to_code,
110 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
127 sink_->Put(kBuiltin + how_to_code + where_to_point, "Builtin");
128 sink_->PutInt(builtin_index, "builtin_index");
129}
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
188 if (isolate->logger()->is_logging_code_events() ||
189 isolate->cpu_profiler()->is_profiling()) {
190 String* name = isolate->heap()->empty_string();
191 if (result->script()->IsScript()) {
192 Script* script = Script::cast(result->script());
193 if (script->name()->IsString()) name = String::cast(script->name());
194 }
Ben Murdochc5610432016-08-08 18:44:38 +0100195 isolate->logger()->CodeCreateEvent(Logger::SCRIPT_TAG,
196 result->abstract_code(), *result, name);
Ben Murdochda12d292016-06-02 14:46:10 +0100197 }
198 return scope.CloseAndEscape(result);
199}
200
201class Checksum {
202 public:
203 explicit Checksum(Vector<const byte> payload) {
204#ifdef MEMORY_SANITIZER
205 // Computing the checksum includes padding bytes for objects like strings.
206 // Mark every object as initialized in the code serializer.
207 MSAN_MEMORY_IS_INITIALIZED(payload.start(), payload.length());
208#endif // MEMORY_SANITIZER
209 // Fletcher's checksum. Modified to reduce 64-bit sums to 32-bit.
210 uintptr_t a = 1;
211 uintptr_t b = 0;
212 const uintptr_t* cur = reinterpret_cast<const uintptr_t*>(payload.start());
213 DCHECK(IsAligned(payload.length(), kIntptrSize));
214 const uintptr_t* end = cur + payload.length() / kIntptrSize;
215 while (cur < end) {
216 // Unsigned overflow expected and intended.
217 a += *cur++;
218 b += a;
219 }
220#if V8_HOST_ARCH_64_BIT
221 a ^= a >> 32;
222 b ^= b >> 32;
223#endif // V8_HOST_ARCH_64_BIT
224 a_ = static_cast<uint32_t>(a);
225 b_ = static_cast<uint32_t>(b);
226 }
227
228 bool Check(uint32_t a, uint32_t b) const { return a == a_ && b == b_; }
229
230 uint32_t a() const { return a_; }
231 uint32_t b() const { return b_; }
232
233 private:
234 uint32_t a_;
235 uint32_t b_;
236
237 DISALLOW_COPY_AND_ASSIGN(Checksum);
238};
239
240SerializedCodeData::SerializedCodeData(const List<byte>& payload,
241 const CodeSerializer& cs) {
242 DisallowHeapAllocation no_gc;
243 const List<uint32_t>* stub_keys = cs.stub_keys();
244
245 List<Reservation> reservations;
246 cs.EncodeReservations(&reservations);
247
248 // Calculate sizes.
249 int reservation_size = reservations.length() * kInt32Size;
250 int num_stub_keys = stub_keys->length();
251 int stub_keys_size = stub_keys->length() * kInt32Size;
252 int payload_offset = kHeaderSize + reservation_size + stub_keys_size;
253 int padded_payload_offset = POINTER_SIZE_ALIGN(payload_offset);
254 int size = padded_payload_offset + payload.length();
255
256 // Allocate backing store and create result data.
257 AllocateData(size);
258
259 // Set header values.
260 SetMagicNumber(cs.isolate());
261 SetHeaderValue(kVersionHashOffset, Version::Hash());
262 SetHeaderValue(kSourceHashOffset, SourceHash(cs.source()));
263 SetHeaderValue(kCpuFeaturesOffset,
264 static_cast<uint32_t>(CpuFeatures::SupportedFeatures()));
265 SetHeaderValue(kFlagHashOffset, FlagList::Hash());
266 SetHeaderValue(kNumReservationsOffset, reservations.length());
267 SetHeaderValue(kNumCodeStubKeysOffset, num_stub_keys);
268 SetHeaderValue(kPayloadLengthOffset, payload.length());
269
270 Checksum checksum(payload.ToConstVector());
271 SetHeaderValue(kChecksum1Offset, checksum.a());
272 SetHeaderValue(kChecksum2Offset, checksum.b());
273
274 // Copy reservation chunk sizes.
275 CopyBytes(data_ + kHeaderSize, reinterpret_cast<byte*>(reservations.begin()),
276 reservation_size);
277
278 // Copy code stub keys.
279 CopyBytes(data_ + kHeaderSize + reservation_size,
280 reinterpret_cast<byte*>(stub_keys->begin()), stub_keys_size);
281
282 memset(data_ + payload_offset, 0, padded_payload_offset - payload_offset);
283
284 // Copy serialized data.
285 CopyBytes(data_ + padded_payload_offset, payload.begin(),
286 static_cast<size_t>(payload.length()));
287}
288
289SerializedCodeData::SanityCheckResult SerializedCodeData::SanityCheck(
290 Isolate* isolate, String* source) const {
291 uint32_t magic_number = GetMagicNumber();
292 if (magic_number != ComputeMagicNumber(isolate)) return MAGIC_NUMBER_MISMATCH;
293 uint32_t version_hash = GetHeaderValue(kVersionHashOffset);
294 uint32_t source_hash = GetHeaderValue(kSourceHashOffset);
295 uint32_t cpu_features = GetHeaderValue(kCpuFeaturesOffset);
296 uint32_t flags_hash = GetHeaderValue(kFlagHashOffset);
297 uint32_t c1 = GetHeaderValue(kChecksum1Offset);
298 uint32_t c2 = GetHeaderValue(kChecksum2Offset);
299 if (version_hash != Version::Hash()) return VERSION_MISMATCH;
300 if (source_hash != SourceHash(source)) return SOURCE_MISMATCH;
301 if (cpu_features != static_cast<uint32_t>(CpuFeatures::SupportedFeatures())) {
302 return CPU_FEATURES_MISMATCH;
303 }
304 if (flags_hash != FlagList::Hash()) return FLAGS_MISMATCH;
305 if (!Checksum(Payload()).Check(c1, c2)) return CHECKSUM_MISMATCH;
306 return CHECK_SUCCESS;
307}
308
309uint32_t SerializedCodeData::SourceHash(String* source) const {
310 return source->length();
311}
312
313// Return ScriptData object and relinquish ownership over it to the caller.
314ScriptData* SerializedCodeData::GetScriptData() {
315 DCHECK(owns_data_);
316 ScriptData* result = new ScriptData(data_, size_);
317 result->AcquireDataOwnership();
318 owns_data_ = false;
319 data_ = NULL;
320 return result;
321}
322
323Vector<const SerializedData::Reservation> SerializedCodeData::Reservations()
324 const {
325 return Vector<const Reservation>(
326 reinterpret_cast<const Reservation*>(data_ + kHeaderSize),
327 GetHeaderValue(kNumReservationsOffset));
328}
329
330Vector<const byte> SerializedCodeData::Payload() const {
331 int reservations_size = GetHeaderValue(kNumReservationsOffset) * kInt32Size;
332 int code_stubs_size = GetHeaderValue(kNumCodeStubKeysOffset) * kInt32Size;
333 int payload_offset = kHeaderSize + reservations_size + code_stubs_size;
334 int padded_payload_offset = POINTER_SIZE_ALIGN(payload_offset);
335 const byte* payload = data_ + padded_payload_offset;
336 DCHECK(IsAligned(reinterpret_cast<intptr_t>(payload), kPointerAlignment));
337 int length = GetHeaderValue(kPayloadLengthOffset);
338 DCHECK_EQ(data_ + size_, payload + length);
339 return Vector<const byte>(payload, length);
340}
341
342Vector<const uint32_t> SerializedCodeData::CodeStubKeys() const {
343 int reservations_size = GetHeaderValue(kNumReservationsOffset) * kInt32Size;
344 const byte* start = data_ + kHeaderSize + reservations_size;
345 return Vector<const uint32_t>(reinterpret_cast<const uint32_t*>(start),
346 GetHeaderValue(kNumCodeStubKeysOffset));
347}
348
349SerializedCodeData::SerializedCodeData(ScriptData* data)
350 : SerializedData(const_cast<byte*>(data->data()), data->length()) {}
351
352SerializedCodeData* SerializedCodeData::FromCachedData(Isolate* isolate,
353 ScriptData* cached_data,
354 String* source) {
355 DisallowHeapAllocation no_gc;
356 SerializedCodeData* scd = new SerializedCodeData(cached_data);
357 SanityCheckResult r = scd->SanityCheck(isolate, source);
358 if (r == CHECK_SUCCESS) return scd;
359 cached_data->Reject();
360 source->GetIsolate()->counters()->code_cache_reject_reason()->AddSample(r);
361 delete scd;
362 return NULL;
363}
364
365} // namespace internal
366} // namespace v8