• 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 #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