wasm-serialization.cc 23.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Copyright 2017 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/wasm/wasm-serialization.h"

#include "src/assembler-inl.h"
#include "src/external-reference-table.h"
#include "src/objects-inl.h"
#include "src/objects.h"
11
#include "src/ostreams.h"
12
#include "src/snapshot/code-serializer.h"
13
#include "src/snapshot/serializer-common.h"
14
#include "src/utils.h"
15
#include "src/version.h"
16
#include "src/wasm/function-compiler.h"
17 18
#include "src/wasm/module-compiler.h"
#include "src/wasm/module-decoder.h"
19
#include "src/wasm/wasm-code-manager.h"
20 21 22 23 24 25 26 27
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/wasm-result.h"

namespace v8 {
namespace internal {
namespace wasm {
28

29 30
namespace {

31 32
// TODO(bbudge) Try to unify the various implementations of readers and writers
// in WASM, e.g. StreamProcessor and ZoneBuffer, with these.
33 34
class Writer {
 public:
35
  explicit Writer(Vector<byte> buffer)
36 37 38 39 40 41 42 43 44
      : start_(buffer.start()), end_(buffer.end()), pos_(buffer.start()) {}

  size_t bytes_written() const { return pos_ - start_; }
  byte* current_location() const { return pos_; }
  size_t current_size() const { return end_ - pos_; }
  Vector<byte> current_buffer() const {
    return {current_location(), current_size()};
  }

45 46
  template <typename T>
  void Write(const T& value) {
47 48 49
    DCHECK_GE(current_size(), sizeof(T));
    WriteUnalignedValue(reinterpret_cast<Address>(current_location()), value);
    pos_ += sizeof(T);
50
    if (FLAG_trace_wasm_serialization) {
51 52
      StdoutStream{} << "wrote: " << static_cast<size_t>(value)
                     << " sized: " << sizeof(T) << std::endl;
53 54 55
    }
  }

56 57 58 59 60
  void WriteVector(const Vector<const byte> v) {
    DCHECK_GE(current_size(), v.size());
    if (v.size() > 0) {
      memcpy(current_location(), v.start(), v.size());
      pos_ += v.size();
61
    }
62
    if (FLAG_trace_wasm_serialization) {
63 64
      StdoutStream{} << "wrote vector of " << v.size() << " elements"
                     << std::endl;
65 66 67
    }
  }

68
  void Skip(size_t size) { pos_ += size; }
69

70
 private:
71
  byte* const start_;
72 73
  byte* const end_;
  byte* pos_;
74 75 76 77
};

class Reader {
 public:
78
  explicit Reader(Vector<const byte> buffer)
79 80 81 82 83 84 85 86 87
      : start_(buffer.start()), end_(buffer.end()), pos_(buffer.start()) {}

