V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
wasm-serialization.cc
1 // Copyright 2017 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/wasm/wasm-serialization.h"
6 
7 #include "src/assembler-inl.h"
8 #include "src/external-reference-table.h"
9 #include "src/objects-inl.h"
10 #include "src/objects.h"
11 #include "src/snapshot/code-serializer.h"
12 #include "src/snapshot/serializer-common.h"
13 #include "src/utils.h"
14 #include "src/version.h"
15 #include "src/wasm/function-compiler.h"
16 #include "src/wasm/module-compiler.h"
17 #include "src/wasm/module-decoder.h"
18 #include "src/wasm/wasm-code-manager.h"
19 #include "src/wasm/wasm-module.h"
20 #include "src/wasm/wasm-objects-inl.h"
21 #include "src/wasm/wasm-objects.h"
22 #include "src/wasm/wasm-result.h"
23 
24 namespace v8 {
25 namespace internal {
26 namespace wasm {
27 
28 namespace {
29 
30 // TODO(bbudge) Try to unify the various implementations of readers and writers
31 // in WASM, e.g. StreamProcessor and ZoneBuffer, with these.
32 class Writer {
33  public:
34  explicit Writer(Vector<byte> buffer)
35  : start_(buffer.start()), end_(buffer.end()), pos_(buffer.start()) {}
36 
37  size_t bytes_written() const { return pos_ - start_; }
38  byte* current_location() const { return pos_; }
39  size_t current_size() const { return end_ - pos_; }
40  Vector<byte> current_buffer() const {
41  return {current_location(), current_size()};
42  }
43 
44  template <typename T>
45  void Write(const T& value) {
46  DCHECK_GE(current_size(), sizeof(T));
47  WriteUnalignedValue(reinterpret_cast<Address>(current_location()), value);
48  pos_ += sizeof(T);
49  if (FLAG_trace_wasm_serialization) {
50  StdoutStream{} << "wrote: " << static_cast<size_t>(value)
51  << " sized: " << sizeof(T) << std::endl;
52  }
53  }
54 
55  void WriteVector(const Vector<const byte> v) {
56  DCHECK_GE(current_size(), v.size());
57  if (v.size() > 0) {
58  memcpy(current_location(), v.start(), v.size());
59  pos_ += v.size();
60  }
61  if (FLAG_trace_wasm_serialization) {
62  StdoutStream{} << "wrote vector of " << v.size() << " elements"
63  << std::endl;
64  }
65  }
66 
67  void Skip(size_t size) { pos_ += size; }
68 
69  private:
70  byte* const start_;
71  byte* const end_;
72  byte* pos_;
73 };
74 
75 class Reader {
76  public:
77  explicit Reader(Vector<const byte> buffer)
78  : start_(buffer.start()), end_(buffer.end()), pos_(buffer.start()) {}
79 
80  size_t bytes_read() const { return pos_ - start_; }
81  const byte* current_location() const { return pos_; }
82  size_t current_size() const { return end_ - pos_; }
83  Vector<const byte> current_buffer() const {
84  return {current_location(), current_size()};
85  }
86 
87  template <typename T>
88  T Read() {
89  DCHECK_GE(current_size(), sizeof(T));
90  T value =
91  ReadUnalignedValue<T>(reinterpret_cast<Address>(current_location()));
92  pos_ += sizeof(T);
93  if (FLAG_trace_wasm_serialization) {
94  StdoutStream{} << "read: " << static_cast<size_t>(value)
95  << " sized: " << sizeof(T) << std::endl;
96  }
97  return value;
98  }
99 
100  void ReadVector(Vector<byte> v) {
101  if (v.size() > 0) {
102  DCHECK_GE(current_size(), v.size());
103  memcpy(v.start(), current_location(), v.size());
104  pos_ += v.size();
105  }
106  if (FLAG_trace_wasm_serialization) {
107  StdoutStream{} << "read vector of " << v.size() << " elements"
108  << std::endl;
109  }
110  }
111 
112  void Skip(size_t size) { pos_ += size; }
113 
114  private:
115  const byte* const start_;
116  const byte* const end_;
117  const byte* pos_;
118 };
119 
120 constexpr size_t kVersionSize = 4 * sizeof(uint32_t);
121 
122 void WriteVersion(Isolate* isolate, Writer* writer) {
123  writer->Write(
124  SerializedData::ComputeMagicNumber(isolate->external_reference_table()));
125  writer->Write(Version::Hash());
126  writer->Write(static_cast<uint32_t>(CpuFeatures::SupportedFeatures()));
127  writer->Write(FlagList::Hash());
128 }
129 
130 // On Intel, call sites are encoded as a displacement. For linking and for
131 // serialization/deserialization, we want to store/retrieve a tag (the function
132 // index). On Intel, that means accessing the raw displacement.
133 // On ARM64, call sites are encoded as either a literal load or a direct branch.
134 // Other platforms simply require accessing the target address.
135 void SetWasmCalleeTag(RelocInfo* rinfo, uint32_t tag) {
136 #if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32
137  *(reinterpret_cast<uint32_t*>(rinfo->target_address_address())) = tag;
138 #elif V8_TARGET_ARCH_ARM64
139  Instruction* instr = reinterpret_cast<Instruction*>(rinfo->pc());
140  if (instr->IsLdrLiteralX()) {
141  Memory<Address>(rinfo->constant_pool_entry_address()) =
142  static_cast<Address>(tag);
143  } else {
144  DCHECK(instr->IsBranchAndLink() || instr->IsUnconditionalBranch());
145  instr->SetBranchImmTarget(
146  reinterpret_cast<Instruction*>(rinfo->pc() + tag * kInstrSize));
147  }
148 #else
149  Address addr = static_cast<Address>(tag);
150  if (rinfo->rmode() == RelocInfo::EXTERNAL_REFERENCE) {
151  rinfo->set_target_external_reference(addr, SKIP_ICACHE_FLUSH);
152  } else if (rinfo->rmode() == RelocInfo::WASM_STUB_CALL) {
153  rinfo->set_wasm_stub_call_address(addr, SKIP_ICACHE_FLUSH);
154  } else {
155  rinfo->set_target_address(addr, SKIP_WRITE_BARRIER, SKIP_ICACHE_FLUSH);
156  }
157 #endif
158 }
159 
160 uint32_t GetWasmCalleeTag(RelocInfo* rinfo) {
161 #if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32
162  return *(reinterpret_cast<uint32_t*>(rinfo->target_address_address()));
163 #elif V8_TARGET_ARCH_ARM64
164  Instruction* instr = reinterpret_cast<Instruction*>(rinfo->pc());
165  if (instr->IsLdrLiteralX()) {
166  return static_cast<uint32_t>(
167  Memory<Address>(rinfo->constant_pool_entry_address()));
168  } else {
169  DCHECK(instr->IsBranchAndLink() || instr->IsUnconditionalBranch());
170  return static_cast<uint32_t>(instr->ImmPCOffset() / kInstrSize);
171  }
172 #else
173  Address addr;
174  if (rinfo->rmode() == RelocInfo::EXTERNAL_REFERENCE) {
175  addr = rinfo->target_external_reference();
176  } else if (rinfo->rmode() == RelocInfo::WASM_STUB_CALL) {
177  addr = rinfo->wasm_stub_call_address();
178  } else {
179  addr = rinfo->target_address();
180  }
181  return static_cast<uint32_t>(addr);
182 #endif
183 }
184 
185 constexpr size_t kHeaderSize =
186  sizeof(uint32_t) + // total wasm function count
187  sizeof(uint32_t); // imported functions (index of first wasm function)
188 
189 constexpr size_t kCodeHeaderSize =
190  sizeof(size_t) + // size of code section
191  sizeof(size_t) + // offset of constant pool
192  sizeof(size_t) + // offset of safepoint table
193  sizeof(size_t) + // offset of handler table
194  sizeof(uint32_t) + // stack slots
195  sizeof(size_t) + // code size
196  sizeof(size_t) + // reloc size
197  sizeof(size_t) + // source positions size
198  sizeof(size_t) + // protected instructions size
199  sizeof(WasmCode::Tier); // tier
200 
201 } // namespace
202 
203 class V8_EXPORT_PRIVATE NativeModuleSerializer {
204  public:
205  NativeModuleSerializer() = delete;
208 
209  size_t Measure() const;
210  bool Write(Writer* writer);
211 
212  private:
213  size_t MeasureCode(const WasmCode*) const;
214  void WriteHeader(Writer* writer);
215  void WriteCode(const WasmCode*, Writer* writer);
216 
217  Isolate* const isolate_;
218  const NativeModule* const native_module_;
219  Vector<WasmCode* const> code_table_;
220  bool write_called_;
221 
222  // Reverse lookup tables for embedded addresses.
223  std::map<Address, uint32_t> wasm_stub_targets_lookup_;
224  std::map<Address, uint32_t> reference_table_lookup_;
225 
226  DISALLOW_COPY_AND_ASSIGN(NativeModuleSerializer);
227 };
228 
229 NativeModuleSerializer::NativeModuleSerializer(
230  Isolate* isolate, const NativeModule* module,
231  Vector<WasmCode* const> code_table)
232  : isolate_(isolate),
233  native_module_(module),
234  code_table_(code_table),
235  write_called_(false) {
236  DCHECK_NOT_NULL(isolate_);
237  DCHECK_NOT_NULL(native_module_);
238  // TODO(mtrofin): persist the export wrappers. Ideally, we'd only persist
239  // the unique ones, i.e. the cache.
240  for (uint32_t i = 0; i < WasmCode::kRuntimeStubCount; ++i) {
241  Address addr =
242  native_module_->runtime_stub(static_cast<WasmCode::RuntimeStubId>(i))
243  ->instruction_start();
244  wasm_stub_targets_lookup_.insert(std::make_pair(addr, i));
245  }
246  ExternalReferenceTable* table = isolate_->external_reference_table();
247  for (uint32_t i = 0; i < table->size(); ++i) {
248  Address addr = table->address(i);
249  reference_table_lookup_.insert(std::make_pair(addr, i));
250  }
251 }
252 
253 size_t NativeModuleSerializer::MeasureCode(const WasmCode* code) const {
254  if (code == nullptr) return sizeof(size_t);
255  DCHECK_EQ(WasmCode::kFunction, code->kind());
256  return kCodeHeaderSize + code->instructions().size() +
257  code->reloc_info().size() + code->source_positions().size() +
258  code->protected_instructions().size() *
259  sizeof(trap_handler::ProtectedInstructionData);
260 }
261 
262 size_t NativeModuleSerializer::Measure() const {
263  size_t size = kHeaderSize;
264  for (WasmCode* code : code_table_) {
265  size += MeasureCode(code);
266  }
267  return size;
268 }
269 
270 void NativeModuleSerializer::WriteHeader(Writer* writer) {
271  // TODO(eholk): We need to properly preserve the flag whether the trap
272  // handler was used or not when serializing.
273 
274  writer->Write(native_module_->num_functions());
275  writer->Write(native_module_->num_imported_functions());
276 }
277 
278 void NativeModuleSerializer::WriteCode(const WasmCode* code, Writer* writer) {
279  if (code == nullptr) {
280  writer->Write(size_t{0});
281  return;
282  }
283  DCHECK_EQ(WasmCode::kFunction, code->kind());
284  // Write the size of the entire code section, followed by the code header.
285  writer->Write(MeasureCode(code));
286  writer->Write(code->constant_pool_offset());
287  writer->Write(code->safepoint_table_offset());
288  writer->Write(code->handler_table_offset());
289  writer->Write(code->stack_slots());
290  writer->Write(code->instructions().size());
291  writer->Write(code->reloc_info().size());
292  writer->Write(code->source_positions().size());
293  writer->Write(code->protected_instructions().size());
294  writer->Write(code->tier());
295 
296  // Get a pointer to the destination buffer, to hold relocated code.
297  byte* serialized_code_start = writer->current_buffer().start();
298  byte* code_start = serialized_code_start;
299  size_t code_size = code->instructions().size();
300  writer->Skip(code_size);
301  // Write the reloc info, source positions, and protected code.
302  writer->WriteVector(code->reloc_info());
303  writer->WriteVector(code->source_positions());
304  writer->WriteVector(Vector<byte>::cast(code->protected_instructions()));
305 #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_ARM
306  // On platforms that don't support misaligned word stores, copy to an aligned
307  // buffer if necessary so we can relocate the serialized code.
308  std::unique_ptr<byte[]> aligned_buffer;
309  if (!IsAligned(reinterpret_cast<Address>(serialized_code_start),
310  kInt32Size)) {
311  aligned_buffer.reset(new byte[code_size]);
312  code_start = aligned_buffer.get();
313  }
314 #endif
315  memcpy(code_start, code->instructions().start(), code_size);
316  // Relocate the code.
317  int mask = RelocInfo::ModeMask(RelocInfo::WASM_CALL) |
318  RelocInfo::ModeMask(RelocInfo::WASM_STUB_CALL) |
319  RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
320  RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
321  RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED);
322  RelocIterator orig_iter(code->instructions(), code->reloc_info(),
323  code->constant_pool(), mask);
324  for (RelocIterator iter(
325  {code_start, code->instructions().size()}, code->reloc_info(),
326  reinterpret_cast<Address>(code_start) + code->constant_pool_offset(),
327  mask);
328  !iter.done(); iter.next(), orig_iter.next()) {
329  RelocInfo::Mode mode = orig_iter.rinfo()->rmode();
330  switch (mode) {
331  case RelocInfo::WASM_CALL: {
332  Address orig_target = orig_iter.rinfo()->wasm_call_address();
333  uint32_t tag =
334  native_module_->GetFunctionIndexFromJumpTableSlot(orig_target);
335  SetWasmCalleeTag(iter.rinfo(), tag);
336  } break;
337  case RelocInfo::WASM_STUB_CALL: {
338  Address orig_target = orig_iter.rinfo()->wasm_stub_call_address();
339  auto stub_iter = wasm_stub_targets_lookup_.find(orig_target);
340  DCHECK(stub_iter != wasm_stub_targets_lookup_.end());
341  uint32_t tag = stub_iter->second;
342  SetWasmCalleeTag(iter.rinfo(), tag);
343  } break;
344  case RelocInfo::EXTERNAL_REFERENCE: {
345  Address orig_target = orig_iter.rinfo()->target_external_reference();
346  auto ref_iter = reference_table_lookup_.find(orig_target);
347  DCHECK(ref_iter != reference_table_lookup_.end());
348  uint32_t tag = ref_iter->second;
349  SetWasmCalleeTag(iter.rinfo(), tag);
350  } break;
351  case RelocInfo::INTERNAL_REFERENCE:
352  case RelocInfo::INTERNAL_REFERENCE_ENCODED: {
353  Address orig_target = orig_iter.rinfo()->target_internal_reference();
354  Address offset = orig_target - code->instruction_start();
355  Assembler::deserialization_set_target_internal_reference_at(
356  iter.rinfo()->pc(), offset, mode);
357  } break;
358  default:
359  UNREACHABLE();
360  }
361  }
362  // If we copied to an aligned buffer, copy code into serialized buffer.
363  if (code_start != serialized_code_start) {
364  memcpy(serialized_code_start, code_start, code_size);
365  }
366 }
367 
368 bool NativeModuleSerializer::Write(Writer* writer) {
369  DCHECK(!write_called_);
370  write_called_ = true;
371 
372  WriteHeader(writer);
373 
374  for (WasmCode* code : code_table_) {
375  WriteCode(code, writer);
376  }
377  return true;
378 }
379 
380 WasmSerializer::WasmSerializer(Isolate* isolate, NativeModule* native_module)
381  : isolate_(isolate),
382  native_module_(native_module),
383  code_table_(native_module->SnapshotCodeTable()) {}
384 
385 size_t WasmSerializer::GetSerializedNativeModuleSize() const {
386  NativeModuleSerializer serializer(isolate_, native_module_,
387  VectorOf(code_table_));
388  return kVersionSize + serializer.Measure();
389 }
390 
391 bool WasmSerializer::SerializeNativeModule(Vector<byte> buffer) const {
392  NativeModuleSerializer serializer(isolate_, native_module_,
393  VectorOf(code_table_));
394  size_t measured_size = kVersionSize + serializer.Measure();
395  if (buffer.size() < measured_size) return false;
396 
397  Writer writer(buffer);
398  WriteVersion(isolate_, &writer);
399 
400  if (!serializer.Write(&writer)) return false;
401  DCHECK_EQ(measured_size, writer.bytes_written());
402  return true;
403 }
404 
405 class V8_EXPORT_PRIVATE NativeModuleDeserializer {
406  public:
407  NativeModuleDeserializer() = delete;
409 
410  bool Read(Reader* reader);
411 
412  private:
413  bool ReadHeader(Reader* reader);
414  bool ReadCode(uint32_t fn_index, Reader* reader);
415 
416  Isolate* const isolate_;
417  NativeModule* const native_module_;
418  bool read_called_;
419 
420  DISALLOW_COPY_AND_ASSIGN(NativeModuleDeserializer);
421 };
422 
423 NativeModuleDeserializer::NativeModuleDeserializer(Isolate* isolate,
424  NativeModule* native_module)
425  : isolate_(isolate), native_module_(native_module), read_called_(false) {}
426 
427 bool NativeModuleDeserializer::Read(Reader* reader) {
428  DCHECK(!read_called_);
429  read_called_ = true;
430 
431  if (!ReadHeader(reader)) return false;
432  uint32_t total_fns = native_module_->num_functions();
433  uint32_t first_wasm_fn = native_module_->num_imported_functions();
434  for (uint32_t i = first_wasm_fn; i < total_fns; ++i) {
435  if (!ReadCode(i, reader)) return false;
436  }
437  return reader->current_size() == 0;
438 }
439 
440 bool NativeModuleDeserializer::ReadHeader(Reader* reader) {
441  size_t functions = reader->Read<uint32_t>();
442  size_t imports = reader->Read<uint32_t>();
443  return functions == native_module_->num_functions() &&
444  imports == native_module_->num_imported_functions();
445 }
446 
447 bool NativeModuleDeserializer::ReadCode(uint32_t fn_index, Reader* reader) {
448  size_t code_section_size = reader->Read<size_t>();
449  if (code_section_size == 0) return true;
450  size_t constant_pool_offset = reader->Read<size_t>();
451  size_t safepoint_table_offset = reader->Read<size_t>();
452  size_t handler_table_offset = reader->Read<size_t>();
453  uint32_t stack_slot_count = reader->Read<uint32_t>();
454  size_t code_size = reader->Read<size_t>();
455  size_t reloc_size = reader->Read<size_t>();
456  size_t source_position_size = reader->Read<size_t>();
457  size_t protected_instructions_size = reader->Read<size_t>();
458  WasmCode::Tier tier = reader->Read<WasmCode::Tier>();
459 
460  Vector<const byte> code_buffer = {reader->current_location(), code_size};
461  reader->Skip(code_size);
462 
463  OwnedVector<byte> reloc_info = OwnedVector<byte>::New(reloc_size);
464  reader->ReadVector(reloc_info.as_vector());
465  OwnedVector<byte> source_pos = OwnedVector<byte>::New(source_position_size);
466  reader->ReadVector(source_pos.as_vector());
467  auto protected_instructions =
468  OwnedVector<trap_handler::ProtectedInstructionData>::New(
469  protected_instructions_size);
470  reader->ReadVector(Vector<byte>::cast(protected_instructions.as_vector()));
471 
472  WasmCode* code = native_module_->AddDeserializedCode(
473  fn_index, code_buffer, stack_slot_count, safepoint_table_offset,
474  handler_table_offset, constant_pool_offset,
475  std::move(protected_instructions), std::move(reloc_info),
476  std::move(source_pos), tier);
477 
478  // Relocate the code.
479  int mask = RelocInfo::ModeMask(RelocInfo::WASM_CALL) |
480  RelocInfo::ModeMask(RelocInfo::WASM_STUB_CALL) |
481  RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
482  RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
483  RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED);
484  for (RelocIterator iter(code->instructions(), code->reloc_info(),
485  code->constant_pool(), mask);
486  !iter.done(); iter.next()) {
487  RelocInfo::Mode mode = iter.rinfo()->rmode();
488  switch (mode) {
489  case RelocInfo::WASM_CALL: {
490  uint32_t tag = GetWasmCalleeTag(iter.rinfo());
491  Address target = native_module_->GetCallTargetForFunction(tag);
492  iter.rinfo()->set_wasm_call_address(target, SKIP_ICACHE_FLUSH);
493  break;
494  }
495  case RelocInfo::WASM_STUB_CALL: {
496  uint32_t tag = GetWasmCalleeTag(iter.rinfo());
497  DCHECK_LT(tag, WasmCode::kRuntimeStubCount);
498  Address target =
499  native_module_
500  ->runtime_stub(static_cast<WasmCode::RuntimeStubId>(tag))
501  ->instruction_start();
502  iter.rinfo()->set_wasm_stub_call_address(target, SKIP_ICACHE_FLUSH);
503  break;
504  }
505  case RelocInfo::EXTERNAL_REFERENCE: {
506  uint32_t tag = GetWasmCalleeTag(iter.rinfo());
507  Address address = isolate_->external_reference_table()->address(tag);
508  iter.rinfo()->set_target_external_reference(address, SKIP_ICACHE_FLUSH);
509  break;
510  }
511  case RelocInfo::INTERNAL_REFERENCE:
512  case RelocInfo::INTERNAL_REFERENCE_ENCODED: {
513  Address offset = iter.rinfo()->target_internal_reference();
514  Address target = code->instruction_start() + offset;
515  Assembler::deserialization_set_target_internal_reference_at(
516  iter.rinfo()->pc(), target, mode);
517  break;
518  }
519  default:
520  UNREACHABLE();
521  }
522  }
523 
524  if (FLAG_print_code || FLAG_print_wasm_code) code->Print();
525  code->Validate();
526 
527  // Finally, flush the icache for that code.
528  Assembler::FlushICache(code->instructions().start(),
529  code->instructions().size());
530 
531  return true;
532 }
533 
534 bool IsSupportedVersion(Isolate* isolate, Vector<const byte> version) {
535  if (version.size() < kVersionSize) return false;
536  byte current_version[kVersionSize];
537  Writer writer({current_version, kVersionSize});
538  WriteVersion(isolate, &writer);
539  return memcmp(version.start(), current_version, kVersionSize) == 0;
540 }
541 
542 MaybeHandle<WasmModuleObject> DeserializeNativeModule(
543  Isolate* isolate, Vector<const byte> data, Vector<const byte> wire_bytes) {
544  if (!IsWasmCodegenAllowed(isolate, isolate->native_context())) {
545  return {};
546  }
547  if (!IsSupportedVersion(isolate, data)) {
548  return {};
549  }
550  // TODO(titzer): module features should be part of the serialization format.
551  WasmFeatures enabled_features = WasmFeaturesFromIsolate(isolate);
552  ModuleResult decode_result = DecodeWasmModule(
553  enabled_features, wire_bytes.start(), wire_bytes.end(), false,
554  i::wasm::kWasmOrigin, isolate->counters(), isolate->allocator());
555  if (decode_result.failed()) return {};
556  CHECK_NOT_NULL(decode_result.value());
557  WasmModule* module = decode_result.value().get();
558  Handle<Script> script =
559  CreateWasmScript(isolate, wire_bytes, module->source_map_url);
560 
561  OwnedVector<uint8_t> wire_bytes_copy = OwnedVector<uint8_t>::Of(wire_bytes);
562 
563  Handle<WasmModuleObject> module_object = WasmModuleObject::New(
564  isolate, enabled_features, std::move(decode_result).value(),
565  std::move(wire_bytes_copy), script, Handle<ByteArray>::null());
566  NativeModule* native_module = module_object->native_module();
567 
568  if (FLAG_wasm_lazy_compilation) {
569  native_module->SetLazyBuiltin(BUILTIN_CODE(isolate, WasmCompileLazy));
570  }
571  NativeModuleDeserializer deserializer(isolate, native_module);
572 
573  Reader reader(data + kVersionSize);
574  if (!deserializer.Read(&reader)) return {};
575 
576  CompileJsToWasmWrappers(isolate, native_module,
577  handle(module_object->export_wrappers(), isolate));
578 
579  // Log the code within the generated module for profiling.
580  native_module->LogWasmCodes(isolate);
581 
582  return module_object;
583 }
584 
585 } // namespace wasm
586 } // namespace internal
587 } // namespace v8
Definition: libplatform.h:13