普通文本  |  3068行  |  120.19 KB

// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/wasm/module-compiler.h"

#include "src/api.h"
#include "src/asmjs/asm-js.h"
#include "src/base/optional.h"
#include "src/base/template-utils.h"
#include "src/base/utils/random-number-generator.h"
#include "src/compiler/wasm-compiler.h"
#include "src/counters.h"
#include "src/identity-map.h"
#include "src/property-descriptor.h"
#include "src/tracing/trace-event.h"
#include "src/trap-handler/trap-handler.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/streaming-decoder.h"
#include "src/wasm/wasm-code-manager.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-js.h"
#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-memory.h"
#include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-result.h"

#define TRACE(...)                                      \
  do {                                                  \
    if (FLAG_trace_wasm_instances) PrintF(__VA_ARGS__); \
  } while (false)

#define TRACE_COMPILE(...)                             \
  do {                                                 \
    if (FLAG_trace_wasm_compiler) PrintF(__VA_ARGS__); \
  } while (false)

#define TRACE_STREAMING(...)                            \
  do {                                                  \
    if (FLAG_trace_wasm_streaming) PrintF(__VA_ARGS__); \
  } while (false)

#define TRACE_LAZY(...)                                        \
  do {                                                         \
    if (FLAG_trace_wasm_lazy_compilation) PrintF(__VA_ARGS__); \
  } while (false)

namespace v8 {
namespace internal {
namespace wasm {

enum class CompilationEvent : uint8_t {
  kFinishedBaselineCompilation,
  kFinishedTopTierCompilation,
  kFailedCompilation
};

enum class CompileMode : uint8_t { kRegular, kTiering };

// The CompilationState keeps track of the compilation state of the
// owning NativeModule, i.e. which functions are left to be compiled.
// It contains a task manager to allow parallel and asynchronous background
// compilation of functions.
class CompilationState {
 public:
  CompilationState(internal::Isolate*, const ModuleEnv&);
  ~CompilationState();

  // Set the number of compilations unit expected to be executed. Needs to be
  // set before {AddCompilationUnits} is run, which triggers background
  // compilation.
  void SetNumberOfFunctionsToCompile(size_t num_functions);

  // Set the callback function to be called on compilation events. Needs to be
  // set before {AddCompilationUnits} is run.
  void SetCallback(
      std::function<void(CompilationEvent, ErrorThrower*)> callback);

  // Inserts new functions to compile and kicks off compilation.
  void AddCompilationUnits(
      std::vector<std::unique_ptr<WasmCompilationUnit>>& baseline_units,
      std::vector<std::unique_ptr<WasmCompilationUnit>>& tiering_units);
  std::unique_ptr<WasmCompilationUnit> GetNextCompilationUnit();
  std::unique_ptr<WasmCompilationUnit> GetNextExecutedUnit();

  bool HasCompilationUnitToFinish();

  void OnError(ErrorThrower* thrower);
  void OnFinishedUnit();
  void ScheduleUnitForFinishing(std::unique_ptr<WasmCompilationUnit> unit,
                                ExecutionTier mode);

  void OnBackgroundTaskStopped(const WasmFeatures& detected);
  void PublishDetectedFeatures(Isolate* isolate, const WasmFeatures& detected);
  void RestartBackgroundTasks(size_t max = std::numeric_limits<size_t>::max());
  // Only one foreground thread (finisher) is allowed to run at a time.
  // {SetFinisherIsRunning} returns whether the flag changed its state.
  bool SetFinisherIsRunning(bool value);
  void ScheduleFinisherTask();

  void Abort();

  Isolate* isolate() const { return isolate_; }

  bool failed() const {
    base::LockGuard<base::Mutex> guard(&mutex_);
    return failed_;
  }

  bool baseline_compilation_finished() const {
    return baseline_compilation_finished_;
  }

  WasmEngine* wasm_engine() const { return wasm_engine_; }
  CompileMode compile_mode() const { return compile_mode_; }
  ModuleEnv* module_env() { return &module_env_; }
  WasmFeatures* detected_features() { return &detected_features_; }

 private:
  void NotifyOnEvent(CompilationEvent event, ErrorThrower* thrower);

  std::vector<std::unique_ptr<WasmCompilationUnit>>& finish_units() {
    return baseline_compilation_finished_ ? tiering_finish_units_
                                          : baseline_finish_units_;
  }

  // TODO(7423): Get rid of the Isolate field to make sure the CompilationState
  // can be shared across multiple Isolates.
  Isolate* const isolate_;
  WasmEngine* const wasm_engine_;
  // TODO(clemensh): Remove ModuleEnv, generate it when needed.
  ModuleEnv module_env_;
  const CompileMode compile_mode_;
  bool baseline_compilation_finished_ = false;

  // This mutex protects all information of this CompilationState which is being
  // accessed concurrently.
  mutable base::Mutex mutex_;

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

  std::vector<std::unique_ptr<WasmCompilationUnit>> baseline_compilation_units_;
  std::vector<std::unique_ptr<WasmCompilationUnit>> tiering_compilation_units_;

  bool finisher_is_running_ = false;
  bool failed_ = false;
  size_t num_background_tasks_ = 0;

  std::vector<std::unique_ptr<WasmCompilationUnit>> baseline_finish_units_;
  std::vector<std::unique_ptr<WasmCompilationUnit>> tiering_finish_units_;

  // Features detected to be used in this module. Features can be detected
  // as a module is being compiled.
  WasmFeatures detected_features_ = kNoWasmFeatures;

  // End of fields protected by {mutex_}.
  //////////////////////////////////////////////////////////////////////////////

  // Callback function to be called on compilation events.
  std::function<void(CompilationEvent, ErrorThrower*)> callback_;

  CancelableTaskManager background_task_manager_;
  CancelableTaskManager foreground_task_manager_;
  std::shared_ptr<v8::TaskRunner> foreground_task_runner_;

  const size_t max_background_tasks_ = 0;

  size_t outstanding_units_ = 0;
  size_t num_tiering_units_ = 0;
};

namespace {

void UpdateFeatureUseCounts(Isolate* isolate, const WasmFeatures& detected) {
  if (detected.threads) {
    isolate->CountUsage(v8::Isolate::UseCounterFeature::kWasmThreadOpcodes);
  }
}

class JSToWasmWrapperCache {
 public:
  Handle<Code> GetOrCompileJSToWasmWrapper(Isolate* isolate,
                                           const NativeModule* native_module,
                                           uint32_t func_index,
                                           UseTrapHandler use_trap_handler) {
    const WasmModule* module = native_module->module();
    const WasmFunction* func = &module->functions[func_index];
    bool is_import = func_index < module->num_imported_functions;
    std::pair<bool, FunctionSig> key(is_import, *func->sig);
    Handle<Code>& cached = cache_[key];
    if (!cached.is_null()) return cached;

    Handle<Code> code =
        compiler::CompileJSToWasmWrapper(isolate, native_module, func->sig,
                                         is_import, use_trap_handler)
            .ToHandleChecked();
    cached = code;
    return code;
  }

 private:
  // We generate different code for calling imports than calling wasm functions
  // in this module. Both are cached separately.
  using CacheKey = std::pair<bool, FunctionSig>;
  std::unordered_map<CacheKey, Handle<Code>, base::hash<CacheKey>> cache_;
};

// A helper class to simplify instantiating a module from a module object.
// It closes over the {Isolate}, the {ErrorThrower}, etc.
class InstanceBuilder {
 public:
  InstanceBuilder(Isolate* isolate, ErrorThrower* thrower,
                  Handle<WasmModuleObject> module_object,
                  MaybeHandle<JSReceiver> ffi,
                  MaybeHandle<JSArrayBuffer> memory);

  // Build an instance, in all of its glory.
  MaybeHandle<WasmInstanceObject> Build();
  // Run the start function, if any.
  bool ExecuteStartFunction();

 private:
  // Represents the initialized state of a table.
  struct TableInstance {
    Handle<WasmTableObject> table_object;  // WebAssembly.Table instance
    Handle<FixedArray> js_wrappers;        // JSFunctions exported
    size_t table_size;
  };

  // A pre-evaluated value to use in import binding.
  struct SanitizedImport {
    Handle<String> module_name;
    Handle<String> import_name;
    Handle<Object> value;
  };

  Isolate* isolate_;
  const WasmFeatures enabled_;
  const WasmModule* const module_;
  ErrorThrower* thrower_;
  Handle<WasmModuleObject> module_object_;
  MaybeHandle<JSReceiver> ffi_;
  MaybeHandle<JSArrayBuffer> memory_;
  Handle<JSArrayBuffer> globals_;
  std::vector<TableInstance> table_instances_;
  std::vector<Handle<JSFunction>> js_wrappers_;
  Handle<WasmExportedFunction> start_function_;
  JSToWasmWrapperCache js_to_wasm_cache_;
  std::vector<SanitizedImport> sanitized_imports_;

  UseTrapHandler use_trap_handler() const {
    return module_object_->native_module()->use_trap_handler() ? kUseTrapHandler
                                                               : kNoTrapHandler;
  }

// Helper routines to print out errors with imports.
#define ERROR_THROWER_WITH_MESSAGE(TYPE)                                      \
  void Report##TYPE(const char* error, uint32_t index,                        \
                    Handle<String> module_name, Handle<String> import_name) { \
    thrower_->TYPE("Import #%d module=\"%s\" function=\"%s\" error: %s",      \
                   index, module_name->ToCString().get(),                     \
                   import_name->ToCString().get(), error);                    \
  }                                                                           \
                                                                              \
  MaybeHandle<Object> Report##TYPE(const char* error, uint32_t index,         \
                                   Handle<String> module_name) {              \
    thrower_->TYPE("Import #%d module=\"%s\" error: %s", index,               \
                   module_name->ToCString().get(), error);                    \
    return MaybeHandle<Object>();                                             \
  }

  ERROR_THROWER_WITH_MESSAGE(LinkError)
  ERROR_THROWER_WITH_MESSAGE(TypeError)

#undef ERROR_THROWER_WITH_MESSAGE

  // Look up an import value in the {ffi_} object.
  MaybeHandle<Object> LookupImport(uint32_t index, Handle<String> module_name,
                                   Handle<String> import_name);

  // Look up an import value in the {ffi_} object specifically for linking an
  // asm.js module. This only performs non-observable lookups, which allows
  // falling back to JavaScript proper (and hence re-executing all lookups) if
  // module instantiation fails.
  MaybeHandle<Object> LookupImportAsm(uint32_t index,
                                      Handle<String> import_name);

  uint32_t EvalUint32InitExpr(const WasmInitExpr& expr);

  // Load data segments into the memory.
  void LoadDataSegments(Handle<WasmInstanceObject> instance);

  void WriteGlobalValue(const WasmGlobal& global, double value);
  void WriteGlobalValue(const WasmGlobal& global,
                        Handle<WasmGlobalObject> value);

  void SanitizeImports();

  // Find the imported memory buffer if there is one. This is used to see if we
  // need to recompile with bounds checks before creating the instance.
  MaybeHandle<JSArrayBuffer> FindImportedMemoryBuffer() const;

  // Process the imports, including functions, tables, globals, and memory, in
  // order, loading them from the {ffi_} object. Returns the number of imported
  // functions.
  int ProcessImports(Handle<WasmInstanceObject> instance);

  template <typename T>
  T* GetRawGlobalPtr(const WasmGlobal& global);

  // Process initialization of globals.
  void InitGlobals();

  // Allocate memory for a module instance as a new JSArrayBuffer.
  Handle<JSArrayBuffer> AllocateMemory(uint32_t num_pages);

  bool NeedsWrappers() const;

  // Process the exports, creating wrappers for functions, tables, memories,
  // and globals.
  void ProcessExports(Handle<WasmInstanceObject> instance);

  void InitializeTables(Handle<WasmInstanceObject> instance);