  size_t bytes_read() const { return pos_ - start_; }
  const byte* current_location() const { return pos_; }
  size_t current_size() const { return end_ - pos_; }
  Vector<const byte> current_buffer() const {
    return {current_location(), current_size()};
  }

88 89
  template <typename T>
  T Read() {
90 91 92 93
    DCHECK_GE(current_size(), sizeof(T));
    T value =
        ReadUnalignedValue<T>(reinterpret_cast<Address>(current_location()));
    pos_ += sizeof(T);
94
    if (FLAG_trace_wasm_serialization) {
95 96
      StdoutStream{} << "read: " << static_cast<size_t>(value)
                     << " sized: " << sizeof(T) << std::endl;
97
    }
98
    return value;
99 100
  }

101 102 103 104 105
  void ReadVector(Vector<byte> v) {
    if (v.size() > 0) {
      DCHECK_GE(current_size(), v.size());
      memcpy(v.start(), current_location(), v.size());
      pos_ += v.size();
106
    }
107
    if (FLAG_trace_wasm_serialization) {
108 109
      StdoutStream{} << "read vector of " << v.size() << " elements"
                     << std::endl;
110 111 112
    }
  }

113
  void Skip(size_t size) { pos_ += size; }
114

115
 private:
116
  const byte* const start_;
117 118
  const byte* const end_;
  const byte* pos_;
119 120
};

121
constexpr size_t kVersionSize = 4 * sizeof(uint32_t);
122

123 124
void WriteVersion(Writer* writer) {
  writer->Write(SerializedData::kMagicNumber);
125 126 127
  writer->Write(Version::Hash());
  writer->Write(static_cast<uint32_t>(CpuFeatures::SupportedFeatures()));
  writer->Write(FlagList::Hash());
128 129
}

130 131
// On Intel, call sites are encoded as a displacement. For linking and for
// serialization/deserialization, we want to store/retrieve a tag (the function
132 133 134
// index). On Intel, that means accessing the raw displacement.
// On ARM64, call sites are encoded as either a literal load or a direct branch.
// Other platforms simply require accessing the target address.
135 136
void SetWasmCalleeTag(RelocInfo* rinfo, uint32_t tag) {
#if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32
137
  DCHECK(rinfo->HasTargetAddressAddress());
138
  WriteUnalignedValue(rinfo->target_address_address(), tag);
139 140 141
#elif V8_TARGET_ARCH_ARM64
  Instruction* instr = reinterpret_cast<Instruction*>(rinfo->pc());
  if (instr->IsLdrLiteralX()) {
142 143
    WriteUnalignedValue(rinfo->constant_pool_entry_address(),
                        static_cast<Address>(tag));
144 145 146
  } else {
    DCHECK(instr->IsBranchAndLink() || instr->IsUnconditionalBranch());
    instr->SetBranchImmTarget(
147
        reinterpret_cast<Instruction*>(rinfo->pc() + tag * kInstrSize));
148
  }
149
#else
150
  Address addr = static_cast<Address>(tag);
151 152
  if (rinfo->rmode() == RelocInfo::EXTERNAL_REFERENCE) {
    rinfo->set_target_external_reference(addr, SKIP_ICACHE_FLUSH);
153 154
  } else if (rinfo->rmode() == RelocInfo::WASM_STUB_CALL) {
    rinfo->set_wasm_stub_call_address(addr, SKIP_ICACHE_FLUSH);
155 156 157
  } else {
    rinfo->set_target_address(addr, SKIP_WRITE_BARRIER, SKIP_ICACHE_FLUSH);
  }
158 159 160 161 162
#endif
}

uint32_t GetWasmCalleeTag(RelocInfo* rinfo) {
#if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32
163
  return ReadUnalignedValue<uint32_t>(rinfo->target_address_address());
164 165 166
#elif V8_TARGET_ARCH_ARM64
  Instruction* instr = reinterpret_cast<Instruction*>(rinfo->pc());
  if (instr->IsLdrLiteralX()) {
167
    return ReadUnalignedValue<uint32_t>(rinfo->constant_pool_entry_address());
168 169
  } else {
    DCHECK(instr->IsBranchAndLink() || instr->IsUnconditionalBranch());
170
    return static_cast<uint32_t>(instr->ImmPCOffset() / kInstrSize);
171
  }
172
#else
173 174 175 176 177 178 179 180
  Address addr;
  if (rinfo->rmode() == RelocInfo::EXTERNAL_REFERENCE) {
    addr = rinfo->target_external_reference();
  } else if (rinfo->rmode() == RelocInfo::WASM_STUB_CALL) {
    addr = rinfo->wasm_stub_call_address();
  } else {
    addr = rinfo->target_address();
  }
181
  return static_cast<uint32_t>(addr);
182 183 184
#endif
}

185 186
constexpr size_t kHeaderSize =
    sizeof(uint32_t) +  // total wasm function count
187
    sizeof(uint32_t);   // imported functions (index of first wasm function)
188 189 190 191 192 193

constexpr size_t kCodeHeaderSize =
    sizeof(size_t) +         // size of code section
    sizeof(size_t) +         // offset of constant pool
    sizeof(size_t) +         // offset of safepoint table
    sizeof(size_t) +         // offset of handler table
194 195
    sizeof(size_t) +         // offset of code comments
    sizeof(size_t) +         // unpadded binary size
196
    sizeof(uint32_t) +       // stack slots
197
    sizeof(uint32_t) +       // tagged parameter slots
198 199 200 201 202
    sizeof(size_t) +         // code size
    sizeof(size_t) +         // reloc size
    sizeof(size_t) +         // source positions size
    sizeof(size_t) +         // protected instructions size
    sizeof(WasmCode::Tier);  // tier
203

204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
// A List of all isolate-independent external references. This is used to create
// a tag from the Address of an external reference and vice versa.
class ExternalReferenceList {
 public:
  uint32_t tag_from_address(Address ext_ref_address) const {
    auto tag_addr_less_than = [this](uint32_t tag, Address searched_addr) {
      return external_reference_by_tag_[tag] < searched_addr;
    };
    auto it = std::lower_bound(std::begin(tags_ordered_by_address_),
                               std::end(tags_ordered_by_address_),
                               ext_ref_address, tag_addr_less_than);
    DCHECK_NE(std::end(tags_ordered_by_address_), it);
    uint32_t tag = *it;
    DCHECK_EQ(address_from_tag(tag), ext_ref_address);
    return tag;
  }

