code-serializer.cc 19 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/snapshot/code-serializer.h"

#include "src/code-stubs.h"
8
#include "src/counters.h"
Marja Hölttä's avatar
Marja Hölttä committed
9
#include "src/debug/debug.h"
10 11
#include "src/log.h"
#include "src/macro-assembler.h"
12
#include "src/objects-inl.h"
13
#include "src/snapshot/object-deserializer.h"
14
#include "src/snapshot/snapshot.h"
15
#include "src/version.h"
16
#include "src/visitors.h"
17 18 19 20

namespace v8 {
namespace internal {

21 22 23 24 25 26 27 28 29 30 31
ScriptData::ScriptData(const byte* data, int length)
    : owns_data_(false), rejected_(false), data_(data), length_(length) {
  if (!IsAligned(reinterpret_cast<intptr_t>(data), kPointerAlignment)) {
    byte* copy = NewArray<byte>(length);
    DCHECK(IsAligned(reinterpret_cast<intptr_t>(copy), kPointerAlignment));
    CopyBytes(copy, data, length);
    data_ = copy;
    AcquireDataOwnership();
  }
}

32 33 34 35 36
CodeSerializer::CodeSerializer(Isolate* isolate, uint32_t source_hash)
    : Serializer(isolate), source_hash_(source_hash) {
  allocator()->UseCustomChunkSize(FLAG_serialization_chunk_size);
}

37 38
// static
ScriptCompiler::CachedData* CodeSerializer::Serialize(
39
    Handle<SharedFunctionInfo> info) {
40 41 42 43 44 45 46
  Isolate* isolate = info->GetIsolate();
  TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8", "V8.Execute");
  HistogramTimerScope histogram_timer(isolate->counters()->compile_serialize());
  RuntimeCallTimerScope runtimeTimer(isolate,
                                     RuntimeCallCounterId::kCompileSerialize);
  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.CompileSerialize");

47 48
  base::ElapsedTimer timer;
  if (FLAG_profile_deserialization) timer.Start();
49
  Handle<Script> script(Script::cast(info->script()), isolate);
50 51
  if (FLAG_trace_serializer) {
    PrintF("[Serializing from");
52
    script->name()->ShortPrint();
53 54
    PrintF("]\n");
  }
55 56 57
  // TODO(7110): Enable serialization of Asm modules once the AsmWasmData is
  // context independent.
  if (script->ContainsAsmModule()) return nullptr;
58

59 60
  isolate->heap()->read_only_space()->ClearStringPaddingIfNeeded();

61
  // Serialize code object.
62
  Handle<String> source(String::cast(script->source()), isolate);
63 64
  CodeSerializer cs(isolate, SerializedCodeData::SourceHash(
                                 source, script->origin_options()));
65
  DisallowHeapAllocation no_gc;
66
  cs.reference_map()->AddAttachedReference(*source);
67
  ScriptData* script_data = cs.SerializeSharedFunctionInfo(info);
68 69 70

  if (FLAG_profile_deserialization) {
    double ms = timer.Elapsed().InMillisecondsF();
71
    int length = script_data->length();
72 73 74
    PrintF("[Serializing to %d bytes took %0.3f ms]\n", length, ms);
  }

75 76 77 78 79 80 81
  ScriptCompiler::CachedData* result =
      new ScriptCompiler::CachedData(script_data->data(), script_data->length(),
                                     ScriptCompiler::CachedData::BufferOwned);
  script_data->ReleaseDataOwnership();
  delete script_data;

  return result;
82 83
}

84 85
ScriptData* CodeSerializer::SerializeSharedFunctionInfo(
    Handle<SharedFunctionInfo> info) {
86 87
  DisallowHeapAllocation no_gc;

88
  VisitRootPointer(Root::kHandleScope, nullptr,
89
                   Handle<Object>::cast(info).location());
90 91 92
  SerializeDeferredObjects();
  Pad();

93
  SerializedCodeData data(sink_.data(), this);
94 95

  return data.GetScriptData();
96 97
}

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
bool CodeSerializer::SerializeReadOnlyObject(HeapObject* obj,
                                             HowToCode how_to_code,
                                             WhereToPoint where_to_point,
                                             int skip) {
  PagedSpace* read_only_space = isolate()->heap()->read_only_space();
  if (!read_only_space->Contains(obj)) return false;

  // For objects in RO_SPACE, never serialize the object, but instead create a
  // back reference that encodes the page number as the chunk_index and the
  // offset within the page as the chunk_offset.
  Address address = obj->address();
  Page* page = Page::FromAddress(address);
  uint32_t chunk_index = 0;
  for (Page* p : *read_only_space) {
    if (p == page) break;
    ++chunk_index;
  }
  uint32_t chunk_offset = static_cast<uint32_t>(page->Offset(address));
  SerializerReference back_reference =
      SerializerReference::BackReference(RO_SPACE, chunk_index, chunk_offset);
  reference_map()->Add(obj, back_reference);
  CHECK(SerializeBackReference(obj, how_to_code, where_to_point, skip));
  return true;
}

123 124
void CodeSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
                                     WhereToPoint where_to_point, int skip) {
125 126
  if (SerializeHotObject(obj, how_to_code, where_to_point, skip)) return;

127
  if (SerializeRoot(obj, how_to_code, where_to_point, skip)) return;
128

129
  if (SerializeBackReference(obj, how_to_code, where_to_point, skip)) return;
130

131 132
  if (SerializeReadOnlyObject(obj, how_to_code, where_to_point, skip)) return;

133 134 135 136 137 138 139 140
  FlushSkip(skip);

  if (obj->IsCode()) {
    Code* code_object = Code::cast(obj);
    switch (code_object->kind()) {
      case Code::OPTIMIZED_FUNCTION:  // No optimized code compiled yet.
      case Code::REGEXP:              // No regexp literals initialized yet.
      case Code::NUMBER_OF_KINDS:     // Pseudo enum value.
141
      case Code::BYTECODE_HANDLER:    // No direct references to handlers.
142
        break;                        // hit UNREACHABLE below.
143
      case Code::BUILTIN:
144
        SerializeBuiltinReference(code_object, how_to_code, where_to_point, 0);
145 146
        return;
      case Code::STUB:
147 148 149
        if (code_object->builtin_index() == -1) {
          SerializeCodeStub(code_object, how_to_code, where_to_point);
        } else {
150 151
          SerializeBuiltinReference(code_object, how_to_code, where_to_point,
                                    0);
152
        }
153
        return;
154 155
      default:
        return SerializeCodeObject(code_object, how_to_code, where_to_point);
156 157 158 159
    }
    UNREACHABLE();
  }

160
  ReadOnlyRoots roots(isolate());
161
  if (ElideObject(obj)) {
162 163
    return SerializeObject(roots.undefined_value(), how_to_code, where_to_point,
                           skip);
164
  }
165 166

  if (obj->IsScript()) {
167 168 169 170 171 172
    Script* script_obj = Script::cast(obj);
    DCHECK_NE(script_obj->compilation_type(), Script::COMPILATION_TYPE_EVAL);
    // We want to differentiate between undefined and uninitialized_symbol for
    // context_data for now. It is hack to allow debugging for scripts that are
    // included as a part of custom snapshot. (see debug::Script::IsEmbedded())
    Object* context_data = script_obj->context_data();
173 174 175
    if (context_data != roots.undefined_value() &&
        context_data != roots.uninitialized_symbol()) {
      script_obj->set_context_data(roots.undefined_value());
176 177 178 179
    }
    // We don't want to serialize host options to avoid serializing unnecessary
    // object graph.
    FixedArray* host_options = script_obj->host_defined_options();
180
    script_obj->set_host_defined_options(roots.empty_fixed_array());
181 182 183 184
    SerializeGeneric(obj, how_to_code, where_to_point);
    script_obj->set_host_defined_options(host_options);
    script_obj->set_context_data(context_data);
    return;
185 186
  }

187 188
  if (obj->IsSharedFunctionInfo()) {
    SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj);
189 190 191
    // TODO(7110): Enable serializing of Asm modules once the AsmWasmData
    // is context independent.
    DCHECK(!sfi->IsApiFunction() && !sfi->HasAsmWasmData());
192 193 194 195 196 197 198 199 200 201

    DebugInfo* debug_info = nullptr;
    BytecodeArray* debug_bytecode_array = nullptr;
    if (sfi->HasDebugInfo()) {
      // Clear debug info.
      debug_info = sfi->GetDebugInfo();
      if (debug_info->HasInstrumentedBytecodeArray()) {
        debug_bytecode_array = debug_info->DebugBytecodeArray();
        sfi->SetDebugBytecodeArray(debug_info->OriginalBytecodeArray());
      }
202
      sfi->set_script_or_debug_info(debug_info->script());
203 204
    }
    DCHECK(!sfi->HasDebugInfo());
205

206 207 208 209 210
    // Mark SFI to indicate whether the code is cached.
    bool was_deserialized = sfi->deserialized();
    sfi->set_deserialized(sfi->is_compiled());
    SerializeGeneric(obj, how_to_code, where_to_point);
    sfi->set_deserialized(was_deserialized);
211 212 213

    // Restore debug info
    if (debug_info != nullptr) {
214
      sfi->set_script_or_debug_info(debug_info);
215 216 217 218
      if (debug_bytecode_array != nullptr) {
        sfi->SetDebugBytecodeArray(debug_bytecode_array);
      }
    }
219 220 221
    return;
  }

222 223 224 225 226
  if (obj->IsBytecodeArray()) {
    // Clear the stack frame cache if present
    BytecodeArray::cast(obj)->ClearFrameCacheFromSourcePositionTable();
  }

227 228 229 230
  // Past this point we should not see any (context-specific) maps anymore.
  CHECK(!obj->IsMap());
  // There should be no references to the global object embedded.
  CHECK(!obj->IsJSGlobalProxy() && !obj->IsJSGlobalObject());
231 232
  // Embedded FixedArrays that need rehashing must support rehashing.
  CHECK_IMPLIES(obj->NeedsRehashing(), obj->CanBeRehashed());
233 234 235 236 237 238 239 240 241 242
  // We expect no instantiated function objects or contexts.
  CHECK(!obj->IsJSFunction() && !obj->IsContext());

