wasm-engine.cc 20.9 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/code-tracer.h"
8
#include "src/compilation-statistics.h"
9
#include "src/counters.h"
10
#include "src/objects-inl.h"
11
#include "src/objects/heap-number.h"
12
#include "src/objects/js-promise.h"
13
#include "src/ostreams.h"
14
#include "src/wasm/function-compiler.h"
15
#include "src/wasm/module-compiler.h"
Marja Hölttä's avatar
Marja Hölttä committed
16
#include "src/wasm/module-decoder.h"
17
#include "src/wasm/module-instantiate.h"
Marja Hölttä's avatar
Marja Hölttä committed
18
#include "src/wasm/streaming-decoder.h"
19
#include "src/wasm/wasm-objects-inl.h"
20 21 22 23 24

namespace v8 {
namespace internal {
namespace wasm {

25 26 27
namespace {
class LogCodesTask : public Task {
 public:
28 29 30 31 32 33 34 35 36 37 38
  LogCodesTask(base::Mutex* mutex, LogCodesTask** task_slot, Isolate* isolate)
      : mutex_(mutex), task_slot_(task_slot), isolate_(isolate) {
    DCHECK_NOT_NULL(task_slot);
    DCHECK_NOT_NULL(isolate);
  }

  ~LogCodesTask() {
    // 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();
  }
39 40 41 42 43

  // Hold the {mutex_} when calling this method.
  void AddCode(WasmCode* code) { code_to_log_.push_back(code); }

  void Run() override {
44 45
    if (cancelled()) return;
    DeregisterTask();
46 47 48 49 50 51 52 53 54 55 56 57 58
    // If by now we should not log code any more, do not log it.
    if (!WasmCode::ShouldBeLogged(isolate_)) return;
    for (WasmCode* code : code_to_log_) {
      code->LogCode(isolate_);
    }
  }

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

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
  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;
  }

74 75 76 77
 private:
  // The mutex of the WasmEngine.
  base::Mutex* const mutex_;
  // The slot in the WasmEngine where this LogCodesTask is stored. This is
78 79
  // cleared by this task before execution or on task destruction.
  LogCodesTask** task_slot_;
80 81 82 83 84
  Isolate* isolate_;
  std::vector<WasmCode*> code_to_log_;
};
}  // namespace

85
struct WasmEngine::IsolateInfo {
86 87
  explicit IsolateInfo(Isolate* isolate)
      : log_codes(WasmCode::ShouldBeLogged(isolate)) {
88 89 90 91 92
    v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
    v8::Platform* platform = V8::GetCurrentPlatform();
    foreground_task_runner = platform->GetForegroundTaskRunner(v8_isolate);
  }

93 94 95
  // All native modules that are being used by this Isolate (currently only
  // grows, never shrinks).
  std::set<NativeModule*> native_modules;
96

97 98 99
  // Caches whether code needs to be logged on this isolate.
  bool log_codes;

100 101 102 103 104
  // The currently scheduled LogCodesTask.
  LogCodesTask* log_codes_task = nullptr;