  void LoadTableSegments(Handle<WasmInstanceObject> instance);
};

}  // namespace

MaybeHandle<WasmInstanceObject> InstantiateToInstanceObject(
    Isolate* isolate, ErrorThrower* thrower,
    Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
    MaybeHandle<JSArrayBuffer> memory) {
  InstanceBuilder builder(isolate, thrower, module_object, imports, memory);
  auto instance = builder.Build();
  if (!instance.is_null() && builder.ExecuteStartFunction()) {
    return instance;
  }
  return {};
}

WasmCode* LazyCompileFunction(Isolate* isolate, NativeModule* native_module,
                              int func_index) {
  base::ElapsedTimer compilation_timer;
  DCHECK(!native_module->has_code(static_cast<uint32_t>(func_index)));

  compilation_timer.Start();

  ModuleEnv* module_env = native_module->compilation_state()->module_env();
  // TODO(wasm): Refactor this to only get the name if it is really needed for
  // tracing / debugging.
  WasmName func_name;
  {
    ModuleWireBytes wire_bytes(native_module->wire_bytes());
    WireBytesRef name_ref =
        module_env->module->LookupFunctionName(wire_bytes, func_index);
    func_name = wire_bytes.GetName(name_ref);
  }

  TRACE_LAZY("Compiling function '%.*s' (#%d).\n", func_name.length(),
             func_name.start(), func_index);

  const uint8_t* module_start = native_module->wire_bytes().start();

  const WasmFunction* func = &module_env->module->functions[func_index];
  FunctionBody body{func->sig, func->code.offset(),
                    module_start + func->code.offset(),
                    module_start + func->code.end_offset()};

  ErrorThrower thrower(isolate, "WasmLazyCompile");
  WasmCompilationUnit unit(isolate->wasm_engine(), module_env, native_module,
                           body, func_name, func_index, isolate->counters());
  unit.ExecuteCompilation(
      native_module->compilation_state()->detected_features());
  WasmCode* wasm_code = unit.FinishCompilation(&thrower);

  if (WasmCode::ShouldBeLogged(isolate)) wasm_code->LogCode(isolate);

  // If there is a pending error, something really went wrong. The module was
  // verified before starting execution with lazy compilation.
  // This might be OOM, but then we cannot continue execution anyway.
  // TODO(clemensh): According to the spec, we can actually skip validation at
  // module creation time, and return a function that always traps here.
  CHECK(!thrower.error());

  int64_t func_size =
      static_cast<int64_t>(func->code.end_offset() - func->code.offset());
  int64_t compilation_time = compilation_timer.Elapsed().InMicroseconds();

  auto counters = isolate->counters();
  counters->wasm_lazily_compiled_functions()->Increment();

  counters->wasm_lazy_compilation_throughput()->AddSample(
      compilation_time != 0 ? static_cast<int>(func_size / compilation_time)
                            : 0);

  return wasm_code;
}

Address CompileLazy(Isolate* isolate, NativeModule* native_module,
                    uint32_t func_index) {
  HistogramTimerScope lazy_time_scope(
      isolate->counters()->wasm_lazy_compilation_time());

  DCHECK(!native_module->lazy_compile_frozen());

  NativeModuleModificationScope native_module_modification_scope(native_module);

  WasmCode* result = LazyCompileFunction(isolate, native_module, func_index);
  DCHECK_NOT_NULL(result);
  DCHECK_EQ(func_index, result->index());

  return result->instruction_start();
}

namespace {
bool compile_lazy(const WasmModule* module) {
  return FLAG_wasm_lazy_compilation ||
         (FLAG_asm_wasm_lazy_compilation && module->origin == kAsmJsOrigin);
}

byte* raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer, int offset) {
  return static_cast<byte*>(buffer.ToHandleChecked()->backing_store()) + offset;
}

void RecordStats(const Code* code, Counters* counters) {
  counters->wasm_generated_code_size()->Increment(code->body_size());
  counters->wasm_reloc_size()->Increment(code->relocation_info()->length());
}

bool in_bounds(uint32_t offset, size_t size, size_t upper) {
  return offset + size <= upper && offset + size >= offset;
}

using WasmInstanceMap =
    IdentityMap<Handle<WasmInstanceObject>, FreeStoreAllocationPolicy>;

double MonotonicallyIncreasingTimeInMs() {
  return V8::GetCurrentPlatform()->MonotonicallyIncreasingTime() *
         base::Time::kMillisecondsPerSecond;
}

ModuleEnv CreateDefaultModuleEnv(const WasmModule* module,
                                 bool allow_trap_handler = true) {
  UseTrapHandler use_trap_handler =
      trap_handler::IsTrapHandlerEnabled() && allow_trap_handler
          ? kUseTrapHandler
          : kNoTrapHandler;
  return ModuleEnv(module, use_trap_handler, kRuntimeExceptionSupport);
}

// The CompilationUnitBuilder builds compilation units and stores them in an
// internal buffer. The buffer is moved into the working queue of the
// CompilationState when {Commit} is called.
class CompilationUnitBuilder {
 public:
  explicit CompilationUnitBuilder(NativeModule* native_module)
      : native_module_(native_module),
        compilation_state_(native_module->compilation_state()) {}

  void AddUnit(const WasmFunction* function, uint32_t buffer_offset,
               Vector<const uint8_t> bytes, WasmName name) {
    switch (compilation_state_->compile_mode()) {
      case CompileMode::kTiering:
        tiering_units_.emplace_back(CreateUnit(
            function, buffer_offset, bytes, name, ExecutionTier::kOptimized));
        baseline_units_.emplace_back(CreateUnit(
            function, buffer_offset, bytes, name, ExecutionTier::kBaseline));
        return;
      case CompileMode::kRegular:
        baseline_units_.emplace_back(
            CreateUnit(function, buffer_offset, bytes, name,
                       WasmCompilationUnit::GetDefaultExecutionTier()));
        return;
    }
    UNREACHABLE();
  }

  bool Commit() {
    if (baseline_units_.empty() && tiering_units_.empty()) return false;
    compilation_state_->AddCompilationUnits(baseline_units_, tiering_units_);
    Clear();
    return true;
  }

  void Clear() {
    baseline_units_.clear();
    tiering_units_.clear();
  }

 private:
  std::unique_ptr<WasmCompilationUnit> CreateUnit(const WasmFunction* function,
                                                  uint32_t buffer_offset,
                                                  Vector<const uint8_t> bytes,
                                                  WasmName name,
                                                  ExecutionTier mode) {
    return base::make_unique<WasmCompilationUnit>(
        compilation_state_->wasm_engine(), compilation_state_->module_env(),
        native_module_,
        FunctionBody{function->sig, buffer_offset, bytes.begin(), bytes.end()},
        name, function->func_index,
        compilation_state_->isolate()->async_counters().get(), mode);
  }

  NativeModule* native_module_;
  CompilationState* compilation_state_;
  std::vector<std::unique_ptr<WasmCompilationUnit>> baseline_units_;
  std::vector<std::unique_ptr<WasmCompilationUnit>> tiering_units_;
};

// Run by each compilation task and by the main thread (i.e. in both
// foreground and background threads). The no_finisher_callback is called
// within the result_mutex_ lock when no finishing task is running, i.e. when
// the finisher_is_running_ flag is not set.
bool FetchAndExecuteCompilationUnit(CompilationState* compilation_state,
                                    WasmFeatures* detected) {
  DisallowHeapAccess no_heap_access;

  std::unique_ptr<WasmCompilationUnit> unit =
      compilation_state->GetNextCompilationUnit();
  if (unit == nullptr) return false;

  // TODO(kimanh): We need to find out in which mode the unit
  // should be compiled in before compiling it, as it might fallback
  // to Turbofan if it cannot be compiled using Liftoff. This can be removed
  // later as soon as Liftoff can compile any function. Then, we can directly
  // access {unit->mode()} within {ScheduleUnitForFinishing()}.
  ExecutionTier mode = unit->mode();
  unit->ExecuteCompilation(detected);
  compilation_state->ScheduleUnitForFinishing(std::move(unit), mode);

  return true;
}

void InitializeCompilationUnits(NativeModule* native_module) {
  ModuleWireBytes wire_bytes(native_module->wire_bytes());
  const WasmModule* module = native_module->module();
  CompilationUnitBuilder builder(native_module);
  uint32_t start = module->num_imported_functions;
  uint32_t end = start + module->num_declared_functions;
  for (uint32_t i = start; i < end; ++i) {
    const WasmFunction* func = &module->functions[i];
    uint32_t buffer_offset = func->code.offset();
    Vector<const uint8_t> bytes(wire_bytes.start() + func->code.offset(),
                                func->code.end_offset() - func->code.offset());

    WasmName name = wire_bytes.GetName(func, module);
    DCHECK_NOT_NULL(native_module);
    builder.AddUnit(func, buffer_offset, bytes, name);
  }
  builder.Commit();
}

void FinishCompilationUnits(CompilationState* compilation_state,
                            ErrorThrower* thrower) {
  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "FinishCompilationUnits");
  while (true) {
    if (compilation_state->failed()) break;
    std::unique_ptr<WasmCompilationUnit> unit =
        compilation_state->GetNextExecutedUnit();
    if (unit == nullptr) break;
    WasmCode* result = unit->FinishCompilation(thrower);

    if (thrower->error()) {
      compilation_state->Abort();
      break;
    }

    // Update the compilation state.
    compilation_state->OnFinishedUnit();
    DCHECK_IMPLIES(result == nullptr, thrower->error());
    if (result == nullptr) break;
  }
  if (!compilation_state->failed()) {
    compilation_state->RestartBackgroundTasks();
  }
}

void CompileInParallel(Isolate* isolate, NativeModule* native_module,
                       Handle<WasmModuleObject> module_object,
                       ErrorThrower* thrower) {
  // Data structures for the parallel compilation.

  //-----------------------------------------------------------------------
  // For parallel compilation:
  // 1) The main thread allocates a compilation unit for each wasm function
  //    and stores them in the vector {compilation_units} within the
  //    {compilation_state}. By adding units to the {compilation_state}, new
  //    {BackgroundCompileTasks} instances are spawned which run on
  //    the background threads.
  // 2.a) The background threads and the main thread pick one compilation
  //      unit at a time and execute the parallel phase of the compilation
  //      unit. After finishing the execution of the parallel phase, the
  //      result is enqueued in {baseline_finish_units_}.
  // 2.b) If {baseline_finish_units_} contains a compilation unit, the main
  //      thread dequeues it and finishes the compilation.
  // 3) After the parallel phase of all compilation units has started, the
  //    main thread continues to finish all compilation units as long as
  //    baseline-compilation units are left to be processed.
  // 4) If tier-up is enabled, the main thread restarts background tasks
  //    that take care of compiling and finishing the top-tier compilation
  //    units.

  // Turn on the {CanonicalHandleScope} so that the background threads can
  // use the node cache.
  CanonicalHandleScope canonical(isolate);

  CompilationState* compilation_state = native_module->compilation_state();
  // Make sure that no foreground task is spawned for finishing
  // the compilation units. This foreground thread will be
  // responsible for finishing compilation.
  compilation_state->SetFinisherIsRunning(true);
  uint32_t num_wasm_functions =
      native_module->num_functions() - native_module->num_imported_functions();
  compilation_state->SetNumberOfFunctionsToCompile(num_wasm_functions);

  // 1) The main thread allocates a compilation unit for each wasm function
  //    and stores them in the vector {compilation_units} within the
  //    {compilation_state}. By adding units to the {compilation_state}, new
  //    {BackgroundCompileTask} instances are spawned which run on
  //    background threads.
  InitializeCompilationUnits(native_module);

  // 2.a) The background threads and the main thread pick one compilation
  //      unit at a time and execute the parallel phase of the compilation
  //      unit. After finishing the execution of the parallel phase, the
  //      result is enqueued in {baseline_finish_units_}.
  //      The foreground task bypasses waiting on memory threshold, because
  //      its results will immediately be converted to code (below).
  WasmFeatures detected_features;
  while (
      FetchAndExecuteCompilationUnit(compilation_state, &detected_features) &&
      !compilation_state->baseline_compilation_finished()) {
    // 2.b) If {baseline_finish_units_} contains a compilation unit, the main
    //      thread dequeues it and finishes the compilation unit. Compilation
    //      units are finished concurrently to the background threads to save
    //      memory.
    FinishCompilationUnits(compilation_state, thrower);

    if (compilation_state->failed()) break;
  }

  while (!compilation_state->failed()) {
    // 3) After the parallel phase of all compilation units has started, the
    //    main thread continues to finish compilation units as long as
    //    baseline compilation units are left to be processed. If compilation
    //    already failed, all background tasks have already been canceled
    //    in {FinishCompilationUnits}, and there are no units to finish.
    FinishCompilationUnits(compilation_state, thrower);

    if (compilation_state->baseline_compilation_finished()) break;
  }

  // Publish features from the foreground and background tasks.
  compilation_state->PublishDetectedFeatures(isolate, detected_features);

  // 4) If tiering-compilation is enabled, we need to set the finisher
  //    to false, such that the background threads will spawn a foreground
  //    thread to finish the top-tier compilation units.
  if (!compilation_state->failed() &&
      compilation_state->compile_mode() == CompileMode::kTiering) {
    compilation_state->SetFinisherIsRunning(false);
    compilation_state->RestartBackgroundTasks();
  }
}

void CompileSequentially(Isolate* isolate, NativeModule* native_module,
                         ModuleEnv* module_env, ErrorThrower* thrower) {
  DCHECK(!thrower->error());

  ModuleWireBytes wire_bytes(native_module->wire_bytes());
  const WasmModule* module = module_env->module;
  WasmFeatures detected = kNoWasmFeatures;
  for (uint32_t i = 0; i < module->functions.size(); ++i) {
    const WasmFunction& func = module->functions[i];
    if (func.imported) continue;  // Imports are compiled at instantiation time.

    // Compile the function.
    WasmCode* code = WasmCompilationUnit::CompileWasmFunction(
        isolate, native_module, &detected, thrower, module_env, &func);
    if (code == nullptr) {
      TruncatedUserString<> name(wire_bytes.GetName(&func, module));
      thrower->CompileError("Compilation of #%d:%.*s failed.", i, name.length(),
                            name.start());
      break;
    }
  }
  UpdateFeatureUseCounts(isolate, detected);
}

void ValidateSequentially(Isolate* isolate, NativeModule* native_module,
                          ErrorThrower* thrower) {
  DCHECK(!thrower->error());

  ModuleWireBytes wire_bytes(native_module->wire_bytes());
  const WasmModule* module = native_module->module();
  uint32_t start = module->num_imported_functions;
  uint32_t end = start + module->num_declared_functions;
  for (uint32_t i = start; i < end; ++i) {
    const WasmFunction& func = module->functions[i];

    const byte* base = wire_bytes.start();
    FunctionBody body{func.sig, func.code.offset(), base + func.code.offset(),
                      base + func.code.end_offset()};
    DecodeResult result;
    {
      auto time_counter =
          SELECT_WASM_COUNTER(isolate->async_counters(), module->origin,
                              wasm_decode, function_time);

      TimedHistogramScope wasm_decode_function_time_scope(time_counter);
      WasmFeatures detected;
      result = VerifyWasmCode(isolate->allocator(),
                              native_module->enabled_features(), module,
                              &detected, body);
    }
    if (result.failed()) {
      TruncatedUserString<> name(wire_bytes.GetName(&func, module));
      thrower->CompileError("Compiling function #%d:%.*s failed: %s @+%u", i,
                            name.length(), name.start(),
                            result.error_msg().c_str(), result.error_offset());
      break;
    }
  }
}

