wasm-engine.h 11.8 KB
Newer Older
1 2 3 4
// 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.

5 6
#ifndef V8_WASM_WASM_ENGINE_H_
#define V8_WASM_WASM_ENGINE_H_
7 8

#include <memory>
9
#include <unordered_set>
10

11
#include "src/tasks/cancelable-task.h"
12
#include "src/wasm/wasm-code-manager.h"
13
#include "src/wasm/wasm-memory.h"
14
#include "src/wasm/wasm-tier.h"
15
#include "src/zone/accounting-allocator.h"
16 17 18 19

namespace v8 {
namespace internal {

20
class AsmWasmData;
21
class CodeTracer;
22
class CompilationStatistics;
23
class HeapNumber;
24
class WasmInstanceObject;
25
class WasmModuleObject;
26

27 28
namespace wasm {

29
class AsyncCompileJob;
30 31
class ErrorThrower;
struct ModuleWireBytes;
32
struct WasmFeatures;
33

34 35 36 37
class V8_EXPORT_PRIVATE CompilationResultResolver {
 public:
  virtual void OnCompilationSucceeded(Handle<WasmModuleObject> result) = 0;
  virtual void OnCompilationFailed(Handle<Object> error_reason) = 0;
38
  virtual ~CompilationResultResolver() = default;
39 40 41 42 43 44
};

class V8_EXPORT_PRIVATE InstantiationResultResolver {
 public:
  virtual void OnInstantiationSucceeded(Handle<WasmInstanceObject> result) = 0;
  virtual void OnInstantiationFailed(Handle<Object> error_reason) = 0;
45
  virtual ~InstantiationResultResolver() = default;
46 47
};

48 49
// The central data structure that represents an engine instance capable of
// loading, instantiating, and executing WASM code.
50
class V8_EXPORT_PRIVATE WasmEngine {
51
 public:
52
  WasmEngine();
53
  ~WasmEngine();
54

55 56
  // Synchronously validates the given bytes that represent an encoded WASM
  // module.
57 58
  bool SyncValidate(Isolate* isolate, const WasmFeatures& enabled,
                    const ModuleWireBytes& bytes);
59

60 61
  // Synchronously compiles the given bytes that represent a translated
  // asm.js module.
62
  MaybeHandle<AsmWasmData> SyncCompileTranslatedAsmJs(
63
      Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
64
      Vector<const byte> asm_js_offset_table_bytes,
65
      Handle<HeapNumber> uses_bitset, LanguageMode language_mode);
66 67 68
  Handle<WasmModuleObject> FinalizeTranslatedAsmJs(
      Isolate* isolate, Handle<AsmWasmData> asm_wasm_data,
      Handle<Script> script);
69 70 71 72

  // Synchronously compiles the given bytes that represent an encoded WASM
  // module.
  MaybeHandle<WasmModuleObject> SyncCompile(Isolate* isolate,
73
                                            const WasmFeatures& enabled,
74 75 76 77 78 79 80 81 82 83 84 85
                                            ErrorThrower* thrower,
                                            const ModuleWireBytes& bytes);

  // Synchronously instantiate the given WASM module with the given imports.
  // If the module represents an asm.js module, then the supplied {memory}
  // should be used as the memory of the instance.
  MaybeHandle<WasmInstanceObject> SyncInstantiate(
      Isolate* isolate, ErrorThrower* thrower,
      Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
      MaybeHandle<JSArrayBuffer> memory);

  // Begin an asynchronous compilation of the given bytes that represent an
86
  // encoded WASM module.
87 88
  // The {is_shared} flag indicates if the bytes backing the module could
  // be shared across threads, i.e. could be concurrently modified.
89
  void AsyncCompile(Isolate* isolate, const WasmFeatures& enabled,
90
                    std::shared_ptr<CompilationResultResolver> resolver,
91 92
                    const ModuleWireBytes& bytes, bool is_shared,
                    const char* api_method_name_for_errors);
93

94 95 96
  // Begin an asynchronous instantiation of the given WASM module.
  void AsyncInstantiate(Isolate* isolate,
                        std::unique_ptr<InstantiationResultResolver> resolver,
97 98 99
                        Handle<WasmModuleObject> module_object,
                        MaybeHandle<JSReceiver> imports);

100
  std::shared_ptr<StreamingDecoder> StartStreamingCompilation(
101
      Isolate* isolate, const WasmFeatures& enabled, Handle<Context> context,
102
      const char* api_method_name,
103
      std::shared_ptr<CompilationResultResolver> resolver);
104

105 106 107 108
  // Compiles the function with the given index at a specific compilation tier.
  // Errors are stored internally in the CompilationState.
  // This is mostly used for testing to force a function into a specific tier.
  void CompileFunction(Isolate* isolate, NativeModule* native_module,
109
                       uint32_t function_index, ExecutionTier tier);
110

111 112 113 114 115 116 117 118
  // Exports the sharable parts of the given module object so that they can be
  // transferred to a different Context/Isolate using the same engine.
  std::shared_ptr<NativeModule> ExportNativeModule(
      Handle<WasmModuleObject> module_object);