  // The foreground task runner of the isolate (can be called from background).
  std::shared_ptr<v8::TaskRunner> foreground_task_runner;
105 106
};

107
WasmEngine::WasmEngine()
108
    : code_manager_(&memory_tracker_, FLAG_wasm_max_code_space * MB) {}
109

110
WasmEngine::~WasmEngine() {
111 112
  // Synchronize on all background compile tasks.
  background_compile_task_manager_.CancelAndWait();
113
  // All AsyncCompileJobs have been canceled.
114
  DCHECK(async_compile_jobs_.empty());
115 116
  // All Isolates have been deregistered.
  DCHECK(isolates_.empty());
117 118
  // All NativeModules did die.
  DCHECK(isolates_per_native_module_.empty());
119
}
120

121 122
bool WasmEngine::SyncValidate(Isolate* isolate, const WasmFeatures& enabled,
                              const ModuleWireBytes& bytes) {
123 124
  // TODO(titzer): remove dependency on the isolate.
  if (bytes.start() == nullptr || bytes.length() == 0) return false;
125
  ModuleResult result =
126
      DecodeWasmModule(enabled, bytes.start(), bytes.end(), true, kWasmOrigin,
127
                       isolate->counters(), allocator());
128 129 130
  return result.ok();
}

131
MaybeHandle<AsmWasmData> WasmEngine::SyncCompileTranslatedAsmJs(
132
    Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
133 134
    Vector<const byte> asm_js_offset_table_bytes,
    Handle<HeapNumber> uses_bitset) {
135
  ModuleResult result =
136 137
      DecodeWasmModule(kAsmjsWasmFeatures, bytes.start(), bytes.end(), false,
                       kAsmJsOrigin, isolate->counters(), allocator());
138 139
  CHECK(!result.failed());

140
  // Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
  // in {CompileToNativeModule}.
  Handle<FixedArray> export_wrappers;
  std::unique_ptr<NativeModule> native_module =
      CompileToNativeModule(isolate, kAsmjsWasmFeatures, thrower,
                            std::move(result).value(), bytes, &export_wrappers);
  if (!native_module) return {};

  // Create heap objects for asm.js offset table to be stored in the module
  // object.
  Handle<ByteArray> asm_js_offset_table =
      isolate->factory()->NewByteArray(asm_js_offset_table_bytes.length());
  asm_js_offset_table->copy_in(0, asm_js_offset_table_bytes.start(),
                               asm_js_offset_table_bytes.length());

  return AsmWasmData::New(isolate, std::move(native_module), export_wrappers,
                          asm_js_offset_table, uses_bitset);
}

Handle<WasmModuleObject> WasmEngine::FinalizeTranslatedAsmJs(
    Isolate* isolate, Handle<AsmWasmData> asm_wasm_data,
    Handle<Script> script) {
  std::shared_ptr<NativeModule> native_module =
      asm_wasm_data->managed_native_module()->get();
  Handle<FixedArray> export_wrappers =
      handle(asm_wasm_data->export_wrappers(), isolate);
  size_t code_size_estimate =
      wasm::WasmCodeManager::EstimateNativeModuleCodeSize(
          native_module->module());

  Handle<WasmModuleObject> module_object =
      WasmModuleObject::New(isolate, std::move(native_module), script,
                            export_wrappers, code_size_estimate);
  module_object->set_asm_js_offset_table(asm_wasm_data->asm_js_offset_table());
  return module_object;
175 176 177
}

MaybeHandle<WasmModuleObject> WasmEngine::SyncCompile(
178 179
    Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
    const ModuleWireBytes& bytes) {
180
  ModuleResult result =
181
      DecodeWasmModule(enabled, bytes.start(), bytes.end(), false, kWasmOrigin,
182
                       isolate->counters(), allocator());
183
  if (result.failed()) {
184
    thrower->CompileFailed(result.error());
185 186 187
    return {};
  }

188
  // Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
189
  // in {CompileToModuleObject}.
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
  Handle<FixedArray> export_wrappers;
  std::unique_ptr<NativeModule> native_module =
      CompileToNativeModule(isolate, enabled, thrower,
                            std::move(result).value(), bytes, &export_wrappers);
  if (!native_module) return {};

  Handle<Script> script =
      CreateWasmScript(isolate, bytes, native_module->module()->source_map_url);
  size_t code_size_estimate =
      wasm::WasmCodeManager::EstimateNativeModuleCodeSize(
          native_module->module());

  // Create the module object.
  // TODO(clemensh): For the same module (same bytes / same hash), we should
  // only have one WasmModuleObject. Otherwise, we might only set
  // breakpoints on a (potentially empty) subset of the instances.

  // 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.
  Handle<WasmModuleObject> module_object =
      WasmModuleObject::New(isolate, std::move(native_module), script,
                            export_wrappers, code_size_estimate);

  // Finish the Wasm script now and make it public to the debugger.
  isolate->debug()->OnAfterCompile(script);
  return module_object;
218 219 220 221 222 223 224 225 226 227
}

MaybeHandle<WasmInstanceObject> WasmEngine::SyncInstantiate(
    Isolate* isolate, ErrorThrower* thrower,
    Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
    MaybeHandle<JSArrayBuffer> memory) {
  return InstantiateToInstanceObject(isolate, thrower, module_object, imports,
                                     memory);
}

228 229 230
void WasmEngine::AsyncInstantiate(
    Isolate* isolate, std::unique_ptr<InstantiationResultResolver> resolver,
    Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports) {
231
  ErrorThrower thrower(isolate, "WebAssembly.instantiate()");
232 233 234 235 236 237 238 239
  // Instantiate a TryCatch so that caught exceptions won't progagate out.
  // They will still be set as pending exceptions on the isolate.
  // TODO(clemensh): Avoid TryCatch, use Execution::TryCall internally to invoke
  // start function and report thrown exception explicitly via out argument.
  v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
  catcher.SetVerbose(false);
  catcher.SetCaptureMessage(false);

240 241
  MaybeHandle<WasmInstanceObject> instance_object = SyncInstantiate(
      isolate, &thrower, module_object, imports, Handle<JSArrayBuffer>::null());
242 243 244 245 246 247

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

248 249 250
  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.
251 252 253 254 255
    Handle<Object> exception(isolate->pending_exception(), isolate);
    isolate->clear_pending_exception();
    DCHECK(*isolate->external_caught_exception_address());
    *isolate->external_caught_exception_address() = false;
    resolver->OnInstantiationFailed(exception);
256 257 258 259
    thrower.Reset();
  } else {
    DCHECK(thrower.error());
    resolver->OnInstantiationFailed(thrower.Reify());
260 261 262
  }
}

263
void WasmEngine::AsyncCompile(
264
    Isolate* isolate, const WasmFeatures& enabled,
265
    std::shared_ptr<CompilationResultResolver> resolver,
266
    const ModuleWireBytes& bytes, bool is_shared) {
267 268 269 270 271 272 273 274
  if (!FLAG_wasm_async_compilation) {
    // Asynchronous compilation disabled; fall back on synchronous compilation.
    ErrorThrower thrower(isolate, "WasmCompile");
    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());
275
      ModuleWireBytes bytes_copy(copy.get(), copy.get() + bytes.length());
276
      module_object = SyncCompile(isolate, enabled, &thrower, bytes_copy);
277 278
    } else {
      // The wire bytes are not shared, OK to use them directly.
279
      module_object = SyncCompile(isolate, enabled, &thrower, bytes);
280 281
    }
    if (thrower.error()) {
282
      resolver->OnCompilationFailed(thrower.Reify());
283 284 285
      return;
    }
    Handle<WasmModuleObject> module = module_object.ToHandleChecked();
286
    resolver->OnCompilationSucceeded(module);
287 288 289 290 291
    return;
  }

  if (FLAG_wasm_test_streaming) {
    std::shared_ptr<StreamingDecoder> streaming_decoder =
292 293 294
        StartStreamingCompilation(isolate, enabled,
                                  handle(isolate->context(), isolate),
                                  std::move(resolver));
295 296 297 298 299 300 301 302
    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());