void CompileNativeModule(Isolate* isolate, ErrorThrower* thrower,
                         Handle<WasmModuleObject> module_object,
                         const WasmModule* wasm_module, ModuleEnv* env) {
  NativeModule* const native_module = module_object->native_module();
  ModuleWireBytes wire_bytes(native_module->wire_bytes());

  if (compile_lazy(wasm_module)) {
    if (wasm_module->origin == kWasmOrigin) {
      // Validate wasm modules for lazy compilation. Don't validate asm.js
      // modules, they are valid by construction (otherwise a CHECK will fail
      // during lazy compilation).
      // TODO(clemensh): According to the spec, we can actually skip validation
      // at module creation time, and return a function that always traps at
      // (lazy) compilation time.
      ValidateSequentially(isolate, native_module, thrower);
      if (thrower->error()) return;
    }

    native_module->SetLazyBuiltin(BUILTIN_CODE(isolate, WasmCompileLazy));
  } else {
    size_t funcs_to_compile =
        wasm_module->functions.size() - wasm_module->num_imported_functions;
    bool compile_parallel =
        !FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks > 0 &&
        funcs_to_compile > 1 &&
        V8::GetCurrentPlatform()->NumberOfWorkerThreads() > 0;

    if (compile_parallel) {
      CompileInParallel(isolate, native_module, module_object, thrower);
    } else {
      CompileSequentially(isolate, native_module, env, thrower);
    }
    if (thrower->error()) return;
  }
}

// The runnable task that finishes compilation in foreground (e.g. updating
// the NativeModule, the code table, etc.).
class FinishCompileTask : public CancelableTask {
 public:
  explicit FinishCompileTask(CompilationState* compilation_state,
                             CancelableTaskManager* task_manager)
      : CancelableTask(task_manager), compilation_state_(compilation_state) {}

  void RunInternal() override {
    Isolate* isolate = compilation_state_->isolate();
    HandleScope scope(isolate);
    SaveContext saved_context(isolate);
    isolate->set_context(nullptr);

    TRACE_COMPILE("(4a) Finishing compilation units...\n");
    if (compilation_state_->failed()) {
      compilation_state_->SetFinisherIsRunning(false);
      return;
    }

    // We execute for 1 ms and then reschedule the task, same as the GC.
    double deadline = MonotonicallyIncreasingTimeInMs() + 1.0;
    while (true) {
      compilation_state_->RestartBackgroundTasks();

      std::unique_ptr<WasmCompilationUnit> unit =
          compilation_state_->GetNextExecutedUnit();

      if (unit == nullptr) {
        // It might happen that a background task just scheduled a unit to be
        // finished, but did not start a finisher task since the flag was still
        // set. Check for this case, and continue if there is more work.
        compilation_state_->SetFinisherIsRunning(false);
        if (compilation_state_->HasCompilationUnitToFinish() &&
            compilation_state_->SetFinisherIsRunning(true)) {
          continue;
        }
        break;
      }

      ErrorThrower thrower(compilation_state_->isolate(), "AsyncCompile");
      WasmCode* result = unit->FinishCompilation(&thrower);

      if (thrower.error()) {
        DCHECK_NULL(result);
        compilation_state_->OnError(&thrower);
        compilation_state_->SetFinisherIsRunning(false);
        thrower.Reset();
        break;
      }

      if (compilation_state_->baseline_compilation_finished()) {
        // If Liftoff compilation finishes it will directly start executing.
        // As soon as we have Turbofan-compiled code available, it will
        // directly be used by Liftoff-compiled code via the jump table.
        DCHECK_EQ(CompileMode::kTiering, compilation_state_->compile_mode());
        DCHECK(!result->is_liftoff());

        if (WasmCode::ShouldBeLogged(isolate)) result->LogCode(isolate);
      }

      // Update the compilation state, and possibly notify
      // threads waiting for events.
      compilation_state_->OnFinishedUnit();

      if (deadline < MonotonicallyIncreasingTimeInMs()) {
        // We reached the deadline. We reschedule this task and return
        // immediately. Since we rescheduled this task already, we do not set
        // the FinisherIsRunning flag to false.
        compilation_state_->ScheduleFinisherTask();
        return;
      }
    }
  }

 private:
  CompilationState* compilation_state_;
};

// The runnable task that performs compilations in the background.
class BackgroundCompileTask : public CancelableTask {
 public:
  explicit BackgroundCompileTask(CompilationState* compilation_state,
                                 CancelableTaskManager* task_manager)
      : CancelableTask(task_manager), compilation_state_(compilation_state) {}

  void RunInternal() override {
    TRACE_COMPILE("(3b) Compiling...\n");
    // The number of currently running background tasks is reduced in
    // {OnBackgroundTaskStopped}.
    while (!compilation_state_->failed()) {
      if (!FetchAndExecuteCompilationUnit(compilation_state_,
                                          &detected_features_)) {
        break;
      }
    }
    compilation_state_->OnBackgroundTaskStopped(detected_features_);
  }

 private:
  CompilationState* compilation_state_;
  WasmFeatures detected_features_ = kNoWasmFeatures;
};
}  // namespace

MaybeHandle<WasmModuleObject> CompileToModuleObject(
    Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
    std::shared_ptr<const WasmModule> module, const ModuleWireBytes& wire_bytes,
    Handle<Script> asm_js_script,
    Vector<const byte> asm_js_offset_table_bytes) {
  const WasmModule* wasm_module = module.get();
  TimedHistogramScope wasm_compile_module_time_scope(SELECT_WASM_COUNTER(
      isolate->counters(), wasm_module->origin, wasm_compile, module_time));

  // Embedder usage count for declared shared memories.
  if (wasm_module->has_shared_memory) {
    isolate->CountUsage(v8::Isolate::UseCounterFeature::kWasmSharedMemory);
  }

  // TODO(6792): No longer needed once WebAssembly code is off heap. Use
  // base::Optional to be able to close the scope before notifying the debugger.
  base::Optional<CodeSpaceMemoryModificationScope> modification_scope(
      base::in_place_t(), isolate->heap());

  // Create heap objects for script, module bytes and asm.js offset table to
  // be stored in the module object.
  Handle<Script> script;
  Handle<ByteArray> asm_js_offset_table;
  if (asm_js_script.is_null()) {
    script = CreateWasmScript(isolate, wire_bytes);
  } else {
    script = asm_js_script;
    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());
  }
  // TODO(wasm): only save the sections necessary to deserialize a
  // {WasmModule}. E.g. function bodies could be omitted.
  OwnedVector<uint8_t> wire_bytes_copy =
      OwnedVector<uint8_t>::Of(wire_bytes.module_bytes());

  // 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.
  ModuleEnv env = CreateDefaultModuleEnv(wasm_module);

  // 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, enabled, std::move(module), env, std::move(wire_bytes_copy),
      script, asm_js_offset_table);
  CompileNativeModule(isolate, thrower, module_object, wasm_module, &env);
  if (thrower->error()) return {};

  // Compile JS->wasm wrappers for exported functions.
  CompileJsToWasmWrappers(isolate, module_object);

  // If we created a wasm script, finish it now and make it public to the
  // debugger.
  if (asm_js_script.is_null()) {
    // Close the CodeSpaceMemoryModificationScope before calling into the
    // debugger.
    modification_scope.reset();
    isolate->debug()->OnAfterCompile(script);
  }

  // Log the code within the generated module for profiling.
  module_object->native_module()->LogWasmCodes(isolate);

  return module_object;
}

InstanceBuilder::InstanceBuilder(Isolate* isolate, ErrorThrower* thrower,
                                 Handle<WasmModuleObject> module_object,
                                 MaybeHandle<JSReceiver> ffi,
                                 MaybeHandle<JSArrayBuffer> memory)
    : isolate_(isolate),
      enabled_(module_object->native_module()->enabled_features()),
      module_(module_object->module()),
      thrower_(thrower),
      module_object_(module_object),
      ffi_(ffi),
      memory_(memory) {
  sanitized_imports_.reserve(module_->import_table.size());
}

// Build an instance, in all of its glory.
MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "InstanceBuilder::Build");
  // Check that an imports argument was provided, if the module requires it.
  // No point in continuing otherwise.
  if (!module_->import_table.empty() && ffi_.is_null()) {
    thrower_->TypeError(
        "Imports argument must be present and must be an object");
    return {};
  }

  SanitizeImports();
  if (thrower_->error()) return {};

  // TODO(6792): No longer needed once WebAssembly code is off heap.
  CodeSpaceMemoryModificationScope modification_scope(isolate_->heap());
  // From here on, we expect the build pipeline to run without exiting to JS.
  DisallowJavascriptExecution no_js(isolate_);
  // Record build time into correct bucket, then build instance.
  TimedHistogramScope wasm_instantiate_module_time_scope(SELECT_WASM_COUNTER(
      isolate_->counters(), module_->origin, wasm_instantiate, module_time));

  //--------------------------------------------------------------------------
  // Allocate the memory array buffer.
  //--------------------------------------------------------------------------
  // We allocate the memory buffer before cloning or reusing the compiled module
  // so we will know whether we need to recompile with bounds checks.
  uint32_t initial_pages = module_->initial_pages;
  auto initial_pages_counter = SELECT_WASM_COUNTER(
      isolate_->counters(), module_->origin, wasm, min_mem_pages_count);
  initial_pages_counter->AddSample(initial_pages);
  // Asm.js has memory_ already set at this point, so we don't want to
  // overwrite it.
  if (memory_.is_null()) {
    memory_ = FindImportedMemoryBuffer();
  }
  if (!memory_.is_null()) {
    // Set externally passed ArrayBuffer non neuterable.
    Handle<JSArrayBuffer> memory = memory_.ToHandleChecked();
    memory->set_is_neuterable(false);

    DCHECK_IMPLIES(use_trap_handler(), module_->origin == kAsmJsOrigin ||
                                           memory->is_wasm_memory() ||
                                           memory->backing_store() == nullptr);
  } else if (initial_pages > 0 || use_trap_handler()) {
    // We need to unconditionally create a guard region if using trap handlers,
    // even when the size is zero to prevent null-dereference issues
    // (e.g. https://crbug.com/769637).
    // Allocate memory if the initial size is more than 0 pages.
    memory_ = AllocateMemory(initial_pages);
    if (memory_.is_null()) return {};  // failed to allocate memory
  }

  //--------------------------------------------------------------------------
  // Recompile module if using trap handlers but could not get guarded memory
  //--------------------------------------------------------------------------
  if (module_->origin == kWasmOrigin && use_trap_handler()) {
    // Make sure the memory has suitable guard regions.
    WasmMemoryTracker* const memory_tracker =
        isolate_->wasm_engine()->memory_tracker();

    if (!memory_tracker->HasFullGuardRegions(
            memory_.ToHandleChecked()->backing_store())) {
      if (!FLAG_wasm_trap_handler_fallback) {
        return {};
      }

      TRACE("Recompiling module without bounds checks\n");
      constexpr bool allow_trap_handler = false;
      ModuleEnv env = CreateDefaultModuleEnv(module_, allow_trap_handler);
      // Disable trap handlers on this native module.
      NativeModule* native_module = module_object_->native_module();
      native_module->DisableTrapHandler();

      // Recompile all functions in this native module.
      ErrorThrower thrower(isolate_, "recompile");
      CompileNativeModule(isolate_, &thrower, module_object_, module_, &env);
      if (thrower.error()) {
        return {};
      }
      DCHECK(!native_module->use_trap_handler());
    }
  }

  //--------------------------------------------------------------------------
  // Create the WebAssembly.Instance object.
  //--------------------------------------------------------------------------
  NativeModule* native_module = module_object_->native_module();
  TRACE("New module instantiation for %p\n", native_module);
  Handle<WasmInstanceObject> instance =
      WasmInstanceObject::New(isolate_, module_object_);
  NativeModuleModificationScope native_modification_scope(native_module);

  //--------------------------------------------------------------------------
  // Set up the globals for the new instance.
  //--------------------------------------------------------------------------
  MaybeHandle<JSArrayBuffer> old_globals;
  uint32_t globals_buffer_size = module_->globals_buffer_size;
  if (globals_buffer_size > 0) {
    void* backing_store =
        isolate_->array_buffer_allocator()->Allocate(globals_buffer_size);
    if (backing_store == nullptr) {
      thrower_->RangeError("Out of memory: wasm globals");
      return {};
    }
    globals_ =
        isolate_->factory()->NewJSArrayBuffer(SharedFlag::kNotShared, TENURED);
    constexpr bool is_external = false;
    constexpr bool is_wasm_memory = false;
    JSArrayBuffer::Setup(globals_, isolate_, is_external, backing_store,
                         globals_buffer_size, SharedFlag::kNotShared,
                         is_wasm_memory);
    if (globals_.is_null()) {
      thrower_->RangeError("Out of memory: wasm globals");
      return {};
    }
    instance->set_globals_start(
        reinterpret_cast<byte*>(globals_->backing_store()));
    instance->set_globals_buffer(*globals_);
  }

  //--------------------------------------------------------------------------
  // Set up the array of references to imported globals' array buffers.
  //--------------------------------------------------------------------------
  if (module_->num_imported_mutable_globals > 0) {
    // TODO(binji): This allocates one slot for each mutable global, which is
    // more than required if multiple globals are imported from the same
    // module.
    Handle<FixedArray> buffers_array = isolate_->factory()->NewFixedArray(
        module_->num_imported_mutable_globals, TENURED);
    instance->set_imported_mutable_globals_buffers(*buffers_array);
  }

  //--------------------------------------------------------------------------
  // Reserve the metadata for indirect function tables.
  //--------------------------------------------------------------------------
  int table_count = static_cast<int>(module_->tables.size());
  table_instances_.resize(table_count);

  //--------------------------------------------------------------------------
  // Process the imports for the module.
  //--------------------------------------------------------------------------
  int num_imported_functions = ProcessImports(instance);
  if (num_imported_functions < 0) return {};

  //--------------------------------------------------------------------------
  // Process the initialization for the module's globals.
  //--------------------------------------------------------------------------
  InitGlobals();

  //--------------------------------------------------------------------------
  // Initialize the indirect tables.
  //--------------------------------------------------------------------------
  if (table_count > 0) {
    InitializeTables(instance);
  }

  //--------------------------------------------------------------------------
  // Create the WebAssembly.Memory object.
  //--------------------------------------------------------------------------
  if (module_->has_memory) {
    if (!instance->has_memory_object()) {
      // No memory object exists. Create one.
      Handle<WasmMemoryObject> memory_object = WasmMemoryObject::New(
          isolate_, memory_,
          module_->maximum_pages != 0 ? module_->maximum_pages : -1);
      instance->set_memory_object(*memory_object);
    }

    // Add the instance object to the list of instances for this memory.
    Handle<WasmMemoryObject> memory_object(instance->memory_object(), isolate_);
    WasmMemoryObject::AddInstance(isolate_, memory_object, instance);

    if (!memory_.is_null()) {
      // Double-check the {memory} array buffer matches the instance.
      Handle<JSArrayBuffer> memory = memory_.ToHandleChecked();
      CHECK_EQ(instance->memory_size(), memory->byte_length()->Number());
      CHECK_EQ(instance->memory_start(), memory->backing_store());
    }
  }

  //--------------------------------------------------------------------------
  // Check that indirect function table segments are within bounds.
  //--------------------------------------------------------------------------
  for (const WasmTableInit& table_init : module_->table_inits) {
    DCHECK(table_init.table_index < table_instances_.size());
    uint32_t base = EvalUint32InitExpr(table_init.offset);
    size_t table_size = table_instances_[table_init.table_index].table_size;
    if (!in_bounds(base, table_init.entries.size(), table_size)) {
      thrower_->LinkError("table initializer is out of bounds");
      return {};
    }
  }

  //--------------------------------------------------------------------------
  // Check that memory segments are within bounds.
  //--------------------------------------------------------------------------
  for (const WasmDataSegment& seg : module_->data_segments) {
    uint32_t base = EvalUint32InitExpr(seg.dest_addr);
    if (!in_bounds(base, seg.source.length(), instance->memory_size())) {
      thrower_->LinkError("data segment is out of bounds");
      return {};
    }
  }

  //--------------------------------------------------------------------------
  // Set up the exports object for the new instance.
  //--------------------------------------------------------------------------
  ProcessExports(instance);
  if (thrower_->error()) return {};

  //--------------------------------------------------------------------------
  // Initialize the indirect function tables.
  //--------------------------------------------------------------------------
  if (table_count > 0) {
    LoadTableSegments(instance);
  }

  //--------------------------------------------------------------------------
  // Initialize the memory by loading data segments.
  //--------------------------------------------------------------------------
  if (module_->data_segments.size() > 0) {
    LoadDataSegments(instance);
  }

  //--------------------------------------------------------------------------
  // Install a finalizer on the new instance object.
  //--------------------------------------------------------------------------
  WasmInstanceObject::InstallFinalizer(isolate_, instance);

  //--------------------------------------------------------------------------
  // Debugging support.
  //--------------------------------------------------------------------------
  // Set all breakpoints that were set on the shared module.
  WasmModuleObject::SetBreakpointsOnNewInstance(module_object_, instance);

  if (FLAG_wasm_interpret_all && module_->origin == kWasmOrigin) {
    Handle<WasmDebugInfo> debug_info =
        WasmInstanceObject::GetOrCreateDebugInfo(instance);
    std::vector<int> func_indexes;
    for (int func_index = num_imported_functions,
             num_wasm_functions = static_cast<int>(module_->functions.size());
         func_index < num_wasm_functions; ++func_index) {
      func_indexes.push_back(func_index);
    }
    WasmDebugInfo::RedirectToInterpreter(
        debug_info, Vector<int>(func_indexes.data(),
                                static_cast<int>(func_indexes.size())));
  }

  //--------------------------------------------------------------------------
  // Create a wrapper for the start function.
  //--------------------------------------------------------------------------
  if (module_->start_function_index >= 0) {
    int start_index = module_->start_function_index;
    FunctionSig* sig = module_->functions[start_index].sig;
    Handle<Code> wrapper_code = js_to_wasm_cache_.GetOrCompileJSToWasmWrapper(
        isolate_, native_module, start_index, use_trap_handler());
    // TODO(clemensh): Don't generate an exported function for the start
    // function. Use CWasmEntry instead.
    start_function_ = WasmExportedFunction::New(
        isolate_, instance, MaybeHandle<String>(), start_index,
        static_cast<int>(sig->parameter_count()), wrapper_code);
  }

  DCHECK(!isolate_->has_pending_exception());
  TRACE("Successfully built instance for module %p\n",
        module_object_->native_module());
  return instance;
}

