blob: d95b6c631e4ffb829c451268c0b9863d3f545a3e [file] [log] [blame]
// 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"
#include "src/snapshot/code-serializer.h"
#include "src/snapshot/serializer-common.h"
#include "src/utils.h"
#include "src/version.h"
#include "src/wasm/function-compiler.h"
#include "src/wasm/module-compiler.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-code-manager.h"
#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 {
namespace {
// TODO(bbudge) Try to unify the various implementations of readers and writers
// in WASM, e.g. StreamProcessor and ZoneBuffer, with these.
class Writer {
public:
explicit Writer(Vector<byte> buffer)
: 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()};
}
template <typename T>
void Write(const T& value) {
DCHECK_GE(current_size(), sizeof(T));
WriteUnalignedValue(reinterpret_cast<Address>(current_location()), value);
pos_ += sizeof(T);
if (FLAG_wasm_trace_serialization) {
OFStream os(stdout);
os << "wrote: " << (size_t)value << " sized: " << sizeof(T) << std::endl;
}
}
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();
}
if (FLAG_wasm_trace_serialization) {
OFStream os(stdout);
os << "wrote vector of " << v.size() << " elements" << std::endl;
}
}
void Skip(size_t size) { pos_ += size; }
private:
byte* const start_;
byte* const end_;
byte* pos_;
};
class Reader {
public:
explicit Reader(Vector<const byte> buffer)
: 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()};
}
template <typename T>
T Read() {
DCHECK_GE(current_size(), sizeof(T));
T value =
ReadUnalignedValue<T>(reinterpret_cast<Address>(current_location()));
pos_ += sizeof(T);
if (FLAG_wasm_trace_serialization) {
OFStream os(stdout);
os << "read: " << (size_t)value << " sized: " << sizeof(T) << std::endl;
}
return value;
}
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();
}
if (FLAG_wasm_trace_serialization) {
OFStream os(stdout);
os << "read vector of " << v.size() << " elements" << std::endl;
}
}
void Skip(size_t size) { pos_ += size; }
private:
const byte* const start_;
const byte* const end_;
const byte* pos_;
};
constexpr size_t kVersionSize = 4 * sizeof(uint32_t);
void WriteVersion(Isolate* isolate, Writer* writer) {
writer->Write(SerializedData::ComputeMagicNumber(
isolate->heap()->external_reference_table()));
writer->Write(Version::Hash());
writer->Write(static_cast<uint32_t>(CpuFeatures::SupportedFeatures()));
writer->Write(FlagList::Hash());
}
bool IsSupportedVersion(Isolate* isolate, const Vector<const byte> version) {
if (version.size() < kVersionSize) return false;
byte current_version[kVersionSize];
Writer writer({current_version, kVersionSize});
WriteVersion(isolate, &writer);
return memcmp(version.start(), current_version, kVersionSize) == 0;
}
// On Intel, call sites are encoded as a displacement. For linking and for
// serialization/deserialization, we want to store/retrieve a tag (the function
// 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.
void SetWasmCalleeTag(RelocInfo* rinfo, uint32_t tag) {
#if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32
*(reinterpret_cast<uint32_t*>(rinfo->target_address_address())) = tag;
#elif V8_TARGET_ARCH_ARM64
Instruction* instr = reinterpret_cast<Instruction*>(rinfo->pc());
if (instr->IsLdrLiteralX()) {
Memory::Address_at(rinfo->constant_pool_entry_address()) =
static_cast<Address>(tag);
} else {
DCHECK(instr->IsBranchAndLink() || instr->IsUnconditionalBranch());
instr->SetBranchImmTarget(
reinterpret_cast<Instruction*>(rinfo->pc() + tag * kInstructionSize));
}
#else
Address addr = static_cast<Address>(tag);
if (rinfo->rmode() == RelocInfo::EXTERNAL_REFERENCE) {
rinfo->set_target_external_reference(addr, SKIP_ICACHE_FLUSH);
} else if (rinfo->rmode() == RelocInfo::WASM_STUB_CALL) {
rinfo->set_wasm_stub_call_address(addr, SKIP_ICACHE_FLUSH);
} else {
rinfo->set_target_address(addr, SKIP_WRITE_BARRIER, SKIP_ICACHE_FLUSH);
}
#endif
}
uint32_t GetWasmCalleeTag(RelocInfo* rinfo) {
#if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32
return *(reinterpret_cast<uint32_t*>(rinfo->target_address_address()));
#elif V8_TARGET_ARCH_ARM64
Instruction* instr = reinterpret_cast<Instruction*>(rinfo->pc());
if (instr->IsLdrLiteralX()) {
return static_cast<uint32_t>(
Memory::Address_at(rinfo->constant_pool_entry_address()));
} else {
DCHECK(instr->IsBranchAndLink() || instr->IsUnconditionalBranch());
return static_cast<uint32_t>(instr->ImmPCOffset() / kInstructionSize);
}
#else
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();
}
return static_cast<uint32_t>(addr);
#endif
}
constexpr size_t kHeaderSize =
sizeof(uint32_t) + // total wasm function count
sizeof(uint32_t); // imported functions (index of first wasm function)
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
sizeof(uint32_t) + // stack slots
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
} // namespace
class V8_EXPORT_PRIVATE NativeModuleSerializer {
public:
NativeModuleSerializer() = delete;
NativeModuleSerializer(Isolate*, const NativeModule*);
size_t Measure() const;
bool Write(Writer* writer);
private:
size_t MeasureCode(const WasmCode*) const;
void WriteHeader(Writer* writer);
void WriteCode(const WasmCode*, Writer* writer);
uint32_t EncodeBuiltin(Address);
Isolate* const isolate_;
const NativeModule* const native_module_;
bool write_called_;
// wasm code targets reverse lookup
std::map<Address, uint32_t> wasm_targets_lookup_;
std::map<Address, uint32_t> wasm_stub_targets_lookup_;
// immovable builtins and runtime entries lookup
std::map<Address, uint32_t> reference_table_lookup_;
std::map<Address, uint32_t> builtin_lookup_;
DISALLOW_COPY_AND_ASSIGN(NativeModuleSerializer);
};
NativeModuleSerializer::NativeModuleSerializer(Isolate* isolate,
const NativeModule* module)
: isolate_(isolate), native_module_(module), write_called_(false) {
DCHECK_NOT_NULL(isolate_);
DCHECK_NOT_NULL(native_module_);
// TODO(mtrofin): persist the export wrappers. Ideally, we'd only persist
// the unique ones, i.e. the cache.
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));
}
ExternalReferenceTable* table = isolate_->heap()->external_reference_table();
for (uint32_t i = 0; i < table->size(); ++i) {
Address addr = table->address(i);
reference_table_lookup_.insert(std::make_pair(addr, i));
}
for (auto pair : native_module_->trampolines_) {
v8::internal::Code* code = Code::GetCodeFromTargetAddress(pair.first);
int builtin_index = code->builtin_index();
DCHECK_GE(builtin_index, 0);
// Note that ARM64 can only encode 26 bits in branch immediate instructions.
DCHECK_LT(builtin_index, 1 << 26);
uint32_t tag = static_cast<uint32_t>(builtin_index);
builtin_lookup_.insert(std::make_pair(pair.second, tag));
}
}
size_t NativeModuleSerializer::MeasureCode(const WasmCode* code) const {
if (code->kind() == WasmCode::kLazyStub) return sizeof(size_t);
DCHECK_EQ(WasmCode::kFunction, code->kind());
return kCodeHeaderSize + code->instructions().size() +
code->reloc_info().size() + code->source_positions().size() +
code->protected_instructions().size() *
sizeof(trap_handler::ProtectedInstructionData);
}
size_t NativeModuleSerializer::Measure() const {
size_t size = kHeaderSize;
for (WasmCode* code : native_module_->code_table()) {
size += MeasureCode(code);
}
return size;
}
void NativeModuleSerializer::WriteHeader(Writer* writer) {
writer->Write(native_module_->num_functions());
writer->Write(native_module_->num_imported_functions());
}
void NativeModuleSerializer::WriteCode(const WasmCode* code, Writer* writer) {
if (code->kind() == WasmCode::kLazyStub) {
writer->Write(size_t{0});
return;
}
DCHECK_EQ(WasmCode::kFunction, code->kind());
// 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());
writer->Write(code->stack_slots());
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());
// Get a pointer to the destination buffer, to hold relocated code.
byte* serialized_code_start = writer->current_buffer().start();
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.
writer->WriteVector(code->reloc_info());
writer->WriteVector(code->source_positions());
writer->WriteVector(
{reinterpret_cast<const byte*>(code->protected_instructions().data()),
sizeof(trap_handler::ProtectedInstructionData) *
code->protected_instructions().size()});
#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);
// Relocate the code.
int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
RelocInfo::ModeMask(RelocInfo::WASM_CALL) |
RelocInfo::ModeMask(RelocInfo::WASM_STUB_CALL) |
RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE);
RelocIterator orig_iter(code->instructions(), code->reloc_info(),
code->constant_pool(), mask);
for (RelocIterator iter(
{code_start, code->instructions().size()}, code->reloc_info(),
reinterpret_cast<Address>(code_start) + code->constant_pool_offset(),
mask);
!iter.done(); iter.next(), orig_iter.next()) {
RelocInfo::Mode mode = orig_iter.rinfo()->rmode();
switch (mode) {
case RelocInfo::CODE_TARGET: {
Address orig_target = orig_iter.rinfo()->target_address();
uint32_t tag = EncodeBuiltin(orig_target);
SetWasmCalleeTag(iter.rinfo(), tag);
} break;
case RelocInfo::WASM_CALL: {
Address orig_target = orig_iter.rinfo()->wasm_call_address();
uint32_t tag = wasm_targets_lookup_[orig_target];
SetWasmCalleeTag(iter.rinfo(), tag);
} break;
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;
case RelocInfo::EXTERNAL_REFERENCE: {
Address orig_target = orig_iter.rinfo()->target_external_reference();
auto ref_iter = reference_table_lookup_.find(orig_target);
DCHECK(ref_iter != reference_table_lookup_.end());
uint32_t tag = ref_iter->second;
SetWasmCalleeTag(iter.rinfo(), tag);
} break;
default:
UNREACHABLE();
}
}
// 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);
}
}
uint32_t NativeModuleSerializer::EncodeBuiltin(Address address) {
DCHECK_EQ(1, builtin_lookup_.count(address));
return builtin_lookup_.find(address)->second;
}
bool NativeModuleSerializer::Write(Writer* writer) {
DCHECK(!write_called_);
write_called_ = true;
WriteHeader(writer);
for (WasmCode* code : native_module_->code_table()) {
WriteCode(code, writer);
}
return true;
}
size_t GetSerializedNativeModuleSize(
Isolate* isolate, Handle<WasmCompiledModule> compiled_module) {
NativeModule* native_module = compiled_module->GetNativeModule();
NativeModuleSerializer serializer(isolate, native_module);
return kVersionSize + serializer.Measure();
}
bool SerializeNativeModule(Isolate* isolate,
Handle<WasmCompiledModule> compiled_module,
Vector<byte> buffer) {
NativeModule* native_module = compiled_module->GetNativeModule();
NativeModuleSerializer serializer(isolate, native_module);
size_t measured_size = kVersionSize + serializer.Measure();
if (buffer.size() < measured_size) return false;
Writer writer(buffer);
WriteVersion(isolate, &writer);
if (!serializer.Write(&writer)) return false;
DCHECK_EQ(measured_size, writer.bytes_written());
return true;
}
class V8_EXPORT_PRIVATE NativeModuleDeserializer {
public:
NativeModuleDeserializer() = delete;
NativeModuleDeserializer(Isolate*, NativeModule*);
bool Read(Reader* reader);
private:
bool ReadHeader(Reader* reader);
bool ReadCode(uint32_t fn_index, Reader* reader);
Address GetBuiltinTrampolineFromTag(uint32_t);
Isolate* const isolate_;
NativeModule* const native_module_;
bool read_called_;
DISALLOW_COPY_AND_ASSIGN(NativeModuleDeserializer);
};
NativeModuleDeserializer::NativeModuleDeserializer(Isolate* isolate,
NativeModule* native_module)
: isolate_(isolate), native_module_(native_module), read_called_(false) {}
bool NativeModuleDeserializer::Read(Reader* reader) {
DCHECK(!read_called_);
read_called_ = true;
if (!ReadHeader(reader)) return false;
uint32_t total_fns = native_module_->num_functions();
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;
}
return reader->current_size() == 0;
}
bool NativeModuleDeserializer::ReadHeader(Reader* reader) {
size_t functions = reader->Read<uint32_t>();
size_t imports = reader->Read<uint32_t>();
return functions == native_module_->num_functions() &&
imports == native_module_->num_imported_functions();
}
bool NativeModuleDeserializer::ReadCode(uint32_t fn_index, Reader* reader) {
size_t code_section_size = reader->Read<size_t>();
if (code_section_size == 0) return true;
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>();
uint32_t stack_slot_count = reader->Read<uint32_t>();
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>();
Vector<const byte> code_buffer = {reader->current_location(), code_size};
reader->Skip(code_size);
std::unique_ptr<byte[]> reloc_info;
if (reloc_size > 0) {
reloc_info.reset(new byte[reloc_size]);
reader->ReadVector({reloc_info.get(), reloc_size});
}
std::unique_ptr<byte[]> source_pos;
if (source_position_size > 0) {
source_pos.reset(new byte[source_position_size]);
reader->ReadVector({source_pos.get(), source_position_size});
}
std::unique_ptr<ProtectedInstructions> protected_instructions(
new ProtectedInstructions(protected_instructions_size));
if (protected_instructions_size > 0) {
size_t size = sizeof(trap_handler::ProtectedInstructionData) *
protected_instructions->size();
Vector<byte> data(reinterpret_cast<byte*>(protected_instructions->data()),
size);
reader->ReadVector(data);
}
WasmCode* ret = native_module_->AddOwnedCode(
code_buffer, std::move(reloc_info), reloc_size, std::move(source_pos),
source_position_size, Just(fn_index), WasmCode::kFunction,
constant_pool_offset, stack_slot_count, safepoint_table_offset,
handler_table_offset, std::move(protected_instructions), tier,
WasmCode::kNoFlushICache);
native_module_->set_code(fn_index, ret);
// Relocate the code.
int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
RelocInfo::ModeMask(RelocInfo::WASM_STUB_CALL) |
RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
RelocInfo::ModeMask(RelocInfo::WASM_CODE_TABLE_ENTRY);
for (RelocIterator iter(ret->instructions(), ret->reloc_info(),
ret->constant_pool(), mask);
!iter.done(); iter.next()) {
RelocInfo::Mode mode = iter.rinfo()->rmode();
switch (mode) {
case RelocInfo::CODE_TARGET: {
uint32_t tag = GetWasmCalleeTag(iter.rinfo());
Address target = GetBuiltinTrampolineFromTag(tag);
iter.rinfo()->set_target_address(target, SKIP_WRITE_BARRIER,
SKIP_ICACHE_FLUSH);
break;
}
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;
}
case RelocInfo::EXTERNAL_REFERENCE: {
uint32_t tag = GetWasmCalleeTag(iter.rinfo());
Address address =
isolate_->heap()->external_reference_table()->address(tag);
iter.rinfo()->set_target_external_reference(address, SKIP_ICACHE_FLUSH);
break;
}
case RelocInfo::WASM_CODE_TABLE_ENTRY: {
DCHECK(FLAG_wasm_tier_up);
DCHECK(ret->is_liftoff());
uint32_t code_table_index =
ret->index() - native_module_->num_imported_functions_;
WasmCode** code_table_entry =
&native_module_->code_table()[code_table_index];
iter.rinfo()->set_wasm_code_table_entry(
reinterpret_cast<Address>(code_table_entry), SKIP_ICACHE_FLUSH);
break;
}
default:
UNREACHABLE();
}
}
// Flush the i-cache here instead of in AddOwnedCode, to include the changes
// made while iterating over the RelocInfo above.
Assembler::FlushICache(ret->instructions().start(),
ret->instructions().size());
if (FLAG_print_code || FLAG_print_wasm_code) {
// TODO(mstarzinger): don't need the isolate here.
ret->Print(isolate_);
}
return true;
}
Address NativeModuleDeserializer::GetBuiltinTrampolineFromTag(uint32_t tag) {
int builtin_id = static_cast<int>(tag);
v8::internal::Code* builtin = isolate_->builtins()->builtin(builtin_id);
return native_module_->GetLocalAddressFor(handle(builtin, isolate_));
}
MaybeHandle<WasmModuleObject> DeserializeNativeModule(
Isolate* isolate, Vector<const byte> data, Vector<const byte> wire_bytes) {
if (!IsWasmCodegenAllowed(isolate, isolate->native_context())) {
return {};
}
if (!IsSupportedVersion(isolate, data)) {
return {};
}
ModuleResult decode_result =
SyncDecodeWasmModule(isolate, wire_bytes.start(), wire_bytes.end(), false,
i::wasm::kWasmOrigin);
if (!decode_result.ok()) return {};
CHECK_NOT_NULL(decode_result.val);
Handle<String> module_bytes =
isolate->factory()
->NewStringFromOneByte(
{wire_bytes.start(), static_cast<size_t>(wire_bytes.length())},
TENURED)
.ToHandleChecked();
DCHECK(module_bytes->IsSeqOneByteString());
// The {managed_module} will take ownership of the {WasmModule} object,
// and it will be destroyed when the GC reclaims it.
size_t module_size = EstimateWasmModuleSize(decode_result.val.get());
Handle<Managed<WasmModule>> managed_module =
Managed<WasmModule>::FromUniquePtr(isolate, module_size,
std::move(decode_result.val));
Handle<Script> script = CreateWasmScript(isolate, wire_bytes);
Handle<WasmSharedModuleData> shared = WasmSharedModuleData::New(
isolate, managed_module, Handle<SeqOneByteString>::cast(module_bytes),
script, Handle<ByteArray>::null());
int export_wrappers_size =
static_cast<int>(shared->module()->num_exported_functions);
Handle<FixedArray> export_wrappers = isolate->factory()->NewFixedArray(
static_cast<int>(export_wrappers_size), TENURED);
// TODO(eholk): We need to properly preserve the flag whether the trap
// handler was used or not when serializing.
UseTrapHandler use_trap_handler =
trap_handler::IsTrapHandlerEnabled() ? kUseTrapHandler : kNoTrapHandler;
wasm::ModuleEnv env(shared->module(), use_trap_handler,
wasm::RuntimeExceptionSupport::kRuntimeExceptionSupport);
Handle<WasmCompiledModule> compiled_module =
WasmCompiledModule::New(isolate, shared->module(), env);
NativeModule* native_module = compiled_module->GetNativeModule();
native_module->SetSharedModuleData(shared);
if (FLAG_wasm_lazy_compilation) {
native_module->SetLazyBuiltin(BUILTIN_CODE(isolate, WasmCompileLazy));
}
NativeModuleDeserializer deserializer(isolate, native_module);
Reader reader(data + kVersionSize);
if (!deserializer.Read(&reader)) return {};
Handle<WasmModuleObject> module_object =
WasmModuleObject::New(isolate, compiled_module, export_wrappers, shared);
// TODO(6792): Wrappers below might be cloned using {Factory::CopyCode}. This
// requires unlocking the code space here. This should eventually be moved
// into the allocator.
CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
CompileJsToWasmWrappers(isolate, module_object, isolate->counters());
// There are no instances for this module yet, which means we need to reset
// the module into a state as if the last instance was collected.
WasmCompiledModule::Reset(isolate, *compiled_module);
// Log the code within the generated module for profiling.
compiled_module->LogWasmCodes(isolate);
return module_object;
}
} // namespace wasm
} // namespace internal
} // namespace v8