wasm-engine.cc 56.1 KB
Newer Older
1 2 3 4 5
// Copyright 2018 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.

#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/logging/counters.h"
15
#include "src/objects/heap-number.h"
16
#include "src/objects/js-promise.h"
17
#include "src/objects/objects-inl.h"
18
#include "src/strings/string-hasher-inl.h"
19
#include "src/utils/ostreams.h"
20
#include "src/wasm/function-compiler.h"
21
#include "src/wasm/module-compiler.h"
Marja Hölttä's avatar
Marja Hölttä committed
22
#include "src/wasm/module-decoder.h"
23
#include "src/wasm/module-instantiate.h"
Marja Hölttä's avatar
Marja Hölttä committed
24
#include "src/wasm/streaming-decoder.h"
25
#include "src/wasm/wasm-debug.h"
26
#include "src/wasm/wasm-limits.h"
27
#include "src/wasm/wasm-objects-inl.h"
28

29 30 31 32
#ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
#include "src/debug/wasm/gdb-server/gdb-server.h"
#endif  // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING

33 34 35 36
namespace v8 {
namespace internal {
namespace wasm {

37 38 39 40 41
#define TRACE_CODE_GC(...)                                         \
  do {                                                             \
    if (FLAG_trace_wasm_code_gc) PrintF("[wasm-gc] " __VA_ARGS__); \
  } while (false)

42
namespace {
43 44 45
// A task to log a set of {WasmCode} objects in an isolate. It does not own any
// data itself, since it is owned by the platform, so lifetime is not really
// bound to the wasm engine.
46 47
class LogCodesTask : public Task {
 public:
48 49 50 51 52 53
  LogCodesTask(base::Mutex* mutex, LogCodesTask** task_slot, Isolate* isolate,
               WasmEngine* engine)
      : mutex_(mutex),
        task_slot_(task_slot),
        isolate_(isolate),
        engine_(engine) {
54 55 56 57
    DCHECK_NOT_NULL(task_slot);
    DCHECK_NOT_NULL(isolate);
  }

58
  ~LogCodesTask() override {
59 60 61
    // If the platform deletes this task before executing it, we also deregister
    // it to avoid use-after-free from still-running background threads.
    if (!cancelled()) DeregisterTask();
62
  }
63 64

  void Run() override {
65 66
    if (cancelled()) return;
    DeregisterTask();
67
    engine_->LogOutstandingCodesForIsolate(isolate_);
68 69 70 71 72 73 74 75
  }

  void Cancel() {
    // Cancel will only be called on Isolate shutdown, which happens on the
    // Isolate's foreground thread. Thus no synchronization needed.
    isolate_ = nullptr;
  }

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
  bool cancelled() const { return isolate_ == nullptr; }

  void DeregisterTask() {
    // The task will only be deregistered from the foreground thread (executing
    // this task or calling its destructor), thus we do not need synchronization
    // on this field access.
    if (task_slot_ == nullptr) return;  // already deregistered.
    // Remove this task from the {IsolateInfo} in the engine. The next
    // logging request will allocate and schedule a new task.
    base::MutexGuard guard(mutex_);
    DCHECK_EQ(this, *task_slot_);
    *task_slot_ = nullptr;
    task_slot_ = nullptr;
  }

91 92 93 94
 private:
  // The mutex of the WasmEngine.
  base::Mutex* const mutex_;
  // The slot in the WasmEngine where this LogCodesTask is stored. This is
95 96
  // cleared by this task before execution or on task destruction.
  LogCodesTask** task_slot_;
97
  Isolate* isolate_;
98
  WasmEngine* const engine_;
99
};
100

101 102 103 104 105 106 107 108 109 110 111 112
void CheckNoArchivedThreads(Isolate* isolate) {
  class ArchivedThreadsVisitor : public ThreadVisitor {
    void VisitThread(Isolate* isolate, ThreadLocalTop* top) override {
      // Archived threads are rarely used, and not combined with Wasm at the
      // moment. Implement this and test it properly once we have a use case for
      // that.
      FATAL("archived threads in combination with wasm not supported");
    }
  } archived_threads_visitor;
  isolate->thread_manager()->IterateArchivedThreads(&archived_threads_visitor);
}

113
class WasmGCForegroundTask : public CancelableTask {
114
 public:
115 116
  explicit WasmGCForegroundTask(Isolate* isolate)
      : CancelableTask(isolate->cancelable_task_manager()), isolate_(isolate) {}
117

118
  void RunInternal() final {
119
    WasmEngine* engine = isolate_->wasm_engine();
120 121 122
    // The stack can contain live frames, for instance when this is invoked
    // during a pause or a breakpoint.
    engine->ReportLiveCodeFromStackForGC(isolate_);
123 124 125 126 127 128
  }

 private:
  Isolate* isolate_;
};

129 130 131 132 133 134 135 136 137
class WeakScriptHandle {
 public:
  explicit WeakScriptHandle(Handle<Script> handle) {
    auto global_handle =
        handle->GetIsolate()->global_handles()->Create(*handle);
    location_ = std::make_unique<Address*>(global_handle.location());
    GlobalHandles::MakeWeak(location_.get());
  }

138 139 140 141 142 143 144
  // Usually the destructor of this class should always be called after the weak
  // callback because the Script keeps the NativeModule alive. So we expect the
  // handle to be destroyed and the location to be reset already.
  // We cannot check this because of one exception. When the native module is
  // freed during isolate shutdown, the destructor will be called
  // first, and the callback will never be called.
  ~WeakScriptHandle() = default;
145

146
  WeakScriptHandle(WeakScriptHandle&&) V8_NOEXCEPT = default;
147 148 149 150 151 152 153 154 155

  Handle<Script> handle() { return Handle<Script>(*location_); }