bool InstanceBuilder::ExecuteStartFunction() {
  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
               "InstanceBuilder::ExecuteStartFunction");
  if (start_function_.is_null()) return true;  // No start function.

  HandleScope scope(isolate_);
  // Call the JS function.
  Handle<Object> undefined = isolate_->factory()->undefined_value();
  MaybeHandle<Object> retval =
      Execution::Call(isolate_, start_function_, undefined, 0, nullptr);

  if (retval.is_null()) {
    DCHECK(isolate_->has_pending_exception());
    return false;
  }
  return true;
}

// Look up an import value in the {ffi_} object.
MaybeHandle<Object> InstanceBuilder::LookupImport(uint32_t index,
                                                  Handle<String> module_name,

                                                  Handle<String> import_name) {
  // We pre-validated in the js-api layer that the ffi object is present, and
  // a JSObject, if the module has imports.
  DCHECK(!ffi_.is_null());

  // Look up the module first.
  MaybeHandle<Object> result = Object::GetPropertyOrElement(
      isolate_, ffi_.ToHandleChecked(), module_name);
  if (result.is_null()) {
    return ReportTypeError("module not found", index, module_name);
  }

  Handle<Object> module = result.ToHandleChecked();

  // Look up the value in the module.
  if (!module->IsJSReceiver()) {
    return ReportTypeError("module is not an object or function", index,
                           module_name);
  }

  result = Object::GetPropertyOrElement(isolate_, module, import_name);
  if (result.is_null()) {
    ReportLinkError("import not found", index, module_name, import_name);
    return MaybeHandle<JSFunction>();
  }

  return result;
}

// Look up an import value in the {ffi_} object specifically for linking an
// asm.js module. This only performs non-observable lookups, which allows
// falling back to JavaScript proper (and hence re-executing all lookups) if
// module instantiation fails.
MaybeHandle<Object> InstanceBuilder::LookupImportAsm(
    uint32_t index, Handle<String> import_name) {
  // Check that a foreign function interface object was provided.
  if (ffi_.is_null()) {
    return ReportLinkError("missing imports object", index, import_name);
  }

  // Perform lookup of the given {import_name} without causing any observable
  // side-effect. We only accept accesses that resolve to data properties,
  // which is indicated by the asm.js spec in section 7 ("Linking") as well.
  Handle<Object> result;
  LookupIterator it = LookupIterator::PropertyOrElement(
      isolate_, ffi_.ToHandleChecked(), import_name);
  switch (it.state()) {
    case LookupIterator::ACCESS_CHECK:
    case LookupIterator::INTEGER_INDEXED_EXOTIC:
    case LookupIterator::INTERCEPTOR:
    case LookupIterator::JSPROXY:
    case LookupIterator::ACCESSOR:
    case LookupIterator::TRANSITION:
      return ReportLinkError("not a data property", index, import_name);
    case LookupIterator::NOT_FOUND:
      // Accepting missing properties as undefined does not cause any
      // observable difference from JavaScript semantics, we are lenient.
      result = isolate_->factory()->undefined_value();
      break;
    case LookupIterator::DATA:
      result = it.GetDataValue();
      break;
  }

  return result;
}

uint32_t InstanceBuilder::EvalUint32InitExpr(const WasmInitExpr& expr) {
  switch (expr.kind) {
    case WasmInitExpr::kI32Const:
      return expr.val.i32_const;
    case WasmInitExpr::kGlobalIndex: {
      uint32_t offset = module_->globals[expr.val.global_index].offset;
      return ReadLittleEndianValue<uint32_t>(
          reinterpret_cast<Address>(raw_buffer_ptr(globals_, offset)));
    }
    default:
      UNREACHABLE();
  }
}

// Load data segments into the memory.
void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) {
  Vector<const uint8_t> wire_bytes =
      module_object_->native_module()->wire_bytes();
  for (const WasmDataSegment& segment : module_->data_segments) {
    uint32_t source_size = segment.source.length();
    // Segments of size == 0 are just nops.
    if (source_size == 0) continue;
    uint32_t dest_offset = EvalUint32InitExpr(segment.dest_addr);
    DCHECK(in_bounds(dest_offset, source_size, instance->memory_size()));
    byte* dest = instance->memory_start() + dest_offset;
    const byte* src = wire_bytes.start() + segment.source.offset();
    memcpy(dest, src, source_size);
  }
}

void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, double num) {
  TRACE("init [globals_start=%p + %u] = %lf, type = %s\n",
        reinterpret_cast<void*>(raw_buffer_ptr(globals_, 0)), global.offset,
        num, ValueTypes::TypeName(global.type));
  switch (global.type) {
    case kWasmI32:
      WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global),
                                      static_cast<int32_t>(num));
      break;
    case kWasmI64:
      // TODO(titzer): initialization of imported i64 globals.
      UNREACHABLE();
      break;
    case kWasmF32:
      WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global),
                                    static_cast<float>(num));
      break;
    case kWasmF64:
      WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global),
                                     static_cast<double>(num));
      break;
    default:
      UNREACHABLE();
  }
}

void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global,
                                       Handle<WasmGlobalObject> value) {
  TRACE("init [globals_start=%p + %u] = ",
        reinterpret_cast<void*>(raw_buffer_ptr(globals_, 0)), global.offset);
  switch (global.type) {
    case kWasmI32: {
      int32_t num = value->GetI32();
      WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global), num);
      TRACE("%d", num);
      break;
    }
    case kWasmI64: {
      int64_t num = value->GetI64();
      WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global), num);
      TRACE("%" PRId64, num);
      break;
    }
    case kWasmF32: {
      float num = value->GetF32();
      WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global), num);
      TRACE("%f", num);
      break;
    }
    case kWasmF64: {
      double num = value->GetF64();
      WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global), num);
      TRACE("%lf", num);
      break;
    }
    default:
      UNREACHABLE();
  }
  TRACE(", type = %s (from WebAssembly.Global)\n",
        ValueTypes::TypeName(global.type));
}

void InstanceBuilder::SanitizeImports() {
  Vector<const uint8_t> wire_bytes =
      module_object_->native_module()->wire_bytes();
  for (size_t index = 0; index < module_->import_table.size(); ++index) {
    const WasmImport& import = module_->import_table[index];

    Handle<String> module_name;
    MaybeHandle<String> maybe_module_name =
        WasmModuleObject::ExtractUtf8StringFromModuleBytes(isolate_, wire_bytes,
                                                           import.module_name);
    if (!maybe_module_name.ToHandle(&module_name)) {
      thrower_->LinkError("Could not resolve module name for import %zu",
                          index);
      return;
    }

    Handle<String> import_name;
    MaybeHandle<String> maybe_import_name =
        WasmModuleObject::ExtractUtf8StringFromModuleBytes(isolate_, wire_bytes,
                                                           import.field_name);
    if (!maybe_import_name.ToHandle(&import_name)) {
      thrower_->LinkError("Could not resolve import name for import %zu",
                          index);
      return;
    }

    int int_index = static_cast<int>(index);
    MaybeHandle<Object> result =
        module_->origin == kAsmJsOrigin
            ? LookupImportAsm(int_index, import_name)
            : LookupImport(int_index, module_name, import_name);
    if (thrower_->error()) {
      thrower_->LinkError("Could not find value for import %zu", index);
      return;
    }
    Handle<Object> value = result.ToHandleChecked();
    sanitized_imports_.push_back({module_name, import_name, value});
  }
}

MaybeHandle<JSArrayBuffer> InstanceBuilder::FindImportedMemoryBuffer() const {
  DCHECK_EQ(module_->import_table.size(), sanitized_imports_.size());
  for (size_t index = 0; index < module_->import_table.size(); index++) {
    const WasmImport& import = module_->import_table[index];

    if (import.kind == kExternalMemory) {
      const auto& value = sanitized_imports_[index].value;
      if (!value->IsWasmMemoryObject()) {
        return {};
      }
      auto memory = Handle<WasmMemoryObject>::cast(value);
      Handle<JSArrayBuffer> buffer(memory->array_buffer(), isolate_);
      return buffer;
    }
  }
  return {};
}

