• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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