 private:
  // Store the location in a unique_ptr so that its address stays the same even
  // when this object is moved/copied.
  std::unique_ptr<Address*> location_;
};

156 157
}  // namespace

158 159 160 161
std::shared_ptr<NativeModule> NativeModuleCache::MaybeGetNativeModule(
    ModuleOrigin origin, Vector<const uint8_t> wire_bytes) {
  if (origin != kWasmOrigin) return nullptr;
  base::MutexGuard lock(&mutex_);
162 163
  size_t prefix_hash = PrefixHash(wire_bytes);
  NativeModuleCache::Key key{prefix_hash, wire_bytes};
164
  while (true) {
165
    auto it = map_.find(key);
166
    if (it == map_.end()) {
167 168 169 170 171 172 173
      // Even though this exact key is not in the cache, there might be a
      // matching prefix hash indicating that a streaming compilation is
      // currently compiling a module with the same prefix. {OnFinishedStream}
      // happens on the main thread too, so waiting for streaming compilation to
      // finish would create a deadlock. Instead, compile the module twice and
      // handle the conflict in {UpdateNativeModuleCache}.

174 175
      // Insert a {nullopt} entry to let other threads know that this
      // {NativeModule} is already being created on another thread.
176 177 178
      auto p = map_.emplace(key, base::nullopt);
      USE(p);
      DCHECK(p.second);
179 180
      return nullptr;
    }
181 182
    if (it->second.has_value()) {
      if (auto shared_native_module = it->second.value().lock()) {
183
        DCHECK_EQ(shared_native_module->wire_bytes(), wire_bytes);
184 185 186 187 188 189 190
        return shared_native_module;
      }
    }
    cache_cv_.Wait(&mutex_);
  }
}

191 192 193 194
bool NativeModuleCache::GetStreamingCompilationOwnership(size_t prefix_hash) {
  base::MutexGuard lock(&mutex_);
  auto it = map_.lower_bound(Key{prefix_hash, {}});
  if (it != map_.end() && it->first.prefix_hash == prefix_hash) {
195 196
    DCHECK_IMPLIES(!it->first.bytes.empty(),
                   PrefixHash(it->first.bytes) == prefix_hash);
197 198 199
    return false;
  }
  Key key{prefix_hash, {}};
200 201 202
  DCHECK_EQ(0, map_.count(key));
  map_.emplace(key, base::nullopt);
  return true;
203 204 205 206
}

void NativeModuleCache::StreamingCompilationFailed(size_t prefix_hash) {
  base::MutexGuard lock(&mutex_);
207 208 209
  Key key{prefix_hash, {}};
  DCHECK_EQ(1, map_.count(key));
  map_.erase(key);
210 211 212 213 214
  cache_cv_.NotifyAll();
}

std::shared_ptr<NativeModule> NativeModuleCache::Update(
    std::shared_ptr<NativeModule> native_module, bool error) {
215
  DCHECK_NOT_NULL(native_module);
216
  if (native_module->module()->origin != kWasmOrigin) return native_module;
217
  Vector<const uint8_t> wire_bytes = native_module->wire_bytes();
218
  DCHECK(!wire_bytes.empty());
219
  size_t prefix_hash = PrefixHash(native_module->wire_bytes());
220
  base::MutexGuard lock(&mutex_);
221 222 223 224 225 226 227
  map_.erase(Key{prefix_hash, {}});
  const Key key{prefix_hash, wire_bytes};
  auto it = map_.find(key);
  if (it != map_.end()) {
    if (it->second.has_value()) {
      auto conflicting_module = it->second.value().lock();
      if (conflicting_module != nullptr) {
228
        DCHECK_EQ(conflicting_module->wire_bytes(), wire_bytes);
229 230 231 232 233
        return conflicting_module;
      }
    }
    map_.erase(it);
  }
234
  if (!error) {
235 236 237 238 239 240 241
    // The key now points to the new native module's owned copy of the bytes,
    // so that it stays valid until the native module is freed and erased from
    // the map.
    auto p = map_.emplace(
        key, base::Optional<std::weak_ptr<NativeModule>>(native_module));
    USE(p);
    DCHECK(p.second);
242 243
  }
  cache_cv_.NotifyAll();
244
  return native_module;
245 246 247
}

void NativeModuleCache::Erase(NativeModule* native_module) {
248 249
  if (native_module->module()->origin != kWasmOrigin) return;
  // Happens in some tests where bytes are set directly.
250
  if (native_module->wire_bytes().empty()) return;
251
  base::MutexGuard lock(&mutex_);
252
  size_t prefix_hash = PrefixHash(native_module->wire_bytes());
253 254
  map_.erase(Key{prefix_hash, native_module->wire_bytes()});
  cache_cv_.NotifyAll();
255 256
}

257 258
// static
size_t NativeModuleCache::WireBytesHash(Vector<const uint8_t> bytes) {
259 260 261 262 263
  return StringHasher::HashSequentialString(
      reinterpret_cast<const char*>(bytes.begin()), bytes.length(),
      kZeroHashSeed);
}

264 265 266 267 268 269 270 271 272 273 274 275
// static
size_t NativeModuleCache::PrefixHash(Vector<const uint8_t> wire_bytes) {
  // Compute the hash as a combined hash of the sections up to the code section
  // header, to mirror the way streaming compilation does it.
  Decoder decoder(wire_bytes.begin(), wire_bytes.end());
  decoder.consume_bytes(8, "module header");
  size_t hash = NativeModuleCache::WireBytesHash(wire_bytes.SubVector(0, 8));
  SectionCode section_id = SectionCode::kUnknownSectionCode;
  while (decoder.ok() && decoder.more()) {
    section_id = static_cast<SectionCode>(decoder.consume_u8());
    uint32_t section_size = decoder.consume_u32v("section size");
    if (section_id == SectionCode::kCodeSectionCode) {
276 277 278 279 280 281
      uint32_t num_functions = decoder.consume_u32v("num functions");
      // If {num_functions} is 0, the streaming decoder skips the section. Do
      // the same here to ensure hashes are consistent.
      if (num_functions != 0) {
        hash = base::hash_combine(hash, section_size);
      }
282 283 284 285 286 287 288 289 290 291 292
      break;
    }
    const uint8_t* payload_start = decoder.pc();
    decoder.consume_bytes(section_size, "section payload");
    size_t section_hash = NativeModuleCache::WireBytesHash(
        Vector<const uint8_t>(payload_start, section_size));
    hash = base::hash_combine(hash, section_hash);
  }
  return hash;
}

293
struct WasmEngine::CurrentGCInfo {
294 295 296 297 298
  explicit CurrentGCInfo(int8_t gc_sequence_index)
      : gc_sequence_index(gc_sequence_index) {
    DCHECK_NE(0, gc_sequence_index);
  }

299 300 301 302 303 304 305
  // Set of isolates that did not scan their stack yet for used WasmCode, and
  // their scheduled foreground task.
  std::unordered_map<Isolate*, WasmGCForegroundTask*> outstanding_isolates;

  // Set of dead code. Filled with all potentially dead code on initialization.
  // Code that is still in-use is removed by the individual isolates.
  std::unordered_set<WasmCode*> dead_code;
306

307 308 309 310 311
  // The number of GCs triggered in the native module that triggered this GC.
  // This is stored in the histogram for each participating isolate during
  // execution of that isolate's foreground task.
  const int8_t gc_sequence_index;

312 313
  // If during this GC, another GC was requested, we skipped that other GC (we
  // only run one GC at a time). Remember though to trigger another one once
314 315 316 317
  // this one finishes. {next_gc_sequence_index} is 0 if no next GC is needed,
  // and >0 otherwise. It stores the {num_code_gcs_triggered} of the native
  // module which triggered the next GC.
  int8_t next_gc_sequence_index = 0;
318 319 320 321

  // The start time of this GC; used for tracing and sampled via {Counters}.
  // Can be null ({TimeTicks::IsNull()}) if timer is not high resolution.
  base::TimeTicks start_time;
322 323
};

324
struct WasmEngine::IsolateInfo {
325
  explicit IsolateInfo(Isolate* isolate)
326 327
      : log_codes(WasmCode::ShouldBeLogged(isolate)),
        async_counters(isolate->async_counters()) {
328 329 330 331 332
    v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
    v8::Platform* platform = V8::GetCurrentPlatform();
    foreground_task_runner = platform->GetForegroundTaskRunner(v8_isolate);
  }

333 334 335 336 337 338 339 340
#ifdef DEBUG
  ~IsolateInfo() {
    // Before destructing, the {WasmEngine} must have cleared outstanding code
    // to log.
    DCHECK_EQ(0, code_to_log.size());
  }
#endif

341
  // All native modules that are being used by this Isolate.
342
  std::unordered_set<NativeModule*> native_modules;
343

344 345 346
  // Scripts created for each native module in this isolate.
  std::unordered_map<NativeModule*, WeakScriptHandle> scripts;

347 348 349
  // Caches whether code needs to be logged on this isolate.
  bool log_codes;

350 351 352
  // The currently scheduled LogCodesTask.
  LogCodesTask* log_codes_task = nullptr;

353 354 355
  // The vector of code objects that still need to be logged in this isolate.
  std::vector<WasmCode*> code_to_log;

356 357
  // The foreground task runner of the isolate (can be called from background).
  std::shared_ptr<v8::TaskRunner> foreground_task_runner;
358 359

  const std::shared_ptr<Counters> async_counters;
360 361 362

  // Keep new modules in tiered down state.
  bool keep_tiered_down = false;
363 364
};

365
struct WasmEngine::NativeModuleInfo {
366 367 368 369 370 371
  explicit NativeModuleInfo(std::weak_ptr<NativeModule> native_module)
      : weak_ptr(std::move(native_module)) {}

  // Weak pointer, to gain back a shared_ptr if needed.
  std::weak_ptr<NativeModule> weak_ptr;

372 373 374
  // Set of isolates using this NativeModule.
  std::unordered_set<Isolate*> isolates;

375 376 377 378
  // Set of potentially dead code. This set holds one ref for each code object,
  // until code is detected to be really dead. At that point, the ref count is
  // decremented and code is move to the {dead_code} set. If the code is finally
  // deleted, it is also removed from {dead_code}.
379
  std::unordered_set<WasmCode*> potentially_dead_code;
380 381 382 383

  // Code that is not being executed in any isolate any more, but the ref count
  // did not drop to zero yet.
  std::unordered_set<WasmCode*> dead_code;
384 385 386 387

