| Index: src/wasm/wasm-module.cc
|
| diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc
|
| index 3587dd5313020e1b32ad8427eabc8d4113cc2772..df63dce97c85dcb3f68cfcd7199419b70ea5a845 100644
|
| --- a/src/wasm/wasm-module.cc
|
| +++ b/src/wasm/wasm-module.cc
|
| @@ -22,6 +22,8 @@ namespace v8 {
|
| namespace internal {
|
| namespace wasm {
|
|
|
| +static const int kPlaceholderMarker = 1000000000;
|
| +
|
| static const char* wasmSections[] = {
|
| #define F(enumerator, order, string) string,
|
| FOR_EACH_WASM_SECTION_TYPE(F)
|
| @@ -113,54 +115,61 @@ std::ostream& operator<<(std::ostream& os, const WasmFunctionName& pair) {
|
| // placeholder code objects for calling functions that are not yet compiled.
|
| class WasmLinker {
|
| public:
|
| - WasmLinker(Isolate* isolate, size_t size)
|
| - : isolate_(isolate), placeholder_code_(size), function_code_(size) {}
|
| -
|
| - // Get the code object for a function, allocating a placeholder if it has
|
| - // not yet been compiled.
|
| - Handle<Code> GetFunctionCode(uint32_t index) {
|
| - DCHECK(index < function_code_.size());
|
| - if (function_code_[index].is_null()) {
|
| - // 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.
|
| - 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());
|
| - code->set_constant_pool_offset(index + kPlaceholderMarker);
|
| - placeholder_code_[index] = code;
|
| - function_code_[index] = code;
|
| + WasmLinker(Isolate* isolate, std::vector<Handle<Code>>* functions)
|
| + : isolate_(isolate),
|
| + placeholder_code_(functions->size()),
|
| + function_code_(functions) {
|
| + for (uint32_t i = 0; i < placeholder_code_.size(); ++i) {
|
| + CreatePlaceholder(i);
|
| }
|
| - return function_code_[index];
|
| + }
|
| +
|
| + Handle<Code> GetPlaceholderCode(uint32_t index) const {
|
| + return placeholder_code_[index];
|
| }
|
|
|
| void Finish(uint32_t index, Handle<Code> code) {
|
| - DCHECK(index < function_code_.size());
|
| - function_code_[index] = code;
|
| + DCHECK(index < function_code().size());
|
| + function_code()[index] = code;
|
| }
|
|
|
| void Link(Handle<FixedArray> function_table,
|
| const std::vector<uint16_t>& functions) {
|
| - for (size_t i = 0; i < function_code_.size(); i++) {
|
| - LinkFunction(function_code_[i]);
|
| + for (size_t i = 0; i < function_code().size(); i++) {
|
| + LinkFunction(function_code()[i]);
|
| }
|
| if (!function_table.is_null()) {
|
| int table_size = static_cast<int>(functions.size());
|
| DCHECK_EQ(function_table->length(), table_size * 2);
|
| for (int i = 0; i < table_size; i++) {
|
| - function_table->set(i + table_size, *function_code_[functions[i]]);
|
| + function_table->set(i + table_size, *function_code()[functions[i]]);
|
| }
|
| }
|
| }
|
|
|
| private:
|
| - static const int kPlaceholderMarker = 1000000000;
|
| + std::vector<Handle<Code>>& function_code() { return *function_code_; }
|
| +
|
| + void CreatePlaceholder(uint32_t index) {
|
| + DCHECK(index < function_code().size());
|
| + DCHECK(function_code()[index].is_null());
|
| + // 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.
|
| + 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());
|
| + code->set_constant_pool_offset(static_cast<int>(index) +
|
| + kPlaceholderMarker);
|
| + placeholder_code_[index] = code;
|
| + function_code()[index] = code;
|
| + }
|
|
|
| Isolate* isolate_;
|
| std::vector<Handle<Code>> placeholder_code_;
|
| - std::vector<Handle<Code>> function_code_;
|
| + std::vector<Handle<Code>>* function_code_;
|
|
|
| void LinkFunction(Handle<Code> code) {
|
| bool modified = false;
|
| @@ -175,8 +184,8 @@ class WasmLinker {
|
| target->constant_pool_offset() >= kPlaceholderMarker) {
|
| // Patch direct calls to placeholder code objects.
|
| uint32_t index = target->constant_pool_offset() - kPlaceholderMarker;
|
| - CHECK(index < function_code_.size());
|
| - Handle<Code> new_target = function_code_[index];
|
| + CHECK(index < function_code().size());
|
| + Handle<Code> new_target = function_code()[index];
|
| if (target != *new_target) {
|
| CHECK_EQ(*placeholder_code_[index], target);
|
| it.rinfo()->set_target_address(new_target->instruction_start(),
|
| @@ -494,12 +503,6 @@ void InitializeParallelCompilation(
|
| Isolate* isolate, const 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++) {
|
| compilation_units[i] = new compiler::WasmCompilationUnit(
|
| &thrower, isolate, &module_env, &functions[i], i);
|
| @@ -561,15 +564,76 @@ void FinishCompilationUnits(
|
| }
|
| }
|
|
|
| -bool FinishCompilation(Isolate* isolate, const 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, CodeStats& code_stats,
|
| - PropertyDescriptor& desc) {
|
| - if (thrower.error()) return false;
|
| +void CompileInParallel(Isolate* isolate, const WasmModule* module,
|
| + std::vector<Handle<Code>>& functions,
|
| + ErrorThrower* thrower, ModuleEnv* module_env) {
|
| + // Data structures for the parallel compilation.
|
| + std::vector<compiler::WasmCompilationUnit*> compilation_units(
|
| + module->functions.size());
|
| + std::queue<compiler::WasmCompilationUnit*> executed_units;
|
| +
|
| + //-----------------------------------------------------------------------
|
| + // 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, module->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(executed_units, functions, 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(executed_units, functions, result_mutex);
|
| +}
|
| +
|
| +void CompileSequentially(Isolate* isolate, const WasmModule* module,
|
| + std::vector<Handle<Code>>& functions,
|
| + ErrorThrower* thrower, ModuleEnv* module_env) {
|
| + DCHECK(!thrower->error());
|
| +
|
| for (uint32_t i = FLAG_skip_compiling_wasm_funcs;
|
| i < module->functions.size(); i++) {
|
| const WasmFunction& func = module->functions[i];
|
| @@ -577,29 +641,36 @@ bool FinishCompilation(Isolate* isolate, const WasmModule* module,
|
| DCHECK_EQ(i, func.func_index);
|
| WasmName str = module->GetName(func.name_offset, func.name_length);
|
| Handle<Code> code = Handle<Code>::null();
|
| - if (FLAG_wasm_num_compilation_tasks != 0) {
|
| - code = results[i];
|
| - } else {
|
| - // Compile the function.
|
| - code = compiler::WasmCompilationUnit::CompileWasmFunction(
|
| - &thrower, isolate, &module_env, &func);
|
| - }
|
| + // Compile the function.
|
| + code = compiler::WasmCompilationUnit::CompileWasmFunction(
|
| + thrower, isolate, module_env, &func);
|
| if (code.is_null()) {
|
| - thrower.Error("Compilation of #%d:%.*s failed.", i, str.length(),
|
| - str.start());
|
| - return false;
|
| + thrower->Error("Compilation of #%d:%.*s failed.", i, str.length(),
|
| + str.start());
|
| + break;
|
| }
|
| - if (!code.is_null()) {
|
| // Install the code into the linker table.
|
| - module_env.linker->Finish(i, code);
|
| - code_table->set(i, *code);
|
| - code_stats.Record(*code);
|
| - }
|
| + functions[i] = code;
|
| }
|
| - return true;
|
| }
|
| } // namespace
|
|
|
| +void SetDeoptimizationData(Factory* factory, Handle<JSObject> js_object,
|
| + std::vector<Handle<Code>>& functions) {
|
| + for (size_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size(); ++i) {
|
| + Handle<Code> code = functions[i];
|
| + DCHECK(code->deoptimization_data() == nullptr ||
|
| + code->deoptimization_data()->length() == 0);
|
| + Handle<FixedArray> deopt_data = factory->NewFixedArray(2, TENURED);
|
| + if (!js_object.is_null()) {
|
| + deopt_data->set(0, *js_object);
|
| + }
|
| + deopt_data->set(1, Smi::FromInt(static_cast<int>(i)));
|
| + deopt_data->set_length(2);
|
| + code->set_deoptimization_data(*deopt_data);
|
| + }
|
| +}
|
| +
|
| // 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
|
| @@ -613,9 +684,6 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
|
| ErrorThrower thrower(isolate, "WasmModule::Instantiate()");
|
| Factory* factory = isolate->factory();
|
|
|
| - PropertyDescriptor desc;
|
| - desc.set_writable(false);
|
| -
|
| // If FLAG_print_wasm_code_size is set, this aggregates the sum of all code
|
| // objects created for this module.
|
| // TODO(titzer): switch this to TRACE_EVENT
|
| @@ -667,7 +735,7 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
|
| isolate->counters()->wasm_compile_module_time());
|
|
|
| instance.function_table = BuildFunctionTable(isolate, this);
|
| - WasmLinker linker(isolate, functions.size());
|
| + WasmLinker linker(isolate, &instance.function_code);
|
| ModuleEnv module_env;
|
| module_env.module = this;
|
| module_env.instance = &instance;
|
| @@ -688,75 +756,25 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
|
| {
|
| 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.
|
| + CompileInParallel(isolate, this, instance.function_code, &thrower,
|
| + &module_env);
|
| + } else {
|
| // 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(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(executed_units, results, result_mutex);
|
| + CompileSequentially(isolate, this, instance.function_code, &thrower,
|
| + &module_env);
|
| }
|
| - // 5) The main thread finishes the compilation.
|
| - if (!FinishCompilation(isolate, this, ffi, results, instance, code_table,
|
| - thrower, factory, module_env, code_stats, desc)) {
|
| - instance.js_object = Handle<JSObject>::null();
|
| - return instance.js_object;
|
| + if (thrower.error()) {
|
| + return Handle<JSObject>::null();
|
| + }
|
| +
|
| + // At this point, compilation has completed. Update the code table
|
| + // and record sizes.
|
| + for (size_t i = FLAG_skip_compiling_wasm_funcs;
|
| + i < instance.function_code.size(); ++i) {
|
| + Code* code = *instance.function_code[i];
|
| + code_table->set(static_cast<int>(i), code);
|
| + code_stats.Record(code);
|
| }
|
|
|
| // Patch all direct call sites.
|
| @@ -764,6 +782,8 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
|
| instance.js_object->SetInternalField(kWasmModuleFunctionTable,
|
| Smi::FromInt(0));
|
|
|
| + SetDeoptimizationData(factory, instance.js_object, instance.function_code);
|
| +
|
| //-------------------------------------------------------------------------
|
| // Create and populate the exports object.
|
| //-------------------------------------------------------------------------
|
| @@ -782,12 +802,15 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
|
| exports_object = instance.js_object;
|
| }
|
|
|
| + PropertyDescriptor desc;
|
| + desc.set_writable(false);
|
| +
|
| // Compile wrappers and add them to the exports object.
|
| for (const WasmExport& exp : export_table) {
|
| if (thrower.error()) break;
|
| WasmName str = GetName(exp.name_offset, exp.name_length);
|
| Handle<String> name = factory->InternalizeUtf8String(str);
|
| - Handle<Code> code = linker.GetFunctionCode(exp.func_index);
|
| + Handle<Code> code = instance.function_code[exp.func_index];
|
| Handle<JSFunction> function = compiler::CompileJSToWasmWrapper(
|
| isolate, &module_env, name, code, instance.js_object,
|
| exp.func_index);
|
| @@ -826,7 +849,7 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
|
| HandleScope scope(isolate);
|
| uint32_t index = static_cast<uint32_t>(this->start_function_index);
|
| Handle<String> name = isolate->factory()->NewStringFromStaticChars("start");
|
| - Handle<Code> code = linker.GetFunctionCode(index);
|
| + Handle<Code> code = instance.function_code[index];
|
| Handle<JSFunction> jsfunc = compiler::CompileJSToWasmWrapper(
|
| isolate, &module_env, name, code, instance.js_object, index);
|
|
|
| @@ -842,10 +865,11 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
|
| return instance.js_object;
|
| }
|
|
|
| -Handle<Code> ModuleEnv::GetFunctionCode(uint32_t index) {
|
| +Handle<Code> ModuleEnv::GetCodeOrPlaceholder(uint32_t index) const {
|
| DCHECK(IsValidFunction(index));
|
| - if (linker) return linker->GetFunctionCode(index);
|
| - return instance ? instance->function_code[index] : Handle<Code>::null();
|
| + if (linker != nullptr) return linker->GetPlaceholderCode(index);
|
| + DCHECK_NOT_NULL(instance);
|
| + return instance->function_code[index];
|
| }
|
|
|
| Handle<Code> ModuleEnv::GetImportCode(uint32_t index) {
|
| @@ -907,7 +931,7 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const WasmModule* module) {
|
| instance.function_table = BuildFunctionTable(isolate, module);
|
|
|
| // Create module environment.
|
| - WasmLinker linker(isolate, module->functions.size());
|
| + WasmLinker linker(isolate, &instance.function_code);
|
| ModuleEnv module_env;
|
| module_env.module = module;
|
| module_env.instance = &instance;
|
| @@ -932,7 +956,7 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const WasmModule* module) {
|
|
|
| // Wrap the main code so it can be called as a JS function.
|
| uint32_t main_index = module->export_table.back().func_index;
|
| - Handle<Code> main_code = linker.GetFunctionCode(main_index);
|
| + Handle<Code> main_code = instance.function_code[main_index];
|
| Handle<String> name = isolate->factory()->NewStringFromStaticChars("main");
|
| Handle<JSObject> module_object = Handle<JSObject>(0, isolate);
|
| Handle<JSFunction> jsfunc = compiler::CompileJSToWasmWrapper(
|
|
|