Chromium Code Reviews| Index: src/wasm/wasm-module.cc |
| diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc |
| index 34ba2995f9b8f3ffb1102ee4eb2b88ca9635b697..37d3fa4db95d38d1a675ba08c17af9230ab63ab9 100644 |
| --- a/src/wasm/wasm-module.cc |
| +++ b/src/wasm/wasm-module.cc |
| @@ -76,6 +76,19 @@ bool EnableGuardRegions() { |
| return FLAG_wasm_guard_pages && kGuardRegionsSupported; |
| } |
| +static void RecordStats(Isolate* isolate, Code* code) { |
| + isolate->counters()->wasm_generated_code_size()->Increment(code->body_size()); |
| + isolate->counters()->wasm_reloc_size()->Increment( |
| + code->relocation_info()->length()); |
| +} |
| + |
| +static void RecordStats(Isolate* isolate, Handle<FixedArray> functions) { |
| + DisallowHeapAllocation no_gc; |
| + for (int i = 0; i < functions->length(); ++i) { |
| + RecordStats(isolate, Code::cast(functions->get(i))); |
| + } |
| +} |
| + |
| void* TryAllocateBackingStore(Isolate* isolate, size_t size, |
| bool enable_guard_regions, bool& is_external) { |
| is_external = false; |
| @@ -121,235 +134,415 @@ void FlushICache(Isolate* isolate, Handle<FixedArray> code_table) { |
| } |
| } |
| -// 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 AtomicIncrement returns the value after the atomic increment. |
| - size_t index = next_unit->Increment(1) - 1; |
| - if (index >= compilation_units->size()) { |
| - return false; |
| - } |
| +Handle<Script> CreateWasmScript(Isolate* isolate, |
| + const ModuleWireBytes& wire_bytes) { |
| + Handle<Script> script = |
| + isolate->factory()->NewScript(isolate->factory()->empty_string()); |
| + FixedArray* array = isolate->native_context()->embedder_data(); |
| + script->set_context_data(array->get(v8::Context::kDebugIdIndex)); |
| + script->set_type(Script::TYPE_WASM); |
| - compiler::WasmCompilationUnit* unit = compilation_units->at(index); |
| - if (unit != nullptr) { |
| - unit->ExecuteCompilation(); |
| - base::LockGuard<base::Mutex> guard(result_mutex); |
| - executed_units->push(unit); |
| - } |
| - return true; |
| + int hash = StringHasher::HashSequentialString( |
| + reinterpret_cast<const char*>(wire_bytes.start()), wire_bytes.length(), |
| + kZeroHashSeed); |
| + |
| + const int kBufferSize = 50; |
|
ahaas
2017/02/24 11:59:10
Nit, I think we could use a lower bound here.
|
| + 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; |
| } |
| -class WasmCompilationTask : public CancelableTask { |
| +class JSToWasmWrapperCache { |
| 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(); |
| + Handle<Code> CloneOrCompileJSToWasmWrapper(Isolate* isolate, |
| + const wasm::WasmModule* module, |
| + Handle<Code> 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) { |
| + it.rinfo()->set_target_address(wasm_code->instruction_start()); |
| + break; |
| + } |
| + } |
| + return code; |
| + } |
| + |
| + Handle<Code> code = |
| + compiler::CompileJSToWasmWrapper(isolate, module, wasm_code, index); |
| + uint32_t new_cache_idx = sig_map_.FindOrInsert(func->sig); |
| + DCHECK_EQ(code_cache_.size(), new_cache_idx); |
| + USE(new_cache_idx); |
| + code_cache_.push_back(code); |
| + return code; |
| } |
| - 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_; |
| + private: |
| + // sig_map_ maps signatures to an index in code_cache_. |
| + wasm::SignatureMap sig_map_; |
| + std::vector<Handle<Code>> code_cache_; |
| }; |
| -static void RecordStats(Isolate* isolate, Code* code) { |
| - isolate->counters()->wasm_generated_code_size()->Increment(code->body_size()); |
| - isolate->counters()->wasm_reloc_size()->Increment( |
| - code->relocation_info()->length()); |
| -} |
| - |
| -static void RecordStats(Isolate* isolate, Handle<FixedArray> functions) { |
| - DisallowHeapAllocation no_gc; |
| - for (int i = 0; i < functions->length(); ++i) { |
| - RecordStats(isolate, Code::cast(functions->get(i))); |
| - } |
| -} |
| +// A helper for compiling an entire module. |
| +class CompilationHelper { |
| + public: |
| + CompilationHelper(Isolate* isolate, WasmModule* module) |
| + : isolate_(isolate), module_(module) {} |
| + |
| + // The actual runnable task that performs compilations in the background. |
| + class CompilationTask : public CancelableTask { |
| + public: |
| + CompilationHelper* helper_; |
| + explicit CompilationTask(CompilationHelper* helper) |
| + : CancelableTask(helper->isolate_), helper_(helper) {} |
| + |
| + void RunInternal() override { |
| + while (helper_->FetchAndExecuteCompilationUnit()) { |
| + } |
| + helper_->module_->pending_tasks.get()->Signal(); |
| + } |
| + }; |
| -void InitializeParallelCompilation( |
| - Isolate* isolate, const std::vector<WasmFunction>& functions, |
| - std::vector<compiler::WasmCompilationUnit*>& compilation_units, |
| - ModuleBytesEnv& module_env, ErrorThrower* thrower) { |
| - for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size(); ++i) { |
| - const WasmFunction* func = &functions[i]; |
| - compilation_units[i] = |
| - func->imported ? nullptr : new compiler::WasmCompilationUnit( |
| - thrower, isolate, &module_env, func, i); |
| + Isolate* isolate_; |
| + WasmModule* module_; |
| + std::vector<compiler::WasmCompilationUnit*> compilation_units_; |
| + std::queue<compiler::WasmCompilationUnit*> executed_units_; |
| + base::Mutex result_mutex_; |
| + base::AtomicNumber<size_t> next_unit_; |
| + |
| + // Run by each compilation task and by the main thread. |
| + bool FetchAndExecuteCompilationUnit() { |
| + DisallowHeapAllocation no_allocation; |
| + DisallowHandleAllocation no_handles; |
| + DisallowHandleDereference no_deref; |
| + DisallowCodeDependencyChange no_dependency_change; |
| + |
| + // - 1 because AtomicIncrement 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) { |
| + unit->ExecuteCompilation(); |
| + base::LockGuard<base::Mutex> guard(&result_mutex_); |
| + executed_units_.push(unit); |
| + } |
| + return true; |
| + } |
| + |
| + void InitializeParallelCompilation(const std::vector<WasmFunction>& functions, |
| + ModuleBytesEnv& module_env, |
| + ErrorThrower* thrower) { |
| + compilation_units_.reserve(functions.size()); |
| + for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size(); |
| + ++i) { |
| + const WasmFunction* func = &functions[i]; |
| + compilation_units_.push_back( |
| + func->imported ? nullptr |
| + : new compiler::WasmCompilationUnit( |
| + thrower, isolate_, &module_env, func, i)); |
| + } |
| + } |
| + |
| + uint32_t* StartCompilationTasks() { |
| + 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) { |
| + CompilationTask* task = new CompilationTask(this); |
| + task_ids[i] = task->id(); |
| + V8::GetCurrentPlatform()->CallOnBackgroundThread( |
| + task, v8::Platform::kShortRunningTask); |
| + } |
| + return task_ids; |
| + } |
| + |
| + void WaitForCompilationTasks(uint32_t* task_ids) { |
| + 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]) != |
| + CancelableTaskManager::kTaskAborted) { |
| + module_->pending_tasks.get()->Wait(); |
| + } |
| + } |
| } |
| -} |
| - |
| -uint32_t* StartCompilationTasks( |
| - Isolate* isolate, |
| - std::vector<compiler::WasmCompilationUnit*>& compilation_units, |
| - std::queue<compiler::WasmCompilationUnit*>& executed_units, |
| - 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, &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, |
| - 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]) != |
| - CancelableTaskManager::kTaskAborted) { |
| - pending_tasks->Wait(); |
| + void FinishCompilationUnits(std::vector<Handle<Code>>& results) { |
| + while (true) { |
| + compiler::WasmCompilationUnit* unit = nullptr; |
| + { |
| + base::LockGuard<base::Mutex> guard(&result_mutex_); |
| + if (executed_units_.empty()) { |
| + break; |
| + } |
| + unit = executed_units_.front(); |
| + executed_units_.pop(); |
| + } |
| + int j = unit->index(); |
| + results[j] = unit->FinishCompilation(); |
| + delete unit; |
| } |
| } |
| -} |
| -void FinishCompilationUnits( |
| - std::queue<compiler::WasmCompilationUnit*>& executed_units, |
| - std::vector<Handle<Code>>& results, base::Mutex& result_mutex) { |
| - while (true) { |
| - compiler::WasmCompilationUnit* unit = nullptr; |
| - { |
| - base::LockGuard<base::Mutex> guard(&result_mutex); |
| - if (executed_units.empty()) { |
| + void CompileInParallel(ModuleBytesEnv* module_env, |
| + std::vector<Handle<Code>>& results, |
| + ErrorThrower* thrower) { |
| + const WasmModule* module = module_env->module_env.module; |
| + // Data structures for the parallel compilation. |
| + |
| + //----------------------------------------------------------------------- |
| + // 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 {CompilationTask} 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 {CompilationTask} 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(module->functions, *module_env, thrower); |
| + |
| + // Objects for the synchronization with the background threads. |
| + base::AtomicNumber<size_t> next_unit( |
| + static_cast<size_t>(FLAG_skip_compiling_wasm_funcs)); |
| + |
| + // 2) The main thread spawns {CompilationTask} instances which run on |
| + // the background threads. |
| + std::unique_ptr<uint32_t[]> task_ids(StartCompilationTasks()); |
| + |
| + // 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()) { |
| + // 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(results); |
| + } |
| + // 4) After the parallel phase of all compilation units has started, the |
| + // main thread waits for all {CompilationTask} instances to finish. |
| + WaitForCompilationTasks(task_ids.get()); |
| + // Finish the compilation of the remaining compilation units. |
| + FinishCompilationUnits(results); |
| + } |
| + |
| + void CompileSequentially(ModuleBytesEnv* module_env, |
| + std::vector<Handle<Code>>& results, |
| + ErrorThrower* thrower) { |
| + DCHECK(!thrower->error()); |
| + |
| + const WasmModule* module = module_env->module_env.module; |
| + for (uint32_t i = FLAG_skip_compiling_wasm_funcs; |
| + i < module->functions.size(); ++i) { |
| + const WasmFunction& func = module->functions[i]; |
| + if (func.imported) |
| + continue; // Imports are compiled at instantiation time. |
| + |
| + Handle<Code> code = Handle<Code>::null(); |
| + // Compile the function. |
| + code = compiler::WasmCompilationUnit::CompileWasmFunction( |
| + thrower, isolate_, module_env, &func); |
| + if (code.is_null()) { |
| + WasmName str = module_env->wire_bytes.GetName(&func); |
| + thrower->CompileError("Compilation of #%d:%.*s failed.", i, |
| + str.length(), str.start()); |
| break; |
| } |
| - unit = executed_units.front(); |
| - executed_units.pop(); |
| + results[i] = code; |
| } |
| - int j = unit->index(); |
| - results[j] = unit->FinishCompilation(); |
| - delete unit; |
| } |
| -} |
| -void CompileInParallel(Isolate* isolate, ModuleBytesEnv* module_env, |
| - std::vector<Handle<Code>>& functions, |
| - ErrorThrower* thrower) { |
| - const WasmModule* module = module_env->module_env.module; |
| - // 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::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. |
| - std::unique_ptr<uint32_t[]> task_ids(StartCompilationTasks( |
| - isolate, compilation_units, executed_units, module->pending_tasks.get(), |
| - 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(), module->pending_tasks.get()); |
| - // Finish the compilation of the remaining compilation units. |
| - FinishCompilationUnits(executed_units, functions, result_mutex); |
| -} |
| + MaybeHandle<WasmModuleObject> CompileToModuleObject( |
| + ErrorThrower* thrower, const ModuleWireBytes& wire_bytes, |
| + Handle<Script> asm_js_script, |
| + Vector<const byte> asm_js_offset_table_bytes) { |
| + Factory* factory = isolate_->factory(); |
| + // The {module_wrapper} will take ownership of the {WasmModule} object, |
| + // and it will be destroyed when the GC reclaims the wrapper object. |
| + Handle<WasmModuleWrapper> module_wrapper = |
| + WasmModuleWrapper::New(isolate_, module_); |
| + WasmInstance temp_instance(module_); |
| + temp_instance.context = isolate_->native_context(); |
| + temp_instance.mem_size = WasmModule::kPageSize * module_->min_mem_pages; |
| + temp_instance.mem_start = nullptr; |
| + temp_instance.globals_start = nullptr; |
| + |
| + // Initialize the indirect tables with placeholders. |
| + int function_table_count = |
| + static_cast<int>(module_->function_tables.size()); |
| + Handle<FixedArray> function_tables = |
| + factory->NewFixedArray(function_table_count, TENURED); |
| + Handle<FixedArray> signature_tables = |
| + factory->NewFixedArray(function_table_count, TENURED); |
| + for (int i = 0; i < function_table_count; ++i) { |
| + temp_instance.function_tables[i] = factory->NewFixedArray(1, TENURED); |
| + temp_instance.signature_tables[i] = factory->NewFixedArray(1, TENURED); |
| + function_tables->set(i, *temp_instance.function_tables[i]); |
| + signature_tables->set(i, *temp_instance.signature_tables[i]); |
| + } |
| + |
| + HistogramTimerScope wasm_compile_module_time_scope( |
| + isolate_->counters()->wasm_compile_module_time()); |
| + |
| + ModuleBytesEnv module_env(module_, &temp_instance, wire_bytes); |
| + |
| + // 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() + |
| + module_->num_exported_functions); |
| + Handle<FixedArray> code_table = |
| + factory->NewFixedArray(static_cast<int>(code_table_size), TENURED); |
| + |
| + // Initialize the code table with the illegal builtin. All call sites will |
| + // be |
| + // patched at instantiation. |
| + Handle<Code> illegal_builtin = isolate_->builtins()->Illegal(); |
| + for (uint32_t i = 0; i < module_->functions.size(); ++i) { |
| + code_table->set(static_cast<int>(i), *illegal_builtin); |
| + temp_instance.function_code[i] = illegal_builtin; |
| + } |
| + |
| + isolate_->counters()->wasm_functions_per_module()->AddSample( |
| + static_cast<int>(module_->functions.size())); |
| + CompilationHelper helper(isolate_, module_); |
| + if (!FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks != 0) { |
| + // Avoid a race condition by collecting results into a second vector. |
| + std::vector<Handle<Code>> results(temp_instance.function_code); |
| + helper.CompileInParallel(&module_env, results, thrower); |
| + temp_instance.function_code.swap(results); |
| + } else { |
| + helper.CompileSequentially(&module_env, temp_instance.function_code, |
| + thrower); |
| + } |
| + if (thrower->error()) return {}; |
| + |
| + // At this point, compilation has completed. Update the code table. |
| + for (size_t i = FLAG_skip_compiling_wasm_funcs; |
| + i < temp_instance.function_code.size(); ++i) { |
| + Code* code = *temp_instance.function_code[i]; |
| + code_table->set(static_cast<int>(i), code); |
| + RecordStats(isolate_, code); |
| + } |
| + |
| + // Create heap objects for script, module bytes and asm.js offset table to |
| + // be |
| + // stored in the shared module data. |
| + Handle<Script> script; |
| + Handle<ByteArray> asm_js_offset_table; |
| + if (asm_js_script.is_null()) { |
| + script = CreateWasmScript(isolate_, wire_bytes); |
| + } else { |
| + script = asm_js_script; |
| + asm_js_offset_table = |
| + isolate_->factory()->NewByteArray(asm_js_offset_table_bytes.length()); |
| + asm_js_offset_table->copy_in(0, asm_js_offset_table_bytes.start(), |
| + asm_js_offset_table_bytes.length()); |
| + } |
| + // TODO(wasm): only save the sections necessary to deserialize a |
| + // {WasmModule}. E.g. function bodies could be omitted. |
| + Handle<String> module_bytes = |
| + factory |
| + ->NewStringFromOneByte({wire_bytes.start(), wire_bytes.length()}, |
| + TENURED) |
| + .ToHandleChecked(); |
| + DCHECK(module_bytes->IsSeqOneByteString()); |
| + |
| + // Create the shared module data. |
| + // TODO(clemensh): For the same module (same bytes / same hash), we should |
| + // only have one WasmSharedModuleData. Otherwise, we might only set |
| + // breakpoints on a (potentially empty) subset of the instances. |
| + |
| + Handle<WasmSharedModuleData> shared = WasmSharedModuleData::New( |
| + isolate_, module_wrapper, Handle<SeqOneByteString>::cast(module_bytes), |
| + script, asm_js_offset_table); |
| + |
| + // 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 = |
| + WasmCompiledModule::New(isolate_, shared); |
| + compiled_module->set_num_imported_functions( |
| + module_->num_imported_functions); |
| + compiled_module->set_code_table(code_table); |
| + compiled_module->set_min_mem_pages(module_->min_mem_pages); |
| + compiled_module->set_max_mem_pages(module_->max_mem_pages); |
| + if (function_table_count > 0) { |
| + compiled_module->set_function_tables(function_tables); |
| + compiled_module->set_signature_tables(signature_tables); |
| + compiled_module->set_empty_function_tables(function_tables); |
| + } |
| + |
| + // If we created a wasm script, finish it now and make it public to the |
| + // debugger. |
| + if (asm_js_script.is_null()) { |
| + script->set_wasm_compiled_module(*compiled_module); |
| + isolate_->debug()->OnAfterCompile(script); |
| + } |
| + |
| + // Compile JS->WASM wrappers for exported functions. |
| + JSToWasmWrapperCache js_to_wasm_cache; |
| + int func_index = 0; |
| + for (auto exp : module_->export_table) { |
| + if (exp.kind != kExternalFunction) continue; |
| + Handle<Code> wasm_code(Code::cast(code_table->get(exp.index)), isolate_); |
| + Handle<Code> wrapper_code = |
| + js_to_wasm_cache.CloneOrCompileJSToWasmWrapper(isolate_, module_, |
| + wasm_code, exp.index); |
| + int export_index = |
| + static_cast<int>(module_->functions.size() + func_index); |
| + code_table->set(export_index, *wrapper_code); |
| + RecordStats(isolate_, *wrapper_code); |
| + func_index++; |
| + } |
| -void CompileSequentially(Isolate* isolate, ModuleBytesEnv* module_env, |
| - std::vector<Handle<Code>>& functions, |
| - ErrorThrower* thrower) { |
| - DCHECK(!thrower->error()); |
| - |
| - const WasmModule* module = module_env->module_env.module; |
| - for (uint32_t i = FLAG_skip_compiling_wasm_funcs; |
| - i < module->functions.size(); ++i) { |
| - const WasmFunction& func = module->functions[i]; |
| - if (func.imported) continue; // Imports are compiled at instantiation time. |
| - |
| - Handle<Code> code = Handle<Code>::null(); |
| - // Compile the function. |
| - code = compiler::WasmCompilationUnit::CompileWasmFunction( |
| - thrower, isolate, module_env, &func); |
| - if (code.is_null()) { |
| - WasmName str = module_env->wire_bytes.GetName(&func); |
| - thrower->CompileError("Compilation of #%d:%.*s failed.", i, str.length(), |
| - str.start()); |
| - break; |
| - } |
| - // Install the code into the linker table. |
| - functions[i] = code; |
| - } |
| + return WasmModuleObject::New(isolate_, compiled_module); |
| } |
| +}; |
| static void ResetCompiledModule(Isolate* isolate, WasmInstanceObject* owner, |
| WasmCompiledModule* compiled_module) { |
| @@ -544,78 +737,6 @@ std::pair<int, int> GetFunctionOffsetAndLength( |
| return {static_cast<int>(func.code_start_offset), |
| static_cast<int>(func.code_end_offset - func.code_start_offset)}; |
| } |
| - |
| -Handle<Script> CreateWasmScript(Isolate* isolate, |
| - const ModuleWireBytes& wire_bytes) { |
| - Handle<Script> script = |
| - isolate->factory()->NewScript(isolate->factory()->empty_string()); |
| - FixedArray* array = isolate->native_context()->embedder_data(); |
| - script->set_context_data(array->get(v8::Context::kDebugIdIndex)); |
| - script->set_type(Script::TYPE_WASM); |
| - |
| - int hash = StringHasher::HashSequentialString( |
| - reinterpret_cast<const char*>(wire_bytes.start()), wire_bytes.length(), |
| - kZeroHashSeed); |
| - |
| - const int kBufferSize = 50; |
| - 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; |
| -} |
| - |
| -class JSToWasmWrapperCache { |
| - public: |
| - Handle<Code> CloneOrCompileJSToWasmWrapper(Isolate* isolate, |
| - const wasm::WasmModule* module, |
| - Handle<Code> 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) { |
| - it.rinfo()->set_target_address(wasm_code->instruction_start()); |
| - break; |
| - } |
| - } |
| - return code; |
| - } |
| - |
| - Handle<Code> code = |
| - compiler::CompileJSToWasmWrapper(isolate, module, wasm_code, index); |
| - uint32_t new_cache_idx = sig_map_.FindOrInsert(func->sig); |
| - DCHECK_EQ(code_cache_.size(), new_cache_idx); |
| - USE(new_cache_idx); |
| - code_cache_.push_back(code); |
| - return code; |
| - } |
| - |
| - private: |
| - // sig_map_ maps signatures to an index in code_cache_. |
| - wasm::SignatureMap sig_map_; |
| - std::vector<Handle<Code>> code_cache_; |
| -}; |
| - |
| } // namespace |
| Handle<JSArrayBuffer> SetupArrayBuffer(Isolate* isolate, void* backing_store, |
| @@ -723,146 +844,6 @@ int wasm::GetFunctionCodeOffset(Handle<WasmCompiledModule> compiled_module, |
| WasmModule::WasmModule(Zone* owned) |
| : owned_zone(owned), pending_tasks(new base::Semaphore(0)) {} |
| -MaybeHandle<WasmModuleObject> CompileToModuleObject( |
| - Isolate* isolate, WasmModule* m, ErrorThrower* thrower, |
| - const ModuleWireBytes& wire_bytes, Handle<Script> asm_js_script, |
| - Vector<const byte> asm_js_offset_table_bytes) { |
| - Factory* factory = isolate->factory(); |
| - MaybeHandle<WasmModuleObject> nothing; |
| - // The {module_wrapper} will take ownership of the {WasmModule} object, |
| - // and it will be destroyed when the GC reclaims the wrapper object. |
| - Handle<WasmModuleWrapper> module_wrapper = WasmModuleWrapper::New(isolate, m); |
| - WasmInstance temp_instance(m); |
| - temp_instance.context = isolate->native_context(); |
| - temp_instance.mem_size = WasmModule::kPageSize * m->min_mem_pages; |
| - temp_instance.mem_start = nullptr; |
| - temp_instance.globals_start = nullptr; |
| - |
| - // Initialize the indirect tables with placeholders. |
| - int function_table_count = static_cast<int>(m->function_tables.size()); |
| - Handle<FixedArray> function_tables = |
| - factory->NewFixedArray(function_table_count, TENURED); |
| - Handle<FixedArray> signature_tables = |
| - factory->NewFixedArray(function_table_count, TENURED); |
| - for (int i = 0; i < function_table_count; ++i) { |
| - temp_instance.function_tables[i] = factory->NewFixedArray(1, TENURED); |
| - temp_instance.signature_tables[i] = factory->NewFixedArray(1, TENURED); |
| - function_tables->set(i, *temp_instance.function_tables[i]); |
| - signature_tables->set(i, *temp_instance.signature_tables[i]); |
| - } |
| - |
| - HistogramTimerScope wasm_compile_module_time_scope( |
| - isolate->counters()->wasm_compile_module_time()); |
| - |
| - ModuleBytesEnv module_env(m, &temp_instance, wire_bytes); |
| - |
| - // 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>(m->functions.size() + m->num_exported_functions); |
| - Handle<FixedArray> code_table = |
| - factory->NewFixedArray(static_cast<int>(code_table_size), TENURED); |
| - |
| - // Initialize the code table with the illegal builtin. All call sites will be |
| - // patched at instantiation. |
| - Handle<Code> illegal_builtin = isolate->builtins()->Illegal(); |
| - for (uint32_t i = 0; i < m->functions.size(); ++i) { |
| - code_table->set(static_cast<int>(i), *illegal_builtin); |
| - temp_instance.function_code[i] = illegal_builtin; |
| - } |
| - |
| - isolate->counters()->wasm_functions_per_module()->AddSample( |
| - static_cast<int>(m->functions.size())); |
| - if (!FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks != 0) { |
| - // Avoid a race condition by collecting results into a second vector. |
| - std::vector<Handle<Code>> results(temp_instance.function_code); |
| - CompileInParallel(isolate, &module_env, results, thrower); |
| - temp_instance.function_code.swap(results); |
| - } else { |
| - CompileSequentially(isolate, &module_env, temp_instance.function_code, |
| - thrower); |
| - } |
| - if (thrower->error()) return nothing; |
| - |
| - // At this point, compilation has completed. Update the code table. |
| - for (size_t i = FLAG_skip_compiling_wasm_funcs; |
| - i < temp_instance.function_code.size(); ++i) { |
| - Code* code = *temp_instance.function_code[i]; |
| - code_table->set(static_cast<int>(i), code); |
| - RecordStats(isolate, code); |
| - } |
| - |
| - // Create heap objects for script, module bytes and asm.js offset table to be |
| - // stored in the shared module data. |
| - Handle<Script> script; |
| - Handle<ByteArray> asm_js_offset_table; |
| - if (asm_js_script.is_null()) { |
| - script = CreateWasmScript(isolate, wire_bytes); |
| - } else { |
| - script = asm_js_script; |
| - asm_js_offset_table = |
| - isolate->factory()->NewByteArray(asm_js_offset_table_bytes.length()); |
| - asm_js_offset_table->copy_in(0, asm_js_offset_table_bytes.start(), |
| - asm_js_offset_table_bytes.length()); |
| - } |
| - // TODO(wasm): only save the sections necessary to deserialize a |
| - // {WasmModule}. E.g. function bodies could be omitted. |
| - Handle<String> module_bytes = |
| - factory |
| - ->NewStringFromOneByte({wire_bytes.start(), wire_bytes.length()}, |
| - TENURED) |
| - .ToHandleChecked(); |
| - DCHECK(module_bytes->IsSeqOneByteString()); |
| - |
| - // Create the shared module data. |
| - // TODO(clemensh): For the same module (same bytes / same hash), we should |
| - // only have one WasmSharedModuleData. Otherwise, we might only set |
| - // breakpoints on a (potentially empty) subset of the instances. |
| - |
| - Handle<WasmSharedModuleData> shared = WasmSharedModuleData::New( |
| - isolate, module_wrapper, Handle<SeqOneByteString>::cast(module_bytes), |
| - script, asm_js_offset_table); |
| - |
| - // 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 = |
| - WasmCompiledModule::New(isolate, shared); |
| - compiled_module->set_num_imported_functions(m->num_imported_functions); |
| - compiled_module->set_code_table(code_table); |
| - compiled_module->set_min_mem_pages(m->min_mem_pages); |
| - compiled_module->set_max_mem_pages(m->max_mem_pages); |
| - if (function_table_count > 0) { |
| - compiled_module->set_function_tables(function_tables); |
| - compiled_module->set_signature_tables(signature_tables); |
| - compiled_module->set_empty_function_tables(function_tables); |
| - } |
| - |
| - // If we created a wasm script, finish it now and make it public to the |
| - // debugger. |
| - if (asm_js_script.is_null()) { |
| - script->set_wasm_compiled_module(*compiled_module); |
| - isolate->debug()->OnAfterCompile(script); |
| - } |
| - |
| - // Compile JS->WASM wrappers for exported functions. |
| - JSToWasmWrapperCache js_to_wasm_cache; |
| - int func_index = 0; |
| - for (auto exp : m->export_table) { |
| - if (exp.kind != kExternalFunction) continue; |
| - Handle<Code> wasm_code(Code::cast(code_table->get(exp.index)), isolate); |
| - Handle<Code> wrapper_code = js_to_wasm_cache.CloneOrCompileJSToWasmWrapper( |
| - isolate, m, wasm_code, exp.index); |
| - int export_index = static_cast<int>(m->functions.size() + func_index); |
| - code_table->set(export_index, *wrapper_code); |
| - RecordStats(isolate, *wrapper_code); |
| - func_index++; |
| - } |
| - |
| - return WasmModuleObject::New(isolate, compiled_module); |
| -} |
| - |
| static WasmFunction* GetWasmFunctionForImportWrapper(Isolate* isolate, |
| Handle<Object> target) { |
| if (target->IsJSFunction()) { |
| @@ -982,14 +963,12 @@ class InstantiationHelper { |
| // Build an instance, in all of its glory. |
| MaybeHandle<WasmInstanceObject> Build() { |
| - MaybeHandle<WasmInstanceObject> nothing; |
| - |
| // Check that an imports argument was provided, if the module requires it. |
| // No point in continuing otherwise. |
| if (!module_->import_table.empty() && ffi_.is_null()) { |
| thrower_->TypeError( |
| "Imports argument must be present and must be an object"); |
| - return nothing; |
| + return {}; |
| } |
| HistogramTimerScope wasm_instantiate_module_time_scope( |
| @@ -1086,7 +1065,7 @@ class InstantiationHelper { |
| globals_ = global_buffer; |
| if (globals_.is_null()) { |
| thrower_->RangeError("Out of memory: wasm globals"); |
| - return nothing; |
| + return {}; |
| } |
| Address old_globals_start = nullptr; |
| if (!owner.is_null()) { |
| @@ -1116,7 +1095,7 @@ class InstantiationHelper { |
| // Process the imports for the module. |
| //-------------------------------------------------------------------------- |
| int num_imported_functions = ProcessImports(code_table, instance); |
| - if (num_imported_functions < 0) return nothing; |
| + if (num_imported_functions < 0) return {}; |
| //-------------------------------------------------------------------------- |
| // Process the initialization for the module's globals. |
| @@ -1145,7 +1124,7 @@ class InstantiationHelper { |
| memory_->has_guard_region()); |
| } else if (min_mem_pages > 0) { |
| memory_ = AllocateMemory(min_mem_pages); |
| - if (memory_.is_null()) return nothing; // failed to allocate memory |
| + if (memory_.is_null()) return {}; // failed to allocate memory |
| } |
| //-------------------------------------------------------------------------- |
| @@ -1159,7 +1138,7 @@ class InstantiationHelper { |
| if (!in_bounds(base, static_cast<uint32_t>(table_init.entries.size()), |
| table_size)) { |
| thrower_->LinkError("table initializer is out of bounds"); |
| - return nothing; |
| + return {}; |
| } |
| } |
| @@ -1172,7 +1151,7 @@ class InstantiationHelper { |
| ? 0 : static_cast<uint32_t>(memory_->byte_length()->Number()); |
| if (!in_bounds(base, seg.source_size, mem_size)) { |
| thrower_->LinkError("data segment is out of bounds"); |
| - return nothing; |
| + return {}; |
| } |
| } |
| @@ -1340,7 +1319,7 @@ class InstantiationHelper { |
| // chain. However, we need to set up everything before executing the |
| // start function, such that stack trace information can be generated |
| // correctly already in the start function. |
| - return nothing; |
| + return {}; |
| } |
| } |
| @@ -2613,7 +2592,6 @@ MaybeHandle<WasmModuleObject> wasm::SyncCompileTranslatedAsmJs( |
| Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes, |
| Handle<Script> asm_js_script, |
| Vector<const byte> asm_js_offset_table_bytes) { |
| - MaybeHandle<WasmModuleObject> nothing; |
| ModuleResult result = DecodeWasmModule(isolate, bytes.start(), bytes.end(), |
| false, kAsmJsOrigin); |
| @@ -2621,22 +2599,20 @@ MaybeHandle<WasmModuleObject> wasm::SyncCompileTranslatedAsmJs( |
| // TODO(titzer): use Result<std::unique_ptr<const WasmModule*>>? |
| if (result.val) delete result.val; |
| thrower->CompileFailed("Wasm decoding failed", result); |
| - return nothing; |
| + return {}; |
| } |
| - return CompileToModuleObject(isolate, const_cast<WasmModule*>(result.val), |
| - thrower, bytes, asm_js_script, |
| - asm_js_offset_table_bytes); |
| + CompilationHelper helper(isolate, const_cast<WasmModule*>(result.val)); |
| + return helper.CompileToModuleObject(thrower, bytes, asm_js_script, |
| + asm_js_offset_table_bytes); |
| } |
| MaybeHandle<WasmModuleObject> wasm::SyncCompile(Isolate* isolate, |
| ErrorThrower* thrower, |
| const ModuleWireBytes& bytes) { |
| - MaybeHandle<WasmModuleObject> nothing; |
| - |
| if (!IsWasmCodegenAllowed(isolate, isolate->native_context())) { |
| thrower->CompileError("Wasm code generation disallowed in this context"); |
| - return nothing; |
| + return {}; |
| } |
| ModuleResult result = |
| @@ -2644,12 +2620,12 @@ MaybeHandle<WasmModuleObject> wasm::SyncCompile(Isolate* isolate, |
| if (result.failed()) { |
| if (result.val) delete result.val; |
| thrower->CompileFailed("Wasm decoding failed", result); |
| - return nothing; |
| + return {}; |
| } |
| - return CompileToModuleObject(isolate, const_cast<WasmModule*>(result.val), |
| - thrower, bytes, Handle<Script>(), |
| - Vector<const byte>()); |
| + CompilationHelper helper(isolate, const_cast<WasmModule*>(result.val)); |
| + return helper.CompileToModuleObject(thrower, bytes, Handle<Script>(), |
| + Vector<const byte>()); |
| } |
| MaybeHandle<WasmInstanceObject> wasm::SyncInstantiate( |