  // Imports the shared part of a module from a different Context/Isolate using
  // the the same engine, recreating a full module object in the given Isolate.
  Handle<WasmModuleObject> ImportNativeModule(
119
      Isolate* isolate, std::shared_ptr<NativeModule> shared_module);
120

121
  WasmCodeManager* code_manager() { return &code_manager_; }
122

123 124
  WasmMemoryTracker* memory_tracker() { return &memory_tracker_; }

125 126
  AccountingAllocator* allocator() { return &allocator_; }

127 128 129 130 131 132
  // Compilation statistics for TurboFan compilations.
  CompilationStatistics* GetOrCreateTurboStatistics();

  // Prints the gathered compilation statistics, then resets them.
  void DumpAndResetTurboStatistics();

133 134 135
  // Used to redirect tracing output from {stdout} to a file.
  CodeTracer* GetCodeTracer();

136
  // Remove {job} from the list of active compile jobs.
137
  std::unique_ptr<AsyncCompileJob> RemoveCompileJob(AsyncCompileJob* job);
138

139 140 141
  // Returns true if at least one AsyncCompileJob that belongs to the given
  // Isolate is currently running.
  bool HasRunningCompileJob(Isolate* isolate);
142

143 144 145 146 147
  // Deletes all AsyncCompileJobs that belong to the given context. All
  // compilation is aborted, no more callbacks will be triggered. This is used
  // when a context is disposed, e.g. because of browser navigation.
  void DeleteCompileJobsOnContext(Handle<Context> context);

148
  // Deletes all AsyncCompileJobs that belong to the given Isolate. All
149 150
  // compilation is aborted, no more callbacks will be triggered. This is used
  // for tearing down an isolate, or to clean it up to be reused.
151
  void DeleteCompileJobsOnIsolate(Isolate* isolate);
152

153 154 155 156
  // Manage the set of Isolates that use this WasmEngine.
  void AddIsolate(Isolate* isolate);
  void RemoveIsolate(Isolate* isolate);

157 158 159 160 161 162
  template <typename T, typename... Args>
  std::unique_ptr<T> NewBackgroundCompileTask(Args&&... args) {
    return base::make_unique<T>(&background_compile_task_manager_,
                                std::forward<Args>(args)...);
  }

163 164 165 166 167
  // Trigger code logging for this WasmCode in all Isolates which have access to
  // the NativeModule containing this code. This method can be called from
  // background threads.
  void LogCode(WasmCode*);

168 169 170 171 172
  // Enable code logging for the given Isolate. Initially, code logging is
  // enabled if {WasmCode::ShouldBeLogged(Isolate*)} returns true during
  // {AddIsolate}.
  void EnableCodeLogging(Isolate*);

173 174 175 176
  // This is called from the foreground thread of the Isolate to log all
  // outstanding code objects (added via {LogCode}).
  void LogOutstandingCodesForIsolate(Isolate*);

177 178 179 180 181 182
  // Create a new NativeModule. The caller is responsible for its
  // lifetime. The native module will be given some memory for code,
  // which will be page size aligned. The size of the initial memory
  // is determined with a heuristic based on the total size of wasm
  // code. The native module may later request more memory.
  // TODO(titzer): isolate is only required here for CompilationState.
183 184 185
  std::shared_ptr<NativeModule> NewNativeModule(
      Isolate* isolate, const WasmFeatures& enabled_features,
      std::shared_ptr<const WasmModule> module);
186
  std::shared_ptr<NativeModule> NewNativeModule(
187 188 189 190 191 192
      Isolate* isolate, const WasmFeatures& enabled_features,
      size_t code_size_estimate, bool can_request_more,
      std::shared_ptr<const WasmModule> module);