  Address address_from_tag(uint32_t tag) const {
    DCHECK_GT(kNumExternalReferences, tag);
    return external_reference_by_tag_[tag];
  }

  static const ExternalReferenceList& Get() {
    static ExternalReferenceList list;  // Lazily initialized.
    return list;
  }

 private:
  // Private constructor. There will only be a single instance of this object.
  ExternalReferenceList() {
    for (uint32_t i = 0; i < kNumExternalReferences; ++i) {
      tags_ordered_by_address_[i] = i;
    }
    auto addr_by_tag_less_than = [this](uint32_t a, uint32_t b) {
      return external_reference_by_tag_[a] < external_reference_by_tag_[b];
    };
    std::sort(std::begin(tags_ordered_by_address_),
              std::end(tags_ordered_by_address_), addr_by_tag_less_than);
  }

#define COUNT_EXTERNAL_REFERENCE(name, desc) +1
  static constexpr uint32_t kNumExternalReferences =
      EXTERNAL_REFERENCE_LIST(COUNT_EXTERNAL_REFERENCE);
#undef COUNT_EXTERNAL_REFERENCE

#define EXT_REF_ADDR(name, desc) ExternalReference::name().address(),
  Address external_reference_by_tag_[kNumExternalReferences] = {
      EXTERNAL_REFERENCE_LIST(EXT_REF_ADDR)};
#undef EXT_REF_ADDR
  uint32_t tags_ordered_by_address_[kNumExternalReferences];
  DISALLOW_COPY_AND_ASSIGN(ExternalReferenceList);
};

static_assert(std::is_trivially_destructible<ExternalReferenceList>::value,
              "static destructors not allowed");

260
}  // namespace
261 262 263

class V8_EXPORT_PRIVATE NativeModuleSerializer {
 public:
264
  NativeModuleSerializer() = delete;
265
  NativeModuleSerializer(const NativeModule*, Vector<WasmCode* const>);
266

267
  size_t Measure() const;
268
  bool Write(Writer* writer);
269 270

