| Index: src/wasm/wasm-module.cc
|
| diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc
|
| index f880d03e34112320947c015ad23d3911a6bd71d8..9ec439d4b5192229dea9573907e2236830cc3135 100644
|
| --- a/src/wasm/wasm-module.cc
|
| +++ b/src/wasm/wasm-module.cc
|
| @@ -2,7 +2,6 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| -#include "src/base/atomic-utils.h"
|
| #include "src/macro-assembler.h"
|
| #include "src/objects.h"
|
| #include "src/property-descriptor.h"
|
| @@ -130,11 +129,11 @@
|
| // Create a placeholder code object and encode the corresponding index in
|
| // the {constant_pool_offset} field of the code object.
|
| // TODO(titzer): placeholder code objects are somewhat dangerous.
|
| + Handle<Code> self(nullptr, isolate_);
|
| byte buffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; // fake instructions.
|
| CodeDesc desc = {buffer, 8, 8, 0, 0, nullptr};
|
| Handle<Code> code = isolate_->factory()->NewCode(
|
| - desc, Code::KindField::encode(Code::WASM_FUNCTION),
|
| - Handle<Object>::null());
|
| + desc, Code::KindField::encode(Code::WASM_FUNCTION), self);
|
| code->set_constant_pool_offset(index + kPlaceholderMarker);
|
| placeholder_code_[index] = code;
|
| function_code_[index] = code;
|
| @@ -396,245 +395,6 @@
|
| return Handle<JSFunction>::cast(function);
|
| }
|
|
|
| -namespace {
|
| -// Fetches the compilation unit of a wasm function and executes its parallel
|
| -// phase.
|
| -bool FetchAndExecuteCompilationUnit(
|
| - Isolate* isolate,
|
| - std::vector<compiler::WasmCompilationUnit*>* compilation_units,
|
| - std::queue<compiler::WasmCompilationUnit*>* executed_units,
|
| - base::Mutex* result_mutex, base::AtomicNumber<size_t>* next_unit) {
|
| - DisallowHeapAllocation no_allocation;
|
| - DisallowHandleAllocation no_handles;
|
| - DisallowHandleDereference no_deref;
|
| - DisallowCodeDependencyChange no_dependency_change;
|
| -
|
| - // - 1 because AtomicIntrement returns the value after the atomic increment.
|
| - size_t index = next_unit->Increment(1) - 1;
|
| - if (index >= compilation_units->size()) {
|
| - return false;
|
| - }
|
| -
|
| - compiler::WasmCompilationUnit* unit = compilation_units->at(index);
|
| - if (unit != nullptr) {
|
| - compiler::ExecuteCompilation(unit);
|
| - {
|
| - base::LockGuard<base::Mutex> guard(result_mutex);
|
| - executed_units->push(unit);
|
| - }
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -class WasmCompilationTask : public CancelableTask {
|
| - public:
|
| - WasmCompilationTask(
|
| - Isolate* isolate,
|
| - std::vector<compiler::WasmCompilationUnit*>* compilation_units,
|
| - std::queue<compiler::WasmCompilationUnit*>* executed_units,
|
| - base::Semaphore* on_finished, base::Mutex* result_mutex,
|
| - base::AtomicNumber<size_t>* next_unit)
|
| - : CancelableTask(isolate),
|
| - isolate_(isolate),
|
| - compilation_units_(compilation_units),
|
| - executed_units_(executed_units),
|
| - on_finished_(on_finished),
|
| - result_mutex_(result_mutex),
|
| - next_unit_(next_unit) {}
|
| -
|
| - void RunInternal() override {
|
| - while (FetchAndExecuteCompilationUnit(isolate_, compilation_units_,
|
| - executed_units_, result_mutex_,
|
| - next_unit_)) {
|
| - }
|
| - on_finished_->Signal();
|
| - }
|
| -
|
| - Isolate* isolate_;
|
| - std::vector<compiler::WasmCompilationUnit*>* compilation_units_;
|
| - std::queue<compiler::WasmCompilationUnit*>* executed_units_;
|
| - base::Semaphore* on_finished_;
|
| - base::Mutex* result_mutex_;
|
| - base::AtomicNumber<size_t>* next_unit_;
|
| -};
|
| -
|
| -void record_code_size(uint32_t& total_code_size, Code* code) {
|
| - if (FLAG_print_wasm_code_size) {
|
| - total_code_size += code->body_size() + code->relocation_info()->length();
|
| - }
|
| -}
|
| -
|
| -bool CompileWrappersToImportedFunctions(Isolate* isolate, WasmModule* module,
|
| - const Handle<JSReceiver> ffi,
|
| - WasmModuleInstance* instance,
|
| - ErrorThrower* thrower, Factory* factory,
|
| - ModuleEnv* module_env,
|
| - uint32_t& total_code_size) {
|
| - uint32_t index = 0;
|
| - if (module->import_table.size() > 0) {
|
| - instance->import_code.reserve(module->import_table.size());
|
| - for (const WasmImport& import : module->import_table) {
|
| - WasmName module_name = module->GetNameOrNull(import.module_name_offset,
|
| - import.module_name_length);
|
| - WasmName function_name = module->GetNameOrNull(
|
| - import.function_name_offset, import.function_name_length);
|
| - MaybeHandle<JSFunction> function = LookupFunction(
|
| - *thrower, factory, ffi, index, module_name, function_name);
|
| - if (function.is_null()) return false;
|
| -
|
| - Handle<Code> code = compiler::CompileWasmToJSWrapper(
|
| - isolate, module_env, function.ToHandleChecked(), import.sig,
|
| - module_name, function_name);
|
| - instance->import_code.push_back(code);
|
| - record_code_size(total_code_size, *code);
|
| - index++;
|
| - }
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -void InitializeParallelCompilation(
|
| - Isolate* isolate, std::vector<WasmFunction>& functions,
|
| - std::vector<compiler::WasmCompilationUnit*>& compilation_units,
|
| - ModuleEnv& module_env, ErrorThrower& thrower) {
|
| - // Create a placeholder code object for all functions.
|
| - // TODO(ahaas): Maybe we could skip this for external functions.
|
| - for (uint32_t i = 0; i < functions.size(); i++) {
|
| - module_env.linker->GetFunctionCode(i);
|
| - }
|
| -
|
| - for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size(); i++) {
|
| - if (!functions[i].external) {
|
| - compilation_units[i] = compiler::CreateWasmCompilationUnit(
|
| - &thrower, isolate, &module_env, &functions[i], i);
|
| - } else {
|
| - compilation_units[i] = nullptr;
|
| - }
|
| - }
|
| -}
|
| -
|
| -uint32_t* StartCompilationTasks(
|
| - Isolate* isolate,
|
| - std::vector<compiler::WasmCompilationUnit*>& compilation_units,
|
| - std::queue<compiler::WasmCompilationUnit*>& executed_units,
|
| - const base::SmartPointer<base::Semaphore>& pending_tasks,
|
| - base::Mutex& result_mutex, base::AtomicNumber<size_t>& next_unit) {
|
| - const size_t num_tasks =
|
| - Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks),
|
| - V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads());
|
| - uint32_t* task_ids = new uint32_t[num_tasks];
|
| - for (size_t i = 0; i < num_tasks; i++) {
|
| - WasmCompilationTask* task =
|
| - new WasmCompilationTask(isolate, &compilation_units, &executed_units,
|
| - pending_tasks.get(), &result_mutex, &next_unit);
|
| - task_ids[i] = task->id();
|
| - V8::GetCurrentPlatform()->CallOnBackgroundThread(
|
| - task, v8::Platform::kShortRunningTask);
|
| - }
|
| - return task_ids;
|
| -}
|
| -
|
| -void WaitForCompilationTasks(
|
| - Isolate* isolate, uint32_t* task_ids,
|
| - const base::SmartPointer<base::Semaphore>& pending_tasks) {
|
| - const size_t num_tasks =
|
| - Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks),
|
| - V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads());
|
| - for (size_t i = 0; i < num_tasks; i++) {
|
| - // If the task has not started yet, then we abort it. Otherwise we wait for
|
| - // it to finish.
|
| - if (!isolate->cancelable_task_manager()->TryAbort(task_ids[i])) {
|
| - pending_tasks->Wait();
|
| - }
|
| - }
|
| -}
|
| -
|
| -void FinishCompilationUnits(
|
| - WasmModule* module,
|
| - std::queue<compiler::WasmCompilationUnit*>& executed_units,
|
| - std::vector<Handle<Code>>& results, base::Mutex& result_mutex) {
|
| - while (!executed_units.empty()) {
|
| - compiler::WasmCompilationUnit* unit = nullptr;
|
| - {
|
| - base::LockGuard<base::Mutex> guard(&result_mutex);
|
| - unit = executed_units.front();
|
| - executed_units.pop();
|
| - }
|
| - int j = compiler::GetIndexOfWasmCompilationUnit(unit);
|
| - if (!module->functions[j].external) {
|
| - results[j] = compiler::FinishCompilation(unit);
|
| - }
|
| - }
|
| -}
|
| -
|
| -bool FinishCompilation(Isolate* isolate, WasmModule* module,
|
| - const Handle<JSReceiver> ffi,
|
| - const std::vector<Handle<Code>>& results,
|
| - const WasmModuleInstance& instance,
|
| - const Handle<FixedArray>& code_table,
|
| - ErrorThrower& thrower, Factory* factory,
|
| - ModuleEnv& module_env, uint32_t& total_code_size,
|
| - PropertyDescriptor& desc) {
|
| - for (uint32_t i = FLAG_skip_compiling_wasm_funcs;
|
| - i < module->functions.size(); i++) {
|
| - const WasmFunction& func = module->functions[i];
|
| - if (thrower.error()) break;
|
| -
|
| - DCHECK_EQ(i, func.func_index);
|
| - WasmName str = module->GetName(func.name_offset, func.name_length);
|
| - WasmName str_null = {nullptr, 0};
|
| - Handle<String> name = factory->InternalizeUtf8String(str);
|
| - Handle<Code> code = Handle<Code>::null();
|
| - Handle<JSFunction> function = Handle<JSFunction>::null();
|
| - if (func.external) {
|
| - // Lookup external function in FFI object.
|
| - MaybeHandle<JSFunction> function =
|
| - LookupFunction(thrower, factory, ffi, i, str, str_null);
|
| - if (function.is_null()) {
|
| - return false;
|
| - }
|
| - code = compiler::CompileWasmToJSWrapper(isolate, &module_env,
|
| - function.ToHandleChecked(),
|
| - func.sig, str, str_null);
|
| - } else {
|
| - if (FLAG_wasm_num_compilation_tasks != 0) {
|
| - code = results[i];
|
| - } else {
|
| - // Compile the function.
|
| - code = compiler::CompileWasmFunction(&thrower, isolate, &module_env,
|
| - &func);
|
| - }
|
| - if (code.is_null()) {
|
| - thrower.Error("Compilation of #%d:%.*s failed.", i, str.length(),
|
| - str.start());
|
| - return false;
|
| - }
|
| - if (func.exported) {
|
| - function = compiler::CompileJSToWasmWrapper(
|
| - isolate, &module_env, name, code, instance.js_object, i);
|
| - record_code_size(total_code_size, function->code());
|
| - }
|
| - }
|
| - if (!code.is_null()) {
|
| - // Install the code into the linker table.
|
| - module_env.linker->Finish(i, code);
|
| - code_table->set(i, *code);
|
| - record_code_size(total_code_size, *code);
|
| - }
|
| - if (func.exported) {
|
| - // Exported functions are installed as read-only properties on the
|
| - // module.
|
| - desc.set_value(function);
|
| - Maybe<bool> status = JSReceiver::DefineOwnProperty(
|
| - isolate, instance.js_object, name, &desc, Object::THROW_ON_ERROR);
|
| - if (!status.IsJust())
|
| - thrower.Error("export of %.*s failed.", str.length(), str.start());
|
| - }
|
| - }
|
| - return true;
|
| -}
|
| -} // namespace
|
| -
|
| // Instantiates a wasm module as a JSObject.
|
| // * allocates a backing store of {mem_size} bytes.
|
| // * installs a named property "memory" for that buffer if exported
|
| @@ -656,6 +416,10 @@
|
| // objects created for this module.
|
| // TODO(titzer): switch this to TRACE_EVENT
|
| uint32_t total_code_size = 0;
|
| + auto record_code_size = [&total_code_size](Code* code) {
|
| + if (FLAG_print_wasm_code_size)
|
| + total_code_size += code->body_size() + code->relocation_info()->length();
|
| + };
|
|
|
| //-------------------------------------------------------------------------
|
| // Allocate the instance and its JS counterpart.
|
| @@ -702,6 +466,10 @@
|
| HistogramTimerScope wasm_compile_module_time_scope(
|
| isolate->counters()->wasm_compile_module_time());
|
|
|
| + //-------------------------------------------------------------------------
|
| + // Compile wrappers to imported functions.
|
| + //-------------------------------------------------------------------------
|
| + uint32_t index = 0;
|
| instance.function_table = BuildFunctionTable(isolate, this);
|
| WasmLinker linker(isolate, functions.size());
|
| ModuleEnv module_env;
|
| @@ -710,14 +478,25 @@
|
| module_env.linker = &linker;
|
| module_env.origin = origin;
|
|
|
| - //-------------------------------------------------------------------------
|
| - // Compile wrappers to imported functions.
|
| - //-------------------------------------------------------------------------
|
| - if (!CompileWrappersToImportedFunctions(isolate, this, ffi, &instance,
|
| - &thrower, factory, &module_env,
|
| - total_code_size)) {
|
| - return MaybeHandle<JSObject>();
|
| - }
|
| + if (import_table.size() > 0) {
|
| + instance.import_code.reserve(import_table.size());
|
| + for (const WasmImport& import : import_table) {
|
| + WasmName module_name =
|
| + GetNameOrNull(import.module_name_offset, import.module_name_length);
|
| + WasmName function_name = GetNameOrNull(import.function_name_offset,
|
| + import.function_name_length);
|
| + MaybeHandle<JSFunction> function = LookupFunction(
|
| + thrower, factory, ffi, index, module_name, function_name);
|
| + if (function.is_null()) return MaybeHandle<JSObject>();
|
| + Handle<Code> code = compiler::CompileWasmToJSWrapper(
|
| + isolate, &module_env, function.ToHandleChecked(), import.sig,
|
| + module_name, function_name);
|
| + instance.import_code.push_back(code);
|
| + record_code_size(*code);
|
| + index++;
|
| + }
|
| + }
|
| +
|
| //-------------------------------------------------------------------------
|
| // Compile all functions in the module.
|
| //-------------------------------------------------------------------------
|
| @@ -725,77 +504,103 @@
|
| isolate->counters()->wasm_functions_per_module()->AddSample(
|
| static_cast<int>(functions.size()));
|
|
|
| - // Data structures for the parallel compilation.
|
| std::vector<compiler::WasmCompilationUnit*> compilation_units(
|
| functions.size());
|
| std::queue<compiler::WasmCompilationUnit*> executed_units;
|
| std::vector<Handle<Code>> results(functions.size());
|
|
|
| - if (FLAG_wasm_num_compilation_tasks != 0) {
|
| - //-----------------------------------------------------------------------
|
| - // For parallel compilation:
|
| - // 1) The main thread allocates a compilation unit for each wasm function
|
| - // and stores them in the vector {compilation_units}.
|
| - // 2) The main thread spawns {WasmCompilationTask} instances which run on
|
| - // the background threads.
|
| - // 3.a) The background threads and the main thread pick one compilation
|
| - // unit at a time and execute the parallel phase of the compilation
|
| - // unit. After finishing the execution of the parallel phase, the
|
| - // result is enqueued in {executed_units}.
|
| - // 3.b) If {executed_units} contains a compilation unit, the main thread
|
| - // dequeues it and finishes the compilation.
|
| - // 4) After the parallel phase of all compilation units has started, the
|
| - // main thread waits for all {WasmCompilationTask} instances to finish.
|
| - // 5) The main thread finishes the compilation.
|
| -
|
| - // Turn on the {CanonicalHandleScope} so that the background threads can
|
| - // use the node cache.
|
| - CanonicalHandleScope canonical(isolate);
|
| -
|
| - // 1) The main thread allocates a compilation unit for each wasm function
|
| - // and stores them in the vector {compilation_units}.
|
| - InitializeParallelCompilation(isolate, functions, compilation_units,
|
| - module_env, thrower);
|
| -
|
| - // Objects for the synchronization with the background threads.
|
| - base::SmartPointer<base::Semaphore> pending_tasks(new base::Semaphore(0));
|
| - base::Mutex result_mutex;
|
| - base::AtomicNumber<size_t> next_unit(
|
| - static_cast<size_t>(FLAG_skip_compiling_wasm_funcs));
|
| -
|
| - // 2) The main thread spawns {WasmCompilationTask} instances which run on
|
| - // the background threads.
|
| - base::SmartArrayPointer<uint32_t> task_ids(
|
| - StartCompilationTasks(isolate, compilation_units, executed_units,
|
| - pending_tasks, result_mutex, next_unit));
|
| -
|
| - // 3.a) The background threads and the main thread pick one compilation
|
| - // unit at a time and execute the parallel phase of the compilation
|
| - // unit. After finishing the execution of the parallel phase, the
|
| - // result is enqueued in {executed_units}.
|
| - while (FetchAndExecuteCompilationUnit(isolate, &compilation_units,
|
| - &executed_units, &result_mutex,
|
| - &next_unit)) {
|
| - // 3.b) If {executed_units} contains a compilation unit, the main thread
|
| - // dequeues it and finishes the compilation unit. Compilation units
|
| - // are finished concurrently to the background threads to save
|
| - // memory.
|
| - FinishCompilationUnits(this, executed_units, results, result_mutex);
|
| - }
|
| - // 4) After the parallel phase of all compilation units has started, the
|
| - // main thread waits for all {WasmCompilationTask} instances to finish.
|
| - WaitForCompilationTasks(isolate, task_ids.get(), pending_tasks);
|
| - // Finish the compilation of the remaining compilation units.
|
| - FinishCompilationUnits(this, executed_units, results, result_mutex);
|
| - }
|
| - // 5) The main thread finishes the compilation.
|
| - if (!FinishCompilation(isolate, this, ffi, results, instance, code_table,
|
| - thrower, factory, module_env, total_code_size,
|
| - desc)) {
|
| - return MaybeHandle<JSObject>();
|
| - }
|
| -
|
| - // Patch all direct call sites.
|
| + if (FLAG_wasm_parallel_compilation) {
|
| + // Create a placeholder code object for all functions.
|
| + // TODO(ahaas): Maybe we could skip this for external functions.
|
| + for (uint32_t i = 0; i < functions.size(); i++) {
|
| + linker.GetFunctionCode(i);
|
| + }
|
| +
|
| + for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size();
|
| + i++) {
|
| + if (!functions[i].external) {
|
| + compilation_units[i] = compiler::CreateWasmCompilationUnit(
|
| + &thrower, isolate, &module_env, &functions[i], i);
|
| + }
|
| + }
|
| +
|
| + index = FLAG_skip_compiling_wasm_funcs;
|
| + while (true) {
|
| + while (!executed_units.empty()) {
|
| + compiler::WasmCompilationUnit* unit = executed_units.front();
|
| + executed_units.pop();
|
| + int i = compiler::GetIndexOfWasmCompilationUnit(unit);
|
| + results[i] = compiler::FinishCompilation(unit);
|
| + }
|
| + if (index < functions.size()) {
|
| + if (!functions[index].external) {
|
| + compiler::ExecuteCompilation(compilation_units[index]);
|
| + executed_units.push(compilation_units[index]);
|
| + index++;
|
| + }
|
| + } else {
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // First pass: compile each function and initialize the code table.
|
| + for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size();
|
| + i++) {
|
| + const WasmFunction& func = functions[i];
|
| + if (thrower.error()) break;
|
| + DCHECK_EQ(i, func.func_index);
|
| +
|
| + WasmName str = GetName(func.name_offset, func.name_length);
|
| + WasmName str_null = {nullptr, 0};
|
| + Handle<String> name = factory->InternalizeUtf8String(str);
|
| + Handle<Code> code = Handle<Code>::null();
|
| + Handle<JSFunction> function = Handle<JSFunction>::null();
|
| + if (func.external) {
|
| + // Lookup external function in FFI object.
|
| + MaybeHandle<JSFunction> function =
|
| + LookupFunction(thrower, factory, ffi, i, str, str_null);
|
| + if (function.is_null()) return MaybeHandle<JSObject>();
|
| + code = compiler::CompileWasmToJSWrapper(isolate, &module_env,
|
| + function.ToHandleChecked(),
|
| + func.sig, str, str_null);
|
| + } else {
|
| + if (FLAG_wasm_parallel_compilation) {
|
| + code = results[i];
|
| + } else {
|
| + // Compile the function.
|
| + code = compiler::CompileWasmFunction(&thrower, isolate, &module_env,
|
| + &func);
|
| + }
|
| + if (code.is_null()) {
|
| + thrower.Error("Compilation of #%d:%.*s failed.", i, str.length(),
|
| + str.start());
|
| + return MaybeHandle<JSObject>();
|
| + }
|
| + if (func.exported) {
|
| + function = compiler::CompileJSToWasmWrapper(
|
| + isolate, &module_env, name, code, instance.js_object, i);
|
| + record_code_size(function->code());
|
| + }
|
| + }
|
| + if (!code.is_null()) {
|
| + // Install the code into the linker table.
|
| + linker.Finish(i, code);
|
| + code_table->set(i, *code);
|
| + record_code_size(*code);
|
| + }
|
| + if (func.exported) {
|
| + // Exported functions are installed as read-only properties on the
|
| + // module.
|
| + desc.set_value(function);
|
| + Maybe<bool> status = JSReceiver::DefineOwnProperty(
|
| + isolate, instance.js_object, name, &desc, Object::THROW_ON_ERROR);
|
| + if (!status.IsJust())
|
| + thrower.Error("export of %.*s failed.", str.length(), str.start());
|
| + }
|
| + }
|
| +
|
| + // Second pass: patch all direct call sites.
|
| linker.Link(instance.function_table, this->function_table);
|
| instance.js_object->SetInternalField(kWasmModuleFunctionTable,
|
| Smi::FromInt(0));
|
| @@ -822,7 +627,7 @@
|
| Handle<JSFunction> function = compiler::CompileJSToWasmWrapper(
|
| isolate, &module_env, name, code, instance.js_object,
|
| exp.func_index);
|
| - record_code_size(total_code_size, function->code());
|
| + record_code_size(function->code());
|
| desc.set_value(function);
|
| Maybe<bool> status = JSReceiver::DefineOwnProperty(
|
| isolate, exports_object, name, &desc, Object::THROW_ON_ERROR);
|
|
|