// Process the imports, including functions, tables, globals, and memory, in
// order, loading them from the {ffi_} object. Returns the number of imported
// functions.
int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
  int num_imported_functions = 0;
  int num_imported_tables = 0;
  int num_imported_mutable_globals = 0;

  DCHECK_EQ(module_->import_table.size(), sanitized_imports_.size());
  int num_imports = static_cast<int>(module_->import_table.size());
  NativeModule* native_module = instance->module_object()->native_module();
  for (int index = 0; index < num_imports; ++index) {
    const WasmImport& import = module_->import_table[index];

    Handle<String> module_name = sanitized_imports_[index].module_name;
    Handle<String> import_name = sanitized_imports_[index].import_name;
    Handle<Object> value = sanitized_imports_[index].value;

    switch (import.kind) {
      case kExternalFunction: {
        // Function imports must be callable.
        if (!value->IsCallable()) {
          ReportLinkError("function import requires a callable", index,
                          module_name, import_name);
          return -1;
        }
        uint32_t func_index = import.index;
        DCHECK_EQ(num_imported_functions, func_index);
        FunctionSig* expected_sig = module_->functions[func_index].sig;
        if (WasmExportedFunction::IsWasmExportedFunction(*value)) {
          // The imported function is a WASM function from another instance.
          Handle<WasmExportedFunction> imported_function(
              WasmExportedFunction::cast(*value), isolate_);
          Handle<WasmInstanceObject> imported_instance(
              imported_function->instance(), isolate_);
          FunctionSig* imported_sig =
              imported_instance->module()
                  ->functions[imported_function->function_index()]
                  .sig;
          if (*imported_sig != *expected_sig) {
            ReportLinkError(
                "imported function does not match the expected type", index,
                module_name, import_name);
            return -1;
          }
          // The import reference is the instance object itself.
          Address imported_target = imported_function->GetWasmCallTarget();
          ImportedFunctionEntry entry(instance, func_index);
          entry.set_wasm_to_wasm(*imported_instance, imported_target);
        } else {
          // The imported function is a callable.
          Handle<JSReceiver> js_receiver(JSReceiver::cast(*value), isolate_);
          Handle<Code> wrapper_code =
              compiler::CompileWasmToJSWrapper(
                  isolate_, js_receiver, expected_sig, func_index,
                  module_->origin, use_trap_handler())
                  .ToHandleChecked();
          RecordStats(*wrapper_code, isolate_->counters());

          WasmCode* wasm_code = native_module->AddCodeCopy(
              wrapper_code, WasmCode::kWasmToJsWrapper, func_index);
          ImportedFunctionEntry entry(instance, func_index);
          entry.set_wasm_to_js(*js_receiver, wasm_code);
        }
        num_imported_functions++;
        break;
      }
      case kExternalTable: {
        if (!value->IsWasmTableObject()) {
          ReportLinkError("table import requires a WebAssembly.Table", index,
                          module_name, import_name);
          return -1;
        }
        uint32_t table_num = import.index;
        DCHECK_EQ(table_num, num_imported_tables);
        const WasmTable& table = module_->tables[table_num];
        TableInstance& table_instance = table_instances_[table_num];
        table_instance.table_object = Handle<WasmTableObject>::cast(value);
        instance->set_table_object(*table_instance.table_object);
        table_instance.js_wrappers = Handle<FixedArray>(
            table_instance.table_object->functions(), isolate_);

        int imported_table_size = table_instance.js_wrappers->length();
        if (imported_table_size < static_cast<int>(table.initial_size)) {
          thrower_->LinkError(
              "table import %d is smaller than initial %d, got %u", index,
              table.initial_size, imported_table_size);
          return -1;
        }

        if (table.has_maximum_size) {
          int64_t imported_maximum_size =
              table_instance.table_object->maximum_length()->Number();
          if (imported_maximum_size < 0) {
            thrower_->LinkError(
                "table import %d has no maximum length, expected %d", index,
                table.maximum_size);
            return -1;
          }
          if (imported_maximum_size > table.maximum_size) {
            thrower_->LinkError(
                " table import %d has a larger maximum size %" PRIx64
                " than the module's declared maximum %u",
                index, imported_maximum_size, table.maximum_size);
            return -1;
          }
        }

        // Allocate a new dispatch table.
        if (!instance->has_indirect_function_table()) {
          WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
              instance, imported_table_size);
          table_instances_[table_num].table_size = imported_table_size;
        }
        // Initialize the dispatch table with the (foreign) JS functions
        // that are already in the table.
        for (int i = 0; i < imported_table_size; ++i) {
          Handle<Object> val(table_instance.js_wrappers->get(i), isolate_);
          // TODO(mtrofin): this is the same logic as WasmTableObject::Set:
          // insert in the local table a wrapper from the other module, and add
          // a reference to the owning instance of the other module.
          if (!val->IsJSFunction()) continue;
          if (!WasmExportedFunction::IsWasmExportedFunction(*val)) {
            thrower_->LinkError("table import %d[%d] is not a wasm function",
                                index, i);
            return -1;
          }
          // Look up the signature's canonical id. If there is no canonical
          // id, then the signature does not appear at all in this module,
          // so putting {-1} in the table will cause checks to always fail.
          auto target = Handle<WasmExportedFunction>::cast(val);
          Handle<WasmInstanceObject> imported_instance =
              handle(target->instance(), isolate_);
          Address exported_call_target = target->GetWasmCallTarget();
          FunctionSig* sig = imported_instance->module()
                                 ->functions[target->function_index()]
                                 .sig;
          IndirectFunctionTableEntry(instance, i)
              .set(module_->signature_map.Find(*sig), *imported_instance,
                   exported_call_target);
        }
        num_imported_tables++;
        break;
      }
      case kExternalMemory: {
        // Validation should have failed if more than one memory object was
        // provided.
        DCHECK(!instance->has_memory_object());
        if (!value->IsWasmMemoryObject()) {
          ReportLinkError("memory import must be a WebAssembly.Memory object",
                          index, module_name, import_name);
          return -1;
        }
        auto memory = Handle<WasmMemoryObject>::cast(value);
        instance->set_memory_object(*memory);
        Handle<JSArrayBuffer> buffer(memory->array_buffer(), isolate_);
        // memory_ should have already been assigned in Build().
        DCHECK_EQ(*memory_.ToHandleChecked(), *buffer);
        uint32_t imported_cur_pages = static_cast<uint32_t>(
            buffer->byte_length()->Number() / kWasmPageSize);
        if (imported_cur_pages < module_->initial_pages) {
          thrower_->LinkError(
              "memory import %d is smaller than initial %u, got %u", index,
              module_->initial_pages, imported_cur_pages);
        }
        int32_t imported_maximum_pages = memory->maximum_pages();
        if (module_->has_maximum_pages) {
          if (imported_maximum_pages < 0) {
            thrower_->LinkError(
                "memory import %d has no maximum limit, expected at most %u",
                index, imported_maximum_pages);
            return -1;
          }
          if (static_cast<uint32_t>(imported_maximum_pages) >
              module_->maximum_pages) {
            thrower_->LinkError(
                "memory import %d has a larger maximum size %u than the "
                "module's declared maximum %u",
                index, imported_maximum_pages, module_->maximum_pages);
            return -1;
          }
        }
        if (module_->has_shared_memory != buffer->is_shared()) {
          thrower_->LinkError(
              "mismatch in shared state of memory, declared = %d, imported = "
              "%d",
              module_->has_shared_memory, buffer->is_shared());
          return -1;
        }

        break;
      }
      case kExternalGlobal: {
        // Immutable global imports are converted to numbers and written into
        // the {globals_} array buffer.
        //
        // Mutable global imports instead have their backing array buffers
        // referenced by this instance, and store the address of the imported
        // global in the {imported_mutable_globals_} array.
        const WasmGlobal& global = module_->globals[import.index];

        // The mutable-global proposal allows importing i64 values, but only if
        // they are passed as a WebAssembly.Global object.
        if (global.type == kWasmI64 &&
            !(enabled_.mut_global && value->IsWasmGlobalObject())) {
          ReportLinkError("global import cannot have type i64", index,
                          module_name, import_name);
          return -1;
        }
        if (module_->origin == kAsmJsOrigin) {
          // Accepting {JSFunction} on top of just primitive values here is a
          // workaround to support legacy asm.js code with broken binding. Note
          // that using {NaN} (or Smi::kZero) here is what using the observable
          // conversion via {ToPrimitive} would produce as well.
          // TODO(mstarzinger): Still observable if Function.prototype.valueOf
          // or friends are patched, we might need to check for that as well.
          if (value->IsJSFunction()) value = isolate_->factory()->nan_value();
          if (value->IsPrimitive() && !value->IsSymbol()) {
            if (global.type == kWasmI32) {
              value = Object::ToInt32(isolate_, value).ToHandleChecked();
            } else {
              value = Object::ToNumber(isolate_, value).ToHandleChecked();
            }
          }
        }
        if (enabled_.mut_global) {
          if (value->IsWasmGlobalObject()) {
            auto global_object = Handle<WasmGlobalObject>::cast(value);
            if (global_object->type() != global.type) {
              ReportLinkError(
                  "imported global does not match the expected type", index,
                  module_name, import_name);
              return -1;
            }
            if (global_object->is_mutable() != global.mutability) {
              ReportLinkError(
                  "imported global does not match the expected mutability",
                  index, module_name, import_name);
              return -1;
            }
            if (global.mutability) {
              Handle<JSArrayBuffer> buffer(global_object->array_buffer(),
                                           isolate_);
              int index = num_imported_mutable_globals++;
              instance->imported_mutable_globals_buffers()->set(index, *buffer);
              // It is safe in this case to store the raw pointer to the buffer
              // since the backing store of the JSArrayBuffer will not be
              // relocated.
              instance->imported_mutable_globals()[index] =
                  reinterpret_cast<Address>(
                      raw_buffer_ptr(buffer, global_object->offset()));
            } else {
              WriteGlobalValue(global, global_object);
            }
          } else if (value->IsNumber()) {
            if (global.mutability) {
              ReportLinkError(
                  "imported mutable global must be a WebAssembly.Global object",
                  index, module_name, import_name);
              return -1;
            }
            WriteGlobalValue(global, value->Number());
          } else {
            ReportLinkError(
                "global import must be a number or WebAssembly.Global object",
                index, module_name, import_name);
            return -1;
          }
        } else {
          if (value->IsNumber()) {
            WriteGlobalValue(global, value->Number());
          } else {
            ReportLinkError("global import must be a number", index,
                            module_name, import_name);
            return -1;
          }
        }
        break;
      }
      default:
        UNREACHABLE();
        break;
    }
  }

  DCHECK_EQ(module_->num_imported_mutable_globals,
            num_imported_mutable_globals);

  return num_imported_functions;
}

template <typename T>
T* InstanceBuilder::GetRawGlobalPtr(const WasmGlobal& global) {
  return reinterpret_cast<T*>(raw_buffer_ptr(globals_, global.offset));
}

// Process initialization of globals.
void InstanceBuilder::InitGlobals() {
  for (auto global : module_->globals) {
    if (global.mutability && global.imported) {
      continue;
    }

    switch (global.init.kind) {
      case WasmInitExpr::kI32Const:
        WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global),
                                        global.init.val.i32_const);
        break;
      case WasmInitExpr::kI64Const:
        WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global),
                                        global.init.val.i64_const);
        break;
      case WasmInitExpr::kF32Const:
        WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global),
                                      global.init.val.f32_const);
        break;
      case WasmInitExpr::kF64Const:
        WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global),
                                       global.init.val.f64_const);
        break;
      case WasmInitExpr::kGlobalIndex: {
        // Initialize with another global.
        uint32_t new_offset = global.offset;
        uint32_t old_offset =
            module_->globals[global.init.val.global_index].offset;
        TRACE("init [globals+%u] = [globals+%d]\n", global.offset, old_offset);
        size_t size = (global.type == kWasmI64 || global.type == kWasmF64)
                          ? sizeof(double)
                          : sizeof(int32_t);
        memcpy(raw_buffer_ptr(globals_, new_offset),
               raw_buffer_ptr(globals_, old_offset), size);
        break;
      }
      case WasmInitExpr::kNone:
        // Happens with imported globals.
        break;
      default:
        UNREACHABLE();
        break;
    }
  }
}

// Allocate memory for a module instance as a new JSArrayBuffer.
Handle<JSArrayBuffer> InstanceBuilder::AllocateMemory(uint32_t num_pages) {
  if (num_pages > FLAG_wasm_max_mem_pages) {
    thrower_->RangeError("Out of memory: wasm memory too large");
    return Handle<JSArrayBuffer>::null();
  }
  const bool is_shared_memory = module_->has_shared_memory && enabled_.threads;
  i::SharedFlag shared_flag =
      is_shared_memory ? i::SharedFlag::kShared : i::SharedFlag::kNotShared;
  Handle<JSArrayBuffer> mem_buffer;
  if (!NewArrayBuffer(isolate_, num_pages * kWasmPageSize, shared_flag)
           .ToHandle(&mem_buffer)) {
    thrower_->RangeError("Out of memory: wasm memory");
  }
  return mem_buffer;
}

bool InstanceBuilder::NeedsWrappers() const {
  if (module_->num_exported_functions > 0) return true;
  for (auto& table_instance : table_instances_) {
    if (!table_instance.js_wrappers.is_null()) return true;
  }
  for (auto& table : module_->tables) {
    if (table.exported) return true;
  }
  return false;
}