 private:
271 272 273
  size_t MeasureCode(const WasmCode*) const;
  void WriteHeader(Writer* writer);
  void WriteCode(const WasmCode*, Writer* writer);
274

275
  const NativeModule* const native_module_;
276
  Vector<WasmCode* const> code_table_;
277 278
  bool write_called_;

279
  // Reverse lookup tables for embedded addresses.
280
  std::map<Address, uint32_t> wasm_stub_targets_lookup_;
281

282
  DISALLOW_COPY_AND_ASSIGN(NativeModuleSerializer);
283 284
};

285
NativeModuleSerializer::NativeModuleSerializer(
286 287
    const NativeModule* module, Vector<WasmCode* const> code_table)
    : native_module_(module), code_table_(code_table), write_called_(false) {
288 289 290
  DCHECK_NOT_NULL(native_module_);
  // TODO(mtrofin): persist the export wrappers. Ideally, we'd only persist
  // the unique ones, i.e. the cache.
291 292 293 294 295 296
  for (uint32_t i = 0; i < WasmCode::kRuntimeStubCount; ++i) {
    Address addr =
        native_module_->runtime_stub(static_cast<WasmCode::RuntimeStubId>(i))
            ->instruction_start();
    wasm_stub_targets_lookup_.insert(std::make_pair(addr, i));
  }
297 298 299
}

size_t NativeModuleSerializer::MeasureCode(const WasmCode* code) const {
300
  if (code == nullptr) return sizeof(size_t);
301 302 303
  DCHECK_EQ(WasmCode::kFunction, code->kind());
  return kCodeHeaderSize + code->instructions().size() +
         code->reloc_info().size() + code->source_positions().size() +
304
         code->protected_instructions().size() *
305 306 307 308
             sizeof(trap_handler::ProtectedInstructionData);
}

size_t NativeModuleSerializer::Measure() const {
309
  size_t size = kHeaderSize;
310
  for (WasmCode* code : code_table_) {
311
    size += MeasureCode(code);
312
  }
313
  return size;
314 315
}

316
void NativeModuleSerializer::WriteHeader(Writer* writer) {
317 318 319
  // TODO(eholk): We need to properly preserve the flag whether the trap
  // handler was used or not when serializing.

320
  writer->Write(native_module_->num_functions());
321
  writer->Write(native_module_->num_imported_functions());
322 323
}

324
void NativeModuleSerializer::WriteCode(const WasmCode* code, Writer* writer) {
325
  if (code == nullptr) {
326 327 328 329
    writer->Write(size_t{0});
    return;
  }
  DCHECK_EQ(WasmCode::kFunction, code->kind());
330 331 332 333 334
  // Write the size of the entire code section, followed by the code header.
  writer->Write(MeasureCode(code));
  writer->Write(code->constant_pool_offset());
  writer->Write(code->safepoint_table_offset());
  writer->Write(code->handler_table_offset());
335 336
  writer->Write(code->code_comments_offset());
  writer->Write(code->unpadded_binary_size());
337
  writer->Write(code->stack_slots());
338
  writer->Write(code->tagged_parameter_slots());
339 340 341 342 343
  writer->Write(code->instructions().size());
  writer->Write(code->reloc_info().size());
  writer->Write(code->source_positions().size());
  writer->Write(code->protected_instructions().size());
  writer->Write(code->tier());
344 345

  // Get a pointer to the destination buffer, to hold relocated code.
346
  byte* serialized_code_start = writer->current_buffer().start();
347 348 349 350
  byte* code_start = serialized_code_start;
  size_t code_size = code->instructions().size();
  writer->Skip(code_size);
  // Write the reloc info, source positions, and protected code.
351 352
  writer->WriteVector(code->reloc_info());
  writer->WriteVector(code->source_positions());
353
  writer->WriteVector(Vector<byte>::cast(code->protected_instructions()));
354 355 356 357 358 359 360 361 362 363 364
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_ARM
  // On platforms that don't support misaligned word stores, copy to an aligned
  // buffer if necessary so we can relocate the serialized code.
  std::unique_ptr<byte[]> aligned_buffer;
  if (!IsAligned(reinterpret_cast<Address>(serialized_code_start),
                 kInt32Size)) {
    aligned_buffer.reset(new byte[code_size]);
    code_start = aligned_buffer.get();
  }
#endif
  memcpy(code_start, code->instructions().start(), code_size);
365
  // Relocate the code.
366
  int mask = RelocInfo::ModeMask(RelocInfo::WASM_CALL) |
367
             RelocInfo::ModeMask(RelocInfo::WASM_STUB_CALL) |
368 369 370
             RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
             RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
             RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED);
371 372
  RelocIterator orig_iter(code->instructions(), code->reloc_info(),
                          code->constant_pool(), mask);
373 374
  for (RelocIterator iter(
           {code_start, code->instructions().size()}, code->reloc_info(),
375
           reinterpret_cast<Address>(code_start) + code->constant_pool_offset(),
376
           mask);
377 378 379 380 381
       !iter.done(); iter.next(), orig_iter.next()) {
    RelocInfo::Mode mode = orig_iter.rinfo()->rmode();
    switch (mode) {
      case RelocInfo::WASM_CALL: {
        Address orig_target = orig_iter.rinfo()->wasm_call_address();
382 383
        uint32_t tag =
            native_module_->GetFunctionIndexFromJumpTableSlot(orig_target);
384
        SetWasmCalleeTag(iter.rinfo(), tag);
385
      } break;
386 387 388 389 390 391 392
      case RelocInfo::WASM_STUB_CALL: {
        Address orig_target = orig_iter.rinfo()->wasm_stub_call_address();
        auto stub_iter = wasm_stub_targets_lookup_.find(orig_target);
        DCHECK(stub_iter != wasm_stub_targets_lookup_.end());
        uint32_t tag = stub_iter->second;
        SetWasmCalleeTag(iter.rinfo(), tag);
      } break;
393 394
      case RelocInfo::EXTERNAL_REFERENCE: {
        Address orig_target = orig_iter.rinfo()->target_external_reference();
395 396 397
        uint32_t ext_ref_tag =
            ExternalReferenceList::Get().tag_from_address(orig_target);
        SetWasmCalleeTag(iter.rinfo(), ext_ref_tag);
398
      } break;
399 400 401 402 403 404 405
      case RelocInfo::INTERNAL_REFERENCE:
      case RelocInfo::INTERNAL_REFERENCE_ENCODED: {
        Address orig_target = orig_iter.rinfo()->target_internal_reference();
        Address offset = orig_target - code->instruction_start();
        Assembler::deserialization_set_target_internal_reference_at(
            iter.rinfo()->pc(), offset, mode);
      } break;
406 407 408 409
      default:
        UNREACHABLE();
    }
  }
410 411 412 413
  // If we copied to an aligned buffer, copy code into serialized buffer.
  if (code_start != serialized_code_start) {
    memcpy(serialized_code_start, code_start, code_size);
  }
414 415
}

416 417 418 419 420 421
bool NativeModuleSerializer::Write(Writer* writer) {
  DCHECK(!write_called_);
  write_called_ = true;

  WriteHeader(writer);

422
  for (WasmCode* code : code_table_) {
423
    WriteCode(code, writer);
424
  }
425
  return true;
426 427
}

428 429
WasmSerializer::WasmSerializer(NativeModule* native_module)
    : native_module_(native_module),
430 431 432
      code_table_(native_module->SnapshotCodeTable()) {}

size_t WasmSerializer::GetSerializedNativeModuleSize() const {
433
  NativeModuleSerializer serializer(native_module_, VectorOf(code_table_));
434 435
  return kVersionSize + serializer.Measure();
}
436

437
bool WasmSerializer::SerializeNativeModule(Vector<byte> buffer) const {
438
  NativeModuleSerializer serializer(native_module_, VectorOf(code_table_));
439
  size_t measured_size = kVersionSize + serializer.Measure();
440
  if (buffer.size() < measured_size) return false;
441

442
  Writer writer(buffer);
443
  WriteVersion(&writer);
444

445 446 447
  if (!serializer.Write(&writer)) return false;
  DCHECK_EQ(measured_size, writer.bytes_written());
  return true;
448 449
}

450 451 452
class V8_EXPORT_PRIVATE NativeModuleDeserializer {
 public:
  NativeModuleDeserializer() = delete;
453
  explicit NativeModuleDeserializer(NativeModule*);
454

455 456 457 458 459 460 461 462 463 464 465 466
  bool Read(Reader* reader);

