| Index: src/wasm/wasm-module.cc
|
| diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc
|
| index 34ba2995f9b8f3ffb1102ee4eb2b88ca9635b697..5b24d7702e8f13f5232a9706c1ccb6e7c96b90a0 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 = 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;
|
| }
|
|
|
| -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(
|
|
|