summaryrefslogtreecommitdiff
path: root/deps/v8/src/wasm/module-compiler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/wasm/module-compiler.cc')
-rw-r--r--deps/v8/src/wasm/module-compiler.cc2138
1 files changed, 1438 insertions, 700 deletions
diff --git a/deps/v8/src/wasm/module-compiler.cc b/deps/v8/src/wasm/module-compiler.cc
index e42c139ce1..4bd52a2a8f 100644
--- a/deps/v8/src/wasm/module-compiler.cc
+++ b/deps/v8/src/wasm/module-compiler.cc
@@ -9,15 +9,18 @@
#include "src/api.h"
#include "src/asmjs/asm-js.h"
#include "src/assembler-inl.h"
+#include "src/base/optional.h"
#include "src/base/template-utils.h"
#include "src/base/utils/random-number-generator.h"
#include "src/code-stubs.h"
#include "src/compiler/wasm-compiler.h"
#include "src/counters.h"
#include "src/property-descriptor.h"
+#include "src/trap-handler/trap-handler.h"
#include "src/wasm/compilation-manager.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-code-specialization.h"
+#include "src/wasm/wasm-heap.h"
#include "src/wasm/wasm-js.h"
#include "src/wasm/wasm-memory.h"
#include "src/wasm/wasm-objects-inl.h"
@@ -42,6 +45,12 @@
do { \
if (FLAG_trace_wasm_streaming) PrintF(__VA_ARGS__); \
} while (false)
+
+#define TRACE_LAZY(...) \
+ do { \
+ if (FLAG_trace_wasm_lazy_compilation) PrintF(__VA_ARGS__); \
+ } while (false)
+
static const int kInvalidSigIndex = -1;
namespace v8 {
@@ -51,8 +60,8 @@ namespace wasm {
// A class compiling an entire module.
class ModuleCompiler {
public:
- ModuleCompiler(Isolate* isolate, WasmModule* module,
- Handle<Code> centry_stub);
+ ModuleCompiler(Isolate* isolate, WasmModule* module, Handle<Code> centry_stub,
+ wasm::NativeModule* native_module);
// The actual runnable task that performs compilations in the background.
class CompilationTask : public CancelableTask {
@@ -81,14 +90,16 @@ class ModuleCompiler {
~CompilationUnitBuilder() { DCHECK(units_.empty()); }
- void AddUnit(compiler::ModuleEnv* module_env, const WasmFunction* function,
- uint32_t buffer_offset, Vector<const uint8_t> bytes,
- WasmName name) {
+ void AddUnit(compiler::ModuleEnv* module_env,
+ wasm::NativeModule* native_module,
+ const WasmFunction* function, uint32_t buffer_offset,
+ Vector<const uint8_t> bytes, WasmName name) {
units_.emplace_back(new compiler::WasmCompilationUnit(
- compiler_->isolate_, module_env,
+ compiler_->isolate_, module_env, native_module,
wasm::FunctionBody{function->sig, buffer_offset, bytes.begin(),
bytes.end()},
name, function->func_index, compiler_->centry_stub_,
+ compiler::WasmCompilationUnit::GetDefaultCompilationMode(),
compiler_->counters()));
}
@@ -171,8 +182,7 @@ class ModuleCompiler {
void SetFinisherIsRunning(bool value);
- MaybeHandle<Code> FinishCompilationUnit(ErrorThrower* thrower,
- int* func_index);
+ WasmCodeWrapper FinishCompilationUnit(ErrorThrower* thrower, int* func_index);
void CompileInParallel(const ModuleWireBytes& wire_bytes,
compiler::ModuleEnv* module_env,
@@ -215,8 +225,11 @@ class ModuleCompiler {
size_t stopped_compilation_tasks_ = 0;
base::Mutex tasks_mutex_;
Handle<Code> centry_stub_;
+ wasm::NativeModule* native_module_;
};
+namespace {
+
class JSToWasmWrapperCache {
public:
void SetContextAddress(Address context_address) {
@@ -227,23 +240,37 @@ class JSToWasmWrapperCache {
Handle<Code> CloneOrCompileJSToWasmWrapper(Isolate* isolate,
wasm::WasmModule* module,
- Handle<Code> wasm_code,
+ WasmCodeWrapper wasm_code,
uint32_t index) {
const wasm::WasmFunction* func = &module->functions[index];
int cached_idx = sig_map_.Find(func->sig);
if (cached_idx >= 0) {
Handle<Code> code = isolate->factory()->CopyCode(code_cache_[cached_idx]);
// Now patch the call to wasm code.
- for (RelocIterator it(*code, RelocInfo::kCodeTargetMask);; it.next()) {
- DCHECK(!it.done());
- Code* target =
- Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
- if (target->kind() == Code::WASM_FUNCTION ||
- target->kind() == Code::WASM_TO_JS_FUNCTION ||
- target->builtin_index() == Builtins::kIllegal ||
- target->builtin_index() == Builtins::kWasmCompileLazy) {
- it.rinfo()->set_target_address(isolate,
- wasm_code->instruction_start());
+ if (wasm_code.IsCodeObject()) {
+ for (RelocIterator it(*code, RelocInfo::kCodeTargetMask);; it.next()) {
+ DCHECK(!it.done());
+ Code* target =
+ Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
+ if (target->kind() == Code::WASM_FUNCTION ||
+ target->kind() == Code::WASM_TO_JS_FUNCTION ||
+ target->kind() == Code::WASM_TO_WASM_FUNCTION ||
+ target->builtin_index() == Builtins::kIllegal ||
+ target->builtin_index() == Builtins::kWasmCompileLazy) {
+ it.rinfo()->set_target_address(
+ isolate, wasm_code.GetCode()->instruction_start());
+ break;
+ }
+ }
+ } else {
+ for (RelocIterator it(*code,
+ RelocInfo::ModeMask(RelocInfo::JS_TO_WASM_CALL));
+ ; it.next()) {
+ DCHECK(!it.done());
+ it.rinfo()->set_js_to_wasm_address(
+ isolate, wasm_code.is_null()
+ ? nullptr
+ : wasm_code.GetWasmCode()->instructions().start());
break;
}
}
@@ -335,6 +362,8 @@ class InstanceBuilder {
ERROR_THROWER_WITH_MESSAGE(LinkError)
ERROR_THROWER_WITH_MESSAGE(TypeError)
+#undef ERROR_THROWER_WITH_MESSAGE
+
// Look up an import value in the {ffi_} object.
MaybeHandle<Object> LookupImport(uint32_t index, Handle<String> module_name,
Handle<String> import_name);
@@ -349,7 +378,7 @@ class InstanceBuilder {
uint32_t EvalUint32InitExpr(const WasmInitExpr& expr);
// Load data segments into the memory.
- void LoadDataSegments(Address mem_addr, size_t mem_size);
+ void LoadDataSegments(WasmContext* wasm_context);
void WriteGlobalValue(WasmGlobal& global, Handle<Object> value);
@@ -388,7 +417,7 @@ class InstanceBuilder {
};
// TODO(titzer): move to wasm-objects.cc
-static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
+void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
DisallowHeapAllocation no_gc;
JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter());
WasmInstanceObject* owner = reinterpret_cast<WasmInstanceObject*>(*p);
@@ -396,21 +425,32 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
// If a link to shared memory instances exists, update the list of memory
// instances before the instance is destroyed.
WasmCompiledModule* compiled_module = owner->compiled_module();
- TRACE("Finalizing %d {\n", compiled_module->instance_id());
- DCHECK(compiled_module->has_weak_wasm_module());
- WeakCell* weak_wasm_module = compiled_module->ptr_to_weak_wasm_module();
+ wasm::NativeModule* native_module = compiled_module->GetNativeModule();
+ if (FLAG_wasm_jit_to_native) {
+ if (native_module) {
+ TRACE("Finalizing %zu {\n", native_module->instance_id);
+ } else {
+ TRACE("Finalized already cleaned up compiled module\n");
+ }
+ } else {
+ TRACE("Finalizing %d {\n", compiled_module->instance_id());
- if (trap_handler::UseTrapHandler()) {
- Handle<FixedArray> code_table = compiled_module->code_table();
- for (int i = 0; i < code_table->length(); ++i) {
- Handle<Code> code = code_table->GetValueChecked<Code>(isolate, i);
- int index = code->trap_handler_index()->value();
- if (index >= 0) {
- trap_handler::ReleaseHandlerData(index);
- code->set_trap_handler_index(Smi::FromInt(trap_handler::kInvalidIndex));
+ if (trap_handler::UseTrapHandler()) {
+ // TODO(6792): No longer needed once WebAssembly code is off heap.
+ CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
+ Handle<FixedArray> code_table = compiled_module->code_table();
+ for (int i = 0; i < code_table->length(); ++i) {
+ Handle<Code> code = code_table->GetValueChecked<Code>(isolate, i);
+ int index = code->trap_handler_index()->value();
+ if (index >= 0) {
+ trap_handler::ReleaseHandlerData(index);
+ code->set_trap_handler_index(
+ Smi::FromInt(trap_handler::kInvalidIndex));
+ }
}
}
}
+ WeakCell* weak_wasm_module = compiled_module->ptr_to_weak_wasm_module();
// Since the order of finalizers is not guaranteed, it can be the case
// that {instance->compiled_module()->module()}, which is a
@@ -425,56 +465,32 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
}
// weak_wasm_module may have been cleared, meaning the module object
- // was GC-ed. In that case, there won't be any new instances created,
- // and we don't need to maintain the links between instances.
+ // was GC-ed. We still want to maintain the links between instances, to
+ // release the WasmCompiledModule corresponding to the WasmModuleInstance
+ // being finalized here.
+ WasmModuleObject* wasm_module = nullptr;
if (!weak_wasm_module->cleared()) {
- WasmModuleObject* wasm_module =
- WasmModuleObject::cast(weak_wasm_module->value());
+ wasm_module = WasmModuleObject::cast(weak_wasm_module->value());
WasmCompiledModule* current_template = wasm_module->compiled_module();
TRACE("chain before {\n");
TRACE_CHAIN(current_template);
TRACE("}\n");
- DCHECK(!current_template->has_weak_prev_instance());
- WeakCell* next = compiled_module->maybe_ptr_to_weak_next_instance();
- WeakCell* prev = compiled_module->maybe_ptr_to_weak_prev_instance();
-
+ DCHECK(!current_template->has_prev_instance());
if (current_template == compiled_module) {
- if (next == nullptr) {
+ if (!compiled_module->has_next_instance()) {
WasmCompiledModule::Reset(isolate, compiled_module);
} else {
- WasmCompiledModule* next_compiled_module =
- WasmCompiledModule::cast(next->value());
WasmModuleObject::cast(wasm_module)
- ->set_compiled_module(next_compiled_module);
- DCHECK_NULL(prev);
- next_compiled_module->reset_weak_prev_instance();
- }
- } else {
- DCHECK(!(prev == nullptr && next == nullptr));
- // the only reason prev or next would be cleared is if the
- // respective objects got collected, but if that happened,
- // we would have relinked the list.
- if (prev != nullptr) {
- DCHECK(!prev->cleared());
- if (next == nullptr) {
- WasmCompiledModule::cast(prev->value())->reset_weak_next_instance();
- } else {
- WasmCompiledModule::cast(prev->value())
- ->set_ptr_to_weak_next_instance(next);
- }
- }
- if (next != nullptr) {
- DCHECK(!next->cleared());
- if (prev == nullptr) {
- WasmCompiledModule::cast(next->value())->reset_weak_prev_instance();
- } else {
- WasmCompiledModule::cast(next->value())
- ->set_ptr_to_weak_prev_instance(prev);
- }
+ ->set_compiled_module(compiled_module->ptr_to_next_instance());
}
}
+ }
+
+ compiled_module->RemoveFromChain();
+
+ if (wasm_module != nullptr) {
TRACE("chain after {\n");
TRACE_CHAIN(wasm_module->compiled_module());
TRACE("}\n");
@@ -484,6 +500,8 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
TRACE("}\n");
}
+} // namespace
+
bool SyncValidate(Isolate* isolate, const ModuleWireBytes& bytes) {
if (bytes.start() == nullptr || bytes.length() == 0) return false;
ModuleResult result = SyncDecodeWasmModule(isolate, bytes.start(),
@@ -547,9 +565,8 @@ MaybeHandle<WasmInstanceObject> SyncCompileAndInstantiate(
DCHECK_EQ(thrower->error(), module.is_null());
if (module.is_null()) return {};
- return SyncInstantiate(isolate, thrower, module.ToHandleChecked(),
- Handle<JSReceiver>::null(),
- Handle<JSArrayBuffer>::null());
+ return SyncInstantiate(isolate, thrower, module.ToHandleChecked(), imports,
+ memory);
}
void RejectPromise(Isolate* isolate, Handle<Context> context,
@@ -617,7 +634,7 @@ void AsyncCompile(Isolate* isolate, Handle<JSPromise> promise,
promise);
}
-Handle<Code> CompileLazy(Isolate* isolate) {
+Handle<Code> CompileLazyOnGCHeap(Isolate* isolate) {
HistogramTimerScope lazy_time_scope(
isolate->counters()->wasm_lazy_compilation_time());
@@ -635,13 +652,14 @@ Handle<Code> CompileLazy(Isolate* isolate) {
Handle<WasmInstanceObject> instance;
Handle<FixedArray> exp_deopt_data;
int func_index = -1;
+ // If the lazy compile stub has deopt data, use that to determine the
+ // instance and function index. Otherwise this must be a wasm->wasm call
+ // within one instance, so extract the information from the caller.
if (lazy_compile_code->deoptimization_data()->length() > 0) {
- // Then it's an indirect call or via JS->wasm wrapper.
- DCHECK_LE(2, lazy_compile_code->deoptimization_data()->length());
exp_deopt_data = handle(lazy_compile_code->deoptimization_data(), isolate);
- auto* weak_cell = WeakCell::cast(exp_deopt_data->get(0));
- instance = handle(WasmInstanceObject::cast(weak_cell->value()), isolate);
- func_index = Smi::ToInt(exp_deopt_data->get(1));
+ auto func_info = GetWasmFunctionInfo(isolate, lazy_compile_code);
+ instance = func_info.instance.ToHandleChecked();
+ func_index = func_info.func_index;
}
it.Advance();
// Third frame: The calling wasm code or js-to-wasm wrapper.
@@ -655,7 +673,7 @@ Handle<Code> CompileLazy(Isolate* isolate) {
// via deopt data to the lazy compile stub). Just use the instance of the
// caller.
instance =
- handle(WasmInstanceObject::GetOwningInstance(*caller_code), isolate);
+ handle(WasmInstanceObject::GetOwningInstanceGC(*caller_code), isolate);
}
int offset =
static_cast<int>(it.frame()->pc() - caller_code->instruction_start());
@@ -668,9 +686,17 @@ Handle<Code> CompileLazy(Isolate* isolate) {
bool patch_caller = caller_code->kind() == Code::JS_TO_WASM_FUNCTION ||
exp_deopt_data.is_null() || exp_deopt_data->length() <= 2;
- Handle<Code> compiled_code = WasmCompiledModule::CompileLazy(
+ wasm::LazyCompilationOrchestrator* orchestrator =
+ Managed<wasm::LazyCompilationOrchestrator>::cast(
+ instance->compiled_module()
+ ->shared()
+ ->lazy_compilation_orchestrator())
+ ->get();
+ Handle<Code> compiled_code = orchestrator->CompileLazyOnGCHeap(
isolate, instance, caller_code, offset, func_index, patch_caller);
if (!exp_deopt_data.is_null() && exp_deopt_data->length() > 2) {
+ TRACE_LAZY("Patching %d position(s) in function tables.\n",
+ (exp_deopt_data->length() - 2) / 2);
// See EnsureExportedLazyDeoptData: exp_deopt_data[2...(len-1)] are pairs of
// <export_table, index> followed by undefined values.
// Use this information here to patch all export tables.
@@ -682,6 +708,8 @@ Handle<Code> CompileLazy(Isolate* isolate) {
DCHECK(exp_table->get(exp_index) == *lazy_compile_code);
exp_table->set(exp_index, *compiled_code);
}
+ // TODO(6792): No longer needed once WebAssembly code is off heap.
+ CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
// After processing, remove the list of exported entries, such that we don't
// do the patching redundantly.
Handle<FixedArray> new_deopt_data =
@@ -692,48 +720,178 @@ Handle<Code> CompileLazy(Isolate* isolate) {
return compiled_code;
}
+Address CompileLazy(Isolate* isolate) {
+ HistogramTimerScope lazy_time_scope(
+ isolate->counters()->wasm_lazy_compilation_time());
+
+ // Find the wasm frame which triggered the lazy compile, to get the wasm
+ // instance.
+ StackFrameIterator it(isolate);
+ // First frame: C entry stub.
+ DCHECK(!it.done());
+ DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
+ it.Advance();
+ // Second frame: WasmCompileLazy builtin.
+ DCHECK(!it.done());
+ Handle<WasmInstanceObject> instance;
+ Maybe<uint32_t> func_index_to_compile = Nothing<uint32_t>();
+ Handle<Object> exp_deopt_data_entry;
+ const wasm::WasmCode* lazy_stub_or_copy =
+ isolate->wasm_code_manager()->LookupCode(it.frame()->pc());
+ DCHECK_EQ(wasm::WasmCode::LazyStub, lazy_stub_or_copy->kind());
+ if (!lazy_stub_or_copy->IsAnonymous()) {
+ // Then it's an indirect call or via JS->wasm wrapper.
+ instance = lazy_stub_or_copy->owner()->compiled_module()->owning_instance();
+ func_index_to_compile = Just(lazy_stub_or_copy->index());
+ exp_deopt_data_entry =
+ handle(instance->compiled_module()->lazy_compile_data()->get(
+ static_cast<int>(lazy_stub_or_copy->index())),
+ isolate);
+ }
+ it.Advance();
+ // Third frame: The calling wasm code (direct or indirect), or js-to-wasm
+ // wrapper.
+ DCHECK(!it.done());
+ DCHECK(it.frame()->is_js_to_wasm() || it.frame()->is_wasm_compiled());
+ Handle<Code> js_to_wasm_caller_code;
+ const WasmCode* wasm_caller_code = nullptr;
+ Maybe<uint32_t> offset = Nothing<uint32_t>();
+ if (it.frame()->is_js_to_wasm()) {
+ DCHECK(!instance.is_null());
+ js_to_wasm_caller_code = handle(it.frame()->LookupCode(), isolate);
+ } else {
+ wasm_caller_code =
+ isolate->wasm_code_manager()->LookupCode(it.frame()->pc());
+ offset = Just(static_cast<uint32_t>(
+ it.frame()->pc() - wasm_caller_code->instructions().start()));
+ if (instance.is_null()) {
+ // Then this is a direct call (otherwise we would have attached the
+ // instance via deopt data to the lazy compile stub). Just use the
+ // instance of the caller.
+ instance =
+ wasm_caller_code->owner()->compiled_module()->owning_instance();
+ }
+ }
+
+ Handle<WasmCompiledModule> compiled_module(instance->compiled_module());
+
+ wasm::LazyCompilationOrchestrator* orchestrator =
+ Managed<wasm::LazyCompilationOrchestrator>::cast(
+ compiled_module->shared()->lazy_compilation_orchestrator())
+ ->get();
+ const wasm::WasmCode* result = nullptr;
+ // The caller may be js to wasm calling a function
+ // also available for indirect calls.
+ if (!js_to_wasm_caller_code.is_null()) {
+ result = orchestrator->CompileFromJsToWasm(
+ isolate, instance, js_to_wasm_caller_code,
+ func_index_to_compile.ToChecked());
+ } else {
+ DCHECK_NOT_NULL(wasm_caller_code);
+ if (func_index_to_compile.IsNothing() ||
+ (!exp_deopt_data_entry.is_null() &&
+ !exp_deopt_data_entry->IsFixedArray())) {
+ result = orchestrator->CompileDirectCall(
+ isolate, instance, func_index_to_compile, wasm_caller_code,
+ offset.ToChecked());
+ } else {
+ result = orchestrator->CompileIndirectCall(
+ isolate, instance, func_index_to_compile.ToChecked());
+ }
+ }
+ DCHECK_NOT_NULL(result);
+
+ int func_index = static_cast<int>(result->index());
+ if (!exp_deopt_data_entry.is_null() && exp_deopt_data_entry->IsFixedArray()) {
+ Handle<FixedArray> exp_deopt_data =
+ Handle<FixedArray>::cast(exp_deopt_data_entry);
+
+ TRACE_LAZY("Patching %d position(s) in function tables.\n",
+ exp_deopt_data->length() / 2);
+
+ // See EnsureExportedLazyDeoptData: exp_deopt_data[0...(len-1)] are pairs
+ // of <export_table, index> followed by undefined values. Use this
+ // information here to patch all export tables.
+ for (int idx = 0, end = exp_deopt_data->length(); idx < end; idx += 2) {
+ if (exp_deopt_data->get(idx)->IsUndefined(isolate)) break;
+ FixedArray* exp_table = FixedArray::cast(exp_deopt_data->get(idx));
+ int exp_index = Smi::ToInt(exp_deopt_data->get(idx + 1));
+ Handle<Foreign> foreign_holder = isolate->factory()->NewForeign(
+ result->instructions().start(), TENURED);
+ exp_table->set(exp_index, *foreign_holder);
+ }
+ // TODO(6792): No longer needed once WebAssembly code is off heap.
+ CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
+ // After processing, remove the list of exported entries, such that we don't
+ // do the patching redundantly.
+ compiled_module->lazy_compile_data()->set(
+ func_index, isolate->heap()->undefined_value());
+ }
+
+ return result->instructions().start();
+}
+
compiler::ModuleEnv CreateModuleEnvFromCompiledModule(
Isolate* isolate, Handle<WasmCompiledModule> compiled_module) {
DisallowHeapAllocation no_gc;
WasmModule* module = compiled_module->module();
+ std::vector<Handle<Code>> empty_code;
+ if (FLAG_wasm_jit_to_native) {
+ NativeModule* native_module = compiled_module->GetNativeModule();
+ std::vector<GlobalHandleAddress> function_tables =
+ native_module->function_tables();
+ std::vector<GlobalHandleAddress> signature_tables =
+ native_module->signature_tables();
+
+ compiler::ModuleEnv result = {module, // --
+ function_tables, // --
+ signature_tables, // --
+ empty_code,
+ BUILTIN_CODE(isolate, WasmCompileLazy)};
+ return result;
+ } else {
+ std::vector<GlobalHandleAddress> function_tables;
+ std::vector<GlobalHandleAddress> signature_tables;
- std::vector<GlobalHandleAddress> function_tables;
- std::vector<GlobalHandleAddress> signature_tables;
- std::vector<SignatureMap*> signature_maps;
+ int num_function_tables = static_cast<int>(module->function_tables.size());
+ for (int i = 0; i < num_function_tables; ++i) {
+ FixedArray* ft = compiled_module->ptr_to_function_tables();
+ FixedArray* st = compiled_module->ptr_to_signature_tables();
- int num_function_tables = static_cast<int>(module->function_tables.size());
- for (int i = 0; i < num_function_tables; ++i) {
- FixedArray* ft = compiled_module->ptr_to_function_tables();
- FixedArray* st = compiled_module->ptr_to_signature_tables();
+ // TODO(clemensh): defer these handles for concurrent compilation.
+ function_tables.push_back(WasmCompiledModule::GetTableValue(ft, i));
+ signature_tables.push_back(WasmCompiledModule::GetTableValue(st, i));
+ }
- // TODO(clemensh): defer these handles for concurrent compilation.
- function_tables.push_back(WasmCompiledModule::GetTableValue(ft, i));
- signature_tables.push_back(WasmCompiledModule::GetTableValue(st, i));
- signature_maps.push_back(&module->function_tables[i].map);
+ compiler::ModuleEnv result = {module, // --
+ function_tables, // --
+ signature_tables, // --
+ empty_code, // --
+ BUILTIN_CODE(isolate, WasmCompileLazy)};
+ return result;
}
-
- std::vector<Handle<Code>> empty_code;
-
- compiler::ModuleEnv result = {
- module, // --
- function_tables, // --
- signature_tables, // --
- signature_maps, // --
- empty_code, // --
- BUILTIN_CODE(isolate, WasmCompileLazy), // --
- reinterpret_cast<uintptr_t>( // --
- compiled_module->GetGlobalsStartOrNull()) // --
- };
- return result;
}
-void LazyCompilationOrchestrator::CompileFunction(
+const wasm::WasmCode* LazyCompilationOrchestrator::CompileFunction(
Isolate* isolate, Handle<WasmInstanceObject> instance, int func_index) {
+ base::ElapsedTimer compilation_timer;
+ compilation_timer.Start();
Handle<WasmCompiledModule> compiled_module(instance->compiled_module(),
isolate);
- if (Code::cast(compiled_module->code_table()->get(func_index))->kind() ==
- Code::WASM_FUNCTION) {
- return;
+ if (FLAG_wasm_jit_to_native) {
+ wasm::WasmCode* existing_code = compiled_module->GetNativeModule()->GetCode(
+ static_cast<uint32_t>(func_index));
+ if (existing_code != nullptr &&
+ existing_code->kind() == wasm::WasmCode::Function) {
+ TRACE_LAZY("Function %d already compiled.\n", func_index);
+ return existing_code;
+ }
+ } else {
+ if (Code::cast(compiled_module->code_table()->get(func_index))->kind() ==
+ Code::WASM_FUNCTION) {
+ TRACE_LAZY("Function %d already compiled.\n", func_index);
+ return nullptr;
+ }
}
compiler::ModuleEnv module_env =
@@ -756,11 +914,14 @@ void LazyCompilationOrchestrator::CompileFunction(
func_name.assign(name.start(), static_cast<size_t>(name.length()));
}
ErrorThrower thrower(isolate, "WasmLazyCompile");
- compiler::WasmCompilationUnit unit(isolate, &module_env, body,
+ compiler::WasmCompilationUnit unit(isolate, &module_env,
+ compiled_module->GetNativeModule(), body,
CStrVector(func_name.c_str()), func_index,
CEntryStub(isolate, 1).GetCode());
unit.ExecuteCompilation();
- MaybeHandle<Code> maybe_code = unit.FinishCompilation(&thrower);
+ // TODO(6792): No longer needed once WebAssembly code is off heap.
+ CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
+ WasmCodeWrapper code_wrapper = unit.FinishCompilation(&thrower);
// If there is a pending error, something really went wrong. The module was
// verified before starting execution with lazy compilation.
@@ -768,33 +929,53 @@ void LazyCompilationOrchestrator::CompileFunction(
// TODO(clemensh): According to the spec, we can actually skip validation at
// module creation time, and return a function that always traps here.
CHECK(!thrower.error());
- Handle<Code> code = maybe_code.ToHandleChecked();
-
- Handle<FixedArray> deopt_data = isolate->factory()->NewFixedArray(2, TENURED);
- Handle<WeakCell> weak_instance = isolate->factory()->NewWeakCell(instance);
- // TODO(wasm): Introduce constants for the indexes in wasm deopt data.
- deopt_data->set(0, *weak_instance);
- deopt_data->set(1, Smi::FromInt(func_index));
- code->set_deoptimization_data(*deopt_data);
-
- DCHECK_EQ(Builtins::kWasmCompileLazy,
- Code::cast(compiled_module->code_table()->get(func_index))
- ->builtin_index());
- compiled_module->code_table()->set(func_index, *code);
-
// Now specialize the generated code for this instance.
+
+ // {code} is used only when !FLAG_wasm_jit_to_native, so it may be removed
+ // when that flag is removed.
+ Handle<Code> code;
+ if (code_wrapper.IsCodeObject()) {
+ code = code_wrapper.GetCode();
+ AttachWasmFunctionInfo(isolate, code, instance, func_index);
+ DCHECK_EQ(Builtins::kWasmCompileLazy,
+ Code::cast(compiled_module->code_table()->get(func_index))
+ ->builtin_index());
+ compiled_module->code_table()->set(func_index, *code);
+ }
Zone specialization_zone(isolate->allocator(), ZONE_NAME);
CodeSpecialization code_specialization(isolate, &specialization_zone);
code_specialization.RelocateDirectCalls(instance);
- code_specialization.ApplyToWasmCode(*code, SKIP_ICACHE_FLUSH);
- Assembler::FlushICache(isolate, code->instruction_start(),
- code->instruction_size());
+ code_specialization.ApplyToWasmCode(code_wrapper, SKIP_ICACHE_FLUSH);
+ int64_t func_size =
+ static_cast<int64_t>(func->code.end_offset() - func->code.offset());
+ int64_t compilation_time = compilation_timer.Elapsed().InMicroseconds();
+
auto counters = isolate->counters();
counters->wasm_lazily_compiled_functions()->Increment();
- counters->wasm_generated_code_size()->Increment(code->body_size());
- counters->wasm_reloc_size()->Increment(code->relocation_info()->length());
+
+ if (!code_wrapper.IsCodeObject()) {
+ const wasm::WasmCode* wasm_code = code_wrapper.GetWasmCode();
+ Assembler::FlushICache(isolate, wasm_code->instructions().start(),
+ wasm_code->instructions().size());
+ counters->wasm_generated_code_size()->Increment(
+ static_cast<int>(wasm_code->instructions().size()));
+ counters->wasm_reloc_size()->Increment(
+ static_cast<int>(wasm_code->reloc_info().size()));
+
+ } else {
+ Assembler::FlushICache(isolate, code->instruction_start(),
+ code->instruction_size());
+ counters->wasm_generated_code_size()->Increment(code->body_size());
+ counters->wasm_reloc_size()->Increment(code->relocation_info()->length());
+ }
+ counters->wasm_lazy_compilation_throughput()->AddSample(
+ compilation_time != 0 ? static_cast<int>(func_size / compilation_time)
+ : 0);
+ return !code_wrapper.IsCodeObject() ? code_wrapper.GetWasmCode() : nullptr;
}
+namespace {
+
int AdvanceSourcePositionTableIterator(SourcePositionTableIterator& iterator,
int offset) {
DCHECK(!iterator.done());
@@ -806,7 +987,38 @@ int AdvanceSourcePositionTableIterator(SourcePositionTableIterator& iterator,
return byte_pos;
}
-Handle<Code> LazyCompilationOrchestrator::CompileLazy(
+Code* ExtractWasmToWasmCallee(Code* wasm_to_wasm) {
+ DCHECK_EQ(Code::WASM_TO_WASM_FUNCTION, wasm_to_wasm->kind());
+ // Find the one code target in this wrapper.
+ RelocIterator it(wasm_to_wasm, RelocInfo::kCodeTargetMask);
+ DCHECK(!it.done());
+ Code* callee = Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
+#ifdef DEBUG
+ it.next();
+ DCHECK(it.done());
+#endif
+ return callee;
+}
+
+void PatchWasmToWasmWrapper(Isolate* isolate, Code* wasm_to_wasm,
+ Code* new_target) {
+ DCHECK_EQ(Code::WASM_TO_WASM_FUNCTION, wasm_to_wasm->kind());
+ // Find the one code target in this wrapper.
+ RelocIterator it(wasm_to_wasm, RelocInfo::kCodeTargetMask);
+ DCHECK(!it.done());
+ DCHECK_EQ(Builtins::kWasmCompileLazy,
+ Code::GetCodeFromTargetAddress(it.rinfo()->target_address())
+ ->builtin_index());
+ it.rinfo()->set_target_address(isolate, new_target->instruction_start());
+#ifdef DEBUG
+ it.next();
+ DCHECK(it.done());
+#endif
+}
+
+} // namespace
+
+Handle<Code> LazyCompilationOrchestrator::CompileLazyOnGCHeap(
Isolate* isolate, Handle<WasmInstanceObject> instance, Handle<Code> caller,
int call_offset, int exported_func_index, bool patch_caller) {
struct NonCompiledFunction {
@@ -820,65 +1032,122 @@ Handle<Code> LazyCompilationOrchestrator::CompileLazy(
Handle<WasmCompiledModule> compiled_module(instance->compiled_module(),
isolate);
- if (is_js_to_wasm) {
- non_compiled_functions.push_back({0, exported_func_index});
- } else if (patch_caller) {
+ TRACE_LAZY(
+ "Starting lazy compilation (func %d @%d, js-to-wasm: %d, "
+ "patch caller: %d).\n",
+ exported_func_index, call_offset, is_js_to_wasm, patch_caller);
+
+ // If this lazy compile stub is being called through a wasm-to-wasm wrapper,
+ // remember that code object.
+ Handle<Code> wasm_to_wasm_callee;
+
+ // For js-to-wasm wrappers, don't iterate the reloc info. There is just one
+ // call site in there anyway.
+ if (patch_caller && !is_js_to_wasm) {
DisallowHeapAllocation no_gc;
- SeqOneByteString* module_bytes = compiled_module->module_bytes();
SourcePositionTableIterator source_pos_iterator(
caller->SourcePositionTable());
- DCHECK_EQ(2, caller->deoptimization_data()->length());
- int caller_func_index = Smi::ToInt(caller->deoptimization_data()->get(1));
+ auto caller_func_info = GetWasmFunctionInfo(isolate, caller);
+ Handle<WasmCompiledModule> caller_module(
+ caller_func_info.instance.ToHandleChecked()->compiled_module(),
+ isolate);
+ SeqOneByteString* module_bytes = caller_module->module_bytes();
const byte* func_bytes =
- module_bytes->GetChars() +
- compiled_module->module()->functions[caller_func_index].code.offset();
+ module_bytes->GetChars() + caller_module->module()
+ ->functions[caller_func_info.func_index]
+ .code.offset();
+ Code* lazy_callee = nullptr;
for (RelocIterator it(*caller, RelocInfo::kCodeTargetMask); !it.done();
it.next()) {
Code* callee =
Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
- if (callee->builtin_index() != Builtins::kWasmCompileLazy) continue;
// TODO(clemensh): Introduce safe_cast<T, bool> which (D)CHECKS
// (depending on the bool) against limits of T and then static_casts.
size_t offset_l = it.rinfo()->pc() - caller->instruction_start();
DCHECK_GE(kMaxInt, offset_l);
int offset = static_cast<int>(offset_l);
+ // Call offset points to the instruction after the call. Remember the last
+ // called code object before that offset.
+ if (offset < call_offset) lazy_callee = callee;
+ if (callee->builtin_index() != Builtins::kWasmCompileLazy) continue;
int byte_pos =
AdvanceSourcePositionTableIterator(source_pos_iterator, offset);
int called_func_index =
ExtractDirectCallIndex(decoder, func_bytes + byte_pos);
non_compiled_functions.push_back({offset, called_func_index});
- // Call offset one instruction after the call. Remember the last called
- // function before that offset.
if (offset < call_offset) func_to_return_idx = called_func_index;
}
+ TRACE_LAZY("Found %zu non-compiled functions in caller.\n",
+ non_compiled_functions.size());
+ DCHECK_NOT_NULL(lazy_callee);
+ if (lazy_callee->kind() == Code::WASM_TO_WASM_FUNCTION) {
+ TRACE_LAZY("Callee is a wasm-to-wasm.\n");
+ wasm_to_wasm_callee = handle(lazy_callee, isolate);
+ // If we call a wasm-to-wasm wrapper, then this wrapper actually
+ // tail-called the lazy compile stub. Find it in the wrapper.
+ lazy_callee = ExtractWasmToWasmCallee(lazy_callee);
+ // This lazy compile stub belongs to the instance that was passed.
+ DCHECK_EQ(*instance,
+ *GetWasmFunctionInfo(isolate, handle(lazy_callee, isolate))
+ .instance.ToHandleChecked());
+ DCHECK_LE(2, lazy_callee->deoptimization_data()->length());
+ func_to_return_idx =
+ Smi::ToInt(lazy_callee->deoptimization_data()->get(1));
+ }
+ DCHECK_EQ(Builtins::kWasmCompileLazy, lazy_callee->builtin_index());
+ // There must be at least one call to patch (the one that lead to calling
+ // the lazy compile stub).
+ DCHECK(!non_compiled_functions.empty() || !wasm_to_wasm_callee.is_null());
}
+ TRACE_LAZY("Compiling function %d.\n", func_to_return_idx);
+
// TODO(clemensh): compile all functions in non_compiled_functions in
// background, wait for func_to_return_idx.
CompileFunction(isolate, instance, func_to_return_idx);
- if (is_js_to_wasm || patch_caller) {
+ Handle<Code> compiled_function(
+ Code::cast(compiled_module->code_table()->get(func_to_return_idx)),
+ isolate);
+ DCHECK_EQ(Code::WASM_FUNCTION, compiled_function->kind());
+
+ if (patch_caller || is_js_to_wasm) {
DisallowHeapAllocation no_gc;
+ // TODO(6792): No longer needed once WebAssembly code is off heap.
+ CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
// Now patch the code object with all functions which are now compiled.
int idx = 0;
+ int patched = 0;
for (RelocIterator it(*caller, RelocInfo::kCodeTargetMask); !it.done();
it.next()) {
Code* callee =
Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
- if (callee->builtin_index() != Builtins::kWasmCompileLazy) continue;
- DCHECK_GT(non_compiled_functions.size(), idx);
- int called_func_index = non_compiled_functions[idx].func_index;
+ if (callee->builtin_index() != Builtins::kWasmCompileLazy) {
+ // If the callee is the wasm-to-wasm wrapper triggering this lazy
+ // compilation, patch it. If is_js_to_wasm is set, we did not set the
+ // wasm_to_wasm_callee, so just check the code kind (this is the only
+ // call in that wrapper anyway).
+ if ((is_js_to_wasm && callee->kind() == Code::WASM_TO_WASM_FUNCTION) ||
+ (!wasm_to_wasm_callee.is_null() &&
+ callee == *wasm_to_wasm_callee)) {
+ TRACE_LAZY("Patching wasm-to-wasm wrapper.\n");
+ PatchWasmToWasmWrapper(isolate, callee, *compiled_function);
+ ++patched;
+ }
+ continue;
+ }
+ int called_func_index = func_to_return_idx;
+ if (!is_js_to_wasm) {
+ DCHECK_GT(non_compiled_functions.size(), idx);
+ called_func_index = non_compiled_functions[idx].func_index;
+ DCHECK_EQ(non_compiled_functions[idx].offset,
+ it.rinfo()->pc() - caller->instruction_start());
+ ++idx;
+ }
// Check that the callee agrees with our assumed called_func_index.
DCHECK_IMPLIES(callee->deoptimization_data()->length() > 0,
Smi::ToInt(callee->deoptimization_data()->get(1)) ==
called_func_index);
- if (is_js_to_wasm) {
- DCHECK_EQ(func_to_return_idx, called_func_index);
- } else {
- DCHECK_EQ(non_compiled_functions[idx].offset,
- it.rinfo()->pc() - caller->instruction_start());
- }
- ++idx;
Handle<Code> callee_compiled(
Code::cast(compiled_module->code_table()->get(called_func_index)));
if (callee_compiled->builtin_index() == Builtins::kWasmCompileLazy) {
@@ -888,14 +1157,160 @@ Handle<Code> LazyCompilationOrchestrator::CompileLazy(
DCHECK_EQ(Code::WASM_FUNCTION, callee_compiled->kind());
it.rinfo()->set_target_address(isolate,
callee_compiled->instruction_start());
+ ++patched;
}
DCHECK_EQ(non_compiled_functions.size(), idx);
+ TRACE_LAZY("Patched %d location(s) in the caller.\n", patched);
+ DCHECK_LT(0, patched);
+ USE(patched);
+ }
+
+ return compiled_function;
+}
+
+const wasm::WasmCode* LazyCompilationOrchestrator::CompileFromJsToWasm(
+ Isolate* isolate, Handle<WasmInstanceObject> instance,
+ Handle<Code> js_to_wasm_caller, uint32_t exported_func_index) {
+ Decoder decoder(nullptr, nullptr);
+ Handle<WasmCompiledModule> compiled_module(instance->compiled_module(),
+ isolate);
+
+ TRACE_LAZY(
+ "Starting lazy compilation (func %u, js_to_wasm: true, patch caller: "
+ "true). \n",
+ exported_func_index);
+ CompileFunction(isolate, instance, exported_func_index);
+ {
+ DisallowHeapAllocation no_gc;
+ int idx = 0;
+ for (RelocIterator it(*js_to_wasm_caller,
+ RelocInfo::ModeMask(RelocInfo::JS_TO_WASM_CALL));
+ !it.done(); it.next()) {
+ ++idx;
+ const wasm::WasmCode* callee_compiled =
+ compiled_module->GetNativeModule()->GetCode(exported_func_index);
+ DCHECK_NOT_NULL(callee_compiled);
+ it.rinfo()->set_js_to_wasm_address(
+ isolate, callee_compiled->instructions().start());
+ }
+ DCHECK_EQ(1, idx);
+ }
+
+ wasm::WasmCode* ret =
+ compiled_module->GetNativeModule()->GetCode(exported_func_index);
+ DCHECK_NOT_NULL(ret);
+ DCHECK_EQ(wasm::WasmCode::Function, ret->kind());
+ return ret;
+}
+
+const wasm::WasmCode* LazyCompilationOrchestrator::CompileIndirectCall(
+ Isolate* isolate, Handle<WasmInstanceObject> instance,
+ uint32_t func_index) {
+ TRACE_LAZY(
+ "Starting lazy compilation (func %u, js_to_wasm: false, patch caller: "
+ "false). \n",
+ func_index);
+ return CompileFunction(isolate, instance, func_index);
+}
+
+const wasm::WasmCode* LazyCompilationOrchestrator::CompileDirectCall(
+ Isolate* isolate, Handle<WasmInstanceObject> instance,
+ Maybe<uint32_t> maybe_func_to_return_idx, const wasm::WasmCode* wasm_caller,
+ int call_offset) {
+ struct WasmDirectCallData {
+ uint32_t offset = 0;
+ uint32_t func_index = 0;
+ };
+ std::vector<Maybe<WasmDirectCallData>> non_compiled_functions;
+ Decoder decoder(nullptr, nullptr);
+ {
+ DisallowHeapAllocation no_gc;
+ Handle<WasmCompiledModule> caller_module(
+ wasm_caller->owner()->compiled_module(), isolate);
+ SeqOneByteString* module_bytes = caller_module->module_bytes();
+ uint32_t caller_func_index = wasm_caller->index();
+ SourcePositionTableIterator source_pos_iterator(
+ Handle<ByteArray>(ByteArray::cast(
+ caller_module->source_positions()->get(caller_func_index))));
+
+ const byte* func_bytes =
+ module_bytes->GetChars() +
+ caller_module->module()->functions[caller_func_index].code.offset();
+ for (RelocIterator it(wasm_caller->instructions(),
+ wasm_caller->reloc_info(),
+ wasm_caller->constant_pool(),
+ RelocInfo::ModeMask(RelocInfo::WASM_CALL));
+ !it.done(); it.next()) {
+ const WasmCode* callee = isolate->wasm_code_manager()->LookupCode(
+ it.rinfo()->target_address());
+ if (callee->kind() != WasmCode::LazyStub) {
+ non_compiled_functions.push_back(Nothing<WasmDirectCallData>());
+ continue;
+ }
+ // TODO(clemensh): Introduce safe_cast<T, bool> which (D)CHECKS
+ // (depending on the bool) against limits of T and then static_casts.
+ size_t offset_l = it.rinfo()->pc() - wasm_caller->instructions().start();
+ DCHECK_GE(kMaxInt, offset_l);
+ int offset = static_cast<int>(offset_l);
+ int byte_pos =
+ AdvanceSourcePositionTableIterator(source_pos_iterator, offset);
+ uint32_t called_func_index =
+ ExtractDirectCallIndex(decoder, func_bytes + byte_pos);
+ DCHECK_LT(called_func_index,
+ caller_module->GetNativeModule()->FunctionCount());
+ WasmDirectCallData data;
+ data.offset = offset;
+ data.func_index = called_func_index;
+ non_compiled_functions.push_back(Just<WasmDirectCallData>(data));
+ // Call offset one instruction after the call. Remember the last called
+ // function before that offset.
+ if (offset < call_offset) {
+ maybe_func_to_return_idx = Just(called_func_index);
+ }
+ }
}
+ uint32_t func_to_return_idx = maybe_func_to_return_idx.ToChecked();
- Code* ret =
- Code::cast(compiled_module->code_table()->get(func_to_return_idx));
- DCHECK_EQ(Code::WASM_FUNCTION, ret->kind());
- return handle(ret, isolate);
+ TRACE_LAZY(
+ "Starting lazy compilation (func %u @%d, js_to_wasm: false, patch "
+ "caller: true). \n",
+ func_to_return_idx, call_offset);
+
+ // TODO(clemensh): compile all functions in non_compiled_functions in
+ // background, wait for func_to_return_idx.
+ CompileFunction(isolate, instance, func_to_return_idx);
+
+ Handle<WasmCompiledModule> compiled_module(instance->compiled_module(),
+ isolate);
+ WasmCode* ret =
+ compiled_module->GetNativeModule()->GetCode(func_to_return_idx);
+
+ DCHECK_NOT_NULL(ret);
+ {
+ DisallowHeapAllocation no_gc;
+ // Now patch the code object with all functions which are now compiled. This
+ // will pick up any other compiled functions, not only {ret}.
+ size_t idx = 0;
+ size_t patched = 0;
+ for (RelocIterator
+ it(wasm_caller->instructions(), wasm_caller->reloc_info(),
+ wasm_caller->constant_pool(),
+ RelocInfo::ModeMask(RelocInfo::WASM_CALL));
+ !it.done(); it.next(), ++idx) {
+ auto& info = non_compiled_functions[idx];
+ if (info.IsNothing()) continue;
+ uint32_t lookup = info.ToChecked().func_index;
+ const WasmCode* callee_compiled =
+ compiled_module->GetNativeModule()->GetCode(lookup);
+ if (callee_compiled->kind() != WasmCode::Function) continue;
+ it.rinfo()->set_wasm_call_address(
+ isolate, callee_compiled->instructions().start());
+ ++patched;
+ }
+ DCHECK_EQ(non_compiled_functions.size(), idx);
+ TRACE_LAZY("Patched %zu location(s) in the caller.\n", patched);
+ }
+ return ret;
}
ModuleCompiler::CodeGenerationSchedule::CodeGenerationSchedule(
@@ -942,7 +1357,8 @@ size_t ModuleCompiler::CodeGenerationSchedule::GetRandomIndexInSchedule() {
}
ModuleCompiler::ModuleCompiler(Isolate* isolate, WasmModule* module,
- Handle<Code> centry_stub)
+ Handle<Code> centry_stub,
+ wasm::NativeModule* native_module)
: isolate_(isolate),
module_(module),
async_counters_(isolate->async_counters()),
@@ -956,7 +1372,8 @@ ModuleCompiler::ModuleCompiler(Isolate* isolate, WasmModule* module,
Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks),
V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads())),
stopped_compilation_tasks_(num_background_tasks_),
- centry_stub_(centry_stub) {}
+ centry_stub_(centry_stub),
+ native_module_(native_module) {}
// The actual runnable task that performs compilations in the background.
void ModuleCompiler::OnBackgroundTaskStopped() {
@@ -965,7 +1382,7 @@ void ModuleCompiler::OnBackgroundTaskStopped() {
DCHECK_LE(stopped_compilation_tasks_, num_background_tasks_);
}
-// Run by each compilation task The no_finisher_callback is called
+// Run by each compilation task. The no_finisher_callback is called
// within the result_mutex_ lock when no finishing task is running,
// i.e. when the finisher_is_running_ flag is not set.
bool ModuleCompiler::FetchAndExecuteCompilationUnit(
@@ -1009,18 +1426,22 @@ size_t ModuleCompiler::InitializeCompilationUnits(
Vector<const uint8_t> bytes(wire_bytes.start() + func->code.offset(),
func->code.end_offset() - func->code.offset());
WasmName name = wire_bytes.GetName(func);
- builder.AddUnit(module_env, func, buffer_offset, bytes, name);
+ DCHECK_IMPLIES(FLAG_wasm_jit_to_native, native_module_ != nullptr);
+ builder.AddUnit(module_env, native_module_, func, buffer_offset, bytes,
+ name);
}
builder.Commit();
return funcs_to_compile;
}
void ModuleCompiler::RestartCompilationTasks() {
+ v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
+ std::shared_ptr<v8::TaskRunner> task_runner =
+ V8::GetCurrentPlatform()->GetBackgroundTaskRunner(v8_isolate);
+
base::LockGuard<base::Mutex> guard(&tasks_mutex_);
for (; stopped_compilation_tasks_ > 0; --stopped_compilation_tasks_) {
- V8::GetCurrentPlatform()->CallOnBackgroundThread(
- new CompilationTask(this),
- v8::Platform::ExpectedRuntime::kShortRunningTask);
+ task_runner->PostTask(base::make_unique<CompilationTask>(this));
}
}
@@ -1029,12 +1450,14 @@ size_t ModuleCompiler::FinishCompilationUnits(
size_t finished = 0;
while (true) {
int func_index = -1;
- MaybeHandle<Code> result = FinishCompilationUnit(thrower, &func_index);
+ WasmCodeWrapper result = FinishCompilationUnit(thrower, &func_index);
if (func_index < 0) break;
++finished;
DCHECK_IMPLIES(result.is_null(), thrower->error());
if (result.is_null()) break;
- results[func_index] = result.ToHandleChecked();
+ if (result.IsCodeObject()) {
+ results[func_index] = result.GetCode();
+ }
}
bool do_restart;
{
@@ -1050,8 +1473,8 @@ void ModuleCompiler::SetFinisherIsRunning(bool value) {
finisher_is_running_ = value;
}
-MaybeHandle<Code> ModuleCompiler::FinishCompilationUnit(ErrorThrower* thrower,
- int* func_index) {
+WasmCodeWrapper ModuleCompiler::FinishCompilationUnit(ErrorThrower* thrower,
+ int* func_index) {
std::unique_ptr<compiler::WasmCompilationUnit> unit;
{
base::LockGuard<base::Mutex> guard(&result_mutex_);
@@ -1132,15 +1555,17 @@ void ModuleCompiler::CompileSequentially(const ModuleWireBytes& wire_bytes,
if (func.imported) continue; // Imports are compiled at instantiation time.
// Compile the function.
- MaybeHandle<Code> code = compiler::WasmCompilationUnit::CompileWasmFunction(
- thrower, isolate_, wire_bytes, module_env, &func);
+ WasmCodeWrapper code = compiler::WasmCompilationUnit::CompileWasmFunction(
+ native_module_, thrower, isolate_, wire_bytes, module_env, &func);
if (code.is_null()) {
TruncatedUserString<> name(wire_bytes.GetName(&func));
thrower->CompileError("Compilation of #%d:%.*s failed.", i, name.length(),
name.start());
break;
}
- results[i] = code.ToHandleChecked();
+ if (code.IsCodeObject()) {
+ results[i] = code.GetCode();
+ }
}
}
@@ -1175,7 +1600,13 @@ MaybeHandle<WasmModuleObject> ModuleCompiler::CompileToModuleObject(
const ModuleWireBytes& wire_bytes, Handle<Script> asm_js_script,
Vector<const byte> asm_js_offset_table_bytes) {
Handle<Code> centry_stub = CEntryStub(isolate, 1).GetCode();
- ModuleCompiler compiler(isolate, module.get(), centry_stub);
+ // TODO(mtrofin): the wasm::NativeModule parameter to the ModuleCompiler
+ // constructor is null here, and initialized in CompileToModuleObjectInternal.
+ // This is a point-in-time, until we remove the FLAG_wasm_jit_to_native flag,
+ // and stop needing a FixedArray for code for the non-native case. Otherwise,
+ // we end up moving quite a bit of initialization logic here that is also
+ // needed in CompileToModuleObjectInternal, complicating the change.
+ ModuleCompiler compiler(isolate, module.get(), centry_stub, nullptr);
return compiler.CompileToModuleObjectInternal(thrower, std::move(module),
wire_bytes, asm_js_script,
asm_js_offset_table_bytes);
@@ -1187,9 +1618,19 @@ bool compile_lazy(const WasmModule* module) {
(FLAG_asm_wasm_lazy_compilation && module->is_asm_js());
}
-void FlushICache(Isolate* isolate, Handle<FixedArray> code_table) {
- for (int i = 0; i < code_table->length(); ++i) {
- Handle<Code> code = code_table->GetValueChecked<Code>(isolate, i);
+void FlushICache(Isolate* isolate, const wasm::NativeModule* native_module) {
+ for (uint32_t i = 0, e = native_module->FunctionCount(); i < e; ++i) {
+ const wasm::WasmCode* code = native_module->GetCode(i);
+ if (code == nullptr) continue;
+ Assembler::FlushICache(isolate, code->instructions().start(),
+ code->instructions().size());
+ }
+}
+
+void FlushICache(Isolate* isolate, Handle<FixedArray> functions) {
+ for (int i = 0, e = functions->length(); i < e; ++i) {
+ if (!functions->get(i)->IsCode()) continue;
+ Code* code = Code::cast(functions->get(i));
Assembler::FlushICache(isolate, code->instruction_start(),
code->instruction_size());
}
@@ -1199,11 +1640,26 @@ byte* raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer, int offset) {
return static_cast<byte*>(buffer.ToHandleChecked()->backing_store()) + offset;
}
-void RecordStats(Code* code, Counters* counters) {
+void RecordStats(const Code* code, Counters* counters) {
counters->wasm_generated_code_size()->Increment(code->body_size());
counters->wasm_reloc_size()->Increment(code->relocation_info()->length());
}
+void RecordStats(const wasm::WasmCode* code, Counters* counters) {
+ counters->wasm_generated_code_size()->Increment(
+ static_cast<int>(code->instructions().size()));
+ counters->wasm_reloc_size()->Increment(
+ static_cast<int>(code->reloc_info().size()));
+}
+
+void RecordStats(WasmCodeWrapper wrapper, Counters* counters) {
+ if (wrapper.IsCodeObject()) {
+ RecordStats(*wrapper.GetCode(), counters);
+ } else {
+ RecordStats(wrapper.GetWasmCode(), counters);
+ }
+}
+
void RecordStats(Handle<FixedArray> functions, Counters* counters) {
DisallowHeapAllocation no_gc;
for (int i = 0; i < functions->length(); ++i) {
@@ -1211,123 +1667,163 @@ void RecordStats(Handle<FixedArray> functions, Counters* counters) {
if (val->IsCode()) RecordStats(Code::cast(val), counters);
}
}
-Handle<Script> CreateWasmScript(Isolate* isolate,
- const ModuleWireBytes& wire_bytes) {
- Handle<Script> script =
- isolate->factory()->NewScript(isolate->factory()->empty_string());
- script->set_context_data(isolate->native_context()->debug_context_id());
- script->set_type(Script::TYPE_WASM);
-
- int hash = StringHasher::HashSequentialString(
- reinterpret_cast<const char*>(wire_bytes.start()),
- static_cast<int>(wire_bytes.length()), kZeroHashSeed);
- const int kBufferSize = 32;
- char buffer[kBufferSize];
- int url_chars = SNPrintF(ArrayVector(buffer), "wasm://wasm/%08x", hash);
- DCHECK(url_chars >= 0 && url_chars < kBufferSize);
- MaybeHandle<String> url_str = isolate->factory()->NewStringFromOneByte(
- Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), url_chars),
- TENURED);
- script->set_source_url(*url_str.ToHandleChecked());
-
- int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash);
- DCHECK(name_chars >= 0 && name_chars < kBufferSize);
- MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte(
- Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), name_chars),
- TENURED);
- script->set_name(*name_str.ToHandleChecked());
-
- return script;
+void RecordStats(const wasm::NativeModule* native_module, Counters* counters) {
+ for (uint32_t i = 0, e = native_module->FunctionCount(); i < e; ++i) {
+ const wasm::WasmCode* code = native_module->GetCode(i);
+ if (code != nullptr) RecordStats(code, counters);
+ }
}
// Ensure that the code object in <code_table> at offset <func_index> has
// deoptimization data attached. This is needed for lazy compile stubs which are
// called from JS_TO_WASM functions or via exported function tables. The deopt
// data is used to determine which function this lazy compile stub belongs to.
-Handle<Code> EnsureExportedLazyDeoptData(Isolate* isolate,
- Handle<WasmInstanceObject> instance,
- Handle<FixedArray> code_table,
- int func_index) {
- Handle<Code> code(Code::cast(code_table->get(func_index)), isolate);
- if (code->builtin_index() != Builtins::kWasmCompileLazy) {
- // No special deopt data needed for compiled functions, and imported
- // functions, which map to Illegal at this point (they get compiled at
- // instantiation time).
- DCHECK(code->kind() == Code::WASM_FUNCTION ||
- code->kind() == Code::WASM_TO_JS_FUNCTION ||
- code->builtin_index() == Builtins::kIllegal);
- return code;
+// TODO(mtrofin): remove the instance and code_table members once we remove the
+// FLAG_wasm_jit_to_native
+WasmCodeWrapper EnsureExportedLazyDeoptData(Isolate* isolate,
+ Handle<WasmInstanceObject> instance,
+ Handle<FixedArray> code_table,
+ wasm::NativeModule* native_module,
+ uint32_t func_index) {
+ if (!FLAG_wasm_jit_to_native) {
+ Handle<Code> code(Code::cast(code_table->get(func_index)), isolate);
+ if (code->builtin_index() != Builtins::kWasmCompileLazy) {
+ // No special deopt data needed for compiled functions, and imported
+ // functions, which map to Illegal at this point (they get compiled at
+ // instantiation time).
+ DCHECK(code->kind() == Code::WASM_FUNCTION ||
+ code->kind() == Code::WASM_TO_JS_FUNCTION ||
+ code->kind() == Code::WASM_TO_WASM_FUNCTION ||
+ code->builtin_index() == Builtins::kIllegal);
+ return WasmCodeWrapper(code);
+ }
+
+ // deopt_data:
+ // #0: weak instance
+ // #1: func_index
+ // might be extended later for table exports (see
+ // EnsureTableExportLazyDeoptData).
+ Handle<FixedArray> deopt_data(code->deoptimization_data());
+ DCHECK_EQ(0, deopt_data->length() % 2);
+ if (deopt_data->length() == 0) {
+ code = isolate->factory()->CopyCode(code);
+ code_table->set(func_index, *code);
+ AttachWasmFunctionInfo(isolate, code, instance, func_index);
+ }
+#ifdef DEBUG
+ auto func_info = GetWasmFunctionInfo(isolate, code);
+ DCHECK_IMPLIES(!instance.is_null(),
+ *func_info.instance.ToHandleChecked() == *instance);
+ DCHECK_EQ(func_index, func_info.func_index);
+#endif
+ return WasmCodeWrapper(code);
+ } else {
+ wasm::WasmCode* code = native_module->GetCode(func_index);
+ // {code} will be nullptr when exporting imports.
+ if (code == nullptr || code->kind() != wasm::WasmCode::LazyStub ||
+ !code->IsAnonymous()) {
+ return WasmCodeWrapper(code);
+ }
+ // Clone the lazy builtin into the native module.
+ return WasmCodeWrapper(native_module->CloneLazyBuiltinInto(func_index));
}
- // deopt_data:
- // #0: weak instance
- // #1: func_index
- // might be extended later for table exports (see
- // EnsureTableExportLazyDeoptData).
- Handle<FixedArray> deopt_data(code->deoptimization_data());
- DCHECK_EQ(0, deopt_data->length() % 2);
- if (deopt_data->length() == 0) {
- code = isolate->factory()->CopyCode(code);
- code_table->set(func_index, *code);
- deopt_data = isolate->factory()->NewFixedArray(2, TENURED);
- code->set_deoptimization_data(*deopt_data);
- if (!instance.is_null()) {
- Handle<WeakCell> weak_instance =
- isolate->factory()->NewWeakCell(instance);
- deopt_data->set(0, *weak_instance);
- }
- deopt_data->set(1, Smi::FromInt(func_index));
- }
- DCHECK_IMPLIES(!instance.is_null(),
- WeakCell::cast(code->deoptimization_data()->get(0))->value() ==
- *instance);
- DCHECK_EQ(func_index, Smi::ToInt(code->deoptimization_data()->get(1)));
- return code;
}
// Ensure that the code object in <code_table> at offset <func_index> has
// deoptimization data attached. This is needed for lazy compile stubs which are
// called from JS_TO_WASM functions or via exported function tables. The deopt
// data is used to determine which function this lazy compile stub belongs to.
-Handle<Code> EnsureTableExportLazyDeoptData(
+// TODO(mtrofin): remove the instance and code_table members once we remove the
+// FLAG_wasm_jit_to_native
+WasmCodeWrapper EnsureTableExportLazyDeoptData(
Isolate* isolate, Handle<WasmInstanceObject> instance,
- Handle<FixedArray> code_table, int func_index,
- Handle<FixedArray> export_table, int export_index,
- std::unordered_map<uint32_t, uint32_t>& table_export_count) {
- Handle<Code> code =
- EnsureExportedLazyDeoptData(isolate, instance, code_table, func_index);
- if (code->builtin_index() != Builtins::kWasmCompileLazy) return code;
-
- // deopt_data:
- // #0: weak instance
- // #1: func_index
- // [#2: export table
- // #3: export table index]
- // [#4: export table
- // #5: export table index]
- // ...
- // table_export_count counts down and determines the index for the new export
- // table entry.
- auto table_export_entry = table_export_count.find(func_index);
- DCHECK(table_export_entry != table_export_count.end());
- DCHECK_LT(0, table_export_entry->second);
- uint32_t this_idx = 2 * table_export_entry->second;
- --table_export_entry->second;
- Handle<FixedArray> deopt_data(code->deoptimization_data());
- DCHECK_EQ(0, deopt_data->length() % 2);
- if (deopt_data->length() == 2) {
- // Then only the "header" (#0 and #1) exists. Extend for the export table
- // entries (make space for this_idx + 2 elements).
- deopt_data = isolate->factory()->CopyFixedArrayAndGrow(deopt_data, this_idx,
- TENURED);
- code->set_deoptimization_data(*deopt_data);
- }
- DCHECK_LE(this_idx + 2, deopt_data->length());
- DCHECK(deopt_data->get(this_idx)->IsUndefined(isolate));
- DCHECK(deopt_data->get(this_idx + 1)->IsUndefined(isolate));
- deopt_data->set(this_idx, *export_table);
- deopt_data->set(this_idx + 1, Smi::FromInt(export_index));
- return code;
+ Handle<FixedArray> code_table, wasm::NativeModule* native_module,
+ uint32_t func_index, Handle<FixedArray> export_table, int export_index,
+ std::unordered_map<uint32_t, uint32_t>* table_export_count) {
+ if (!FLAG_wasm_jit_to_native) {
+ Handle<Code> code =
+ EnsureExportedLazyDeoptData(isolate, instance, code_table,
+ native_module, func_index)
+ .GetCode();
+ if (code->builtin_index() != Builtins::kWasmCompileLazy)
+ return WasmCodeWrapper(code);
+
+ // TODO(6792): No longer needed once WebAssembly code is off heap.
+ CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
+
+ // deopt_data:
+ // #0: weak instance
+ // #1: func_index
+ // [#2: export table
+ // #3: export table index]
+ // [#4: export table
+ // #5: export table index]
+ // ...
+ // table_export_count counts down and determines the index for the new
+ // export table entry.
+ auto table_export_entry = table_export_count->find(func_index);
+ DCHECK(table_export_entry != table_export_count->end());
+ DCHECK_LT(0, table_export_entry->second);
+ uint32_t this_idx = 2 * table_export_entry->second;
+ --table_export_entry->second;
+ Handle<FixedArray> deopt_data(code->deoptimization_data());
+ DCHECK_EQ(0, deopt_data->length() % 2);
+ if (deopt_data->length() == 2) {
+ // Then only the "header" (#0 and #1) exists. Extend for the export table
+ // entries (make space for this_idx + 2 elements).
+ deopt_data = isolate->factory()->CopyFixedArrayAndGrow(deopt_data,
+ this_idx, TENURED);
+ code->set_deoptimization_data(*deopt_data);
+ }
+ DCHECK_LE(this_idx + 2, deopt_data->length());
+ DCHECK(deopt_data->get(this_idx)->IsUndefined(isolate));
+ DCHECK(deopt_data->get(this_idx + 1)->IsUndefined(isolate));
+ deopt_data->set(this_idx, *export_table);
+ deopt_data->set(this_idx + 1, Smi::FromInt(export_index));
+ return WasmCodeWrapper(code);
+ } else {
+ const wasm::WasmCode* code =
+ EnsureExportedLazyDeoptData(isolate, instance, code_table,
+ native_module, func_index)
+ .GetWasmCode();
+ if (code == nullptr || code->kind() != wasm::WasmCode::LazyStub)
+ return WasmCodeWrapper(code);
+
+ // deopt_data:
+ // [#0: export table
+ // #1: export table index]
+ // [#2: export table
+ // #3: export table index]
+ // ...
+ // table_export_count counts down and determines the index for the new
+ // export table entry.
+ auto table_export_entry = table_export_count->find(func_index);
+ DCHECK(table_export_entry != table_export_count->end());
+ DCHECK_LT(0, table_export_entry->second);
+ --table_export_entry->second;
+ uint32_t this_idx = 2 * table_export_entry->second;
+ int int_func_index = static_cast<int>(func_index);
+ Object* deopt_entry =
+ native_module->compiled_module()->lazy_compile_data()->get(
+ int_func_index);
+ FixedArray* deopt_data = nullptr;
+ if (!deopt_entry->IsFixedArray()) {
+ // we count indices down, so we enter here first for the
+ // largest index.
+ deopt_data = *isolate->factory()->NewFixedArray(this_idx + 2, TENURED);
+ native_module->compiled_module()->lazy_compile_data()->set(int_func_index,
+ deopt_data);
+ } else {
+ deopt_data = FixedArray::cast(deopt_entry);
+ DCHECK_LE(this_idx + 2, deopt_data->length());
+ }
+ DCHECK(deopt_data->get(this_idx)->IsUndefined(isolate));
+ DCHECK(deopt_data->get(this_idx + 1)->IsUndefined(isolate));
+ deopt_data->set(this_idx, *export_table);
+ deopt_data->set(this_idx + 1, Smi::FromInt(export_index));
+ return WasmCodeWrapper(code);
+ }
}
bool in_bounds(uint32_t offset, uint32_t size, uint32_t upper) {
@@ -1337,47 +1833,66 @@ bool in_bounds(uint32_t offset, uint32_t size, uint32_t upper) {
using WasmInstanceMap =
IdentityMap<Handle<WasmInstanceObject>, FreeStoreAllocationPolicy>;
-Handle<Code> UnwrapExportOrCompileImportWrapper(
- Isolate* isolate, int index, FunctionSig* sig, Handle<JSReceiver> target,
- ModuleOrigin origin, WasmInstanceMap* imported_instances,
- Handle<FixedArray> js_imports_table, Handle<WasmInstanceObject> instance) {
- WasmFunction* other_func = GetWasmFunctionForExport(isolate, target);
- if (other_func) {
- if (!sig->Equals(other_func->sig)) return Handle<Code>::null();
- // Signature matched. Unwrap the import wrapper and return the raw wasm
- // function code.
- // Remember the wasm instance of the import. We have to keep it alive.
- Handle<WasmInstanceObject> imported_instance(
- Handle<WasmExportedFunction>::cast(target)->instance(), isolate);
- imported_instances->Set(imported_instance, imported_instance);
- Handle<Code> wasm_code =
- UnwrapExportWrapper(Handle<JSFunction>::cast(target));
- // Create a WasmToWasm wrapper to replace the current wasm context with
- // the imported_instance one, in order to access the right memory.
- // If the imported instance does not have memory, avoid the wrapper.
- // TODO(wasm): Avoid the wrapper also if instance memory and imported
- // instance share the same memory object.
- bool needs_wasm_to_wasm_wrapper = imported_instance->has_memory_object();
- if (!needs_wasm_to_wasm_wrapper) return wasm_code;
- Address new_wasm_context =
- reinterpret_cast<Address>(imported_instance->wasm_context());
+WasmCodeWrapper MakeWasmToWasmWrapper(
+ Isolate* isolate, Handle<WasmExportedFunction> imported_function,
+ FunctionSig* expected_sig, FunctionSig** sig,
+ WasmInstanceMap* imported_instances, Handle<WasmInstanceObject> instance,
+ uint32_t index) {
+ // TODO(wasm): cache WASM-to-WASM wrappers by signature and clone+patch.
+ Handle<WasmInstanceObject> imported_instance(imported_function->instance(),
+ isolate);
+ imported_instances->Set(imported_instance, imported_instance);
+ WasmContext* new_wasm_context = imported_instance->wasm_context()->get();
+ Address new_wasm_context_address =
+ reinterpret_cast<Address>(new_wasm_context);
+ *sig = imported_instance->module()
+ ->functions[imported_function->function_index()]
+ .sig;
+ if (expected_sig && !expected_sig->Equals(*sig)) return {};
+
+ if (!FLAG_wasm_jit_to_native) {
Handle<Code> wrapper_code = compiler::CompileWasmToWasmWrapper(
- isolate, wasm_code, sig, index, new_wasm_context);
- // Set the deoptimization data for the WasmToWasm wrapper.
- // TODO(wasm): Remove the deoptimization data when we will use tail calls
- // for WasmToWasm wrappers.
- Factory* factory = isolate->factory();
- Handle<WeakCell> weak_link = factory->NewWeakCell(instance);
- Handle<FixedArray> deopt_data = factory->NewFixedArray(2, TENURED);
- deopt_data->set(0, *weak_link);
- deopt_data->set(1, Smi::FromInt(index));
- wrapper_code->set_deoptimization_data(*deopt_data);
- return wrapper_code;
+ isolate, imported_function->GetWasmCode(), *sig,
+ new_wasm_context_address);
+ // Set the deoptimization data for the WasmToWasm wrapper. This is
+ // needed by the interpreter to find the imported instance for
+ // a cross-instance call.
+ AttachWasmFunctionInfo(isolate, wrapper_code, imported_instance,
+ imported_function->function_index());
+ return WasmCodeWrapper(wrapper_code);
+ } else {
+ Handle<Code> code = compiler::CompileWasmToWasmWrapper(
+ isolate, imported_function->GetWasmCode(), *sig,
+ new_wasm_context_address);
+ return WasmCodeWrapper(
+ instance->compiled_module()->GetNativeModule()->AddCodeCopy(
+ code, wasm::WasmCode::WasmToWasmWrapper, index));
+ }
+}
+
+WasmCodeWrapper UnwrapExportOrCompileImportWrapper(
+ Isolate* isolate, FunctionSig* sig, Handle<JSReceiver> target,
+ uint32_t import_index, ModuleOrigin origin,
+ WasmInstanceMap* imported_instances, Handle<FixedArray> js_imports_table,
+ Handle<WasmInstanceObject> instance) {
+ if (WasmExportedFunction::IsWasmExportedFunction(*target)) {
+ FunctionSig* unused = nullptr;
+ return MakeWasmToWasmWrapper(
+ isolate, Handle<WasmExportedFunction>::cast(target), sig, &unused,
+ imported_instances, instance, import_index);
}
// No wasm function or being debugged. Compile a new wrapper for the new
// signature.
- return compiler::CompileWasmToJSWrapper(isolate, target, sig, index, origin,
- js_imports_table);
+ if (FLAG_wasm_jit_to_native) {
+ Handle<Code> temp_code = compiler::CompileWasmToJSWrapper(
+ isolate, target, sig, import_index, origin, js_imports_table);
+ return WasmCodeWrapper(
+ instance->compiled_module()->GetNativeModule()->AddCodeCopy(
+ temp_code, wasm::WasmCode::WasmToJsWrapper, import_index));
+ } else {
+ return WasmCodeWrapper(compiler::CompileWasmToJSWrapper(
+ isolate, target, sig, import_index, origin, js_imports_table));
+ }
}
double MonotonicallyIncreasingTimeInMs() {
@@ -1394,7 +1909,6 @@ std::unique_ptr<compiler::ModuleEnv> CreateDefaultModuleEnv(
Isolate* isolate, WasmModule* module, Handle<Code> illegal_builtin) {
std::vector<GlobalHandleAddress> function_tables;
std::vector<GlobalHandleAddress> signature_tables;
- std::vector<SignatureMap*> signature_maps;
for (size_t i = 0; i < module->function_tables.size(); i++) {
Handle<Object> func_table =
@@ -1409,7 +1923,6 @@ std::unique_ptr<compiler::ModuleEnv> CreateDefaultModuleEnv(
v8::WeakCallbackType::kFinalizer);
function_tables.push_back(func_table.address());
signature_tables.push_back(sig_table.address());
- signature_maps.push_back(&module->function_tables[i].map);
}
std::vector<Handle<Code>> empty_code;
@@ -1418,20 +1931,20 @@ std::unique_ptr<compiler::ModuleEnv> CreateDefaultModuleEnv(
module, // --
function_tables, // --
signature_tables, // --
- signature_maps, // --
empty_code, // --
- illegal_builtin, // --
- 0 // --
+ illegal_builtin // --
};
return std::unique_ptr<compiler::ModuleEnv>(new compiler::ModuleEnv(result));
}
-Handle<WasmCompiledModule> NewCompiledModule(
- Isolate* isolate, Handle<WasmSharedModuleData> shared,
- Handle<FixedArray> code_table, Handle<FixedArray> export_wrappers,
- compiler::ModuleEnv* env) {
+// TODO(mtrofin): remove code_table when we don't need FLAG_wasm_jit_to_native
+Handle<WasmCompiledModule> NewCompiledModule(Isolate* isolate,
+ WasmModule* module,
+ Handle<FixedArray> code_table,
+ Handle<FixedArray> export_wrappers,
+ compiler::ModuleEnv* env) {
Handle<WasmCompiledModule> compiled_module =
- WasmCompiledModule::New(isolate, shared, code_table, export_wrappers,
+ WasmCompiledModule::New(isolate, module, code_table, export_wrappers,
env->function_tables, env->signature_tables);
return compiled_module;
}
@@ -1453,7 +1966,11 @@ MaybeHandle<WasmModuleObject> ModuleCompiler::CompileToModuleObjectInternal(
TimedHistogramScope wasm_compile_module_time_scope(
module_->is_wasm() ? counters()->wasm_compile_wasm_module_time()
: counters()->wasm_compile_asm_module_time());
- // The {module> parameter is passed in to transfer ownership of the WasmModule
+ // TODO(6792): No longer needed once WebAssembly code is off heap. Use
+ // base::Optional to be able to close the scope before notifying the debugger.
+ base::Optional<CodeSpaceMemoryModificationScope> modification_scope(
+ base::in_place_t(), isolate_->heap());
+ // The {module} parameter is passed in to transfer ownership of the WasmModule
// to this function. The WasmModule itself existed already as an instance
// variable of the ModuleCompiler. We check here that the parameter and the
// instance variable actually point to the same object.
@@ -1462,69 +1979,6 @@ MaybeHandle<WasmModuleObject> ModuleCompiler::CompileToModuleObjectInternal(
bool lazy_compile = compile_lazy(module_);
Factory* factory = isolate_->factory();
-
- // If lazy compile: Initialize the code table with the lazy compile builtin.
- // Otherwise: Initialize with the illegal builtin. All call sites will be
- // patched at instantiation.
- Handle<Code> init_builtin = lazy_compile
- ? BUILTIN_CODE(isolate_, WasmCompileLazy)
- : BUILTIN_CODE(isolate_, Illegal);
-
- auto env = CreateDefaultModuleEnv(isolate_, module_, init_builtin);
-
- // The {code_table} array contains import wrappers and functions (which
- // are both included in {functions.size()}, and export wrappers).
- int code_table_size = static_cast<int>(module_->functions.size());
- int export_wrappers_size = static_cast<int>(module_->num_exported_functions);
- Handle<FixedArray> code_table =
- factory->NewFixedArray(static_cast<int>(code_table_size), TENURED);
- Handle<FixedArray> export_wrappers =
- factory->NewFixedArray(static_cast<int>(export_wrappers_size), TENURED);
- // Initialize the code table.
- for (int i = 0, e = code_table->length(); i < e; ++i) {
- code_table->set(i, *init_builtin);
- }
-
- for (int i = 0, e = export_wrappers->length(); i < e; ++i) {
- export_wrappers->set(i, *init_builtin);
- }
-
- if (!lazy_compile) {
- size_t funcs_to_compile =
- module_->functions.size() - module_->num_imported_functions;
- bool compile_parallel =
- !FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks > 0 &&
- funcs_to_compile > 1 &&
- V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads() > 0;
- // Avoid a race condition by collecting results into a second vector.
- std::vector<Handle<Code>> results(env->module->functions.size());
-
- if (compile_parallel) {
- CompileInParallel(wire_bytes, env.get(), results, thrower);
- } else {
- CompileSequentially(wire_bytes, env.get(), results, thrower);
- }
- if (thrower->error()) return {};
-
- // At this point, compilation has completed. Update the code table.
- for (size_t i =
- module_->num_imported_functions + FLAG_skip_compiling_wasm_funcs;
- i < results.size(); ++i) {
- Code* code = *results[i];
- code_table->set(static_cast<int>(i), code);
- RecordStats(code, counters());
- }
- } else if (module_->is_wasm()) {
- // Validate wasm modules for lazy compilation. Don't validate asm.js
- // modules, they are valid by construction (otherwise a CHECK will fail
- // during lazy compilation).
- // TODO(clemensh): According to the spec, we can actually skip validation
- // at module creation time, and return a function that always traps at
- // (lazy) compilation time.
- ValidateSequentially(wire_bytes, env.get(), thrower);
- }
- if (thrower->error()) return {};
-
// Create heap objects for script, module bytes and asm.js offset table to
// be stored in the shared module data.
Handle<Script> script;
@@ -1562,34 +2016,104 @@ MaybeHandle<WasmModuleObject> ModuleCompiler::CompileToModuleObjectInternal(
script, asm_js_offset_table);
if (lazy_compile) WasmSharedModuleData::PrepareForLazyCompilation(shared);
+ Handle<Code> init_builtin = lazy_compile
+ ? BUILTIN_CODE(isolate_, WasmCompileLazy)
+ : BUILTIN_CODE(isolate_, Illegal);
+
+ // TODO(mtrofin): remove code_table and code_table_size when we don't
+ // need FLAG_wasm_jit_to_native anymore. Keep export_wrappers.
+ int code_table_size = static_cast<int>(module_->functions.size());
+ int export_wrappers_size = static_cast<int>(module_->num_exported_functions);
+ Handle<FixedArray> code_table =
+ factory->NewFixedArray(static_cast<int>(code_table_size), TENURED);
+ Handle<FixedArray> export_wrappers =
+ factory->NewFixedArray(static_cast<int>(export_wrappers_size), TENURED);
+ // Initialize the code table.
+ for (int i = 0, e = code_table->length(); i < e; ++i) {
+ code_table->set(i, *init_builtin);
+ }
+
+ for (int i = 0, e = export_wrappers->length(); i < e; ++i) {
+ export_wrappers->set(i, *init_builtin);
+ }
+ auto env = CreateDefaultModuleEnv(isolate_, module_, init_builtin);
+
// Create the compiled module object and populate with compiled functions
// and information needed at instantiation time. This object needs to be
// serializable. Instantiation may occur off a deserialized version of this
// object.
Handle<WasmCompiledModule> compiled_module = NewCompiledModule(
- isolate_, shared, code_table, export_wrappers, env.get());
+ isolate_, shared->module(), code_table, export_wrappers, env.get());
+ native_module_ = compiled_module->GetNativeModule();
+ compiled_module->OnWasmModuleDecodingComplete(shared);
+ if (lazy_compile && FLAG_wasm_jit_to_native) {
+ compiled_module->set_lazy_compile_data(isolate_->factory()->NewFixedArray(
+ static_cast<int>(module_->functions.size()), TENURED));
+ }
+
+ if (!lazy_compile) {
+ size_t funcs_to_compile =
+ module_->functions.size() - module_->num_imported_functions;
+ bool compile_parallel =
+ !FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks > 0 &&
+ funcs_to_compile > 1 &&
+ V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads() > 0;
+ // Avoid a race condition by collecting results into a second vector.
+ std::vector<Handle<Code>> results(
+ FLAG_wasm_jit_to_native ? 0 : env->module->functions.size());
+
+ if (compile_parallel) {
+ CompileInParallel(wire_bytes, env.get(), results, thrower);
+ } else {
+ CompileSequentially(wire_bytes, env.get(), results, thrower);
+ }
+ if (thrower->error()) return {};
+
+ if (!FLAG_wasm_jit_to_native) {
+ // At this point, compilation has completed. Update the code table.
+ for (size_t i =
+ module_->num_imported_functions + FLAG_skip_compiling_wasm_funcs;
+ i < results.size(); ++i) {
+ Code* code = *results[i];
+ code_table->set(static_cast<int>(i), code);
+ RecordStats(code, counters());
+ }
+ } else {
+ RecordStats(native_module_, counters());
+ }
+ } else {
+ if (module_->is_wasm()) {
+ // Validate wasm modules for lazy compilation. Don't validate asm.js
+ // modules, they are valid by construction (otherwise a CHECK will fail
+ // during lazy compilation).
+ // TODO(clemensh): According to the spec, we can actually skip validation
+ // at module creation time, and return a function that always traps at
+ // (lazy) compilation time.
+ ValidateSequentially(wire_bytes, env.get(), thrower);
+ }
+ if (FLAG_wasm_jit_to_native) {
+ native_module_->SetLazyBuiltin(BUILTIN_CODE(isolate_, WasmCompileLazy));
+ }
+ }
+ if (thrower->error()) return {};
+
+ // Compile JS->wasm wrappers for exported functions.
+ CompileJsToWasmWrappers(isolate_, compiled_module, counters());
+
+ Handle<WasmModuleObject> result =
+ WasmModuleObject::New(isolate_, compiled_module);
// If we created a wasm script, finish it now and make it public to the
// debugger.
if (asm_js_script.is_null()) {
+ // Close the CodeSpaceMemoryModificationScope before calling into the
+ // debugger.
+ modification_scope.reset();
script->set_wasm_compiled_module(*compiled_module);
isolate_->debug()->OnAfterCompile(script);
}
- // Compile JS->wasm wrappers for exported functions.
- JSToWasmWrapperCache js_to_wasm_cache;
- int wrapper_index = 0;
- for (auto exp : module_->export_table) {
- if (exp.kind != kExternalFunction) continue;
- Handle<Code> wasm_code = EnsureExportedLazyDeoptData(
- isolate_, Handle<WasmInstanceObject>::null(), code_table, exp.index);
- Handle<Code> wrapper_code = js_to_wasm_cache.CloneOrCompileJSToWasmWrapper(
- isolate_, module_, wasm_code, exp.index);
- export_wrappers->set(wrapper_index, *wrapper_code);
- RecordStats(*wrapper_code, counters());
- ++wrapper_index;
- }
- return WasmModuleObject::New(isolate_, compiled_module);
+ return result;
}
InstanceBuilder::InstanceBuilder(
@@ -1621,6 +2145,11 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
SanitizeImports();
if (thrower_->error()) return {};
+ // TODO(6792): No longer needed once WebAssembly code is off heap.
+ // Use base::Optional to be able to close the scope before executing the start
+ // function.
+ base::Optional<CodeSpaceMemoryModificationScope> modification_scope(
+ base::in_place_t(), isolate_->heap());
// From here on, we expect the build pipeline to run without exiting to JS.
// Exception is when we run the startup function.
DisallowJavascriptExecution no_js(isolate_);
@@ -1633,6 +2162,8 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
//--------------------------------------------------------------------------
// Reuse the compiled module (if no owner), otherwise clone.
//--------------------------------------------------------------------------
+ // TODO(mtrofin): remove code_table and old_code_table
+ // when FLAG_wasm_jit_to_native is not needed
Handle<FixedArray> code_table;
Handle<FixedArray> wrapper_table;
// We keep around a copy of the old code table, because we'll be replacing
@@ -1640,6 +2171,11 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
// able to relocate.
Handle<FixedArray> old_code_table;
MaybeHandle<WasmInstanceObject> owner;
+ // native_module is the one we're building now, old_module
+ // is the one we clone from. They point to the same place if
+ // we don't need to clone.
+ wasm::NativeModule* native_module = nullptr;
+ wasm::NativeModule* old_module = nullptr;
TRACE("Starting new module instantiation\n");
{
@@ -1665,41 +2201,49 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
// the owner + original state used for cloning and patching
// won't be mutated by possible finalizer runs.
DCHECK(!owner.is_null());
- TRACE("Cloning from %d\n", original->instance_id());
- old_code_table = original->code_table();
- compiled_module_ = WasmCompiledModule::Clone(isolate_, original);
- code_table = compiled_module_->code_table();
- wrapper_table = compiled_module_->export_wrappers();
- // Avoid creating too many handles in the outer scope.
- HandleScope scope(isolate_);
-
- // Clone the code for wasm functions and exports.
- for (int i = 0; i < code_table->length(); ++i) {
- Handle<Code> orig_code(Code::cast(code_table->get(i)), isolate_);
- switch (orig_code->kind()) {
- case Code::WASM_TO_JS_FUNCTION:
- // Imports will be overwritten with newly compiled wrappers.
- break;
- case Code::BUILTIN:
- DCHECK_EQ(Builtins::kWasmCompileLazy, orig_code->builtin_index());
- // If this code object has deoptimization data, then we need a
- // unique copy to attach updated deoptimization data.
- if (orig_code->deoptimization_data()->length() > 0) {
+ if (FLAG_wasm_jit_to_native) {
+ TRACE("Cloning from %zu\n", original->GetNativeModule()->instance_id);
+ compiled_module_ = WasmCompiledModule::Clone(isolate_, original);
+ native_module = compiled_module_->GetNativeModule();
+ wrapper_table = compiled_module_->export_wrappers();
+ } else {
+ TRACE("Cloning from %d\n", original->instance_id());
+ old_code_table = original->code_table();
+ compiled_module_ = WasmCompiledModule::Clone(isolate_, original);
+ code_table = compiled_module_->code_table();
+ wrapper_table = compiled_module_->export_wrappers();
+ // Avoid creating too many handles in the outer scope.
+ HandleScope scope(isolate_);
+
+ // Clone the code for wasm functions and exports.
+ for (int i = 0; i < code_table->length(); ++i) {
+ Handle<Code> orig_code(Code::cast(code_table->get(i)), isolate_);
+ switch (orig_code->kind()) {
+ case Code::WASM_TO_JS_FUNCTION:
+ case Code::WASM_TO_WASM_FUNCTION:
+ // Imports will be overwritten with newly compiled wrappers.
+ break;
+ case Code::BUILTIN:
+ DCHECK_EQ(Builtins::kWasmCompileLazy, orig_code->builtin_index());
+ // If this code object has deoptimization data, then we need a
+ // unique copy to attach updated deoptimization data.
+ if (orig_code->deoptimization_data()->length() > 0) {
+ Handle<Code> code = factory->CopyCode(orig_code);
+ AttachWasmFunctionInfo(isolate_, code,
+ Handle<WasmInstanceObject>(), i);
+ code_table->set(i, *code);
+ }
+ break;
+ case Code::WASM_FUNCTION: {
Handle<Code> code = factory->CopyCode(orig_code);
- Handle<FixedArray> deopt_data =
- factory->NewFixedArray(2, TENURED);
- deopt_data->set(1, Smi::FromInt(i));
- code->set_deoptimization_data(*deopt_data);
+ AttachWasmFunctionInfo(isolate_, code,
+ Handle<WasmInstanceObject>(), i);
code_table->set(i, *code);
+ break;
}
- break;
- case Code::WASM_FUNCTION: {
- Handle<Code> code = factory->CopyCode(orig_code);
- code_table->set(i, *code);
- break;
+ default:
+ UNREACHABLE();
}
- default:
- UNREACHABLE();
}
}
for (int i = 0; i < wrapper_table->length(); ++i) {
@@ -1708,22 +2252,34 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
Handle<Code> code = factory->CopyCode(orig_code);
wrapper_table->set(i, *code);
}
-
- RecordStats(code_table, counters());
+ if (FLAG_wasm_jit_to_native) {
+ RecordStats(native_module, counters());
+ } else {
+ RecordStats(code_table, counters());
+ }
RecordStats(wrapper_table, counters());
} else {
// There was no owner, so we can reuse the original.
compiled_module_ = original;
- old_code_table = factory->CopyFixedArray(compiled_module_->code_table());
- code_table = compiled_module_->code_table();
wrapper_table = compiled_module_->export_wrappers();
- TRACE("Reusing existing instance %d\n", compiled_module_->instance_id());
+ if (FLAG_wasm_jit_to_native) {
+ old_module = compiled_module_->GetNativeModule();
+ native_module = old_module;
+ TRACE("Reusing existing instance %zu\n",
+ compiled_module_->GetNativeModule()->instance_id);
+ } else {
+ old_code_table =
+ factory->CopyFixedArray(compiled_module_->code_table());
+ code_table = compiled_module_->code_table();
+ TRACE("Reusing existing instance %d\n",
+ compiled_module_->instance_id());
+ }
}
compiled_module_->set_native_context(isolate_->native_context());
}
//--------------------------------------------------------------------------
- // Allocate the instance object.
+ // Create the WebAssembly.Instance object.
//--------------------------------------------------------------------------
Zone instantiation_zone(isolate_->allocator(), ZONE_NAME);
CodeSpecialization code_specialization(isolate_, &instantiation_zone);
@@ -1733,6 +2289,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
//--------------------------------------------------------------------------
// Set up the globals for the new instance.
//--------------------------------------------------------------------------
+ WasmContext* wasm_context = instance->wasm_context()->get();
MaybeHandle<JSArrayBuffer> old_globals;
uint32_t globals_size = module_->globals_size;
if (globals_size > 0) {
@@ -1744,22 +2301,13 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
thrower_->RangeError("Out of memory: wasm globals");
return {};
}
- Address old_globals_start = compiled_module_->GetGlobalsStartOrNull();
- Address new_globals_start =
- static_cast<Address>(global_buffer->backing_store());
- code_specialization.RelocateGlobals(old_globals_start, new_globals_start);
- // The address of the backing buffer for the golbals is in native memory
- // and, thus, not moving. We need it saved for
- // serialization/deserialization purposes - so that the other end
- // understands how to relocate the references. We still need to save the
- // JSArrayBuffer on the instance, to keep it all alive.
- WasmCompiledModule::SetGlobalsStartAddressFrom(factory, compiled_module_,
- global_buffer);
+ wasm_context->globals_start =
+ reinterpret_cast<byte*>(global_buffer->backing_store());
instance->set_globals_buffer(*global_buffer);
}
//--------------------------------------------------------------------------
- // Prepare for initialization of function tables.
+ // Reserve the metadata for indirect function tables.
//--------------------------------------------------------------------------
int function_table_count = static_cast<int>(module_->function_tables.size());
table_instances_.reserve(module_->function_tables.size());
@@ -1781,13 +2329,14 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
InitGlobals();
//--------------------------------------------------------------------------
- // Set up the indirect function tables for the new instance.
+ // Initialize the indirect tables.
//--------------------------------------------------------------------------
- if (function_table_count > 0)
+ if (function_table_count > 0) {
InitializeTables(instance, &code_specialization);
+ }
//--------------------------------------------------------------------------
- // Set up the memory for the new instance.
+ // Allocate the memory array buffer.
//--------------------------------------------------------------------------
uint32_t initial_pages = module_->initial_pages;
(module_->is_wasm() ? counters()->wasm_wasm_min_mem_pages_count()
@@ -1795,18 +2344,45 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
->AddSample(initial_pages);
if (!memory_.is_null()) {
- Handle<JSArrayBuffer> memory = memory_.ToHandleChecked();
// Set externally passed ArrayBuffer non neuterable.
+ Handle<JSArrayBuffer> memory = memory_.ToHandleChecked();
memory->set_is_neuterable(false);
DCHECK_IMPLIES(trap_handler::UseTrapHandler(),
module_->is_asm_js() || memory->has_guard_region());
} else if (initial_pages > 0) {
+ // Allocate memory if the initial size is more than 0 pages.
memory_ = AllocateMemory(initial_pages);
if (memory_.is_null()) return {}; // failed to allocate memory
}
//--------------------------------------------------------------------------
+ // Create the WebAssembly.Memory object.
+ //--------------------------------------------------------------------------
+ if (module_->has_memory) {
+ if (!instance->has_memory_object()) {
+ // No memory object exists. Create one.
+ Handle<WasmMemoryObject> memory_object = WasmMemoryObject::New(
+ isolate_, memory_,
+ module_->maximum_pages != 0 ? module_->maximum_pages : -1);
+ instance->set_memory_object(*memory_object);
+ }
+
+ // Add the instance object to the list of instances for this memory.
+ Handle<WasmMemoryObject> memory_object(instance->memory_object(), isolate_);
+ WasmMemoryObject::AddInstance(isolate_, memory_object, instance);
+
+ if (!memory_.is_null()) {
+ // Double-check the {memory} array buffer matches the context.
+ Handle<JSArrayBuffer> memory = memory_.ToHandleChecked();
+ uint32_t mem_size = 0;
+ CHECK(memory->byte_length()->ToUint32(&mem_size));
+ CHECK_EQ(wasm_context->mem_size, mem_size);
+ CHECK_EQ(wasm_context->mem_start, memory->backing_store());
+ }
+ }
+
+ //--------------------------------------------------------------------------
// Check that indirect function table segments are within bounds.
//--------------------------------------------------------------------------
for (WasmTableInit& table_init : module_->table_inits) {
@@ -1826,78 +2402,44 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
//--------------------------------------------------------------------------
for (WasmDataSegment& seg : module_->data_segments) {
uint32_t base = EvalUint32InitExpr(seg.dest_addr);
- uint32_t mem_size = 0;
- if (!memory_.is_null()) {
- CHECK(memory_.ToHandleChecked()->byte_length()->ToUint32(&mem_size));
- }
- if (!in_bounds(base, seg.source.length(), mem_size)) {
+ if (!in_bounds(base, seg.source.length(), wasm_context->mem_size)) {
thrower_->LinkError("data segment is out of bounds");
return {};
}
}
- //--------------------------------------------------------------------------
- // Initialize memory.
- //--------------------------------------------------------------------------
- Address mem_start = nullptr;
- uint32_t mem_size = 0;
- if (!memory_.is_null()) {
- Handle<JSArrayBuffer> memory = memory_.ToHandleChecked();
- mem_start = static_cast<Address>(memory->backing_store());
- CHECK(memory->byte_length()->ToUint32(&mem_size));
- LoadDataSegments(mem_start, mem_size);
- // Just like with globals, we need to keep both the JSArrayBuffer
- // and save the start pointer.
- instance->set_memory_buffer(*memory);
- }
-
- //--------------------------------------------------------------------------
- // Create a memory object to have a WasmContext.
- //--------------------------------------------------------------------------
- if (module_->has_memory) {
- if (!instance->has_memory_object()) {
- Handle<WasmMemoryObject> memory_object = WasmMemoryObject::New(
- isolate_,
- instance->has_memory_buffer() ? handle(instance->memory_buffer())
- : Handle<JSArrayBuffer>::null(),
- module_->maximum_pages != 0 ? module_->maximum_pages : -1);
- instance->set_memory_object(*memory_object);
- }
-
- code_specialization.RelocateWasmContextReferences(
- reinterpret_cast<Address>(instance->wasm_context()));
- // Store the wasm_context address in the JSToWasmWrapperCache so that it can
- // be used to compile JSToWasmWrappers.
- js_to_wasm_cache_.SetContextAddress(
- reinterpret_cast<Address>(instance->wasm_context()));
- }
+ // Set the WasmContext address in wrappers.
+ // TODO(wasm): the wasm context should only appear as a constant in wrappers;
+ // this code specialization is applied to the whole instance.
+ Address wasm_context_address = reinterpret_cast<Address>(wasm_context);
+ code_specialization.RelocateWasmContextReferences(wasm_context_address);
+ js_to_wasm_cache_.SetContextAddress(wasm_context_address);
+
+ if (!FLAG_wasm_jit_to_native) {
+ //--------------------------------------------------------------------------
+ // Set up the runtime support for the new instance.
+ //--------------------------------------------------------------------------
+ Handle<WeakCell> weak_link = factory->NewWeakCell(instance);
- //--------------------------------------------------------------------------
- // Set up the runtime support for the new instance.
- //--------------------------------------------------------------------------
- Handle<WeakCell> weak_link = factory->NewWeakCell(instance);
-
- for (int i = num_imported_functions + FLAG_skip_compiling_wasm_funcs,
- num_functions = static_cast<int>(module_->functions.size());
- i < num_functions; ++i) {
- Handle<Code> code = handle(Code::cast(code_table->get(i)), isolate_);
- if (code->kind() == Code::WASM_FUNCTION) {
- Handle<FixedArray> deopt_data = factory->NewFixedArray(2, TENURED);
- deopt_data->set(0, *weak_link);
- deopt_data->set(1, Smi::FromInt(i));
- code->set_deoptimization_data(*deopt_data);
- continue;
- }
- DCHECK_EQ(Builtins::kWasmCompileLazy, code->builtin_index());
- int deopt_len = code->deoptimization_data()->length();
- if (deopt_len == 0) continue;
- DCHECK_LE(2, deopt_len);
- DCHECK_EQ(i, Smi::ToInt(code->deoptimization_data()->get(1)));
- code->deoptimization_data()->set(0, *weak_link);
- // Entries [2, deopt_len) encode information about table exports of this
- // function. This is rebuilt in {LoadTableSegments}, so reset it here.
- for (int i = 2; i < deopt_len; ++i) {
- code->deoptimization_data()->set_undefined(isolate_, i);
+ for (int i = num_imported_functions + FLAG_skip_compiling_wasm_funcs,
+ num_functions = static_cast<int>(module_->functions.size());
+ i < num_functions; ++i) {
+ Handle<Code> code = handle(Code::cast(code_table->get(i)), isolate_);
+ if (code->kind() == Code::WASM_FUNCTION) {
+ AttachWasmFunctionInfo(isolate_, code, weak_link, i);
+ continue;
+ }
+ DCHECK_EQ(Builtins::kWasmCompileLazy, code->builtin_index());
+ int deopt_len = code->deoptimization_data()->length();
+ if (deopt_len == 0) continue;
+ DCHECK_LE(2, deopt_len);
+ DCHECK_EQ(i, Smi::ToInt(code->deoptimization_data()->get(1)));
+ code->deoptimization_data()->set(0, *weak_link);
+ // Entries [2, deopt_len) encode information about table exports of this
+ // function. This is rebuilt in {LoadTableSegments}, so reset it here.
+ for (int i = 2; i < deopt_len; ++i) {
+ code->deoptimization_data()->set_undefined(isolate_, i);
+ }
}
}
@@ -1908,67 +2450,58 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
if (thrower_->error()) return {};
//--------------------------------------------------------------------------
- // Add instance to Memory object
+ // Initialize the indirect function tables.
//--------------------------------------------------------------------------
- if (instance->has_memory_object()) {
- Handle<WasmMemoryObject> memory(instance->memory_object(), isolate_);
- WasmMemoryObject::AddInstance(isolate_, memory, instance);
+ if (function_table_count > 0) {
+ LoadTableSegments(code_table, instance);
}
//--------------------------------------------------------------------------
- // Initialize the indirect function tables.
+ // Initialize the memory by loading data segments.
//--------------------------------------------------------------------------
- if (function_table_count > 0) LoadTableSegments(code_table, instance);
+ if (module_->data_segments.size() > 0) {
+ LoadDataSegments(wasm_context);
+ }
// Patch all code with the relocations registered in code_specialization.
code_specialization.RelocateDirectCalls(instance);
code_specialization.ApplyToWholeInstance(*instance, SKIP_ICACHE_FLUSH);
- FlushICache(isolate_, code_table);
+ if (FLAG_wasm_jit_to_native) {
+ FlushICache(isolate_, native_module);
+ } else {
+ FlushICache(isolate_, code_table);
+ }
FlushICache(isolate_, wrapper_table);
//--------------------------------------------------------------------------
// Unpack and notify signal handler of protected instructions.
//--------------------------------------------------------------------------
if (trap_handler::UseTrapHandler()) {
- UnpackAndRegisterProtectedInstructions(isolate_, code_table);
+ if (FLAG_wasm_jit_to_native) {
+ UnpackAndRegisterProtectedInstructions(isolate_, native_module);
+ } else {
+ UnpackAndRegisterProtectedInstructionsGC(isolate_, code_table);
+ }
}
//--------------------------------------------------------------------------
- // Set up and link the new instance.
+ // Insert the compiled module into the weak list of compiled modules.
//--------------------------------------------------------------------------
{
Handle<Object> global_handle =
isolate_->global_handles()->Create(*instance);
- Handle<WeakCell> link_to_clone = factory->NewWeakCell(compiled_module_);
Handle<WeakCell> link_to_owning_instance = factory->NewWeakCell(instance);
- MaybeHandle<WeakCell> link_to_original;
- MaybeHandle<WasmCompiledModule> original;
if (!owner.is_null()) {
- // prepare the data needed for publishing in a chain, but don't link
- // just yet, because
- // we want all the publishing to happen free from GC interruptions, and
- // so we do it in
- // one GC-free scope afterwards.
- original = handle(owner.ToHandleChecked()->compiled_module());
- link_to_original = factory->NewWeakCell(original.ToHandleChecked());
- }
- // Publish the new instance to the instances chain.
- {
+ // Publish the new instance to the instances chain.
DisallowHeapAllocation no_gc;
- if (!link_to_original.is_null()) {
- compiled_module_->set_weak_next_instance(
- link_to_original.ToHandleChecked());
- original.ToHandleChecked()->set_weak_prev_instance(link_to_clone);
- compiled_module_->set_weak_wasm_module(
- original.ToHandleChecked()->weak_wasm_module());
- }
- module_object_->set_compiled_module(*compiled_module_);
- compiled_module_->set_weak_owning_instance(link_to_owning_instance);
- GlobalHandles::MakeWeak(
- global_handle.location(), global_handle.location(),
- instance_finalizer_callback_, v8::WeakCallbackType::kFinalizer);
+ compiled_module_->InsertInChain(*module_object_);
}
+ module_object_->set_compiled_module(*compiled_module_);
+ compiled_module_->set_weak_owning_instance(link_to_owning_instance);
+ GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(),
+ instance_finalizer_callback_,
+ v8::WeakCallbackType::kFinalizer);
}
//--------------------------------------------------------------------------
@@ -1993,22 +2526,24 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
}
//--------------------------------------------------------------------------
- // Run the start function if one was specified.
+ // Execute the start function if one was specified.
//--------------------------------------------------------------------------
if (module_->start_function_index >= 0) {
HandleScope scope(isolate_);
int start_index = module_->start_function_index;
- Handle<Code> startup_code = EnsureExportedLazyDeoptData(
- isolate_, instance, code_table, start_index);
+ WasmCodeWrapper startup_code = EnsureExportedLazyDeoptData(
+ isolate_, instance, code_table, native_module, start_index);
FunctionSig* sig = module_->functions[start_index].sig;
Handle<Code> wrapper_code = js_to_wasm_cache_.CloneOrCompileJSToWasmWrapper(
isolate_, module_, startup_code, start_index);
Handle<WasmExportedFunction> startup_fct = WasmExportedFunction::New(
isolate_, instance, MaybeHandle<String>(), start_index,
static_cast<int>(sig->parameter_count()), wrapper_code);
- RecordStats(*startup_code, counters());
+ RecordStats(startup_code, counters());
// Call the JS function.
Handle<Object> undefined = factory->undefined_value();
+ // Close the CodeSpaceMemoryModificationScope to execute the start function.
+ modification_scope.reset();
{
// We're OK with JS execution here. The instance is fully setup.
AllowJavascriptExecution allow_js(isolate_);
@@ -2027,7 +2562,12 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
}
DCHECK(!isolate_->has_pending_exception());
- TRACE("Finishing instance %d\n", compiled_module_->instance_id());
+ if (FLAG_wasm_jit_to_native) {
+ TRACE("Successfully built instance %zu\n",
+ compiled_module_->GetNativeModule()->instance_id);
+ } else {
+ TRACE("Finishing instance %d\n", compiled_module_->instance_id());
+ }
TRACE_CHAIN(module_object_->compiled_module());
return instance;
}
@@ -2117,7 +2657,7 @@ uint32_t InstanceBuilder::EvalUint32InitExpr(const WasmInitExpr& expr) {
}
// Load data segments into the memory.
-void InstanceBuilder::LoadDataSegments(Address mem_addr, size_t mem_size) {
+void InstanceBuilder::LoadDataSegments(WasmContext* wasm_context) {
Handle<SeqOneByteString> module_bytes(compiled_module_->module_bytes(),
isolate_);
for (const WasmDataSegment& segment : module_->data_segments) {
@@ -2125,9 +2665,8 @@ void InstanceBuilder::LoadDataSegments(Address mem_addr, size_t mem_size) {
// Segments of size == 0 are just nops.
if (source_size == 0) continue;
uint32_t dest_offset = EvalUint32InitExpr(segment.dest_addr);
- DCHECK(
- in_bounds(dest_offset, source_size, static_cast<uint32_t>(mem_size)));
- byte* dest = mem_addr + dest_offset;
+ DCHECK(in_bounds(dest_offset, source_size, wasm_context->mem_size));
+ byte* dest = wasm_context->mem_start + dest_offset;
const byte* src = reinterpret_cast<const byte*>(
module_bytes->GetCharsAddress() + segment.source.offset());
memcpy(dest, src, source_size);
@@ -2137,8 +2676,9 @@ void InstanceBuilder::LoadDataSegments(Address mem_addr, size_t mem_size) {
void InstanceBuilder::WriteGlobalValue(WasmGlobal& global,
Handle<Object> value) {
double num = value->Number();
- TRACE("init [globals+%u] = %lf, type = %s\n", global.offset, num,
- WasmOpcodes::TypeName(global.type));
+ TRACE("init [globals_start=%p + %u] = %lf, type = %s\n",
+ reinterpret_cast<void*>(raw_buffer_ptr(globals_, 0)), global.offset,
+ num, WasmOpcodes::TypeName(global.type));
switch (global.type) {
case kWasmI32:
*GetRawGlobalPtr<int32_t>(global) = static_cast<int32_t>(num);
@@ -2245,18 +2785,20 @@ int InstanceBuilder::ProcessImports(Handle<FixedArray> code_table,
module_name, import_name);
return -1;
}
-
- Handle<Code> import_code = UnwrapExportOrCompileImportWrapper(
- isolate_, index, module_->functions[import.index].sig,
- Handle<JSReceiver>::cast(value), module_->origin(),
- &imported_wasm_instances, js_imports_table, instance);
+ WasmCodeWrapper import_code = UnwrapExportOrCompileImportWrapper(
+ isolate_, module_->functions[import.index].sig,
+ Handle<JSReceiver>::cast(value), num_imported_functions,
+ module_->origin(), &imported_wasm_instances, js_imports_table,
+ instance);
if (import_code.is_null()) {
ReportLinkError("imported function does not match the expected type",
index, module_name, import_name);
return -1;
}
- code_table->set(num_imported_functions, *import_code);
- RecordStats(*import_code, counters());
+ if (!FLAG_wasm_jit_to_native) {
+ code_table->set(num_imported_functions, *import_code.GetCode());
+ }
+ RecordStats(import_code, counters());
num_imported_functions++;
break;
}
@@ -2270,6 +2812,7 @@ int InstanceBuilder::ProcessImports(Handle<FixedArray> code_table,
module_->function_tables[num_imported_tables];
TableInstance& table_instance = table_instances_[num_imported_tables];
table_instance.table_object = Handle<WasmTableObject>::cast(value);
+ instance->set_table_object(*table_instance.table_object);
table_instance.js_wrappers = Handle<FixedArray>(
table_instance.table_object->functions(), isolate_);
@@ -2313,17 +2856,55 @@ int InstanceBuilder::ProcessImports(Handle<FixedArray> code_table,
// that are already in the table.
for (int i = 0; i < table_size; ++i) {
Handle<Object> val(table_instance.js_wrappers->get(i), isolate_);
+ // TODO(mtrofin): this is the same logic as WasmTableObject::Set:
+ // insert in the local table a wrapper from the other module, and add
+ // a reference to the owning instance of the other module.
if (!val->IsJSFunction()) continue;
- WasmFunction* function = GetWasmFunctionForExport(isolate_, val);
- if (function == nullptr) {
+ if (!WasmExportedFunction::IsWasmExportedFunction(*val)) {
thrower_->LinkError("table import %d[%d] is not a wasm function",
index, i);
return -1;
}
- int sig_index = table.map.FindOrInsert(function->sig);
- table_instance.signature_table->set(i, Smi::FromInt(sig_index));
- table_instance.function_table->set(
- i, *UnwrapExportWrapper(Handle<JSFunction>::cast(val)));
+ // Look up the signature's canonical id. If there is no canonical
+ // id, then the signature does not appear at all in this module,
+ // so putting {-1} in the table will cause checks to always fail.
+ auto target = Handle<WasmExportedFunction>::cast(val);
+ if (!FLAG_wasm_jit_to_native) {
+ FunctionSig* sig = nullptr;
+ Handle<Code> code =
+ MakeWasmToWasmWrapper(isolate_, target, nullptr, &sig,
+ &imported_wasm_instances, instance, 0)
+ .GetCode();
+ int sig_index = module_->signature_map.Find(sig);
+ table_instance.signature_table->set(i, Smi::FromInt(sig_index));
+ table_instance.function_table->set(i, *code);
+ } else {
+ const wasm::WasmCode* exported_code =
+ target->GetWasmCode().GetWasmCode();
+ wasm::NativeModule* exporting_module = exported_code->owner();
+ Handle<WasmInstanceObject> imported_instance =
+ handle(target->instance());
+ imported_wasm_instances.Set(imported_instance, imported_instance);
+ FunctionSig* sig = imported_instance->module()
+ ->functions[exported_code->index()]
+ .sig;
+ wasm::WasmCode* wrapper_code =
+ exporting_module->GetExportedWrapper(exported_code->index());
+ if (wrapper_code == nullptr) {
+ WasmContext* other_context =
+ imported_instance->wasm_context()->get();
+ Handle<Code> wrapper = compiler::CompileWasmToWasmWrapper(
+ isolate_, target->GetWasmCode(), sig,
+ reinterpret_cast<Address>(other_context));
+ wrapper_code = exporting_module->AddExportedWrapper(
+ wrapper, exported_code->index());
+ }
+ int sig_index = module_->signature_map.Find(sig);
+ table_instance.signature_table->set(i, Smi::FromInt(sig_index));
+ Handle<Foreign> foreign_holder = isolate_->factory()->NewForeign(
+ wrapper_code->instructions().start(), TENURED);
+ table_instance.function_table->set(i, *foreign_holder);
+ }
}
num_imported_tables++;
@@ -2642,7 +3223,7 @@ void InstanceBuilder::ProcessExports(
}
v8::Maybe<bool> status = JSReceiver::DefineOwnProperty(
- isolate_, export_to, name, &desc, Object::THROW_ON_ERROR);
+ isolate_, export_to, name, &desc, kThrowOnError);
if (!status.IsJust()) {
TruncatedUserString<> trunc_name(name->GetCharVector<uint8_t>());
thrower_->LinkError("export of %.*s failed.", trunc_name.length(),
@@ -2653,8 +3234,8 @@ void InstanceBuilder::ProcessExports(
DCHECK_EQ(export_index, weak_exported_functions->length());
if (module_->is_wasm()) {
- v8::Maybe<bool> success = JSReceiver::SetIntegrityLevel(
- exports_object, FROZEN, Object::DONT_THROW);
+ v8::Maybe<bool> success =
+ JSReceiver::SetIntegrityLevel(exports_object, FROZEN, kDontThrow);
DCHECK(success.FromMaybe(false));
USE(success);
}
@@ -2663,28 +3244,56 @@ void InstanceBuilder::ProcessExports(
void InstanceBuilder::InitializeTables(
Handle<WasmInstanceObject> instance,
CodeSpecialization* code_specialization) {
- int function_table_count = static_cast<int>(module_->function_tables.size());
- Handle<FixedArray> new_function_tables =
- isolate_->factory()->NewFixedArray(function_table_count, TENURED);
- Handle<FixedArray> new_signature_tables =
- isolate_->factory()->NewFixedArray(function_table_count, TENURED);
- Handle<FixedArray> old_function_tables = compiled_module_->function_tables();
- Handle<FixedArray> old_signature_tables =
- compiled_module_->signature_tables();
+ size_t function_table_count = module_->function_tables.size();
+ std::vector<GlobalHandleAddress> new_function_tables(function_table_count);
+ std::vector<GlobalHandleAddress> new_signature_tables(function_table_count);
+
+ wasm::NativeModule* native_module = compiled_module_->GetNativeModule();
+ std::vector<GlobalHandleAddress> empty;
+ std::vector<GlobalHandleAddress>& old_function_tables =
+ FLAG_wasm_jit_to_native ? native_module->function_tables() : empty;
+ std::vector<GlobalHandleAddress>& old_signature_tables =
+ FLAG_wasm_jit_to_native ? native_module->signature_tables() : empty;
+
+ Handle<FixedArray> old_function_tables_gc =
+ FLAG_wasm_jit_to_native ? Handle<FixedArray>::null()
+ : compiled_module_->function_tables();
+ Handle<FixedArray> old_signature_tables_gc =
+ FLAG_wasm_jit_to_native ? Handle<FixedArray>::null()
+ : compiled_module_->signature_tables();
+
+ // function_table_count is 0 or 1, so we just create these objects even if not
+ // needed for native wasm.
+ // TODO(mtrofin): remove the {..}_gc variables when we don't need
+ // FLAG_wasm_jit_to_native
+ Handle<FixedArray> new_function_tables_gc =
+ isolate_->factory()->NewFixedArray(static_cast<int>(function_table_count),
+ TENURED);
+ Handle<FixedArray> new_signature_tables_gc =
+ isolate_->factory()->NewFixedArray(static_cast<int>(function_table_count),
+ TENURED);
// These go on the instance.
Handle<FixedArray> rooted_function_tables =
- isolate_->factory()->NewFixedArray(function_table_count, TENURED);
+ isolate_->factory()->NewFixedArray(static_cast<int>(function_table_count),
+ TENURED);
Handle<FixedArray> rooted_signature_tables =
- isolate_->factory()->NewFixedArray(function_table_count, TENURED);
+ isolate_->factory()->NewFixedArray(static_cast<int>(function_table_count),
+ TENURED);
instance->set_function_tables(*rooted_function_tables);
instance->set_signature_tables(*rooted_signature_tables);
- DCHECK_EQ(old_function_tables->length(), new_function_tables->length());
- DCHECK_EQ(old_signature_tables->length(), new_signature_tables->length());
-
- for (int index = 0; index < function_table_count; ++index) {
+ if (FLAG_wasm_jit_to_native) {
+ DCHECK_EQ(old_function_tables.size(), new_function_tables.size());
+ DCHECK_EQ(old_signature_tables.size(), new_signature_tables.size());
+ } else {
+ DCHECK_EQ(old_function_tables_gc->length(),
+ new_function_tables_gc->length());
+ DCHECK_EQ(old_signature_tables_gc->length(),
+ new_signature_tables_gc->length());
+ }
+ for (size_t index = 0; index < function_table_count; ++index) {
WasmIndirectFunctionTable& table = module_->function_tables[index];
TableInstance& table_instance = table_instances_[index];
int table_size = static_cast<int>(table.initial_size);
@@ -2734,31 +3343,45 @@ void InstanceBuilder::InitializeTables(
GlobalHandleAddress new_func_table_addr = global_func_table.address();
GlobalHandleAddress new_sig_table_addr = global_sig_table.address();
- WasmCompiledModule::SetTableValue(isolate_, new_function_tables, int_index,
- new_func_table_addr);
- WasmCompiledModule::SetTableValue(isolate_, new_signature_tables, int_index,
- new_sig_table_addr);
-
- GlobalHandleAddress old_func_table_addr =
- WasmCompiledModule::GetTableValue(*old_function_tables, int_index);
- GlobalHandleAddress old_sig_table_addr =
- WasmCompiledModule::GetTableValue(*old_signature_tables, int_index);
+ GlobalHandleAddress old_func_table_addr;
+ GlobalHandleAddress old_sig_table_addr;
+ if (!FLAG_wasm_jit_to_native) {
+ WasmCompiledModule::SetTableValue(isolate_, new_function_tables_gc,
+ int_index, new_func_table_addr);
+ WasmCompiledModule::SetTableValue(isolate_, new_signature_tables_gc,
+ int_index, new_sig_table_addr);
+
+ old_func_table_addr =
+ WasmCompiledModule::GetTableValue(*old_function_tables_gc, int_index);
+ old_sig_table_addr = WasmCompiledModule::GetTableValue(
+ *old_signature_tables_gc, int_index);
+ } else {
+ new_function_tables[int_index] = new_func_table_addr;
+ new_signature_tables[int_index] = new_sig_table_addr;
+ old_func_table_addr = old_function_tables[int_index];
+ old_sig_table_addr = old_signature_tables[int_index];
+ }
code_specialization->RelocatePointer(old_func_table_addr,
new_func_table_addr);
code_specialization->RelocatePointer(old_sig_table_addr,
new_sig_table_addr);
}
- compiled_module_->set_function_tables(new_function_tables);
- compiled_module_->set_signature_tables(new_signature_tables);
+ if (FLAG_wasm_jit_to_native) {
+ native_module->function_tables() = new_function_tables;
+ native_module->signature_tables() = new_signature_tables;
+ } else {
+ compiled_module_->set_function_tables(new_function_tables_gc);
+ compiled_module_->set_signature_tables(new_signature_tables_gc);
+ }
}
void InstanceBuilder::LoadTableSegments(Handle<FixedArray> code_table,
Handle<WasmInstanceObject> instance) {
+ wasm::NativeModule* native_module = compiled_module_->GetNativeModule();
int function_table_count = static_cast<int>(module_->function_tables.size());
for (int index = 0; index < function_table_count; ++index) {
- WasmIndirectFunctionTable& table = module_->function_tables[index];
TableInstance& table_instance = table_instances_[index];
Handle<FixedArray> all_dispatch_tables;
@@ -2774,12 +3397,20 @@ void InstanceBuilder::LoadTableSegments(Handle<FixedArray> code_table,
if (compile_lazy(module_)) {
for (auto& table_init : module_->table_inits) {
for (uint32_t func_index : table_init.entries) {
- Code* code =
- Code::cast(code_table->get(static_cast<int>(func_index)));
- // Only increase the counter for lazy compile builtins (it's not
- // needed otherwise).
- if (code->is_wasm_code()) continue;
- DCHECK_EQ(Builtins::kWasmCompileLazy, code->builtin_index());
+ if (!FLAG_wasm_jit_to_native) {
+ Code* code =
+ Code::cast(code_table->get(static_cast<int>(func_index)));
+ // Only increase the counter for lazy compile builtins (it's not
+ // needed otherwise).
+ if (code->is_wasm_code()) continue;
+ DCHECK_EQ(Builtins::kWasmCompileLazy, code->builtin_index());
+ } else {
+ const wasm::WasmCode* code = native_module->GetCode(func_index);
+ // Only increase the counter for lazy compile builtins (it's not
+ // needed otherwise).
+ if (code->kind() == wasm::WasmCode::Function) continue;
+ DCHECK_EQ(wasm::WasmCode::LazyStub, code->kind());
+ }
++num_table_exports[func_index];
}
}
@@ -2796,15 +3427,22 @@ void InstanceBuilder::LoadTableSegments(Handle<FixedArray> code_table,
uint32_t func_index = table_init.entries[i];
WasmFunction* function = &module_->functions[func_index];
int table_index = static_cast<int>(i + base);
- int32_t sig_index = table.map.Find(function->sig);
- DCHECK_GE(sig_index, 0);
+ uint32_t sig_index = module_->signature_ids[function->sig_index];
table_instance.signature_table->set(table_index,
Smi::FromInt(sig_index));
- Handle<Code> wasm_code = EnsureTableExportLazyDeoptData(
- isolate_, instance, code_table, func_index,
- table_instance.function_table, table_index, num_table_exports);
- table_instance.function_table->set(table_index, *wasm_code);
-
+ WasmCodeWrapper wasm_code = EnsureTableExportLazyDeoptData(
+ isolate_, instance, code_table, native_module, func_index,
+ table_instance.function_table, table_index, &num_table_exports);
+ Handle<Object> value_to_update_with;
+ if (!wasm_code.IsCodeObject()) {
+ Handle<Foreign> as_foreign = isolate_->factory()->NewForeign(
+ wasm_code.GetWasmCode()->instructions().start(), TENURED);
+ table_instance.function_table->set(table_index, *as_foreign);
+ value_to_update_with = as_foreign;
+ } else {
+ table_instance.function_table->set(table_index, *wasm_code.GetCode());
+ value_to_update_with = wasm_code.GetCode();
+ }
if (!all_dispatch_tables.is_null()) {
if (js_wrappers_[func_index].is_null()) {
// No JSFunction entry yet exists for this function. Create one.
@@ -2831,9 +3469,30 @@ void InstanceBuilder::LoadTableSegments(Handle<FixedArray> code_table,
}
table_instance.js_wrappers->set(table_index,
*js_wrappers_[func_index]);
-
+ // When updating dispatch tables, we need to provide a wasm-to-wasm
+ // wrapper for wasm_code - unless wasm_code is already a wrapper. If
+ // it's a wasm-to-js wrapper, we don't need to construct a
+ // wasm-to-wasm wrapper because there's no context switching required.
+ // The remaining case is that it's a wasm-to-wasm wrapper, in which
+ // case it's already doing "the right thing", and wrapping it again
+ // would be redundant.
+ if (func_index >= module_->num_imported_functions) {
+ value_to_update_with = GetOrCreateIndirectCallWrapper(
+ isolate_, instance, wasm_code, func_index, function->sig);
+ } else {
+ if (wasm_code.IsCodeObject()) {
+ DCHECK(wasm_code.GetCode()->kind() == Code::WASM_TO_JS_FUNCTION ||
+ wasm_code.GetCode()->kind() ==
+ Code::WASM_TO_WASM_FUNCTION);
+ } else {
+ DCHECK(wasm_code.GetWasmCode()->kind() ==
+ WasmCode::WasmToJsWrapper ||
+ wasm_code.GetWasmCode()->kind() ==
+ WasmCode::WasmToWasmWrapper);
+ }
+ }
UpdateDispatchTables(isolate_, all_dispatch_tables, table_index,
- function, wasm_code);
+ function, value_to_update_with);
}
}
}
@@ -2866,6 +3525,10 @@ AsyncCompileJob::AsyncCompileJob(Isolate* isolate,
async_counters_(isolate->async_counters()),
bytes_copy_(std::move(bytes_copy)),
wire_bytes_(bytes_copy_.get(), bytes_copy_.get() + length) {
+ v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
+ v8::Platform* platform = V8::GetCurrentPlatform();
+ foreground_task_runner_ = platform->GetForegroundTaskRunner(v8_isolate);
+ background_task_runner_ = platform->GetBackgroundTaskRunner(v8_isolate);
// The handles for the context and promise must be deferred.
DeferredHandleScope deferred(isolate);
context_ = Handle<Context>(*context);
@@ -3007,11 +3670,7 @@ void AsyncCompileJob::StartForegroundTask() {
++num_pending_foreground_tasks_;
DCHECK_EQ(1, num_pending_foreground_tasks_);
- v8::Platform* platform = V8::GetCurrentPlatform();
- // TODO(ahaas): This is a CHECK to debug issue 764313.
- CHECK(platform);
- platform->CallOnForegroundThread(reinterpret_cast<v8::Isolate*>(isolate_),
- new CompileTask(this, true));
+ foreground_task_runner_->PostTask(base::make_unique<CompileTask>(this, true));
}
template <typename Step, typename... Args>
@@ -3021,8 +3680,8 @@ void AsyncCompileJob::DoSync(Args&&... args) {
}
void AsyncCompileJob::StartBackgroundTask() {
- V8::GetCurrentPlatform()->CallOnBackgroundThread(
- new CompileTask(this, false), v8::Platform::kShortRunningTask);
+ background_task_runner_->PostTask(
+ base::make_unique<CompileTask>(this, false));
}
void AsyncCompileJob::RestartBackgroundTasks() {
@@ -3111,26 +3770,30 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
void RunInForeground() override {
TRACE_COMPILE("(2) Prepare and start compile...\n");
Isolate* isolate = job_->isolate_;
-
Factory* factory = isolate->factory();
+
Handle<Code> illegal_builtin = BUILTIN_CODE(isolate, Illegal);
+ if (!FLAG_wasm_jit_to_native) {
+ // The {code_table} array contains import wrappers and functions (which
+ // are both included in {functions.size()}.
+ // The results of compilation will be written into it.
+ // Initialize {code_table_} with the illegal builtin. All call sites
+ // will be patched at instantiation.
+ int code_table_size = static_cast<int>(module_->functions.size());
+ job_->code_table_ = factory->NewFixedArray(code_table_size, TENURED);
+
+ for (int i = 0, e = module_->num_imported_functions; i < e; ++i) {
+ job_->code_table_->set(i, *illegal_builtin);
+ }
+ } else {
+ // Just makes it easier to deal with code that wants code_table, while
+ // we have FLAG_wasm_jit_to_native around.
+ job_->code_table_ = factory->NewFixedArray(0, TENURED);
+ }
+
job_->module_env_ =
CreateDefaultModuleEnv(isolate, module_, illegal_builtin);
- // The {code_table} array contains import wrappers and functions (which
- // are both included in {functions.size()}.
- // The results of compilation will be written into it.
- // Initialize {code_table_} with the illegal builtin. All call sites
- // will be patched at instantiation.
- int code_table_size = static_cast<int>(module_->functions.size());
- int export_wrapper_size = static_cast<int>(module_->num_exported_functions);
- job_->code_table_ = factory->NewFixedArray(code_table_size, TENURED);
- job_->export_wrappers_ =
- factory->NewFixedArray(export_wrapper_size, TENURED);
-
- for (int i = 0, e = module_->num_imported_functions; i < e; ++i) {
- job_->code_table_->set(i, *illegal_builtin);
- }
// Transfer ownership of the {WasmModule} to the {ModuleCompiler}, but
// keep a pointer.
Handle<Code> centry_stub = CEntryStub(isolate, 1).GetCode();
@@ -3141,8 +3804,6 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
centry_stub = Handle<Code>(*centry_stub, isolate);
job_->code_table_ = Handle<FixedArray>(*job_->code_table_, isolate);
- job_->export_wrappers_ =
- Handle<FixedArray>(*job_->export_wrappers_, isolate);
compiler::ModuleEnv* env = job_->module_env_.get();
ReopenHandles(isolate, env->function_code);
Handle<Code>* mut =
@@ -3152,12 +3813,32 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
job_->deferred_handles_.push_back(deferred.Detach());
}
- job_->compiler_.reset(new ModuleCompiler(isolate, module_, centry_stub));
+ DCHECK_LE(module_->num_imported_functions, module_->functions.size());
+ // Create the compiled module object and populate with compiled functions
+ // and information needed at instantiation time. This object needs to be
+ // serializable. Instantiation may occur off a deserialized version of
+ // this object.
+ int export_wrapper_size = static_cast<int>(module_->num_exported_functions);
+ Handle<FixedArray> export_wrappers =
+ job_->isolate_->factory()->NewFixedArray(export_wrapper_size, TENURED);
+
+ job_->compiled_module_ =
+ NewCompiledModule(job_->isolate_, module_, job_->code_table_,
+ export_wrappers, job_->module_env_.get());
+
+ job_->compiler_.reset(
+ new ModuleCompiler(isolate, module_, centry_stub,
+ job_->compiled_module_->GetNativeModule()));
job_->compiler_->EnableThrottling();
- DCHECK_LE(module_->num_imported_functions, module_->functions.size());
+ {
+ DeferredHandleScope deferred(job_->isolate_);
+ job_->compiled_module_ = handle(*job_->compiled_module_, job_->isolate_);
+ job_->deferred_handles_.push_back(deferred.Detach());
+ }
size_t num_functions =
module_->functions.size() - module_->num_imported_functions;
+
if (num_functions == 0) {
// Degenerate case of an empty module.
job_->DoSync<FinishCompile>();
@@ -3171,7 +3852,6 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks),
V8::GetCurrentPlatform()
->NumberOfAvailableBackgroundThreads())));
-
if (start_compilation_) {
// TODO(ahaas): Try to remove the {start_compilation_} check when
// streaming decoding is done in the background. If
@@ -3216,6 +3896,11 @@ class AsyncCompileJob::ExecuteAndFinishCompilationUnits : public CompileStep {
}
void RunInForeground() override {
+ // TODO(6792): No longer needed once WebAssembly code is off heap.
+ // Use base::Optional to be able to close the scope before we resolve or
+ // reject the promise.
+ base::Optional<CodeSpaceMemoryModificationScope> modification_scope(
+ base::in_place_t(), job_->isolate_->heap());
TRACE_COMPILE("(4a) Finishing compilation units...\n");
if (failed_) {
// The job failed already, no need to do more work.
@@ -3234,7 +3919,7 @@ class AsyncCompileJob::ExecuteAndFinishCompilationUnits : public CompileStep {
int func_index = -1;
- MaybeHandle<Code> result =
+ WasmCodeWrapper result =
job_->compiler_->FinishCompilationUnit(&thrower, &func_index);
if (thrower.error()) {
@@ -3249,7 +3934,9 @@ class AsyncCompileJob::ExecuteAndFinishCompilationUnits : public CompileStep {
break;
} else {
DCHECK_LE(0, func_index);
- job_->code_table_->set(func_index, *result.ToHandleChecked());
+ if (result.IsCodeObject()) {
+ job_->code_table_->set(func_index, *result.GetCode());
+ }
--job_->outstanding_units_;
}
@@ -3267,6 +3954,10 @@ class AsyncCompileJob::ExecuteAndFinishCompilationUnits : public CompileStep {
if (thrower.error()) {
// Make sure all compilation tasks stopped running.
job_->background_task_manager_.CancelAndWait();
+
+ // Close the CodeSpaceMemoryModificationScope before we reject the promise
+ // in AsyncCompileFailed. Promise::Reject calls directly into JavaScript.
+ modification_scope.reset();
return job_->AsyncCompileFailed(thrower);
}
if (job_->outstanding_units_ == 0) {
@@ -3287,12 +3978,16 @@ class AsyncCompileJob::ExecuteAndFinishCompilationUnits : public CompileStep {
class AsyncCompileJob::FinishCompile : public CompileStep {
void RunInForeground() override {
TRACE_COMPILE("(5b) Finish compile...\n");
- // At this point, compilation has completed. Update the code table.
- for (int i = FLAG_skip_compiling_wasm_funcs,
- e = job_->code_table_->length();
- i < e; ++i) {
- Object* val = job_->code_table_->get(i);
- if (val->IsCode()) RecordStats(Code::cast(val), job_->counters());
+ if (FLAG_wasm_jit_to_native) {
+ RecordStats(job_->compiled_module_->GetNativeModule(), job_->counters());
+ } else {
+ // At this point, compilation has completed. Update the code table.
+ for (int i = FLAG_skip_compiling_wasm_funcs,
+ e = job_->code_table_->length();
+ i < e; ++i) {
+ Object* val = job_->code_table_->get(i);
+ if (val->IsCode()) RecordStats(Code::cast(val), job_->counters());
+ }
}
// Create heap objects for script and module bytes to be stored in the
@@ -3326,21 +4021,13 @@ class AsyncCompileJob::FinishCompile : public CompileStep {
WasmSharedModuleData::New(job_->isolate_, module_wrapper,
Handle<SeqOneByteString>::cast(module_bytes),
script, asm_js_offset_table);
+ job_->compiled_module_->OnWasmModuleDecodingComplete(shared);
+ script->set_wasm_compiled_module(*job_->compiled_module_);
- // Create the compiled module object and populate with compiled functions
- // and information needed at instantiation time. This object needs to be
- // serializable. Instantiation may occur off a deserialized version of
- // this object.
- job_->compiled_module_ =
- NewCompiledModule(job_->isolate_, shared, job_->code_table_,
- job_->export_wrappers_, job_->module_env_.get());
// Finish the wasm script now and make it public to the debugger.
- script->set_wasm_compiled_module(*job_->compiled_module_);
- job_->isolate_->debug()->OnAfterCompile(script);
+ job_->isolate_->debug()->OnAfterCompile(
+ handle(job_->compiled_module_->script()));
- DeferredHandleScope deferred(job_->isolate_);
- job_->compiled_module_ = handle(*job_->compiled_module_, job_->isolate_);
- job_->deferred_handles_.push_back(deferred.Detach());
// TODO(wasm): compiling wrappers should be made async as well.
job_->DoSync<CompileWrappers>();
}
@@ -3354,22 +4041,11 @@ class AsyncCompileJob::CompileWrappers : public CompileStep {
// and the wrappers for the function table elements.
void RunInForeground() override {
TRACE_COMPILE("(6) Compile wrappers...\n");
+ // TODO(6792): No longer needed once WebAssembly code is off heap.
+ CodeSpaceMemoryModificationScope modification_scope(job_->isolate_->heap());
// Compile JS->wasm wrappers for exported functions.
- JSToWasmWrapperCache js_to_wasm_cache;
- int wrapper_index = 0;
- WasmModule* module = job_->compiled_module_->module();
- for (auto exp : module->export_table) {
- if (exp.kind != kExternalFunction) continue;
- Handle<Code> wasm_code(Code::cast(job_->code_table_->get(exp.index)),
- job_->isolate_);
- Handle<Code> wrapper_code =
- js_to_wasm_cache.CloneOrCompileJSToWasmWrapper(job_->isolate_, module,
- wasm_code, exp.index);
- job_->export_wrappers_->set(wrapper_index, *wrapper_code);
- RecordStats(*wrapper_code, job_->counters());
- ++wrapper_index;
- }
-
+ CompileJsToWasmWrappers(job_->isolate_, job_->compiled_module_,
+ job_->counters());
job_->DoSync<FinishModule>();
}
};
@@ -3483,8 +4159,6 @@ bool AsyncStreamingProcessor::ProcessCodeSectionHeader(size_t functions_count,
// Set outstanding_finishers_ to 2, because both the AsyncCompileJob and the
// AsyncStreamingProcessor have to finish.
job_->outstanding_finishers_.SetValue(2);
- next_function_ = decoder_.module()->num_imported_functions +
- FLAG_skip_compiling_wasm_funcs;
compilation_unit_builder_.reset(
new ModuleCompiler::CompilationUnitBuilder(job_->compiler_.get()));
return true;
@@ -3495,16 +4169,20 @@ bool AsyncStreamingProcessor::ProcessFunctionBody(Vector<const uint8_t> bytes,
uint32_t offset) {
TRACE_STREAMING("Process function body %d ...\n", next_function_);
- decoder_.DecodeFunctionBody(
- next_function_, static_cast<uint32_t>(bytes.length()), offset, false);
- if (next_function_ >= decoder_.module()->num_imported_functions +
- FLAG_skip_compiling_wasm_funcs) {
- const WasmFunction* func = &decoder_.module()->functions[next_function_];
+ if (next_function_ >= FLAG_skip_compiling_wasm_funcs) {
+ decoder_.DecodeFunctionBody(
+ next_function_, static_cast<uint32_t>(bytes.length()), offset, false);
+
+ uint32_t index = next_function_ + decoder_.module()->num_imported_functions;
+ const WasmFunction* func = &decoder_.module()->functions[index];
WasmName name = {nullptr, 0};
- compilation_unit_builder_->AddUnit(job_->module_env_.get(), func, offset,
- bytes, name);
+ compilation_unit_builder_->AddUnit(
+ job_->module_env_.get(), job_->compiled_module_->GetNativeModule(),
+ func, offset, bytes, name);
}
++next_function_;
+ // This method always succeeds. The return value is necessary to comply with
+ // the StreamingProcessor interface.
return true;
}
@@ -3526,8 +4204,18 @@ void AsyncStreamingProcessor::OnFinishedStream(std::unique_ptr<uint8_t[]> bytes,
ModuleResult result = decoder_.FinishDecoding(false);
DCHECK(result.ok());
job_->module_ = std::move(result.val);
- if (job_->DecrementAndCheckFinisherCount())
- job_->DoSync<AsyncCompileJob::FinishCompile>();
+ if (job_->DecrementAndCheckFinisherCount()) {
+ if (!job_->compiler_) {
+ // We are processing a WebAssembly module without code section. We need to
+ // prepare compilation first before we can finish it.
+ // {PrepareAndStartCompile} will call {FinishCompile} by itself if there
+ // is no code section.
+ job_->DoSync<AsyncCompileJob::PrepareAndStartCompile>(job_->module_.get(),
+ true);
+ } else {
+ job_->DoSync<AsyncCompileJob::FinishCompile>();
+ }
+ }
}
// Report an error detected in the StreamingDecoder.
@@ -3541,12 +4229,62 @@ void AsyncStreamingProcessor::OnAbort() {
job_->Abort();
}
+void CompileJsToWasmWrappers(Isolate* isolate,
+ Handle<WasmCompiledModule> compiled_module,
+ Counters* counters) {
+ JSToWasmWrapperCache js_to_wasm_cache;
+ int wrapper_index = 0;
+ Handle<FixedArray> export_wrappers = compiled_module->export_wrappers();
+ NativeModule* native_module = compiled_module->GetNativeModule();
+ for (auto exp : compiled_module->module()->export_table) {
+ if (exp.kind != kExternalFunction) continue;
+ WasmCodeWrapper wasm_code = EnsureExportedLazyDeoptData(
+ isolate, Handle<WasmInstanceObject>::null(),
+ compiled_module->code_table(), native_module, exp.index);
+ Handle<Code> wrapper_code = js_to_wasm_cache.CloneOrCompileJSToWasmWrapper(
+ isolate, compiled_module->module(), wasm_code, exp.index);
+ export_wrappers->set(wrapper_index, *wrapper_code);
+ RecordStats(*wrapper_code, counters);
+ ++wrapper_index;
+ }
+}
+
+Handle<Script> CreateWasmScript(Isolate* isolate,
+ const ModuleWireBytes& wire_bytes) {
+ Handle<Script> script =
+ isolate->factory()->NewScript(isolate->factory()->empty_string());
+ script->set_context_data(isolate->native_context()->debug_context_id());
+ script->set_type(Script::TYPE_WASM);
+
+ int hash = StringHasher::HashSequentialString(
+ reinterpret_cast<const char*>(wire_bytes.start()),
+ static_cast<int>(wire_bytes.length()), kZeroHashSeed);
+
+ const int kBufferSize = 32;
+ char buffer[kBufferSize];
+ int url_chars = SNPrintF(ArrayVector(buffer), "wasm://wasm/%08x", hash);
+ DCHECK(url_chars >= 0 && url_chars < kBufferSize);
+ MaybeHandle<String> url_str = isolate->factory()->NewStringFromOneByte(
+ Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), url_chars),
+ TENURED);
+ script->set_source_url(*url_str.ToHandleChecked());
+
+ int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash);
+ DCHECK(name_chars >= 0 && name_chars < kBufferSize);
+ MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte(
+ Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), name_chars),
+ TENURED);
+ script->set_name(*name_str.ToHandleChecked());
+
+ return script;
+}
+
} // namespace wasm
} // namespace internal
} // namespace v8
#undef TRACE
+#undef TRACE_CHAIN
#undef TRACE_COMPILE
#undef TRACE_STREAMING
-#undef TRACE_CHAIN
-#undef ERROR_THROWER_WITH_MESSAGE
+#undef TRACE_LAZY