 private:
  bool ReadHeader(Reader* reader);
  bool ReadCode(uint32_t fn_index, Reader* reader);

  NativeModule* const native_module_;
  bool read_called_;

  DISALLOW_COPY_AND_ASSIGN(NativeModuleDeserializer);
};

467 468
NativeModuleDeserializer::NativeModuleDeserializer(NativeModule* native_module)
    : native_module_(native_module), read_called_(false) {}
469 470 471 472 473 474

bool NativeModuleDeserializer::Read(Reader* reader) {
  DCHECK(!read_called_);
  read_called_ = true;

  if (!ReadHeader(reader)) return false;
475
  uint32_t total_fns = native_module_->num_functions();
476 477 478
  uint32_t first_wasm_fn = native_module_->num_imported_functions();
  for (uint32_t i = first_wasm_fn; i < total_fns; ++i) {
    if (!ReadCode(i, reader)) return false;
479
  }
480
  return reader->current_size() == 0;
481 482
}

483 484 485
bool NativeModuleDeserializer::ReadHeader(Reader* reader) {
  size_t functions = reader->Read<uint32_t>();
  size_t imports = reader->Read<uint32_t>();
486
  return functions == native_module_->num_functions() &&
487
         imports == native_module_->num_imported_functions();
488 489
}

490 491
bool NativeModuleDeserializer::ReadCode(uint32_t fn_index, Reader* reader) {
  size_t code_section_size = reader->Read<size_t>();
492
  if (code_section_size == 0) return true;
493 494 495
  size_t constant_pool_offset = reader->Read<size_t>();
  size_t safepoint_table_offset = reader->Read<size_t>();
  size_t handler_table_offset = reader->Read<size_t>();
496 497
  size_t code_comment_offset = reader->Read<size_t>();
  size_t unpadded_binary_size = reader->Read<size_t>();
498
  uint32_t stack_slot_count = reader->Read<uint32_t>();
499
  uint32_t tagged_parameter_slots = reader->Read<uint32_t>();
500 501 502 503 504
  size_t code_size = reader->Read<size_t>();
  size_t reloc_size = reader->Read<size_t>();
  size_t source_position_size = reader->Read<size_t>();
  size_t protected_instructions_size = reader->Read<size_t>();
  WasmCode::Tier tier = reader->Read<WasmCode::Tier>();
505

506 507 508
  Vector<const byte> code_buffer = {reader->current_location(), code_size};
  reader->Skip(code_size);

509 510 511 512
  OwnedVector<byte> reloc_info = OwnedVector<byte>::New(reloc_size);
  reader->ReadVector(reloc_info.as_vector());
  OwnedVector<byte> source_pos = OwnedVector<byte>::New(source_position_size);
  reader->ReadVector(source_pos.as_vector());
513 514 515 516
  auto protected_instructions =
      OwnedVector<trap_handler::ProtectedInstructionData>::New(
          protected_instructions_size);
  reader->ReadVector(Vector<byte>::cast(protected_instructions.as_vector()));
517 518

  WasmCode* code = native_module_->AddDeserializedCode(
519 520 521 522 523
      fn_index, code_buffer, stack_slot_count, tagged_parameter_slots,
      safepoint_table_offset, handler_table_offset, constant_pool_offset,
      code_comment_offset, unpadded_binary_size,
      std::move(protected_instructions), std::move(reloc_info),
      std::move(source_pos), tier);
524

525
  // Relocate the code.
526 527
  int mask = RelocInfo::ModeMask(RelocInfo::WASM_CALL) |
             RelocInfo::ModeMask(RelocInfo::WASM_STUB_CALL) |
528 529 530
             RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
             RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
             RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED);
531 532
  for (RelocIterator iter(code->instructions(), code->reloc_info(),
                          code->constant_pool(), mask);
533 534 535
       !iter.done(); iter.next()) {
    RelocInfo::Mode mode = iter.rinfo()->rmode();
    switch (mode) {
536 537 538 539 540 541
      case RelocInfo::WASM_CALL: {
        uint32_t tag = GetWasmCalleeTag(iter.rinfo());
        Address target = native_module_->GetCallTargetForFunction(tag);
        iter.rinfo()->set_wasm_call_address(target, SKIP_ICACHE_FLUSH);
        break;
      }
542 543 544 545 546 547 548 549 550 551
      case RelocInfo::WASM_STUB_CALL: {
        uint32_t tag = GetWasmCalleeTag(iter.rinfo());
        DCHECK_LT(tag, WasmCode::kRuntimeStubCount);
        Address target =
            native_module_
                ->runtime_stub(static_cast<WasmCode::RuntimeStubId>(tag))
                ->instruction_start();
        iter.rinfo()->set_wasm_stub_call_address(target, SKIP_ICACHE_FLUSH);
        break;
      }
552 553
      case RelocInfo::EXTERNAL_REFERENCE: {
        uint32_t tag = GetWasmCalleeTag(iter.rinfo());
554
        Address address = ExternalReferenceList::Get().address_from_tag(tag);
555
        iter.rinfo()->set_target_external_reference(address, SKIP_ICACHE_FLUSH);
556
        break;
557
      }
558 559 560 561 562 563 564 565
      case RelocInfo::INTERNAL_REFERENCE:
      case RelocInfo::INTERNAL_REFERENCE_ENCODED: {
        Address offset = iter.rinfo()->target_internal_reference();
        Address target = code->instruction_start() + offset;
        Assembler::deserialization_set_target_internal_reference_at(
            iter.rinfo()->pc(), target, mode);
        break;
      }
566 567
      default:
        UNREACHABLE();
568 569
    }
  }
570

571
  code->MaybePrint();
572 573 574 575 576 577
  code->Validate();