// Process the exports, creating wrappers for functions, tables, memories,
// and globals.
void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
  Handle<FixedArray> export_wrappers(module_object_->export_wrappers(),
                                     isolate_);
  if (NeedsWrappers()) {
    // Fill the table to cache the exported JSFunction wrappers.
    js_wrappers_.insert(js_wrappers_.begin(), module_->functions.size(),
                        Handle<JSFunction>::null());

    // If an imported WebAssembly function gets exported, the exported function
    // has to be identical to to imported function. Therefore we put all
    // imported WebAssembly functions into the js_wrappers_ list.
    for (int index = 0, end = static_cast<int>(module_->import_table.size());
         index < end; ++index) {
      const WasmImport& import = module_->import_table[index];
      if (import.kind == kExternalFunction) {
        Handle<Object> value = sanitized_imports_[index].value;
        if (WasmExportedFunction::IsWasmExportedFunction(*value)) {
          js_wrappers_[import.index] = Handle<JSFunction>::cast(value);
        }
      }
    }
  }

  Handle<JSObject> exports_object;
  bool is_asm_js = false;
  switch (module_->origin) {
    case kWasmOrigin: {
      // Create the "exports" object.
      exports_object = isolate_->factory()->NewJSObjectWithNullProto();
      break;
    }
    case kAsmJsOrigin: {
      Handle<JSFunction> object_function = Handle<JSFunction>(
          isolate_->native_context()->object_function(), isolate_);
      exports_object = isolate_->factory()->NewJSObject(object_function);
      is_asm_js = true;
      break;
    }
    default:
      UNREACHABLE();
  }
  instance->set_exports_object(*exports_object);

  Handle<String> single_function_name =
      isolate_->factory()->InternalizeUtf8String(AsmJs::kSingleFunctionName);

  PropertyDescriptor desc;
  desc.set_writable(is_asm_js);
  desc.set_enumerable(true);
  desc.set_configurable(is_asm_js);

  // Process each export in the export table.
  int export_index = 0;  // Index into {export_wrappers}.
  for (const WasmExport& exp : module_->export_table) {
    Handle<String> name = WasmModuleObject::ExtractUtf8StringFromModuleBytes(
                              isolate_, module_object_, exp.name)
                              .ToHandleChecked();
    Handle<JSObject> export_to;
    if (is_asm_js && exp.kind == kExternalFunction &&
        String::Equals(isolate_, name, single_function_name)) {
      export_to = instance;
    } else {
      export_to = exports_object;
    }

    switch (exp.kind) {
      case kExternalFunction: {
        // Wrap and export the code as a JSFunction.
        const WasmFunction& function = module_->functions[exp.index];
        Handle<JSFunction> js_function = js_wrappers_[exp.index];
        if (js_function.is_null()) {
          // Wrap the exported code as a JSFunction.
          Handle<Code> export_code =
              export_wrappers->GetValueChecked<Code>(isolate_, export_index);
          MaybeHandle<String> func_name;
          if (is_asm_js) {
            // For modules arising from asm.js, honor the names section.
            WireBytesRef func_name_ref = module_->LookupFunctionName(
                module_object_->native_module()->wire_bytes(),
                function.func_index);
            func_name = WasmModuleObject::ExtractUtf8StringFromModuleBytes(
                            isolate_, module_object_, func_name_ref)
                            .ToHandleChecked();
          }
          js_function = WasmExportedFunction::New(
              isolate_, instance, func_name, function.func_index,
              static_cast<int>(function.sig->parameter_count()), export_code);
          js_wrappers_[exp.index] = js_function;
        }
        desc.set_value(js_function);
        export_index++;
        break;
      }
      case kExternalTable: {
        // Export a table as a WebAssembly.Table object.
        TableInstance& table_instance = table_instances_[exp.index];
        const WasmTable& table = module_->tables[exp.index];
        if (table_instance.table_object.is_null()) {
          uint32_t maximum = table.has_maximum_size ? table.maximum_size
                                                    : FLAG_wasm_max_table_size;
          table_instance.table_object =
              WasmTableObject::New(isolate_, table.initial_size, maximum,
                                   &table_instance.js_wrappers);
        }
        desc.set_value(table_instance.table_object);
        break;
      }
      case kExternalMemory: {
        // Export the memory as a WebAssembly.Memory object. A WasmMemoryObject
        // should already be available if the module has memory, since we always
        // create or import it when building an WasmInstanceObject.
        DCHECK(instance->has_memory_object());
        desc.set_value(
            Handle<WasmMemoryObject>(instance->memory_object(), isolate_));
        break;
      }
      case kExternalGlobal: {
        const WasmGlobal& global = module_->globals[exp.index];
        if (enabled_.mut_global) {
          Handle<JSArrayBuffer> buffer;
          uint32_t offset;

          if (global.mutability && global.imported) {
            Handle<FixedArray> buffers_array(
                instance->imported_mutable_globals_buffers(), isolate_);
            buffer = buffers_array->GetValueChecked<JSArrayBuffer>(
                isolate_, global.index);
            Address global_addr =
                instance->imported_mutable_globals()[global.index];

            uint32_t buffer_size = 0;
            CHECK(buffer->byte_length()->ToUint32(&buffer_size));

            Address backing_store =
                reinterpret_cast<Address>(buffer->backing_store());
            CHECK(global_addr >= backing_store &&
                  global_addr < backing_store + buffer_size);
            offset = static_cast<uint32_t>(global_addr - backing_store);
          } else {
            buffer = handle(instance->globals_buffer(), isolate_);
            offset = global.offset;
          }

          // Since the global's array buffer is always provided, allocation
          // should never fail.
          Handle<WasmGlobalObject> global_obj =
              WasmGlobalObject::New(isolate_, buffer, global.type, offset,
                                    global.mutability)
                  .ToHandleChecked();
          desc.set_value(global_obj);
        } else {
          // Export the value of the global variable as a number.
          double num = 0;
          switch (global.type) {
            case kWasmI32:
              num = ReadLittleEndianValue<int32_t>(
                  GetRawGlobalPtr<int32_t>(global));
              break;
            case kWasmF32:
              num =
                  ReadLittleEndianValue<float>(GetRawGlobalPtr<float>(global));
              break;
            case kWasmF64:
              num = ReadLittleEndianValue<double>(
                  GetRawGlobalPtr<double>(global));
              break;
            case kWasmI64:
              thrower_->LinkError(
                  "export of globals of type I64 is not allowed.");
              return;
            default:
              UNREACHABLE();
          }
          desc.set_value(isolate_->factory()->NewNumber(num));
        }
        break;
      }
      default:
        UNREACHABLE();
        break;
    }

    v8::Maybe<bool> status = JSReceiver::DefineOwnProperty(
        isolate_, export_to, name, &desc, kThrowOnError);
    if (!status.IsJust()) {
      TruncatedUserString<> trunc_name(name->GetCharVector<uint8_t>());
      thrower_->LinkError("export of %.*s failed.", trunc_name.length(),
                          trunc_name.start());
      return;
    }
  }
  DCHECK_EQ(export_index, export_wrappers->length());

  if (module_->origin == kWasmOrigin) {
    v8::Maybe<bool> success =
        JSReceiver::SetIntegrityLevel(exports_object, FROZEN, kDontThrow);
    DCHECK(success.FromMaybe(false));
    USE(success);
  }
}

void InstanceBuilder::InitializeTables(Handle<WasmInstanceObject> instance) {
  size_t table_count = module_->tables.size();
  for (size_t index = 0; index < table_count; ++index) {
    const WasmTable& table = module_->tables[index];
    TableInstance& table_instance = table_instances_[index];

    if (!instance->has_indirect_function_table() &&
        table.type == kWasmAnyFunc) {
      WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
          instance, table.initial_size);
      table_instance.table_size = table.initial_size;
    }
  }
}

void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
  NativeModule* native_module = module_object_->native_module();
  for (auto& table_init : module_->table_inits) {
    uint32_t base = EvalUint32InitExpr(table_init.offset);
    uint32_t num_entries = static_cast<uint32_t>(table_init.entries.size());
    uint32_t index = table_init.table_index;
    TableInstance& table_instance = table_instances_[index];
    DCHECK(in_bounds(base, num_entries, table_instance.table_size));
    for (uint32_t i = 0; i < num_entries; ++i) {
      uint32_t func_index = table_init.entries[i];
      const WasmFunction* function = &module_->functions[func_index];
      int table_index = static_cast<int>(i + base);

      // Update the local dispatch table first.
      uint32_t sig_id = module_->signature_ids[function->sig_index];
      Handle<WasmInstanceObject> target_instance = instance;
      Address call_target;
      const bool is_import = func_index < module_->num_imported_functions;
      if (is_import) {
        // For imported calls, take target instance and address from the
        // import table.
        ImportedFunctionEntry entry(instance, func_index);
        target_instance = handle(entry.instance(), isolate_);
        call_target = entry.target();
      } else {
        call_target = native_module->GetCallTargetForFunction(func_index);
      }
      IndirectFunctionTableEntry(instance, table_index)
          .set(sig_id, *target_instance, call_target);

      if (!table_instance.table_object.is_null()) {
        // Update the table object's other dispatch tables.
        if (js_wrappers_[func_index].is_null()) {
          // No JSFunction entry yet exists for this function. Create one.
          // TODO(titzer): We compile JS->wasm wrappers for functions are
          // not exported but are in an exported table. This should be done
          // at module compile time and cached instead.

          Handle<Code> wrapper_code =
              js_to_wasm_cache_.GetOrCompileJSToWasmWrapper(
                  isolate_, native_module, func_index, use_trap_handler());
          MaybeHandle<String> func_name;
          if (module_->origin == kAsmJsOrigin) {
            // For modules arising from asm.js, honor the names section.
            WireBytesRef func_name_ref = module_->LookupFunctionName(
                native_module->wire_bytes(), func_index);
            func_name = WasmModuleObject::ExtractUtf8StringFromModuleBytes(
                            isolate_, module_object_, func_name_ref)
                            .ToHandleChecked();
          }
          Handle<WasmExportedFunction> js_function = WasmExportedFunction::New(
              isolate_, instance, func_name, func_index,
              static_cast<int>(function->sig->parameter_count()), wrapper_code);
          js_wrappers_[func_index] = js_function;
        }
        table_instance.js_wrappers->set(table_index, *js_wrappers_[func_index]);
        // UpdateDispatchTables() updates all other dispatch tables, since
        // we have not yet added the dispatch table we are currently building.
        WasmTableObject::UpdateDispatchTables(
            isolate_, table_instance.table_object, table_index, function->sig,
            target_instance, call_target);
      }
    }
  }

  int table_count = static_cast<int>(module_->tables.size());
  for (int index = 0; index < table_count; ++index) {
    TableInstance& table_instance = table_instances_[index];

    // Add the new dispatch table at the end to avoid redundant lookups.
    if (!table_instance.table_object.is_null()) {
      WasmTableObject::AddDispatchTable(isolate_, table_instance.table_object,
                                        instance, index);
    }
  }
}

AsyncCompileJob::AsyncCompileJob(
    Isolate* isolate, const WasmFeatures& enabled,
    std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
    std::shared_ptr<CompilationResultResolver> resolver)
    : isolate_(isolate),
      enabled_features_(enabled),
      async_counters_(isolate->async_counters()),
      bytes_copy_(std::move(bytes_copy)),
      wire_bytes_(bytes_copy_.get(), bytes_copy_.get() + length),
      resolver_(std::move(resolver)) {
  v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
  v8::Platform* platform = V8::GetCurrentPlatform();
  foreground_task_runner_ = platform->GetForegroundTaskRunner(v8_isolate);
  // The handle for the context must be deferred.
  DeferredHandleScope deferred(isolate);
  native_context_ = Handle<Context>(context->native_context(), isolate);
  DCHECK(native_context_->IsNativeContext());
  deferred_handles_.push_back(deferred.Detach());
}

void AsyncCompileJob::Start() {
  DoAsync<DecodeModule>();  // --
}

void AsyncCompileJob::Abort() {
  // Removing this job will trigger the destructor, which will cancel all
  // compilation.
  isolate_->wasm_engine()->RemoveCompileJob(this);
}

class AsyncStreamingProcessor final : public StreamingProcessor {
 public:
  explicit AsyncStreamingProcessor(AsyncCompileJob* job);

  bool ProcessModuleHeader(Vector<const uint8_t> bytes,
                           uint32_t offset) override;

  bool ProcessSection(SectionCode section_code, Vector<const uint8_t> bytes,
                      uint32_t offset) override;

  bool ProcessCodeSectionHeader(size_t functions_count,
                                uint32_t offset) override;

  bool ProcessFunctionBody(Vector<const uint8_t> bytes,
                           uint32_t offset) override;

  void OnFinishedChunk() override;

  void OnFinishedStream(OwnedVector<uint8_t> bytes) override;

  void OnError(DecodeResult result) override;

  void OnAbort() override;

 private:
  // Finishes the AsyncCompileJob with an error.
  void FinishAsyncCompileJobWithError(ResultBase result);

  void CommitCompilationUnits();

  ModuleDecoder decoder_;
  AsyncCompileJob* job_;
  std::unique_ptr<CompilationUnitBuilder> compilation_unit_builder_;
  uint32_t next_function_ = 0;
};

std::shared_ptr<StreamingDecoder> AsyncCompileJob::CreateStreamingDecoder() {
  DCHECK_NULL(stream_);
  stream_.reset(
      new StreamingDecoder(base::make_unique<AsyncStreamingProcessor>(this)));
  return stream_;
}

AsyncCompileJob::~AsyncCompileJob() {
  background_task_manager_.CancelAndWait();
  if (native_module_) native_module_->compilation_state()->Abort();
  // Tell the streaming decoder that the AsyncCompileJob is not available
  // anymore.
  // TODO(ahaas): Is this notification really necessary? Check
  // https://crbug.com/888170.
  if (stream_) stream_->NotifyCompilationEnded();
  CancelPendingForegroundTask();
  for (auto d : deferred_handles_) delete d;
}

// This function assumes that it is executed in a HandleScope, and that a
// context is set on the isolate.
void AsyncCompileJob::FinishCompile() {
  DCHECK_NOT_NULL(isolate_->context());
  // Finish the wasm script now and make it public to the debugger.
  Handle<Script> script(module_object_->script(), isolate_);
  isolate_->debug()->OnAfterCompile(script);

  // Log the code within the generated module for profiling.
  native_module_->LogWasmCodes(isolate_);

  // We can only update the feature counts once the entire compile is done.
  auto compilation_state = native_module_->compilation_state();
  compilation_state->PublishDetectedFeatures(
      isolate_, *compilation_state->detected_features());

  // TODO(wasm): compiling wrappers should be made async as well.
  DoSync<CompileWrappers>();
}

void AsyncCompileJob::AsyncCompileFailed(Handle<Object> error_reason) {
  // {job} keeps the {this} pointer alive.
  std::shared_ptr<AsyncCompileJob> job =
      isolate_->wasm_engine()->RemoveCompileJob(this);
  resolver_->OnCompilationFailed(error_reason);
}

void AsyncCompileJob::AsyncCompileSucceeded(Handle<WasmModuleObject> result) {
  resolver_->OnCompilationSucceeded(result);
}

// A closure to run a compilation step (either as foreground or background
// task) and schedule the next step(s), if any.
class AsyncCompileJob::CompileStep {
 public:
  virtual ~CompileStep() {}

  void Run(bool on_foreground) {
    if (on_foreground) {
      HandleScope scope(job_->isolate_);
      SaveContext saved_context(job_->isolate_);
      job_->isolate_->set_context(*job_->native_context_);
      RunInForeground();
    } else {
      RunInBackground();
    }
  }

  virtual void RunInForeground() { UNREACHABLE(); }
  virtual void RunInBackground() { UNREACHABLE(); }

  AsyncCompileJob* job_ = nullptr;
};

class AsyncCompileJob::CompileTask : public CancelableTask {
 public:
  CompileTask(AsyncCompileJob* job, bool on_foreground)
      // We only manage the background tasks with the {CancelableTaskManager} of
      // the {AsyncCompileJob}. Foreground tasks are managed by the system's
      // {CancelableTaskManager}. Background tasks cannot spawn tasks managed by
      // their own task manager.
      : CancelableTask(on_foreground ? job->isolate_->cancelable_task_manager()
                                     : &job->background_task_manager_),
        job_(job),
        on_foreground_(on_foreground) {}

  ~CompileTask() {
    if (job_ != nullptr && on_foreground_) ResetPendingForegroundTask();
  }

  void RunInternal() final {
    if (!job_) return;
    if (on_foreground_) ResetPendingForegroundTask();
    job_->step_->Run(on_foreground_);
    // After execution, reset {job_} such that we don't try to reset the pending
    // foreground task when the task is deleted.
    job_ = nullptr;
  }

