GetOrCompileJSToWasmWrapper(Isolate* isolate, const NativeModule* native_module, uint32_t func_index, UseTrapHandler use_trap_handler) { const WasmModule* module = native_module->module(); const WasmFunction* func = &module->functions[func_index]; bool is_import = func_index < module->num_imported_functions; std::pair key(is_import, *func->sig); Handle& cached = cache_[key]; if (!cached.is_null()) return cached; Handle code = compiler::CompileJSToWasmWrapper(isolate, native_module, func->sig, is_import, use_trap_handler) .ToHandleChecked(); cached = code; return code; } private: // We generate different code for calling imports than calling wasm functions // in this module. Both are cached separately. using CacheKey = std::pair; std::unordered_map, base::hash> cache_; }; // A helper class to simplify instantiating a module from a module object. // It closes over the {Isolate}, the {ErrorThrower}, etc. class InstanceBuilder { public: InstanceBuilder(Isolate* isolate, ErrorThrower* thrower, Handle module_object, MaybeHandle ffi, MaybeHandle memory); // Build an instance, in all of its glory. MaybeHandle Build(); // Run the start function, if any. bool ExecuteStartFunction(); private: // Represents the initialized state of a table. struct TableInstance { Handle table_object; // WebAssembly.Table instance Handle js_wrappers; // JSFunctions exported size_t table_size; }; // A pre-evaluated value to use in import binding. struct SanitizedImport { Handle module_name; Handle import_name; Handle value; }; Isolate* isolate_; const WasmFeatures enabled_; const WasmModule* const module_; ErrorThrower* thrower_; Handle module_object_; MaybeHandle ffi_; MaybeHandle memory_; Handle globals_; std::vector table_instances_; std::vector> js_wrappers_; Handle start_function_; JSToWasmWrapperCache js_to_wasm_cache_; std::vector sanitized_imports_; UseTrapHandler use_trap_handler() const { return module_object_->native_module()->use_trap_handler() ? kUseTrapHandler : kNoTrapHandler; } // Helper routines to print out errors with imports. #define ERROR_THROWER_WITH_MESSAGE(TYPE) \ void Report##TYPE(const char* error, uint32_t index, \ Handle module_name, Handle import_name) { \ thrower_->TYPE("Import #%d module=\"%s\" function=\"%s\" error: %s", \ index, module_name->ToCString().get(), \ import_name->ToCString().get(), error); \ } \ \ MaybeHandle Report##TYPE(const char* error, uint32_t index, \ Handle module_name) { \ thrower_->TYPE("Import #%d module=\"%s\" error: %s", index, \ module_name->ToCString().get(), error); \ return MaybeHandle(); \ } ERROR_THROWER_WITH_MESSAGE(LinkError) ERROR_THROWER_WITH_MESSAGE(TypeError) #undef ERROR_THROWER_WITH_MESSAGE // Look up an import value in the {ffi_} object. MaybeHandle LookupImport(uint32_t index, Handle module_name, Handle import_name); // Look up an import value in the {ffi_} object specifically for linking an // asm.js module. This only performs non-observable lookups, which allows // falling back to JavaScript proper (and hence re-executing all lookups) if // module instantiation fails. MaybeHandle LookupImportAsm(uint32_t index, Handle import_name); uint32_t EvalUint32InitExpr(const WasmInitExpr& expr); // Load data segments into the memory. void LoadDataSegments(Handle instance); void WriteGlobalValue(const WasmGlobal& global, double value); void WriteGlobalValue(const WasmGlobal& global, Handle value); void SanitizeImports(); // Find the imported memory buffer if there is one. This is used to see if we // need to recompile with bounds checks before creating the instance. MaybeHandle FindImportedMemoryBuffer() const; // Process the imports, including functions, tables, globals, and memory, in // order, loading them from the {ffi_} object. Returns the number of imported // functions. int ProcessImports(Handle instance); template T* GetRawGlobalPtr(const WasmGlobal& global); // Process initialization of globals. void InitGlobals(); // Allocate memory for a module instance as a new JSArrayBuffer. Handle AllocateMemory(uint32_t num_pages); bool NeedsWrappers() const; // Process the exports, creating wrappers for functions, tables, memories, // and globals. void ProcessExports(Handle instance); void InitializeTables(Handle instance); void LoadTableSegments(Handle instance); }; } // namespace MaybeHandle InstantiateToInstanceObject( Isolate* isolate, ErrorThrower* thrower, Handle module_object, MaybeHandle imports, MaybeHandle memory) { InstanceBuilder builder(isolate, thrower, module_object, imports, memory); auto instance = builder.Build(); if (!instance.is_null() && builder.ExecuteStartFunction()) { return instance; } return {}; } WasmCode* LazyCompileFunction(Isolate* isolate, NativeModule* native_module, int func_index) { base::ElapsedTimer compilation_timer; DCHECK(!native_module->has_code(static_cast(func_index))); compilation_timer.Start(); ModuleEnv* module_env = native_module->compilation_state()->module_env(); // TODO(wasm): Refactor this to only get the name if it is really needed for // tracing / debugging. WasmName func_name; { ModuleWireBytes wire_bytes(native_module->wire_bytes()); WireBytesRef name_ref = module_env->module->LookupFunctionName(wire_bytes, func_index); func_name = wire_bytes.GetName(name_ref); } TRACE_LAZY("Compiling function '%.*s' (#%d).\n", func_name.length(), func_name.start(), func_index); const uint8_t* module_start = native_module->wire_bytes().start(); const WasmFunction* func = &module_env->module->functions[func_index]; FunctionBody body{func->sig, func->code.offset(), module_start + func->code.offset(), module_start + func->code.end_offset()}; ErrorThrower thrower(isolate, "WasmLazyCompile"); WasmCompilationUnit unit(isolate->wasm_engine(), module_env, native_module, body, func_name, func_index, isolate->counters()); unit.ExecuteCompilation( native_module->compilation_state()->detected_features()); WasmCode* wasm_code = unit.FinishCompilation(&thrower); if (WasmCode::ShouldBeLogged(isolate)) wasm_code->LogCode(isolate); // If there is a pending error, something really went wrong. The module was // verified before starting execution with lazy compilation. // This might be OOM, but then we cannot continue execution anyway. // TODO(clemensh): According to the spec, we can actually skip validation at // module creation time, and return a function that always traps here. CHECK(!thrower.error()); int64_t func_size = static_cast(func->code.end_offset() - func->code.offset()); int64_t compilation_time = compilation_timer.Elapsed().InMicroseconds(); auto counters = isolate->counters(); counters->wasm_lazily_compiled_functions()->Increment(); counters->wasm_lazy_compilation_throughput()->AddSample( compilation_time != 0 ? static_cast(func_size / compilation_time) : 0); return wasm_code; } Address CompileLazy(Isolate* isolate, NativeModule* native_module, uint32_t func_index) { HistogramTimerScope lazy_time_scope( isolate->counters()->wasm_lazy_compilation_time()); DCHECK(!native_module->lazy_compile_frozen()); NativeModuleModificationScope native_module_modification_scope(native_module); WasmCode* result = LazyCompileFunction(isolate, native_module, func_index); DCHECK_NOT_NULL(result); DCHECK_EQ(func_index, result->index()); return result->instruction_start(); } namespace { bool compile_lazy(const WasmModule* module) { return FLAG_wasm_lazy_compilation || (FLAG_asm_wasm_lazy_compilation && module->origin == kAsmJsOrigin); } byte* raw_buffer_ptr(MaybeHandle buffer, int offset) { return static_cast(buffer.ToHandleChecked()->backing_store()) + offset; } void RecordStats(const Code* code, Counters* counters) { counters->wasm_generated_code_size()->Increment(code->body_size()); counters->wasm_reloc_size()->Increment(code->relocation_info()->length()); } bool in_bounds(uint32_t offset, size_t size, size_t upper) { return offset + size <= upper && offset + size >= offset; } using WasmInstanceMap = IdentityMap, FreeStoreAllocationPolicy>; double MonotonicallyIncreasingTimeInMs() { return V8::GetCurrentPlatform()->MonotonicallyIncreasingTime() * base::Time::kMillisecondsPerSecond; } ModuleEnv CreateDefaultModuleEnv(const WasmModule* module, bool allow_trap_handler = true) { UseTrapHandler use_trap_handler = trap_handler::IsTrapHandlerEnabled() && allow_trap_handler ? kUseTrapHandler : kNoTrapHandler; return ModuleEnv(module, use_trap_handler, kRuntimeExceptionSupport); } // The CompilationUnitBuilder builds compilation units and stores them in an // internal buffer. The buffer is moved into the working queue of the // CompilationState when {Commit} is called. class CompilationUnitBuilder { public: explicit CompilationUnitBuilder(NativeModule* native_module) : native_module_(native_module), compilation_state_(native_module->compilation_state()) {} void AddUnit(const WasmFunction* function, uint32_t buffer_offset, Vector bytes, WasmName name) { switch (compilation_state_->compile_mode()) { case CompileMode::kTiering: tiering_units_.emplace_back(CreateUnit( function, buffer_offset, bytes, name, ExecutionTier::kOptimized)); baseline_units_.emplace_back(CreateUnit( function, buffer_offset, bytes, name, ExecutionTier::kBaseline)); return; case CompileMode::kRegular: baseline_units_.emplace_back( CreateUnit(function, buffer_offset, bytes, name, WasmCompilationUnit::GetDefaultExecutionTier())); return; } UNREACHABLE(); } bool Commit() { if (baseline_units_.empty() && tiering_units_.empty()) return false; compilation_state_->AddCompilationUnits(baseline_units_, tiering_units_); Clear(); return true; } void Clear() { baseline_units_.clear(); tiering_units_.clear(); } private: std::unique_ptr CreateUnit(const WasmFunction* function, uint32_t buffer_offset, Vector bytes, WasmName name, ExecutionTier mode) { return base::make_unique( compilation_state_->wasm_engine(), compilation_state_->module_env(), native_module_, FunctionBody{function->sig, buffer_offset, bytes.begin(), bytes.end()}, name, function->func_index, compilation_state_->isolate()->async_counters().get(), mode); } NativeModule* native_module_; CompilationState* compilation_state_; std::vector> baseline_units_; std::vector> tiering_units_; }; // Run by each compilation task and by the main thread (i.e. in both // foreground and background threads). The no_finisher_callback is called // within the result_mutex_ lock when no finishing task is running, i.e. when // the finisher_is_running_ flag is not set. bool FetchAndExecuteCompilationUnit(CompilationState* compilation_state, WasmFeatures* detected) { DisallowHeapAccess no_heap_access; std::unique_ptr unit = compilation_state->GetNextCompilationUnit(); if (unit == nullptr) return false; // TODO(kimanh): We need to find out in which mode the unit // should be compiled in before compiling it, as it might fallback // to Turbofan if it cannot be compiled using Liftoff. This can be removed // later as soon as Liftoff can compile any function. Then, we can directly // access {unit->mode()} within {ScheduleUnitForFinishing()}. ExecutionTier mode = unit->mode(); unit->ExecuteCompilation(detected); compilation_state->ScheduleUnitForFinishing(std::move(unit), mode); return true; } void InitializeCompilationUnits(NativeModule* native_module) { ModuleWireBytes wire_bytes(native_module->wire_bytes()); const WasmModule* module = native_module->module(); CompilationUnitBuilder builder(native_module); uint32_t start = module->num_imported_functions; uint32_t end = start + module->num_declared_functions; for (uint32_t i = start; i < end; ++i) { const WasmFunction* func = &module->functions[i]; uint32_t buffer_offset = func->code.offset(); Vector bytes(wire_bytes.start() + func->code.offset(), func->code.end_offset() - func->code.offset()); WasmName name = wire_bytes.GetName(func, module); DCHECK_NOT_NULL(native_module); builder.AddUnit(func, buffer_offset, bytes, name); } builder.Commit(); } void FinishCompilationUnits(CompilationState* compilation_state, ErrorThrower* thrower) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "FinishCompilationUnits"); while (true) { if (compilation_state->failed()) break; std::unique_ptr unit = compilation_state->GetNextExecutedUnit(); if (unit == nullptr) break; WasmCode* result = unit->FinishCompilation(thrower); if (thrower->error()) { compilation_state->Abort(); break; } // Update the compilation state. compilation_state->OnFinishedUnit(); DCHECK_IMPLIES(result == nullptr, thrower->error()); if (result == nullptr) break; } if (!compilation_state->failed()) { compilation_state->RestartBackgroundTasks(); } } void CompileInParallel(Isolate* isolate, NativeModule* native_module, Handle module_object, ErrorThrower* thrower) { // 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} within the // {compilation_state}. By adding units to the {compilation_state}, new // {BackgroundCompileTasks} instances are spawned which run on // the background threads. // 2.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 {baseline_finish_units_}. // 2.b) If {baseline_finish_units_} contains a compilation unit, the main // thread dequeues it and finishes the compilation. // 3) After the parallel phase of all compilation units has started, the // main thread continues to finish all compilation units as long as // baseline-compilation units are left to be processed. // 4) If tier-up is enabled, the main thread restarts background tasks // that take care of compiling and finishing the top-tier compilation // units. // Turn on the {CanonicalHandleScope} so that the background threads can // use the node cache. CanonicalHandleScope canonical(isolate); CompilationState* compilation_state = native_module->compilation_state(); // Make sure that no foreground task is spawned for finishing // the compilation units. This foreground thread will be // responsible for finishing compilation. compilation_state->SetFinisherIsRunning(true); uint32_t num_wasm_functions = native_module->num_functions() - native_module->num_imported_functions(); compilation_state->SetNumberOfFunctionsToCompile(num_wasm_functions); // 1) The main thread allocates a compilation unit for each wasm function // and stores them in the vector {compilation_units} within the // {compilation_state}. By adding units to the {compilation_state}, new // {BackgroundCompileTask} instances are spawned which run on // background threads. InitializeCompilationUnits(native_module); // 2.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 {baseline_finish_units_}. // The foreground task bypasses waiting on memory threshold, because // its results will immediately be converted to code (below). WasmFeatures detected_features; while ( FetchAndExecuteCompilationUnit(compilation_state, &detected_features) && !compilation_state->baseline_compilation_finished()) { // 2.b) If {baseline_finish_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(compilation_state, thrower); if (compilation_state->failed()) break; } while (!compilation_state->failed()) { // 3) After the parallel phase of all compilation units has started, the // main thread continues to finish compilation units as long as // baseline compilation units are left to be processed. If compilation // already failed, all background tasks have already been canceled // in {FinishCompilationUnits}, and there are no units to finish. FinishCompilationUnits(compilation_state, thrower); if (compilation_state->baseline_compilation_finished()) break; } // Publish features from the foreground and background tasks. compilation_state->PublishDetectedFeatures(isolate, detected_features); // 4) If tiering-compilation is enabled, we need to set the finisher // to false, such that the background threads will spawn a foreground // thread to finish the top-tier compilation units. if (!compilation_state->failed() && compilation_state->compile_mode() == CompileMode::kTiering) { compilation_state->SetFinisherIsRunning(false); compilation_state->RestartBackgroundTasks(); } } void CompileSequentially(Isolate* isolate, NativeModule* native_module, ModuleEnv* module_env, ErrorThrower* thrower) { DCHECK(!thrower->error()); ModuleWireBytes wire_bytes(native_module->wire_bytes()); const WasmModule* module = module_env->module; WasmFeatures detected = kNoWasmFeatures; for (uint32_t i = 0; i < module->functions.size(); ++i) { const WasmFunction& func = module->functions[i]; if (func.imported) continue; // Imports are compiled at instantiation time. // Compile the function. WasmCode* code = WasmCompilationUnit::CompileWasmFunction( isolate, native_module, &detected, thrower, module_env, &func); if (code == nullptr) { TruncatedUserString<> name(wire_bytes.GetName(&func, module)); thrower->CompileError("Compilation of #%d:%.*s failed.", i, name.length(), name.start()); break; } } UpdateFeatureUseCounts(isolate, detected); } void ValidateSequentially(Isolate* isolate, NativeModule* native_module, ErrorThrower* thrower) { DCHECK(!thrower->error()); ModuleWireBytes wire_bytes(native_module->wire_bytes()); const WasmModule* module = native_module->module(); uint32_t start = module->num_imported_functions; uint32_t end = start + module->num_declared_functions; for (uint32_t i = start; i < end; ++i) { const WasmFunction& func = module->functions[i]; const byte* base = wire_bytes.start(); FunctionBody body{func.sig, func.code.offset(), base + func.code.offset(), base + func.code.end_offset()}; DecodeResult result; { auto time_counter = SELECT_WASM_COUNTER(isolate->async_counters(), module->origin, wasm_decode, function_time); TimedHistogramScope wasm_decode_function_time_scope(time_counter); WasmFeatures detected; result = VerifyWasmCode(isolate->allocator(), native_module->enabled_features(), module, &detected, body); } if (result.failed()) { TruncatedUserString<> name(wire_bytes.GetName(&func, module)); thrower->CompileError("Compiling function #%d:%.*s failed: %s @+%u", i, name.length(), name.start(), result.error_msg().c_str(), result.error_offset()); break; } } } void CompileNativeModule(Isolate* isolate, ErrorThrower* thrower, Handle module_object, const WasmModule* wasm_module, ModuleEnv* env) { NativeModule* const native_module = module_object->native_module(); ModuleWireBytes wire_bytes(native_module->wire_bytes()); if (compile_lazy(wasm_module)) { if (wasm_module->origin == kWasmOrigin) { // Validate wasm modules for lazy compilation. Don't validate asm.js // modules, they are valid by construction (otherwise a CHECK will fail // during lazy compilation). // TODO(clemensh): According to the spec, we can actually skip validation // at module creation time, and return a function that always traps at // (lazy) compilation time. ValidateSequentially(isolate, native_module, thrower); if (thrower->error()) return; } native_module->SetLazyBuiltin(BUILTIN_CODE(isolate, WasmCompileLazy)); } else { size_t funcs_to_compile = wasm_module->functions.size() - wasm_module->num_imported_functions; bool compile_parallel = !FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks > 0 && funcs_to_compile > 1 && V8::GetCurrentPlatform()->NumberOfWorkerThreads() > 0; if (compile_parallel) { CompileInParallel(isolate, native_module, module_object, thrower); } else { CompileSequentially(isolate, native_module, env, thrower); } if (thrower->error()) return; } } // The runnable task that finishes compilation in foreground (e.g. updating // the NativeModule, the code table, etc.). class FinishCompileTask : public CancelableTask { public: explicit FinishCompileTask(CompilationState* compilation_state, CancelableTaskManager* task_manager) : CancelableTask(task_manager), compilation_state_(compilation_state) {} void RunInternal() override { Isolate* isolate = compilation_state_->isolate(); HandleScope scope(isolate); SaveContext saved_context(isolate); isolate->set_context(nullptr); TRACE_COMPILE("(4a) Finishing compilation units...\n"); if (compilation_state_->failed()) { compilation_state_->SetFinisherIsRunning(false); return; } // We execute for 1 ms and then reschedule the task, same as the GC. double deadline = MonotonicallyIncreasingTimeInMs() + 1.0; while (true) { compilation_state_->RestartBackgroundTasks(); std::unique_ptr unit = compilation_state_->GetNextExecutedUnit(); if (unit == nullptr) { // It might happen that a background task just scheduled a unit to be // finished, but did not start a finisher task since the flag was still // set. Check for this case, and continue if there is more work. compilation_state_->SetFinisherIsRunning(false); if (compilation_state_->HasCompilationUnitToFinish() && compilation_state_->SetFinisherIsRunning(true)) { continue; } break; } ErrorThrower thrower(compilation_state_->isolate(), "AsyncCompile"); WasmCode* result = unit->FinishCompilation(&thrower); if (thrower.error()) { DCHECK_NULL(result); compilation_state_->OnError(&thrower); compilation_state_->SetFinisherIsRunning(false); thrower.Reset(); break; } if (compilation_state_->baseline_compilation_finished()) { // If Liftoff compilation finishes it will directly start executing. // As soon as we have Turbofan-compiled code available, it will // directly be used by Liftoff-compiled code via the jump table. DCHECK_EQ(CompileMode::kTiering, compilation_state_->compile_mode()); DCHECK(!result->is_liftoff()); if (WasmCode::ShouldBeLogged(isolate)) result->LogCode(isolate); } // Update the compilation state, and possibly notify // threads waiting for events. compilation_state_->OnFinishedUnit(); if (deadline < MonotonicallyIncreasingTimeInMs()) { // We reached the deadline. We reschedule this task and return // immediately. Since we rescheduled this task already, we do not set // the FinisherIsRunning flag to false. compilation_state_->ScheduleFinisherTask(); return; } } } private: CompilationState* compilation_state_; }; // The runnable task that performs compilations in the background. class BackgroundCompileTask : public CancelableTask { public: explicit BackgroundCompileTask(CompilationState* compilation_state, CancelableTaskManager* task_manager) : CancelableTask(task_manager), compilation_state_(compilation_state) {} void RunInternal() override { TRACE_COMPILE("(3b) Compiling...\n"); // The number of currently running background tasks is reduced in // {OnBackgroundTaskStopped}. while (!compilation_state_->failed()) { if (!FetchAndExecuteCompilationUnit(compilation_state_, &detected_features_)) { break; } } compilation_state_->OnBackgroundTaskStopped(detected_features_); } private: CompilationState* compilation_state_; WasmFeatures detected_features_ = kNoWasmFeatures; }; } // namespace MaybeHandle CompileToModuleObject( Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower, std::shared_ptr module, const ModuleWireBytes& wire_bytes, Handle 登录后可以享受更多权益 您还没有登录,登录后您可以: 收藏Android系统代码 收藏喜欢的文章 多个平台共享账号 去登录 首次使用?从这里 注册
& cached = cache_[key]; if (!cached.is_null()) return cached; Handle code = compiler::CompileJSToWasmWrapper(isolate, native_module, func->sig, is_import, use_trap_handler) .ToHandleChecked(); cached = code; return code; } private: // We generate different code for calling imports than calling wasm functions // in this module. Both are cached separately. using CacheKey = std::pair; std::unordered_map, base::hash> cache_; }; // A helper class to simplify instantiating a module from a module object. // It closes over the {Isolate}, the {ErrorThrower}, etc. class InstanceBuilder { public: InstanceBuilder(Isolate* isolate, ErrorThrower* thrower, Handle module_object, MaybeHandle ffi, MaybeHandle memory); // Build an instance, in all of its glory. MaybeHandle Build(); // Run the start function, if any. bool ExecuteStartFunction(); private: // Represents the initialized state of a table. struct TableInstance { Handle table_object; // WebAssembly.Table instance Handle js_wrappers; // JSFunctions exported size_t table_size; }; // A pre-evaluated value to use in import binding. struct SanitizedImport { Handle module_name; Handle import_name; Handle value; }; Isolate* isolate_; const WasmFeatures enabled_; const WasmModule* const module_; ErrorThrower* thrower_; Handle module_object_; MaybeHandle ffi_; MaybeHandle memory_; Handle globals_; std::vector table_instances_; std::vector> js_wrappers_; Handle start_function_; JSToWasmWrapperCache js_to_wasm_cache_; std::vector sanitized_imports_; UseTrapHandler use_trap_handler() const { return module_object_->native_module()->use_trap_handler() ? kUseTrapHandler : kNoTrapHandler; } // Helper routines to print out errors with imports. #define ERROR_THROWER_WITH_MESSAGE(TYPE) \ void Report##TYPE(const char* error, uint32_t index, \ Handle module_name, Handle import_name) { \ thrower_->TYPE("Import #%d module=\"%s\" function=\"%s\" error: %s", \ index, module_name->ToCString().get(), \ import_name->ToCString().get(), error); \ } \ \ MaybeHandle Report##TYPE(const char* error, uint32_t index, \ Handle module_name) { \ thrower_->TYPE("Import #%d module=\"%s\" error: %s", index, \ module_name->ToCString().get(), error); \ return MaybeHandle(); \ } ERROR_THROWER_WITH_MESSAGE(LinkError) ERROR_THROWER_WITH_MESSAGE(TypeError) #undef ERROR_THROWER_WITH_MESSAGE // Look up an import value in the {ffi_} object. MaybeHandle LookupImport(uint32_t index, Handle module_name, Handle import_name); // Look up an import value in the {ffi_} object specifically for linking an // asm.js module. This only performs non-observable lookups, which allows // falling back to JavaScript proper (and hence re-executing all lookups) if // module instantiation fails. MaybeHandle LookupImportAsm(uint32_t index, Handle import_name); uint32_t EvalUint32InitExpr(const WasmInitExpr& expr); // Load data segments into the memory. void LoadDataSegments(Handle instance); void WriteGlobalValue(const WasmGlobal& global, double value); void WriteGlobalValue(const WasmGlobal& global, Handle value); void SanitizeImports(); // Find the imported memory buffer if there is one. This is used to see if we // need to recompile with bounds checks before creating the instance. MaybeHandle FindImportedMemoryBuffer() const; // Process the imports, including functions, tables, globals, and memory, in // order, loading them from the {ffi_} object. Returns the number of imported // functions. int ProcessImports(Handle instance); template T* GetRawGlobalPtr(const WasmGlobal& global); // Process initialization of globals. void InitGlobals(); // Allocate memory for a module instance as a new JSArrayBuffer. Handle AllocateMemory(uint32_t num_pages); bool NeedsWrappers() const; // Process the exports, creating wrappers for functions, tables, memories, // and globals. void ProcessExports(Handle instance); void InitializeTables(Handle instance); void LoadTableSegments(Handle instance); }; } // namespace MaybeHandle InstantiateToInstanceObject( Isolate* isolate, ErrorThrower* thrower, Handle module_object, MaybeHandle imports, MaybeHandle memory) { InstanceBuilder builder(isolate, thrower, module_object, imports, memory); auto instance = builder.Build(); if (!instance.is_null() && builder.ExecuteStartFunction()) { return instance; } return {}; } WasmCode* LazyCompileFunction(Isolate* isolate, NativeModule* native_module, int func_index) { base::ElapsedTimer compilation_timer; DCHECK(!native_module->has_code(static_cast(func_index))); compilation_timer.Start(); ModuleEnv* module_env = native_module->compilation_state()->module_env(); // TODO(wasm): Refactor this to only get the name if it is really needed for // tracing / debugging. WasmName func_name; { ModuleWireBytes wire_bytes(native_module->wire_bytes()); WireBytesRef name_ref = module_env->module->LookupFunctionName(wire_bytes, func_index); func_name = wire_bytes.GetName(name_ref); } TRACE_LAZY("Compiling function '%.*s' (#%d).\n", func_name.length(), func_name.start(), func_index); const uint8_t* module_start = native_module->wire_bytes().start(); const WasmFunction* func = &module_env->module->functions[func_index]; FunctionBody body{func->sig, func->code.offset(), module_start + func->code.offset(), module_start + func->code.end_offset()}; ErrorThrower thrower(isolate, "WasmLazyCompile"); WasmCompilationUnit unit(isolate->wasm_engine(), module_env, native_module, body, func_name, func_index, isolate->counters()); unit.ExecuteCompilation( native_module->compilation_state()->detected_features()); WasmCode* wasm_code = unit.FinishCompilation(&thrower); if (WasmCode::ShouldBeLogged(isolate)) wasm_code->LogCode(isolate); // If there is a pending error, something really went wrong. The module was // verified before starting execution with lazy compilation. // This might be OOM, but then we cannot continue execution anyway. // TODO(clemensh): According to the spec, we can actually skip validation at // module creation time, and return a function that always traps here. CHECK(!thrower.error()); int64_t func_size = static_cast(func->code.end_offset() - func->code.offset()); int64_t compilation_time = compilation_timer.Elapsed().InMicroseconds(); auto counters = isolate->counters(); counters->wasm_lazily_compiled_functions()->Increment(); counters->wasm_lazy_compilation_throughput()->AddSample( compilation_time != 0 ? static_cast(func_size / compilation_time) : 0); return wasm_code; } Address CompileLazy(Isolate* isolate, NativeModule* native_module, uint32_t func_index) { HistogramTimerScope lazy_time_scope( isolate->counters()->wasm_lazy_compilation_time()); DCHECK(!native_module->lazy_compile_frozen()); NativeModuleModificationScope native_module_modification_scope(native_module); WasmCode* result = LazyCompileFunction(isolate, native_module, func_index); DCHECK_NOT_NULL(result); DCHECK_EQ(func_index, result->index()); return result->instruction_start(); } namespace { bool compile_lazy(const WasmModule* module) { return FLAG_wasm_lazy_compilation || (FLAG_asm_wasm_lazy_compilation && module->origin == kAsmJsOrigin); } byte* raw_buffer_ptr(MaybeHandle buffer, int offset) { return static_cast(buffer.ToHandleChecked()->backing_store()) + offset; } void RecordStats(const Code* code, Counters* counters) { counters->wasm_generated_code_size()->Increment(code->body_size()); counters->wasm_reloc_size()->Increment(code->relocation_info()->length()); } bool in_bounds(uint32_t offset, size_t size, size_t upper) { return offset + size <= upper && offset + size >= offset; } using WasmInstanceMap = IdentityMap, FreeStoreAllocationPolicy>; double MonotonicallyIncreasingTimeInMs() { return V8::GetCurrentPlatform()->MonotonicallyIncreasingTime() * base::Time::kMillisecondsPerSecond; } ModuleEnv CreateDefaultModuleEnv(const WasmModule* module, bool allow_trap_handler = true) { UseTrapHandler use_trap_handler = trap_handler::IsTrapHandlerEnabled() && allow_trap_handler ? kUseTrapHandler : kNoTrapHandler; return ModuleEnv(module, use_trap_handler, kRuntimeExceptionSupport); } // The CompilationUnitBuilder builds compilation units and stores them in an // internal buffer. The buffer is moved into the working queue of the // CompilationState when {Commit} is called. class CompilationUnitBuilder { public: explicit CompilationUnitBuilder(NativeModule* native_module) : native_module_(native_module), compilation_state_(native_module->compilation_state()) {} void AddUnit(const WasmFunction* function, uint32_t buffer_offset, Vector bytes, WasmName name) { switch (compilation_state_->compile_mode()) { case CompileMode::kTiering: tiering_units_.emplace_back(CreateUnit( function, buffer_offset, bytes, name, ExecutionTier::kOptimized)); baseline_units_.emplace_back(CreateUnit( function, buffer_offset, bytes, name, ExecutionTier::kBaseline)); return; case CompileMode::kRegular: baseline_units_.emplace_back( CreateUnit(function, buffer_offset, bytes, name, WasmCompilationUnit::GetDefaultExecutionTier())); return; } UNREACHABLE(); } bool Commit() { if (baseline_units_.empty() && tiering_units_.empty()) return false; compilation_state_->AddCompilationUnits(baseline_units_, tiering_units_); Clear(); return true; } void Clear() { baseline_units_.clear(); tiering_units_.clear(); } private: std::unique_ptr CreateUnit(const WasmFunction* function, uint32_t buffer_offset, Vector bytes, WasmName name, ExecutionTier mode) { return base::make_unique( compilation_state_->wasm_engine(), compilation_state_->module_env(), native_module_, FunctionBody{function->sig, buffer_offset, bytes.begin(), bytes.end()}, name, function->func_index, compilation_state_->isolate()->async_counters().get(), mode); } NativeModule* native_module_; CompilationState* compilation_state_; std::vector> baseline_units_; std::vector> tiering_units_; }; // Run by each compilation task and by the main thread (i.e. in both // foreground and background threads). The no_finisher_callback is called // within the result_mutex_ lock when no finishing task is running, i.e. when // the finisher_is_running_ flag is not set. bool FetchAndExecuteCompilationUnit(CompilationState* compilation_state, WasmFeatures* detected) { DisallowHeapAccess no_heap_access; std::unique_ptr unit = compilation_state->GetNextCompilationUnit(); if (unit == nullptr) return false; // TODO(kimanh): We need to find out in which mode the unit // should be compiled in before compiling it, as it might fallback // to Turbofan if it cannot be compiled using Liftoff. This can be removed // later as soon as Liftoff can compile any function. Then, we can directly // access {unit->mode()} within {ScheduleUnitForFinishing()}. ExecutionTier mode = unit->mode(); unit->ExecuteCompilation(detected); compilation_state->ScheduleUnitForFinishing(std::move(unit), mode); return true; } void InitializeCompilationUnits(NativeModule* native_module) { ModuleWireBytes wire_bytes(native_module->wire_bytes()); const WasmModule* module = native_module->module(); CompilationUnitBuilder builder(native_module); uint32_t start = module->num_imported_functions; uint32_t end = start + module->num_declared_functions; for (uint32_t i = start; i < end; ++i) { const WasmFunction* func = &module->functions[i]; uint32_t buffer_offset = func->code.offset(); Vector bytes(wire_bytes.start() + func->code.offset(), func->code.end_offset() - func->code.offset()); WasmName name = wire_bytes.GetName(func, module); DCHECK_NOT_NULL(native_module); builder.AddUnit(func, buffer_offset, bytes, name); } builder.Commit(); } void FinishCompilationUnits(CompilationState* compilation_state, ErrorThrower* thrower) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "FinishCompilationUnits"); while (true) { if (compilation_state->failed()) break; std::unique_ptr unit = compilation_state->GetNextExecutedUnit(); if (unit == nullptr) break; WasmCode* result = unit->FinishCompilation(thrower); if (thrower->error()) { compilation_state->Abort(); break; } // Update the compilation state. compilation_state->OnFinishedUnit(); DCHECK_IMPLIES(result == nullptr, thrower->error()); if (result == nullptr) break; } if (!compilation_state->failed()) { compilation_state->RestartBackgroundTasks(); } } void CompileInParallel(Isolate* isolate, NativeModule* native_module, Handle module_object, ErrorThrower* thrower) { // 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} within the // {compilation_state}. By adding units to the {compilation_state}, new // {BackgroundCompileTasks} instances are spawned which run on // the background threads. // 2.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 {baseline_finish_units_}. // 2.b) If {baseline_finish_units_} contains a compilation unit, the main // thread dequeues it and finishes the compilation. // 3) After the parallel phase of all compilation units has started, the // main thread continues to finish all compilation units as long as // baseline-compilation units are left to be processed. // 4) If tier-up is enabled, the main thread restarts background tasks // that take care of compiling and finishing the top-tier compilation // units. // Turn on the {CanonicalHandleScope} so that the background threads can // use the node cache. CanonicalHandleScope canonical(isolate); CompilationState* compilation_state = native_module->compilation_state(); // Make sure that no foreground task is spawned for finishing // the compilation units. This foreground thread will be // responsible for finishing compilation. compilation_state->SetFinisherIsRunning(true); uint32_t num_wasm_functions = native_module->num_functions() - native_module->num_imported_functions(); compilation_state->SetNumberOfFunctionsToCompile(num_wasm_functions); // 1) The main thread allocates a compilation unit for each wasm function // and stores them in the vector {compilation_units} within the // {compilation_state}. By adding units to the {compilation_state}, new // {BackgroundCompileTask} instances are spawned which run on // background threads. InitializeCompilationUnits(native_module); // 2.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 {baseline_finish_units_}. // The foreground task bypasses waiting on memory threshold, because // its results will immediately be converted to code (below). WasmFeatures detected_features; while ( FetchAndExecuteCompilationUnit(compilation_state, &detected_features) && !compilation_state->baseline_compilation_finished()) { // 2.b) If {baseline_finish_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(compilation_state, thrower); if (compilation_state->failed()) break; } while (!compilation_state->failed()) { // 3) After the parallel phase of all compilation units has started, the // main thread continues to finish compilation units as long as // baseline compilation units are left to be processed. If compilation // already failed, all background tasks have already been canceled // in {FinishCompilationUnits}, and there are no units to finish. FinishCompilationUnits(compilation_state, thrower); if (compilation_state->baseline_compilation_finished()) break; } // Publish features from the foreground and background tasks. compilation_state->PublishDetectedFeatures(isolate, detected_features); // 4) If tiering-compilation is enabled, we need to set the finisher // to false, such that the background threads will spawn a foreground // thread to finish the top-tier compilation units. if (!compilation_state->failed() && compilation_state->compile_mode() == CompileMode::kTiering) { compilation_state->SetFinisherIsRunning(false); compilation_state->RestartBackgroundTasks(); } } void CompileSequentially(Isolate* isolate, NativeModule* native_module, ModuleEnv* module_env, ErrorThrower* thrower) { DCHECK(!thrower->error()); ModuleWireBytes wire_bytes(native_module->wire_bytes()); const WasmModule* module = module_env->module; WasmFeatures detected = kNoWasmFeatures; for (uint32_t i = 0; i < module->functions.size(); ++i) { const WasmFunction& func = module->functions[i]; if (func.imported) continue; // Imports are compiled at instantiation time. // Compile the function. WasmCode* code = WasmCompilationUnit::CompileWasmFunction( isolate, native_module, &detected, thrower, module_env, &func); if (code == nullptr) { TruncatedUserString<> name(wire_bytes.GetName(&func, module)); thrower->CompileError("Compilation of #%d:%.*s failed.", i, name.length(), name.start()); break; } } UpdateFeatureUseCounts(isolate, detected); } void ValidateSequentially(Isolate* isolate, NativeModule* native_module, ErrorThrower* thrower) { DCHECK(!thrower->error()); ModuleWireBytes wire_bytes(native_module->wire_bytes()); const WasmModule* module = native_module->module(); uint32_t start = module->num_imported_functions; uint32_t end = start + module->num_declared_functions; for (uint32_t i = start; i < end; ++i) { const WasmFunction& func = module->functions[i]; const byte* base = wire_bytes.start(); FunctionBody body{func.sig, func.code.offset(), base + func.code.offset(), base + func.code.end_offset()}; DecodeResult result; { auto time_counter = SELECT_WASM_COUNTER(isolate->async_counters(), module->origin, wasm_decode, function_time); TimedHistogramScope wasm_decode_function_time_scope(time_counter); WasmFeatures detected; result = VerifyWasmCode(isolate->allocator(), native_module->enabled_features(), module, &detected, body); } if (result.failed()) { TruncatedUserString<> name(wire_bytes.GetName(&func, module)); thrower->CompileError("Compiling function #%d:%.*s failed: %s @+%u", i, name.length(), name.start(), result.error_msg().c_str(), result.error_offset()); break; } } } void CompileNativeModule(Isolate* isolate, ErrorThrower* thrower, Handle module_object, const WasmModule* wasm_module, ModuleEnv* env) { NativeModule* const native_module = module_object->native_module(); ModuleWireBytes wire_bytes(native_module->wire_bytes()); if (compile_lazy(wasm_module)) { if (wasm_module->origin == kWasmOrigin) { // Validate wasm modules for lazy compilation. Don't validate asm.js // modules, they are valid by construction (otherwise a CHECK will fail // during lazy compilation). // TODO(clemensh): According to the spec, we can actually skip validation // at module creation time, and return a function that always traps at // (lazy) compilation time. ValidateSequentially(isolate, native_module, thrower); if (thrower->error()) return; } native_module->SetLazyBuiltin(BUILTIN_CODE(isolate, WasmCompileLazy)); } else { size_t funcs_to_compile = wasm_module->functions.size() - wasm_module->num_imported_functions; bool compile_parallel = !FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks > 0 && funcs_to_compile > 1 && V8::GetCurrentPlatform()->NumberOfWorkerThreads() > 0; if (compile_parallel) { CompileInParallel(isolate, native_module, module_object, thrower); } else { CompileSequentially(isolate, native_module, env, thrower); } if (thrower->error()) return; } } // The runnable task that finishes compilation in foreground (e.g. updating // the NativeModule, the code table, etc.). class FinishCompileTask : public CancelableTask { public: explicit FinishCompileTask(CompilationState* compilation_state, CancelableTaskManager* task_manager) : CancelableTask(task_manager), compilation_state_(compilation_state) {} void RunInternal() override { Isolate* isolate = compilation_state_->isolate(); HandleScope scope(isolate); SaveContext saved_context(isolate); isolate->set_context(nullptr); TRACE_COMPILE("(4a) Finishing compilation units...\n"); if (compilation_state_->failed()) { compilation_state_->SetFinisherIsRunning(false); return; } // We execute for 1 ms and then reschedule the task, same as the GC. double deadline = MonotonicallyIncreasingTimeInMs() + 1.0; while (true) { compilation_state_->RestartBackgroundTasks(); std::unique_ptr unit = compilation_state_->GetNextExecutedUnit(); if (unit == nullptr) { // It might happen that a background task just scheduled a unit to be // finished, but did not start a finisher task since the flag was still // set. Check for this case, and continue if there is more work. compilation_state_->SetFinisherIsRunning(false); if (compilation_state_->HasCompilationUnitToFinish() && compilation_state_->SetFinisherIsRunning(true)) { continue; } break; } ErrorThrower thrower(compilation_state_->isolate(), "AsyncCompile"); WasmCode* result = unit->FinishCompilation(&thrower); if (thrower.error()) { DCHECK_NULL(result); compilation_state_->OnError(&thrower); compilation_state_->SetFinisherIsRunning(false); thrower.Reset(); break; } if (compilation_state_->baseline_compilation_finished()) { // If Liftoff compilation finishes it will directly start executing. // As soon as we have Turbofan-compiled code available, it will // directly be used by Liftoff-compiled code via the jump table. DCHECK_EQ(CompileMode::kTiering, compilation_state_->compile_mode()); DCHECK(!result->is_liftoff()); if (WasmCode::ShouldBeLogged(isolate)) result->LogCode(isolate); } // Update the compilation state, and possibly notify // threads waiting for events. compilation_state_->OnFinishedUnit(); if (deadline < MonotonicallyIncreasingTimeInMs()) { // We reached the deadline. We reschedule this task and return // immediately. Since we rescheduled this task already, we do not set // the FinisherIsRunning flag to false. compilation_state_->ScheduleFinisherTask(); return; } } } private: CompilationState* compilation_state_; }; // The runnable task that performs compilations in the background. class BackgroundCompileTask : public CancelableTask { public: explicit BackgroundCompileTask(CompilationState* compilation_state, CancelableTaskManager* task_manager) : CancelableTask(task_manager), compilation_state_(compilation_state) {} void RunInternal() override { TRACE_COMPILE("(3b) Compiling...\n"); // The number of currently running background tasks is reduced in // {OnBackgroundTaskStopped}. while (!compilation_state_->failed()) { if (!FetchAndExecuteCompilationUnit(compilation_state_, &detected_features_)) { break; } } compilation_state_->OnBackgroundTaskStopped(detected_features_); } private: CompilationState* compilation_state_; WasmFeatures detected_features_ = kNoWasmFeatures; }; } // namespace MaybeHandle CompileToModuleObject( Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower, std::shared_ptr module, const ModuleWireBytes& wire_bytes, Handle 登录后可以享受更多权益 您还没有登录,登录后您可以: 收藏Android系统代码 收藏喜欢的文章 多个平台共享账号 去登录 首次使用?从这里 注册
code = compiler::CompileJSToWasmWrapper(isolate, native_module, func->sig, is_import, use_trap_handler) .ToHandleChecked(); cached = code; return code; } private: // We generate different code for calling imports than calling wasm functions // in this module. Both are cached separately. using CacheKey = std::pair; std::unordered_map, base::hash> cache_; }; // A helper class to simplify instantiating a module from a module object. // It closes over the {Isolate}, the {ErrorThrower}, etc. class InstanceBuilder { public: InstanceBuilder(Isolate* isolate, ErrorThrower* thrower, Handle module_object, MaybeHandle ffi, MaybeHandle memory); // Build an instance, in all of its glory. MaybeHandle Build(); // Run the start function, if any. bool ExecuteStartFunction(); private: // Represents the initialized state of a table. struct TableInstance { Handle table_object; // WebAssembly.Table instance Handle js_wrappers; // JSFunctions exported size_t table_size; }; // A pre-evaluated value to use in import binding. struct SanitizedImport { Handle module_name; Handle import_name; Handle value; }; Isolate* isolate_; const WasmFeatures enabled_; const WasmModule* const module_; ErrorThrower* thrower_; Handle module_object_; MaybeHandle ffi_; MaybeHandle memory_; Handle globals_; std::vector table_instances_; std::vector> js_wrappers_; Handle start_function_; JSToWasmWrapperCache js_to_wasm_cache_; std::vector sanitized_imports_; UseTrapHandler use_trap_handler() const { return module_object_->native_module()->use_trap_handler() ? kUseTrapHandler : kNoTrapHandler; } // Helper routines to print out errors with imports. #define ERROR_THROWER_WITH_MESSAGE(TYPE) \ void Report##TYPE(const char* error, uint32_t index, \ Handle module_name, Handle import_name) { \ thrower_->TYPE("Import #%d module=\"%s\" function=\"%s\" error: %s", \ index, module_name->ToCString().get(), \ import_name->ToCString().get(), error); \ } \ \ MaybeHandle Report##TYPE(const char* error, uint32_t index, \ Handle module_name) { \ thrower_->TYPE("Import #%d module=\"%s\" error: %s", index, \ module_name->ToCString().get(), error); \ return MaybeHandle(); \ } ERROR_THROWER_WITH_MESSAGE(LinkError) ERROR_THROWER_WITH_MESSAGE(TypeError) #undef ERROR_THROWER_WITH_MESSAGE // Look up an import value in the {ffi_} object. MaybeHandle LookupImport(uint32_t index, Handle module_name, Handle import_name); // Look up an import value in the {ffi_} object specifically for linking an // asm.js module. This only performs non-observable lookups, which allows // falling back to JavaScript proper (and hence re-executing all lookups) if // module instantiation fails. MaybeHandle LookupImportAsm(uint32_t index, Handle import_name); uint32_t EvalUint32InitExpr(const WasmInitExpr& expr); // Load data segments into the memory. void LoadDataSegments(Handle instance); void WriteGlobalValue(const WasmGlobal& global, double value); void WriteGlobalValue(const WasmGlobal& global, Handle value); void SanitizeImports(); // Find the imported memory buffer if there is one. This is used to see if we // need to recompile with bounds checks before creating the instance. MaybeHandle FindImportedMemoryBuffer() const; // Process the imports, including functions, tables, globals, and memory, in // order, loading them from the {ffi_} object. Returns the number of imported // functions. int ProcessImports(Handle instance); template T* GetRawGlobalPtr(const WasmGlobal& global); // Process initialization of globals. void InitGlobals(); // Allocate memory for a module instance as a new JSArrayBuffer. Handle AllocateMemory(uint32_t num_pages); bool NeedsWrappers() const; // Process the exports, creating wrappers for functions, tables, memories, // and globals. void ProcessExports(Handle instance); void InitializeTables(Handle instance); void LoadTableSegments(Handle instance); }; } // namespace MaybeHandle InstantiateToInstanceObject( Isolate* isolate, ErrorThrower* thrower, Handle module_object, MaybeHandle imports, MaybeHandle memory) { InstanceBuilder builder(isolate, thrower, module_object, imports, memory); auto instance = builder.Build(); if (!instance.is_null() && builder.ExecuteStartFunction()) { return instance; } return {}; } WasmCode* LazyCompileFunction(Isolate* isolate, NativeModule* native_module, int func_index) { base::ElapsedTimer compilation_timer; DCHECK(!native_module->has_code(static_cast(func_index))); compilation_timer.Start(); ModuleEnv* module_env = native_module->compilation_state()->module_env(); // TODO(wasm): Refactor this to only get the name if it is really needed for // tracing / debugging. WasmName func_name; { ModuleWireBytes wire_bytes(native_module->wire_bytes()); WireBytesRef name_ref = module_env->module->LookupFunctionName(wire_bytes, func_index); func_name = wire_bytes.GetName(name_ref); } TRACE_LAZY("Compiling function '%.*s' (#%d).\n", func_name.length(), func_name.start(), func_index); const uint8_t* module_start = native_module->wire_bytes().start(); const WasmFunction* func = &module_env->module->functions[func_index]; FunctionBody body{func->sig, func->code.offset(), module_start + func->code.offset(), module_start + func->code.end_offset()}; ErrorThrower thrower(isolate, "WasmLazyCompile"); WasmCompilationUnit unit(isolate->wasm_engine(), module_env, native_module, body, func_name, func_index, isolate->counters()); unit.ExecuteCompilation( native_module->compilation_state()->detected_features()); WasmCode* wasm_code = unit.FinishCompilation(&thrower); if (WasmCode::ShouldBeLogged(isolate)) wasm_code->LogCode(isolate); // If there is a pending error, something really went wrong. The module was // verified before starting execution with lazy compilation. // This might be OOM, but then we cannot continue execution anyway. // TODO(clemensh): According to the spec, we can actually skip validation at // module creation time, and return a function that always traps here. CHECK(!thrower.error()); int64_t func_size = static_cast(func->code.end_offset() - func->code.offset()); int64_t compilation_time = compilation_timer.Elapsed().InMicroseconds(); auto counters = isolate->counters(); counters->wasm_lazily_compiled_functions()->Increment(); counters->wasm_lazy_compilation_throughput()->AddSample( compilation_time != 0 ? static_cast(func_size / compilation_time) : 0); return wasm_code; } Address CompileLazy(Isolate* isolate, NativeModule* native_module, uint32_t func_index) { HistogramTimerScope lazy_time_scope( isolate->counters()->wasm_lazy_compilation_time()); DCHECK(!native_module->lazy_compile_frozen()); NativeModuleModificationScope native_module_modification_scope(native_module); WasmCode* result = LazyCompileFunction(isolate, native_module, func_index); DCHECK_NOT_NULL(result); DCHECK_EQ(func_index, result->index()); return result->instruction_start(); } namespace { bool compile_lazy(const WasmModule* module) { return FLAG_wasm_lazy_compilation || (FLAG_asm_wasm_lazy_compilation && module->origin == kAsmJsOrigin); } byte* raw_buffer_ptr(MaybeHandle buffer, int offset) { return static_cast(buffer.ToHandleChecked()->backing_store()) + offset; } void RecordStats(const Code* code, Counters* counters) { counters->wasm_generated_code_size()->Increment(code->body_size()); counters->wasm_reloc_size()->Increment(code->relocation_info()->length()); } bool in_bounds(uint32_t offset, size_t size, size_t upper) { return offset + size <= upper && offset + size >= offset; } using WasmInstanceMap = IdentityMap, FreeStoreAllocationPolicy>; double MonotonicallyIncreasingTimeInMs() { return V8::GetCurrentPlatform()->MonotonicallyIncreasingTime() * base::Time::kMillisecondsPerSecond; } ModuleEnv CreateDefaultModuleEnv(const WasmModule* module, bool allow_trap_handler = true) { UseTrapHandler use_trap_handler = trap_handler::IsTrapHandlerEnabled() && allow_trap_handler ? kUseTrapHandler : kNoTrapHandler; return ModuleEnv(module, use_trap_handler, kRuntimeExceptionSupport); } // The CompilationUnitBuilder builds compilation units and stores them in an // internal buffer. The buffer is moved into the working queue of the // CompilationState when {Commit} is called. class CompilationUnitBuilder { public: explicit CompilationUnitBuilder(NativeModule* native_module) : native_module_(native_module), compilation_state_(native_module->compilation_state()) {} void AddUnit(const WasmFunction* function, uint32_t buffer_offset, Vector bytes, WasmName name) { switch (compilation_state_->compile_mode()) { case CompileMode::kTiering: tiering_units_.emplace_back(CreateUnit( function, buffer_offset, bytes, name, ExecutionTier::kOptimized)); baseline_units_.emplace_back(CreateUnit( function, buffer_offset, bytes, name, ExecutionTier::kBaseline)); return; case CompileMode::kRegular: baseline_units_.emplace_back( CreateUnit(function, buffer_offset, bytes, name, WasmCompilationUnit::GetDefaultExecutionTier())); return; } UNREACHABLE(); } bool Commit() { if (baseline_units_.empty() && tiering_units_.empty()) return false; compilation_state_->AddCompilationUnits(baseline_units_, tiering_units_); Clear(); return true; } void Clear() { baseline_units_.clear(); tiering_units_.clear(); } private: std::unique_ptr CreateUnit(const WasmFunction* function, uint32_t buffer_offset, Vector bytes, WasmName name, ExecutionTier mode) { return base::make_unique( compilation_state_->wasm_engine(), compilation_state_->module_env(), native_module_, FunctionBody{function->sig, buffer_offset, bytes.begin(), bytes.end()}, name, function->func_index, compilation_state_->isolate()->async_counters().get(), mode); } NativeModule* native_module_; CompilationState* compilation_state_; std::vector> baseline_units_; std::vector> tiering_units_; }; // Run by each compilation task and by the main thread (i.e. in both // foreground and background threads). The no_finisher_callback is called // within the result_mutex_ lock when no finishing task is running, i.e. when // the finisher_is_running_ flag is not set. bool FetchAndExecuteCompilationUnit(CompilationState* compilation_state, WasmFeatures* detected) { DisallowHeapAccess no_heap_access; std::unique_ptr unit = compilation_state->GetNextCompilationUnit(); if (unit == nullptr) return false; // TODO(kimanh): We need to find out in which mode the unit // should be compiled in before compiling it, as it might fallback // to Turbofan if it cannot be compiled using Liftoff. This can be removed // later as soon as Liftoff can compile any function. Then, we can directly // access {unit->mode()} within {ScheduleUnitForFinishing()}. ExecutionTier mode = unit->mode(); unit->ExecuteCompilation(detected); compilation_state->ScheduleUnitForFinishing(std::move(unit), mode); return true; } void InitializeCompilationUnits(NativeModule* native_module) { ModuleWireBytes wire_bytes(native_module->wire_bytes()); const WasmModule* module = native_module->module(); CompilationUnitBuilder builder(native_module); uint32_t start = module->num_imported_functions; uint32_t end = start + module->num_declared_functions; for (uint32_t i = start; i < end; ++i) { const WasmFunction* func = &module->functions[i]; uint32_t buffer_offset = func->code.offset(); Vector bytes(wire_bytes.start() + func->code.offset(), func->code.end_offset() - func->code.offset()); WasmName name = wire_bytes.GetName(func, module); DCHECK_NOT_NULL(native_module); builder.AddUnit(func, buffer_offset, bytes, name); } builder.Commit(); } void FinishCompilationUnits(CompilationState* compilation_state, ErrorThrower* thrower) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "FinishCompilationUnits"); while (true) { if (compilation_state->failed()) break; std::unique_ptr unit = compilation_state->GetNextExecutedUnit(); if (unit == nullptr) break; WasmCode* result = unit->FinishCompilation(thrower); if (thrower->error()) { compilation_state->Abort(); break; } // Update the compilation state. compilation_state->OnFinishedUnit(); DCHECK_IMPLIES(result == nullptr, thrower->error()); if (result == nullptr) break; } if (!compilation_state->failed()) { compilation_state->RestartBackgroundTasks(); } } void CompileInParallel(Isolate* isolate, NativeModule* native_module, Handle module_object, ErrorThrower* thrower) { // 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} within the // {compilation_state}. By adding units to the {compilation_state}, new // {BackgroundCompileTasks} instances are spawned which run on // the background threads. // 2.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 {baseline_finish_units_}. // 2.b) If {baseline_finish_units_} contains a compilation unit, the main // thread dequeues it and finishes the compilation. // 3) After the parallel phase of all compilation units has started, the // main thread continues to finish all compilation units as long as // baseline-compilation units are left to be processed. // 4) If tier-up is enabled, the main thread restarts background tasks // that take care of compiling and finishing the top-tier compilation // units. // Turn on the {CanonicalHandleScope} so that the background threads can // use the node cache. CanonicalHandleScope canonical(isolate); CompilationState* compilation_state = native_module->compilation_state(); // Make sure that no foreground task is spawned for finishing // the compilation units. This foreground thread will be // responsible for finishing compilation. compilation_state->SetFinisherIsRunning(true); uint32_t num_wasm_functions = native_module->num_functions() - native_module->num_imported_functions(); compilation_state->SetNumberOfFunctionsToCompile(num_wasm_functions); // 1) The main thread allocates a compilation unit for each wasm function // and stores them in the vector {compilation_units} within the // {compilation_state}. By adding units to the {compilation_state}, new // {BackgroundCompileTask} instances are spawned which run on // background threads. InitializeCompilationUnits(native_module); // 2.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 {baseline_finish_units_}. // The foreground task bypasses waiting on memory threshold, because // its results will immediately be converted to code (below). WasmFeatures detected_features; while ( FetchAndExecuteCompilationUnit(compilation_state, &detected_features) && !compilation_state->baseline_compilation_finished()) { // 2.b) If {baseline_finish_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(compilation_state, thrower); if (compilation_state->failed()) break; } while (!compilation_state->failed()) { // 3) After the parallel phase of all compilation units has started, the // main thread continues to finish compilation units as long as // baseline compilation units are left to be processed. If compilation // already failed, all background tasks have already been canceled // in {FinishCompilationUnits}, and there are no units to finish. FinishCompilationUnits(compilation_state, thrower); if (compilation_state->baseline_compilation_finished()) break; } // Publish features from the foreground and background tasks. compilation_state->PublishDetectedFeatures(isolate, detected_features); // 4) If tiering-compilation is enabled, we need to set the finisher // to false, such that the background threads will spawn a foreground // thread to finish the top-tier compilation units. if (!compilation_state->failed() && compilation_state->compile_mode() == CompileMode::kTiering) { compilation_state->SetFinisherIsRunning(false); compilation_state->RestartBackgroundTasks(); } } void CompileSequentially(Isolate* isolate, NativeModule* native_module, ModuleEnv* module_env, ErrorThrower* thrower) { DCHECK(!thrower->error()); ModuleWireBytes wire_bytes(native_module->wire_bytes()); const WasmModule* module = module_env->module; WasmFeatures detected = kNoWasmFeatures; for (uint32_t i = 0; i < module->functions.size(); ++i) { const WasmFunction& func = module->functions[i]; if (func.imported) continue; // Imports are compiled at instantiation time. // Compile the function. WasmCode* code = WasmCompilationUnit::CompileWasmFunction( isolate, native_module, &detected, thrower, module_env, &func); if (code == nullptr) { TruncatedUserString<> name(wire_bytes.GetName(&func, module)); thrower->CompileError("Compilation of #%d:%.*s failed.", i, name.length(), name.start()); break; } } UpdateFeatureUseCounts(isolate, detected); } void ValidateSequentially(Isolate* isolate, NativeModule* native_module, ErrorThrower* thrower) { DCHECK(!thrower->error()); ModuleWireBytes wire_bytes(native_module->wire_bytes()); const WasmModule* module = native_module->module(); uint32_t start = module->num_imported_functions; uint32_t end = start + module->num_declared_functions; for (uint32_t i = start; i < end; ++i) { const WasmFunction& func = module->functions[i]; const byte* base = wire_bytes.start(); FunctionBody body{func.sig, func.code.offset(), base + func.code.offset(), base + func.code.end_offset()}; DecodeResult result; { auto time_counter = SELECT_WASM_COUNTER(isolate->async_counters(), module->origin, wasm_decode, function_time); TimedHistogramScope wasm_decode_function_time_scope(time_counter); WasmFeatures detected; result = VerifyWasmCode(isolate->allocator(), native_module->enabled_features(), module, &detected, body); } if (result.failed()) { TruncatedUserString<> name(wire_bytes.GetName(&func, module)); thrower->CompileError("Compiling function #%d:%.*s failed: %s @+%u", i, name.length(), name.start(), result.error_msg().c_str(), result.error_offset()); break; } } } void CompileNativeModule(Isolate* isolate, ErrorThrower* thrower, Handle module_object, const WasmModule* wasm_module, ModuleEnv* env) { NativeModule* const native_module = module_object->native_module(); ModuleWireBytes wire_bytes(native_module->wire_bytes()); if (compile_lazy(wasm_module)) { if (wasm_module->origin == kWasmOrigin) { // Validate wasm modules for lazy compilation. Don't validate asm.js // modules, they are valid by construction (otherwise a CHECK will fail // during lazy compilation). // TODO(clemensh): According to the spec, we can actually skip validation // at module creation time, and return a function that always traps at // (lazy) compilation time. ValidateSequentially(isolate, native_module, thrower); if (thrower->error()) return; } native_module->SetLazyBuiltin(BUILTIN_CODE(isolate, WasmCompileLazy)); } else { size_t funcs_to_compile = wasm_module->functions.size() - wasm_module->num_imported_functions; bool compile_parallel = !FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks > 0 && funcs_to_compile > 1 && V8::GetCurrentPlatform()->NumberOfWorkerThreads() > 0; if (compile_parallel) { CompileInParallel(isolate, native_module, module_object, thrower); } else { CompileSequentially(isolate, native_module, env, thrower); } if (thrower->error()) return; } } // The runnable task that finishes compilation in foreground (e.g. updating // the NativeModule, the code table, etc.). class FinishCompileTask : public CancelableTask { public: explicit FinishCompileTask(CompilationState* compilation_state, CancelableTaskManager* task_manager) : CancelableTask(task_manager), compilation_state_(compilation_state) {} void RunInternal() override { Isolate* isolate = compilation_state_->isolate(); HandleScope scope(isolate); SaveContext saved_context(isolate); isolate->set_context(nullptr); TRACE_COMPILE("(4a) Finishing compilation units...\n"); if (compilation_state_->failed()) { compilation_state_->SetFinisherIsRunning(false); return; } // We execute for 1 ms and then reschedule the task, same as the GC. double deadline = MonotonicallyIncreasingTimeInMs() + 1.0; while (true) { compilation_state_->RestartBackgroundTasks(); std::unique_ptr unit = compilation_state_->GetNextExecutedUnit(); if (unit == nullptr) { // It might happen that a background task just scheduled a unit to be // finished, but did not start a finisher task since the flag was still // set. Check for this case, and continue if there is more work. compilation_state_->SetFinisherIsRunning(false); if (compilation_state_->HasCompilationUnitToFinish() && compilation_state_->SetFinisherIsRunning(true)) { continue; } break; } ErrorThrower thrower(compilation_state_->isolate(), "AsyncCompile"); WasmCode* result = unit->FinishCompilation(&thrower); if (thrower.error()) { DCHECK_NULL(result); compilation_state_->OnError(&thrower); compilation_state_->SetFinisherIsRunning(false); thrower.Reset(); break; } if (compilation_state_->baseline_compilation_finished()) { // If Liftoff compilation finishes it will directly start executing. // As soon as we have Turbofan-compiled code available, it will // directly be used by Liftoff-compiled code via the jump table. DCHECK_EQ(CompileMode::kTiering, compilation_state_->compile_mode()); DCHECK(!result->is_liftoff()); if (WasmCode::ShouldBeLogged(isolate)) result->LogCode(isolate); } // Update the compilation state, and possibly notify // threads waiting for events. compilation_state_->OnFinishedUnit(); if (deadline < MonotonicallyIncreasingTimeInMs()) { // We reached the deadline. We reschedule this task and return // immediately. Since we rescheduled this task already, we do not set // the FinisherIsRunning flag to false. compilation_state_->ScheduleFinisherTask(); return; } } } private: CompilationState* compilation_state_; }; // The runnable task that performs compilations in the background. class BackgroundCompileTask : public CancelableTask { public: explicit BackgroundCompileTask(CompilationState* compilation_state, CancelableTaskManager* task_manager) : CancelableTask(task_manager), compilation_state_(compilation_state) {} void RunInternal() override { TRACE_COMPILE("(3b) Compiling...\n"); // The number of currently running background tasks is reduced in // {OnBackgroundTaskStopped}. while (!compilation_state_->failed()) { if (!FetchAndExecuteCompilationUnit(compilation_state_, &detected_features_)) { break; } } compilation_state_->OnBackgroundTaskStopped(detected_features_); } private: CompilationState* compilation_state_; WasmFeatures detected_features_ = kNoWasmFeatures; }; } // namespace MaybeHandle CompileToModuleObject( Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower, std::shared_ptr module, const ModuleWireBytes& wire_bytes, Handle 登录后可以享受更多权益 您还没有登录,登录后您可以: 收藏Android系统代码 收藏喜欢的文章 多个平台共享账号 去登录 首次使用?从这里 注册
您还没有登录,登录后您可以:
首次使用?从这里 注册