303

304
  AsyncCompileJob* job = CreateAsyncCompileJob(
305
      isolate, enabled, std::move(copy), bytes.length(),
306
      handle(isolate->context(), isolate), std::move(resolver));
307 308 309 310
  job->Start();
}

std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation(
311
    Isolate* isolate, const WasmFeatures& enabled, Handle<Context> context,
312
    std::shared_ptr<CompilationResultResolver> resolver) {
313
  AsyncCompileJob* job =
314 315
      CreateAsyncCompileJob(isolate, enabled, std::unique_ptr<byte[]>(nullptr),
                            0, context, std::move(resolver));
316
  return job->CreateStreamingDecoder();
317 318
}

319
void WasmEngine::CompileFunction(Isolate* isolate, NativeModule* native_module,
320
                                 uint32_t function_index, ExecutionTier tier) {
321 322
  // Note we assume that "one-off" compilations can discard detected features.
  WasmFeatures detected = kNoWasmFeatures;
323
  WasmCompilationUnit::CompileWasmFunction(
324
      isolate, native_module, &detected,
325
      &native_module->module()->functions[function_index], tier);
326 327
}

328 329
std::shared_ptr<NativeModule> WasmEngine::ExportNativeModule(
    Handle<WasmModuleObject> module_object) {
330
  return module_object->shared_native_module();
331 332 333
}

Handle<WasmModuleObject> WasmEngine::ImportNativeModule(
334 335 336 337
    Isolate* isolate, std::shared_ptr<NativeModule> shared_native_module) {
  NativeModule* native_module = shared_native_module.get();
  ModuleWireBytes wire_bytes(native_module->wire_bytes());
  const WasmModule* module = native_module->module();
338 339
  Handle<Script> script =
      CreateWasmScript(isolate, wire_bytes, module->source_map_url);
340
  size_t code_size = native_module->committed_code_space();
341
  Handle<WasmModuleObject> module_object = WasmModuleObject::New(
342 343
      isolate, std::move(shared_native_module), script, code_size);
  CompileJsToWasmWrappers(isolate, native_module->module(),
344
                          handle(module_object->export_wrappers(), isolate));
345 346 347 348 349 350 351
  {
    base::MutexGuard lock(&mutex_);
    DCHECK_EQ(1, isolates_.count(isolate));
    isolates_[isolate]->native_modules.insert(native_module);
    DCHECK_EQ(1, isolates_per_native_module_.count(native_module));
    isolates_per_native_module_[native_module].insert(isolate);
  }
352 353 354
  return module_object;
}

