// Copyright 2017 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #if !V8_ENABLE_WEBASSEMBLY #error This header should only be included if WebAssembly is enabled. #endif // !V8_ENABLE_WEBASSEMBLY #ifndef V8_WASM_MODULE_COMPILER_H_ #define V8_WASM_MODULE_COMPILER_H_ #include #include #include #include "include/v8-metrics.h" #include "src/base/optional.h" #include "src/common/globals.h" #include "src/logging/metrics.h" #include "src/tasks/cancelable-task.h" #include "src/wasm/compilation-environment.h" #include "src/wasm/wasm-features.h" #include "src/wasm/wasm-import-wrapper-cache.h" #include "src/wasm/wasm-module.h" namespace v8 { namespace base { template class Vector; } // namespace base namespace internal { class JSArrayBuffer; class JSPromise; class Counters; class WasmModuleObject; class WasmInstanceObject; namespace wasm { struct CompilationEnv; class CompilationResultResolver; class ErrorThrower; class ModuleCompiler; class NativeModule; class StreamingDecoder; class WasmCode; struct WasmModule; V8_EXPORT_PRIVATE std::shared_ptr CompileToNativeModule( Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower, std::shared_ptr module, const ModuleWireBytes& wire_bytes, Handle* export_wrappers_out, int compilation_id, v8::metrics::Recorder::ContextId context_id); void RecompileNativeModule(NativeModule* native_module, TieringState new_tiering_state); V8_EXPORT_PRIVATE void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module, Handle* export_wrappers_out); // Compiles the wrapper for this (kind, sig) pair and sets the corresponding // cache entry. Assumes the key already exists in the cache but has not been // compiled yet. V8_EXPORT_PRIVATE WasmCode* CompileImportWrapper( NativeModule* native_module, Counters* counters, compiler::WasmImportCallKind kind, const FunctionSig* sig, int expected_arity, Suspend suspend, WasmImportWrapperCache::ModificationScope* cache_scope); // Triggered by the WasmCompileLazy builtin. The return value indicates whether // compilation was successful. Lazy compilation can fail only if validation is // also lazy. bool CompileLazy(Isolate*, Handle, int func_index); V8_EXPORT_PRIVATE void TriggerTierUp(Isolate*, NativeModule*, int func_index, Handle instance); template class WrapperQueue { public: // Removes an arbitrary key from the queue and returns it. // If the queue is empty, returns nullopt. // Thread-safe. base::Optional pop() { base::Optional key = base::nullopt; base::MutexGuard lock(&mutex_); auto it = queue_.begin(); if (it != queue_.end()) { key = *it; queue_.erase(it); } return key; } // Add the given key to the queue and returns true iff the insert was // successful. // Not thread-safe. bool insert(const Key& key) { return queue_.insert(key).second; } size_t size() { base::MutexGuard lock(&mutex_); return queue_.size(); } private: base::Mutex mutex_; std::unordered_set queue_; }; // Encapsulates all the state and steps of an asynchronous compilation. // An asynchronous compile job consists of a number of tasks that are executed // as foreground and background tasks. Any phase that touches the V8 heap or // allocates on the V8 heap (e.g. creating the module object) must be a // foreground task. All other tasks (e.g. decoding and validating, the majority // of the work of compilation) can be background tasks. // TODO(wasm): factor out common parts of this with the synchronous pipeline. class AsyncCompileJob { public: AsyncCompileJob(Isolate* isolate, const WasmFeatures& enabled_features, std::unique_ptr bytes_copy, size_t length, Handle context, Handle incumbent_context, const char* api_method_name, std::shared_ptr resolver, int compilation_id); ~AsyncCompileJob(); void Start(); std::shared_ptr CreateStreamingDecoder(); void Abort(); void CancelPendingForegroundTask(); Isolate* isolate() const { return isolate_; } Handle context() const { return native_context_; } v8::metrics::Recorder::ContextId context_id() const { return context_id_; } private: class CompileTask; class CompileStep; class CompilationStateCallback; // States of the AsyncCompileJob. class DecodeModule; // Step 1 (async) class DecodeFail; // Step 1b (sync) class PrepareAndStartCompile; // Step 2 (sync) class CompileFailed; // Step 3a (sync) class CompileFinished; // Step 3b (sync) friend class AsyncStreamingProcessor; // Decrements the number of outstanding finishers. The last caller of this // function should finish the asynchronous compilation, see the comment on // {outstanding_finishers_}. V8_WARN_UNUSED_RESULT bool DecrementAndCheckFinisherCount() { DCHECK_LT(0, outstanding_finishers_.load()); return outstanding_finishers_.fetch_sub(1) == 1; } void CreateNativeModule(std::shared_ptr module, size_t code_size_estimate); // Return true for cache hit, false for cache miss. bool GetOrCreateNativeModule(std::shared_ptr module, size_t code_size_estimate); void PrepareRuntimeObjects(); void FinishCompile(bool is_after_cache_hit); void DecodeFailed(const WasmError&); void AsyncCompileFailed(); void AsyncCompileSucceeded(Handle result); void FinishModule(); void StartForegroundTask(); void ExecuteForegroundTaskImmediately(); void StartBackgroundTask(); enum UseExistingForegroundTask : bool { kUseExistingForegroundTask = true, kAssertNoExistingForegroundTask = false }; // Switches to the compilation step {Step} and starts a foreground task to // execute it. Most of the time we know that there cannot be a running // foreground task. If there might be one, then pass // kUseExistingForegroundTask to avoid spawning a second one. template void DoSync(Args&&... args); // Switches to the compilation step {Step} and immediately executes that step. template void DoImmediately(Args&&... args); // Switches to the compilation step {Step} and starts a background task to // execute it. template void DoAsync(Args&&... args); // Switches to the compilation step {Step} but does not start a task to // execute it. template void NextStep(Args&&... args); Isolate* const isolate_; const char* const api_method_name_; const WasmFeatures enabled_features_; const DynamicTiering dynamic_tiering_; const bool wasm_lazy_compilation_; base::TimeTicks start_time_; // Copy of the module wire bytes, moved into the {native_module_} on its // creation. std::unique_ptr bytes_copy_; // Reference to the wire bytes (held in {bytes_copy_} or as part of // {native_module_}). ModuleWireBytes wire_bytes_; Handle native_context_; Handle incumbent_context_; v8::metrics::Recorder::ContextId context_id_; v8::metrics::WasmModuleDecoded metrics_event_; const std::shared_ptr resolver_; Handle module_object_; std::shared_ptr native_module_; std::unique_ptr step_; CancelableTaskManager background_task_manager_; std::shared_ptr foreground_task_runner_; // For async compilation the AsyncCompileJob is the only finisher. For // streaming compilation also the AsyncStreamingProcessor has to finish before // compilation can be finished. std::atomic outstanding_finishers_{1}; // A reference to a pending foreground task, or {nullptr} if none is pending. CompileTask* pending_foreground_task_ = nullptr; // The AsyncCompileJob owns the StreamingDecoder because the StreamingDecoder // contains data which is needed by the AsyncCompileJob for streaming // compilation. The AsyncCompileJob does not actively use the // StreamingDecoder. std::shared_ptr stream_; // The compilation id to identify trace events linked to this compilation. const int compilation_id_; }; } // namespace wasm } // namespace internal } // namespace v8 #endif // V8_WASM_MODULE_COMPILER_H_