1 // Copyright 2017 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #if !V8_ENABLE_WEBASSEMBLY 6 #error This header should only be included if WebAssembly is enabled. 7 #endif // !V8_ENABLE_WEBASSEMBLY 8 9 #ifndef V8_WASM_MODULE_COMPILER_H_ 10 #define V8_WASM_MODULE_COMPILER_H_ 11 12 #include <atomic> 13 #include <functional> 14 #include <memory> 15 16 #include "include/v8-metrics.h" 17 #include "src/base/optional.h" 18 #include "src/common/globals.h" 19 #include "src/logging/metrics.h" 20 #include "src/tasks/cancelable-task.h" 21 #include "src/wasm/compilation-environment.h" 22 #include "src/wasm/wasm-features.h" 23 #include "src/wasm/wasm-import-wrapper-cache.h" 24 #include "src/wasm/wasm-module.h" 25 26 namespace v8 { 27 28 namespace base { 29 template <typename T> 30 class Vector; 31 } // namespace base 32 33 namespace internal { 34 35 class JSArrayBuffer; 36 class JSPromise; 37 class Counters; 38 class WasmModuleObject; 39 class WasmInstanceObject; 40 41 namespace wasm { 42 43 struct CompilationEnv; 44 class CompilationResultResolver; 45 class ErrorThrower; 46 class ModuleCompiler; 47 class NativeModule; 48 class StreamingDecoder; 49 class WasmCode; 50 struct WasmModule; 51 52 V8_EXPORT_PRIVATE 53 std::shared_ptr<NativeModule> CompileToNativeModule( 54 Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower, 55 std::shared_ptr<const WasmModule> module, const ModuleWireBytes& wire_bytes, 56 Handle<FixedArray>* export_wrappers_out, int compilation_id, 57 v8::metrics::Recorder::ContextId context_id); 58 59 void RecompileNativeModule(NativeModule* native_module, 60 TieringState new_tiering_state); 61 62 V8_EXPORT_PRIVATE 63 void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module, 64 Handle<FixedArray>* export_wrappers_out); 65 66 // Compiles the wrapper for this (kind, sig) pair and sets the corresponding 67 // cache entry. Assumes the key already exists in the cache but has not been 68 // compiled yet. 69 V8_EXPORT_PRIVATE 70 WasmCode* CompileImportWrapper( 71 NativeModule* native_module, Counters* counters, 72 compiler::WasmImportCallKind kind, const FunctionSig* sig, 73 int expected_arity, Suspend suspend, 74 WasmImportWrapperCache::ModificationScope* cache_scope); 75 76 // Triggered by the WasmCompileLazy builtin. The return value indicates whether 77 // compilation was successful. Lazy compilation can fail only if validation is 78 // also lazy. 79 bool CompileLazy(Isolate*, Handle<WasmInstanceObject>, int func_index); 80 81 V8_EXPORT_PRIVATE void TriggerTierUp(Isolate*, NativeModule*, int func_index, 82 Handle<WasmInstanceObject> instance); 83 84 template <typename Key, typename Hash> 85 class WrapperQueue { 86 public: 87 // Removes an arbitrary key from the queue and returns it. 88 // If the queue is empty, returns nullopt. 89 // Thread-safe. pop()90 base::Optional<Key> pop() { 91 base::Optional<Key> key = base::nullopt; 92 base::MutexGuard lock(&mutex_); 93 auto it = queue_.begin(); 94 if (it != queue_.end()) { 95 key = *it; 96 queue_.erase(it); 97 } 98 return key; 99 } 100 101 // Add the given key to the queue and returns true iff the insert was 102 // successful. 103 // Not thread-safe. insert(const Key & key)104 bool insert(const Key& key) { return queue_.insert(key).second; } 105 size()106 size_t size() { 107 base::MutexGuard lock(&mutex_); 108 return queue_.size(); 109 } 110 111 private: 112 base::Mutex mutex_; 113 std::unordered_set<Key, Hash> queue_; 114 }; 115 116 // Encapsulates all the state and steps of an asynchronous compilation. 117 // An asynchronous compile job consists of a number of tasks that are executed 118 // as foreground and background tasks. Any phase that touches the V8 heap or 119 // allocates on the V8 heap (e.g. creating the module object) must be a 120 // foreground task. All other tasks (e.g. decoding and validating, the majority 121 // of the work of compilation) can be background tasks. 122 // TODO(wasm): factor out common parts of this with the synchronous pipeline. 123 class AsyncCompileJob { 124 public: 125 AsyncCompileJob(Isolate* isolate, const WasmFeatures& enabled_features, 126 std::unique_ptr<byte[]> bytes_copy, size_t length, 127 Handle<Context> context, Handle<Context> incumbent_context, 128 const char* api_method_name, 129 std::shared_ptr<CompilationResultResolver> resolver, 130 int compilation_id); 131 ~AsyncCompileJob(); 132 133 void Start(); 134 135 std::shared_ptr<StreamingDecoder> CreateStreamingDecoder(); 136 137 void Abort(); 138 void CancelPendingForegroundTask(); 139 isolate()140 Isolate* isolate() const { return isolate_; } 141 context()142 Handle<NativeContext> context() const { return native_context_; } context_id()143 v8::metrics::Recorder::ContextId context_id() const { return context_id_; } 144 145 private: 146 class CompileTask; 147 class CompileStep; 148 class CompilationStateCallback; 149 150 // States of the AsyncCompileJob. 151 class DecodeModule; // Step 1 (async) 152 class DecodeFail; // Step 1b (sync) 153 class PrepareAndStartCompile; // Step 2 (sync) 154 class CompileFailed; // Step 3a (sync) 155 class CompileFinished; // Step 3b (sync) 156 157 friend class AsyncStreamingProcessor; 158 159 // Decrements the number of outstanding finishers. The last caller of this 160 // function should finish the asynchronous compilation, see the comment on 161 // {outstanding_finishers_}. DecrementAndCheckFinisherCount()162 V8_WARN_UNUSED_RESULT bool DecrementAndCheckFinisherCount() { 163 DCHECK_LT(0, outstanding_finishers_.load()); 164 return outstanding_finishers_.fetch_sub(1) == 1; 165 } 166 167 void CreateNativeModule(std::shared_ptr<const WasmModule> module, 168 size_t code_size_estimate); 169 // Return true for cache hit, false for cache miss. 170 bool GetOrCreateNativeModule(std::shared_ptr<const WasmModule> module, 171 size_t code_size_estimate); 172 void PrepareRuntimeObjects(); 173 174 void FinishCompile(bool is_after_cache_hit); 175 176 void DecodeFailed(const WasmError&); 177 void AsyncCompileFailed(); 178 179 void AsyncCompileSucceeded(Handle<WasmModuleObject> result); 180 181 void FinishModule(); 182 183 void StartForegroundTask(); 184 void ExecuteForegroundTaskImmediately(); 185 186 void StartBackgroundTask(); 187 188 enum UseExistingForegroundTask : bool { 189 kUseExistingForegroundTask = true, 190 kAssertNoExistingForegroundTask = false 191 }; 192 // Switches to the compilation step {Step} and starts a foreground task to 193 // execute it. Most of the time we know that there cannot be a running 194 // foreground task. If there might be one, then pass 195 // kUseExistingForegroundTask to avoid spawning a second one. 196 template <typename Step, 197 UseExistingForegroundTask = kAssertNoExistingForegroundTask, 198 typename... Args> 199 void DoSync(Args&&... args); 200 201 // Switches to the compilation step {Step} and immediately executes that step. 202 template <typename Step, typename... Args> 203 void DoImmediately(Args&&... args); 204 205 // Switches to the compilation step {Step} and starts a background task to 206 // execute it. 207 template <typename Step, typename... Args> 208 void DoAsync(Args&&... args); 209 210 // Switches to the compilation step {Step} but does not start a task to 211 // execute it. 212 template <typename Step, typename... Args> 213 void NextStep(Args&&... args); 214 215 Isolate* const isolate_; 216 const char* const api_method_name_; 217 const WasmFeatures enabled_features_; 218 const DynamicTiering dynamic_tiering_; 219 const bool wasm_lazy_compilation_; 220 base::TimeTicks start_time_; 221 // Copy of the module wire bytes, moved into the {native_module_} on its 222 // creation. 223 std::unique_ptr<byte[]> bytes_copy_; 224 // Reference to the wire bytes (held in {bytes_copy_} or as part of 225 // {native_module_}). 226 ModuleWireBytes wire_bytes_; 227 Handle<NativeContext> native_context_; 228 Handle<Context> incumbent_context_; 229 v8::metrics::Recorder::ContextId context_id_; 230 v8::metrics::WasmModuleDecoded metrics_event_; 231 const std::shared_ptr<CompilationResultResolver> resolver_; 232 233 Handle<WasmModuleObject> module_object_; 234 std::shared_ptr<NativeModule> native_module_; 235 236 std::unique_ptr<CompileStep> step_; 237 CancelableTaskManager background_task_manager_; 238 239 std::shared_ptr<v8::TaskRunner> foreground_task_runner_; 240 241 // For async compilation the AsyncCompileJob is the only finisher. For 242 // streaming compilation also the AsyncStreamingProcessor has to finish before 243 // compilation can be finished. 244 std::atomic<int32_t> outstanding_finishers_{1}; 245 246 // A reference to a pending foreground task, or {nullptr} if none is pending. 247 CompileTask* pending_foreground_task_ = nullptr; 248 249 // The AsyncCompileJob owns the StreamingDecoder because the StreamingDecoder 250 // contains data which is needed by the AsyncCompileJob for streaming 251 // compilation. The AsyncCompileJob does not actively use the 252 // StreamingDecoder. 253 std::shared_ptr<StreamingDecoder> stream_; 254 255 // The compilation id to identify trace events linked to this compilation. 256 const int compilation_id_; 257 }; 258 259 } // namespace wasm 260 } // namespace internal 261 } // namespace v8 262 263 #endif // V8_WASM_MODULE_COMPILER_H_ 264