  SerializeGeneric(obj, how_to_code, where_to_point);
}

void CodeSerializer::SerializeGeneric(HeapObject* heap_object,
                                      HowToCode how_to_code,
                                      WhereToPoint where_to_point) {
  // Object has not yet been serialized.  Serialize it here.
243
  ObjectSerializer serializer(this, heap_object, &sink_, how_to_code,
244 245 246 247
                              where_to_point);
  serializer.Serialize();
}

248
void CodeSerializer::SerializeCodeStub(Code* code_stub, HowToCode how_to_code,
249
                                       WhereToPoint where_to_point) {
250
  // We only arrive here if we have not encountered this code stub before.
251
  DCHECK(!reference_map()->LookupReference(code_stub).is_valid());
252
  uint32_t stub_key = code_stub->stub_key();
253 254
  DCHECK(CodeStub::MajorKeyFromKey(stub_key) != CodeStub::NoCache);
  DCHECK(!CodeStub::GetCode(isolate(), stub_key).is_null());
255
  stub_keys_.push_back(stub_key);
256

257 258
  SerializerReference reference =
      reference_map()->AddAttachedReference(code_stub);
259
  if (FLAG_trace_serializer) {
260 261 262
    PrintF(" Encoding code stub %s as attached reference %d\n",
           CodeStub::MajorName(CodeStub::MajorKeyFromKey(stub_key)),
           reference.attached_reference_index());
263
  }
264
  PutAttachedReference(reference, how_to_code, where_to_point);
265 266 267
}

MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
268 269
    Isolate* isolate, ScriptData* cached_data, Handle<String> source,
    ScriptOriginOptions origin_options) {
270
  base::ElapsedTimer timer;
271
  if (FLAG_profile_deserialization || FLAG_log_function_events) timer.Start();
272 273 274

  HandleScope scope(isolate);

275 276 277
  SerializedCodeData::SanityCheckResult sanity_check_result =
      SerializedCodeData::CHECK_SUCCESS;
  const SerializedCodeData scd = SerializedCodeData::FromCachedData(
278 279
      isolate, cached_data,
      SerializedCodeData::SourceHash(source, origin_options),
280 281
      &sanity_check_result);
  if (sanity_check_result != SerializedCodeData::CHECK_SUCCESS) {
282 283
    if (FLAG_profile_deserialization) PrintF("[Cached code failed check]\n");
    DCHECK(cached_data->rejected());
284
    isolate->counters()->code_cache_reject_reason()->AddSample(
285
        sanity_check_result);
286 287 288 289
    return MaybeHandle<SharedFunctionInfo>();
  }

  // Deserialize.
290 291 292 293 294
  MaybeHandle<SharedFunctionInfo> maybe_result =
      ObjectDeserializer::DeserializeSharedFunctionInfo(isolate, &scd, source);

  Handle<SharedFunctionInfo> result;
  if (!maybe_result.ToHandle(&result)) {
295 296 297 298 299 300 301 302 303 304 305
    // Deserializing may fail if the reservations cannot be fulfilled.
    if (FLAG_profile_deserialization) PrintF("[Deserializing failed]\n");
    return MaybeHandle<SharedFunctionInfo>();
  }

  if (FLAG_profile_deserialization) {
    double ms = timer.Elapsed().InMillisecondsF();
    int length = cached_data->length();
    PrintF("[Deserializing from %d bytes took %0.3f ms]\n", length, ms);
  }

306 307 308
  bool log_code_creation = isolate->logger()->is_listening_to_code_events() ||
                           isolate->is_profiling();
  if (log_code_creation || FLAG_log_function_events) {
309
    String* name = ReadOnlyRoots(isolate).empty_string();
310 311 312
    if (result->script()->IsScript()) {
      Script* script = Script::cast(result->script());
      if (script->name()->IsString()) name = String::cast(script->name());
313 314 315 316 317 318 319 320 321 322
      if (FLAG_log_function_events) {
        LOG(isolate, FunctionEvent("deserialize", script->id(),
                                   timer.Elapsed().InMillisecondsF(),
                                   result->StartPosition(),
                                   result->EndPosition(), name));
      }
    }
    if (log_code_creation) {
      PROFILE(isolate, CodeCreateEvent(CodeEventListener::SCRIPT_TAG,
                                       result->abstract_code(), *result, name));
323 324
    }
  }
325 326 327 328 329

  if (isolate->NeedsSourcePositionsForProfiling()) {
    Handle<Script> script(Script::cast(result->script()), isolate);
    Script::InitLineEnds(script);
  }
330 331 332 333
  return scope.CloseAndEscape(result);
}


