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