Line data Source code
1 : // 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 <memory>
8 :
9 : #include "src/code-stubs.h"
10 : #include "src/counters.h"
11 : #include "src/log.h"
12 : #include "src/macro-assembler.h"
13 : #include "src/objects-inl.h"
14 : #include "src/snapshot/object-deserializer.h"
15 : #include "src/snapshot/snapshot.h"
16 : #include "src/version.h"
17 : #include "src/visitors.h"
18 : #include "src/wasm/wasm-module.h"
19 : #include "src/wasm/wasm-objects-inl.h"
20 :
21 : namespace v8 {
22 : namespace internal {
23 :
24 305 : ScriptData* CodeSerializer::Serialize(Isolate* isolate,
25 : Handle<SharedFunctionInfo> info,
26 : Handle<String> source) {
27 : base::ElapsedTimer timer;
28 305 : if (FLAG_profile_deserialization) timer.Start();
29 305 : if (FLAG_trace_serializer) {
30 0 : PrintF("[Serializing from");
31 : Object* script = info->script();
32 0 : if (script->IsScript()) Script::cast(script)->name()->ShortPrint();
33 0 : PrintF("]\n");
34 : }
35 :
36 : // Serialize code object.
37 : CodeSerializer cs(isolate, SerializedCodeData::SourceHash(source));
38 : DisallowHeapAllocation no_gc;
39 305 : cs.reference_map()->AddAttachedReference(*source);
40 305 : ScriptData* ret = cs.Serialize(info);
41 :
42 305 : if (FLAG_profile_deserialization) {
43 0 : double ms = timer.Elapsed().InMillisecondsF();
44 : int length = ret->length();
45 0 : PrintF("[Serializing to %d bytes took %0.3f ms]\n", length, ms);
46 : }
47 :
48 305 : return ret;
49 : }
50 :
51 418 : ScriptData* CodeSerializer::Serialize(Handle<HeapObject> obj) {
52 : DisallowHeapAllocation no_gc;
53 :
54 418 : VisitRootPointer(Root::kHandleScope, Handle<Object>::cast(obj).location());
55 418 : SerializeDeferredObjects();
56 418 : Pad();
57 :
58 418 : SerializedCodeData data(sink_.data(), this);
59 :
60 836 : return data.GetScriptData();
61 : }
62 :
63 337501 : void CodeSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
64 : WhereToPoint where_to_point, int skip) {
65 338395 : if (SerializeHotObject(obj, how_to_code, where_to_point, skip)) return;
66 :
67 240973 : int root_index = root_index_map()->Lookup(obj);
68 240973 : if (root_index != RootIndexMap::kInvalidRootIndex) {
69 151437 : PutRoot(root_index, obj, how_to_code, where_to_point, skip);
70 151437 : return;
71 : }
72 :
73 89536 : if (SerializeBackReference(obj, how_to_code, where_to_point, skip)) return;
74 :
75 64938 : FlushSkip(skip);
76 :
77 64938 : if (obj->IsCode()) {
78 : Code* code_object = Code::cast(obj);
79 14208 : switch (code_object->kind()) {
80 : case Code::OPTIMIZED_FUNCTION: // No optimized code compiled yet.
81 : case Code::REGEXP: // No regexp literals initialized yet.
82 : case Code::NUMBER_OF_KINDS: // Pseudo enum value.
83 : case Code::BYTECODE_HANDLER: // No direct references to handlers.
84 0 : CHECK(false);
85 : case Code::BUILTIN:
86 13799 : SerializeBuiltinReference(code_object, how_to_code, where_to_point, 0);
87 13799 : return;
88 : case Code::STUB:
89 93 : if (code_object->builtin_index() == -1) {
90 93 : SerializeCodeStub(code_object, how_to_code, where_to_point);
91 : } else {
92 : SerializeBuiltinReference(code_object, how_to_code, where_to_point,
93 0 : 0);
94 : }
95 : return;
96 : default:
97 316 : return SerializeCodeObject(code_object, how_to_code, where_to_point);
98 : }
99 : UNREACHABLE();
100 : }
101 :
102 50730 : if (ElideObject(obj)) {
103 476 : return SerializeObject(isolate()->heap()->undefined_value(), how_to_code,
104 952 : where_to_point, skip);
105 : }
106 :
107 50254 : if (obj->IsScript()) {
108 : // Wrapper object is a context-dependent JSValue. Reset it here.
109 418 : Script::cast(obj)->set_wrapper(isolate()->heap()->undefined_value());
110 : }
111 :
112 50254 : if (obj->IsSharedFunctionInfo()) {
113 : SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj);
114 : // Mark SFI to indicate whether the code is cached.
115 : bool was_deserialized = sfi->deserialized();
116 6808 : sfi->set_deserialized(sfi->is_compiled());
117 6808 : SerializeGeneric(obj, how_to_code, where_to_point);
118 6808 : sfi->set_deserialized(was_deserialized);
119 6808 : return;
120 : }
121 :
122 : // Past this point we should not see any (context-specific) maps anymore.
123 43446 : CHECK(!obj->IsMap());
124 : // There should be no references to the global object embedded.
125 86892 : CHECK(!obj->IsJSGlobalProxy() && !obj->IsJSGlobalObject());
126 : // There should be no hash table embedded. They would require rehashing.
127 43446 : CHECK(!obj->IsHashTable());
128 : // We expect no instantiated function objects or contexts.
129 86892 : CHECK(!obj->IsJSFunction() && !obj->IsContext());
130 :
131 43446 : SerializeGeneric(obj, how_to_code, where_to_point);
132 : }
133 :
134 50530 : void CodeSerializer::SerializeGeneric(HeapObject* heap_object,
135 : HowToCode how_to_code,
136 : WhereToPoint where_to_point) {
137 : // Object has not yet been serialized. Serialize it here.
138 : ObjectSerializer serializer(this, heap_object, &sink_, how_to_code,
139 50530 : where_to_point);
140 50530 : serializer.Serialize();
141 50530 : }
142 :
143 93 : void CodeSerializer::SerializeCodeStub(Code* code_stub, HowToCode how_to_code,
144 : WhereToPoint where_to_point) {
145 : // We only arrive here if we have not encountered this code stub before.
146 : DCHECK(!reference_map()->Lookup(code_stub).is_valid());
147 93 : uint32_t stub_key = code_stub->stub_key();
148 : DCHECK(CodeStub::MajorKeyFromKey(stub_key) != CodeStub::NoCache);
149 : DCHECK(!CodeStub::GetCode(isolate(), stub_key).is_null());
150 93 : stub_keys_.push_back(stub_key);
151 :
152 : SerializerReference reference =
153 93 : reference_map()->AddAttachedReference(code_stub);
154 93 : if (FLAG_trace_serializer) {
155 : PrintF(" Encoding code stub %s as attached reference %d\n",
156 : CodeStub::MajorName(CodeStub::MajorKeyFromKey(stub_key)),
157 0 : reference.attached_reference_index());
158 : }
159 93 : PutAttachedReference(reference, how_to_code, where_to_point);
160 93 : }
161 :
162 235 : MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
163 430 : Isolate* isolate, ScriptData* cached_data, Handle<String> source) {
164 : base::ElapsedTimer timer;
165 235 : if (FLAG_profile_deserialization) timer.Start();
166 :
167 : HandleScope scope(isolate);
168 :
169 : SerializedCodeData::SanityCheckResult sanity_check_result =
170 235 : SerializedCodeData::CHECK_SUCCESS;
171 : const SerializedCodeData scd = SerializedCodeData::FromCachedData(
172 : isolate, cached_data, SerializedCodeData::SourceHash(source),
173 235 : &sanity_check_result);
174 235 : if (sanity_check_result != SerializedCodeData::CHECK_SUCCESS) {
175 15 : if (FLAG_profile_deserialization) PrintF("[Cached code failed check]\n");
176 : DCHECK(cached_data->rejected());
177 : source->GetIsolate()->counters()->code_cache_reject_reason()->AddSample(
178 30 : sanity_check_result);
179 15 : return MaybeHandle<SharedFunctionInfo>();
180 : }
181 :
182 : // Deserialize.
183 : MaybeHandle<SharedFunctionInfo> maybe_result =
184 220 : ObjectDeserializer::DeserializeSharedFunctionInfo(isolate, &scd, source);
185 :
186 : Handle<SharedFunctionInfo> result;
187 220 : if (!maybe_result.ToHandle(&result)) {
188 : // Deserializing may fail if the reservations cannot be fulfilled.
189 0 : if (FLAG_profile_deserialization) PrintF("[Deserializing failed]\n");
190 0 : return MaybeHandle<SharedFunctionInfo>();
191 : }
192 :
193 220 : if (FLAG_profile_deserialization) {
194 0 : double ms = timer.Elapsed().InMillisecondsF();
195 : int length = cached_data->length();
196 0 : PrintF("[Deserializing from %d bytes took %0.3f ms]\n", length, ms);
197 : }
198 :
199 430 : if (isolate->logger()->is_logging_code_events() || isolate->is_profiling()) {
200 10 : String* name = isolate->heap()->empty_string();
201 10 : if (result->script()->IsScript()) {
202 : Script* script = Script::cast(result->script());
203 10 : if (script->name()->IsString()) name = String::cast(script->name());
204 : }
205 10 : PROFILE(isolate, CodeCreateEvent(CodeEventListener::SCRIPT_TAG,
206 : result->abstract_code(), *result, name));
207 : }
208 220 : return scope.CloseAndEscape(result);
209 : }
210 :
211 113 : WasmCompiledModuleSerializer::WasmCompiledModuleSerializer(
212 : Isolate* isolate, uint32_t source_hash, Handle<Context> native_context,
213 : Handle<SeqOneByteString> module_bytes)
214 113 : : CodeSerializer(isolate, source_hash) {
215 226 : reference_map()->AddAttachedReference(*isolate->native_context());
216 113 : reference_map()->AddAttachedReference(*module_bytes);
217 113 : }
218 :
219 113 : std::unique_ptr<ScriptData> WasmCompiledModuleSerializer::SerializeWasmModule(
220 : Isolate* isolate, Handle<FixedArray> input) {
221 : Handle<WasmCompiledModule> compiled_module =
222 : Handle<WasmCompiledModule>::cast(input);
223 : WasmCompiledModuleSerializer wasm_cs(isolate, 0, isolate->native_context(),
224 113 : handle(compiled_module->module_bytes()));
225 113 : ScriptData* data = wasm_cs.Serialize(compiled_module);
226 113 : return std::unique_ptr<ScriptData>(data);
227 : }
228 :
229 132 : MaybeHandle<FixedArray> WasmCompiledModuleSerializer::DeserializeWasmModule(
230 : Isolate* isolate, ScriptData* data, Vector<const byte> wire_bytes) {
231 : MaybeHandle<FixedArray> nothing;
232 132 : if (!wasm::IsWasmCodegenAllowed(isolate, isolate->native_context())) {
233 6 : return nothing;
234 : }
235 : SerializedCodeData::SanityCheckResult sanity_check_result =
236 126 : SerializedCodeData::CHECK_SUCCESS;
237 :
238 : const SerializedCodeData scd = SerializedCodeData::FromCachedData(
239 126 : isolate, data, 0, &sanity_check_result);
240 :
241 126 : if (sanity_check_result != SerializedCodeData::CHECK_SUCCESS) {
242 46 : return nothing;
243 : }
244 :
245 : MaybeHandle<WasmCompiledModule> maybe_result =
246 : ObjectDeserializer::DeserializeWasmCompiledModule(isolate, &scd,
247 80 : wire_bytes);
248 :
249 : Handle<WasmCompiledModule> result;
250 80 : if (!maybe_result.ToHandle(&result)) return nothing;
251 :
252 80 : WasmCompiledModule::ReinitializeAfterDeserialization(isolate, result);
253 : DCHECK(WasmCompiledModule::IsWasmCompiledModule(*result));
254 80 : return result;
255 : }
256 :
257 316 : void WasmCompiledModuleSerializer::SerializeCodeObject(
258 : Code* code_object, HowToCode how_to_code, WhereToPoint where_to_point) {
259 : Code::Kind kind = code_object->kind();
260 316 : switch (kind) {
261 : case Code::WASM_FUNCTION:
262 : case Code::JS_TO_WASM_FUNCTION: {
263 : // Because the trap handler index is not meaningful across copies and
264 : // serializations, we need to serialize it as kInvalidIndex. We do this by
265 : // saving the old value, setting the index to kInvalidIndex and then
266 : // restoring the old value.
267 : const int old_trap_handler_index =
268 : code_object->trap_handler_index()->value();
269 : code_object->set_trap_handler_index(
270 276 : Smi::FromInt(trap_handler::kInvalidIndex));
271 :
272 : // Just serialize the code_object.
273 276 : SerializeGeneric(code_object, how_to_code, where_to_point);
274 276 : code_object->set_trap_handler_index(Smi::FromInt(old_trap_handler_index));
275 276 : break;
276 : }
277 : case Code::WASM_INTERPRETER_ENTRY:
278 : case Code::WASM_TO_JS_FUNCTION:
279 : // Serialize the illegal builtin instead. On instantiation of a
280 : // deserialized module, these will be replaced again.
281 80 : SerializeBuiltinReference(*BUILTIN_CODE(isolate(), Illegal), how_to_code,
282 40 : where_to_point, 0);
283 40 : break;
284 : default:
285 0 : UNREACHABLE();
286 : }
287 316 : }
288 :
289 1893 : bool WasmCompiledModuleSerializer::ElideObject(Object* obj) {
290 4840 : return obj->IsWeakCell() || obj->IsForeign() || obj->IsBreakPointInfo();
291 : }
292 :
293 : class Checksum {
294 : public:
295 : explicit Checksum(Vector<const byte> payload) {
296 : #ifdef MEMORY_SANITIZER
297 : // Computing the checksum includes padding bytes for objects like strings.
298 : // Mark every object as initialized in the code serializer.
299 : MSAN_MEMORY_IS_INITIALIZED(payload.start(), payload.length());
300 : #endif // MEMORY_SANITIZER
301 : // Fletcher's checksum. Modified to reduce 64-bit sums to 32-bit.
302 : uintptr_t a = 1;
303 : uintptr_t b = 0;
304 : const uintptr_t* cur = reinterpret_cast<const uintptr_t*>(payload.start());
305 : DCHECK(IsAligned(payload.length(), kIntptrSize));
306 723 : const uintptr_t* end = cur + payload.length() / kIntptrSize;
307 26957338 : while (cur < end) {
308 : // Unsigned overflow expected and intended.
309 26956615 : a += *cur++;
310 26956615 : b += a;
311 : }
312 : #if V8_HOST_ARCH_64_BIT
313 723 : a ^= a >> 32;
314 723 : b ^= b >> 32;
315 : #endif // V8_HOST_ARCH_64_BIT
316 723 : a_ = static_cast<uint32_t>(a);
317 723 : b_ = static_cast<uint32_t>(b);
318 : }
319 :
320 305 : bool Check(uint32_t a, uint32_t b) const { return a == a_ && b == b_; }
321 :
322 : uint32_t a() const { return a_; }
323 : uint32_t b() const { return b_; }
324 :
325 : private:
326 : uint32_t a_;
327 : uint32_t b_;
328 :
329 : DISALLOW_COPY_AND_ASSIGN(Checksum);
330 : };
331 :
332 1672 : SerializedCodeData::SerializedCodeData(const std::vector<byte>* payload,
333 4180 : const CodeSerializer* cs) {
334 : DisallowHeapAllocation no_gc;
335 418 : const std::vector<uint32_t>* stub_keys = cs->stub_keys();
336 418 : std::vector<Reservation> reservations = cs->EncodeReservations();
337 :
338 : // Calculate sizes.
339 : uint32_t reservation_size =
340 836 : static_cast<uint32_t>(reservations.size()) * kUInt32Size;
341 418 : uint32_t num_stub_keys = static_cast<uint32_t>(stub_keys->size());
342 418 : uint32_t stub_keys_size = num_stub_keys * kUInt32Size;
343 418 : uint32_t payload_offset = kHeaderSize + reservation_size + stub_keys_size;
344 418 : uint32_t padded_payload_offset = POINTER_SIZE_ALIGN(payload_offset);
345 : uint32_t size =
346 418 : padded_payload_offset + static_cast<uint32_t>(payload->size());
347 :
348 : // Allocate backing store and create result data.
349 418 : AllocateData(size);
350 :
351 : // Set header values.
352 418 : SetMagicNumber(cs->isolate());
353 418 : SetHeaderValue(kVersionHashOffset, Version::Hash());
354 : SetHeaderValue(kSourceHashOffset, cs->source_hash());
355 : SetHeaderValue(kCpuFeaturesOffset,
356 : static_cast<uint32_t>(CpuFeatures::SupportedFeatures()));
357 418 : SetHeaderValue(kFlagHashOffset, FlagList::Hash());
358 : SetHeaderValue(kNumReservationsOffset,
359 836 : static_cast<uint32_t>(reservations.size()));
360 : SetHeaderValue(kNumCodeStubKeysOffset, num_stub_keys);
361 418 : SetHeaderValue(kPayloadLengthOffset, static_cast<uint32_t>(payload->size()));
362 :
363 : // Zero out any padding in the header.
364 418 : memset(data_ + kUnalignedHeaderSize, 0, kHeaderSize - kUnalignedHeaderSize);
365 :
366 : // Copy reservation chunk sizes.
367 : CopyBytes(data_ + kHeaderSize,
368 : reinterpret_cast<const byte*>(reservations.data()),
369 836 : reservation_size);
370 :
371 : // Copy code stub keys.
372 418 : CopyBytes(data_ + kHeaderSize + reservation_size,
373 1254 : reinterpret_cast<const byte*>(stub_keys->data()), stub_keys_size);
374 :
375 : // Zero out any padding before the payload.
376 418 : memset(data_ + payload_offset, 0, padded_payload_offset - payload_offset);
377 :
378 : // Copy serialized data.
379 : CopyBytes(data_ + padded_payload_offset, payload->data(),
380 418 : static_cast<size_t>(payload->size()));
381 :
382 : Checksum checksum(DataWithoutHeader());
383 : SetHeaderValue(kChecksum1Offset, checksum.a());
384 : SetHeaderValue(kChecksum2Offset, checksum.b());
385 418 : }
386 :
387 361 : SerializedCodeData::SanityCheckResult SerializedCodeData::SanityCheck(
388 : Isolate* isolate, uint32_t expected_source_hash) const {
389 361 : if (this->size_ < kHeaderSize) return INVALID_HEADER;
390 639 : uint32_t magic_number = GetMagicNumber();
391 328 : if (magic_number != ComputeMagicNumber(isolate)) return MAGIC_NUMBER_MISMATCH;
392 : uint32_t version_hash = GetHeaderValue(kVersionHashOffset);
393 : uint32_t source_hash = GetHeaderValue(kSourceHashOffset);
394 : uint32_t cpu_features = GetHeaderValue(kCpuFeaturesOffset);
395 : uint32_t flags_hash = GetHeaderValue(kFlagHashOffset);
396 : uint32_t payload_length = GetHeaderValue(kPayloadLengthOffset);
397 : uint32_t c1 = GetHeaderValue(kChecksum1Offset);
398 : uint32_t c2 = GetHeaderValue(kChecksum2Offset);
399 328 : if (version_hash != Version::Hash()) return VERSION_MISMATCH;
400 316 : if (source_hash != expected_source_hash) return SOURCE_MISMATCH;
401 316 : if (cpu_features != static_cast<uint32_t>(CpuFeatures::SupportedFeatures())) {
402 : return CPU_FEATURES_MISMATCH;
403 : }
404 316 : if (flags_hash != FlagList::Hash()) return FLAGS_MISMATCH;
405 : uint32_t max_payload_length =
406 : this->size_ -
407 622 : POINTER_SIZE_ALIGN(kHeaderSize +
408 : GetHeaderValue(kNumReservationsOffset) * kInt32Size +
409 : GetHeaderValue(kNumCodeStubKeysOffset) * kInt32Size);
410 311 : if (payload_length > max_payload_length) return LENGTH_MISMATCH;
411 305 : if (!Checksum(DataWithoutHeader()).Check(c1, c2)) return CHECKSUM_MISMATCH;
412 300 : return CHECK_SUCCESS;
413 : }
414 :
415 0 : uint32_t SerializedCodeData::SourceHash(Handle<String> source) {
416 540 : return source->length();
417 : }
418 :
419 : // Return ScriptData object and relinquish ownership over it to the caller.
420 418 : ScriptData* SerializedCodeData::GetScriptData() {
421 : DCHECK(owns_data_);
422 418 : ScriptData* result = new ScriptData(data_, size_);
423 : result->AcquireDataOwnership();
424 418 : owns_data_ = false;
425 418 : data_ = nullptr;
426 418 : return result;
427 : }
428 :
429 300 : Vector<const SerializedData::Reservation> SerializedCodeData::Reservations()
430 : const {
431 : return Vector<const Reservation>(
432 : reinterpret_cast<const Reservation*>(data_ + kHeaderSize),
433 600 : GetHeaderValue(kNumReservationsOffset));
434 : }
435 :
436 300 : Vector<const byte> SerializedCodeData::Payload() const {
437 600 : int reservations_size = GetHeaderValue(kNumReservationsOffset) * kInt32Size;
438 300 : int code_stubs_size = GetHeaderValue(kNumCodeStubKeysOffset) * kInt32Size;
439 300 : int payload_offset = kHeaderSize + reservations_size + code_stubs_size;
440 300 : int padded_payload_offset = POINTER_SIZE_ALIGN(payload_offset);
441 300 : const byte* payload = data_ + padded_payload_offset;
442 : DCHECK(IsAligned(reinterpret_cast<intptr_t>(payload), kPointerAlignment));
443 300 : int length = GetHeaderValue(kPayloadLengthOffset);
444 : DCHECK_EQ(data_ + size_, payload + length);
445 300 : return Vector<const byte>(payload, length);
446 : }
447 :
448 300 : Vector<const uint32_t> SerializedCodeData::CodeStubKeys() const {
449 600 : int reservations_size = GetHeaderValue(kNumReservationsOffset) * kInt32Size;
450 300 : const byte* start = data_ + kHeaderSize + reservations_size;
451 : return Vector<const uint32_t>(reinterpret_cast<const uint32_t*>(start),
452 300 : GetHeaderValue(kNumCodeStubKeysOffset));
453 : }
454 :
455 361 : SerializedCodeData::SerializedCodeData(ScriptData* data)
456 361 : : SerializedData(const_cast<byte*>(data->data()), data->length()) {}
457 :
458 361 : SerializedCodeData SerializedCodeData::FromCachedData(
459 : Isolate* isolate, ScriptData* cached_data, uint32_t expected_source_hash,
460 : SanityCheckResult* rejection_result) {
461 : DisallowHeapAllocation no_gc;
462 : SerializedCodeData scd(cached_data);
463 361 : *rejection_result = scd.SanityCheck(isolate, expected_source_hash);
464 361 : if (*rejection_result != CHECK_SUCCESS) {
465 : cached_data->Reject();
466 : return SerializedCodeData(nullptr, 0);
467 : }
468 : return scd;
469 : }
470 :
471 : } // namespace internal
472 : } // namespace v8
|