334
SerializedCodeData::SerializedCodeData(const std::vector<byte>* payload,
335
                                       const CodeSerializer* cs) {
336
  DisallowHeapAllocation no_gc;
337
  const std::vector<uint32_t>* stub_keys = cs->stub_keys();
338
  std::vector<Reservation> reservations = cs->EncodeReservations();
339 340

  // Calculate sizes.
341 342 343 344 345 346 347 348
  uint32_t reservation_size =
      static_cast<uint32_t>(reservations.size()) * kUInt32Size;
  uint32_t num_stub_keys = static_cast<uint32_t>(stub_keys->size());
  uint32_t stub_keys_size = num_stub_keys * kUInt32Size;
  uint32_t payload_offset = kHeaderSize + reservation_size + stub_keys_size;
  uint32_t padded_payload_offset = POINTER_SIZE_ALIGN(payload_offset);
  uint32_t size =
      padded_payload_offset + static_cast<uint32_t>(payload->size());
349
  DCHECK(IsAligned(size, kPointerAlignment));
350 351 352 353

  // Allocate backing store and create result data.
  AllocateData(size);

354 355 356
  // Zero out pre-payload data. Part of that is only used for padding.
  memset(data_, 0, padded_payload_offset);

357
  // Set header values.
358
  SetMagicNumber(cs->isolate());
359
  SetHeaderValue(kVersionHashOffset, Version::Hash());
360
  SetHeaderValue(kSourceHashOffset, cs->source_hash());
361 362 363
  SetHeaderValue(kCpuFeaturesOffset,
                 static_cast<uint32_t>(CpuFeatures::SupportedFeatures()));
  SetHeaderValue(kFlagHashOffset, FlagList::Hash());
364 365
  SetHeaderValue(kNumReservationsOffset,
                 static_cast<uint32_t>(reservations.size()));
366
  SetHeaderValue(kNumCodeStubKeysOffset, num_stub_keys);
367
  SetHeaderValue(kPayloadLengthOffset, static_cast<uint32_t>(payload->size()));
368

369 370 371
  // Zero out any padding in the header.
  memset(data_ + kUnalignedHeaderSize, 0, kHeaderSize - kUnalignedHeaderSize);

372
  // Copy reservation chunk sizes.
373 374
  CopyBytes(data_ + kHeaderSize,
            reinterpret_cast<const byte*>(reservations.data()),
375 376 377 378
            reservation_size);

  // Copy code stub keys.
  CopyBytes(data_ + kHeaderSize + reservation_size,
379
            reinterpret_cast<const byte*>(stub_keys->data()), stub_keys_size);
380 381

  // Copy serialized data.
382 383
  CopyBytes(data_ + padded_payload_offset, payload->data(),
            static_cast<size_t>(payload->size()));
384

385 386 387
  Checksum checksum(ChecksummedContent());
  SetHeaderValue(kChecksumPartAOffset, checksum.a());
  SetHeaderValue(kChecksumPartBOffset, checksum.b());
388 389 390
}