355
CompilationStatistics* WasmEngine::GetOrCreateTurboStatistics() {
356
  base::MutexGuard guard(&mutex_);
357 358 359 360 361 362 363
  if (compilation_stats_ == nullptr) {
    compilation_stats_.reset(new CompilationStatistics());
  }
  return compilation_stats_.get();
}

void WasmEngine::DumpAndResetTurboStatistics() {
364
  base::MutexGuard guard(&mutex_);
365 366 367 368 369 370 371
  if (compilation_stats_ != nullptr) {
    StdoutStream os;
    os << AsPrintableStatistics{*compilation_stats_.get(), false} << std::endl;
  }
  compilation_stats_.reset();
}

372
CodeTracer* WasmEngine::GetCodeTracer() {
373
  base::MutexGuard guard(&mutex_);
374 375 376 377
  if (code_tracer_ == nullptr) code_tracer_.reset(new CodeTracer(-1));
  return code_tracer_.get();
}

378
AsyncCompileJob* WasmEngine::CreateAsyncCompileJob(
379 380
    Isolate* isolate, const WasmFeatures& enabled,
    std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
381
    std::shared_ptr<CompilationResultResolver> resolver) {
382 383 384
  AsyncCompileJob* job =
      new AsyncCompileJob(isolate, enabled, std::move(bytes_copy), length,
                          context, std::move(resolver));
385
  // Pass ownership to the unique_ptr in {async_compile_jobs_}.
386
  base::MutexGuard guard(&mutex_);
387
  async_compile_jobs_[job] = std::unique_ptr<AsyncCompileJob>(job);
388 389 390
  return job;
}

391 392
std::unique_ptr<AsyncCompileJob> WasmEngine::RemoveCompileJob(
    AsyncCompileJob* job) {
393
  base::MutexGuard guard(&mutex_);
394 395
  auto item = async_compile_jobs_.find(job);
  DCHECK(item != async_compile_jobs_.end());
396
  std::unique_ptr<AsyncCompileJob> result = std::move(item->second);
397
  async_compile_jobs_.erase(item);
398
  return result;
399 400
}

401
bool WasmEngine::HasRunningCompileJob(Isolate* isolate) {
402
  base::MutexGuard guard(&mutex_);
403
  DCHECK_EQ(1, isolates_.count(isolate));
404
  for (auto& entry : async_compile_jobs_) {
405 406 407 408 409
    if (entry.first->isolate() == isolate) return true;
  }
  return false;
}

410
void WasmEngine::DeleteCompileJobsOnIsolate(Isolate* isolate) {
411 412 413 414 415 416
  // 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));
417 418
    for (auto it = async_compile_jobs_.begin();
         it != async_compile_jobs_.end();) {
419 420 421 422 423
      if (it->first->isolate() != isolate) {
        ++it;
        continue;
      }
      jobs_to_delete.push_back(std::move(it->second));
424
      it = async_compile_jobs_.erase(it);
425 426 427 428
    }
  }
}

429
void WasmEngine::AddIsolate(Isolate* isolate) {
430
  base::MutexGuard guard(&mutex_);
431
  DCHECK_EQ(0, isolates_.count(isolate));
432
  isolates_.emplace(isolate, base::make_unique<IsolateInfo>(isolate));
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452

  // 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);
    WasmEngine* engine = isolate->wasm_engine();
    base::MutexGuard lock(&engine->mutex_);
    DCHECK_EQ(1, engine->isolates_.count(isolate));
    for (NativeModule* native_module :
         engine->isolates_[isolate]->native_modules) {
      int code_size =
          static_cast<int>(native_module->committed_code_space() / MB);
      isolate->counters()->wasm_module_code_size_mb()->AddSample(code_size);
    }
  };
  isolate->heap()->AddGCEpilogueCallback(callback, v8::kGCTypeMarkSweepCompact,
                                         nullptr);
453 454 455
}

