LCOV - code coverage report
Current view: top level - src/wasm - module-compiler.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 684 707 96.7 %
Date: 2019-04-18 Functions: 103 119 86.6 %

          Line data    Source code
       1             : // Copyright 2017 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #include "src/wasm/module-compiler.h"
       6             : 
       7             : #include <algorithm>
       8             : 
       9             : #include "src/api.h"
      10             : #include "src/asmjs/asm-js.h"
      11             : #include "src/base/enum-set.h"
      12             : #include "src/base/optional.h"
      13             : #include "src/base/platform/mutex.h"
      14             : #include "src/base/platform/semaphore.h"
      15             : #include "src/base/template-utils.h"
      16             : #include "src/base/utils/random-number-generator.h"
      17             : #include "src/compiler/wasm-compiler.h"
      18             : #include "src/counters.h"
      19             : #include "src/heap/heap-inl.h"  // For CodeSpaceMemoryModificationScope.
      20             : #include "src/identity-map.h"
      21             : #include "src/property-descriptor.h"
      22             : #include "src/task-utils.h"
      23             : #include "src/tracing/trace-event.h"
      24             : #include "src/trap-handler/trap-handler.h"
      25             : #include "src/wasm/js-to-wasm-wrapper-cache.h"
      26             : #include "src/wasm/module-decoder.h"
      27             : #include "src/wasm/streaming-decoder.h"
      28             : #include "src/wasm/wasm-code-manager.h"
      29             : #include "src/wasm/wasm-engine.h"
      30             : #include "src/wasm/wasm-import-wrapper-cache.h"
      31             : #include "src/wasm/wasm-js.h"
      32             : #include "src/wasm/wasm-limits.h"
      33             : #include "src/wasm/wasm-memory.h"
      34             : #include "src/wasm/wasm-objects-inl.h"
      35             : #include "src/wasm/wasm-result.h"
      36             : #include "src/wasm/wasm-serialization.h"
      37             : 
      38             : #define TRACE_COMPILE(...)                             \
      39             :   do {                                                 \
      40             :     if (FLAG_trace_wasm_compiler) PrintF(__VA_ARGS__); \
      41             :   } while (false)
      42             : 
      43             : #define TRACE_STREAMING(...)                            \
      44             :   do {                                                  \
      45             :     if (FLAG_trace_wasm_streaming) PrintF(__VA_ARGS__); \
      46             :   } while (false)
      47             : 
      48             : #define TRACE_LAZY(...)                                        \
      49             :   do {                                                         \
      50             :     if (FLAG_trace_wasm_lazy_compilation) PrintF(__VA_ARGS__); \
      51             :   } while (false)
      52             : 
      53             : namespace v8 {
      54             : namespace internal {
      55             : namespace wasm {
      56             : 
      57             : namespace {
      58             : 
      59             : enum class CompileMode : uint8_t { kRegular, kTiering };
      60             : 
      61             : // Background compile jobs hold a shared pointer to this token. The token is
      62             : // used to notify them that they should stop. As soon as they see this (after
      63             : // finishing their current compilation unit), they will stop.
      64             : // This allows to already remove the NativeModule without having to synchronize
      65             : // on background compile jobs.
      66     2484714 : class BackgroundCompileToken {
      67             :  public:
      68             :   explicit BackgroundCompileToken(
      69             :       const std::shared_ptr<NativeModule>& native_module)
      70     1242357 :       : native_module_(native_module) {}
      71             : 
      72     1250508 :   void Cancel() {
      73     1250508 :     base::SharedMutexGuard<base::kExclusive> mutex_guard(&mutex_);
      74             :     native_module_.reset();
      75     1250508 :   }
      76             : 
      77             :  private:
      78             :   friend class BackgroundCompileScope;
      79             :   base::SharedMutex mutex_;
      80             :   std::weak_ptr<NativeModule> native_module_;
      81             : 
      82             :   std::shared_ptr<NativeModule> StartScope() {
      83      804601 :     mutex_.LockShared();
      84             :     return native_module_.lock();
      85             :   }
      86             : 
      87      809453 :   void ExitScope() { mutex_.UnlockShared(); }
      88             : };
      89             : 
      90             : class CompilationStateImpl;
      91             : 
      92             : // Keep these scopes short, as they hold the mutex of the token, which
      93             : // sequentializes all these scopes. The mutex is also acquired from foreground
      94             : // tasks, which should not be blocked for a long time.
      95             : class BackgroundCompileScope {
      96             :  public:
      97      804601 :   explicit BackgroundCompileScope(
      98             :       const std::shared_ptr<BackgroundCompileToken>& token)
      99     1614170 :       : token_(token.get()), native_module_(token->StartScope()) {}
     100             : 
     101     1618903 :   ~BackgroundCompileScope() { token_->ExitScope(); }
     102             : 
     103             :   bool cancelled() const { return native_module_ == nullptr; }
     104             : 
     105             :   NativeModule* native_module() {
     106             :     DCHECK(!cancelled());
     107             :     return native_module_.get();
     108             :   }
     109             : 
     110             :   inline CompilationStateImpl* compilation_state();
     111             : 
     112             :  private:
     113             :   BackgroundCompileToken* const token_;
     114             :   // Keep the native module alive while in this scope.
     115             :   std::shared_ptr<NativeModule> const native_module_;
     116             : };
     117             : 
     118             : enum CompileBaselineOnly : bool {
     119             :   kBaselineOnly = true,
     120             :   kBaselineOrTopTier = false
     121             : };
     122             : 
     123             : // A set of work-stealing queues (vectors of units). Each background compile
     124             : // task owns one of the queues and steals from all others once its own queue
     125             : // runs empty.
     126     1242357 : class CompilationUnitQueues {
     127             :  public:
     128     1242357 :   explicit CompilationUnitQueues(int max_tasks) : queues_(max_tasks) {
     129             :     DCHECK_LT(0, max_tasks);
     130    18635245 :     for (int task_id = 0; task_id < max_tasks; ++task_id) {
     131    17392888 :       queues_[task_id].next_steal_task_id_ = next_task_id(task_id);
     132             :     }
     133     3727070 :     for (auto& atomic_counter : num_units_) {
     134             :       std::atomic_init(&atomic_counter, size_t{0});
     135             :     }
     136     1242357 :   }
     137             : 
     138      788778 :   std::unique_ptr<WasmCompilationUnit> GetNextUnit(
     139             :       int task_id, CompileBaselineOnly baseline_only) {
     140             :     DCHECK_LE(0, task_id);
     141             :     DCHECK_GT(queues_.size(), task_id);
     142             : 
     143             :     // As long as any lower-tier units are outstanding we need to steal them
     144             :     // before executing own higher-tier units.
     145      788778 :     int max_tier = baseline_only ? kBaseline : kTopTier;
     146      846828 :     for (int tier = GetLowestTierWithUnits(); tier <= max_tier; ++tier) {
     147      352988 :       Queue* queue = &queues_[task_id];
     148             :       // First, check whether our own queue has a unit of the wanted tier. If
     149             :       // so, return it, otherwise get the task id to steal from.
     150             :       int steal_task_id;
     151             :       {
     152      352988 :         base::MutexGuard mutex_guard(&queue->mutex_);
     153      353271 :         if (!queue->units_[tier].empty()) {
     154      305183 :           auto unit = std::move(queue->units_[tier].back());
     155             :           queue->units_[tier].pop_back();
     156             :           DecrementUnitCount(tier);
     157             :           return unit;
     158             :         }
     159       48048 :         steal_task_id = queue->next_steal_task_id_;
     160             :       }
     161             : 
     162             :       // Try to steal from all other queues. If none of this succeeds, the outer
     163             :       // loop increases the tier and retries.
     164             :       size_t steal_trials = queues_.size();
     165      820210 :       for (; steal_trials > 0;
     166             :            --steal_trials, steal_task_id = next_task_id(steal_task_id)) {
     167      276379 :         if (steal_task_id == task_id) continue;
     168      711891 :         if (auto unit = StealUnitsAndGetFirst(task_id, steal_task_id, tier)) {
     169             :           DecrementUnitCount(tier);
     170       19020 :           return unit;
     171             :         }
     172             :       }
     173             :     }
     174             :     return {};
     175             :   }
     176             : 
     177      138237 :   void AddUnits(Vector<std::unique_ptr<WasmCompilationUnit>> baseline_units,
     178             :                 Vector<std::unique_ptr<WasmCompilationUnit>> top_tier_units) {
     179             :     DCHECK_LT(0, baseline_units.size() + top_tier_units.size());
     180             :     // Add to the individual queues in a round-robin fashion. No special care is
     181             :     // taken to balance them; they will be balanced by work stealing.
     182      138237 :     int queue_to_add = next_queue_to_add.load(std::memory_order_relaxed);
     183      276474 :     while (!next_queue_to_add.compare_exchange_weak(
     184             :         queue_to_add, next_task_id(queue_to_add), std::memory_order_relaxed)) {
     185             :       // Retry with updated {queue_to_add}.
     186             :     }
     187             : 
     188      138237 :     Queue* queue = &queues_[queue_to_add];
     189      138237 :     base::MutexGuard guard(&queue->mutex_);
     190      138238 :     if (!baseline_units.empty()) {
     191             :       queue->units_[kBaseline].insert(
     192             :           queue->units_[kBaseline].end(),
     193             :           std::make_move_iterator(baseline_units.begin()),
     194      138168 :           std::make_move_iterator(baseline_units.end()));
     195             :       num_units_[kBaseline].fetch_add(baseline_units.size(),
     196             :                                       std::memory_order_relaxed);
     197             :     }
     198      138239 :     if (!top_tier_units.empty()) {
     199             :       queue->units_[kTopTier].insert(
     200             :           queue->units_[kTopTier].end(),
     201             :           std::make_move_iterator(top_tier_units.begin()),
     202       51792 :           std::make_move_iterator(top_tier_units.end()));
     203             :       num_units_[kTopTier].fetch_add(top_tier_units.size(),
     204             :                                      std::memory_order_relaxed);
     205             :     }
     206      138238 :   }
     207             : 
     208             :   // Get the current total number of units in all queues. This is only a
     209             :   // momentary snapshot, it's not guaranteed that {GetNextUnit} returns a unit
     210             :   // if this method returns non-zero.
     211             :   size_t GetTotalSize() const {
     212             :     size_t total = 0;
     213     1075142 :     for (auto& atomic_counter : num_units_) {
     214      716762 :       total += atomic_counter.load(std::memory_order_relaxed);
     215             :     }
     216             :     return total;
     217             :   }
     218             : 
     219             :  private:
     220             :   // Store tier in int so we can easily loop over it:
     221             :   static constexpr int kBaseline = 0;
     222             :   static constexpr int kTopTier = 1;
     223             :   static constexpr int kNumTiers = kTopTier + 1;
     224             : 
     225    34785783 :   struct Queue {
     226             :     base::Mutex mutex_;
     227             : 
     228             :     // Protected by {mutex_}:
     229             :     std::vector<std::unique_ptr<WasmCompilationUnit>> units_[kNumTiers];
     230             :     int next_steal_task_id_;
     231             :     // End of fields protected by {mutex_}.
     232             :   };
     233             : 
     234             :   std::vector<Queue> queues_;
     235             : 
     236             :   std::atomic<size_t> num_units_[kNumTiers];
     237             :   std::atomic<int> next_queue_to_add{0};
     238             : 
     239             :   int next_task_id(int task_id) const {
     240     9111097 :     int next = task_id + 1;
     241     9111097 :     return next == static_cast<int>(queues_.size()) ? 0 : next;
     242             :   }
     243             : 
     244             :   int GetLowestTierWithUnits() const {
     245     2726032 :     for (int tier = 0; tier < kNumTiers; ++tier) {
     246     1340598 :       if (num_units_[tier].load(std::memory_order_relaxed) > 0) return tier;
     247             :     }
     248             :     return kNumTiers;
     249             :   }
     250             : 
     251             :   void DecrementUnitCount(int tier) {
     252             :     size_t old_units_count = num_units_[tier].fetch_sub(1);
     253             :     DCHECK_LE(1, old_units_count);
     254             :     USE(old_units_count);
     255             :   }
     256             : 
     257             :   // Steal units of {wanted_tier} from {steal_from_task_id} to {task_id}. Return
     258             :   // first stolen unit (rest put in queue of {task_id}), or {nullptr} if
     259             :   // {steal_from_task_id} had no units of {wanted_tier}.
     260      243525 :   std::unique_ptr<WasmCompilationUnit> StealUnitsAndGetFirst(
     261             :       int task_id, int steal_from_task_id, int wanted_tier) {
     262             :     DCHECK_NE(task_id, steal_from_task_id);
     263      243762 :     std::vector<std::unique_ptr<WasmCompilationUnit>> stolen;
     264             :     {
     265      243525 :       Queue* steal_queue = &queues_[steal_from_task_id];
     266      243525 :       base::MutexGuard guard(&steal_queue->mutex_);
     267      243742 :       if (steal_queue->units_[wanted_tier].empty()) return {};
     268             :       auto* steal_from_vector = &steal_queue->units_[wanted_tier];
     269       19021 :       size_t remaining = steal_from_vector->size() / 2;
     270             :       stolen.assign(
     271             :           std::make_move_iterator(steal_from_vector->begin()) + remaining,
     272             :           std::make_move_iterator(steal_from_vector->end()));
     273       19018 :       steal_from_vector->resize(remaining);
     274             :     }
     275             :     DCHECK(!stolen.empty());
     276       19019 :     auto returned_unit = std::move(stolen.back());
     277             :     stolen.pop_back();
     278       19021 :     Queue* queue = &queues_[task_id];
     279       19021 :     base::MutexGuard guard(&queue->mutex_);
     280       19020 :     auto* target_queue = &queue->units_[wanted_tier];
     281             :     target_queue->insert(target_queue->end(),
     282             :                          std::make_move_iterator(stolen.begin()),
     283       19020 :                          std::make_move_iterator(stolen.end()));
     284       19013 :     queue->next_steal_task_id_ = next_task_id(steal_from_task_id);
     285             :     return returned_unit;
     286             :   }
     287             : };
     288             : 
     289             : // The {CompilationStateImpl} keeps track of the compilation state of the
     290             : // owning NativeModule, i.e. which functions are left to be compiled.
     291             : // It contains a task manager to allow parallel and asynchronous background
     292             : // compilation of functions.
     293             : // Its public interface {CompilationState} lives in compilation-environment.h.
     294     4969428 : class CompilationStateImpl {
     295             :  public:
     296             :   CompilationStateImpl(const std::shared_ptr<NativeModule>& native_module,
     297             :                        std::shared_ptr<Counters> async_counters);
     298             : 
     299             :   // Cancel all background compilation and wait for all tasks to finish. Call
     300             :   // this before destructing this object.
     301             :   void AbortCompilation();
     302             : 
     303             :   // Set the number of compilations unit expected to be executed. Needs to be
     304             :   // set before {AddCompilationUnits} is run, which triggers background
     305             :   // compilation.
     306             :   void SetNumberOfFunctionsToCompile(int num_functions, int num_lazy_functions);
     307             : 
     308             :   // Add the callback function to be called on compilation events. Needs to be
     309             :   // set before {AddCompilationUnits} is run to ensure that it receives all
     310             :   // events. The callback object must support being deleted from any thread.
     311             :   void AddCallback(CompilationState::callback_t);
     312             : 
     313             :   // Inserts new functions to compile and kicks off compilation.
     314             :   void AddCompilationUnits(
     315             :       Vector<std::unique_ptr<WasmCompilationUnit>> baseline_units,
     316             :       Vector<std::unique_ptr<WasmCompilationUnit>> top_tier_units);
     317             :   void AddTopTierCompilationUnit(std::unique_ptr<WasmCompilationUnit>);
     318             :   std::unique_ptr<WasmCompilationUnit> GetNextCompilationUnit(
     319             :       int task_id, CompileBaselineOnly baseline_only);
     320             : 
     321             :   void OnFinishedUnit(WasmCode*);
     322             :   void OnFinishedUnits(Vector<WasmCode*>);
     323             : 
     324             :   void OnBackgroundTaskStopped(int task_id, const WasmFeatures& detected);
     325             :   void UpdateDetectedFeatures(const WasmFeatures& detected);
     326             :   void PublishDetectedFeatures(Isolate*);
     327             :   void RestartBackgroundTasks();
     328             : 
     329             :   void SetError();
     330             : 
     331             :   bool failed() const {
     332             :     return compile_failed_.load(std::memory_order_relaxed);
     333             :   }
     334             : 
     335             :   bool baseline_compilation_finished() const {
     336      358372 :     base::MutexGuard guard(&callbacks_mutex_);
     337             :     DCHECK_LE(outstanding_baseline_functions_, outstanding_top_tier_functions_);
     338      358374 :     return outstanding_baseline_functions_ == 0;
     339             :   }
     340             : 
     341             :   CompileMode compile_mode() const { return compile_mode_; }
     342             :   Counters* counters() const { return async_counters_.get(); }
     343        8143 :   WasmFeatures* detected_features() { return &detected_features_; }
     344             : 
     345     2340168 :   void SetWireBytesStorage(
     346             :       std::shared_ptr<WireBytesStorage> wire_bytes_storage) {
     347     2340168 :     base::MutexGuard guard(&mutex_);
     348             :     wire_bytes_storage_ = wire_bytes_storage;
     349     2340168 :   }
     350             : 
     351             :   std::shared_ptr<WireBytesStorage> GetWireBytesStorage() const {
     352     1214252 :     base::MutexGuard guard(&mutex_);
     353             :     DCHECK_NOT_NULL(wire_bytes_storage_);
     354             :     return wire_bytes_storage_;
     355             :   }
     356             : 
     357             :   const std::shared_ptr<BackgroundCompileToken>& background_compile_token()
     358             :       const {
     359      256709 :     return background_compile_token_;
     360             :   }
     361             : 
     362             :  private:
     363             :   NativeModule* const native_module_;
     364             :   const std::shared_ptr<BackgroundCompileToken> background_compile_token_;
     365             :   const CompileMode compile_mode_;
     366             :   const std::shared_ptr<Counters> async_counters_;
     367             : 
     368             :   // Compilation error, atomically updated. This flag can be updated and read
     369             :   // using relaxed semantics.
     370             :   std::atomic<bool> compile_failed_{false};
     371             : 
     372             :   const int max_background_tasks_ = 0;
     373             : 
     374             :   CompilationUnitQueues compilation_unit_queues_;
     375             : 
     376             :   // This mutex protects all information of this {CompilationStateImpl} which is
     377             :   // being accessed concurrently.
     378             :   mutable base::Mutex mutex_;
     379             : 
     380             :   //////////////////////////////////////////////////////////////////////////////
     381             :   // Protected by {mutex_}:
     382             : 
     383             :   // Set of unused task ids; <= {max_background_tasks_} many.
     384             :   std::vector<int> available_task_ids_;
     385             : 
     386             :   // Features detected to be used in this module. Features can be detected
     387             :   // as a module is being compiled.
     388             :   WasmFeatures detected_features_ = kNoWasmFeatures;
     389             : 
     390             :   // Abstraction over the storage of the wire bytes. Held in a shared_ptr so
     391             :   // that background compilation jobs can keep the storage alive while
     392             :   // compiling.
     393             :   std::shared_ptr<WireBytesStorage> wire_bytes_storage_;
     394             : 
     395             :   // End of fields protected by {mutex_}.
     396             :   //////////////////////////////////////////////////////////////////////////////
     397             : 
     398             :   // This mutex protects the callbacks vector, and the counters used to
     399             :   // determine which callbacks to call. The counters plus the callbacks
     400             :   // themselves need to be synchronized to ensure correct order of events.
     401             :   mutable base::Mutex callbacks_mutex_;
     402             : 
     403             :   //////////////////////////////////////////////////////////////////////////////
     404             :   // Protected by {callbacks_mutex_}:
     405             : 
     406             :   // Callback functions to be called on compilation events.
     407             :   std::vector<CompilationState::callback_t> callbacks_;
     408             : 
     409             :   int outstanding_baseline_functions_ = 0;
     410             :   int outstanding_top_tier_functions_ = 0;
     411             :   std::vector<ExecutionTier> highest_execution_tier_;
     412             : 
     413             :   // End of fields protected by {callbacks_mutex_}.
     414             :   //////////////////////////////////////////////////////////////////////////////
     415             : };
     416             : 
     417             : CompilationStateImpl* Impl(CompilationState* compilation_state) {
     418             :   return reinterpret_cast<CompilationStateImpl*>(compilation_state);
     419             : }
     420             : const CompilationStateImpl* Impl(const CompilationState* compilation_state) {
     421             :   return reinterpret_cast<const CompilationStateImpl*>(compilation_state);
     422             : }
     423             : 
     424             : CompilationStateImpl* BackgroundCompileScope::compilation_state() {
     425             :   return Impl(native_module()->compilation_state());
     426             : }
     427             : 
     428             : void UpdateFeatureUseCounts(Isolate* isolate, const WasmFeatures& detected) {
     429      144347 :   if (detected.threads) {
     430        1405 :     isolate->CountUsage(v8::Isolate::UseCounterFeature::kWasmThreadOpcodes);
     431             :   }
     432             : }
     433             : 
     434             : }  // namespace
     435             : 
     436             : //////////////////////////////////////////////////////
     437             : // PIMPL implementation of {CompilationState}.
     438             : 
     439     1242357 : CompilationState::~CompilationState() { Impl(this)->~CompilationStateImpl(); }
     440             : 
     441     1242357 : void CompilationState::AbortCompilation() { Impl(this)->AbortCompilation(); }
     442             : 
     443           0 : void CompilationState::SetError() { Impl(this)->SetError(); }
     444             : 
     445     2339904 : void CompilationState::SetWireBytesStorage(
     446             :     std::shared_ptr<WireBytesStorage> wire_bytes_storage) {
     447     4679808 :   Impl(this)->SetWireBytesStorage(std::move(wire_bytes_storage));
     448     2339904 : }
     449             : 
     450      731780 : std::shared_ptr<WireBytesStorage> CompilationState::GetWireBytesStorage()
     451             :     const {
     452      731780 :   return Impl(this)->GetWireBytesStorage();
     453             : }
     454             : 
     455        2216 : void CompilationState::AddCallback(CompilationState::callback_t callback) {
     456        4432 :   return Impl(this)->AddCallback(std::move(callback));
     457             : }
     458             : 
     459          32 : bool CompilationState::failed() const { return Impl(this)->failed(); }
     460             : 
     461           0 : void CompilationState::OnFinishedUnit(WasmCode* code) {
     462             :   Impl(this)->OnFinishedUnit(code);
     463           0 : }
     464             : 
     465           0 : void CompilationState::OnFinishedUnits(Vector<WasmCode*> code_vector) {
     466           0 :   Impl(this)->OnFinishedUnits(code_vector);
     467           0 : }
     468             : 
     469             : // static
     470     1242357 : std::unique_ptr<CompilationState> CompilationState::New(
     471             :     const std::shared_ptr<NativeModule>& native_module,
     472             :     std::shared_ptr<Counters> async_counters) {
     473             :   return std::unique_ptr<CompilationState>(reinterpret_cast<CompilationState*>(
     474     2484714 :       new CompilationStateImpl(native_module, std::move(async_counters))));
     475             : }
     476             : 
     477             : // End of PIMPL implementation of {CompilationState}.
     478             : //////////////////////////////////////////////////////
     479             : 
     480             : namespace {
     481             : 
     482         252 : ExecutionTier ApplyHintToExecutionTier(WasmCompilationHintTier hint,
     483             :                                        ExecutionTier default_tier) {
     484         252 :   switch (hint) {
     485             :     case WasmCompilationHintTier::kDefault:
     486         150 :       return default_tier;
     487             :     case WasmCompilationHintTier::kInterpreter:
     488             :       return ExecutionTier::kInterpreter;
     489             :     case WasmCompilationHintTier::kBaseline:
     490          45 :       return ExecutionTier::kLiftoff;
     491             :     case WasmCompilationHintTier::kOptimized:
     492          45 :       return ExecutionTier::kTurbofan;
     493             :   }
     494           0 :   UNREACHABLE();
     495             : }
     496             : 
     497             : const WasmCompilationHint* GetCompilationHint(const WasmModule* module,
     498             :                                               uint32_t func_index) {
     499             :   DCHECK_LE(module->num_imported_functions, func_index);
     500        1347 :   uint32_t hint_index = func_index - module->num_imported_functions;
     501             :   const std::vector<WasmCompilationHint>& compilation_hints =
     502             :       module->compilation_hints;
     503        2694 :   if (hint_index < compilation_hints.size()) {
     504             :     return &compilation_hints[hint_index];
     505             :   }
     506             :   return nullptr;
     507             : }
     508             : 
     509             : bool IsLazyCompilation(const WasmModule* module,
     510             :                        const WasmFeatures& enabled_features,
     511             :                        uint32_t func_index) {
     512      545544 :   if (enabled_features.compilation_hints) {
     513             :     const WasmCompilationHint* hint = GetCompilationHint(module, func_index);
     514        1247 :     return hint != nullptr &&
     515         395 :            hint->strategy == WasmCompilationHintStrategy::kLazy;
     516             :   }
     517             :   return false;
     518             : }
     519             : 
     520             : bool IsLazyCompilation(const WasmModule* module,
     521             :                        const NativeModule* native_module,
     522             :                        const WasmFeatures& enabled_features,
     523             :                        uint32_t func_index) {
     524      545570 :   if (native_module->lazy_compilation()) return true;
     525             :   return IsLazyCompilation(module, enabled_features, func_index);
     526             : }
     527             : 
     528             : struct ExecutionTierPair {
     529             :   ExecutionTier baseline_tier;
     530             :   ExecutionTier top_tier;
     531             : };
     532             : 
     533      553498 : ExecutionTierPair GetRequestedExecutionTiers(
     534             :     const WasmModule* module, CompileMode compile_mode,
     535             :     const WasmFeatures& enabled_features, uint32_t func_index) {
     536             :   ExecutionTierPair result;
     537      553498 :   switch (compile_mode) {
     538             :     case CompileMode::kRegular:
     539             :       result.baseline_tier =
     540      274244 :           WasmCompilationUnit::GetDefaultExecutionTier(module);
     541             :       result.top_tier = result.baseline_tier;
     542      274245 :       return result;
     543             :     case CompileMode::kTiering:
     544             : 
     545             :       // Default tiering behaviour.
     546             :       result.baseline_tier = ExecutionTier::kLiftoff;
     547             :       result.top_tier = ExecutionTier::kTurbofan;
     548             : 
     549             :       // Check if compilation hints override default tiering behaviour.
     550      279254 :       if (enabled_features.compilation_hints) {
     551             :         const WasmCompilationHint* hint =
     552             :             GetCompilationHint(module, func_index);
     553         495 :         if (hint != nullptr) {
     554         126 :           result.baseline_tier = ApplyHintToExecutionTier(hint->baseline_tier,
     555             :                                                           result.baseline_tier);
     556             :           result.top_tier =
     557         126 :               ApplyHintToExecutionTier(hint->top_tier, result.top_tier);
     558             :         }
     559             :       }
     560             : 
     561             :       // Correct top tier if necessary.
     562             :       static_assert(ExecutionTier::kInterpreter < ExecutionTier::kLiftoff &&
     563             :                         ExecutionTier::kLiftoff < ExecutionTier::kTurbofan,
     564             :                     "Assume an order on execution tiers");
     565      279253 :       if (result.baseline_tier > result.top_tier) {
     566             :         result.top_tier = result.baseline_tier;
     567             :       }
     568      279253 :       return result;
     569             :   }
     570           0 :   UNREACHABLE();
     571             : }
     572             : 
     573             : // The {CompilationUnitBuilder} builds compilation units and stores them in an
     574             : // internal buffer. The buffer is moved into the working queue of the
     575             : // {CompilationStateImpl} when {Commit} is called.
     576      144488 : class CompilationUnitBuilder {
     577             :  public:
     578             :   explicit CompilationUnitBuilder(NativeModule* native_module)
     579             :       : native_module_(native_module),
     580      144488 :         default_tier_(WasmCompilationUnit::GetDefaultExecutionTier(
     581      288973 :             native_module->module())) {}
     582             : 
     583      230910 :   void AddUnits(uint32_t func_index) {
     584             :     ExecutionTierPair tiers = GetRequestedExecutionTiers(
     585             :         native_module_->module(), compilation_state()->compile_mode(),
     586      461820 :         native_module_->enabled_features(), func_index);
     587      230912 :     baseline_units_.emplace_back(CreateUnit(func_index, tiers.baseline_tier));
     588      230913 :     if (tiers.baseline_tier != tiers.top_tier) {
     589       95260 :       tiering_units_.emplace_back(CreateUnit(func_index, tiers.top_tier));
     590             :     }
     591      230913 :   }
     592             : 
     593      144555 :   bool Commit() {
     594      144555 :     if (baseline_units_.empty() && tiering_units_.empty()) return false;
     595             :     compilation_state()->AddCompilationUnits(VectorOf(baseline_units_),
     596             :                                              VectorOf(tiering_units_));
     597      138169 :     Clear();
     598      138169 :     return true;
     599             :   }
     600             : 
     601      138229 :   void Clear() {
     602             :     baseline_units_.clear();
     603             :     tiering_units_.clear();
     604      138229 :   }
     605             : 
     606             :  private:
     607             :   std::unique_ptr<WasmCompilationUnit> CreateUnit(uint32_t func_index,
     608             :                                                   ExecutionTier tier) {
     609      326173 :     return base::make_unique<WasmCompilationUnit>(func_index, tier);
     610             :   }
     611             : 
     612             :   CompilationStateImpl* compilation_state() const {
     613             :     return Impl(native_module_->compilation_state());
     614             :   }
     615             : 
     616             :   NativeModule* const native_module_;
     617             :   const ExecutionTier default_tier_;
     618             :   std::vector<std::unique_ptr<WasmCompilationUnit>> baseline_units_;
     619             :   std::vector<std::unique_ptr<WasmCompilationUnit>> tiering_units_;
     620             : };
     621             : 
     622        7765 : void SetCompileError(ErrorThrower* thrower, ModuleWireBytes wire_bytes,
     623             :                      const WasmFunction* func, const WasmModule* module,
     624             :                      WasmError error) {
     625        7765 :   WasmName name = wire_bytes.GetNameOrNull(func, module);
     626        7765 :   if (name.start() == nullptr) {
     627             :     thrower->CompileError("Compiling function #%d failed: %s @+%u",
     628        7397 :                           func->func_index, error.message().c_str(),
     629        7397 :                           error.offset());
     630             :   } else {
     631             :     TruncatedUserString<> truncated_name(name);
     632             :     thrower->CompileError("Compiling function #%d:\"%.*s\" failed: %s @+%u",
     633         368 :                           func->func_index, truncated_name.length(),
     634             :                           truncated_name.start(), error.message().c_str(),
     635         368 :                           error.offset());
     636             :   }
     637        7765 : }
     638             : 
     639        8952 : DecodeResult ValidateSingleFunction(const WasmModule* module, int func_index,
     640             :                                     Vector<const uint8_t> code,
     641             :                                     Counters* counters,
     642             :                                     AccountingAllocator* allocator,
     643             :                                     WasmFeatures enabled_features) {
     644        8952 :   const WasmFunction* func = &module->functions[func_index];
     645        8952 :   FunctionBody body{func->sig, func->code.offset(), code.start(), code.end()};
     646             :   DecodeResult result;
     647             : 
     648             :   auto time_counter =
     649        8952 :       SELECT_WASM_COUNTER(counters, module->origin, wasm_decode, function_time);
     650             :   TimedHistogramScope wasm_decode_function_time_scope(time_counter);
     651        8952 :   WasmFeatures detected;
     652       17904 :   result = VerifyWasmCode(allocator, enabled_features, module, &detected, body);
     653             : 
     654        8952 :   return result;
     655             : }
     656             : 
     657             : enum class OnlyLazyFunctions : bool { kNo = false, kYes = true };
     658             : 
     659        7830 : void ValidateSequentially(
     660             :     const WasmModule* module, NativeModule* native_module, Counters* counters,
     661             :     AccountingAllocator* allocator, ErrorThrower* thrower,
     662             :     OnlyLazyFunctions only_lazy_functions = OnlyLazyFunctions::kNo) {
     663             :   DCHECK(!thrower->error());
     664        7830 :   uint32_t start = module->num_imported_functions;
     665        7830 :   uint32_t end = start + module->num_declared_functions;
     666        7830 :   auto enabled_features = native_module->enabled_features();
     667       25762 :   for (uint32_t func_index = start; func_index < end; func_index++) {
     668             :     // Skip non-lazy functions if requested.
     669        9063 :     if (only_lazy_functions == OnlyLazyFunctions::kYes &&
     670             :         !IsLazyCompilation(module, native_module, enabled_features,
     671             :                            func_index)) {
     672          78 :       continue;
     673             :     }
     674             : 
     675             :     ModuleWireBytes wire_bytes{native_module->wire_bytes()};
     676        8888 :     const WasmFunction* func = &module->functions[func_index];
     677        8888 :     Vector<const uint8_t> code = wire_bytes.GetFunctionBytes(func);
     678       17776 :     DecodeResult result = ValidateSingleFunction(
     679             :         module, func_index, code, counters, allocator, enabled_features);
     680        8888 :     if (result.failed()) {
     681       15482 :       SetCompileError(thrower, wire_bytes, func, module, result.error());
     682             :     }
     683             :   }
     684        7830 : }
     685             : 
     686             : }  // namespace
     687             : 
     688        8143 : bool CompileLazy(Isolate* isolate, NativeModule* native_module,
     689             :                  uint32_t func_index) {
     690             :   const WasmModule* module = native_module->module();
     691        8143 :   auto enabled_features = native_module->enabled_features();
     692             :   Counters* counters = isolate->counters();
     693             : 
     694             :   DCHECK(!native_module->lazy_compile_frozen());
     695             :   HistogramTimerScope lazy_time_scope(counters->wasm_lazy_compilation_time());
     696       16286 :   NativeModuleModificationScope native_module_modification_scope(native_module);
     697             : 
     698             :   base::ElapsedTimer compilation_timer;
     699             :   compilation_timer.Start();
     700             : 
     701             :   DCHECK(!native_module->HasCode(static_cast<uint32_t>(func_index)));
     702             : 
     703             :   TRACE_LAZY("Compiling wasm-function#%d.\n", func_index);
     704             : 
     705             :   CompilationStateImpl* compilation_state =
     706             :       Impl(native_module->compilation_state());
     707             :   ExecutionTierPair tiers = GetRequestedExecutionTiers(
     708        8143 :       module, compilation_state->compile_mode(), enabled_features, func_index);
     709             : 
     710       16286 :   WasmCompilationUnit baseline_unit(func_index, tiers.baseline_tier);
     711        8143 :   CompilationEnv env = native_module->CreateCompilationEnv();
     712             :   WasmCompilationResult result = baseline_unit.ExecuteCompilation(
     713        8143 :       isolate->wasm_engine(), &env, compilation_state->GetWireBytesStorage(),
     714       16286 :       counters, compilation_state->detected_features());
     715             : 
     716             :   // During lazy compilation, we can only get compilation errors when
     717             :   // {--wasm-lazy-validation} is enabled. Otherwise, the module was fully
     718             :   // verified before starting its execution.
     719             :   DCHECK_IMPLIES(result.failed(), FLAG_wasm_lazy_validation);
     720        8143 :   const WasmFunction* func = &module->functions[func_index];
     721        8143 :   if (result.failed()) {
     722          24 :     ErrorThrower thrower(isolate, nullptr);
     723             :     Vector<const uint8_t> code =
     724          48 :         compilation_state->GetWireBytesStorage()->GetCode(func->code);
     725          48 :     DecodeResult decode_result = ValidateSingleFunction(
     726             :         module, func_index, code, counters, isolate->wasm_engine()->allocator(),
     727             :         enabled_features);
     728          24 :     CHECK(decode_result.failed());
     729          48 :     SetCompileError(&thrower, ModuleWireBytes(native_module->wire_bytes()),
     730          24 :                     func, module, decode_result.error());
     731             :     return false;
     732             :   }
     733             : 
     734       16238 :   WasmCodeRefScope code_ref_scope;
     735        8119 :   WasmCode* code = native_module->AddCompiledCode(std::move(result));
     736             :   DCHECK_EQ(func_index, code->index());
     737             : 
     738        8119 :   if (WasmCode::ShouldBeLogged(isolate)) code->LogCode(isolate);
     739             : 
     740        8119 :   double func_kb = 1e-3 * func->code.length();
     741        8119 :   double compilation_seconds = compilation_timer.Elapsed().InSecondsF();
     742             : 
     743        8119 :   counters->wasm_lazily_compiled_functions()->Increment();
     744             : 
     745        8119 :   int throughput_sample = static_cast<int>(func_kb / compilation_seconds);
     746        8119 :   counters->wasm_lazy_compilation_throughput()->AddSample(throughput_sample);
     747             : 
     748        8119 :   if (tiers.baseline_tier < tiers.top_tier) {
     749             :     auto tiering_unit =
     750         138 :         base::make_unique<WasmCompilationUnit>(func_index, tiers.top_tier);
     751          69 :     compilation_state->AddTopTierCompilationUnit(std::move(tiering_unit));
     752             :   }
     753             : 
     754             :   return true;
     755             : }
     756             : 
     757             : namespace {
     758             : 
     759      216393 : void RecordStats(const Code code, Counters* counters) {
     760      432786 :   counters->wasm_generated_code_size()->Increment(code->body_size());
     761      216393 :   counters->wasm_reloc_size()->Increment(code->relocation_info()->length());
     762      216393 : }
     763             : 
     764             : constexpr int kMainThreadTaskId = -1;
     765             : 
     766             : // Run by the main thread and background tasks to take part in compilation.
     767             : // Returns whether any units were executed.
     768      485343 : bool ExecuteCompilationUnits(
     769             :     const std::shared_ptr<BackgroundCompileToken>& token, Counters* counters,
     770             :     int task_id, CompileBaselineOnly baseline_only) {
     771             :   TRACE_COMPILE("Compiling (task %d)...\n", task_id);
     772     1456178 :   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "ExecuteCompilationUnits");
     773             : 
     774      485346 :   const bool is_foreground = task_id == kMainThreadTaskId;
     775             :   // The main thread uses task id 0, which might collide with one of the
     776             :   // background tasks. This is fine, as it will only cause some contention on
     777             :   // the one queue, but work otherwise.
     778      485346 :   if (is_foreground) task_id = 0;
     779             : 
     780      485346 :   Platform* platform = V8::GetCurrentPlatform();
     781             :   // Deadline is in 50ms from now.
     782             :   static constexpr double kBackgroundCompileTimeLimit =
     783             :       50.0 / base::Time::kMillisecondsPerSecond;
     784             :   const double deadline =
     785      485298 :       platform->MonotonicallyIncreasingTime() + kBackgroundCompileTimeLimit;
     786             : 
     787             :   // These fields are initialized in a {BackgroundCompileScope} before
     788             :   // starting compilation.
     789      485354 :   base::Optional<CompilationEnv> env;
     790      485354 :   std::shared_ptr<WireBytesStorage> wire_bytes;
     791             :   std::shared_ptr<const WasmModule> module;
     792             :   WasmEngine* wasm_engine = nullptr;
     793      970873 :   std::unique_ptr<WasmCompilationUnit> unit;
     794      485354 :   WasmFeatures detected_features = kNoWasmFeatures;
     795             : 
     796             :   auto stop = [is_foreground, task_id,
     797      945546 :                &detected_features](BackgroundCompileScope& compile_scope) {
     798      472773 :     if (is_foreground) {
     799             :       compile_scope.compilation_state()->UpdateDetectedFeatures(
     800      248634 :           detected_features);
     801             :     } else {
     802             :       compile_scope.compilation_state()->OnBackgroundTaskStopped(
     803      224139 :           task_id, detected_features);
     804             :     }
     805      472823 :   };
     806             : 
     807             :   // Preparation (synchronized): Initialize the fields above and get the first
     808             :   // compilation unit.
     809             :   {
     810      674062 :     BackgroundCompileScope compile_scope(token);
     811      782261 :     if (compile_scope.cancelled()) return false;
     812      948764 :     env.emplace(compile_scope.native_module()->CreateCompilationEnv());
     813      474485 :     wire_bytes = compile_scope.compilation_state()->GetWireBytesStorage();
     814      474488 :     module = compile_scope.native_module()->shared_module();
     815             :     wasm_engine = compile_scope.native_module()->engine();
     816      474355 :     unit = compile_scope.compilation_state()->GetNextCompilationUnit(
     817             :         task_id, baseline_only);
     818      474481 :     if (unit == nullptr) {
     819      285773 :       stop(compile_scope);
     820      285774 :       return false;
     821             :     }
     822             :   }
     823             : 
     824      188698 :   std::vector<WasmCompilationResult> results_to_publish;
     825             : 
     826             :   auto publish_results = [&results_to_publish](
     827      927010 :                              BackgroundCompileScope* compile_scope) {
     828      535712 :     if (results_to_publish.empty()) return;
     829      527538 :     WasmCodeRefScope code_ref_scope;
     830             :     std::vector<WasmCode*> code_vector =
     831             :         compile_scope->native_module()->AddCompiledCode(
     832      263407 :             VectorOf(results_to_publish));
     833      263928 :     compile_scope->compilation_state()->OnFinishedUnits(VectorOf(code_vector));
     834             :     results_to_publish.clear();
     835      188688 :   };
     836             : 
     837             :   bool compilation_failed = false;
     838             :   while (true) {
     839             :     // (asynchronous): Execute the compilation.
     840             :     WasmCompilationResult result = unit->ExecuteCompilation(
     841      459281 :         wasm_engine, &env.value(), wire_bytes, counters, &detected_features);
     842      319940 :     results_to_publish.emplace_back(std::move(result));
     843             : 
     844             :     // (synchronized): Publish the compilation result and get the next unit.
     845             :     {
     846      454992 :       BackgroundCompileScope compile_scope(token);
     847      504848 :       if (compile_scope.cancelled()) return true;
     848      322481 :       if (!results_to_publish.back().succeeded()) {
     849             :         // Compile error.
     850        7914 :         compile_scope.compilation_state()->SetError();
     851        7902 :         stop(compile_scope);
     852             :         compilation_failed = true;
     853        7918 :         break;
     854             :       }
     855             :       // Publish TurboFan units immediately to reduce peak memory consumption.
     856      314567 :       if (result.requested_tier == ExecutionTier::kTurbofan) {
     857      220689 :         publish_results(&compile_scope);
     858             :       }
     859             : 
     860             :       // Get next unit.
     861      314698 :       if (deadline < platform->MonotonicallyIncreasingTime()) {
     862             :         unit = nullptr;
     863             :       } else {
     864      314564 :         unit = compile_scope.compilation_state()->GetNextCompilationUnit(
     865             :             task_id, baseline_only);
     866             :       }
     867             : 
     868      314485 :       if (unit == nullptr) {
     869      179124 :         publish_results(&compile_scope);
     870      179120 :         stop(compile_scope);
     871      179125 :         return true;
     872             :       }
     873             :     }
     874             :   }
     875             :   // We only get here if compilation failed. Other exits return directly.
     876             :   DCHECK(compilation_failed);
     877             :   USE(compilation_failed);
     878        7913 :   token->Cancel();
     879        7914 :   return true;
     880             : }
     881             : 
     882      144223 : void InitializeCompilationUnits(NativeModule* native_module) {
     883             :   // Set number of functions that must be compiled to consider the module fully
     884             :   // compiled.
     885             :   auto wasm_module = native_module->module();
     886      144223 :   int num_functions = wasm_module->num_declared_functions;
     887             :   DCHECK_IMPLIES(!native_module->enabled_features().compilation_hints,
     888             :                  wasm_module->num_lazy_compilation_hints == 0);
     889      144223 :   int num_lazy_functions = wasm_module->num_lazy_compilation_hints;
     890             :   CompilationStateImpl* compilation_state =
     891             :       Impl(native_module->compilation_state());
     892             :   compilation_state->SetNumberOfFunctionsToCompile(num_functions,
     893      144223 :                                                    num_lazy_functions);
     894             : 
     895             :   ModuleWireBytes wire_bytes(native_module->wire_bytes());
     896             :   const WasmModule* module = native_module->module();
     897             :   CompilationUnitBuilder builder(native_module);
     898      144221 :   uint32_t start = module->num_imported_functions;
     899      144221 :   uint32_t end = start + module->num_declared_functions;
     900      605149 :   for (uint32_t func_index = start; func_index < end; func_index++) {
     901      230461 :     if (IsLazyCompilation(module, native_module,
     902             :                           native_module->enabled_features(), func_index)) {
     903          35 :       native_module->UseLazyStub(func_index);
     904             :     } else {
     905      230426 :       builder.AddUnits(func_index);
     906             :     }
     907             :   }
     908      144224 :   builder.Commit();
     909      144224 : }
     910             : 
     911             : bool NeedsDeterministicCompile() {
     912     1384420 :   return FLAG_trace_wasm_decoder || FLAG_wasm_num_compilation_tasks <= 1;
     913             : }
     914             : 
     915      144707 : void CompileNativeModule(Isolate* isolate, ErrorThrower* thrower,
     916             :                          const WasmModule* wasm_module,
     917             :                          NativeModule* native_module) {
     918             :   ModuleWireBytes wire_bytes(native_module->wire_bytes());
     919             : 
     920      144707 :   if (FLAG_wasm_lazy_compilation ||
     921      143403 :       (FLAG_asm_wasm_lazy_compilation && wasm_module->origin == kAsmJsOrigin)) {
     922        2637 :     if (wasm_module->origin == kWasmOrigin && !FLAG_wasm_lazy_validation) {
     923             :       // Validate wasm modules for lazy compilation if requested. Never validate
     924             :       // asm.js modules as these are valid by construction (otherwise a CHECK
     925             :       // will fail during lazy compilation).
     926             :       ValidateSequentially(wasm_module, native_module, isolate->counters(),
     927         176 :                            isolate->allocator(), thrower);
     928             :       // On error: Return and leave the module in an unexecutable state.
     929        2820 :       if (thrower->error()) return;
     930             :     }
     931             :     native_module->set_lazy_compilation(true);
     932        2629 :     native_module->UseLazyStubs();
     933        2628 :     return;
     934             :   }
     935             : 
     936      142151 :   if (native_module->enabled_features().compilation_hints &&
     937          81 :       !FLAG_wasm_lazy_validation) {
     938             :     ValidateSequentially(wasm_module, native_module, isolate->counters(),
     939             :                          isolate->allocator(), thrower,
     940          73 :                          OnlyLazyFunctions::kYes);
     941             :     // On error: Return and leave the module in an unexecutable state.
     942          73 :     if (thrower->error()) return;
     943             :   }
     944             : 
     945             :   // Turn on the {CanonicalHandleScope} so that the background threads can
     946             :   // use the node cache.
     947      284125 :   CanonicalHandleScope canonical(isolate);
     948             : 
     949             :   auto* compilation_state = Impl(native_module->compilation_state());
     950             :   DCHECK_GE(kMaxInt, native_module->module()->num_declared_functions);
     951             : 
     952             :   // Install a callback to notify us once background compilation finished, or
     953             :   // compilation failed.
     954      284125 :   auto baseline_finished_semaphore = std::make_shared<base::Semaphore>(0);
     955             :   // The callback captures a shared ptr to the semaphore.
     956      284126 :   compilation_state->AddCallback(
     957      284126 :       [baseline_finished_semaphore](CompilationEvent event) {
     958      553080 :         if (event == CompilationEvent::kFinishedBaselineCompilation ||
     959      276540 :             event == CompilationEvent::kFailedCompilation) {
     960      142063 :           baseline_finished_semaphore->Signal();
     961             :         }
     962      142063 :       });
     963             : 
     964             :   // Initialize the compilation units and kick off background compile tasks.
     965      142063 :   InitializeCompilationUnits(native_module);
     966             : 
     967             :   // If tiering is disabled, the main thread can execute any unit (all of them
     968             :   // are part of initial compilation). Otherwise, just execute baseline units.
     969             :   bool is_tiering = compilation_state->compile_mode() == CompileMode::kTiering;
     970      142063 :   auto baseline_only = is_tiering ? kBaselineOnly : kBaselineOrTopTier;
     971             :   // The main threads contributes to the compilation, except if we need
     972             :   // deterministic compilation; in that case, the single background task will
     973             :   // execute all compilation.
     974      142063 :   if (!NeedsDeterministicCompile()) {
     975      513418 :     while (ExecuteCompilationUnits(
     976             :         compilation_state->background_compile_token(), isolate->counters(),
     977             :         kMainThreadTaskId, baseline_only)) {
     978             :       // Continue executing compilation units.
     979             :     }
     980             :   }
     981             : 
     982             :   // Now wait until baseline compilation finished.
     983      142063 :   baseline_finished_semaphore->Wait();
     984             : 
     985      142063 :   compilation_state->PublishDetectedFeatures(isolate);
     986             : 
     987      142063 :   if (compilation_state->failed()) {
     988             :     ValidateSequentially(wasm_module, native_module, isolate->counters(),
     989        7505 :                          isolate->allocator(), thrower);
     990        7505 :     CHECK(thrower->error());
     991             :   }
     992             : }
     993             : 
     994             : // The runnable task that performs compilations in the background.
     995      686548 : class BackgroundCompileTask : public CancelableTask {
     996             :  public:
     997             :   explicit BackgroundCompileTask(CancelableTaskManager* manager,
     998             :                                  std::shared_ptr<BackgroundCompileToken> token,
     999             :                                  std::shared_ptr<Counters> async_counters,
    1000             :                                  int task_id)
    1001             :       : CancelableTask(manager),
    1002             :         token_(std::move(token)),
    1003             :         async_counters_(std::move(async_counters)),
    1004      457800 :         task_id_(task_id) {}
    1005             : 
    1006      228793 :   void RunInternal() override {
    1007      228793 :     ExecuteCompilationUnits(token_, async_counters_.get(), task_id_,
    1008      228793 :                             kBaselineOrTopTier);
    1009      228814 :   }
    1010             : 
    1011             :  private:
    1012             :   const std::shared_ptr<BackgroundCompileToken> token_;
    1013             :   const std::shared_ptr<Counters> async_counters_;
    1014             :   const int task_id_;
    1015             : };
    1016             : 
    1017             : }  // namespace
    1018             : 
    1019      144698 : std::shared_ptr<NativeModule> CompileToNativeModule(
    1020             :     Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
    1021             :     std::shared_ptr<const WasmModule> module, const ModuleWireBytes& wire_bytes,
    1022             :     Handle<FixedArray>* export_wrappers_out) {
    1023             :   const WasmModule* wasm_module = module.get();
    1024      144698 :   TimedHistogramScope wasm_compile_module_time_scope(SELECT_WASM_COUNTER(
    1025      144698 :       isolate->counters(), wasm_module->origin, wasm_compile, module_time));
    1026             : 
    1027             :   // Embedder usage count for declared shared memories.
    1028      144704 :   if (wasm_module->has_shared_memory) {
    1029        1437 :     isolate->CountUsage(v8::Isolate::UseCounterFeature::kWasmSharedMemory);
    1030             :   }
    1031      144704 :   int export_wrapper_size = static_cast<int>(module->num_exported_functions);
    1032             : 
    1033             :   // TODO(wasm): only save the sections necessary to deserialize a
    1034             :   // {WasmModule}. E.g. function bodies could be omitted.
    1035             :   OwnedVector<uint8_t> wire_bytes_copy =
    1036      144704 :       OwnedVector<uint8_t>::Of(wire_bytes.module_bytes());
    1037             : 
    1038             :   // Create and compile the native module.
    1039             :   size_t code_size_estimate =
    1040      144707 :       wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module.get());
    1041             : 
    1042             :   // Create a new {NativeModule} first.
    1043             :   auto native_module = isolate->wasm_engine()->NewNativeModule(
    1044             :       isolate, enabled, code_size_estimate,
    1045      289412 :       wasm::NativeModule::kCanAllocateMoreMemory, std::move(module));
    1046      289414 :   native_module->SetWireBytes(std::move(wire_bytes_copy));
    1047      144707 :   native_module->SetRuntimeStubs(isolate);
    1048             : 
    1049      144706 :   CompileNativeModule(isolate, thrower, wasm_module, native_module.get());
    1050      144707 :   if (thrower->error()) return {};
    1051             : 
    1052             :   // Compile JS->wasm wrappers for exported functions.
    1053             :   *export_wrappers_out = isolate->factory()->NewFixedArray(
    1054      137186 :       export_wrapper_size, AllocationType::kOld);
    1055             :   CompileJsToWasmWrappers(isolate, native_module->module(),
    1056      137183 :                           *export_wrappers_out);
    1057             : 
    1058             :   // Log the code within the generated module for profiling.
    1059      137185 :   native_module->LogWasmCodes(isolate);
    1060             : 
    1061             :   return native_module;
    1062             : }
    1063             : 
    1064        2663 : AsyncCompileJob::AsyncCompileJob(
    1065             :     Isolate* isolate, const WasmFeatures& enabled,
    1066             :     std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
    1067             :     std::shared_ptr<CompilationResultResolver> resolver)
    1068             :     : isolate_(isolate),
    1069             :       enabled_features_(enabled),
    1070             :       bytes_copy_(std::move(bytes_copy)),
    1071             :       wire_bytes_(bytes_copy_.get(), bytes_copy_.get() + length),
    1072       10652 :       resolver_(std::move(resolver)) {
    1073             :   v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
    1074        2663 :   v8::Platform* platform = V8::GetCurrentPlatform();
    1075        5326 :   foreground_task_runner_ = platform->GetForegroundTaskRunner(v8_isolate);
    1076             :   native_context_ =
    1077        2663 :       isolate->global_handles()->Create(context->native_context());
    1078             :   DCHECK(native_context_->IsNativeContext());
    1079        2663 : }
    1080             : 
    1081        2203 : void AsyncCompileJob::Start() {
    1082        4406 :   DoAsync<DecodeModule>(isolate_->counters());  // --
    1083        2203 : }
    1084             : 
    1085          72 : void AsyncCompileJob::Abort() {
    1086             :   // Removing this job will trigger the destructor, which will cancel all
    1087             :   // compilation.
    1088          72 :   isolate_->wasm_engine()->RemoveCompileJob(this);
    1089          72 : }
    1090             : 
    1091        1840 : class AsyncStreamingProcessor final : public StreamingProcessor {
    1092             :  public:
    1093             :   explicit AsyncStreamingProcessor(AsyncCompileJob* job);
    1094             : 
    1095             :   bool ProcessModuleHeader(Vector<const uint8_t> bytes,
    1096             :                            uint32_t offset) override;
    1097             : 
    1098             :   bool ProcessSection(SectionCode section_code, Vector<const uint8_t> bytes,
    1099             :                       uint32_t offset) override;
    1100             : 
    1101             :   bool ProcessCodeSectionHeader(int functions_count, uint32_t offset,
    1102             :                                 std::shared_ptr<WireBytesStorage>) override;
    1103             : 
    1104             :   bool ProcessFunctionBody(Vector<const uint8_t> bytes,
    1105             :                            uint32_t offset) override;
    1106             : 
    1107             :   void OnFinishedChunk() override;
    1108             : 
    1109             :   void OnFinishedStream(OwnedVector<uint8_t> bytes) override;
    1110             : 
    1111             :   void OnError(const WasmError&) override;
    1112             : 
    1113             :   void OnAbort() override;
    1114             : 
    1115             :   bool Deserialize(Vector<const uint8_t> wire_bytes,
    1116             :                    Vector<const uint8_t> module_bytes) override;
    1117             : 
    1118             :  private:
    1119             :   // Finishes the AsyncCompileJob with an error.
    1120             :   void FinishAsyncCompileJobWithError(const WasmError&);
    1121             : 
    1122             :   void CommitCompilationUnits();
    1123             : 
    1124             :   ModuleDecoder decoder_;
    1125             :   AsyncCompileJob* job_;
    1126             :   std::unique_ptr<CompilationUnitBuilder> compilation_unit_builder_;
    1127             :   int num_functions_ = 0;
    1128             : };
    1129             : 
    1130         460 : std::shared_ptr<StreamingDecoder> AsyncCompileJob::CreateStreamingDecoder() {
    1131             :   DCHECK_NULL(stream_);
    1132        1380 :   stream_.reset(
    1133        1840 :       new StreamingDecoder(base::make_unique<AsyncStreamingProcessor>(this)));
    1134         460 :   return stream_;
    1135             : }
    1136             : 
    1137        7989 : AsyncCompileJob::~AsyncCompileJob() {
    1138             :   // Note: This destructor always runs on the foreground thread of the isolate.
    1139        2663 :   background_task_manager_.CancelAndWait();
    1140             :   // If the runtime objects were not created yet, then initial compilation did
    1141             :   // not finish yet. In this case we can abort compilation.
    1142        5120 :   if (native_module_ && module_object_.is_null()) {
    1143         173 :     Impl(native_module_->compilation_state())->AbortCompilation();
    1144             :   }
    1145             :   // Tell the streaming decoder that the AsyncCompileJob is not available
    1146             :   // anymore.
    1147             :   // TODO(ahaas): Is this notification really necessary? Check
    1148             :   // https://crbug.com/888170.
    1149        2663 :   if (stream_) stream_->NotifyCompilationEnded();
    1150             :   CancelPendingForegroundTask();
    1151        2663 :   isolate_->global_handles()->Destroy(native_context_.location());
    1152        2663 :   if (!module_object_.is_null()) {
    1153        2284 :     isolate_->global_handles()->Destroy(module_object_.location());
    1154             :   }
    1155        2663 : }
    1156             : 
    1157        2453 : void AsyncCompileJob::CreateNativeModule(
    1158             :     std::shared_ptr<const WasmModule> module) {
    1159             :   // Embedder usage count for declared shared memories.
    1160        2453 :   if (module->has_shared_memory) {
    1161           0 :     isolate_->CountUsage(v8::Isolate::UseCounterFeature::kWasmSharedMemory);
    1162             :   }
    1163             : 
    1164             :   // TODO(wasm): Improve efficiency of storing module wire bytes. Only store
    1165             :   // relevant sections, not function bodies
    1166             : 
    1167             :   // Create the module object and populate with compiled functions and
    1168             :   // information needed at instantiation time.
    1169             :   // TODO(clemensh): For the same module (same bytes / same hash), we should
    1170             :   // only have one {WasmModuleObject}. Otherwise, we might only set
    1171             :   // breakpoints on a (potentially empty) subset of the instances.
    1172             :   // Create the module object.
    1173             : 
    1174             :   size_t code_size_estimate =
    1175        2453 :       wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module.get());
    1176        7359 :   native_module_ = isolate_->wasm_engine()->NewNativeModule(
    1177             :       isolate_, enabled_features_, code_size_estimate,
    1178             :       wasm::NativeModule::kCanAllocateMoreMemory, std::move(module));
    1179        4906 :   native_module_->SetWireBytes({std::move(bytes_copy_), wire_bytes_.length()});
    1180        2453 :   native_module_->SetRuntimeStubs(isolate_);
    1181             : 
    1182        2453 :   if (stream_) stream_->NotifyNativeModuleCreated(native_module_);
    1183        2453 : }
    1184             : 
    1185        2280 : void AsyncCompileJob::PrepareRuntimeObjects() {
    1186             :   // Create heap objects for script and module bytes to be stored in the
    1187             :   // module object. Asm.js is not compiled asynchronously.
    1188             :   const WasmModule* module = native_module_->module();
    1189             :   Handle<Script> script =
    1190        2280 :       CreateWasmScript(isolate_, wire_bytes_, module->source_map_url);
    1191             : 
    1192             :   size_t code_size_estimate =
    1193        2280 :       wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module);
    1194             :   Handle<WasmModuleObject> module_object = WasmModuleObject::New(
    1195        4560 :       isolate_, native_module_, script, code_size_estimate);
    1196             : 
    1197        4560 :   module_object_ = isolate_->global_handles()->Create(*module_object);
    1198        2280 : }
    1199             : 
    1200             : // This function assumes that it is executed in a HandleScope, and that a
    1201             : // context is set on the isolate.
    1202        2284 : void AsyncCompileJob::FinishCompile() {
    1203             :   bool is_after_deserialization = !module_object_.is_null();
    1204        2284 :   if (!is_after_deserialization) {
    1205        2280 :     PrepareRuntimeObjects();
    1206             :   }
    1207             :   DCHECK(!isolate_->context().is_null());
    1208             :   // Finish the wasm script now and make it public to the debugger.
    1209        2284 :   Handle<Script> script(module_object_->script(), isolate_);
    1210        4568 :   if (script->type() == Script::TYPE_WASM &&
    1211             :       module_object_->module()->source_map_url.size() != 0) {
    1212           0 :     MaybeHandle<String> src_map_str = isolate_->factory()->NewStringFromUtf8(
    1213             :         CStrVector(module_object_->module()->source_map_url.c_str()),
    1214           0 :         AllocationType::kOld);
    1215           0 :     script->set_source_mapping_url(*src_map_str.ToHandleChecked());
    1216             :   }
    1217        2284 :   isolate_->debug()->OnAfterCompile(script);
    1218             : 
    1219             :   // We can only update the feature counts once the entire compile is done.
    1220             :   auto compilation_state =
    1221             :       Impl(module_object_->native_module()->compilation_state());
    1222        2284 :   compilation_state->PublishDetectedFeatures(isolate_);
    1223             : 
    1224             :   // TODO(bbudge) Allow deserialization without wrapper compilation, so we can
    1225             :   // just compile wrappers here.
    1226        2284 :   if (!is_after_deserialization) {
    1227             :     // TODO(wasm): compiling wrappers should be made async.
    1228        2280 :     CompileWrappers();
    1229             :   }
    1230        2284 :   FinishModule();
    1231        2284 : }
    1232             : 
    1233         221 : void AsyncCompileJob::DecodeFailed(const WasmError& error) {
    1234         442 :   ErrorThrower thrower(isolate_, "WebAssembly.compile()");
    1235             :   thrower.CompileFailed(error);
    1236             :   // {job} keeps the {this} pointer alive.
    1237             :   std::shared_ptr<AsyncCompileJob> job =
    1238         442 :       isolate_->wasm_engine()->RemoveCompileJob(this);
    1239         221 :   resolver_->OnCompilationFailed(thrower.Reify());
    1240         221 : }
    1241             : 
    1242          76 : void AsyncCompileJob::AsyncCompileFailed() {
    1243         152 :   ErrorThrower thrower(isolate_, "WebAssembly.compile()");
    1244             :   ValidateSequentially(native_module_->module(), native_module_.get(),
    1245          76 :                        isolate_->counters(), isolate_->allocator(), &thrower);
    1246             :   DCHECK(thrower.error());
    1247             :   // {job} keeps the {this} pointer alive.
    1248             :   std::shared_ptr<AsyncCompileJob> job =
    1249         152 :       isolate_->wasm_engine()->RemoveCompileJob(this);
    1250          76 :   resolver_->OnCompilationFailed(thrower.Reify());
    1251          76 : }
    1252             : 
    1253           0 : void AsyncCompileJob::AsyncCompileSucceeded(Handle<WasmModuleObject> result) {
    1254        2284 :   resolver_->OnCompilationSucceeded(result);
    1255           0 : }
    1256             : 
    1257             : class AsyncCompileJob::CompilationStateCallback {
    1258             :  public:
    1259             :   explicit CompilationStateCallback(AsyncCompileJob* job) : job_(job) {}
    1260             : 
    1261        4590 :   void operator()(CompilationEvent event) {
    1262             :     // This callback is only being called from a foreground task.
    1263        4590 :     switch (event) {
    1264             :       case CompilationEvent::kFinishedBaselineCompilation:
    1265             :         DCHECK(!last_event_.has_value());
    1266        4512 :         if (job_->DecrementAndCheckFinisherCount()) {
    1267        2212 :           job_->DoSync<CompileFinished>();
    1268             :         }
    1269             :         break;
    1270             :       case CompilationEvent::kFinishedTopTierCompilation:
    1271             :         DCHECK_EQ(CompilationEvent::kFinishedBaselineCompilation, last_event_);
    1272             :         // At this point, the job will already be gone, thus do not access it
    1273             :         // here.
    1274             :         break;
    1275             :       case CompilationEvent::kFailedCompilation: {
    1276             :         DCHECK(!last_event_.has_value());
    1277         160 :         if (job_->DecrementAndCheckFinisherCount()) {
    1278          59 :           job_->DoSync<CompileFailed>();
    1279             :         }
    1280             :         break;
    1281             :       }
    1282             :       default:
    1283           0 :         UNREACHABLE();
    1284             :     }
    1285             : #ifdef DEBUG
    1286             :     last_event_ = event;
    1287             : #endif
    1288        4590 :   }
    1289             : 
    1290             :  private:
    1291             :   AsyncCompileJob* job_;
    1292             : #ifdef DEBUG
    1293             :   // This will be modified by different threads, but they externally
    1294             :   // synchronize, so no explicit synchronization (currently) needed here.
    1295             :   base::Optional<CompilationEvent> last_event_;
    1296             : #endif
    1297             : };
    1298             : 
    1299             : // A closure to run a compilation step (either as foreground or background
    1300             : // task) and schedule the next step(s), if any.
    1301        7121 : class AsyncCompileJob::CompileStep {
    1302             :  public:
    1303        7120 :   virtual ~CompileStep() = default;
    1304             : 
    1305        7120 :   void Run(AsyncCompileJob* job, bool on_foreground) {
    1306        7120 :     if (on_foreground) {
    1307        4917 :       HandleScope scope(job->isolate_);
    1308        4917 :       SaveAndSwitchContext saved_context(job->isolate_, *job->native_context_);
    1309        4917 :       RunInForeground(job);
    1310             :     } else {
    1311        2203 :       RunInBackground(job);
    1312             :     }
    1313        7120 :   }
    1314             : 
    1315           0 :   virtual void RunInForeground(AsyncCompileJob*) { UNREACHABLE(); }
    1316           0 :   virtual void RunInBackground(AsyncCompileJob*) { UNREACHABLE(); }
    1317             : };
    1318             : 
    1319             : class AsyncCompileJob::CompileTask : public CancelableTask {
    1320             :  public:
    1321             :   CompileTask(AsyncCompileJob* job, bool on_foreground)
    1322             :       // We only manage the background tasks with the {CancelableTaskManager} of
    1323             :       // the {AsyncCompileJob}. Foreground tasks are managed by the system's
    1324             :       // {CancelableTaskManager}. Background tasks cannot spawn tasks managed by
    1325             :       // their own task manager.
    1326        4918 :       : CancelableTask(on_foreground ? job->isolate_->cancelable_task_manager()
    1327             :                                      : &job->background_task_manager_),
    1328             :         job_(job),
    1329        9324 :         on_foreground_(on_foreground) {}
    1330             : 
    1331       21363 :   ~CompileTask() override {
    1332        7121 :     if (job_ != nullptr && on_foreground_) ResetPendingForegroundTask();
    1333       14242 :   }
    1334             : 
    1335        7120 :   void RunInternal() final {
    1336        7120 :     if (!job_) return;
    1337        7120 :     if (on_foreground_) ResetPendingForegroundTask();
    1338       14240 :     job_->step_->Run(job_, on_foreground_);
    1339             :     // After execution, reset {job_} such that we don't try to reset the pending
    1340             :     // foreground task when the task is deleted.
    1341        7120 :     job_ = nullptr;
    1342             :   }
    1343             : 
    1344             :   void Cancel() {
    1345             :     DCHECK_NOT_NULL(job_);
    1346           1 :     job_ = nullptr;
    1347             :   }
    1348             : 
    1349             :  private:
    1350             :   // {job_} will be cleared to cancel a pending task.
    1351             :   AsyncCompileJob* job_;
    1352             :   bool on_foreground_;
    1353             : 
    1354             :   void ResetPendingForegroundTask() const {
    1355             :     DCHECK_EQ(this, job_->pending_foreground_task_);
    1356        4917 :     job_->pending_foreground_task_ = nullptr;
    1357             :   }
    1358             : };
    1359             : 
    1360        4652 : void AsyncCompileJob::StartForegroundTask() {
    1361             :   DCHECK_NULL(pending_foreground_task_);
    1362             : 
    1363        4652 :   auto new_task = base::make_unique<CompileTask>(this, true);
    1364        4654 :   pending_foreground_task_ = new_task.get();
    1365       13962 :   foreground_task_runner_->PostTask(std::move(new_task));
    1366        4654 : }
    1367             : 
    1368         264 : void AsyncCompileJob::ExecuteForegroundTaskImmediately() {
    1369             :   DCHECK_NULL(pending_foreground_task_);
    1370             : 
    1371         264 :   auto new_task = base::make_unique<CompileTask>(this, true);
    1372         264 :   pending_foreground_task_ = new_task.get();
    1373         264 :   new_task->Run();
    1374         264 : }
    1375             : 
    1376           0 : void AsyncCompileJob::CancelPendingForegroundTask() {
    1377        2663 :   if (!pending_foreground_task_) return;
    1378             :   pending_foreground_task_->Cancel();
    1379           1 :   pending_foreground_task_ = nullptr;
    1380             : }
    1381             : 
    1382        2203 : void AsyncCompileJob::StartBackgroundTask() {
    1383        2203 :   auto task = base::make_unique<CompileTask>(this, false);
    1384             : 
    1385             :   // If --wasm-num-compilation-tasks=0 is passed, do only spawn foreground
    1386             :   // tasks. This is used to make timing deterministic.
    1387        2203 :   if (FLAG_wasm_num_compilation_tasks > 0) {
    1388        6609 :     V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
    1389             :   } else {
    1390           0 :     foreground_task_runner_->PostTask(std::move(task));
    1391             :   }
    1392        2203 : }
    1393             : 
    1394             : template <typename Step,
    1395             :           AsyncCompileJob::UseExistingForegroundTask use_existing_fg_task,
    1396             :           typename... Args>
    1397          64 : void AsyncCompileJob::DoSync(Args&&... args) {
    1398        4653 :   NextStep<Step>(std::forward<Args>(args)...);
    1399          64 :   if (use_existing_fg_task && pending_foreground_task_ != nullptr) return;
    1400        4652 :   StartForegroundTask();
    1401             : }
    1402             : 
    1403             : template <typename Step, typename... Args>
    1404             : void AsyncCompileJob::DoImmediately(Args&&... args) {
    1405         264 :   NextStep<Step>(std::forward<Args>(args)...);
    1406         264 :   ExecuteForegroundTaskImmediately();
    1407             : }
    1408             : 
    1409             : template <typename Step, typename... Args>
    1410             : void AsyncCompileJob::DoAsync(Args&&... args) {
    1411        2203 :   NextStep<Step>(std::forward<Args>(args)...);
    1412        2203 :   StartBackgroundTask();
    1413             : }
    1414             : 
    1415             : template <typename Step, typename... Args>
    1416        7120 : void AsyncCompileJob::NextStep(Args&&... args) {
    1417        7605 :   step_.reset(new Step(std::forward<Args>(args)...));
    1418        7119 : }
    1419             : 
    1420             : //==========================================================================
    1421             : // Step 1: (async) Decode the module.
    1422             : //==========================================================================
    1423        4404 : class AsyncCompileJob::DecodeModule : public AsyncCompileJob::CompileStep {
    1424             :  public:
    1425        2203 :   explicit DecodeModule(Counters* counters) : counters_(counters) {}
    1426             : 
    1427        2203 :   void RunInBackground(AsyncCompileJob* job) override {
    1428        2203 :     ModuleResult result;
    1429             :     {
    1430             :       DisallowHandleAllocation no_handle;
    1431             :       DisallowHeapAllocation no_allocation;
    1432             :       // Decode the module bytes.
    1433             :       TRACE_COMPILE("(1) Decoding module...\n");
    1434        6608 :       TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
    1435             :                    "AsyncCompileJob::DecodeModule");
    1436        4405 :       result = DecodeWasmModule(
    1437             :           job->enabled_features_, job->wire_bytes_.start(),
    1438        2203 :           job->wire_bytes_.end(), false, kWasmOrigin, counters_,
    1439        2202 :           job->isolate()->wasm_engine()->allocator());
    1440             : 
    1441             :       // Validate lazy functions here if requested.
    1442        2202 :       auto enabled_features = job->enabled_features_;
    1443        2202 :       if (enabled_features.compilation_hints && !FLAG_wasm_lazy_validation &&
    1444             :           result.ok()) {
    1445             :         const WasmModule* module = result.value().get();
    1446             :         auto allocator = job->isolate()->wasm_engine()->allocator();
    1447          24 :         int start = module->num_imported_functions;
    1448          24 :         int end = start + module->num_declared_functions;
    1449             : 
    1450          40 :         for (int func_index = start; func_index < end; func_index++) {
    1451          16 :           const WasmFunction* func = &module->functions[func_index];
    1452          16 :           Vector<const uint8_t> code = job->wire_bytes_.GetFunctionBytes(func);
    1453             : 
    1454          32 :           if (IsLazyCompilation(module, enabled_features, func_index)) {
    1455             :             DecodeResult function_result =
    1456          32 :                 ValidateSingleFunction(module, func_index, code, counters_,
    1457             :                                        allocator, enabled_features);
    1458          16 :             if (function_result.failed()) {
    1459          16 :               result = ModuleResult(function_result.error());
    1460             :               break;
    1461             :             }
    1462             :           }
    1463             :         }
    1464             :       }
    1465             :     }
    1466        2202 :     if (result.failed()) {
    1467             :       // Decoding failure; reject the promise and clean up.
    1468             :       job->DoSync<DecodeFail>(std::move(result).error());
    1469             :     } else {
    1470             :       // Decode passed.
    1471        4323 :       job->DoSync<PrepareAndStartCompile>(std::move(result).value(), true);
    1472             :     }
    1473        2203 :   }
    1474             : 
    1475             :  private:
    1476             :   Counters* const counters_;
    1477             : };
    1478             : 
    1479             : //==========================================================================
    1480             : // Step 1b: (sync) Fail decoding the module.
    1481             : //==========================================================================
    1482         442 : class AsyncCompileJob::DecodeFail : public CompileStep {
    1483             :  public:
    1484         221 :   explicit DecodeFail(WasmError error) : error_(std::move(error)) {}
    1485             : 
    1486             :  private:
    1487             :   WasmError error_;
    1488             : 
    1489         221 :   void RunInForeground(AsyncCompileJob* job) override {
    1490             :     TRACE_COMPILE("(1b) Decoding failed.\n");
    1491             :     // {job_} is deleted in DecodeFailed, therefore the {return}.
    1492         221 :     return job->DecodeFailed(error_);
    1493             :   }
    1494             : };
    1495             : 
    1496             : //==========================================================================
    1497             : // Step 2 (sync): Create heap-allocated data and start compile.
    1498             : //==========================================================================
    1499        4851 : class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
    1500             :  public:
    1501             :   PrepareAndStartCompile(std::shared_ptr<const WasmModule> module,
    1502             :                          bool start_compilation)
    1503        4852 :       : module_(std::move(module)), start_compilation_(start_compilation) {}
    1504             : 
    1505             :  private:
    1506             :   std::shared_ptr<const WasmModule> module_;
    1507             :   bool start_compilation_;
    1508             : 
    1509        2425 :   void RunInForeground(AsyncCompileJob* job) override {
    1510             :     TRACE_COMPILE("(2) Prepare and start compile...\n");
    1511             : 
    1512             :     // Make sure all compilation tasks stopped running. Decoding (async step)
    1513             :     // is done.
    1514        2425 :     job->background_task_manager_.CancelAndWait();
    1515             : 
    1516        4850 :     job->CreateNativeModule(module_);
    1517             : 
    1518             :     CompilationStateImpl* compilation_state =
    1519             :         Impl(job->native_module_->compilation_state());
    1520        4850 :     compilation_state->AddCallback(CompilationStateCallback{job});
    1521        2425 :     if (start_compilation_) {
    1522             :       // TODO(ahaas): Try to remove the {start_compilation_} check when
    1523             :       // streaming decoding is done in the background. If
    1524             :       // InitializeCompilationUnits always returns 0 for streaming compilation,
    1525             :       // then DoAsync would do the same as NextStep already.
    1526             : 
    1527             :       // Add compilation units and kick off compilation.
    1528        2161 :       InitializeCompilationUnits(job->native_module_.get());
    1529             :     }
    1530        2425 :   }
    1531             : };
    1532             : 
    1533             : //==========================================================================
    1534             : // Step 3a (sync): Compilation failed.
    1535             : //==========================================================================
    1536         177 : class AsyncCompileJob::CompileFailed : public CompileStep {
    1537             :  private:
    1538          59 :   void RunInForeground(AsyncCompileJob* job) override {
    1539             :     TRACE_COMPILE("(3a) Compilation failed\n");
    1540             :     DCHECK(job->native_module_->compilation_state()->failed());
    1541             : 
    1542             :     // {job_} is deleted in AsyncCompileFailed, therefore the {return}.
    1543          59 :     return job->AsyncCompileFailed();
    1544             :   }
    1545             : };
    1546             : 
    1547             : namespace {
    1548        2212 : class SampleTopTierCodeSizeCallback {
    1549             :  public:
    1550             :   explicit SampleTopTierCodeSizeCallback(
    1551             :       std::weak_ptr<NativeModule> native_module)
    1552             :       : native_module_(std::move(native_module)) {}
    1553             : 
    1554         176 :   void operator()(CompilationEvent event) {
    1555             :     // This callback is registered after baseline compilation finished, so the
    1556             :     // only possible event to follow is {kFinishedTopTierCompilation}.
    1557             :     DCHECK_EQ(CompilationEvent::kFinishedTopTierCompilation, event);
    1558         176 :     if (std::shared_ptr<NativeModule> native_module = native_module_.lock()) {
    1559             :       native_module->engine()->SampleTopTierCodeSizeInAllIsolates(
    1560         176 :           native_module);
    1561             :     }
    1562         176 :   }
    1563             : 
    1564             :  private:
    1565             :   std::weak_ptr<NativeModule> native_module_;
    1566             : };
    1567             : }  // namespace
    1568             : 
    1569             : //==========================================================================
    1570             : // Step 3b (sync): Compilation finished.
    1571             : //==========================================================================
    1572        6636 : class AsyncCompileJob::CompileFinished : public CompileStep {
    1573             :  private:
    1574        2212 :   void RunInForeground(AsyncCompileJob* job) override {
    1575             :     TRACE_COMPILE("(3b) Compilation finished\n");
    1576             :     DCHECK(!job->native_module_->compilation_state()->failed());
    1577             :     // Sample the generated code size when baseline compilation finished.
    1578        2212 :     job->native_module_->SampleCodeSize(job->isolate_->counters(),
    1579        2212 :                                         NativeModule::kAfterBaseline);
    1580             :     // Also, set a callback to sample the code size after top-tier compilation
    1581             :     // finished. This callback will *not* keep the NativeModule alive.
    1582        4424 :     job->native_module_->compilation_state()->AddCallback(
    1583        2212 :         SampleTopTierCodeSizeCallback{job->native_module_});
    1584             :     // Then finalize and publish the generated module.
    1585        2212 :     job->FinishCompile();
    1586        2212 :   }
    1587             : };
    1588             : 
    1589        2280 : void AsyncCompileJob::CompileWrappers() {
    1590             :   // TODO(wasm): Compile all wrappers here, including the start function wrapper
    1591             :   // and the wrappers for the function table elements.
    1592             :   TRACE_COMPILE("(5) Compile wrappers...\n");
    1593             :   // Compile JS->wasm wrappers for exported functions.
    1594        2280 :   CompileJsToWasmWrappers(isolate_, module_object_->native_module()->module(),
    1595        4560 :                           handle(module_object_->export_wrappers(), isolate_));
    1596        2280 : }
    1597             : 
    1598        2284 : void AsyncCompileJob::FinishModule() {
    1599             :   TRACE_COMPILE("(6) Finish module...\n");
    1600             :   AsyncCompileSucceeded(module_object_);
    1601        2284 :   isolate_->wasm_engine()->RemoveCompileJob(this);
    1602        2284 : }
    1603             : 
    1604           0 : AsyncStreamingProcessor::AsyncStreamingProcessor(AsyncCompileJob* job)
    1605             :     : decoder_(job->enabled_features_),
    1606             :       job_(job),
    1607         920 :       compilation_unit_builder_(nullptr) {}
    1608             : 
    1609         180 : void AsyncStreamingProcessor::FinishAsyncCompileJobWithError(
    1610             :     const WasmError& error) {
    1611             :   DCHECK(error.has_error());
    1612             :   // Make sure all background tasks stopped executing before we change the state
    1613             :   // of the AsyncCompileJob to DecodeFail.
    1614         180 :   job_->background_task_manager_.CancelAndWait();
    1615             : 
    1616             :   // Check if there is already a CompiledModule, in which case we have to clean
    1617             :   // up the CompilationStateImpl as well.
    1618         180 :   if (job_->native_module_) {
    1619          64 :     Impl(job_->native_module_->compilation_state())->AbortCompilation();
    1620             : 
    1621          64 :     job_->DoSync<AsyncCompileJob::DecodeFail,
    1622          64 :                  AsyncCompileJob::kUseExistingForegroundTask>(error);
    1623             : 
    1624             :     // Clear the {compilation_unit_builder_} if it exists. This is needed
    1625             :     // because there is a check in the destructor of the
    1626             :     // {CompilationUnitBuilder} that it is empty.
    1627          64 :     if (compilation_unit_builder_) compilation_unit_builder_->Clear();
    1628             :   } else {
    1629             :     job_->DoSync<AsyncCompileJob::DecodeFail>(error);
    1630             :   }
    1631         180 : }
    1632             : 
    1633             : // Process the module header.
    1634         416 : bool AsyncStreamingProcessor::ProcessModuleHeader(Vector<const uint8_t> bytes,
    1635             :                                                   uint32_t offset) {
    1636             :   TRACE_STREAMING("Process module header...\n");
    1637         416 :   decoder_.StartDecoding(job_->isolate()->counters(),
    1638         832 :                          job_->isolate()->wasm_engine()->allocator());
    1639         416 :   decoder_.DecodeModuleHeader(bytes, offset);
    1640         416 :   if (!decoder_.ok()) {
    1641          16 :     FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());
    1642          16 :     return false;
    1643             :   }
    1644             :   return true;
    1645             : }
    1646             : 
    1647             : // Process all sections except for the code section.
    1648        1100 : bool AsyncStreamingProcessor::ProcessSection(SectionCode section_code,
    1649             :                                              Vector<const uint8_t> bytes,
    1650             :                                              uint32_t offset) {
    1651             :   TRACE_STREAMING("Process section %d ...\n", section_code);
    1652        1100 :   if (compilation_unit_builder_) {
    1653             :     // We reached a section after the code section, we do not need the
    1654             :     // compilation_unit_builder_ anymore.
    1655             :     CommitCompilationUnits();
    1656             :     compilation_unit_builder_.reset();
    1657             :   }
    1658        1100 :   if (section_code == SectionCode::kUnknownSectionCode) {
    1659             :     Decoder decoder(bytes, offset);
    1660             :     section_code = ModuleDecoder::IdentifyUnknownSection(
    1661         152 :         decoder, bytes.start() + bytes.length());
    1662         152 :     if (section_code == SectionCode::kUnknownSectionCode) {
    1663             :       // Skip unknown sections that we do not know how to handle.
    1664             :       return true;
    1665             :     }
    1666             :     // Remove the unknown section tag from the payload bytes.
    1667         152 :     offset += decoder.position();
    1668         152 :     bytes = bytes.SubVector(decoder.position(), bytes.size());
    1669             :   }
    1670             :   constexpr bool verify_functions = false;
    1671        1100 :   decoder_.DecodeSection(section_code, bytes, offset, verify_functions);
    1672        1100 :   if (!decoder_.ok()) {
    1673          28 :     FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());
    1674          28 :     return false;
    1675             :   }
    1676             :   return true;
    1677             : }
    1678             : 
    1679             : // Start the code section.
    1680         276 : bool AsyncStreamingProcessor::ProcessCodeSectionHeader(
    1681             :     int functions_count, uint32_t offset,
    1682             :     std::shared_ptr<WireBytesStorage> wire_bytes_storage) {
    1683             :   TRACE_STREAMING("Start the code section with %d functions...\n",
    1684             :                   functions_count);
    1685         276 :   if (!decoder_.CheckFunctionsCount(static_cast<uint32_t>(functions_count),
    1686             :                                     offset)) {
    1687          12 :     FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());
    1688          12 :     return false;
    1689             :   }
    1690             :   // Execute the PrepareAndStartCompile step immediately and not in a separate
    1691             :   // task.
    1692         792 :   job_->DoImmediately<AsyncCompileJob::PrepareAndStartCompile>(
    1693         264 :       decoder_.shared_module(), false);
    1694         264 :   auto* compilation_state = Impl(job_->native_module_->compilation_state());
    1695         528 :   compilation_state->SetWireBytesStorage(std::move(wire_bytes_storage));
    1696             : 
    1697             :   // Set number of functions that must be compiled to consider the module fully
    1698             :   // compiled.
    1699         264 :   auto wasm_module = job_->native_module_->module();
    1700         264 :   int num_functions = wasm_module->num_declared_functions;
    1701             :   DCHECK_IMPLIES(!job_->native_module_->enabled_features().compilation_hints,
    1702             :                  wasm_module->num_lazy_compilation_hints == 0);
    1703         264 :   int num_lazy_functions = wasm_module->num_lazy_compilation_hints;
    1704             :   compilation_state->SetNumberOfFunctionsToCompile(num_functions,
    1705         264 :                                                    num_lazy_functions);
    1706             : 
    1707             :   // Set outstanding_finishers_ to 2, because both the AsyncCompileJob and the
    1708             :   // AsyncStreamingProcessor have to finish.
    1709         264 :   job_->outstanding_finishers_.store(2);
    1710         264 :   compilation_unit_builder_.reset(
    1711         264 :       new CompilationUnitBuilder(job_->native_module_.get()));
    1712             :   return true;
    1713             : }
    1714             : 
    1715             : // Process a function body.
    1716         516 : bool AsyncStreamingProcessor::ProcessFunctionBody(Vector<const uint8_t> bytes,
    1717             :                                                   uint32_t offset) {
    1718             :   TRACE_STREAMING("Process function body %d ...\n", num_functions_);
    1719             : 
    1720        1032 :   decoder_.DecodeFunctionBody(
    1721        1032 :       num_functions_, static_cast<uint32_t>(bytes.length()), offset, false);
    1722             : 
    1723         516 :   NativeModule* native_module = job_->native_module_.get();
    1724             :   const WasmModule* module = native_module->module();
    1725         516 :   auto enabled_features = native_module->enabled_features();
    1726             :   uint32_t func_index =
    1727        1032 :       num_functions_ + decoder_.module()->num_imported_functions;
    1728             : 
    1729         516 :   if (IsLazyCompilation(module, native_module, enabled_features, func_index)) {
    1730          32 :     if (!FLAG_wasm_lazy_validation) {
    1731             :       Counters* counters = Impl(native_module->compilation_state())->counters();
    1732             :       AccountingAllocator* allocator = native_module->engine()->allocator();
    1733             : 
    1734             :       // The native module does not own the wire bytes until {SetWireBytes} is
    1735             :       // called in {OnFinishedStream}. Validation must use {bytes} parameter.
    1736          48 :       DecodeResult result = ValidateSingleFunction(
    1737             :           module, func_index, bytes, counters, allocator, enabled_features);
    1738             : 
    1739          24 :       if (result.failed()) {
    1740           8 :         FinishAsyncCompileJobWithError(result.error());
    1741             :         return false;
    1742             :       }
    1743             :     }
    1744             : 
    1745          24 :     native_module->UseLazyStub(func_index);
    1746             :   } else {
    1747         484 :     compilation_unit_builder_->AddUnits(func_index);
    1748             :   }
    1749             : 
    1750         508 :   ++num_functions_;
    1751             : 
    1752         508 :   return true;
    1753             : }
    1754             : 
    1755           0 : void AsyncStreamingProcessor::CommitCompilationUnits() {
    1756             :   DCHECK(compilation_unit_builder_);
    1757         332 :   compilation_unit_builder_->Commit();
    1758           0 : }
    1759             : 
    1760         404 : void AsyncStreamingProcessor::OnFinishedChunk() {
    1761             :   TRACE_STREAMING("FinishChunk...\n");
    1762         404 :   if (compilation_unit_builder_) CommitCompilationUnits();
    1763         404 : }
    1764             : 
    1765             : // Finish the processing of the stream.
    1766         200 : void AsyncStreamingProcessor::OnFinishedStream(OwnedVector<uint8_t> bytes) {
    1767             :   TRACE_STREAMING("Finish stream...\n");
    1768         596 :   ModuleResult result = decoder_.FinishDecoding(false);
    1769         200 :   if (result.failed()) {
    1770           4 :     FinishAsyncCompileJobWithError(result.error());
    1771           4 :     return;
    1772             :   }
    1773             :   // We have to open a HandleScope and prepare the Context for
    1774             :   // CreateNativeModule, PrepareRuntimeObjects and FinishCompile as this is a
    1775             :   // callback from the embedder.
    1776         196 :   HandleScope scope(job_->isolate_);
    1777         392 :   SaveAndSwitchContext saved_context(job_->isolate_, *job_->native_context_);
    1778             : 
    1779         196 :   bool needs_finish = job_->DecrementAndCheckFinisherCount();
    1780         196 :   if (job_->native_module_ == nullptr) {
    1781             :     // We are processing a WebAssembly module without code section. Create the
    1782             :     // runtime objects now (would otherwise happen in {PrepareAndStartCompile}).
    1783          56 :     job_->CreateNativeModule(std::move(result).value());
    1784             :     DCHECK(needs_finish);
    1785             :   }
    1786         392 :   job_->wire_bytes_ = ModuleWireBytes(bytes.as_vector());
    1787         392 :   job_->native_module_->SetWireBytes(std::move(bytes));
    1788         196 :   if (needs_finish) {
    1789         170 :     if (job_->native_module_->compilation_state()->failed()) {
    1790          17 :       job_->AsyncCompileFailed();
    1791             :     } else {
    1792          68 :       job_->FinishCompile();
    1793             :     }
    1794             :   }
    1795             : }
    1796             : 
    1797             : // Report an error detected in the StreamingDecoder.
    1798         112 : void AsyncStreamingProcessor::OnError(const WasmError& error) {
    1799             :   TRACE_STREAMING("Stream error...\n");
    1800         112 :   FinishAsyncCompileJobWithError(error);
    1801         112 : }
    1802             : 
    1803          72 : void AsyncStreamingProcessor::OnAbort() {
    1804             :   TRACE_STREAMING("Abort stream...\n");
    1805          72 :   job_->Abort();
    1806          72 : }
    1807             : 
    1808           8 : bool AsyncStreamingProcessor::Deserialize(Vector<const uint8_t> module_bytes,
    1809             :                                           Vector<const uint8_t> wire_bytes) {
    1810             :   // DeserializeNativeModule and FinishCompile assume that they are executed in
    1811             :   // a HandleScope, and that a context is set on the isolate.
    1812           8 :   HandleScope scope(job_->isolate_);
    1813          16 :   SaveAndSwitchContext saved_context(job_->isolate_, *job_->native_context_);
    1814             : 
    1815             :   MaybeHandle<WasmModuleObject> result =
    1816           8 :       DeserializeNativeModule(job_->isolate_, module_bytes, wire_bytes);
    1817           8 :   if (result.is_null()) return false;
    1818             : 
    1819             :   job_->module_object_ =
    1820           8 :       job_->isolate_->global_handles()->Create(*result.ToHandleChecked());
    1821           8 :   job_->native_module_ = job_->module_object_->shared_native_module();
    1822           4 :   auto owned_wire_bytes = OwnedVector<uint8_t>::Of(wire_bytes);
    1823           8 :   job_->wire_bytes_ = ModuleWireBytes(owned_wire_bytes.as_vector());
    1824           8 :   job_->native_module_->SetWireBytes(std::move(owned_wire_bytes));
    1825           4 :   job_->FinishCompile();
    1826             :   return true;
    1827             : }
    1828             : 
    1829             : namespace {
    1830     1242357 : int GetMaxBackgroundTasks() {
    1831     1242357 :   if (NeedsDeterministicCompile()) return 1;
    1832     1242349 :   int num_worker_threads = V8::GetCurrentPlatform()->NumberOfWorkerThreads();
    1833             :   int num_compile_tasks =
    1834     1242349 :       std::min(FLAG_wasm_num_compilation_tasks, num_worker_threads);
    1835     2484698 :   return std::max(1, num_compile_tasks);
    1836             : }
    1837             : }  // namespace
    1838             : 
    1839     1242357 : CompilationStateImpl::CompilationStateImpl(
    1840             :     const std::shared_ptr<NativeModule>& native_module,
    1841             :     std::shared_ptr<Counters> async_counters)
    1842             :     : native_module_(native_module.get()),
    1843             :       background_compile_token_(
    1844             :           std::make_shared<BackgroundCompileToken>(native_module)),
    1845      877209 :       compile_mode_(FLAG_wasm_tier_up &&
    1846      877209 :                             native_module->module()->origin == kWasmOrigin
    1847             :                         ? CompileMode::kTiering
    1848             :                         : CompileMode::kRegular),
    1849             :       async_counters_(std::move(async_counters)),
    1850     1242357 :       max_background_tasks_(GetMaxBackgroundTasks()),
    1851             :       compilation_unit_queues_(max_background_tasks_),
    1852     7454142 :       available_task_ids_(max_background_tasks_) {
    1853    18635245 :   for (int i = 0; i < max_background_tasks_; ++i) {
    1854             :     // Ids are popped on task creation, so reverse this list. This ensures that
    1855             :     // the first background task gets id 0.
    1856    17392888 :     available_task_ids_[i] = max_background_tasks_ - 1 - i;
    1857             :   }
    1858     1242357 : }
    1859             : 
    1860     1242594 : void CompilationStateImpl::AbortCompilation() {
    1861     1242594 :   background_compile_token_->Cancel();
    1862             :   // No more callbacks after abort.
    1863     1242594 :   base::MutexGuard callbacks_guard(&callbacks_mutex_);
    1864             :   callbacks_.clear();
    1865     1242594 : }
    1866             : 
    1867      144485 : void CompilationStateImpl::SetNumberOfFunctionsToCompile(
    1868             :     int num_functions, int num_lazy_functions) {
    1869             :   DCHECK(!failed());
    1870      144485 :   base::MutexGuard guard(&callbacks_mutex_);
    1871             : 
    1872      144488 :   int num_functions_to_compile = num_functions - num_lazy_functions;
    1873      144488 :   outstanding_baseline_functions_ = num_functions_to_compile;
    1874      144488 :   outstanding_top_tier_functions_ = num_functions_to_compile;
    1875      288975 :   highest_execution_tier_.assign(num_functions, ExecutionTier::kNone);
    1876             : 
    1877             :   // Degenerate case of an empty module. Trigger callbacks immediately.
    1878      144487 :   if (num_functions_to_compile == 0) {
    1879       12646 :     for (auto& callback : callbacks_) {
    1880             :       callback(CompilationEvent::kFinishedBaselineCompilation);
    1881             :     }
    1882       12646 :     for (auto& callback : callbacks_) {
    1883             :       callback(CompilationEvent::kFinishedTopTierCompilation);
    1884             :     }
    1885             :     // Clear the callbacks because no more events will be delivered.
    1886             :     callbacks_.clear();
    1887             :   }
    1888      144488 : }
    1889             : 
    1890      146703 : void CompilationStateImpl::AddCallback(CompilationState::callback_t callback) {
    1891      146703 :   base::MutexGuard callbacks_guard(&callbacks_mutex_);
    1892      146703 :   callbacks_.emplace_back(std::move(callback));
    1893      146704 : }
    1894             : 
    1895             : void CompilationStateImpl::AddCompilationUnits(
    1896             :     Vector<std::unique_ptr<WasmCompilationUnit>> baseline_units,
    1897             :     Vector<std::unique_ptr<WasmCompilationUnit>> top_tier_units) {
    1898      138237 :   compilation_unit_queues_.AddUnits(baseline_units, top_tier_units);
    1899             : 
    1900      138238 :   RestartBackgroundTasks();
    1901             : }
    1902             : 
    1903          69 : void CompilationStateImpl::AddTopTierCompilationUnit(
    1904             :     std::unique_ptr<WasmCompilationUnit> unit) {
    1905             :   AddCompilationUnits({}, {&unit, 1});
    1906          69 : }
    1907             : 
    1908             : std::unique_ptr<WasmCompilationUnit>
    1909             : CompilationStateImpl::GetNextCompilationUnit(
    1910             :     int task_id, CompileBaselineOnly baseline_only) {
    1911      789001 :   return compilation_unit_queues_.GetNextUnit(task_id, baseline_only);
    1912             : }
    1913             : 
    1914             : void CompilationStateImpl::OnFinishedUnit(WasmCode* code) {
    1915           0 :   OnFinishedUnits({&code, 1});
    1916             : }
    1917             : 
    1918      263773 : void CompilationStateImpl::OnFinishedUnits(Vector<WasmCode*> code_vector) {
    1919      263773 :   base::MutexGuard guard(&callbacks_mutex_);
    1920             : 
    1921             :   // Assume an order of execution tiers that represents the quality of their
    1922             :   // generated code.
    1923             :   static_assert(ExecutionTier::kNone < ExecutionTier::kInterpreter &&
    1924             :                     ExecutionTier::kInterpreter < ExecutionTier::kLiftoff &&
    1925             :                     ExecutionTier::kLiftoff < ExecutionTier::kTurbofan,
    1926             :                 "Assume an order on execution tiers");
    1927             : 
    1928      263946 :   auto module = native_module_->module();
    1929      263946 :   auto enabled_features = native_module_->enabled_features();
    1930      892912 :   for (WasmCode* code : code_vector) {
    1931             :     DCHECK_NOT_NULL(code);
    1932             :     DCHECK_NE(code->tier(), ExecutionTier::kNone);
    1933      314482 :     native_module_->engine()->LogCode(code);
    1934             : 
    1935             :     // Skip lazily compiled code as we do not consider this for the completion
    1936             :     // of baseline respectively top tier compilation.
    1937             :     int func_index = code->index();
    1938      628992 :     if (IsLazyCompilation(module, native_module_, enabled_features,
    1939             :                           func_index)) {
    1940          52 :       continue;
    1941             :     }
    1942             : 
    1943             :     // Determine whether we are reaching baseline or top tier with the given
    1944             :     // code.
    1945      314444 :     uint32_t slot_index = code->index() - module->num_imported_functions;
    1946             :     ExecutionTierPair requested_tiers = GetRequestedExecutionTiers(
    1947      314444 :         module, compile_mode(), enabled_features, func_index);
    1948             :     DCHECK_EQ(highest_execution_tier_.size(), module->num_declared_functions);
    1949      628874 :     ExecutionTier prior_tier = highest_execution_tier_[slot_index];
    1950      314437 :     bool had_reached_baseline = prior_tier >= requested_tiers.baseline_tier;
    1951             :     bool had_reached_top_tier = prior_tier >= requested_tiers.top_tier;
    1952             :     DCHECK_IMPLIES(had_reached_baseline, prior_tier > ExecutionTier::kNone);
    1953             :     bool reaches_baseline = !had_reached_baseline;
    1954             :     bool reaches_top_tier =
    1955      314437 :         !had_reached_top_tier && code->tier() >= requested_tiers.top_tier;
    1956             :     DCHECK_IMPLIES(reaches_baseline,
    1957             :                    code->tier() >= requested_tiers.baseline_tier);
    1958             :     DCHECK_IMPLIES(reaches_top_tier, had_reached_baseline || reaches_baseline);
    1959             : 
    1960             :     // Remember compilation state before update.
    1961             :     bool had_completed_baseline_compilation =
    1962      314437 :         outstanding_baseline_functions_ == 0;
    1963             :     bool had_completed_top_tier_compilation =
    1964      314437 :         outstanding_top_tier_functions_ == 0;
    1965             : 
    1966             :     // Update compilation state.
    1967      314437 :     if (code->tier() > prior_tier) {
    1968      298971 :       highest_execution_tier_[slot_index] = code->tier();
    1969             :     }
    1970      314437 :     if (reaches_baseline) outstanding_baseline_functions_--;
    1971      314437 :     if (reaches_top_tier) outstanding_top_tier_functions_--;
    1972             :     DCHECK_LE(0, outstanding_baseline_functions_);
    1973             :     DCHECK_LE(outstanding_baseline_functions_, outstanding_top_tier_functions_);
    1974             : 
    1975             :     // Conclude if we are completing baseline or top tier compilation.
    1976      547087 :     bool completes_baseline_compilation = !had_completed_baseline_compilation &&
    1977      232650 :                                           outstanding_baseline_functions_ == 0;
    1978      619843 :     bool completes_top_tier_compilation = !had_completed_top_tier_compilation &&
    1979      305406 :                                           outstanding_top_tier_functions_ == 0;
    1980             :     DCHECK_IMPLIES(
    1981             :         completes_top_tier_compilation,
    1982             :         had_completed_baseline_compilation || completes_baseline_compilation);
    1983             : 
    1984             :     // Trigger callbacks.
    1985      314437 :     if (completes_baseline_compilation) {
    1986      260986 :       for (auto& callback : callbacks_) {
    1987             :         callback(CompilationEvent::kFinishedBaselineCompilation);
    1988             :       }
    1989             :     }
    1990      314437 :     if (completes_top_tier_compilation) {
    1991      260992 :       for (auto& callback : callbacks_) {
    1992             :         callback(CompilationEvent::kFinishedTopTierCompilation);
    1993             :       }
    1994             :       // Clear the callbacks because no more events will be delivered.
    1995             :       callbacks_.clear();
    1996             :     }
    1997             :   }
    1998      263956 : }
    1999             : 
    2000      224137 : void CompilationStateImpl::OnBackgroundTaskStopped(
    2001             :     int task_id, const WasmFeatures& detected) {
    2002             :   {
    2003      224137 :     base::MutexGuard guard(&mutex_);
    2004             :     DCHECK_EQ(0, std::count(available_task_ids_.begin(),
    2005             :                             available_task_ids_.end(), task_id));
    2006             :     DCHECK_GT(max_background_tasks_, available_task_ids_.size());
    2007      224217 :     available_task_ids_.push_back(task_id);
    2008      224210 :     UnionFeaturesInto(&detected_features_, detected);
    2009             :   }
    2010             : 
    2011             :   // The background task could have stopped while we were adding new units, or
    2012             :   // because it reached its deadline. In both cases we need to restart tasks to
    2013             :   // avoid a potential deadlock.
    2014      224202 :   RestartBackgroundTasks();
    2015      224186 : }
    2016             : 
    2017      248634 : void CompilationStateImpl::UpdateDetectedFeatures(
    2018             :     const WasmFeatures& detected) {
    2019      248634 :   base::MutexGuard guard(&mutex_);
    2020      248638 :   UnionFeaturesInto(&detected_features_, detected);
    2021      248637 : }
    2022             : 
    2023      144347 : void CompilationStateImpl::PublishDetectedFeatures(Isolate* isolate) {
    2024             :   // Notifying the isolate of the feature counts must take place under
    2025             :   // the mutex, because even if we have finished baseline compilation,
    2026             :   // tiering compilations may still occur in the background.
    2027      144347 :   base::MutexGuard guard(&mutex_);
    2028             :   UpdateFeatureUseCounts(isolate, detected_features_);
    2029      144347 : }
    2030             : 
    2031      362399 : void CompilationStateImpl::RestartBackgroundTasks() {
    2032             :   // Create new tasks, but only spawn them after releasing the mutex, because
    2033             :   // some platforms (e.g. the predictable platform) might execute tasks right
    2034             :   // away.
    2035      358386 :   std::vector<std::unique_ptr<Task>> new_tasks;
    2036             :   {
    2037      362399 :     base::MutexGuard guard(&mutex_);
    2038             :     // Explicit fast path (quite common): If no more task ids are available
    2039             :     // (i.e. {max_background_tasks_} tasks are already running), spawn nothing.
    2040      362451 :     if (available_task_ids_.empty()) return;
    2041             :     // No need to restart tasks if compilation already failed.
    2042      362440 :     if (failed()) return;
    2043             : 
    2044             :     size_t max_num_restart = compilation_unit_queues_.GetTotalSize();
    2045             : 
    2046      816180 :     while (!available_task_ids_.empty() && max_num_restart-- > 0) {
    2047      228899 :       int task_id = available_task_ids_.back();
    2048             :       available_task_ids_.pop_back();
    2049             :       new_tasks.emplace_back(
    2050      228899 :           native_module_->engine()
    2051      457799 :               ->NewBackgroundCompileTask<BackgroundCompileTask>(
    2052      228900 :                   background_compile_token_, async_counters_, task_id));
    2053             :     }
    2054             :   }
    2055             : 
    2056      358385 :   if (baseline_compilation_finished()) {
    2057      138519 :     for (auto& task : new_tasks) {
    2058       20448 :       V8::GetCurrentPlatform()->CallLowPriorityTaskOnWorkerThread(
    2059       20448 :           std::move(task));
    2060             :     }
    2061             :   } else {
    2062      448766 :     for (auto& task : new_tasks) {
    2063      656026 :       V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
    2064             :     }
    2065             :   }
    2066             : }
    2067             : 
    2068        7919 : void CompilationStateImpl::SetError() {
    2069        7919 :   bool expected = false;
    2070        7919 :   if (!compile_failed_.compare_exchange_strong(expected, true,
    2071             :                                                std::memory_order_relaxed)) {
    2072         334 :     return;  // Already failed before.
    2073             :   }
    2074             : 
    2075        7585 :   base::MutexGuard callbacks_guard(&callbacks_mutex_);
    2076       15170 :   for (auto& callback : callbacks_) {
    2077             :     callback(CompilationEvent::kFailedCompilation);
    2078             :   }
    2079             :   // No more callbacks after an error.
    2080             :   callbacks_.clear();
    2081             : }
    2082             : 
    2083      139806 : void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module,
    2084             :                              Handle<FixedArray> export_wrappers) {
    2085             :   JSToWasmWrapperCache js_to_wasm_cache;
    2086             :   int wrapper_index = 0;
    2087             : 
    2088             :   // TODO(6792): Wrappers below are allocated with {Factory::NewCode}. As an
    2089             :   // optimization we keep the code space unlocked to avoid repeated unlocking
    2090             :   // because many such wrapper are allocated in sequence below.
    2091      279616 :   CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
    2092      358991 :   for (auto exp : module->export_table) {
    2093      219181 :     if (exp.kind != kExternalFunction) continue;
    2094      216393 :     auto& function = module->functions[exp.index];
    2095             :     Handle<Code> wrapper_code = js_to_wasm_cache.GetOrCompileJSToWasmWrapper(
    2096      216393 :         isolate, function.sig, function.imported);
    2097      432786 :     export_wrappers->set(wrapper_index, *wrapper_code);
    2098      216393 :     RecordStats(*wrapper_code, isolate->counters());
    2099      216393 :     ++wrapper_index;
    2100             :   }
    2101      139809 : }
    2102             : 
    2103      137346 : Handle<Script> CreateWasmScript(Isolate* isolate,
    2104             :                                 const ModuleWireBytes& wire_bytes,
    2105             :                                 const std::string& source_map_url) {
    2106             :   Handle<Script> script =
    2107      137346 :       isolate->factory()->NewScript(isolate->factory()->empty_string());
    2108      412038 :   script->set_context_data(isolate->native_context()->debug_context_id());
    2109             :   script->set_type(Script::TYPE_WASM);
    2110             : 
    2111      137346 :   int hash = StringHasher::HashSequentialString(
    2112             :       reinterpret_cast<const char*>(wire_bytes.start()),
    2113      137346 :       static_cast<int>(wire_bytes.length()), kZeroHashSeed);
    2114             : 
    2115             :   const int kBufferSize = 32;
    2116             :   char buffer[kBufferSize];
    2117             : 
    2118      137346 :   int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash);
    2119             :   DCHECK(name_chars >= 0 && name_chars < kBufferSize);
    2120             :   MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte(
    2121      137345 :       VectorOf(reinterpret_cast<uint8_t*>(buffer), name_chars),
    2122      137345 :       AllocationType::kOld);
    2123      274690 :   script->set_name(*name_str.ToHandleChecked());
    2124             : 
    2125      137345 :   if (source_map_url.size() != 0) {
    2126             :     MaybeHandle<String> src_map_str = isolate->factory()->NewStringFromUtf8(
    2127           4 :         CStrVector(source_map_url.c_str()), AllocationType::kOld);
    2128           8 :     script->set_source_mapping_url(*src_map_str.ToHandleChecked());
    2129             :   }
    2130      137345 :   return script;
    2131             : }
    2132             : 
    2133             : }  // namespace wasm
    2134             : }  // namespace internal
    2135      122036 : }  // namespace v8
    2136             : 
    2137             : #undef TRACE_COMPILE
    2138             : #undef TRACE_STREAMING
    2139             : #undef TRACE_LAZY

Generated by: LCOV version 1.10