SerializedCodeData::SanityCheckResult SerializedCodeData::SanityCheck(
391
    Isolate* isolate, uint32_t expected_source_hash) const {
392
  if (this->size_ < kHeaderSize) return INVALID_HEADER;
393 394 395 396 397 398
  uint32_t magic_number = GetMagicNumber();
  if (magic_number != ComputeMagicNumber(isolate)) return MAGIC_NUMBER_MISMATCH;
  uint32_t version_hash = GetHeaderValue(kVersionHashOffset);
  uint32_t source_hash = GetHeaderValue(kSourceHashOffset);
  uint32_t cpu_features = GetHeaderValue(kCpuFeaturesOffset);
  uint32_t flags_hash = GetHeaderValue(kFlagHashOffset);
399
  uint32_t payload_length = GetHeaderValue(kPayloadLengthOffset);
400 401
  uint32_t c1 = GetHeaderValue(kChecksumPartAOffset);
  uint32_t c2 = GetHeaderValue(kChecksumPartBOffset);
402
  if (version_hash != Version::Hash()) return VERSION_MISMATCH;
403
  if (source_hash != expected_source_hash) return SOURCE_MISMATCH;
404 405 406 407
  if (cpu_features != static_cast<uint32_t>(CpuFeatures::SupportedFeatures())) {
    return CPU_FEATURES_MISMATCH;
  }
  if (flags_hash != FlagList::Hash()) return FLAGS_MISMATCH;
408 409 410 411 412 413
  uint32_t max_payload_length =
      this->size_ -
      POINTER_SIZE_ALIGN(kHeaderSize +
                         GetHeaderValue(kNumReservationsOffset) * kInt32Size +
                         GetHeaderValue(kNumCodeStubKeysOffset) * kInt32Size);
  if (payload_length > max_payload_length) return LENGTH_MISMATCH;
414
  if (!Checksum(ChecksummedContent()).Check(c1, c2)) return CHECKSUM_MISMATCH;
415 416 417
  return CHECK_SUCCESS;
}