void WasmEngine::RemoveIsolate(Isolate* isolate) {
456
  base::MutexGuard guard(&mutex_);
457 458 459 460 461 462
  auto it = isolates_.find(isolate);
  DCHECK_NE(isolates_.end(), it);
  for (NativeModule* native_module : it->second->native_modules) {
    DCHECK_EQ(1, isolates_per_native_module_[native_module].count(isolate));
    isolates_per_native_module_[native_module].erase(isolate);
  }
463
  if (auto* task = it->second->log_codes_task) task->Cancel();
464 465 466
  isolates_.erase(it);
}

467 468 469 470 471 472 473
void WasmEngine::LogCode(WasmCode* code) {
  base::MutexGuard guard(&mutex_);
  NativeModule* native_module = code->native_module();
  DCHECK_EQ(1, isolates_per_native_module_.count(native_module));
  for (Isolate* isolate : isolates_per_native_module_[native_module]) {
    DCHECK_EQ(1, isolates_.count(isolate));
    IsolateInfo* info = isolates_[isolate].get();
474
    if (info->log_codes == false) continue;
475 476 477 478 479 480 481 482 483 484
    if (info->log_codes_task == nullptr) {
      auto new_task = base::make_unique<LogCodesTask>(
          &mutex_, &info->log_codes_task, isolate);
      info->log_codes_task = new_task.get();
      info->foreground_task_runner->PostTask(std::move(new_task));
    }
    info->log_codes_task->AddCode(code);
  }
}

485 486 487 488 489 490 491
void WasmEngine::EnableCodeLogging(Isolate* isolate) {
  base::MutexGuard guard(&mutex_);
  auto it = isolates_.find(isolate);
  DCHECK_NE(isolates_.end(), it);
  it->second->log_codes = true;
}

492 493 494 495 496 497 498 499
std::unique_ptr<NativeModule> WasmEngine::NewNativeModule(
    Isolate* isolate, const WasmFeatures& enabled, size_t code_size_estimate,
    bool can_request_more, std::shared_ptr<const WasmModule> module) {
  std::unique_ptr<NativeModule> native_module =
      code_manager_.NewNativeModule(this, isolate, enabled, code_size_estimate,
                                    can_request_more, std::move(module));
  base::MutexGuard lock(&mutex_);
  isolates_per_native_module_[native_module.get()].insert(isolate);
500
  DCHECK_EQ(1, isolates_.count(isolate));
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
  isolates_[isolate]->native_modules.insert(native_module.get());
  return native_module;
}

void WasmEngine::FreeNativeModule(NativeModule* native_module) {
  {
    base::MutexGuard guard(&mutex_);
    auto it = isolates_per_native_module_.find(native_module);
    DCHECK_NE(isolates_per_native_module_.end(), it);
    for (Isolate* isolate : it->second) {
      DCHECK_EQ(1, isolates_.count(isolate));
      DCHECK_EQ(1, isolates_[isolate]->native_modules.count(native_module));
      isolates_[isolate]->native_modules.erase(native_module);
    }
    isolates_per_native_module_.erase(it);
  }
  code_manager_.FreeNativeModule(native_module);
518 519
}

520 521
namespace {

522
DEFINE_LAZY_LEAKY_OBJECT_GETTER(std::shared_ptr<WasmEngine>,
523
                                GetSharedWasmEngine)
524 525 526

}  // namespace

527
// static
528 529
void WasmEngine::InitializeOncePerProcess() {
  if (!FLAG_wasm_shared_engine) return;
530
  *GetSharedWasmEngine() = std::make_shared<WasmEngine>();
531 532
}

533
// static
534 535
void WasmEngine::GlobalTearDown() {
  if (!FLAG_wasm_shared_engine) return;
536
  GetSharedWasmEngine()->reset();
537 538
}

539
// static
540
std::shared_ptr<WasmEngine> WasmEngine::GetWasmEngine() {
541 542
  if (FLAG_wasm_shared_engine) return *GetSharedWasmEngine();
  return std::make_shared<WasmEngine>();
543 544
}

545 546 547 548 549 550
// {max_mem_pages} is declared in wasm-limits.h.
uint32_t max_mem_pages() {
  STATIC_ASSERT(kV8MaxWasmMemoryPages <= kMaxUInt32);
  return std::min(uint32_t{kV8MaxWasmMemoryPages}, FLAG_wasm_max_mem_pages);
}

551 552 553
}  // namespace wasm
}  // namespace internal
}  // namespace v8