  void Cancel() {
    DCHECK_NOT_NULL(job_);
    job_ = nullptr;
  }

 private:
  // {job_} will be cleared to cancel a pending task.
  AsyncCompileJob* job_;
  bool on_foreground_;

  void ResetPendingForegroundTask() const {
    DCHECK_EQ(this, job_->pending_foreground_task_);
    job_->pending_foreground_task_ = nullptr;
  }
};

void AsyncCompileJob::StartForegroundTask() {
  DCHECK_NULL(pending_foreground_task_);

  auto new_task = base::make_unique<CompileTask>(this, true);
  pending_foreground_task_ = new_task.get();
  foreground_task_runner_->PostTask(std::move(new_task));
}

void AsyncCompileJob::ExecuteForegroundTaskImmediately() {
  DCHECK_NULL(pending_foreground_task_);

  auto new_task = base::make_unique<CompileTask>(this, true);
  pending_foreground_task_ = new_task.get();
  new_task->Run();
}

void AsyncCompileJob::CancelPendingForegroundTask() {
  if (!pending_foreground_task_) return;
  pending_foreground_task_->Cancel();
  pending_foreground_task_ = nullptr;
}

void AsyncCompileJob::StartBackgroundTask() {
  auto task = base::make_unique<CompileTask>(this, false);

  // If --wasm-num-compilation-tasks=0 is passed, do only spawn foreground
  // tasks. This is used to make timing deterministic.
  if (FLAG_wasm_num_compilation_tasks > 0) {
    V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
  } else {
    foreground_task_runner_->PostTask(std::move(task));
  }
}

template <typename Step, typename... Args>
void AsyncCompileJob::DoSync(Args&&... args) {
  NextStep<Step>(std::forward<Args>(args)...);
  StartForegroundTask();
}

template <typename Step, typename... Args>
void AsyncCompileJob::DoImmediately(Args&&... args) {
  NextStep<Step>(std::forward<Args>(args)...);
  ExecuteForegroundTaskImmediately();
}

template <typename Step, typename... Args>
void AsyncCompileJob::DoAsync(Args&&... args) {
  NextStep<Step>(std::forward<Args>(args)...);
  StartBackgroundTask();
}

template <typename Step, typename... Args>
void AsyncCompileJob::NextStep(Args&&... args) {
  step_.reset(new Step(std::forward<Args>(args)...));
  step_->job_ = this;
}

//==========================================================================
// Step 1: (async) Decode the module.
//==========================================================================
class AsyncCompileJob::DecodeModule : public AsyncCompileJob::CompileStep {
 public:
  void RunInBackground() override {
    ModuleResult result;
    {
      DisallowHandleAllocation no_handle;
      DisallowHeapAllocation no_allocation;
      // Decode the module bytes.
      TRACE_COMPILE("(1) Decoding module...\n");
      result =
          DecodeWasmModule(job_->enabled_features_, job_->wire_bytes_.start(),
                           job_->wire_bytes_.end(), false, kWasmOrigin,
                           job_->async_counters().get(),
                           job_->isolate()->wasm_engine()->allocator());
    }
    if (result.failed()) {
      // Decoding failure; reject the promise and clean up.
      job_->DoSync<DecodeFail>(std::move(result));
    } else {
      // Decode passed.
      job_->DoSync<PrepareAndStartCompile>(std::move(result.val), true);
    }
  }
};

//==========================================================================
// Step 1b: (sync) Fail decoding the module.
//==========================================================================
class AsyncCompileJob::DecodeFail : public CompileStep {
 public:
  explicit DecodeFail(ModuleResult result) : result_(std::move(result)) {}

 private:
  ModuleResult result_;
  void RunInForeground() override {
    TRACE_COMPILE("(1b) Decoding failed.\n");
    ErrorThrower thrower(job_->isolate_, "AsyncCompile");
    thrower.CompileFailed("Wasm decoding failed", result_);
    // {job_} is deleted in AsyncCompileFailed, therefore the {return}.
    return job_->AsyncCompileFailed(thrower.Reify());
  }
};

//==========================================================================
// Step 2 (sync): Create heap-allocated data and start compile.
//==========================================================================
class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
 public:
  PrepareAndStartCompile(std::shared_ptr<const WasmModule> module,
                         bool start_compilation)
      : module_(module), start_compilation_(start_compilation) {}

 private:
  std::shared_ptr<const WasmModule> module_;
  bool start_compilation_;

  void RunInForeground() override {
    TRACE_COMPILE("(2) Prepare and start compile...\n");

    // Make sure all compilation tasks stopped running. Decoding (async step)
    // is done.
    job_->background_task_manager_.CancelAndWait();

    // Embedder usage count for declared shared memories.
    if (module_->has_shared_memory) {
      job_->isolate_->CountUsage(
          v8::Isolate::UseCounterFeature::kWasmSharedMemory);
    }

    // Create heap objects for script and module bytes to be stored in the
    // module object. Asm.js is not compiled asynchronously.
    Handle<Script> script = CreateWasmScript(job_->isolate_, job_->wire_bytes_);
    Handle<ByteArray> asm_js_offset_table;

    const WasmModule* module = module_.get();
    ModuleEnv env = CreateDefaultModuleEnv(module);
    // TODO(wasm): Improve efficiency of storing module wire bytes. Only store
    // relevant sections, not function bodies

    // Create the module object and populate with compiled functions and
    // information needed at instantiation time.
    // 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 module object.
    job_->module_object_ = WasmModuleObject::New(
        job_->isolate_, job_->enabled_features_, module_, env,
        {std::move(job_->bytes_copy_), job_->wire_bytes_.length()}, script,
        asm_js_offset_table);
    job_->native_module_ = job_->module_object_->native_module();

    {
      DeferredHandleScope deferred(job_->isolate_);
      job_->module_object_ = handle(*job_->module_object_, job_->isolate_);
      job_->deferred_handles_.push_back(deferred.Detach());
    }
    size_t num_functions =
        module->functions.size() - module->num_imported_functions;

    if (num_functions == 0) {
      // Tiering has nothing to do if module is empty.
      job_->tiering_completed_ = true;

      // Degenerate case of an empty module.
      job_->FinishCompile();
      return;
    }

    CompilationState* compilation_state =
        job_->native_module_->compilation_state();
    {
      // Instance field {job_} cannot be captured by copy, therefore
      // we need to add a local helper variable {job}. We want to
      // capture the {job} pointer by copy, as it otherwise is dependent
      // on the current step we are in.
      AsyncCompileJob* job = job_;
      compilation_state->SetCallback(
          [job](CompilationEvent event, ErrorThrower* thrower) {
            // Callback is called from a foreground thread.
            switch (event) {
              case CompilationEvent::kFinishedBaselineCompilation:
                if (job->DecrementAndCheckFinisherCount()) {
                  SaveContext saved_context(job->isolate());
                  job->isolate()->set_context(*job->native_context_);
                  job->FinishCompile();
                }
                return;
              case CompilationEvent::kFinishedTopTierCompilation:
                // If a foreground task or a finisher is pending, we rely on
                // FinishModule to remove the job.
                if (job->pending_foreground_task_ ||
                    job->outstanding_finishers_.load() > 0) {
                  job->tiering_completed_ = true;
                  return;
                }
                job->isolate_->wasm_engine()->RemoveCompileJob(job);
                return;
              case CompilationEvent::kFailedCompilation: {
                // Tier-up compilation should not fail if baseline compilation
                // did not fail.
                DCHECK(!job->native_module_->compilation_state()
                            ->baseline_compilation_finished());

                SaveContext saved_context(job->isolate());
                job->isolate()->set_context(*job->native_context_);
                Handle<Object> error = thrower->Reify();

                DeferredHandleScope deferred(job->isolate());
                error = handle(*error, job->isolate());
                job->deferred_handles_.push_back(deferred.Detach());
                job->DoSync<CompileFailed>(error);
                return;
              }
            }
            UNREACHABLE();
          });
    }
    if (start_compilation_) {
      // TODO(ahaas): Try to remove the {start_compilation_} check when
      // streaming decoding is done in the background. If
      // InitializeCompilationUnits always returns 0 for streaming compilation,
      // then DoAsync would do the same as NextStep already.

      compilation_state->SetNumberOfFunctionsToCompile(
          module->num_declared_functions);
      // Add compilation units and kick off compilation.
      InitializeCompilationUnits(job_->native_module_);
    }
  }
};

//==========================================================================
// Step 4b (sync): Compilation failed. Reject Promise.
//==========================================================================
class AsyncCompileJob::CompileFailed : public CompileStep {
 public:
  explicit CompileFailed(Handle<Object> error_reason)
      : error_reason_(error_reason) {}

  void RunInForeground() override {
    TRACE_COMPILE("(4b) Compilation Failed...\n");
    return job_->AsyncCompileFailed(error_reason_);
  }

 private:
  Handle<Object> error_reason_;
};

//==========================================================================
// Step 5 (sync): Compile JS->wasm wrappers.
//==========================================================================
class AsyncCompileJob::CompileWrappers : public CompileStep {
  // TODO(wasm): Compile all wrappers here, including the start function wrapper
  // and the wrappers for the function table elements.
  void RunInForeground() override {
    TRACE_COMPILE("(5) Compile wrappers...\n");
    // TODO(6792): No longer needed once WebAssembly code is off heap.
    CodeSpaceMemoryModificationScope modification_scope(job_->isolate_->heap());
    // Compile JS->wasm wrappers for exported functions.
    CompileJsToWasmWrappers(job_->isolate_, job_->module_object_);
    job_->DoSync<FinishModule>();
  }
};

//==========================================================================
// Step 6 (sync): Finish the module and resolve the promise.
//==========================================================================
class AsyncCompileJob::FinishModule : public CompileStep {
  void RunInForeground() override {
    TRACE_COMPILE("(6) Finish module...\n");
    job_->AsyncCompileSucceeded(job_->module_object_);

    size_t num_functions = job_->native_module_->num_functions() -
                           job_->native_module_->num_imported_functions();
    if (job_->native_module_->compilation_state()->compile_mode() ==
            CompileMode::kRegular ||
        num_functions == 0) {
      // If we do not tier up, the async compile job is done here and
      // can be deleted.
      job_->isolate_->wasm_engine()->RemoveCompileJob(job_);
      return;
    }
    // If background tiering compilation finished before we resolved the
    // promise, switch to patching now. Otherwise, patching will be scheduled
    // by a callback.
    DCHECK_EQ(CompileMode::kTiering,
              job_->native_module_->compilation_state()->compile_mode());
    if (job_->tiering_completed_) {
      job_->isolate_->wasm_engine()->RemoveCompileJob(job_);
    }
  }
};

AsyncStreamingProcessor::AsyncStreamingProcessor(AsyncCompileJob* job)
    : decoder_(job->enabled_features_),
      job_(job),
      compilation_unit_builder_(nullptr) {}

void AsyncStreamingProcessor::FinishAsyncCompileJobWithError(ResultBase error) {
  // Make sure all background tasks stopped executing before we change the state
  // of the AsyncCompileJob to DecodeFail.
  job_->background_task_manager_.CancelAndWait();

  // Create a ModuleResult from the result we got as parameter. Since there was
  // no error, we don't have to provide a real wasm module to the ModuleResult.
  ModuleResult result(nullptr);
  result.MoveErrorFrom(error);

  // Check if there is already a CompiledModule, in which case we have to clean
  // up the CompilationState as well.
  if (job_->native_module_) {
    job_->native_module_->compilation_state()->Abort();

    if (job_->pending_foreground_task_ == nullptr) {
      job_->DoSync<AsyncCompileJob::DecodeFail>(std::move(result));
    } else {
      job_->NextStep<AsyncCompileJob::DecodeFail>(std::move(result));
    }

    // Clear the {compilation_unit_builder_} if it exists. This is needed
    // because there is a check in the destructor of the
    // {CompilationUnitBuilder} that it is empty.
    if (compilation_unit_builder_) compilation_unit_builder_->Clear();
  } else {
    job_->DoSync<AsyncCompileJob::DecodeFail>(std::move(result));
  }
}

// Process the module header.
bool AsyncStreamingProcessor::ProcessModuleHeader(Vector<const uint8_t> bytes,
                                                  uint32_t offset) {
  TRACE_STREAMING("Process module header...\n");
  decoder_.StartDecoding(job_->async_counters().get(),
                         job_->isolate()->wasm_engine()->allocator());
  decoder_.DecodeModuleHeader(bytes, offset);
  if (!decoder_.ok()) {
    FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false));
    return false;
  }
  return true;
}

// Process all sections except for the code section.
bool AsyncStreamingProcessor::ProcessSection(SectionCode section_code,
                                             Vector<const uint8_t> bytes,
                                             uint32_t offset) {
  TRACE_STREAMING("Process section %d ...\n", section_code);
  if (compilation_unit_builder_) {
    // We reached a section after the code section, we do not need the
    // compilation_unit_builder_ anymore.
    CommitCompilationUnits();
    compilation_unit_builder_.reset();
  }
  if (section_code == SectionCode::kUnknownSectionCode) {
    Decoder decoder(bytes, offset);
    section_code = ModuleDecoder::IdentifyUnknownSection(
        decoder, bytes.start() + bytes.length());
    if (section_code == SectionCode::kUnknownSectionCode) {
      // Skip unknown sections that we do not know how to handle.
      return true;
    }
    // Remove the unknown section tag from the payload bytes.
    offset += decoder.position();
    bytes = bytes.SubVector(decoder.position(), bytes.size());
  }
  constexpr bool verify_functions = false;
  decoder_.DecodeSection(section_code, bytes, offset, verify_functions);
  if (!decoder_.ok()) {
    FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false));
    return false;
  }
  return true;
}