418 419 420 421 422 423 424 425 426
uint32_t SerializedCodeData::SourceHash(Handle<String> source,
                                        ScriptOriginOptions origin_options) {
  const uint32_t source_length = source->length();

  static constexpr uint32_t kModuleFlagMask = (1 << 31);
  const uint32_t is_module = origin_options.IsModule() ? kModuleFlagMask : 0;
  DCHECK_EQ(0, source_length & kModuleFlagMask);

  return source_length | is_module;
427 428 429 430 431 432 433 434
}

// Return ScriptData object and relinquish ownership over it to the caller.
ScriptData* SerializedCodeData::GetScriptData() {
  DCHECK(owns_data_);
  ScriptData* result = new ScriptData(data_, size_);
  result->AcquireDataOwnership();
  owns_data_ = false;
435
  data_ = nullptr;
436 437 438
  return result;
}

439
std::vector<SerializedData::Reservation> SerializedCodeData::Reservations()
440
    const {
441 442 443 444 445
  uint32_t size = GetHeaderValue(kNumReservationsOffset);
  std::vector<Reservation> reservations(size);
  memcpy(reservations.data(), data_ + kHeaderSize,
         size * sizeof(SerializedData::Reservation));
  return reservations;
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
}

Vector<const byte> SerializedCodeData::Payload() const {
  int reservations_size = GetHeaderValue(kNumReservationsOffset) * kInt32Size;
  int code_stubs_size = GetHeaderValue(kNumCodeStubKeysOffset) * kInt32Size;
  int payload_offset = kHeaderSize + reservations_size + code_stubs_size;
  int padded_payload_offset = POINTER_SIZE_ALIGN(payload_offset);
  const byte* payload = data_ + padded_payload_offset;
  DCHECK(IsAligned(reinterpret_cast<intptr_t>(payload), kPointerAlignment));
  int length = GetHeaderValue(kPayloadLengthOffset);
  DCHECK_EQ(data_ + size_, payload + length);
  return Vector<const byte>(payload, length);
}

Vector<const uint32_t> SerializedCodeData::CodeStubKeys() const {
  int reservations_size = GetHeaderValue(kNumReservationsOffset) * kInt32Size;
  const byte* start = data_ + kHeaderSize + reservations_size;
  return Vector<const uint32_t>(reinterpret_cast<const uint32_t*>(start),
                                GetHeaderValue(kNumCodeStubKeysOffset));
}

SerializedCodeData::SerializedCodeData(ScriptData* data)
    : SerializedData(const_cast<byte*>(data->data()), data->length()) {}

470
SerializedCodeData SerializedCodeData::FromCachedData(
471 472
    Isolate* isolate, ScriptData* cached_data, uint32_t expected_source_hash,
    SanityCheckResult* rejection_result) {
473
  DisallowHeapAllocation no_gc;
474 475 476 477 478 479 480
  SerializedCodeData scd(cached_data);
  *rejection_result = scd.SanityCheck(isolate, expected_source_hash);
  if (*rejection_result != CHECK_SUCCESS) {
    cached_data->Reject();
    return SerializedCodeData(nullptr, 0);
  }
  return scd;
481 482 483 484
}

}  // namespace internal
}  // namespace v8