xref: /third_party/node/deps/v8/src/wasm/wasm-engine.cc (revision 1cb0ef41)
1// Copyright 2018 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#include "src/wasm/wasm-engine.h"
6
7#include "src/base/functional.h"
8#include "src/base/platform/time.h"
9#include "src/common/globals.h"
10#include "src/diagnostics/code-tracer.h"
11#include "src/diagnostics/compilation-statistics.h"
12#include "src/execution/frames.h"
13#include "src/execution/v8threads.h"
14#include "src/handles/global-handles-inl.h"
15#include "src/logging/counters.h"
16#include "src/logging/metrics.h"
17#include "src/objects/heap-number.h"
18#include "src/objects/js-promise.h"
19#include "src/objects/managed-inl.h"
20#include "src/objects/objects-inl.h"
21#include "src/strings/string-hasher-inl.h"
22#include "src/utils/ostreams.h"
23#include "src/wasm/function-compiler.h"
24#include "src/wasm/memory-protection-key.h"
25#include "src/wasm/module-compiler.h"
26#include "src/wasm/module-decoder.h"
27#include "src/wasm/module-instantiate.h"
28#include "src/wasm/streaming-decoder.h"
29#include "src/wasm/wasm-debug.h"
30#include "src/wasm/wasm-limits.h"
31#include "src/wasm/wasm-objects-inl.h"
32
33#ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
34#include "src/base/platform/wrappers.h"
35#include "src/debug/wasm/gdb-server/gdb-server.h"
36#endif  // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
37
38namespace v8 {
39namespace internal {
40namespace wasm {
41
42#define TRACE_CODE_GC(...)                                         \
43  do {                                                             \
44    if (FLAG_trace_wasm_code_gc) PrintF("[wasm-gc] " __VA_ARGS__); \
45  } while (false)
46
47namespace {
48// A task to log a set of {WasmCode} objects in an isolate. It does not own any
49// data itself, since it is owned by the platform, so lifetime is not really
50// bound to the wasm engine.
51class LogCodesTask : public Task {
52 public:
53  LogCodesTask(base::Mutex* mutex, LogCodesTask** task_slot, Isolate* isolate,
54               WasmEngine* engine)
55      : mutex_(mutex),
56        task_slot_(task_slot),
57        isolate_(isolate),
58        engine_(engine) {
59    DCHECK_NOT_NULL(task_slot);
60    DCHECK_NOT_NULL(isolate);
61  }
62
63  ~LogCodesTask() override {
64    // If the platform deletes this task before executing it, we also deregister
65    // it to avoid use-after-free from still-running background threads.
66    if (!cancelled()) DeregisterTask();
67  }
68
69  void Run() override {
70    if (cancelled()) return;
71    DeregisterTask();
72    engine_->LogOutstandingCodesForIsolate(isolate_);
73  }
74
75  void Cancel() {
76    // Cancel will only be called on Isolate shutdown, which happens on the
77    // Isolate's foreground thread. Thus no synchronization needed.
78    isolate_ = nullptr;
79  }
80
81  bool cancelled() const { return isolate_ == nullptr; }
82
83  void DeregisterTask() {
84    // The task will only be deregistered from the foreground thread (executing
85    // this task or calling its destructor), thus we do not need synchronization
86    // on this field access.
87    if (task_slot_ == nullptr) return;  // already deregistered.
88    // Remove this task from the {IsolateInfo} in the engine. The next
89    // logging request will allocate and schedule a new task.
90    base::MutexGuard guard(mutex_);
91    DCHECK_EQ(this, *task_slot_);
92    *task_slot_ = nullptr;
93    task_slot_ = nullptr;
94  }
95
96 private:
97  // The mutex of the WasmEngine.
98  base::Mutex* const mutex_;
99  // The slot in the WasmEngine where this LogCodesTask is stored. This is
100  // cleared by this task before execution or on task destruction.
101  LogCodesTask** task_slot_;
102  Isolate* isolate_;
103  WasmEngine* const engine_;
104};
105
106void CheckNoArchivedThreads(Isolate* isolate) {
107  class ArchivedThreadsVisitor : public ThreadVisitor {
108    void VisitThread(Isolate* isolate, ThreadLocalTop* top) override {
109      // Archived threads are rarely used, and not combined with Wasm at the
110      // moment. Implement this and test it properly once we have a use case for
111      // that.
112      FATAL("archived threads in combination with wasm not supported");
113    }
114  } archived_threads_visitor;
115  isolate->thread_manager()->IterateArchivedThreads(&archived_threads_visitor);
116}
117
118class WasmGCForegroundTask : public CancelableTask {
119 public:
120  explicit WasmGCForegroundTask(Isolate* isolate)
121      : CancelableTask(isolate->cancelable_task_manager()), isolate_(isolate) {}
122
123  void RunInternal() final {
124    // The stack can contain live frames, for instance when this is invoked
125    // during a pause or a breakpoint.
126    GetWasmEngine()->ReportLiveCodeFromStackForGC(isolate_);
127  }
128
129 private:
130  Isolate* isolate_;
131};
132
133class WeakScriptHandle {
134 public:
135  explicit WeakScriptHandle(Handle<Script> script) : script_id_(script->id()) {
136    DCHECK(script->name().IsString() || script->name().IsUndefined());
137    if (script->name().IsString()) {
138      std::unique_ptr<char[]> source_url =
139          String::cast(script->name()).ToCString();
140      // Convert from {unique_ptr} to {shared_ptr}.
141      source_url_ = {source_url.release(), source_url.get_deleter()};
142    }
143    auto global_handle =
144        script->GetIsolate()->global_handles()->Create(*script);
145    location_ = std::make_unique<Address*>(global_handle.location());
146    GlobalHandles::MakeWeak(location_.get());
147  }
148
149  // Usually the destructor of this class should always be called after the weak
150  // callback because the Script keeps the NativeModule alive. So we expect the
151  // handle to be destroyed and the location to be reset already.
152  // We cannot check this because of one exception. When the native module is
153  // freed during isolate shutdown, the destructor will be called
154  // first, and the callback will never be called.
155  ~WeakScriptHandle() = default;
156
157  WeakScriptHandle(WeakScriptHandle&&) V8_NOEXCEPT = default;
158
159  Handle<Script> handle() const { return Handle<Script>(*location_); }
160
161  int script_id() const { return script_id_; }
162
163  const std::shared_ptr<const char>& source_url() const { return source_url_; }
164
165 private:
166  // Store the location in a unique_ptr so that its address stays the same even
167  // when this object is moved/copied.
168  std::unique_ptr<Address*> location_;
169
170  // Store the script ID independent of the weak handle, such that it's always
171  // available.
172  int script_id_;
173
174  // Similar for the source URL. We cannot dereference the Handle from arbitrary
175  // threads, but we need the URL available for code logging.
176  // The shared pointer is kept alive by unlogged code, even if this entry is
177  // collected in the meantime.
178  // TODO(chromium:1132260): Revisit this for huge URLs.
179  std::shared_ptr<const char> source_url_;
180};
181
182}  // namespace
183
184std::shared_ptr<NativeModule> NativeModuleCache::MaybeGetNativeModule(
185    ModuleOrigin origin, base::Vector<const uint8_t> wire_bytes) {
186  if (origin != kWasmOrigin) return nullptr;
187  base::MutexGuard lock(&mutex_);
188  size_t prefix_hash = PrefixHash(wire_bytes);
189  NativeModuleCache::Key key{prefix_hash, wire_bytes};
190  while (true) {
191    auto it = map_.find(key);
192    if (it == map_.end()) {
193      // Even though this exact key is not in the cache, there might be a
194      // matching prefix hash indicating that a streaming compilation is
195      // currently compiling a module with the same prefix. {OnFinishedStream}
196      // happens on the main thread too, so waiting for streaming compilation to
197      // finish would create a deadlock. Instead, compile the module twice and
198      // handle the conflict in {UpdateNativeModuleCache}.
199
200      // Insert a {nullopt} entry to let other threads know that this
201      // {NativeModule} is already being created on another thread.
202      auto p = map_.emplace(key, base::nullopt);
203      USE(p);
204      DCHECK(p.second);
205      return nullptr;
206    }
207    if (it->second.has_value()) {
208      if (auto shared_native_module = it->second.value().lock()) {
209        DCHECK_EQ(shared_native_module->wire_bytes(), wire_bytes);
210        return shared_native_module;
211      }
212    }
213    // TODO(11858): This deadlocks in predictable mode, because there is only a
214    // single thread.
215    cache_cv_.Wait(&mutex_);
216  }
217}
218
219bool NativeModuleCache::GetStreamingCompilationOwnership(size_t prefix_hash) {
220  base::MutexGuard lock(&mutex_);
221  auto it = map_.lower_bound(Key{prefix_hash, {}});
222  if (it != map_.end() && it->first.prefix_hash == prefix_hash) {
223    DCHECK_IMPLIES(!it->first.bytes.empty(),
224                   PrefixHash(it->first.bytes) == prefix_hash);
225    return false;
226  }
227  Key key{prefix_hash, {}};
228  DCHECK_EQ(0, map_.count(key));
229  map_.emplace(key, base::nullopt);
230  return true;
231}
232
233void NativeModuleCache::StreamingCompilationFailed(size_t prefix_hash) {
234  base::MutexGuard lock(&mutex_);
235  Key key{prefix_hash, {}};
236  DCHECK_EQ(1, map_.count(key));
237  map_.erase(key);
238  cache_cv_.NotifyAll();
239}
240
241std::shared_ptr<NativeModule> NativeModuleCache::Update(
242    std::shared_ptr<NativeModule> native_module, bool error) {
243  DCHECK_NOT_NULL(native_module);
244  if (native_module->module()->origin != kWasmOrigin) return native_module;
245  base::Vector<const uint8_t> wire_bytes = native_module->wire_bytes();
246  DCHECK(!wire_bytes.empty());
247  size_t prefix_hash = PrefixHash(native_module->wire_bytes());
248  base::MutexGuard lock(&mutex_);
249  map_.erase(Key{prefix_hash, {}});
250  const Key key{prefix_hash, wire_bytes};
251  auto it = map_.find(key);
252  if (it != map_.end()) {
253    if (it->second.has_value()) {
254      auto conflicting_module = it->second.value().lock();
255      if (conflicting_module != nullptr) {
256        DCHECK_EQ(conflicting_module->wire_bytes(), wire_bytes);
257        return conflicting_module;
258      }
259    }
260    map_.erase(it);
261  }
262  if (!error) {
263    // The key now points to the new native module's owned copy of the bytes,
264    // so that it stays valid until the native module is freed and erased from
265    // the map.
266    auto p = map_.emplace(
267        key, base::Optional<std::weak_ptr<NativeModule>>(native_module));
268    USE(p);
269    DCHECK(p.second);
270  }
271  cache_cv_.NotifyAll();
272  return native_module;
273}
274
275void NativeModuleCache::Erase(NativeModule* native_module) {
276  if (native_module->module()->origin != kWasmOrigin) return;
277  // Happens in some tests where bytes are set directly.
278  if (native_module->wire_bytes().empty()) return;
279  base::MutexGuard lock(&mutex_);
280  size_t prefix_hash = PrefixHash(native_module->wire_bytes());
281  map_.erase(Key{prefix_hash, native_module->wire_bytes()});
282  cache_cv_.NotifyAll();
283}
284
285// static
286size_t NativeModuleCache::WireBytesHash(base::Vector<const uint8_t> bytes) {
287  return StringHasher::HashSequentialString(
288      reinterpret_cast<const char*>(bytes.begin()), bytes.length(),
289      kZeroHashSeed);
290}
291
292// static
293size_t NativeModuleCache::PrefixHash(base::Vector<const uint8_t> wire_bytes) {
294  // Compute the hash as a combined hash of the sections up to the code section
295  // header, to mirror the way streaming compilation does it.
296  Decoder decoder(wire_bytes.begin(), wire_bytes.end());
297  decoder.consume_bytes(8, "module header");
298  size_t hash = NativeModuleCache::WireBytesHash(wire_bytes.SubVector(0, 8));
299  SectionCode section_id = SectionCode::kUnknownSectionCode;
300  while (decoder.ok() && decoder.more()) {
301    section_id = static_cast<SectionCode>(decoder.consume_u8());
302    uint32_t section_size = decoder.consume_u32v("section size");
303    if (section_id == SectionCode::kCodeSectionCode) {
304      uint32_t num_functions = decoder.consume_u32v("num functions");
305      // If {num_functions} is 0, the streaming decoder skips the section. Do
306      // the same here to ensure hashes are consistent.
307      if (num_functions != 0) {
308        hash = base::hash_combine(hash, section_size);
309      }
310      break;
311    }
312    const uint8_t* payload_start = decoder.pc();
313    decoder.consume_bytes(section_size, "section payload");
314    size_t section_hash = NativeModuleCache::WireBytesHash(
315        base::Vector<const uint8_t>(payload_start, section_size));
316    hash = base::hash_combine(hash, section_hash);
317  }
318  return hash;
319}
320
321struct WasmEngine::CurrentGCInfo {
322  explicit CurrentGCInfo(int8_t gc_sequence_index)
323      : gc_sequence_index(gc_sequence_index) {
324    DCHECK_NE(0, gc_sequence_index);
325  }
326
327  // Set of isolates that did not scan their stack yet for used WasmCode, and
328  // their scheduled foreground task.
329  std::unordered_map<Isolate*, WasmGCForegroundTask*> outstanding_isolates;
330
331  // Set of dead code. Filled with all potentially dead code on initialization.
332  // Code that is still in-use is removed by the individual isolates.
333  std::unordered_set<WasmCode*> dead_code;
334
335  // The number of GCs triggered in the native module that triggered this GC.
336  // This is stored in the histogram for each participating isolate during
337  // execution of that isolate's foreground task.
338  const int8_t gc_sequence_index;
339
340  // If during this GC, another GC was requested, we skipped that other GC (we
341  // only run one GC at a time). Remember though to trigger another one once
342  // this one finishes. {next_gc_sequence_index} is 0 if no next GC is needed,
343  // and >0 otherwise. It stores the {num_code_gcs_triggered} of the native
344  // module which triggered the next GC.
345  int8_t next_gc_sequence_index = 0;
346
347  // The start time of this GC; used for tracing and sampled via {Counters}.
348  // Can be null ({TimeTicks::IsNull()}) if timer is not high resolution.
349  base::TimeTicks start_time;
350};
351
352struct WasmEngine::IsolateInfo {
353  explicit IsolateInfo(Isolate* isolate)
354      : log_codes(WasmCode::ShouldBeLogged(isolate)),
355        async_counters(isolate->async_counters()),
356        wrapper_compilation_barrier_(std::make_shared<OperationsBarrier>()) {
357    v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
358    v8::Platform* platform = V8::GetCurrentPlatform();
359    foreground_task_runner = platform->GetForegroundTaskRunner(v8_isolate);
360  }
361
362#ifdef DEBUG
363  ~IsolateInfo() {
364    // Before destructing, the {WasmEngine} must have cleared outstanding code
365    // to log.
366    DCHECK_EQ(0, code_to_log.size());
367  }
368#endif
369
370  // All native modules that are being used by this Isolate.
371  std::unordered_set<NativeModule*> native_modules;
372
373  // Scripts created for each native module in this isolate.
374  std::unordered_map<NativeModule*, WeakScriptHandle> scripts;
375
376  // Caches whether code needs to be logged on this isolate.
377  bool log_codes;
378
379  // The currently scheduled LogCodesTask.
380  LogCodesTask* log_codes_task = nullptr;
381
382  // Maps script ID to vector of code objects that still need to be logged, and
383  // the respective source URL.
384  struct CodeToLogPerScript {
385    std::vector<WasmCode*> code;
386    std::shared_ptr<const char> source_url;
387  };
388  std::unordered_map<int, CodeToLogPerScript> code_to_log;
389
390  // The foreground task runner of the isolate (can be called from background).
391  std::shared_ptr<v8::TaskRunner> foreground_task_runner;
392
393  const std::shared_ptr<Counters> async_counters;
394
395  // Keep new modules in tiered down state.
396  bool keep_tiered_down = false;
397
398  // Keep track whether we already added a sample for PKU support (we only want
399  // one sample per Isolate).
400  bool pku_support_sampled = false;
401
402  // Elapsed time since last throw/rethrow/catch event.
403  base::ElapsedTimer throw_timer;
404  base::ElapsedTimer rethrow_timer;
405  base::ElapsedTimer catch_timer;
406
407  // Total number of exception events in this isolate.
408  int throw_count = 0;
409  int rethrow_count = 0;
410  int catch_count = 0;
411
412  // Operations barrier to synchronize on wrapper compilation on isolate
413  // shutdown.
414  // TODO(wasm): Remove this once we can use the generic js-to-wasm wrapper
415  // everywhere.
416  std::shared_ptr<OperationsBarrier> wrapper_compilation_barrier_;
417};
418
419struct WasmEngine::NativeModuleInfo {
420  explicit NativeModuleInfo(std::weak_ptr<NativeModule> native_module)
421      : weak_ptr(std::move(native_module)) {}
422
423  // Weak pointer, to gain back a shared_ptr if needed.
424  std::weak_ptr<NativeModule> weak_ptr;
425
426  // Set of isolates using this NativeModule.
427  std::unordered_set<Isolate*> isolates;
428
429  // Set of potentially dead code. This set holds one ref for each code object,
430  // until code is detected to be really dead. At that point, the ref count is
431  // decremented and code is move to the {dead_code} set. If the code is finally
432  // deleted, it is also removed from {dead_code}.
433  std::unordered_set<WasmCode*> potentially_dead_code;
434
435  // Code that is not being executed in any isolate any more, but the ref count
436  // did not drop to zero yet.
437  std::unordered_set<WasmCode*> dead_code;
438
439  // Number of code GCs triggered because code in this native module became
440  // potentially dead.
441  int8_t num_code_gcs_triggered = 0;
442};
443
444WasmEngine::WasmEngine() = default;
445
446WasmEngine::~WasmEngine() {
447#ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
448  // Synchronize on the GDB-remote thread, if running.
449  gdb_server_.reset();
450#endif  // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
451
452  operations_barrier_->CancelAndWait();
453
454  // All AsyncCompileJobs have been canceled.
455  DCHECK(async_compile_jobs_.empty());
456  // All Isolates have been deregistered.
457  DCHECK(isolates_.empty());
458  // All NativeModules did die.
459  DCHECK(native_modules_.empty());
460  // Native module cache does not leak.
461  DCHECK(native_module_cache_.empty());
462}
463
464bool WasmEngine::SyncValidate(Isolate* isolate, const WasmFeatures& enabled,
465                              const ModuleWireBytes& bytes,
466                              std::string* error_message) {
467  TRACE_EVENT0("v8.wasm", "wasm.SyncValidate");
468  // TODO(titzer): remove dependency on the isolate.
469  if (bytes.start() == nullptr || bytes.length() == 0) {
470    if (error_message) *error_message = "empty module wire bytes";
471    return false;
472  }
473  auto result = DecodeWasmModule(
474      enabled, bytes.start(), bytes.end(), true, kWasmOrigin,
475      isolate->counters(), isolate->metrics_recorder(),
476      isolate->GetOrRegisterRecorderContextId(isolate->native_context()),
477      DecodingMethod::kSync, allocator());
478  if (result.failed() && error_message) {
479    *error_message = result.error().message();
480  }
481  return result.ok();
482}
483
484MaybeHandle<AsmWasmData> WasmEngine::SyncCompileTranslatedAsmJs(
485    Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
486    base::Vector<const byte> asm_js_offset_table_bytes,
487    Handle<HeapNumber> uses_bitset, LanguageMode language_mode) {
488  int compilation_id = next_compilation_id_.fetch_add(1);
489  TRACE_EVENT1("v8.wasm", "wasm.SyncCompileTranslatedAsmJs", "id",
490               compilation_id);
491  ModuleOrigin origin = language_mode == LanguageMode::kSloppy
492                            ? kAsmJsSloppyOrigin
493                            : kAsmJsStrictOrigin;
494  // TODO(leszeks): If we want asm.js in UKM, we should figure out a way to pass
495  // the context id in here.
496  v8::metrics::Recorder::ContextId context_id =
497      v8::metrics::Recorder::ContextId::Empty();
498  ModuleResult result = DecodeWasmModule(
499      WasmFeatures::ForAsmjs(), bytes.start(), bytes.end(), false, origin,
500      isolate->counters(), isolate->metrics_recorder(), context_id,
501      DecodingMethod::kSync, allocator());
502  if (result.failed()) {
503    // This happens once in a while when we have missed some limit check
504    // in the asm parser. Output an error message to help diagnose, but crash.
505    std::cout << result.error().message();
506    UNREACHABLE();
507  }
508
509  result.value()->asm_js_offset_information =
510      std::make_unique<AsmJsOffsetInformation>(asm_js_offset_table_bytes);
511
512  // Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
513  // in {CompileToNativeModule}.
514  Handle<FixedArray> export_wrappers;
515  std::shared_ptr<NativeModule> native_module = CompileToNativeModule(
516      isolate, WasmFeatures::ForAsmjs(), thrower, std::move(result).value(),
517      bytes, &export_wrappers, compilation_id, context_id);
518  if (!native_module) return {};
519
520  return AsmWasmData::New(isolate, std::move(native_module), export_wrappers,
521                          uses_bitset);
522}
523
524Handle<WasmModuleObject> WasmEngine::FinalizeTranslatedAsmJs(
525    Isolate* isolate, Handle<AsmWasmData> asm_wasm_data,
526    Handle<Script> script) {
527  std::shared_ptr<NativeModule> native_module =
528      asm_wasm_data->managed_native_module().get();
529  Handle<FixedArray> export_wrappers =
530      handle(asm_wasm_data->export_wrappers(), isolate);
531  Handle<WasmModuleObject> module_object = WasmModuleObject::New(
532      isolate, std::move(native_module), script, export_wrappers);
533  return module_object;
534}
535
536MaybeHandle<WasmModuleObject> WasmEngine::SyncCompile(
537    Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
538    const ModuleWireBytes& bytes) {
539  int compilation_id = next_compilation_id_.fetch_add(1);
540  TRACE_EVENT1("v8.wasm", "wasm.SyncCompile", "id", compilation_id);
541  v8::metrics::Recorder::ContextId context_id =
542      isolate->GetOrRegisterRecorderContextId(isolate->native_context());
543  ModuleResult result =
544      DecodeWasmModule(enabled, bytes.start(), bytes.end(), false, kWasmOrigin,
545                       isolate->counters(), isolate->metrics_recorder(),
546                       context_id, DecodingMethod::kSync, allocator());
547  if (result.failed()) {
548    thrower->CompileFailed(result.error());
549    return {};
550  }
551
552  // Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
553  // in {CompileToNativeModule}.
554  Handle<FixedArray> export_wrappers;
555  std::shared_ptr<NativeModule> native_module = CompileToNativeModule(
556      isolate, enabled, thrower, std::move(result).value(), bytes,
557      &export_wrappers, compilation_id, context_id);
558  if (!native_module) return {};
559
560#ifdef DEBUG
561  // Ensure that code GC will check this isolate for live code.
562  {
563    base::MutexGuard lock(&mutex_);
564    DCHECK_EQ(1, isolates_.count(isolate));
565    DCHECK_EQ(1, isolates_[isolate]->native_modules.count(native_module.get()));
566    DCHECK_EQ(1, native_modules_.count(native_module.get()));
567    DCHECK_EQ(1, native_modules_[native_module.get()]->isolates.count(isolate));
568  }
569#endif
570
571  constexpr base::Vector<const char> kNoSourceUrl;
572  Handle<Script> script =
573      GetOrCreateScript(isolate, native_module, kNoSourceUrl);
574
575  native_module->LogWasmCodes(isolate, *script);
576
577  // Create the compiled module object and populate with compiled functions
578  // and information needed at instantiation time. This object needs to be
579  // serializable. Instantiation may occur off a deserialized version of this
580  // object.
581  Handle<WasmModuleObject> module_object = WasmModuleObject::New(
582      isolate, std::move(native_module), script, export_wrappers);
583
584  // Finish the Wasm script now and make it public to the debugger.
585  isolate->debug()->OnAfterCompile(script);
586  return module_object;
587}
588
589MaybeHandle<WasmInstanceObject> WasmEngine::SyncInstantiate(
590    Isolate* isolate, ErrorThrower* thrower,
591    Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
592    MaybeHandle<JSArrayBuffer> memory) {
593  TRACE_EVENT0("v8.wasm", "wasm.SyncInstantiate");
594  return InstantiateToInstanceObject(isolate, thrower, module_object, imports,
595                                     memory);
596}
597
598void WasmEngine::AsyncInstantiate(
599    Isolate* isolate, std::unique_ptr<InstantiationResultResolver> resolver,
600    Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports) {
601  ErrorThrower thrower(isolate, "WebAssembly.instantiate()");
602  TRACE_EVENT0("v8.wasm", "wasm.AsyncInstantiate");
603  // Instantiate a TryCatch so that caught exceptions won't progagate out.
604  // They will still be set as pending exceptions on the isolate.
605  // TODO(clemensb): Avoid TryCatch, use Execution::TryCall internally to invoke
606  // start function and report thrown exception explicitly via out argument.
607  v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
608  catcher.SetVerbose(false);
609  catcher.SetCaptureMessage(false);
610
611  MaybeHandle<WasmInstanceObject> instance_object = SyncInstantiate(
612      isolate, &thrower, module_object, imports, Handle<JSArrayBuffer>::null());
613
614  if (!instance_object.is_null()) {
615    resolver->OnInstantiationSucceeded(instance_object.ToHandleChecked());
616    return;
617  }
618
619  if (isolate->has_pending_exception()) {
620    // The JS code executed during instantiation has thrown an exception.
621    // We have to move the exception to the promise chain.
622    Handle<Object> exception(isolate->pending_exception(), isolate);
623    isolate->clear_pending_exception();
624    *isolate->external_caught_exception_address() = false;
625    resolver->OnInstantiationFailed(exception);
626    thrower.Reset();
627  } else {
628    DCHECK(thrower.error());
629    resolver->OnInstantiationFailed(thrower.Reify());
630  }
631}
632
633void WasmEngine::AsyncCompile(
634    Isolate* isolate, const WasmFeatures& enabled,
635    std::shared_ptr<CompilationResultResolver> resolver,
636    const ModuleWireBytes& bytes, bool is_shared,
637    const char* api_method_name_for_errors) {
638  int compilation_id = next_compilation_id_.fetch_add(1);
639  TRACE_EVENT1("v8.wasm", "wasm.AsyncCompile", "id", compilation_id);
640  if (!FLAG_wasm_async_compilation) {
641    // Asynchronous compilation disabled; fall back on synchronous compilation.
642    ErrorThrower thrower(isolate, api_method_name_for_errors);
643    MaybeHandle<WasmModuleObject> module_object;
644    if (is_shared) {
645      // Make a copy of the wire bytes to avoid concurrent modification.
646      std::unique_ptr<uint8_t[]> copy(new uint8_t[bytes.length()]);
647      memcpy(copy.get(), bytes.start(), bytes.length());
648      ModuleWireBytes bytes_copy(copy.get(), copy.get() + bytes.length());
649      module_object = SyncCompile(isolate, enabled, &thrower, bytes_copy);
650    } else {
651      // The wire bytes are not shared, OK to use them directly.
652      module_object = SyncCompile(isolate, enabled, &thrower, bytes);
653    }
654    if (thrower.error()) {
655      resolver->OnCompilationFailed(thrower.Reify());
656      return;
657    }
658    Handle<WasmModuleObject> module = module_object.ToHandleChecked();
659    resolver->OnCompilationSucceeded(module);
660    return;
661  }
662
663  if (FLAG_wasm_test_streaming) {
664    std::shared_ptr<StreamingDecoder> streaming_decoder =
665        StartStreamingCompilation(
666            isolate, enabled, handle(isolate->context(), isolate),
667            api_method_name_for_errors, std::move(resolver));
668    streaming_decoder->OnBytesReceived(bytes.module_bytes());
669    streaming_decoder->Finish();
670    return;
671  }
672  // Make a copy of the wire bytes in case the user program changes them
673  // during asynchronous compilation.
674  std::unique_ptr<byte[]> copy(new byte[bytes.length()]);
675  memcpy(copy.get(), bytes.start(), bytes.length());
676
677  AsyncCompileJob* job = CreateAsyncCompileJob(
678      isolate, enabled, std::move(copy), bytes.length(),
679      handle(isolate->context(), isolate), api_method_name_for_errors,
680      std::move(resolver), compilation_id);
681  job->Start();
682}
683
684std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation(
685    Isolate* isolate, const WasmFeatures& enabled, Handle<Context> context,
686    const char* api_method_name,
687    std::shared_ptr<CompilationResultResolver> resolver) {
688  int compilation_id = next_compilation_id_.fetch_add(1);
689  TRACE_EVENT1("v8.wasm", "wasm.StartStreamingCompilation", "id",
690               compilation_id);
691  if (FLAG_wasm_async_compilation) {
692    AsyncCompileJob* job = CreateAsyncCompileJob(
693        isolate, enabled, std::unique_ptr<byte[]>(nullptr), 0, context,
694        api_method_name, std::move(resolver), compilation_id);
695    return job->CreateStreamingDecoder();
696  }
697  return StreamingDecoder::CreateSyncStreamingDecoder(
698      isolate, enabled, context, api_method_name, std::move(resolver));
699}
700
701void WasmEngine::CompileFunction(Isolate* isolate, NativeModule* native_module,
702                                 uint32_t function_index, ExecutionTier tier) {
703  // Note we assume that "one-off" compilations can discard detected features.
704  WasmFeatures detected = WasmFeatures::None();
705  WasmCompilationUnit::CompileWasmFunction(
706      isolate, native_module, &detected,
707      &native_module->module()->functions[function_index], tier);
708}
709
710void WasmEngine::TierDownAllModulesPerIsolate(Isolate* isolate) {
711  std::vector<std::shared_ptr<NativeModule>> native_modules;
712  {
713    base::MutexGuard lock(&mutex_);
714    if (isolates_[isolate]->keep_tiered_down) return;
715    isolates_[isolate]->keep_tiered_down = true;
716    for (auto* native_module : isolates_[isolate]->native_modules) {
717      native_module->SetTieringState(kTieredDown);
718      DCHECK_EQ(1, native_modules_.count(native_module));
719      if (auto shared_ptr = native_modules_[native_module]->weak_ptr.lock()) {
720        native_modules.emplace_back(std::move(shared_ptr));
721      }
722    }
723  }
724  for (auto& native_module : native_modules) {
725    native_module->RecompileForTiering();
726  }
727}
728
729void WasmEngine::TierUpAllModulesPerIsolate(Isolate* isolate) {
730  // Only trigger recompilation after releasing the mutex, otherwise we risk
731  // deadlocks because of lock inversion. The bool tells whether the module
732  // needs recompilation for tier up.
733  std::vector<std::pair<std::shared_ptr<NativeModule>, bool>> native_modules;
734  {
735    base::MutexGuard lock(&mutex_);
736    isolates_[isolate]->keep_tiered_down = false;
737    auto test_can_tier_up = [this](NativeModule* native_module) {
738      DCHECK_EQ(1, native_modules_.count(native_module));
739      for (auto* isolate : native_modules_[native_module]->isolates) {
740        DCHECK_EQ(1, isolates_.count(isolate));
741        if (isolates_[isolate]->keep_tiered_down) return false;
742      }
743      return true;
744    };
745    for (auto* native_module : isolates_[isolate]->native_modules) {
746      DCHECK_EQ(1, native_modules_.count(native_module));
747      auto shared_ptr = native_modules_[native_module]->weak_ptr.lock();
748      if (!shared_ptr) continue;  // The module is not used any more.
749      if (!native_module->IsTieredDown()) continue;
750      // Only start tier-up if no other isolate needs this module in tiered
751      // down state.
752      bool tier_up = test_can_tier_up(native_module);
753      if (tier_up) native_module->SetTieringState(kTieredUp);
754      native_modules.emplace_back(std::move(shared_ptr), tier_up);
755    }
756  }
757  for (auto& entry : native_modules) {
758    auto& native_module = entry.first;
759    bool tier_up = entry.second;
760    // Remove all breakpoints set by this isolate.
761    if (native_module->HasDebugInfo()) {
762      native_module->GetDebugInfo()->RemoveIsolate(isolate);
763    }
764    if (tier_up) native_module->RecompileForTiering();
765  }
766}
767
768std::shared_ptr<NativeModule> WasmEngine::ExportNativeModule(
769    Handle<WasmModuleObject> module_object) {
770  return module_object->shared_native_module();
771}
772
773namespace {
774Handle<Script> CreateWasmScript(Isolate* isolate,
775                                std::shared_ptr<NativeModule> native_module,
776                                base::Vector<const char> source_url) {
777  Handle<Script> script =
778      isolate->factory()->NewScript(isolate->factory()->undefined_value());
779  script->set_compilation_state(Script::COMPILATION_STATE_COMPILED);
780  script->set_context_data(isolate->native_context()->debug_context_id());
781  script->set_type(Script::TYPE_WASM);
782
783  base::Vector<const uint8_t> wire_bytes = native_module->wire_bytes();
784
785  // The source URL of the script is
786  // - the original source URL if available (from the streaming API),
787  // - wasm://wasm/<module name>-<hash> if a module name has been set, or
788  // - wasm://wasm/<hash> otherwise.
789  const WasmModule* module = native_module->module();
790  Handle<String> url_str;
791  if (!source_url.empty()) {
792    url_str = isolate->factory()
793                  ->NewStringFromUtf8(source_url, AllocationType::kOld)
794                  .ToHandleChecked();
795  } else {
796    int hash = StringHasher::HashSequentialString(
797        reinterpret_cast<const char*>(wire_bytes.begin()), wire_bytes.length(),
798        kZeroHashSeed);
799
800    base::EmbeddedVector<char, 32> buffer;
801    if (module->name.is_empty()) {
802      // Build the URL in the form "wasm://wasm/<hash>".
803      int url_len = SNPrintF(buffer, "wasm://wasm/%08x", hash);
804      DCHECK(url_len >= 0 && url_len < buffer.length());
805      url_str = isolate->factory()
806                    ->NewStringFromUtf8(buffer.SubVector(0, url_len),
807                                        AllocationType::kOld)
808                    .ToHandleChecked();
809    } else {
810      // Build the URL in the form "wasm://wasm/<module name>-<hash>".
811      int hash_len = SNPrintF(buffer, "-%08x", hash);
812      DCHECK(hash_len >= 0 && hash_len < buffer.length());
813      Handle<String> prefix =
814          isolate->factory()->NewStringFromStaticChars("wasm://wasm/");
815      Handle<String> module_name =
816          WasmModuleObject::ExtractUtf8StringFromModuleBytes(
817              isolate, wire_bytes, module->name, kNoInternalize);
818      Handle<String> hash_str =
819          isolate->factory()
820              ->NewStringFromUtf8(buffer.SubVector(0, hash_len))
821              .ToHandleChecked();
822      // Concatenate the three parts.
823      url_str = isolate->factory()
824                    ->NewConsString(prefix, module_name)
825                    .ToHandleChecked();
826      url_str = isolate->factory()
827                    ->NewConsString(url_str, hash_str)
828                    .ToHandleChecked();
829    }
830  }
831  script->set_name(*url_str);
832
833  const WasmDebugSymbols& debug_symbols = module->debug_symbols;
834  if (debug_symbols.type == WasmDebugSymbols::Type::SourceMap &&
835      !debug_symbols.external_url.is_empty()) {
836    base::Vector<const char> external_url =
837        ModuleWireBytes(wire_bytes).GetNameOrNull(debug_symbols.external_url);
838    MaybeHandle<String> src_map_str = isolate->factory()->NewStringFromUtf8(
839        external_url, AllocationType::kOld);
840    script->set_source_mapping_url(*src_map_str.ToHandleChecked());
841  }
842
843  // Use the given shared {NativeModule}, but increase its reference count by
844  // allocating a new {Managed<T>} that the {Script} references.
845  size_t code_size_estimate = native_module->committed_code_space();
846  size_t memory_estimate =
847      code_size_estimate +
848      wasm::WasmCodeManager::EstimateNativeModuleMetaDataSize(module);
849  Handle<Managed<wasm::NativeModule>> managed_native_module =
850      Managed<wasm::NativeModule>::FromSharedPtr(isolate, memory_estimate,
851                                                 std::move(native_module));
852  script->set_wasm_managed_native_module(*managed_native_module);
853  script->set_wasm_breakpoint_infos(ReadOnlyRoots(isolate).empty_fixed_array());
854  script->set_wasm_weak_instance_list(
855      ReadOnlyRoots(isolate).empty_weak_array_list());
856  return script;
857}
858}  // namespace
859
860Handle<WasmModuleObject> WasmEngine::ImportNativeModule(
861    Isolate* isolate, std::shared_ptr<NativeModule> shared_native_module,
862    base::Vector<const char> source_url) {
863  NativeModule* native_module = shared_native_module.get();
864  ModuleWireBytes wire_bytes(native_module->wire_bytes());
865  Handle<Script> script =
866      GetOrCreateScript(isolate, shared_native_module, source_url);
867  Handle<FixedArray> export_wrappers;
868  CompileJsToWasmWrappers(isolate, native_module->module(), &export_wrappers);
869  Handle<WasmModuleObject> module_object = WasmModuleObject::New(
870      isolate, std::move(shared_native_module), script, export_wrappers);
871  {
872    base::MutexGuard lock(&mutex_);
873    DCHECK_EQ(1, isolates_.count(isolate));
874    isolates_[isolate]->native_modules.insert(native_module);
875    DCHECK_EQ(1, native_modules_.count(native_module));
876    native_modules_[native_module]->isolates.insert(isolate);
877  }
878
879  // Finish the Wasm script now and make it public to the debugger.
880  isolate->debug()->OnAfterCompile(script);
881  return module_object;
882}
883
884CompilationStatistics* WasmEngine::GetOrCreateTurboStatistics() {
885  base::MutexGuard guard(&mutex_);
886  if (compilation_stats_ == nullptr) {
887    compilation_stats_.reset(new CompilationStatistics());
888  }
889  return compilation_stats_.get();
890}
891
892void WasmEngine::DumpAndResetTurboStatistics() {
893  base::MutexGuard guard(&mutex_);
894  if (compilation_stats_ != nullptr) {
895    StdoutStream os;
896    os << AsPrintableStatistics{*compilation_stats_.get(), false} << std::endl;
897  }
898  compilation_stats_.reset();
899}
900
901void WasmEngine::DumpTurboStatistics() {
902  base::MutexGuard guard(&mutex_);
903  if (compilation_stats_ != nullptr) {
904    StdoutStream os;
905    os << AsPrintableStatistics{*compilation_stats_.get(), false} << std::endl;
906  }
907}
908
909CodeTracer* WasmEngine::GetCodeTracer() {
910  base::MutexGuard guard(&mutex_);
911  if (code_tracer_ == nullptr) code_tracer_.reset(new CodeTracer(-1));
912  return code_tracer_.get();
913}
914
915AsyncCompileJob* WasmEngine::CreateAsyncCompileJob(
916    Isolate* isolate, const WasmFeatures& enabled,
917    std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
918    const char* api_method_name,
919    std::shared_ptr<CompilationResultResolver> resolver, int compilation_id) {
920  Handle<Context> incumbent_context = isolate->GetIncumbentContext();
921  AsyncCompileJob* job = new AsyncCompileJob(
922      isolate, enabled, std::move(bytes_copy), length, context,
923      incumbent_context, api_method_name, std::move(resolver), compilation_id);
924  // Pass ownership to the unique_ptr in {async_compile_jobs_}.
925  base::MutexGuard guard(&mutex_);
926  async_compile_jobs_[job] = std::unique_ptr<AsyncCompileJob>(job);
927  return job;
928}
929
930std::unique_ptr<AsyncCompileJob> WasmEngine::RemoveCompileJob(
931    AsyncCompileJob* job) {
932  base::MutexGuard guard(&mutex_);
933  auto item = async_compile_jobs_.find(job);
934  DCHECK(item != async_compile_jobs_.end());
935  std::unique_ptr<AsyncCompileJob> result = std::move(item->second);
936  async_compile_jobs_.erase(item);
937  return result;
938}
939
940bool WasmEngine::HasRunningCompileJob(Isolate* isolate) {
941  base::MutexGuard guard(&mutex_);
942  DCHECK_EQ(1, isolates_.count(isolate));
943  for (auto& entry : async_compile_jobs_) {
944    if (entry.first->isolate() == isolate) return true;
945  }
946  return false;
947}
948
949void WasmEngine::DeleteCompileJobsOnContext(Handle<Context> context) {
950  // Under the mutex get all jobs to delete. Then delete them without holding
951  // the mutex, such that deletion can reenter the WasmEngine.
952  std::vector<std::unique_ptr<AsyncCompileJob>> jobs_to_delete;
953  {
954    base::MutexGuard guard(&mutex_);
955    for (auto it = async_compile_jobs_.begin();
956         it != async_compile_jobs_.end();) {
957      if (!it->first->context().is_identical_to(context)) {
958        ++it;
959        continue;
960      }
961      jobs_to_delete.push_back(std::move(it->second));
962      it = async_compile_jobs_.erase(it);
963    }
964  }
965}
966
967void WasmEngine::DeleteCompileJobsOnIsolate(Isolate* isolate) {
968  // Under the mutex get all jobs to delete. Then delete them without holding
969  // the mutex, such that deletion can reenter the WasmEngine.
970  std::vector<std::unique_ptr<AsyncCompileJob>> jobs_to_delete;
971  std::vector<std::weak_ptr<NativeModule>> modules_in_isolate;
972  std::shared_ptr<OperationsBarrier> wrapper_compilation_barrier;
973  {
974    base::MutexGuard guard(&mutex_);
975    for (auto it = async_compile_jobs_.begin();
976         it != async_compile_jobs_.end();) {
977      if (it->first->isolate() != isolate) {
978        ++it;
979        continue;
980      }
981      jobs_to_delete.push_back(std::move(it->second));
982      it = async_compile_jobs_.erase(it);
983    }
984    DCHECK_EQ(1, isolates_.count(isolate));
985    auto* isolate_info = isolates_[isolate].get();
986    wrapper_compilation_barrier = isolate_info->wrapper_compilation_barrier_;
987    for (auto* native_module : isolate_info->native_modules) {
988      DCHECK_EQ(1, native_modules_.count(native_module));
989      modules_in_isolate.emplace_back(native_modules_[native_module]->weak_ptr);
990    }
991  }
992
993  // All modules that have not finished initial compilation yet cannot be
994  // shared with other isolates. Hence we cancel their compilation. In
995  // particular, this will cancel wrapper compilation which is bound to this
996  // isolate (this would be a UAF otherwise).
997  for (auto& weak_module : modules_in_isolate) {
998    if (auto shared_module = weak_module.lock()) {
999      shared_module->compilation_state()->CancelInitialCompilation();
1000    }
1001  }
1002
1003  // After cancelling, wait for all current wrapper compilation to actually
1004  // finish.
1005  wrapper_compilation_barrier->CancelAndWait();
1006}
1007
1008OperationsBarrier::Token WasmEngine::StartWrapperCompilation(Isolate* isolate) {
1009  base::MutexGuard guard(&mutex_);
1010  auto isolate_info_it = isolates_.find(isolate);
1011  if (isolate_info_it == isolates_.end()) return {};
1012  return isolate_info_it->second->wrapper_compilation_barrier_->TryLock();
1013}
1014
1015void WasmEngine::AddIsolate(Isolate* isolate) {
1016  base::MutexGuard guard(&mutex_);
1017  DCHECK_EQ(0, isolates_.count(isolate));
1018  isolates_.emplace(isolate, std::make_unique<IsolateInfo>(isolate));
1019
1020  // The isolate might access existing (cached) code without ever compiling any.
1021  // In that case, the current thread might still have the default permissions
1022  // for the memory protection key (== no access). Thus initialize the
1023  // permissions now.
1024  GetWasmCodeManager()->InitializeMemoryProtectionKeyPermissionsIfSupported();
1025
1026  // Install sampling GC callback.
1027  // TODO(v8:7424): For now we sample module sizes in a GC callback. This will
1028  // bias samples towards apps with high memory pressure. We should switch to
1029  // using sampling based on regular intervals independent of the GC.
1030  auto callback = [](v8::Isolate* v8_isolate, v8::GCType type,
1031                     v8::GCCallbackFlags flags, void* data) {
1032    Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
1033    Counters* counters = isolate->counters();
1034    WasmEngine* engine = GetWasmEngine();
1035    base::MutexGuard lock(&engine->mutex_);
1036    DCHECK_EQ(1, engine->isolates_.count(isolate));
1037    for (auto* native_module : engine->isolates_[isolate]->native_modules) {
1038      native_module->SampleCodeSize(counters, NativeModule::kSampling);
1039    }
1040  };
1041  isolate->heap()->AddGCEpilogueCallback(callback, v8::kGCTypeMarkSweepCompact,
1042                                         nullptr);
1043#ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
1044  if (gdb_server_) {
1045    gdb_server_->AddIsolate(isolate);
1046  }
1047#endif  // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
1048}
1049
1050void WasmEngine::RemoveIsolate(Isolate* isolate) {
1051#ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
1052  if (gdb_server_) {
1053    gdb_server_->RemoveIsolate(isolate);
1054  }
1055#endif  // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
1056
1057  base::MutexGuard guard(&mutex_);
1058  auto it = isolates_.find(isolate);
1059  DCHECK_NE(isolates_.end(), it);
1060  std::unique_ptr<IsolateInfo> info = std::move(it->second);
1061  isolates_.erase(it);
1062  for (auto* native_module : info->native_modules) {
1063    DCHECK_EQ(1, native_modules_.count(native_module));
1064    DCHECK_EQ(1, native_modules_[native_module]->isolates.count(isolate));
1065    auto* module = native_modules_[native_module].get();
1066    module->isolates.erase(isolate);
1067    if (current_gc_info_) {
1068      for (WasmCode* code : module->potentially_dead_code) {
1069        current_gc_info_->dead_code.erase(code);
1070      }
1071    }
1072    if (native_module->HasDebugInfo()) {
1073      native_module->GetDebugInfo()->RemoveIsolate(isolate);
1074    }
1075  }
1076  if (current_gc_info_) {
1077    if (RemoveIsolateFromCurrentGC(isolate)) PotentiallyFinishCurrentGC();
1078  }
1079  if (auto* task = info->log_codes_task) {
1080    task->Cancel();
1081    for (auto& log_entry : info->code_to_log) {
1082      WasmCode::DecrementRefCount(base::VectorOf(log_entry.second.code));
1083    }
1084    info->code_to_log.clear();
1085  }
1086  DCHECK(info->code_to_log.empty());
1087}
1088
1089void WasmEngine::LogCode(base::Vector<WasmCode*> code_vec) {
1090  if (code_vec.empty()) return;
1091  base::MutexGuard guard(&mutex_);
1092  NativeModule* native_module = code_vec[0]->native_module();
1093  DCHECK_EQ(1, native_modules_.count(native_module));
1094  for (Isolate* isolate : native_modules_[native_module]->isolates) {
1095    DCHECK_EQ(1, isolates_.count(isolate));
1096    IsolateInfo* info = isolates_[isolate].get();
1097    if (info->log_codes == false) continue;
1098    if (info->log_codes_task == nullptr) {
1099      auto new_task = std::make_unique<LogCodesTask>(
1100          &mutex_, &info->log_codes_task, isolate, this);
1101      info->log_codes_task = new_task.get();
1102      info->foreground_task_runner->PostTask(std::move(new_task));
1103    }
1104    if (info->code_to_log.empty()) {
1105      isolate->stack_guard()->RequestLogWasmCode();
1106    }
1107    for (WasmCode* code : code_vec) {
1108      DCHECK_EQ(native_module, code->native_module());
1109      code->IncRef();
1110    }
1111
1112    auto script_it = info->scripts.find(native_module);
1113    // If the script does not yet exist, logging will happen later. If the weak
1114    // handle is cleared already, we also don't need to log any more.
1115    if (script_it == info->scripts.end()) continue;
1116    auto& log_entry = info->code_to_log[script_it->second.script_id()];
1117    if (!log_entry.source_url) {
1118      log_entry.source_url = script_it->second.source_url();
1119    }
1120    log_entry.code.insert(log_entry.code.end(), code_vec.begin(),
1121                          code_vec.end());
1122  }
1123}
1124
1125void WasmEngine::EnableCodeLogging(Isolate* isolate) {
1126  base::MutexGuard guard(&mutex_);
1127  auto it = isolates_.find(isolate);
1128  DCHECK_NE(isolates_.end(), it);
1129  it->second->log_codes = true;
1130}
1131
1132void WasmEngine::LogOutstandingCodesForIsolate(Isolate* isolate) {
1133  // Under the mutex, get the vector of wasm code to log. Then log and decrement
1134  // the ref count without holding the mutex.
1135  std::unordered_map<int, IsolateInfo::CodeToLogPerScript> code_to_log;
1136  {
1137    base::MutexGuard guard(&mutex_);
1138    DCHECK_EQ(1, isolates_.count(isolate));
1139    code_to_log.swap(isolates_[isolate]->code_to_log);
1140  }
1141
1142  // Check again whether we still need to log code.
1143  bool should_log = WasmCode::ShouldBeLogged(isolate);
1144
1145  TRACE_EVENT0("v8.wasm", "wasm.LogCode");
1146  for (auto& pair : code_to_log) {
1147    for (WasmCode* code : pair.second.code) {
1148      if (should_log) {
1149        code->LogCode(isolate, pair.second.source_url.get(), pair.first);
1150      }
1151    }
1152    WasmCode::DecrementRefCount(base::VectorOf(pair.second.code));
1153  }
1154}
1155
1156std::shared_ptr<NativeModule> WasmEngine::NewNativeModule(
1157    Isolate* isolate, const WasmFeatures& enabled,
1158    std::shared_ptr<const WasmModule> module, size_t code_size_estimate) {
1159#ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
1160  if (FLAG_wasm_gdb_remote && !gdb_server_) {
1161    gdb_server_ = gdb_server::GdbServer::Create();
1162    gdb_server_->AddIsolate(isolate);
1163  }
1164#endif  // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
1165
1166  std::shared_ptr<NativeModule> native_module =
1167      GetWasmCodeManager()->NewNativeModule(
1168          isolate, enabled, code_size_estimate, std::move(module));
1169  base::MutexGuard lock(&mutex_);
1170  auto pair = native_modules_.insert(std::make_pair(
1171      native_module.get(), std::make_unique<NativeModuleInfo>(native_module)));
1172  DCHECK(pair.second);  // inserted new entry.
1173  pair.first->second.get()->isolates.insert(isolate);
1174  auto* isolate_info = isolates_[isolate].get();
1175  isolate_info->native_modules.insert(native_module.get());
1176  if (isolate_info->keep_tiered_down) {
1177    native_module->SetTieringState(kTieredDown);
1178  }
1179
1180  // Record memory protection key support.
1181  if (!isolate_info->pku_support_sampled) {
1182    isolate_info->pku_support_sampled = true;
1183    auto* histogram =
1184        isolate->counters()->wasm_memory_protection_keys_support();
1185    bool has_mpk = GetWasmCodeManager()->HasMemoryProtectionKeySupport();
1186    histogram->AddSample(has_mpk ? 1 : 0);
1187  }
1188
1189  isolate->counters()->wasm_modules_per_isolate()->AddSample(
1190      static_cast<int>(isolate_info->native_modules.size()));
1191  isolate->counters()->wasm_modules_per_engine()->AddSample(
1192      static_cast<int>(native_modules_.size()));
1193  return native_module;
1194}
1195
1196std::shared_ptr<NativeModule> WasmEngine::MaybeGetNativeModule(
1197    ModuleOrigin origin, base::Vector<const uint8_t> wire_bytes,
1198    Isolate* isolate) {
1199  TRACE_EVENT1("v8.wasm", "wasm.GetNativeModuleFromCache", "wire_bytes",
1200               wire_bytes.size());
1201  std::shared_ptr<NativeModule> native_module =
1202      native_module_cache_.MaybeGetNativeModule(origin, wire_bytes);
1203  bool recompile_module = false;
1204  if (native_module) {
1205    TRACE_EVENT0("v8.wasm", "CacheHit");
1206    base::MutexGuard guard(&mutex_);
1207    auto& native_module_info = native_modules_[native_module.get()];
1208    if (!native_module_info) {
1209      native_module_info = std::make_unique<NativeModuleInfo>(native_module);
1210    }
1211    native_module_info->isolates.insert(isolate);
1212    isolates_[isolate]->native_modules.insert(native_module.get());
1213    if (isolates_[isolate]->keep_tiered_down) {
1214      native_module->SetTieringState(kTieredDown);
1215      recompile_module = true;
1216    }
1217  }
1218  // Potentially recompile the module for tier down, after releasing the mutex.
1219  if (recompile_module) native_module->RecompileForTiering();
1220  return native_module;
1221}
1222
1223bool WasmEngine::UpdateNativeModuleCache(
1224    bool error, std::shared_ptr<NativeModule>* native_module,
1225    Isolate* isolate) {
1226  // Pass {native_module} by value here to keep it alive until at least after
1227  // we returned from {Update}. Otherwise, we might {Erase} it inside {Update}
1228  // which would lock the mutex twice.
1229  auto prev = native_module->get();
1230  *native_module = native_module_cache_.Update(*native_module, error);
1231
1232  if (prev == native_module->get()) return true;
1233
1234  bool recompile_module = false;
1235  {
1236    base::MutexGuard guard(&mutex_);
1237    DCHECK_EQ(1, native_modules_.count(native_module->get()));
1238    native_modules_[native_module->get()]->isolates.insert(isolate);
1239    DCHECK_EQ(1, isolates_.count(isolate));
1240    isolates_[isolate]->native_modules.insert(native_module->get());
1241    if (isolates_[isolate]->keep_tiered_down) {
1242      native_module->get()->SetTieringState(kTieredDown);
1243      recompile_module = true;
1244    }
1245  }
1246  // Potentially recompile the module for tier down, after releasing the mutex.
1247  if (recompile_module) native_module->get()->RecompileForTiering();
1248  return false;
1249}
1250
1251bool WasmEngine::GetStreamingCompilationOwnership(size_t prefix_hash) {
1252  TRACE_EVENT0("v8.wasm", "wasm.GetStreamingCompilationOwnership");
1253  if (native_module_cache_.GetStreamingCompilationOwnership(prefix_hash)) {
1254    return true;
1255  }
1256  // This is only a marker, not for tracing execution time. There should be a
1257  // later "wasm.GetNativeModuleFromCache" event for trying to get the module
1258  // from the cache.
1259  TRACE_EVENT0("v8.wasm", "CacheHit");
1260  return false;
1261}
1262
1263void WasmEngine::StreamingCompilationFailed(size_t prefix_hash) {
1264  native_module_cache_.StreamingCompilationFailed(prefix_hash);
1265}
1266
1267void WasmEngine::FreeNativeModule(NativeModule* native_module) {
1268  base::MutexGuard guard(&mutex_);
1269  auto module = native_modules_.find(native_module);
1270  DCHECK_NE(native_modules_.end(), module);
1271  for (Isolate* isolate : module->second->isolates) {
1272    DCHECK_EQ(1, isolates_.count(isolate));
1273    IsolateInfo* info = isolates_[isolate].get();
1274    DCHECK_EQ(1, info->native_modules.count(native_module));
1275    info->native_modules.erase(native_module);
1276    info->scripts.erase(native_module);
1277    // If there are {WasmCode} objects of the deleted {NativeModule}
1278    // outstanding to be logged in this isolate, remove them. Decrementing the
1279    // ref count is not needed, since the {NativeModule} dies anyway.
1280    for (auto& log_entry : info->code_to_log) {
1281      auto part_of_native_module = [native_module](WasmCode* code) {
1282        return code->native_module() == native_module;
1283      };
1284      std::vector<WasmCode*>& code = log_entry.second.code;
1285      auto new_end =
1286          std::remove_if(code.begin(), code.end(), part_of_native_module);
1287      code.erase(new_end, code.end());
1288    }
1289    // Now remove empty entries in {code_to_log}.
1290    for (auto it = info->code_to_log.begin(), end = info->code_to_log.end();
1291         it != end;) {
1292      if (it->second.code.empty()) {
1293        it = info->code_to_log.erase(it);
1294      } else {
1295        ++it;
1296      }
1297    }
1298  }
1299  // If there is a GC running which has references to code contained in the
1300  // deleted {NativeModule}, remove those references.
1301  if (current_gc_info_) {
1302    for (auto it = current_gc_info_->dead_code.begin(),
1303              end = current_gc_info_->dead_code.end();
1304         it != end;) {
1305      if ((*it)->native_module() == native_module) {
1306        it = current_gc_info_->dead_code.erase(it);
1307      } else {
1308        ++it;
1309      }
1310    }
1311    TRACE_CODE_GC("Native module %p died, reducing dead code objects to %zu.\n",
1312                  native_module, current_gc_info_->dead_code.size());
1313  }
1314  native_module_cache_.Erase(native_module);
1315  native_modules_.erase(module);
1316}
1317
1318namespace {
1319class SampleTopTierCodeSizeTask : public CancelableTask {
1320 public:
1321  SampleTopTierCodeSizeTask(Isolate* isolate,
1322                            std::weak_ptr<NativeModule> native_module)
1323      : CancelableTask(isolate),
1324        isolate_(isolate),
1325        native_module_(std::move(native_module)) {}
1326
1327  void RunInternal() override {
1328    if (std::shared_ptr<NativeModule> native_module = native_module_.lock()) {
1329      native_module->SampleCodeSize(isolate_->counters(),
1330                                    NativeModule::kAfterTopTier);
1331    }
1332  }
1333
1334 private:
1335  Isolate* const isolate_;
1336  const std::weak_ptr<NativeModule> native_module_;
1337};
1338}  // namespace
1339
1340void WasmEngine::SampleTopTierCodeSizeInAllIsolates(
1341    const std::shared_ptr<NativeModule>& native_module) {
1342  base::MutexGuard lock(&mutex_);
1343  DCHECK_EQ(1, native_modules_.count(native_module.get()));
1344  for (Isolate* isolate : native_modules_[native_module.get()]->isolates) {
1345    DCHECK_EQ(1, isolates_.count(isolate));
1346    IsolateInfo* info = isolates_[isolate].get();
1347    info->foreground_task_runner->PostTask(
1348        std::make_unique<SampleTopTierCodeSizeTask>(isolate, native_module));
1349  }
1350}
1351
1352void WasmEngine::ReportLiveCodeForGC(Isolate* isolate,
1353                                     base::Vector<WasmCode*> live_code) {
1354  TRACE_EVENT0("v8.wasm", "wasm.ReportLiveCodeForGC");
1355  TRACE_CODE_GC("Isolate %d reporting %zu live code objects.\n", isolate->id(),
1356                live_code.size());
1357  base::MutexGuard guard(&mutex_);
1358  // This report might come in late (note that we trigger both a stack guard and
1359  // a foreground task). In that case, ignore it.
1360  if (current_gc_info_ == nullptr) return;
1361  if (!RemoveIsolateFromCurrentGC(isolate)) return;
1362  isolate->counters()->wasm_module_num_triggered_code_gcs()->AddSample(
1363      current_gc_info_->gc_sequence_index);
1364  for (WasmCode* code : live_code) current_gc_info_->dead_code.erase(code);
1365  PotentiallyFinishCurrentGC();
1366}
1367
1368namespace {
1369void ReportLiveCodeFromFrameForGC(
1370    StackFrame* frame, std::unordered_set<wasm::WasmCode*>& live_wasm_code) {
1371  if (frame->type() != StackFrame::WASM) return;
1372  live_wasm_code.insert(WasmFrame::cast(frame)->wasm_code());
1373#if V8_TARGET_ARCH_X64
1374    if (WasmFrame::cast(frame)->wasm_code()->for_debugging()) {
1375      Address osr_target = base::Memory<Address>(WasmFrame::cast(frame)->fp() -
1376                                                 kOSRTargetOffset);
1377      if (osr_target) {
1378        WasmCode* osr_code = GetWasmCodeManager()->LookupCode(osr_target);
1379        DCHECK_NOT_NULL(osr_code);
1380        live_wasm_code.insert(osr_code);
1381      }
1382    }
1383#endif
1384}
1385}  // namespace
1386
1387void WasmEngine::ReportLiveCodeFromStackForGC(Isolate* isolate) {
1388  wasm::WasmCodeRefScope code_ref_scope;
1389  std::unordered_set<wasm::WasmCode*> live_wasm_code;
1390  if (FLAG_experimental_wasm_stack_switching) {
1391    wasm::StackMemory* current = isolate->wasm_stacks();
1392    DCHECK_NOT_NULL(current);
1393    do {
1394      if (current->IsActive()) {
1395        // The active stack's jump buffer does not match the current state, use
1396        // the thread info below instead.
1397        current = current->next();
1398        continue;
1399      }
1400      for (StackFrameIterator it(isolate, current); !it.done(); it.Advance()) {
1401        StackFrame* const frame = it.frame();
1402        ReportLiveCodeFromFrameForGC(frame, live_wasm_code);
1403      }
1404      current = current->next();
1405    } while (current != isolate->wasm_stacks());
1406  }
1407  for (StackFrameIterator it(isolate); !it.done(); it.Advance()) {
1408    StackFrame* const frame = it.frame();
1409    ReportLiveCodeFromFrameForGC(frame, live_wasm_code);
1410  }
1411
1412  CheckNoArchivedThreads(isolate);
1413
1414  ReportLiveCodeForGC(
1415      isolate, base::OwnedVector<WasmCode*>::Of(live_wasm_code).as_vector());
1416}
1417
1418bool WasmEngine::AddPotentiallyDeadCode(WasmCode* code) {
1419  base::MutexGuard guard(&mutex_);
1420  auto it = native_modules_.find(code->native_module());
1421  DCHECK_NE(native_modules_.end(), it);
1422  NativeModuleInfo* info = it->second.get();
1423  if (info->dead_code.count(code)) return false;  // Code is already dead.
1424  auto added = info->potentially_dead_code.insert(code);
1425  if (!added.second) return false;  // An entry already existed.
1426  new_potentially_dead_code_size_ += code->instructions().size();
1427  if (FLAG_wasm_code_gc) {
1428    // Trigger a GC if 64kB plus 10% of committed code are potentially dead.
1429    size_t dead_code_limit =
1430        FLAG_stress_wasm_code_gc
1431            ? 0
1432            : 64 * KB + GetWasmCodeManager()->committed_code_space() / 10;
1433    if (new_potentially_dead_code_size_ > dead_code_limit) {
1434      bool inc_gc_count =
1435          info->num_code_gcs_triggered < std::numeric_limits<int8_t>::max();
1436      if (current_gc_info_ == nullptr) {
1437        if (inc_gc_count) ++info->num_code_gcs_triggered;
1438        TRACE_CODE_GC(
1439            "Triggering GC (potentially dead: %zu bytes; limit: %zu bytes).\n",
1440            new_potentially_dead_code_size_, dead_code_limit);
1441        TriggerGC(info->num_code_gcs_triggered);
1442      } else if (current_gc_info_->next_gc_sequence_index == 0) {
1443        if (inc_gc_count) ++info->num_code_gcs_triggered;
1444        TRACE_CODE_GC(
1445            "Scheduling another GC after the current one (potentially dead: "
1446            "%zu bytes; limit: %zu bytes).\n",
1447            new_potentially_dead_code_size_, dead_code_limit);
1448        current_gc_info_->next_gc_sequence_index = info->num_code_gcs_triggered;
1449        DCHECK_NE(0, current_gc_info_->next_gc_sequence_index);
1450      }
1451    }
1452  }
1453  return true;
1454}
1455
1456void WasmEngine::FreeDeadCode(const DeadCodeMap& dead_code) {
1457  base::MutexGuard guard(&mutex_);
1458  FreeDeadCodeLocked(dead_code);
1459}
1460
1461void WasmEngine::FreeDeadCodeLocked(const DeadCodeMap& dead_code) {
1462  TRACE_EVENT0("v8.wasm", "wasm.FreeDeadCode");
1463  DCHECK(!mutex_.TryLock());
1464  for (auto& dead_code_entry : dead_code) {
1465    NativeModule* native_module = dead_code_entry.first;
1466    const std::vector<WasmCode*>& code_vec = dead_code_entry.second;
1467    DCHECK_EQ(1, native_modules_.count(native_module));
1468    auto* info = native_modules_[native_module].get();
1469    TRACE_CODE_GC("Freeing %zu code object%s of module %p.\n", code_vec.size(),
1470                  code_vec.size() == 1 ? "" : "s", native_module);
1471    for (WasmCode* code : code_vec) {
1472      DCHECK_EQ(1, info->dead_code.count(code));
1473      info->dead_code.erase(code);
1474    }
1475    native_module->FreeCode(base::VectorOf(code_vec));
1476  }
1477}
1478
1479Handle<Script> WasmEngine::GetOrCreateScript(
1480    Isolate* isolate, const std::shared_ptr<NativeModule>& native_module,
1481    base::Vector<const char> source_url) {
1482  {
1483    base::MutexGuard guard(&mutex_);
1484    DCHECK_EQ(1, isolates_.count(isolate));
1485    auto& scripts = isolates_[isolate]->scripts;
1486    auto it = scripts.find(native_module.get());
1487    if (it != scripts.end()) {
1488      Handle<Script> weak_global_handle = it->second.handle();
1489      if (weak_global_handle.is_null()) {
1490        scripts.erase(it);
1491      } else {
1492        return Handle<Script>::New(*weak_global_handle, isolate);
1493      }
1494    }
1495  }
1496  // Temporarily release the mutex to let the GC collect native modules.
1497  auto script = CreateWasmScript(isolate, native_module, source_url);
1498  {
1499    base::MutexGuard guard(&mutex_);
1500    DCHECK_EQ(1, isolates_.count(isolate));
1501    auto& scripts = isolates_[isolate]->scripts;
1502    DCHECK_EQ(0, scripts.count(native_module.get()));
1503    scripts.emplace(native_module.get(), WeakScriptHandle(script));
1504    return script;
1505  }
1506}
1507
1508std::shared_ptr<OperationsBarrier>
1509WasmEngine::GetBarrierForBackgroundCompile() {
1510  return operations_barrier_;
1511}
1512
1513namespace {
1514void SampleExceptionEvent(base::ElapsedTimer* timer, TimedHistogram* counter) {
1515  if (!timer->IsStarted()) {
1516    timer->Start();
1517    return;
1518  }
1519  counter->AddSample(static_cast<int>(timer->Elapsed().InMilliseconds()));
1520  timer->Restart();
1521}
1522}  // namespace
1523
1524void WasmEngine::SampleThrowEvent(Isolate* isolate) {
1525  base::MutexGuard guard(&mutex_);
1526  IsolateInfo* isolate_info = isolates_[isolate].get();
1527  int& throw_count = isolate_info->throw_count;
1528  // To avoid an int overflow, clip the count to the histogram's max value.
1529  throw_count =
1530      std::min(throw_count + 1, isolate->counters()->wasm_throw_count()->max());
1531  isolate->counters()->wasm_throw_count()->AddSample(throw_count);
1532  SampleExceptionEvent(&isolate_info->throw_timer,
1533                       isolate->counters()->wasm_time_between_throws());
1534}
1535
1536void WasmEngine::SampleRethrowEvent(Isolate* isolate) {
1537  base::MutexGuard guard(&mutex_);
1538  IsolateInfo* isolate_info = isolates_[isolate].get();
1539  int& rethrow_count = isolate_info->rethrow_count;
1540  // To avoid an int overflow, clip the count to the histogram's max value.
1541  rethrow_count = std::min(rethrow_count + 1,
1542                           isolate->counters()->wasm_rethrow_count()->max());
1543  isolate->counters()->wasm_rethrow_count()->AddSample(rethrow_count);
1544  SampleExceptionEvent(&isolate_info->rethrow_timer,
1545                       isolate->counters()->wasm_time_between_rethrows());
1546}
1547
1548void WasmEngine::SampleCatchEvent(Isolate* isolate) {
1549  base::MutexGuard guard(&mutex_);
1550  IsolateInfo* isolate_info = isolates_[isolate].get();
1551  int& catch_count = isolate_info->catch_count;
1552  // To avoid an int overflow, clip the count to the histogram's max value.
1553  catch_count =
1554      std::min(catch_count + 1, isolate->counters()->wasm_catch_count()->max());
1555  isolate->counters()->wasm_catch_count()->AddSample(catch_count);
1556  SampleExceptionEvent(&isolate_info->catch_timer,
1557                       isolate->counters()->wasm_time_between_catch());
1558}
1559
1560void WasmEngine::TriggerGC(int8_t gc_sequence_index) {
1561  DCHECK(!mutex_.TryLock());
1562  DCHECK_NULL(current_gc_info_);
1563  DCHECK(FLAG_wasm_code_gc);
1564  new_potentially_dead_code_size_ = 0;
1565  current_gc_info_.reset(new CurrentGCInfo(gc_sequence_index));
1566  // Add all potentially dead code to this GC, and trigger a GC task in each
1567  // isolate.
1568  for (auto& entry : native_modules_) {
1569    NativeModuleInfo* info = entry.second.get();
1570    if (info->potentially_dead_code.empty()) continue;
1571    for (auto* isolate : native_modules_[entry.first]->isolates) {
1572      auto& gc_task = current_gc_info_->outstanding_isolates[isolate];
1573      if (!gc_task) {
1574        auto new_task = std::make_unique<WasmGCForegroundTask>(isolate);
1575        gc_task = new_task.get();
1576        DCHECK_EQ(1, isolates_.count(isolate));
1577        isolates_[isolate]->foreground_task_runner->PostTask(
1578            std::move(new_task));
1579      }
1580      isolate->stack_guard()->RequestWasmCodeGC();
1581    }
1582    for (WasmCode* code : info->potentially_dead_code) {
1583      current_gc_info_->dead_code.insert(code);
1584    }
1585  }
1586  TRACE_CODE_GC(
1587      "Starting GC (nr %d). Number of potentially dead code objects: %zu\n",
1588      current_gc_info_->gc_sequence_index, current_gc_info_->dead_code.size());
1589  // Ensure that there are outstanding isolates that will eventually finish this
1590  // GC. If there are no outstanding isolates, we finish the GC immediately.
1591  PotentiallyFinishCurrentGC();
1592  DCHECK(current_gc_info_ == nullptr ||
1593         !current_gc_info_->outstanding_isolates.empty());
1594}
1595
1596bool WasmEngine::RemoveIsolateFromCurrentGC(Isolate* isolate) {
1597  DCHECK(!mutex_.TryLock());
1598  DCHECK_NOT_NULL(current_gc_info_);
1599  return current_gc_info_->outstanding_isolates.erase(isolate) != 0;
1600}
1601
1602void WasmEngine::PotentiallyFinishCurrentGC() {
1603  DCHECK(!mutex_.TryLock());
1604  TRACE_CODE_GC(
1605      "Remaining dead code objects: %zu; outstanding isolates: %zu.\n",
1606      current_gc_info_->dead_code.size(),
1607      current_gc_info_->outstanding_isolates.size());
1608
1609  // If there are more outstanding isolates, return immediately.
1610  if (!current_gc_info_->outstanding_isolates.empty()) return;
1611
1612  // All remaining code in {current_gc_info->dead_code} is really dead.
1613  // Move it from the set of potentially dead code to the set of dead code,
1614  // and decrement its ref count.
1615  size_t num_freed = 0;
1616  DeadCodeMap dead_code;
1617  for (WasmCode* code : current_gc_info_->dead_code) {
1618    DCHECK_EQ(1, native_modules_.count(code->native_module()));
1619    auto* native_module_info = native_modules_[code->native_module()].get();
1620    DCHECK_EQ(1, native_module_info->potentially_dead_code.count(code));
1621    native_module_info->potentially_dead_code.erase(code);
1622    DCHECK_EQ(0, native_module_info->dead_code.count(code));
1623    native_module_info->dead_code.insert(code);
1624    if (code->DecRefOnDeadCode()) {
1625      dead_code[code->native_module()].push_back(code);
1626      ++num_freed;
1627    }
1628  }
1629
1630  FreeDeadCodeLocked(dead_code);
1631
1632  TRACE_CODE_GC("Found %zu dead code objects, freed %zu.\n",
1633                current_gc_info_->dead_code.size(), num_freed);
1634  USE(num_freed);
1635
1636  int8_t next_gc_sequence_index = current_gc_info_->next_gc_sequence_index;
1637  current_gc_info_.reset();
1638  if (next_gc_sequence_index != 0) TriggerGC(next_gc_sequence_index);
1639}
1640
1641namespace {
1642
1643struct GlobalWasmState {
1644  // Note: The order of fields is important here, as the WasmEngine's destructor
1645  // must run first. It contains a barrier which ensures that background threads
1646  // finished, and that has to happen before the WasmCodeManager gets destroyed.
1647  WasmCodeManager code_manager;
1648  WasmEngine engine;
1649};
1650
1651GlobalWasmState* global_wasm_state = nullptr;
1652
1653}  // namespace
1654
1655// static
1656void WasmEngine::InitializeOncePerProcess() {
1657  InitializeMemoryProtectionKeySupport();
1658  DCHECK_NULL(global_wasm_state);
1659  global_wasm_state = new GlobalWasmState();
1660}
1661
1662// static
1663void WasmEngine::GlobalTearDown() {
1664  // Note: This can be called multiple times in a row (see
1665  // test-api/InitializeAndDisposeMultiple). This is fine, as
1666  // {global_wasm_engine} will be nullptr then.
1667  delete global_wasm_state;
1668  global_wasm_state = nullptr;
1669}
1670
1671WasmEngine* GetWasmEngine() {
1672  DCHECK_NOT_NULL(global_wasm_state);
1673  return &global_wasm_state->engine;
1674}
1675
1676WasmCodeManager* GetWasmCodeManager() {
1677  DCHECK_NOT_NULL(global_wasm_state);
1678  return &global_wasm_state->code_manager;
1679}
1680
1681// {max_mem_pages} is declared in wasm-limits.h.
1682uint32_t max_mem_pages() {
1683  static_assert(
1684      kV8MaxWasmMemoryPages * kWasmPageSize <= JSArrayBuffer::kMaxByteLength,
1685      "Wasm memories must not be bigger than JSArrayBuffers");
1686  STATIC_ASSERT(kV8MaxWasmMemoryPages <= kMaxUInt32);
1687  return std::min(uint32_t{kV8MaxWasmMemoryPages}, FLAG_wasm_max_mem_pages);
1688}
1689
1690// {max_table_init_entries} is declared in wasm-limits.h.
1691uint32_t max_table_init_entries() {
1692  return std::min(uint32_t{kV8MaxWasmTableInitEntries},
1693                  FLAG_wasm_max_table_size);
1694}
1695
1696// {max_module_size} is declared in wasm-limits.h.
1697size_t max_module_size() {
1698  return FLAG_experimental_wasm_allow_huge_modules
1699             ? RoundDown<kSystemPointerSize>(size_t{kMaxInt})
1700             : kV8MaxWasmModuleSize;
1701}
1702
1703#undef TRACE_CODE_GC
1704
1705}  // namespace wasm
1706}  // namespace internal
1707}  // namespace v8
1708