// Start the code section.
bool AsyncStreamingProcessor::ProcessCodeSectionHeader(size_t functions_count,
                                                       uint32_t offset) {
  TRACE_STREAMING("Start the code section with %zu functions...\n",
                  functions_count);
  if (!decoder_.CheckFunctionsCount(static_cast<uint32_t>(functions_count),
                                    offset)) {
    FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false));
    return false;
  }
  // Execute the PrepareAndStartCompile step immediately and not in a separate
  // task.
  job_->DoImmediately<AsyncCompileJob::PrepareAndStartCompile>(
      decoder_.shared_module(), false);

  job_->native_module_->compilation_state()->SetNumberOfFunctionsToCompile(
      functions_count);

  // Set outstanding_finishers_ to 2, because both the AsyncCompileJob and the
  // AsyncStreamingProcessor have to finish.
  job_->outstanding_finishers_.store(2);
  compilation_unit_builder_.reset(
      new CompilationUnitBuilder(job_->native_module_));
  return true;
}

// Process a function body.
bool AsyncStreamingProcessor::ProcessFunctionBody(Vector<const uint8_t> bytes,
                                                  uint32_t offset) {
  TRACE_STREAMING("Process function body %d ...\n", next_function_);

    decoder_.DecodeFunctionBody(
        next_function_, static_cast<uint32_t>(bytes.length()), offset, false);

    uint32_t index = next_function_ + decoder_.module()->num_imported_functions;
    const WasmFunction* func = &decoder_.module()->functions[index];
    WasmName name = {nullptr, 0};
    compilation_unit_builder_->AddUnit(func, offset, bytes, name);
  ++next_function_;
  // This method always succeeds. The return value is necessary to comply with
  // the StreamingProcessor interface.
  return true;
}

void AsyncStreamingProcessor::CommitCompilationUnits() {
  DCHECK(compilation_unit_builder_);
  compilation_unit_builder_->Commit();
}

void AsyncStreamingProcessor::OnFinishedChunk() {
  TRACE_STREAMING("FinishChunk...\n");
  if (compilation_unit_builder_) CommitCompilationUnits();
}

// Finish the processing of the stream.
void AsyncStreamingProcessor::OnFinishedStream(OwnedVector<uint8_t> bytes) {
  TRACE_STREAMING("Finish stream...\n");
  ModuleResult result = decoder_.FinishDecoding(false);
  DCHECK(result.ok());
  bool needs_finish = job_->DecrementAndCheckFinisherCount();
  if (job_->native_module_ == nullptr) {
    // We are processing a WebAssembly module without code section. We need to
    // prepare compilation first before we can finish it.
    // {PrepareAndStartCompile} will call {FinishCompile} by itself if there
    // is no code section.
    DCHECK(needs_finish);
    needs_finish = false;
    job_->DoImmediately<AsyncCompileJob::PrepareAndStartCompile>(result.val,
                                                                 true);
  }
  job_->wire_bytes_ = ModuleWireBytes(bytes.as_vector());
  job_->native_module_->set_wire_bytes(std::move(bytes));
  if (needs_finish) {
    HandleScope scope(job_->isolate_);
    SaveContext saved_context(job_->isolate_);
    job_->isolate_->set_context(*job_->native_context_);
    job_->FinishCompile();
  }
}

// Report an error detected in the StreamingDecoder.
void AsyncStreamingProcessor::OnError(DecodeResult result) {
  TRACE_STREAMING("Stream error...\n");
  FinishAsyncCompileJobWithError(std::move(result));
}

void AsyncStreamingProcessor::OnAbort() {
  TRACE_STREAMING("Abort stream...\n");
  job_->Abort();
}

void CompilationStateDeleter::operator()(
    CompilationState* compilation_state) const {
  delete compilation_state;
}

std::unique_ptr<CompilationState, CompilationStateDeleter> NewCompilationState(
    Isolate* isolate, const ModuleEnv& env) {
  return std::unique_ptr<CompilationState, CompilationStateDeleter>(
      new CompilationState(isolate, env));
}

ModuleEnv* GetModuleEnv(CompilationState* compilation_state) {
  return compilation_state->module_env();
}

CompilationState::CompilationState(internal::Isolate* isolate,
                                   const ModuleEnv& env)
    : isolate_(isolate),
      wasm_engine_(isolate->wasm_engine()),
      module_env_(env),
      compile_mode_(FLAG_wasm_tier_up && env.module->origin == kWasmOrigin
                        ? CompileMode::kTiering
                        : CompileMode::kRegular),
      max_background_tasks_(std::max(
          1, std::min(FLAG_wasm_num_compilation_tasks,
                      V8::GetCurrentPlatform()->NumberOfWorkerThreads()))) {
  v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
  v8::Platform* platform = V8::GetCurrentPlatform();
  foreground_task_runner_ = platform->GetForegroundTaskRunner(v8_isolate);
}

CompilationState::~CompilationState() {
  background_task_manager_.CancelAndWait();
  foreground_task_manager_.CancelAndWait();
}

void CompilationState::SetNumberOfFunctionsToCompile(size_t num_functions) {
  DCHECK(!failed());
  outstanding_units_ = num_functions;

  if (compile_mode_ == CompileMode::kTiering) {
    outstanding_units_ += num_functions;
    num_tiering_units_ = num_functions;
  }
}

void CompilationState::SetCallback(
    std::function<void(CompilationEvent, ErrorThrower*)> callback) {
  DCHECK_NULL(callback_);
  callback_ = callback;
}

void CompilationState::AddCompilationUnits(
    std::vector<std::unique_ptr<WasmCompilationUnit>>& baseline_units,
    std::vector<std::unique_ptr<WasmCompilationUnit>>& tiering_units) {
  {
    base::LockGuard<base::Mutex> guard(&mutex_);

    if (compile_mode_ == CompileMode::kTiering) {
      DCHECK_EQ(baseline_units.size(), tiering_units.size());
      DCHECK_EQ(tiering_units.back()->mode(), ExecutionTier::kOptimized);
      tiering_compilation_units_.insert(
          tiering_compilation_units_.end(),
          std::make_move_iterator(tiering_units.begin()),
          std::make_move_iterator(tiering_units.end()));
    } else {
      DCHECK(tiering_compilation_units_.empty());
    }

    baseline_compilation_units_.insert(
        baseline_compilation_units_.end(),
        std::make_move_iterator(baseline_units.begin()),
        std::make_move_iterator(baseline_units.end()));
  }

  RestartBackgroundTasks();
}

std::unique_ptr<WasmCompilationUnit>
CompilationState::GetNextCompilationUnit() {
  base::LockGuard<base::Mutex> guard(&mutex_);

  std::vector<std::unique_ptr<WasmCompilationUnit>>& units =
      baseline_compilation_units_.empty() ? tiering_compilation_units_
                                          : baseline_compilation_units_;

  if (!units.empty()) {
    std::unique_ptr<WasmCompilationUnit> unit = std::move(units.back());
    units.pop_back();
    return unit;
  }

  return std::unique_ptr<WasmCompilationUnit>();
}

std::unique_ptr<WasmCompilationUnit> CompilationState::GetNextExecutedUnit() {
  base::LockGuard<base::Mutex> guard(&mutex_);
  std::vector<std::unique_ptr<WasmCompilationUnit>>& units = finish_units();
  if (units.empty()) return {};
  std::unique_ptr<WasmCompilationUnit> ret = std::move(units.back());
  units.pop_back();
  return ret;
}

bool CompilationState::HasCompilationUnitToFinish() {
  base::LockGuard<base::Mutex> guard(&mutex_);
  return !finish_units().empty();
}

void CompilationState::OnError(ErrorThrower* thrower) {
  Abort();
  DCHECK(thrower->error());
  NotifyOnEvent(CompilationEvent::kFailedCompilation, thrower);
}

void CompilationState::OnFinishedUnit() {
  DCHECK_GT(outstanding_units_, 0);
  --outstanding_units_;

  if (outstanding_units_ == 0) {
    background_task_manager_.CancelAndWait();
    baseline_compilation_finished_ = true;

    DCHECK(compile_mode_ == CompileMode::kRegular ||
           compile_mode_ == CompileMode::kTiering);
    NotifyOnEvent(compile_mode_ == CompileMode::kRegular
                      ? CompilationEvent::kFinishedBaselineCompilation
                      : CompilationEvent::kFinishedTopTierCompilation,
                  nullptr);

  } else if (outstanding_units_ == num_tiering_units_) {
    DCHECK_EQ(compile_mode_, CompileMode::kTiering);
    baseline_compilation_finished_ = true;

    // TODO(wasm): For streaming compilation, we want to start top tier
    // compilation before all functions have been compiled with Liftoff, e.g.
    // in the case when all received functions have been compiled with Liftoff
    // and we are waiting for new functions to compile.

    // If we are in {kRegular} mode, {num_tiering_units_} is 0, therefore
    // this case is already caught by the previous check.
    NotifyOnEvent(CompilationEvent::kFinishedBaselineCompilation, nullptr);
    RestartBackgroundTasks();
  }
}

void CompilationState::ScheduleUnitForFinishing(
    std::unique_ptr<WasmCompilationUnit> unit, ExecutionTier mode) {
  base::LockGuard<base::Mutex> guard(&mutex_);
  if (compile_mode_ == CompileMode::kTiering &&
      mode == ExecutionTier::kOptimized) {
    tiering_finish_units_.push_back(std::move(unit));
  } else {
    baseline_finish_units_.push_back(std::move(unit));
  }

  if (!finisher_is_running_ && !failed_) {
    ScheduleFinisherTask();
    // We set the flag here so that not more than one finisher is started.
    finisher_is_running_ = true;
  }
}

void CompilationState::OnBackgroundTaskStopped(const WasmFeatures& detected) {
  base::LockGuard<base::Mutex> guard(&mutex_);
  DCHECK_LE(1, num_background_tasks_);
  --num_background_tasks_;
  UnionFeaturesInto(&detected_features_, detected);
}

void CompilationState::PublishDetectedFeatures(Isolate* isolate,
                                               const WasmFeatures& detected) {
  // Notifying the isolate of the feature counts must take place under
  // the mutex, because even if we have finished baseline compilation,
  // tiering compilations may still occur in the background.
  base::LockGuard<base::Mutex> guard(&mutex_);
  UnionFeaturesInto(&detected_features_, detected);
  UpdateFeatureUseCounts(isolate, detected_features_);
}

void CompilationState::RestartBackgroundTasks(size_t max) {
  size_t num_restart;
  {
    base::LockGuard<base::Mutex> guard(&mutex_);
    // No need to restart tasks if compilation already failed.
    if (failed_) return;

    DCHECK_LE(num_background_tasks_, max_background_tasks_);
    if (num_background_tasks_ == max_background_tasks_) return;
    size_t num_compilation_units =
        baseline_compilation_units_.size() + tiering_compilation_units_.size();
    size_t stopped_tasks = max_background_tasks_ - num_background_tasks_;
    num_restart = std::min(max, std::min(num_compilation_units, stopped_tasks));
    num_background_tasks_ += num_restart;
  }

  for (; num_restart > 0; --num_restart) {
    auto task = base::make_unique<BackgroundCompileTask>(
        this, &background_task_manager_);

    // If --wasm-num-compilation-tasks=0 is passed, do only spawn foreground
    // tasks. This is used to make timing deterministic.
    if (FLAG_wasm_num_compilation_tasks > 0) {
      V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
    } else {
      foreground_task_runner_->PostTask(std::move(task));
    }
  }
}

bool CompilationState::SetFinisherIsRunning(bool value) {
  base::LockGuard<base::Mutex> guard(&mutex_);
  if (finisher_is_running_ == value) return false;
  finisher_is_running_ = value;
  return true;
}

void CompilationState::ScheduleFinisherTask() {
  foreground_task_runner_->PostTask(
      base::make_unique<FinishCompileTask>(this, &foreground_task_manager_));
}

void CompilationState::Abort() {
  {
    base::LockGuard<base::Mutex> guard(&mutex_);
    failed_ = true;
  }
  background_task_manager_.CancelAndWait();
}

void CompilationState::NotifyOnEvent(CompilationEvent event,
                                     ErrorThrower* thrower) {
  if (callback_) callback_(event, thrower);
}

void CompileJsToWasmWrappers(Isolate* isolate,
                             Handle<WasmModuleObject> module_object) {
  JSToWasmWrapperCache js_to_wasm_cache;
  int wrapper_index = 0;
  Handle<FixedArray> export_wrappers(module_object->export_wrappers(), isolate);
  NativeModule* native_module = module_object->native_module();
  UseTrapHandler use_trap_handler =
      native_module->use_trap_handler() ? kUseTrapHandler : kNoTrapHandler;
  const WasmModule* module = native_module->module();
  for (auto exp : module->export_table) {
    if (exp.kind != kExternalFunction) continue;
    Handle<Code> wrapper_code = js_to_wasm_cache.GetOrCompileJSToWasmWrapper(
        isolate, native_module, exp.index, use_trap_handler);
    export_wrappers->set(wrapper_index, *wrapper_code);
    RecordStats(*wrapper_code, isolate->counters());
    ++wrapper_index;
  }
}

Handle<Script> CreateWasmScript(Isolate* isolate,
                                const ModuleWireBytes& wire_bytes) {
  Handle<Script> script =
      isolate->factory()->NewScript(isolate->factory()->empty_string());
  script->set_context_data(isolate->native_context()->debug_context_id());
  script->set_type(Script::TYPE_WASM);

  int hash = StringHasher::HashSequentialString(
      reinterpret_cast<const char*>(wire_bytes.start()),
      static_cast<int>(wire_bytes.length()), kZeroHashSeed);

  const int kBufferSize = 32;
  char buffer[kBufferSize];
  int url_chars = SNPrintF(ArrayVector(buffer), "wasm://wasm/%08x", hash);
  DCHECK(url_chars >= 0 && url_chars < kBufferSize);
  MaybeHandle<String> url_str = isolate->factory()->NewStringFromOneByte(
      Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), url_chars),
      TENURED);
  script->set_source_url(*url_str.ToHandleChecked());

  int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash);
  DCHECK(name_chars >= 0 && name_chars < kBufferSize);
  MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte(
      Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), name_chars),
      TENURED);
  script->set_name(*name_str.ToHandleChecked());

  return script;
}

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

#undef TRACE
#undef TRACE_COMPILE
#undef TRACE_STREAMING
#undef TRACE_LAZY