  // Number of code GCs triggered because code in this native module became
  // potentially dead.
  int8_t num_code_gcs_triggered = 0;
388 389
};

390
WasmEngine::WasmEngine() : code_manager_(FLAG_wasm_max_code_space * MB) {}
391

392
WasmEngine::~WasmEngine() {
393 394
#ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
  // Synchronize on the GDB-remote thread, if running.
395
  gdb_server_.reset();
396 397
#endif  // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
  // Collect the live modules into a vector first, then cancel them while
  // releasing our lock. This will allow the background tasks to finish.
  std::vector<std::shared_ptr<NativeModule>> live_modules;
  {
    base::MutexGuard guard(&mutex_);
    for (auto& entry : native_modules_) {
      if (auto shared_ptr = entry.second->weak_ptr.lock()) {
        live_modules.emplace_back(std::move(shared_ptr));
      }
    }
  }

  for (auto& native_module : live_modules) {
    native_module->compilation_state()->CancelCompilation();
  }
  live_modules.clear();

  // Now wait for all background compile tasks to actually finish.
  std::vector<std::shared_ptr<JobHandle>> compile_job_handles;
  {
    base::MutexGuard guard(&mutex_);
    compile_job_handles = compile_job_handles_;
  }
  for (auto& job_handle : compile_job_handles) {
    if (job_handle->IsRunning()) job_handle->Cancel();
  }

425
  // All AsyncCompileJobs have been canceled.
426
  DCHECK(async_compile_jobs_.empty());
427 428
  // All Isolates have been deregistered.
  DCHECK(isolates_.empty());
429
  // All NativeModules did die.
430
  DCHECK(native_modules_.empty());
431 432
  // Native module cache does not leak.
  DCHECK(native_module_cache_.empty());
433
}
434

435 436
bool WasmEngine::SyncValidate(Isolate* isolate, const WasmFeatures& enabled,
                              const ModuleWireBytes& bytes) {
437
  TRACE_EVENT0("v8.wasm", "wasm.SyncValidate");
438 439
  // TODO(titzer): remove dependency on the isolate.
  if (bytes.start() == nullptr || bytes.length() == 0) return false;
440 441 442 443 444
  ModuleResult result = DecodeWasmModule(
      enabled, bytes.start(), bytes.end(), true, kWasmOrigin,
      isolate->counters(), isolate->metrics_recorder(),
      isolate->GetOrRegisterRecorderContextId(isolate->native_context()),
      DecodingMethod::kSync, allocator());
445 446 447
  return result.ok();
}

448
MaybeHandle<AsmWasmData> WasmEngine::SyncCompileTranslatedAsmJs(
449
    Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
450
    Vector<const byte> asm_js_offset_table_bytes,
451
    Handle<HeapNumber> uses_bitset, LanguageMode language_mode) {
452
  TRACE_EVENT0("v8.wasm", "wasm.SyncCompileTranslatedAsmJs");
453 454 455
  ModuleOrigin origin = language_mode == LanguageMode::kSloppy
                            ? kAsmJsSloppyOrigin
                            : kAsmJsStrictOrigin;
456 457 458 459 460
  ModuleResult result = DecodeWasmModule(
      WasmFeatures::ForAsmjs(), bytes.start(), bytes.end(), false, origin,
      isolate->counters(), isolate->metrics_recorder(),
      isolate->GetOrRegisterRecorderContextId(isolate->native_context()),
      DecodingMethod::kSync, allocator());
461 462 463 464 465 466
  if (result.failed()) {
    // This happens once in a while when we have missed some limit check
    // in the asm parser. Output an error message to help diagnose, but crash.
    std::cout << result.error().message();
    UNREACHABLE();
  }
467

468 469 470
  result.value()->asm_js_offset_information =
      std::make_unique<AsmJsOffsetInformation>(asm_js_offset_table_bytes);

471
  // Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
472 473
  // in {CompileToNativeModule}.
  Handle<FixedArray> export_wrappers;
474
  std::shared_ptr<NativeModule> native_module =
475
      CompileToNativeModule(isolate, WasmFeatures::ForAsmjs(), thrower,
476 477 478 479
                            std::move(result).value(), bytes, &export_wrappers);
  if (!native_module) return {};

  return AsmWasmData::New(isolate, std::move(native_module), export_wrappers,
480
                          uses_bitset);
481 482 483 484 485 486
}

Handle<WasmModuleObject> WasmEngine::FinalizeTranslatedAsmJs(
    Isolate* isolate, Handle<AsmWasmData> asm_wasm_data,
    Handle<Script> script) {
  std::shared_ptr<NativeModule> native_module =
487
      asm_wasm_data->managed_native_module().get();
488 489
  Handle<FixedArray> export_wrappers =
      handle(asm_wasm_data->export_wrappers(), isolate);
490 491
  Handle<WasmModuleObject> module_object = WasmModuleObject::New(
      isolate, std::move(native_module), script, export_wrappers);
492
  return module_object;
493 494 495
}

MaybeHandle<WasmModuleObject> WasmEngine::SyncCompile(
496 497
    Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
    const ModuleWireBytes& bytes) {
498
  TRACE_EVENT0("v8.wasm", "wasm.SyncCompile");
499 500 501 502 503
  ModuleResult result = DecodeWasmModule(
      enabled, bytes.start(), bytes.end(), false, kWasmOrigin,
      isolate->counters(), isolate->metrics_recorder(),
      isolate->GetOrRegisterRecorderContextId(isolate->native_context()),
      DecodingMethod::kSync, allocator());
504
  if (result.failed()) {
505
    thrower->CompileFailed(result.error());
506 507 508
    return {};
  }

509
  // Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
510
  // in {CompileToModuleObject}.
511
  Handle<FixedArray> export_wrappers;
512
  std::shared_ptr<NativeModule> native_module =
513 514 515 516
      CompileToNativeModule(isolate, enabled, thrower,
                            std::move(result).value(), bytes, &export_wrappers);
  if (!native_module) return {};

517 518 519 520 521 522 523 524 525 526 527
#ifdef DEBUG
  // Ensure that code GC will check this isolate for live code.
  {
    base::MutexGuard lock(&mutex_);
    DCHECK_EQ(1, isolates_.count(isolate));
    DCHECK_EQ(1, isolates_[isolate]->native_modules.count(native_module.get()));
    DCHECK_EQ(1, native_modules_.count(native_module.get()));
    DCHECK_EQ(1, native_modules_[native_module.get()]->isolates.count(isolate));
  }
#endif

528
  Handle<Script> script = GetOrCreateScript(isolate, native_module);
529 530 531 532 533

  // Create the compiled module object and populate with compiled functions
  // and information needed at instantiation time. This object needs to be
  // serializable. Instantiation may occur off a deserialized version of this
  // object.
534 535
  Handle<WasmModuleObject> module_object = WasmModuleObject::New(
      isolate, std::move(native_module), script, export_wrappers);
536 537 538 539

  // Finish the Wasm script now and make it public to the debugger.
  isolate->debug()->OnAfterCompile(script);
  return module_object;
540 541 542 543 544 545
}

MaybeHandle<WasmInstanceObject> WasmEngine::SyncInstantiate(
    Isolate* isolate, ErrorThrower* thrower,
    Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
    MaybeHandle<JSArrayBuffer> memory) {
546
  TRACE_EVENT0("v8.wasm", "wasm.SyncInstantiate");
547 548 549 550
  return InstantiateToInstanceObject(isolate, thrower, module_object, imports,
                                     memory);
}

551 552 553
void WasmEngine::AsyncInstantiate(
    Isolate* isolate, std::unique_ptr<InstantiationResultResolver> resolver,
    Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports) {
554
  ErrorThrower thrower(isolate, "WebAssembly.instantiate()");
555
  TRACE_EVENT0("v8.wasm", "wasm.AsyncInstantiate");
556 557
  // Instantiate a TryCatch so that caught exceptions won't progagate out.
  // They will still be set as pending exceptions on the isolate.
558
  // TODO(clemensb): Avoid TryCatch, use Execution::TryCall internally to invoke
559 560 561 562 563
  // start function and report thrown exception explicitly via out argument.
  v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
  catcher.SetVerbose(false);
  catcher.SetCaptureMessage(false);

564 565
  MaybeHandle<WasmInstanceObject> instance_object = SyncInstantiate(
      isolate, &thrower, module_object, imports, Handle<JSArrayBuffer>::null());
566 567 568 569 570 571

  if (!instance_object.is_null()) {
    resolver->OnInstantiationSucceeded(instance_object.ToHandleChecked());
    return;
  }

572 573 574
  if (isolate->has_pending_exception()) {
    // The JS code executed during instantiation has thrown an exception.
    // We have to move the exception to the promise chain.
575 576 577 578
    Handle<Object> exception(isolate->pending_exception(), isolate);
    isolate->clear_pending_exception();
    *isolate->external_caught_exception_address() = false;
    resolver->OnInstantiationFailed(exception);
579 580 581 582
    thrower.Reset();
  } else {
    DCHECK(thrower.error());
    resolver->OnInstantiationFailed(thrower.Reify());
583 584 585
  }
}

586
void WasmEngine::AsyncCompile(
587
    Isolate* isolate, const WasmFeatures& enabled,
588
    std::shared_ptr<CompilationResultResolver> resolver,
589 590
    const ModuleWireBytes& bytes, bool is_shared,
    const char* api_method_name_for_errors) {
591
  TRACE_EVENT0("v8.wasm", "wasm.AsyncCompile");
592 593
  if (!FLAG_wasm_async_compilation) {
    // Asynchronous compilation disabled; fall back on synchronous compilation.
594
    ErrorThrower thrower(isolate, api_method_name_for_errors);
595 596 597 598 599
    MaybeHandle<WasmModuleObject> module_object;
    if (is_shared) {
      // Make a copy of the wire bytes to avoid concurrent modification.
      std::unique_ptr<uint8_t[]> copy(new uint8_t[bytes.length()]);
      memcpy(copy.get(), bytes.start(), bytes.length());
600
      ModuleWireBytes bytes_copy(copy.get(), copy.get() + bytes.length());
601
      module_object = SyncCompile(isolate, enabled, &thrower, bytes_copy);
602 603
    } else {
      // The wire bytes are not shared, OK to use them directly.
604
      module_object = SyncCompile(isolate, enabled, &thrower, bytes);
605 606
    }
    if (thrower.error()) {
607
      resolver->OnCompilationFailed(thrower.Reify());
608 609 610
      return;
    }
    Handle<WasmModuleObject> module = module_object.ToHandleChecked();
611
    resolver->OnCompilationSucceeded(module);
612 613 614 615 616
    return;
  }

  if (FLAG_wasm_test_streaming) {
    std::shared_ptr<StreamingDecoder> streaming_decoder =
617 618 619
        StartStreamingCompilation(
            isolate, enabled, handle(isolate->context(), isolate),
            api_method_name_for_errors, std::move(resolver));
620 621 622 623 624 625 626 627
    streaming_decoder->OnBytesReceived(bytes.module_bytes());
    streaming_decoder->Finish();
    return;
  }
  // Make a copy of the wire bytes in case the user program changes them
  // during asynchronous compilation.
  std::unique_ptr<byte[]> copy(new byte[bytes.length()]);
  memcpy(copy.get(), bytes.start(), bytes.length());
628

629 630 631 632
  AsyncCompileJob* job =
      CreateAsyncCompileJob(isolate, enabled, std::move(copy), bytes.length(),
                            handle(isolate->context(), isolate),
                            api_method_name_for_errors, std::move(resolver));
633 634 635 636
  job->Start();
}

std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation(
637
    Isolate* isolate, const WasmFeatures& enabled, Handle<Context> context,
638
    const char* api_method_name,
639
    std::shared_ptr<CompilationResultResolver> resolver) {
640
  TRACE_EVENT0("v8.wasm", "wasm.StartStreamingCompilation");
641 642 643 644 645 646 647 648
  if (FLAG_wasm_async_compilation) {
    AsyncCompileJob* job = CreateAsyncCompileJob(
        isolate, enabled, std::unique_ptr<byte[]>(nullptr), 0, context,
        api_method_name, std::move(resolver));
    return job->CreateStreamingDecoder();
  }
  return StreamingDecoder::CreateSyncStreamingDecoder(
      isolate, enabled, context, api_method_name, std::move(resolver));
649 650
}

651
void WasmEngine::CompileFunction(Isolate* isolate, NativeModule* native_module,
652
                                 uint32_t function_index, ExecutionTier tier) {
653
  // Note we assume that "one-off" compilations can discard detected features.
654
  WasmFeatures detected = WasmFeatures::None();
655
  WasmCompilationUnit::CompileWasmFunction(
656
      isolate, native_module, &detected,
657
      &native_module->module()->functions[function_index], tier);
658 659
}

660
void WasmEngine::TierDownAllModulesPerIsolate(Isolate* isolate) {
661
  std::vector<std::shared_ptr<NativeModule>> native_modules;
662 663
  {
    base::MutexGuard lock(&mutex_);
664 665
    if (isolates_[isolate]->keep_tiered_down) return;
    isolates_[isolate]->keep_tiered_down = true;
666 667 668 669
    for (auto* native_module : isolates_[isolate]->native_modules) {
      native_module->SetTieringState(kTieredDown);
      DCHECK_EQ(1, native_modules_.count(native_module));
      if (auto shared_ptr = native_modules_[native_module]->weak_ptr.lock()) {
670 671
        native_modules.emplace_back(std::move(shared_ptr));
      }
672 673
    }
  }
674
  for (auto& native_module : native_modules) {
675
    native_module->RecompileForTiering();
676 677 678
  }
}

679
void WasmEngine::TierUpAllModulesPerIsolate(Isolate* isolate) {
680
  // Only trigger recompilation after releasing the mutex, otherwise we risk
681 682 683
  // deadlocks because of lock inversion. The bool tells whether the module
  // needs recompilation for tier up.
  std::vector<std::pair<std::shared_ptr<NativeModule>, bool>> native_modules;
684 685 686
  {
    base::MutexGuard lock(&mutex_);
    isolates_[isolate]->keep_tiered_down = false;
687
    auto test_can_tier_up = [this](NativeModule* native_module) {
688 689 690
      DCHECK_EQ(1, native_modules_.count(native_module));
      for (auto* isolate : native_modules_[native_module]->isolates) {
        DCHECK_EQ(1, isolates_.count(isolate));
691
        if (isolates_[isolate]->keep_tiered_down) return false;
692
      }
693
      return true;
694
    };
695
    for (auto* native_module : isolates_[isolate]->native_modules) {
696 697 698
      DCHECK_EQ(1, native_modules_.count(native_module));
      auto shared_ptr = native_modules_[native_module]->weak_ptr.lock();
      if (!shared_ptr) continue;  // The module is not used any more.
699
      if (!native_module->IsTieredDown()) continue;
700
      // Only start tier-up if no other isolate needs this module in tiered
701
      // down state.
702 703 704
      bool tier_up = test_can_tier_up(native_module);
      if (tier_up) native_module->SetTieringState(kTieredUp);
      native_modules.emplace_back(std::move(shared_ptr), tier_up);
705
    }
706
  }
707 708 709 710 711 712 713 714
  for (auto& entry : native_modules) {
    auto& native_module = entry.first;
    bool tier_up = entry.second;
    // Remove all breakpoints set by this isolate.
    if (native_module->HasDebugInfo()) {
      native_module->GetDebugInfo()->RemoveIsolate(isolate);
    }
    if (tier_up) native_module->RecompileForTiering();
715 716 717
  }
}

718 719
std::shared_ptr<NativeModule> WasmEngine::ExportNativeModule(
    Handle<WasmModuleObject> module_object) {
720
  return module_object->shared_native_module();
721 722
}

723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
namespace {
Handle<Script> CreateWasmScript(Isolate* isolate,
                                std::shared_ptr<NativeModule> native_module,
                                Vector<const char> source_url = {}) {
  Handle<Script> script =
      isolate->factory()->NewScript(isolate->factory()->empty_string());
  script->set_compilation_state(Script::COMPILATION_STATE_COMPILED);
  script->set_context_data(isolate->native_context()->debug_context_id());
  script->set_type(Script::TYPE_WASM);

  Vector<const uint8_t> wire_bytes = native_module->wire_bytes();
  int hash = StringHasher::HashSequentialString(
      reinterpret_cast<const char*>(wire_bytes.begin()), wire_bytes.length(),
      kZeroHashSeed);

  const int kBufferSize = 32;
  char buffer[kBufferSize];

  // Script name is "<module_name>-hash" if name is available and "hash"
  // otherwise.
  const WasmModule* module = native_module->module();
  Handle<String> name_str;
  if (module->name.is_set()) {
    int name_chars = SNPrintF(ArrayVector(buffer), "-%08x", hash);
    DCHECK(name_chars >= 0 && name_chars < kBufferSize);
    Handle<String> name_hash =
        isolate->factory()
            ->NewStringFromOneByte(
                VectorOf(reinterpret_cast<uint8_t*>(buffer), name_chars),
                AllocationType::kOld)
            .ToHandleChecked();
    Handle<String> module_name =
        WasmModuleObject::ExtractUtf8StringFromModuleBytes(
            isolate, wire_bytes, module->name, kNoInternalize);
    name_str = isolate->factory()
                   ->NewConsString(module_name, name_hash)
                   .ToHandleChecked();
  } else {
    int name_chars = SNPrintF(ArrayVector(buffer), "%08x", hash);
    DCHECK(name_chars >= 0 && name_chars < kBufferSize);
    name_str = isolate->factory()
                   ->NewStringFromOneByte(
                       VectorOf(reinterpret_cast<uint8_t*>(buffer), name_chars),
                       AllocationType::kOld)
                   .ToHandleChecked();
  }
  script->set_name(*name_str);
  MaybeHandle<String> url_str;
  if (!source_url.empty()) {
    url_str =
        isolate->factory()->NewStringFromUtf8(source_url, AllocationType::kOld);
  } else {
    Handle<String> url_prefix =
        isolate->factory()->InternalizeString(StaticCharVector("wasm://wasm/"));
    url_str = isolate->factory()->NewConsString(url_prefix, name_str);
  }
  script->set_source_url(*url_str.ToHandleChecked());

781 782 783 784 785 786
  const WasmDebugSymbols& debug_symbols =
      native_module->module()->debug_symbols;
  if (debug_symbols.type == WasmDebugSymbols::Type::SourceMap &&
      !debug_symbols.external_url.is_empty()) {
    Vector<const char> external_url =
        ModuleWireBytes(wire_bytes).GetNameOrNull(debug_symbols.external_url);
787
    MaybeHandle<String> src_map_str = isolate->factory()->NewStringFromUtf8(
788
        external_url, AllocationType::kOld);
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
    script->set_source_mapping_url(*src_map_str.ToHandleChecked());
  }

  // Use the given shared {NativeModule}, but increase its reference count by
  // allocating a new {Managed<T>} that the {Script} references.
  size_t code_size_estimate = native_module->committed_code_space();
  size_t memory_estimate =
      code_size_estimate +
      wasm::WasmCodeManager::EstimateNativeModuleMetaDataSize(module);
  Handle<Managed<wasm::NativeModule>> managed_native_module =
      Managed<wasm::NativeModule>::FromSharedPtr(isolate, memory_estimate,
                                                 std::move(native_module));
  script->set_wasm_managed_native_module(*managed_native_module);
  script->set_wasm_breakpoint_infos(ReadOnlyRoots(isolate).empty_fixed_array());
  script->set_wasm_weak_instance_list(
      ReadOnlyRoots(isolate).empty_weak_array_list());
  return script;
}
}  // namespace

809
Handle<WasmModuleObject> WasmEngine::ImportNativeModule(
810 811
    Isolate* isolate, std::shared_ptr<NativeModule> shared_native_module,
    Vector<const char> source_url) {
812
  DCHECK_EQ(this, shared_native_module->engine());
813 814
  NativeModule* native_module = shared_native_module.get();
  ModuleWireBytes wire_bytes(native_module->wire_bytes());
815 816
  Handle<Script> script =
      GetOrCreateScript(isolate, shared_native_module, source_url);
817 818
  Handle<FixedArray> export_wrappers;
  CompileJsToWasmWrappers(isolate, native_module->module(), &export_wrappers);
819
  Handle<WasmModuleObject> module_object = WasmModuleObject::New(
820
      isolate, std::move(shared_native_module), script, export_wrappers);
821 822 823
  {
    base::MutexGuard lock(&mutex_);
    DCHECK_EQ(1, isolates_.count(isolate));
824
    isolates_[isolate]->native_modules.insert(native_module);
825 826
    DCHECK_EQ(1, native_modules_.count(native_module));
    native_modules_[native_module]->isolates.insert(isolate);
827
  }
828 829 830

  // Finish the Wasm script now and make it public to the debugger.
  isolate->debug()->OnAfterCompile(script);
831 832 833
  return module_object;
}

834
CompilationStatistics* WasmEngine::GetOrCreateTurboStatistics() {
835
  base::MutexGuard guard(&mutex_);
836 837 838 839 840 841 842
  if (compilation_stats_ == nullptr) {
    compilation_stats_.reset(new CompilationStatistics());
  }
  return compilation_stats_.get();
}

void WasmEngine::DumpAndResetTurboStatistics() {
843
  base::MutexGuard guard(&mutex_);
844 845 846 847 848 849 850
  if (compilation_stats_ != nullptr) {
    StdoutStream os;
    os << AsPrintableStatistics{*compilation_stats_.get(), false} << std::endl;
  }
  compilation_stats_.reset();
}

851
CodeTracer* WasmEngine::GetCodeTracer() {
852
  base::MutexGuard guard(&mutex_);
853 854 855 856
  if (code_tracer_ == nullptr) code_tracer_.reset(new CodeTracer(-1));
  return code_tracer_.get();
}

857
AsyncCompileJob* WasmEngine::CreateAsyncCompileJob(
858 859
    Isolate* isolate, const WasmFeatures& enabled,
    std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
860
    const char* api_method_name,
861
    std::shared_ptr<CompilationResultResolver> resolver) {
862 863 864 865
  Handle<Context> incumbent_context = isolate->GetIncumbentContext();
  AsyncCompileJob* job = new AsyncCompileJob(
      isolate, enabled, std::move(bytes_copy), length, context,
      incumbent_context, api_method_name, std::move(resolver));
866
  // Pass ownership to the unique_ptr in {async_compile_jobs_}.
867
  base::MutexGuard guard(&mutex_);
868
  async_compile_jobs_[job] = std::unique_ptr<AsyncCompileJob>(job);
869 870 871
  return job;
}

872 873
std::unique_ptr<AsyncCompileJob> WasmEngine::RemoveCompileJob(
    AsyncCompileJob* job) {
874
  base::MutexGuard guard(&mutex_);
875 876
  auto item = async_compile_jobs_.find(job);
  DCHECK(item != async_compile_jobs_.end());
877
  std::unique_ptr<AsyncCompileJob> result = std::move(item->second);
878
  async_compile_jobs_.erase(item);
879
  return result;
880 881
}

882
bool WasmEngine::HasRunningCompileJob(Isolate* isolate) {
883
  base::MutexGuard guard(&mutex_);
884
  DCHECK_EQ(1, isolates_.count(isolate));
885
  for (auto& entry : async_compile_jobs_) {
886 887 888 889 890
    if (entry.first->isolate() == isolate) return true;
  }
  return false;
}

891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
void WasmEngine::DeleteCompileJobsOnContext(Handle<Context> context) {
  // Under the mutex get all jobs to delete. Then delete them without holding
  // the mutex, such that deletion can reenter the WasmEngine.
  std::vector<std::unique_ptr<AsyncCompileJob>> jobs_to_delete;
  {
    base::MutexGuard guard(&mutex_);
    for (auto it = async_compile_jobs_.begin();
         it != async_compile_jobs_.end();) {
      if (!it->first->context().is_identical_to(context)) {
        ++it;
        continue;
      }
      jobs_to_delete.push_back(std::move(it->second));
      it = async_compile_jobs_.erase(it);
    }
  }
}

909
void WasmEngine::DeleteCompileJobsOnIsolate(Isolate* isolate) {
910 911 912 913 914 915
  // Under the mutex get all jobs to delete. Then delete them without holding
  // the mutex, such that deletion can reenter the WasmEngine.
  std::vector<std::unique_ptr<AsyncCompileJob>> jobs_to_delete;
  {
    base::MutexGuard guard(&mutex_);
    DCHECK_EQ(1, isolates_.count(isolate));
916 917
    for (auto it = async_compile_jobs_.begin();
         it != async_compile_jobs_.end();) {
918 919 920 921 922
      if (it->first->isolate() != isolate) {
        ++it;
        continue;
      }
      jobs_to_delete.push_back(std::move(it->second));
923
      it = async_compile_jobs_.erase(it);
924 925 926 927
    }
  }
}

928
void WasmEngine::AddIsolate(Isolate* isolate) {
929
  base::MutexGuard guard(&mutex_);
930
  DCHECK_EQ(0, isolates_.count(isolate));
931
  isolates_.emplace(isolate, std::make_unique<IsolateInfo>(isolate));
932 933 934 935 936 937 938 939

  // Install sampling GC callback.
  // TODO(v8:7424): For now we sample module sizes in a GC callback. This will
  // bias samples towards apps with high memory pressure. We should switch to
  // using sampling based on regular intervals independent of the GC.
  auto callback = [](v8::Isolate* v8_isolate, v8::GCType type,
                     v8::GCCallbackFlags flags, void* data) {
    Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
940
    Counters* counters = isolate->counters();
941 942 943
    WasmEngine* engine = isolate->wasm_engine();
    base::MutexGuard lock(&engine->mutex_);
    DCHECK_EQ(1, engine->isolates_.count(isolate));
944 945
    for (auto* native_module : engine->isolates_[isolate]->native_modules) {
      native_module->SampleCodeSize(counters, NativeModule::kSampling);
946 947 948 949
    }
  };
  isolate->heap()->AddGCEpilogueCallback(callback, v8::kGCTypeMarkSweepCompact,
                                         nullptr);
950 951 952 953 954
#ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
  if (gdb_server_) {
    gdb_server_->AddIsolate(isolate);
  }
#endif  // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
955 956 957
}

void WasmEngine::RemoveIsolate(Isolate* isolate) {
958 959 960 961 962 963
#ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
  if (gdb_server_) {
    gdb_server_->RemoveIsolate(isolate);
  }
#endif  // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING

964
  base::MutexGuard guard(&mutex_);
965 966
  auto it = isolates_.find(isolate);
  DCHECK_NE(isolates_.end(), it);
967 968
  std::unique_ptr<IsolateInfo> info = std::move(it->second);
  isolates_.erase(it);
969
  for (auto* native_module : info->native_modules) {
970 971 972 973
    DCHECK_EQ(1, native_modules_.count(native_module));
    DCHECK_EQ(1, native_modules_[native_module]->isolates.count(isolate));
    auto* info = native_modules_[native_module].get();
    info->isolates.erase(isolate);
974 975 976 977 978
    if (current_gc_info_) {
      for (WasmCode* code : info->potentially_dead_code) {
        current_gc_info_->dead_code.erase(code);
      }
    }
979 980 981
    if (native_module->HasDebugInfo()) {
      native_module->GetDebugInfo()->RemoveIsolate(isolate);
    }
982
  }
983 984 985
  if (current_gc_info_) {
    if (RemoveIsolateFromCurrentGC(isolate)) PotentiallyFinishCurrentGC();
  }
986 987 988 989 990
  if (auto* task = info->log_codes_task) task->Cancel();
  if (!info->code_to_log.empty()) {
    WasmCode::DecrementRefCount(VectorOf(info->code_to_log));
    info->code_to_log.clear();
  }
991 992
}

993 994
void WasmEngine::LogCode(Vector<WasmCode*> code_vec) {
  if (code_vec.empty()) return;
995
  base::MutexGuard guard(&mutex_);
996
  NativeModule* native_module = code_vec[0]->native_module();
997 998
  DCHECK_EQ(1, native_modules_.count(native_module));
  for (Isolate* isolate : native_modules_[native_module]->isolates) {
999 1000
    DCHECK_EQ(1, isolates_.count(isolate));
    IsolateInfo* info = isolates_[isolate].get();
1001
    if (info->log_codes == false) continue;
1002
    if (info->log_codes_task == nullptr) {
1003
      auto new_task = std::make_unique<LogCodesTask>(
1004
          &mutex_, &info->log_codes_task, isolate, this);
1005 1006
      info->log_codes_task = new_task.get();
      info->foreground_task_runner->PostTask(std::move(new_task));
1007 1008
    }
    if (info->code_to_log.empty()) {
1009
      isolate->stack_guard()->RequestLogWasmCode();
1010
    }
1011 1012 1013 1014 1015 1016
    info->code_to_log.insert(info->code_to_log.end(), code_vec.begin(),
                             code_vec.end());
    for (WasmCode* code : code_vec) {
      DCHECK_EQ(native_module, code->native_module());
      code->IncRef();
    }
1017 1018 1019
  }
}

1020 1021 1022 1023 1024 1025 1026
void WasmEngine::EnableCodeLogging(Isolate* isolate) {
  base::MutexGuard guard(&mutex_);
  auto it = isolates_.find(isolate);
  DCHECK_NE(isolates_.end(), it);
  it->second->log_codes = true;
}

1027 1028 1029 1030
void WasmEngine::LogOutstandingCodesForIsolate(Isolate* isolate) {
  // If by now we should not log code any more, do not log it.
  if (!WasmCode::ShouldBeLogged(isolate)) return;

1031 1032 1033 1034 1035 1036 1037 1038
  // Under the mutex, get the vector of wasm code to log. Then log and decrement
  // the ref count without holding the mutex.
  std::vector<WasmCode*> code_to_log;
  {
    base::MutexGuard guard(&mutex_);
    DCHECK_EQ(1, isolates_.count(isolate));
    code_to_log.swap(isolates_[isolate]->code_to_log);
  }
1039 1040
  TRACE_EVENT1("v8.wasm", "wasm.LogCode", "num_code_objects",
               code_to_log.size());
1041 1042
  if (code_to_log.empty()) return;
  for (WasmCode* code : code_to_log) {
1043 1044
    code->LogCode(isolate);
  }
1045
  WasmCode::DecrementRefCount(VectorOf(code_to_log));
1046 1047
}

1048 1049
std::shared_ptr<NativeModule> WasmEngine::NewNativeModule(
    Isolate* isolate, const WasmFeatures& enabled,
1050
    std::shared_ptr<const WasmModule> module, size_t code_size_estimate) {
1051 1052
#ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
  if (FLAG_wasm_gdb_remote && !gdb_server_) {
1053
    gdb_server_ = gdb_server::GdbServer::Create();
1054
    gdb_server_->AddIsolate(isolate);
1055 1056 1057
  }
#endif  // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING

1058 1059
  std::shared_ptr<NativeModule> native_module = code_manager_.NewNativeModule(
      this, isolate, enabled, code_size_estimate, std::move(module));
1060
  base::MutexGuard lock(&mutex_);
1061
  auto pair = native_modules_.insert(std::make_pair(
1062
      native_module.get(), std::make_unique<NativeModuleInfo>(native_module)));
1063 1064
  DCHECK(pair.second);  // inserted new entry.
  pair.first->second.get()->isolates.insert(isolate);
1065
  auto& modules_per_isolate = isolates_[isolate]->native_modules;
1066
  modules_per_isolate.insert(native_module.get());
1067
  if (isolates_[isolate]->keep_tiered_down) {
1068
    native_module->SetTieringState(kTieredDown);
1069
  }
1070 1071 1072 1073
  isolate->counters()->wasm_modules_per_isolate()->AddSample(
      static_cast<int>(modules_per_isolate.size()));
  isolate->counters()->wasm_modules_per_engine()->AddSample(
      static_cast<int>(native_modules_.size()));
1074 1075 1076
  return native_module;
}

1077
std::shared_ptr<NativeModule> WasmEngine::MaybeGetNativeModule(
1078 1079 1080
    ModuleOrigin origin, Vector<const uint8_t> wire_bytes, Isolate* isolate) {
  std::shared_ptr<NativeModule> native_module =
      native_module_cache_.MaybeGetNativeModule(origin, wire_bytes);
1081
  bool recompile_module = false;
1082 1083 1084 1085
  if (native_module) {
    base::MutexGuard guard(&mutex_);
    auto& native_module_info = native_modules_[native_module.get()];
    if (!native_module_info) {
1086
      native_module_info = std::make_unique<NativeModuleInfo>(native_module);
1087 1088
    }
    native_module_info->isolates.insert(isolate);
1089
    isolates_[isolate]->native_modules.insert(native_module.get());
1090 1091 1092 1093
    if (isolates_[isolate]->keep_tiered_down) {
      native_module->SetTieringState(kTieredDown);
      recompile_module = true;
    }
1094
  }
1095
  // Potentially recompile the module for tier down, after releasing the mutex.
1096
  if (recompile_module) native_module->RecompileForTiering();
1097
  return native_module;
1098 1099
}

1100
bool WasmEngine::UpdateNativeModuleCache(
1101 1102
    bool error, std::shared_ptr<NativeModule>* native_module,
    Isolate* isolate) {
1103
  DCHECK_EQ(this, native_module->get()->engine());
1104 1105 1106 1107 1108
  // Pass {native_module} by value here to keep it alive until at least after
  // we returned from {Update}. Otherwise, we might {Erase} it inside {Update}
  // which would lock the mutex twice.
  auto prev = native_module->get();
  *native_module = native_module_cache_.Update(*native_module, error);
1109 1110 1111

  if (prev == native_module->get()) return true;

1112 1113 1114 1115 1116 1117
  bool recompile_module = false;
  {
    base::MutexGuard guard(&mutex_);
    DCHECK_EQ(1, native_modules_.count(native_module->get()));
    native_modules_[native_module->get()]->isolates.insert(isolate);
    DCHECK_EQ(1, isolates_.count(isolate));
1118
    isolates_[isolate]->native_modules.insert(native_module->get());
1119 1120 1121 1122 1123 1124
    if (isolates_[isolate]->keep_tiered_down) {
      native_module->get()->SetTieringState(kTieredDown);
      recompile_module = true;
    }
  }
  // Potentially recompile the module for tier down, after releasing the mutex.
1125
  if (recompile_module) native_module->get()->RecompileForTiering();
1126
  return false;
1127 1128 1129 1130 1131 1132 1133 1134
}

bool WasmEngine::GetStreamingCompilationOwnership(size_t prefix_hash) {
  return native_module_cache_.GetStreamingCompilationOwnership(prefix_hash);
}

void WasmEngine::StreamingCompilationFailed(size_t prefix_hash) {
  native_module_cache_.StreamingCompilationFailed(prefix_hash);
1135 1136
}

1137
void WasmEngine::FreeNativeModule(NativeModule* native_module) {
1138 1139 1140 1141 1142 1143 1144 1145
  base::MutexGuard guard(&mutex_);
  auto it = native_modules_.find(native_module);
  DCHECK_NE(native_modules_.end(), it);
  for (Isolate* isolate : it->second->isolates) {
    DCHECK_EQ(1, isolates_.count(isolate));
    IsolateInfo* info = isolates_[isolate].get();
    DCHECK_EQ(1, info->native_modules.count(native_module));
    info->native_modules.erase(native_module);
1146
    info->scripts.erase(native_module);
1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157
    // If there are {WasmCode} objects of the deleted {NativeModule}
    // outstanding to be logged in this isolate, remove them. Decrementing the
    // ref count is not needed, since the {NativeModule} dies anyway.
    size_t remaining = info->code_to_log.size();
    if (remaining > 0) {
      for (size_t i = 0; i < remaining; ++i) {
        while (i < remaining &&
               info->code_to_log[i]->native_module() == native_module) {
          // Move the last remaining item to this slot (this can be the same
          // as {i}, which is OK).
          info->code_to_log[i] = info->code_to_log[--remaining];
1158 1159
        }
      }
1160
      info->code_to_log.resize(remaining);
1161
    }
1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172
  }
  // If there is a GC running which has references to code contained in the
  // deleted {NativeModule}, remove those references.
  if (current_gc_info_) {
    for (auto it = current_gc_info_->dead_code.begin(),
              end = current_gc_info_->dead_code.end();
         it != end;) {
      if ((*it)->native_module() == native_module) {
        it = current_gc_info_->dead_code.erase(it);
      } else {
        ++it;
1173 1174
      }
    }
1175 1176
    TRACE_CODE_GC("Native module %p died, reducing dead code objects to %zu.\n",
                  native_module, current_gc_info_->dead_code.size());
1177
  }
1178
  native_module_cache_.Erase(native_module);
1179
  native_modules_.erase(it);
1180 1181
}

1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206
namespace {
class SampleTopTierCodeSizeTask : public CancelableTask {
 public:
  SampleTopTierCodeSizeTask(Isolate* isolate,
                            std::weak_ptr<NativeModule> native_module)
      : CancelableTask(isolate),
        isolate_(isolate),
        native_module_(std::move(native_module)) {}