  // Finally, flush the icache for that code.
  Assembler::FlushICache(code->instructions().start(),
                         code->instructions().size());

578 579 580
  return true;
}

581
bool IsSupportedVersion(Vector<const byte> version) {
582 583 584
  if (version.size() < kVersionSize) return false;
  byte current_version[kVersionSize];
  Writer writer({current_version, kVersionSize});
585
  WriteVersion(&writer);
586 587 588
  return memcmp(version.start(), current_version, kVersionSize) == 0;
}

589
MaybeHandle<WasmModuleObject> DeserializeNativeModule(
590 591
    Isolate* isolate, Vector<const byte> data,
    Vector<const byte> wire_bytes_vec) {
592 593 594
  if (!IsWasmCodegenAllowed(isolate, isolate->native_context())) return {};
  if (!IsSupportedVersion(data)) return {};

595
  ModuleWireBytes wire_bytes(wire_bytes_vec);
596 597
  // TODO(titzer): module features should be part of the serialization format.
  WasmFeatures enabled_features = WasmFeaturesFromIsolate(isolate);
598
  ModuleResult decode_result = DecodeWasmModule(
599 600
      enabled_features, wire_bytes.start(), wire_bytes.end(), false,
      i::wasm::kWasmOrigin, isolate->counters(), isolate->allocator());
601 602 603
  if (decode_result.failed()) return {};
  CHECK_NOT_NULL(decode_result.value());
  WasmModule* module = decode_result.value().get();
604 605
  Handle<Script> script =
      CreateWasmScript(isolate, wire_bytes, module->source_map_url);
606

607 608
  OwnedVector<uint8_t> wire_bytes_copy =
      OwnedVector<uint8_t>::Of(wire_bytes_vec);
609

610
  Handle<WasmModuleObject> module_object = WasmModuleObject::New(
611
      isolate, enabled_features, std::move(decode_result).value(),
612
      std::move(wire_bytes_copy), script, Handle<ByteArray>::null());
613
  NativeModule* native_module = module_object->native_module();
614

615 616 617
  if (FLAG_wasm_lazy_compilation) {
    native_module->SetLazyBuiltin(BUILTIN_CODE(isolate, WasmCompileLazy));
  }
618
  NativeModuleDeserializer deserializer(native_module);
619 620 621

  Reader reader(data + kVersionSize);
  if (!deserializer.Read(&reader)) return {};
622

623
  CompileJsToWasmWrappers(isolate, native_module->module(),
624
                          handle(module_object->export_wrappers(), isolate));
625

626
  // Log the code within the generated module for profiling.
627
  native_module->LogWasmCodes(isolate);
628

629
  return module_object;
630 631 632 633 634
}

}  // namespace wasm
}  // namespace internal
}  // namespace v8