  void FreeNativeModule(NativeModule*);

193 194 195 196 197
  // Sample the code size of the given {NativeModule} in all isolates that have
  // access to it. Call this after top-tier compilation finished.
  // This will spawn foreground tasks that do *not* keep the NativeModule alive.
  void SampleTopTierCodeSizeInAllIsolates(const std::shared_ptr<NativeModule>&);

198 199 200 201 202
  // Called by each Isolate to report its live code for a GC cycle. First
  // version reports an externally determined set of live code (might be empty),
  // second version gets live code from the execution stack of that isolate.
  void ReportLiveCodeForGC(Isolate*, Vector<WasmCode*>);
  void ReportLiveCodeFromStackForGC(Isolate*);
203

204 205 206 207 208 209 210
  // Add potentially dead code. The occurrence in the set of potentially dead
  // code counts as a reference, and is decremented on the next GC.
  // Returns {true} if the code was added to the set of potentially dead code,
  // {false} if an entry already exists. The ref count is *unchanged* in any
  // case.
  V8_WARN_UNUSED_RESULT bool AddPotentiallyDeadCode(WasmCode*);

211 212 213 214
  // Free dead code.
  using DeadCodeMap = std::unordered_map<NativeModule*, std::vector<WasmCode*>>;
  void FreeDeadCode(const DeadCodeMap&);
  void FreeDeadCodeLocked(const DeadCodeMap&);
215

216 217 218 219 220 221 222 223
  // Call on process start and exit.
  static void InitializeOncePerProcess();
  static void GlobalTearDown();

  // Constructs a WasmEngine instance. Depending on whether we are sharing
  // engines this might be a pointer to a new instance or to a shared one.
  static std::shared_ptr<WasmEngine> GetWasmEngine();

224
 private:
225
  struct CurrentGCInfo;
226
  struct IsolateInfo;
227
  struct NativeModuleInfo;
228

229
  AsyncCompileJob* CreateAsyncCompileJob(
230 231
      Isolate* isolate, const WasmFeatures& enabled,
      std::unique_ptr<byte[]> bytes_copy, size_t length,
232
      Handle<Context> context, const char* api_method_name,
233
      std::shared_ptr<CompilationResultResolver> resolver);
234

235
  void TriggerGC(int8_t gc_sequence_index);
236

237 238 239 240 241 242 243 244 245
  // Remove an isolate from the outstanding isolates of the current GC. Returns
  // true if the isolate was still outstanding, false otherwise. Hold {mutex_}
  // when calling this method.
  bool RemoveIsolateFromCurrentGC(Isolate*);

  // Finish a GC if there are no more outstanding isolates. Hold {mutex_} when
  // calling this method.
  void PotentiallyFinishCurrentGC();

246
  WasmMemoryTracker memory_tracker_;
247
  WasmCodeManager code_manager_;
248
  AccountingAllocator allocator_;
249

250 251 252 253
  // Task manager managing all background compile jobs. Before shut down of the
  // engine, they must all be finished because they access the allocator.
  CancelableTaskManager background_compile_task_manager_;

254 255 256 257 258 259 260
  // This mutex protects all information which is mutated concurrently or
  // fields that are initialized lazily on the first access.
  base::Mutex mutex_;

  //////////////////////////////////////////////////////////////////////////////
  // Protected by {mutex_}:

261 262
  // We use an AsyncCompileJob as the key for itself so that we can delete the
  // job from the map when it is finished.
263 264
  std::unordered_map<AsyncCompileJob*, std::unique_ptr<AsyncCompileJob>>
      async_compile_jobs_;
265

266 267 268
  std::unique_ptr<CompilationStatistics> compilation_stats_;
  std::unique_ptr<CodeTracer> code_tracer_;

269 270 271
  // Set of isolates which use this WasmEngine.
  std::unordered_map<Isolate*, std::unique_ptr<IsolateInfo>> isolates_;

272 273 274 275 276 277 278
  // Set of native modules managed by this engine.
  std::unordered_map<NativeModule*, std::unique_ptr<NativeModuleInfo>>
      native_modules_;

  // Size of code that became dead since the last GC. If this exceeds a certain
  // threshold, a new GC is triggered.
  size_t new_potentially_dead_code_size_ = 0;
279

280 281 282 283
  // If an engine-wide GC is currently running, this pointer stores information
  // about that.
  std::unique_ptr<CurrentGCInfo> current_gc_info_;

284 285 286
  // End of fields protected by {mutex_}.
  //////////////////////////////////////////////////////////////////////////////

287
  DISALLOW_COPY_AND_ASSIGN(WasmEngine);
288 289 290 291 292 293
};

}  // namespace wasm
}  // namespace internal
}  // namespace v8

294
#endif  // V8_WASM_WASM_ENGINE_H_