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