  void RunInternal() override {
    if (std::shared_ptr<NativeModule> native_module = native_module_.lock()) {
      native_module->SampleCodeSize(isolate_->counters(),
                                    NativeModule::kAfterTopTier);
    }
  }

 private:
  Isolate* const isolate_;
  const std::weak_ptr<NativeModule> native_module_;
};
}  // namespace

void WasmEngine::SampleTopTierCodeSizeInAllIsolates(
    const std::shared_ptr<NativeModule>& native_module) {
  base::MutexGuard lock(&mutex_);
1207 1208
  DCHECK_EQ(1, native_modules_.count(native_module.get()));
  for (Isolate* isolate : native_modules_[native_module.get()]->isolates) {
1209 1210 1211
    DCHECK_EQ(1, isolates_.count(isolate));
    IsolateInfo* info = isolates_[isolate].get();
    info->foreground_task_runner->PostTask(
1212
        std::make_unique<SampleTopTierCodeSizeTask>(isolate, native_module));
1213 1214 1215
  }
}

1216 1217
void WasmEngine::ReportLiveCodeForGC(Isolate* isolate,
                                     Vector<WasmCode*> live_code) {
1218
  TRACE_EVENT0("v8.wasm", "wasm.ReportLiveCodeForGC");
1219 1220
  TRACE_CODE_GC("Isolate %d reporting %zu live code objects.\n", isolate->id(),
                live_code.size());
1221
  base::MutexGuard guard(&mutex_);
1222 1223 1224
  // This report might come in late (note that we trigger both a stack guard and
  // a foreground task). In that case, ignore it.
  if (current_gc_info_ == nullptr) return;
1225
  if (!RemoveIsolateFromCurrentGC(isolate)) return;
1226 1227
  isolate->counters()->wasm_module_num_triggered_code_gcs()->AddSample(
      current_gc_info_->gc_sequence_index);
1228
  for (WasmCode* code : live_code) current_gc_info_->dead_code.erase(code);
1229
  PotentiallyFinishCurrentGC();
1230 1231
}

1232 1233 1234 1235 1236
void WasmEngine::ReportLiveCodeFromStackForGC(Isolate* isolate) {
  wasm::WasmCodeRefScope code_ref_scope;
  std::unordered_set<wasm::WasmCode*> live_wasm_code;
  for (StackFrameIterator it(isolate); !it.done(); it.Advance()) {
    StackFrame* const frame = it.frame();
1237 1238
    if (frame->type() != StackFrame::WASM) continue;
    live_wasm_code.insert(WasmFrame::cast(frame)->wasm_code());
1239 1240
  }

1241 1242
  CheckNoArchivedThreads(isolate);

1243 1244 1245 1246
  ReportLiveCodeForGC(isolate,
                      OwnedVector<WasmCode*>::Of(live_wasm_code).as_vector());
}

1247 1248 1249 1250
bool WasmEngine::AddPotentiallyDeadCode(WasmCode* code) {
  base::MutexGuard guard(&mutex_);
  auto it = native_modules_.find(code->native_module());
  DCHECK_NE(native_modules_.end(), it);
1251 1252 1253
  NativeModuleInfo* info = it->second.get();
  if (info->dead_code.count(code)) return false;  // Code is already dead.
  auto added = info->potentially_dead_code.insert(code);
1254 1255
  if (!added.second) return false;  // An entry already existed.
  new_potentially_dead_code_size_ += code->instructions().size();
1256
  if (FLAG_wasm_code_gc) {
1257
    // Trigger a GC if 64kB plus 10% of committed code are potentially dead.
1258 1259 1260
    size_t dead_code_limit =
        FLAG_stress_wasm_code_gc
            ? 0
1261
            : 64 * KB + code_manager_.committed_code_space() / 10;
1262
    if (new_potentially_dead_code_size_ > dead_code_limit) {
1263 1264
      bool inc_gc_count =
          info->num_code_gcs_triggered < std::numeric_limits<int8_t>::max();
1265
      if (current_gc_info_ == nullptr) {
1266
        if (inc_gc_count) ++info->num_code_gcs_triggered;
1267 1268 1269
        TRACE_CODE_GC(
            "Triggering GC (potentially dead: %zu bytes; limit: %zu bytes).\n",
            new_potentially_dead_code_size_, dead_code_limit);
1270 1271 1272
        TriggerGC(info->num_code_gcs_triggered);
      } else if (current_gc_info_->next_gc_sequence_index == 0) {
        if (inc_gc_count) ++info->num_code_gcs_triggered;
1273 1274 1275 1276
        TRACE_CODE_GC(
            "Scheduling another GC after the current one (potentially dead: "
            "%zu bytes; limit: %zu bytes).\n",
            new_potentially_dead_code_size_, dead_code_limit);
1277 1278
        current_gc_info_->next_gc_sequence_index = info->num_code_gcs_triggered;
        DCHECK_NE(0, current_gc_info_->next_gc_sequence_index);
1279
      }
1280
    }
1281
  }
1282 1283 1284
  return true;
}

1285 1286 1287 1288 1289 1290
void WasmEngine::FreeDeadCode(const DeadCodeMap& dead_code) {
  base::MutexGuard guard(&mutex_);
  FreeDeadCodeLocked(dead_code);
}

void WasmEngine::FreeDeadCodeLocked(const DeadCodeMap& dead_code) {
1291
  TRACE_EVENT0("v8.wasm", "wasm.FreeDeadCode");
1292 1293 1294 1295
  DCHECK(!mutex_.TryLock());
  for (auto& dead_code_entry : dead_code) {
    NativeModule* native_module = dead_code_entry.first;
    const std::vector<WasmCode*>& code_vec = dead_code_entry.second;
1296 1297
    DCHECK_EQ(1, native_modules_.count(native_module));
    auto* info = native_modules_[native_module].get();
1298 1299 1300
    TRACE_CODE_GC("Freeing %zu code object%s of module %p.\n", code_vec.size(),
                  code_vec.size() == 1 ? "" : "s", native_module);
    for (WasmCode* code : code_vec) {
1301 1302 1303
      DCHECK_EQ(1, info->dead_code.count(code));
      info->dead_code.erase(code);
    }
1304
    native_module->FreeCode(VectorOf(code_vec));
1305 1306 1307
  }
}

1308 1309 1310
Handle<Script> WasmEngine::GetOrCreateScript(
    Isolate* isolate, const std::shared_ptr<NativeModule>& native_module,
    Vector<const char> source_url) {
1311 1312 1313 1314
  {
    base::MutexGuard guard(&mutex_);
    DCHECK_EQ(1, isolates_.count(isolate));
    auto& scripts = isolates_[isolate]->scripts;
1315
    auto it = scripts.find(native_module.get());
1316 1317 1318 1319 1320 1321 1322 1323 1324 1325
    if (it != scripts.end()) {
      Handle<Script> weak_global_handle = it->second.handle();
      if (weak_global_handle.is_null()) {
        scripts.erase(it);
      } else {
        return Handle<Script>::New(*weak_global_handle, isolate);
      }
    }
  }
  // Temporarily release the mutex to let the GC collect native modules.
1326
  auto script = CreateWasmScript(isolate, native_module, source_url);
1327 1328 1329 1330
  {
    base::MutexGuard guard(&mutex_);
    DCHECK_EQ(1, isolates_.count(isolate));
    auto& scripts = isolates_[isolate]->scripts;
1331 1332
    DCHECK_EQ(0, scripts.count(native_module.get()));
    scripts.emplace(native_module.get(), WeakScriptHandle(script));
1333 1334 1335 1336
    return script;
  }
}

1337 1338 1339 1340 1341 1342 1343 1344
void WasmEngine::ShepherdCompileJobHandle(
    std::shared_ptr<JobHandle> job_handle) {
  DCHECK_NOT_NULL(job_handle);
  base::MutexGuard guard(&mutex_);
  // TODO(clemensb): Add occasional cleanup of finished handles.
  compile_job_handles_.emplace_back(std::move(job_handle));
}

1345
void WasmEngine::TriggerGC(int8_t gc_sequence_index) {
1346
  DCHECK(!mutex_.TryLock());
1347 1348
  DCHECK_NULL(current_gc_info_);
  DCHECK(FLAG_wasm_code_gc);
1349
  new_potentially_dead_code_size_ = 0;
1350
  current_gc_info_.reset(new CurrentGCInfo(gc_sequence_index));
1351 1352 1353 1354 1355 1356 1357 1358
  // Add all potentially dead code to this GC, and trigger a GC task in each
  // isolate.
  for (auto& entry : native_modules_) {
    NativeModuleInfo* info = entry.second.get();
    if (info->potentially_dead_code.empty()) continue;
    for (auto* isolate : native_modules_[entry.first]->isolates) {
      auto& gc_task = current_gc_info_->outstanding_isolates[isolate];
      if (!gc_task) {
1359
        auto new_task = std::make_unique<WasmGCForegroundTask>(isolate);
1360 1361 1362 1363 1364
        gc_task = new_task.get();
        DCHECK_EQ(1, isolates_.count(isolate));
        isolates_[isolate]->foreground_task_runner->PostTask(
            std::move(new_task));
      }
1365
      isolate->stack_guard()->RequestWasmCodeGC();
1366 1367 1368 1369 1370
    }
    for (WasmCode* code : info->potentially_dead_code) {
      current_gc_info_->dead_code.insert(code);
    }
  }
1371
  TRACE_CODE_GC(
1372 1373
      "Starting GC (nr %d). Number of potentially dead code objects: %zu\n",
      current_gc_info_->gc_sequence_index, current_gc_info_->dead_code.size());
1374 1375 1376 1377 1378
  // Ensure that there are outstanding isolates that will eventually finish this
  // GC. If there are no outstanding isolates, we finish the GC immediately.
  PotentiallyFinishCurrentGC();
  DCHECK(current_gc_info_ == nullptr ||
         !current_gc_info_->outstanding_isolates.empty());
1379 1380
}

1381 1382 1383
bool WasmEngine::RemoveIsolateFromCurrentGC(Isolate* isolate) {
  DCHECK(!mutex_.TryLock());
  DCHECK_NOT_NULL(current_gc_info_);
1384
  return current_gc_info_->outstanding_isolates.erase(isolate) != 0;
1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399
}

void WasmEngine::PotentiallyFinishCurrentGC() {
  DCHECK(!mutex_.TryLock());
  TRACE_CODE_GC(
      "Remaining dead code objects: %zu; outstanding isolates: %zu.\n",
      current_gc_info_->dead_code.size(),
      current_gc_info_->outstanding_isolates.size());

  // If there are more outstanding isolates, return immediately.
  if (!current_gc_info_->outstanding_isolates.empty()) return;

  // All remaining code in {current_gc_info->dead_code} is really dead.
  // Move it from the set of potentially dead code to the set of dead code,
  // and decrement its ref count.
1400
  size_t num_freed = 0;
1401 1402 1403 1404 1405 1406 1407 1408 1409 1410
  DeadCodeMap dead_code;
  for (WasmCode* code : current_gc_info_->dead_code) {
    DCHECK_EQ(1, native_modules_.count(code->native_module()));
    auto* native_module_info = native_modules_[code->native_module()].get();
    DCHECK_EQ(1, native_module_info->potentially_dead_code.count(code));
    native_module_info->potentially_dead_code.erase(code);
    DCHECK_EQ(0, native_module_info->dead_code.count(code));
    native_module_info->dead_code.insert(code);
    if (code->DecRefOnDeadCode()) {
      dead_code[code->native_module()].push_back(code);
1411
      ++num_freed;
1412 1413
    }
  }
1414 1415 1416

  FreeDeadCodeLocked(dead_code);

1417 1418
  TRACE_CODE_GC("Found %zu dead code objects, freed %zu.\n",
                current_gc_info_->dead_code.size(), num_freed);
1419 1420
  USE(num_freed);

1421
  int8_t next_gc_sequence_index = current_gc_info_->next_gc_sequence_index;
1422
  current_gc_info_.reset();
1423
  if (next_gc_sequence_index != 0) TriggerGC(next_gc_sequence_index);
1424 1425
}

1426 1427
namespace {

1428
DEFINE_LAZY_LEAKY_OBJECT_GETTER(std::shared_ptr<WasmEngine>,
1429
                                GetSharedWasmEngine)
1430 1431 1432

}  // namespace

1433
// static
1434
void WasmEngine::InitializeOncePerProcess() {
1435
  *GetSharedWasmEngine() = std::make_shared<WasmEngine>();
1436 1437
}

1438
// static
1439
void WasmEngine::GlobalTearDown() {
1440
  GetSharedWasmEngine()->reset();
1441 1442
}

1443
// static
1444
std::shared_ptr<WasmEngine> WasmEngine::GetWasmEngine() {
1445
  return *GetSharedWasmEngine();
1446 1447
}

1448 1449
// {max_mem_pages} is declared in wasm-limits.h.
uint32_t max_mem_pages() {
1450 1451 1452 1453
  STATIC_ASSERT(kV8MaxWasmMemoryPages <= kMaxUInt32);
  return std::min(uint32_t{kV8MaxWasmMemoryPages}, FLAG_wasm_max_mem_pages);
}

1454 1455 1456 1457 1458 1459
// {max_table_init_entries} is declared in wasm-limits.h.
uint32_t max_table_init_entries() {
  return std::min(uint32_t{kV8MaxWasmTableInitEntries},
                  FLAG_wasm_max_table_size);
}

1460 1461 1462 1463 1464 1465 1466
// {max_module_size} is declared in wasm-limits.h.
size_t max_module_size() {
  return FLAG_experimental_wasm_allow_huge_modules
             ? RoundDown<kSystemPointerSize>(size_t{kMaxInt})
             : kV8MaxWasmModuleSize;
}

1467 1468
#undef TRACE_CODE_GC

1469 1470 1471
}  // namespace wasm
}  // namespace internal
}  // namespace v8