LCOV - code coverage report
Current view: top level - src/wasm - module-compiler.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 674 697 96.7 %
Date: 2019-04-17 Functions: 102 118 86.4 %

          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     2484756 : class BackgroundCompileToken {
      67             :  public:
      68             :   explicit BackgroundCompileToken(
      69             :       const std::shared_ptr<NativeModule>& native_module)
      70     1242379 :       : native_module_(native_module) {}
      71             : 
      72     1250461 :   void Cancel() {
      73     1250461 :     base::SharedMutexGuard<base::kExclusive> mutex_guard(&mutex_);
      74             :     native_module_.reset();
      75     1250458 :   }
      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      796879 :     mutex_.LockShared();
      84             :     return native_module_.lock();
      85             :   }
      86             : 
      87      800453 :   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      796879 :   explicit BackgroundCompileScope(
      98             :       const std::shared_ptr<BackgroundCompileToken>& token)
      99     1597686 :       : token_(token.get()), native_module_(token->StartScope()) {}
     100             : 
     101     1601101 :   ~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     1242379 : class CompilationUnitQueues {
     127             :  public:
     128     1242379 :   explicit CompilationUnitQueues(int max_tasks) : queues_(max_tasks) {
     129             :     DCHECK_LT(0, max_tasks);
     130    18635589 :     for (int task_id = 0; task_id < max_tasks; ++task_id) {
     131    17393210 :       queues_[task_id].next_steal_task_id_ = next_task_id(task_id);
     132             :     }
     133     3727137 :     for (auto& atomic_counter : num_units_) {
     134             :       std::atomic_init(&atomic_counter, size_t{0});
     135             :     }
     136     1242379 :   }
     137             : 
     138      779553 :   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      779553 :     int max_tier = baseline_only ? kBaseline : kTopTier;
     146      798381 :     for (int tier = GetLowestTierWithUnits(); tier <= max_tier; ++tier) {
     147      342296 :       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      342296 :         base::MutexGuard mutex_guard(&queue->mutex_);
     153      342552 :         if (!queue->units_[tier].empty()) {
     154      315095 :           auto unit = std::move(queue->units_[tier].back());
     155             :           queue->units_[tier].pop_back();
     156             :           DecrementUnitCount(tier);
     157             :           return unit;
     158             :         }
     159       27375 :         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      386713 :       for (; steal_trials > 0;
     166             :            --steal_trials, steal_task_id = next_task_id(steal_task_id)) {
     167      137703 :         if (steal_task_id == task_id) continue;
     168      355543 :         if (auto unit = StealUnitsAndGetFirst(task_id, steal_task_id, tier)) {
     169             :           DecrementUnitCount(tier);
     170       17963 :           return unit;
     171             :         }
     172             :       }
     173             :     }
     174             :     return {};
     175             :   }
     176             : 
     177      138277 :   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      138277 :     int queue_to_add = next_queue_to_add.load(std::memory_order_relaxed);
     183      276554 :     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      138277 :     Queue* queue = &queues_[queue_to_add];
     189      138277 :     base::MutexGuard guard(&queue->mutex_);
     190      138276 :     if (!baseline_units.empty()) {
     191             :       queue->units_[kBaseline].insert(
     192             :           queue->units_[kBaseline].end(),
     193             :           std::make_move_iterator(baseline_units.begin()),
     194      138210 :           std::make_move_iterator(baseline_units.end()));
     195             :       num_units_[kBaseline].fetch_add(baseline_units.size(),
     196             :                                       std::memory_order_relaxed);
     197             :     }
     198      138277 :     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       51834 :           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      138277 :   }
     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     1022511 :     for (auto& atomic_counter : num_units_) {
     214      681683 :       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    34786415 :   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     8972635 :     int next = task_id + 1;
     241     8972635 :     return next == static_cast<int>(queues_.size()) ? 0 : next;
     242             :   }
     243             : 
     244             :   int GetLowestTierWithUnits() const {
     245     2689389 :     for (int tier = 0; tier < kNumTiers; ++tier) {
     246     1319704 :       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      124448 :   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      124546 :     std::vector<std::unique_ptr<WasmCompilationUnit>> stolen;
     264             :     {
     265      124448 :       Queue* steal_queue = &queues_[steal_from_task_id];
     266      124448 :       base::MutexGuard guard(&steal_queue->mutex_);
     267      124536 :       if (steal_queue->units_[wanted_tier].empty()) return {};
     268             :       auto* steal_from_vector = &steal_queue->units_[wanted_tier];
     269       17966 :       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       17960 :       steal_from_vector->resize(remaining);
     274             :     }
     275             :     DCHECK(!stolen.empty());
     276       17966 :     auto returned_unit = std::move(stolen.back());
     277             :     stolen.pop_back();
     278       17966 :     Queue* queue = &queues_[task_id];
     279       17966 :     base::MutexGuard guard(&queue->mutex_);
     280       17963 :     auto* target_queue = &queue->units_[wanted_tier];
     281             :     target_queue->insert(target_queue->end(),
     282             :                          std::make_move_iterator(stolen.begin()),
     283       17963 :                          std::make_move_iterator(stolen.end()));
     284       17955 :     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     4969516 : 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      340837 :     base::MutexGuard guard(&callbacks_mutex_);
     337             :     DCHECK_LE(outstanding_baseline_functions_, outstanding_top_tier_functions_);
     338      340831 :     return outstanding_baseline_functions_ == 0;
     339             :   }
     340             : 
     341             :   CompileMode compile_mode() const { return compile_mode_; }
     342             :   Counters* counters() const { return async_counters_.get(); }
     343        8130 :   WasmFeatures* detected_features() { return &detected_features_; }
     344             : 
     345     2340182 :   void SetWireBytesStorage(
     346             :       std::shared_ptr<WireBytesStorage> wire_bytes_storage) {
     347     2340182 :     base::MutexGuard guard(&mutex_);
     348             :     wire_bytes_storage_ = wire_bytes_storage;
     349     2340182 :   }
     350             : 
     351             :   std::shared_ptr<WireBytesStorage> GetWireBytesStorage() const {
     352     1195897 :     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      256177 :     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      144363 :   if (detected.threads) {
     430        1444 :     isolate->CountUsage(v8::Isolate::UseCounterFeature::kWasmThreadOpcodes);
     431             :   }
     432             : }
     433             : 
     434             : }  // namespace
     435             : 
     436             : //////////////////////////////////////////////////////
     437             : // PIMPL implementation of {CompilationState}.
     438             : 
     439     1242379 : CompilationState::~CompilationState() { Impl(this)->~CompilationStateImpl(); }
     440             : 
     441     1242378 : void CompilationState::AbortCompilation() { Impl(this)->AbortCompilation(); }
     442             : 
     443           0 : void CompilationState::SetError() { Impl(this)->SetError(); }
     444             : 
     445     2339926 : void CompilationState::SetWireBytesStorage(
     446             :     std::shared_ptr<WireBytesStorage> wire_bytes_storage) {
     447     4679852 :   Impl(this)->SetWireBytesStorage(std::move(wire_bytes_storage));
     448     2339926 : }
     449             : 
     450      731780 : std::shared_ptr<WireBytesStorage> CompilationState::GetWireBytesStorage()
     451             :     const {
     452      731780 :   return Impl(this)->GetWireBytesStorage();
     453             : }
     454             : 
     455        2203 : void CompilationState::AddCallback(CompilationState::callback_t callback) {
     456        4406 :   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     1242379 : 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     2484758 :       new CompilationStateImpl(native_module, std::move(async_counters))));
     475             : }
     476             : 
     477             : // End of PIMPL implementation of {CompilationState}.
     478             : //////////////////////////////////////////////////////
     479             : 
     480             : namespace {
     481             : 
     482         198 : ExecutionTier ApplyHintToExecutionTier(WasmCompilationHintTier hint,
     483             :                                        ExecutionTier default_tier) {
     484         198 :   switch (hint) {
     485             :     case WasmCompilationHintTier::kDefault:
     486         144 :       return default_tier;
     487             :     case WasmCompilationHintTier::kInterpreter:
     488             :       return ExecutionTier::kInterpreter;
     489             :     case WasmCompilationHintTier::kBaseline:
     490           9 :       return ExecutionTier::kLiftoff;
     491             :     case WasmCompilationHintTier::kOptimized:
     492          33 :       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        1267 :   uint32_t hint_index = func_index - module->num_imported_functions;
     501             :   const std::vector<WasmCompilationHint>& compilation_hints =
     502             :       module->compilation_hints;
     503        2534 :   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      559268 :   if (enabled_features.compilation_hints) {
     513             :     const WasmCompilationHint* hint = GetCompilationHint(module, func_index);
     514        1141 :     return hint != nullptr &&
     515         342 :            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      559300 :   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      567244 : ExecutionTierPair GetRequestedExecutionTiers(
     534             :     const WasmModule* module, CompileMode compile_mode,
     535             :     const WasmFeatures& enabled_features, uint32_t func_index) {
     536             :   ExecutionTierPair result;
     537      567244 :   switch (compile_mode) {
     538             :     case CompileMode::kRegular:
     539             :       result.baseline_tier =
     540      274256 :           WasmCompilationUnit::GetDefaultExecutionTier(module);
     541             :       result.top_tier = result.baseline_tier;
     542      274256 :       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      292988 :       if (enabled_features.compilation_hints) {
     551             :         const WasmCompilationHint* hint =
     552             :             GetCompilationHint(module, func_index);
     553         468 :         if (hint != nullptr) {
     554          99 :           result.baseline_tier = ApplyHintToExecutionTier(hint->baseline_tier,
     555             :                                                           result.baseline_tier);
     556             :           result.top_tier =
     557          99 :               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      292989 :       if (result.baseline_tier > result.top_tier) {
     566             :         result.top_tier = result.baseline_tier;
     567             :       }
     568      292989 :       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      144503 : class CompilationUnitBuilder {
     577             :  public:
     578             :   explicit CompilationUnitBuilder(NativeModule* native_module)
     579             :       : native_module_(native_module),
     580      144502 :         default_tier_(WasmCompilationUnit::GetDefaultExecutionTier(
     581      289004 :             native_module->module())) {}
     582             : 
     583      235510 :   void AddUnits(uint32_t func_index) {
     584             :     ExecutionTierPair tiers = GetRequestedExecutionTiers(
     585             :         native_module_->module(), compilation_state()->compile_mode(),
     586      471020 :         native_module_->enabled_features(), func_index);
     587      235511 :     baseline_units_.emplace_back(CreateUnit(func_index, tiers.baseline_tier));
     588      235511 :     if (tiers.baseline_tier != tiers.top_tier) {
     589       99860 :       tiering_units_.emplace_back(CreateUnit(func_index, tiers.top_tier));
     590             :     }
     591      235510 :   }
     592             : 
     593      144569 :   bool Commit() {
     594      144569 :     if (baseline_units_.empty() && tiering_units_.empty()) return false;
     595             :     compilation_state()->AddCompilationUnits(VectorOf(baseline_units_),
     596             :                                              VectorOf(tiering_units_));
     597      138211 :     Clear();
     598      138211 :     return true;
     599             :   }
     600             : 
     601      138271 :   void Clear() {
     602             :     baseline_units_.clear();
     603             :     tiering_units_.clear();
     604      138271 :   }
     605             : 
     606             :  private:
     607             :   std::unique_ptr<WasmCompilationUnit> CreateUnit(uint32_t func_index,
     608             :                                                   ExecutionTier tier) {
     609      335371 :     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             : }  // namespace
     623             : 
     624        8130 : void CompileLazy(Isolate* isolate, NativeModule* native_module,
     625             :                  uint32_t func_index) {
     626             :   Counters* counters = isolate->counters();
     627             :   HistogramTimerScope lazy_time_scope(counters->wasm_lazy_compilation_time());
     628             : 
     629             :   DCHECK(!native_module->lazy_compile_frozen());
     630             : 
     631             :   base::ElapsedTimer compilation_timer;
     632             : 
     633       16260 :   NativeModuleModificationScope native_module_modification_scope(native_module);
     634             : 
     635             :   DCHECK(!native_module->HasCode(static_cast<uint32_t>(func_index)));
     636             : 
     637             :   compilation_timer.Start();
     638             : 
     639             :   TRACE_LAZY("Compiling wasm-function#%d.\n", func_index);
     640             : 
     641             :   const uint8_t* module_start = native_module->wire_bytes().start();
     642             : 
     643        8130 :   const WasmFunction* func = &native_module->module()->functions[func_index];
     644             :   FunctionBody func_body{func->sig, func->code.offset(),
     645             :                          module_start + func->code.offset(),
     646             :                          module_start + func->code.end_offset()};
     647             : 
     648             :   CompilationStateImpl* compilation_state =
     649             :       Impl(native_module->compilation_state());
     650             :   ExecutionTierPair tiers = GetRequestedExecutionTiers(
     651             :       native_module->module(), compilation_state->compile_mode(),
     652        8130 :       native_module->enabled_features(), func_index);
     653             : 
     654       16260 :   WasmCompilationUnit baseline_unit(func_index, tiers.baseline_tier);
     655        8130 :   CompilationEnv env = native_module->CreateCompilationEnv();
     656             :   WasmCompilationResult result = baseline_unit.ExecuteCompilation(
     657        8130 :       isolate->wasm_engine(), &env, compilation_state->GetWireBytesStorage(),
     658       16260 :       isolate->counters(), compilation_state->detected_features());
     659       16260 :   WasmCodeRefScope code_ref_scope;
     660        8130 :   WasmCode* code = native_module->AddCompiledCode(std::move(result));
     661             : 
     662        8130 :   if (tiers.baseline_tier < tiers.top_tier) {
     663             :     auto tiering_unit =
     664         132 :         base::make_unique<WasmCompilationUnit>(func_index, tiers.top_tier);
     665          66 :     compilation_state->AddTopTierCompilationUnit(std::move(tiering_unit));
     666             :   }
     667             : 
     668             :   // During lazy compilation, we should never get compilation errors. The module
     669             :   // was verified before starting execution with lazy compilation.
     670             :   // This might be OOM, but then we cannot continue execution anyway.
     671             :   // TODO(clemensh): According to the spec, we can actually skip validation at
     672             :   // module creation time, and return a function that always traps here.
     673        8130 :   CHECK(!compilation_state->failed());
     674             : 
     675             :   // The code we just produced should be the one that was requested.
     676             :   DCHECK_EQ(func_index, code->index());
     677             : 
     678        8130 :   if (WasmCode::ShouldBeLogged(isolate)) code->LogCode(isolate);
     679             : 
     680        8130 :   double func_kb = 1e-3 * func->code.length();
     681        8130 :   double compilation_seconds = compilation_timer.Elapsed().InSecondsF();
     682             : 
     683        8130 :   counters->wasm_lazily_compiled_functions()->Increment();
     684             : 
     685        8130 :   int throughput_sample = static_cast<int>(func_kb / compilation_seconds);
     686        8130 :   counters->wasm_lazy_compilation_throughput()->AddSample(throughput_sample);
     687        8130 : }
     688             : 
     689             : namespace {
     690             : 
     691      221067 : void RecordStats(const Code code, Counters* counters) {
     692      442136 :   counters->wasm_generated_code_size()->Increment(code->body_size());
     693      221070 :   counters->wasm_reloc_size()->Increment(code->relocation_info()->length());
     694      221071 : }
     695             : 
     696             : constexpr int kMainThreadTaskId = -1;
     697             : 
     698             : // Run by the main thread and background tasks to take part in compilation.
     699             : // Returns whether any units were executed.
     700      467604 : bool ExecuteCompilationUnits(
     701             :     const std::shared_ptr<BackgroundCompileToken>& token, Counters* counters,
     702             :     int task_id, CompileBaselineOnly baseline_only) {
     703             :   TRACE_COMPILE("Compiling (task %d)...\n", task_id);
     704     1402882 :   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "ExecuteCompilationUnits");
     705             : 
     706      467608 :   const bool is_foreground = task_id == kMainThreadTaskId;
     707             :   // The main thread uses task id 0, which might collide with one of the
     708             :   // background tasks. This is fine, as it will only cause some contention on
     709             :   // the one queue, but work otherwise.
     710      467608 :   if (is_foreground) task_id = 0;
     711             : 
     712      467608 :   Platform* platform = V8::GetCurrentPlatform();
     713             :   // Deadline is in 50ms from now.
     714             :   static constexpr double kBackgroundCompileTimeLimit =
     715             :       50.0 / base::Time::kMillisecondsPerSecond;
     716             :   const double deadline =
     717      467518 :       platform->MonotonicallyIncreasingTime() + kBackgroundCompileTimeLimit;
     718             : 
     719             :   // These fields are initialized in a {BackgroundCompileScope} before
     720             :   // starting compilation.
     721      467596 :   base::Optional<CompilationEnv> env;
     722      467596 :   std::shared_ptr<WireBytesStorage> wire_bytes;
     723             :   std::shared_ptr<const WasmModule> module;
     724             :   WasmEngine* wasm_engine = nullptr;
     725      935325 :   std::unique_ptr<WasmCompilationUnit> unit;
     726      467596 :   WasmFeatures detected_features = kNoWasmFeatures;
     727             : 
     728             :   auto stop = [is_foreground, task_id,
     729      909168 :                &detected_features](BackgroundCompileScope& compile_scope) {
     730      454584 :     if (is_foreground) {
     731             :       compile_scope.compilation_state()->UpdateDetectedFeatures(
     732      248099 :           detected_features);
     733             :     } else {
     734             :       compile_scope.compilation_state()->OnBackgroundTaskStopped(
     735      206485 :           task_id, detected_features);
     736             :     }
     737      454633 :   };
     738             : 
     739             :   // Preparation (synchronized): Initialize the fields above and get the first
     740             :   // compilation unit.
     741             :   {
     742      655779 :     BackgroundCompileScope compile_scope(token);
     743      747236 :     if (compile_scope.cancelled()) return false;
     744      912116 :     env.emplace(compile_scope.native_module()->CreateCompilationEnv());
     745      456150 :     wire_bytes = compile_scope.compilation_state()->GetWireBytesStorage();
     746      456161 :     module = compile_scope.native_module()->shared_module();
     747             :     wasm_engine = compile_scope.native_module()->engine();
     748      455986 :     unit = compile_scope.compilation_state()->GetNextCompilationUnit(
     749             :         task_id, baseline_only);
     750      456159 :     if (unit == nullptr) {
     751      267976 :       stop(compile_scope);
     752      267981 :       return false;
     753             :     }
     754             :   }
     755             : 
     756      188168 :   std::vector<WasmCompilationResult> results_to_publish;
     757             : 
     758             :   auto publish_results = [&results_to_publish](
     759      940255 :                              BackgroundCompileScope* compile_scope) {
     760      539552 :     if (results_to_publish.empty()) return;
     761      536618 :     WasmCodeRefScope code_ref_scope;
     762             :     std::vector<WasmCode*> code_vector =
     763             :         compile_scope->native_module()->AddCompiledCode(
     764      267933 :             VectorOf(results_to_publish));
     765      268459 :     compile_scope->compilation_state()->OnFinishedUnits(VectorOf(code_vector));
     766             :     results_to_publish.clear();
     767      188154 :   };
     768             : 
     769             :   bool compilation_failed = false;
     770             :   while (true) {
     771             :     // (asynchronous): Execute the compilation.
     772             :     WasmCompilationResult result = unit->ExecuteCompilation(
     773      477770 :         wasm_engine, &env.value(), wire_bytes, counters, &detected_features);
     774      329605 :     results_to_publish.emplace_back(std::move(result));
     775             : 
     776             :     // (synchronized): Publish the compilation result and get the next unit.
     777             :     {
     778      474317 :       BackgroundCompileScope compile_scope(token);
     779      513363 :       if (compile_scope.cancelled()) return true;
     780      331501 :       if (!results_to_publish.back().succeeded()) {
     781             :         // Compile error.
     782        7846 :         compile_scope.compilation_state()->SetError();
     783        7840 :         stop(compile_scope);
     784             :         compilation_failed = true;
     785        7849 :         break;
     786             :       }
     787             :       // Publish TurboFan units immediately to reduce peak memory consumption.
     788      323655 :       if (result.requested_tier == ExecutionTier::kTurbofan) {
     789      225211 :         publish_results(&compile_scope);
     790             :       }
     791             : 
     792             :       // Get next unit.
     793      323745 :       if (deadline < platform->MonotonicallyIncreasingTime()) {
     794             :         unit = nullptr;
     795             :       } else {
     796      323551 :         unit = compile_scope.compilation_state()->GetNextCompilationUnit(
     797             :             task_id, baseline_only);
     798             :       }
     799             : 
     800      323504 :       if (unit == nullptr) {
     801      178797 :         publish_results(&compile_scope);
     802      178802 :         stop(compile_scope);
     803      178800 :         return true;
     804             :       }
     805             :     }
     806             :   }
     807             :   // We only get here if compilation failed. Other exits return directly.
     808             :   DCHECK(compilation_failed);
     809             :   USE(compilation_failed);
     810        7848 :   token->Cancel();
     811        7843 :   return true;
     812             : }
     813             : 
     814        8925 : DecodeResult ValidateSingleFunction(const WasmModule* module, int func_index,
     815             :                                     Vector<const uint8_t> code,
     816             :                                     Counters* counters,
     817             :                                     AccountingAllocator* allocator,
     818             :                                     WasmFeatures enabled_features) {
     819        8925 :   const WasmFunction* func = &module->functions[func_index];
     820        8925 :   FunctionBody body{func->sig, func->code.offset(), code.start(), code.end()};
     821             :   DecodeResult result;
     822             :   {
     823        8925 :     auto time_counter = SELECT_WASM_COUNTER(counters, module->origin,
     824             :                                             wasm_decode, function_time);
     825             :     TimedHistogramScope wasm_decode_function_time_scope(time_counter);
     826        8925 :     WasmFeatures detected;
     827             :     result =
     828       17850 :         VerifyWasmCode(allocator, enabled_features, module, &detected, body);
     829             :   }
     830        8925 :   return result;
     831             : }
     832             : 
     833             : enum class OnlyLazyFunctions : bool { kNo = false, kYes = true };
     834             : 
     835        7821 : void ValidateSequentially(
     836             :     const WasmModule* module, NativeModule* native_module, Counters* counters,
     837             :     AccountingAllocator* allocator, ErrorThrower* thrower,
     838             :     OnlyLazyFunctions only_lazy_functions = OnlyLazyFunctions ::kNo) {
     839             :   DCHECK(!thrower->error());
     840        7821 :   uint32_t start = module->num_imported_functions;
     841        7821 :   uint32_t end = start + module->num_declared_functions;
     842        7821 :   auto enabled_features = native_module->enabled_features();
     843       25735 :   for (uint32_t func_index = start; func_index < end; func_index++) {
     844             :     // Skip non-lazy functions if requested.
     845        9045 :     if (only_lazy_functions == OnlyLazyFunctions::kYes &&
     846             :         !IsLazyCompilation(module, native_module, enabled_features,
     847             :                            func_index)) {
     848          72 :       continue;
     849             :     }
     850             :     ModuleWireBytes wire_bytes{native_module->wire_bytes()};
     851        8885 :     const WasmFunction* func = &module->functions[func_index];
     852        8885 :     Vector<const uint8_t> code = wire_bytes.GetFunctionBytes(func);
     853       17770 :     DecodeResult result = ValidateSingleFunction(
     854             :         module, func_index, code, counters, allocator, enabled_features);
     855             : 
     856        8885 :     if (result.failed()) {
     857        7741 :       WasmName name = wire_bytes.GetNameOrNull(func, module);
     858        7741 :       if (name.start() == nullptr) {
     859             :         thrower->CompileError(
     860        7397 :             "Compiling function #%d failed: %s @+%u", func->func_index,
     861        7397 :             result.error().message().c_str(), result.error().offset());
     862             :       } else {
     863         344 :         TruncatedUserString<> name(wire_bytes.GetNameOrNull(func, module));
     864             :         thrower->CompileError("Compiling function #%d:\"%.*s\" failed: %s @+%u",
     865         344 :                               func->func_index, name.length(), name.start(),
     866             :                               result.error().message().c_str(),
     867         344 :                               result.error().offset());
     868             :       }
     869             :     }
     870             :   }
     871        7821 : }
     872             : 
     873      144245 : void InitializeCompilationUnits(NativeModule* native_module) {
     874             :   // Set number of functions that must be compiled to consider the module fully
     875             :   // compiled.
     876             :   auto wasm_module = native_module->module();
     877      144245 :   int num_functions = wasm_module->num_declared_functions;
     878             :   DCHECK_IMPLIES(!native_module->enabled_features().compilation_hints,
     879             :                  wasm_module->num_lazy_compilation_hints == 0);
     880      144245 :   int num_lazy_functions = wasm_module->num_lazy_compilation_hints;
     881             :   CompilationStateImpl* compilation_state =
     882             :       Impl(native_module->compilation_state());
     883             :   compilation_state->SetNumberOfFunctionsToCompile(num_functions,
     884      144245 :                                                    num_lazy_functions);
     885             : 
     886             :   ModuleWireBytes wire_bytes(native_module->wire_bytes());
     887             :   const WasmModule* module = native_module->module();
     888             :   CompilationUnitBuilder builder(native_module);
     889      144246 :   uint32_t start = module->num_imported_functions;
     890      144246 :   uint32_t end = start + module->num_declared_functions;
     891      614330 :   for (uint32_t func_index = start; func_index < end; func_index++) {
     892      235042 :     if (IsLazyCompilation(module, native_module,
     893             :                           native_module->enabled_features(), func_index)) {
     894          16 :       native_module->UseLazyStub(func_index);
     895             :     } else {
     896      235026 :       builder.AddUnits(func_index);
     897             :     }
     898             :   }
     899      144246 :   builder.Commit();
     900      144247 : }
     901             : 
     902             : bool NeedsDeterministicCompile() {
     903     1384474 :   return FLAG_trace_wasm_decoder || FLAG_wasm_num_compilation_tasks <= 1;
     904             : }
     905             : 
     906      144746 : void CompileNativeModule(Isolate* isolate, ErrorThrower* thrower,
     907             :                          const WasmModule* wasm_module,
     908             :                          NativeModule* native_module) {
     909             :   ModuleWireBytes wire_bytes(native_module->wire_bytes());
     910             : 
     911      144746 :   if (FLAG_wasm_lazy_compilation ||
     912      143442 :       (FLAG_asm_wasm_lazy_compilation && wasm_module->origin == kAsmJsOrigin)) {
     913        2643 :     if (wasm_module->origin == kWasmOrigin) {
     914             :       // Validate wasm modules for lazy compilation. Don't validate asm.js
     915             :       // modules, they are valid by construction (otherwise a CHECK will fail
     916             :       // during lazy compilation).
     917             :       // TODO(clemensh): According to the spec, we can actually skip validation
     918             :       // at module creation time, and return a function that always traps at
     919             :       // (lazy) compilation time.
     920             :       ValidateSequentially(wasm_module, native_module, isolate->counters(),
     921         176 :                            isolate->allocator(), thrower);
     922             :       // On error: Return and leave the module in an unexecutable state.
     923        2827 :       if (thrower->error()) return;
     924             :     }
     925             :     native_module->set_lazy_compilation(true);
     926        2635 :     native_module->UseLazyStubs();
     927        2635 :     return;
     928             :   }
     929             : 
     930      142103 :   if (native_module->enabled_features().compilation_hints) {
     931             :     ValidateSequentially(wasm_module, native_module, isolate->counters(),
     932             :                          isolate->allocator(), thrower,
     933          64 :                          OnlyLazyFunctions::kYes);
     934             :     // On error: Return and leave the module in an unexecutable state.
     935          64 :     if (thrower->error()) return;
     936             :   }
     937             : 
     938             :   // Turn on the {CanonicalHandleScope} so that the background threads can
     939             :   // use the node cache.
     940      284190 :   CanonicalHandleScope canonical(isolate);
     941             : 
     942             :   auto* compilation_state = Impl(native_module->compilation_state());
     943             :   DCHECK_GE(kMaxInt, native_module->module()->num_declared_functions);
     944             : 
     945             :   // Install a callback to notify us once background compilation finished, or
     946             :   // compilation failed.
     947      284189 :   auto baseline_finished_semaphore = std::make_shared<base::Semaphore>(0);
     948             :   // The callback captures a shared ptr to the semaphore.
     949      284190 :   compilation_state->AddCallback(
     950      284186 :       [baseline_finished_semaphore](CompilationEvent event) {
     951      553158 :         if (event == CompilationEvent::kFinishedBaselineCompilation ||
     952      276579 :             event == CompilationEvent::kFailedCompilation) {
     953      142095 :           baseline_finished_semaphore->Signal();
     954             :         }
     955      142094 :       });
     956             : 
     957             :   // Initialize the compilation units and kick off background compile tasks.
     958      142095 :   InitializeCompilationUnits(native_module);
     959             : 
     960             :   // If tiering is disabled, the main thread can execute any unit (all of them
     961             :   // are part of initial compilation). Otherwise, just execute baseline units.
     962             :   bool is_tiering = compilation_state->compile_mode() == CompileMode::kTiering;
     963      142095 :   auto baseline_only = is_tiering ? kBaselineOnly : kBaselineOrTopTier;
     964             :   // The main threads contributes to the compilation, except if we need
     965             :   // deterministic compilation; in that case, the single background task will
     966             :   // execute all compilation.
     967      142095 :   if (!NeedsDeterministicCompile()) {
     968      512354 :     while (ExecuteCompilationUnits(
     969             :         compilation_state->background_compile_token(), isolate->counters(),
     970             :         kMainThreadTaskId, baseline_only)) {
     971             :       // Continue executing compilation units.
     972             :     }
     973             :   }
     974             : 
     975             :   // Now wait until baseline compilation finished.
     976      142095 :   baseline_finished_semaphore->Wait();
     977             : 
     978      142095 :   compilation_state->PublishDetectedFeatures(isolate);
     979             : 
     980      142095 :   if (compilation_state->failed()) {
     981             :     ValidateSequentially(wasm_module, native_module, isolate->counters(),
     982        7505 :                          isolate->allocator(), thrower);
     983        7505 :     CHECK(thrower->error());
     984             :   }
     985             : }
     986             : 
     987             : // The runnable task that performs compilations in the background.
     988      634952 : class BackgroundCompileTask : public CancelableTask {
     989             :  public:
     990             :   explicit BackgroundCompileTask(CancelableTaskManager* manager,
     991             :                                  std::shared_ptr<BackgroundCompileToken> token,
     992             :                                  std::shared_ptr<Counters> async_counters,
     993             :                                  int task_id)
     994             :       : CancelableTask(manager),
     995             :         token_(std::move(token)),
     996             :         async_counters_(std::move(async_counters)),
     997      423398 :         task_id_(task_id) {}
     998             : 
     999      211532 :   void RunInternal() override {
    1000      211532 :     ExecuteCompilationUnits(token_, async_counters_.get(), task_id_,
    1001      211532 :                             kBaselineOrTopTier);
    1002      211546 :   }
    1003             : 
    1004             :  private:
    1005             :   const std::shared_ptr<BackgroundCompileToken> token_;
    1006             :   const std::shared_ptr<Counters> async_counters_;
    1007             :   const int task_id_;
    1008             : };
    1009             : 
    1010             : }  // namespace
    1011             : 
    1012      144734 : std::shared_ptr<NativeModule> CompileToNativeModule(
    1013             :     Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
    1014             :     std::shared_ptr<const WasmModule> module, const ModuleWireBytes& wire_bytes,
    1015             :     Handle<FixedArray>* export_wrappers_out) {
    1016             :   const WasmModule* wasm_module = module.get();
    1017      144734 :   TimedHistogramScope wasm_compile_module_time_scope(SELECT_WASM_COUNTER(
    1018      144734 :       isolate->counters(), wasm_module->origin, wasm_compile, module_time));
    1019             : 
    1020             :   // Embedder usage count for declared shared memories.
    1021      144742 :   if (wasm_module->has_shared_memory) {
    1022        1485 :     isolate->CountUsage(v8::Isolate::UseCounterFeature::kWasmSharedMemory);
    1023             :   }
    1024      144745 :   int export_wrapper_size = static_cast<int>(module->num_exported_functions);
    1025             : 
    1026             :   // TODO(wasm): only save the sections necessary to deserialize a
    1027             :   // {WasmModule}. E.g. function bodies could be omitted.
    1028             :   OwnedVector<uint8_t> wire_bytes_copy =
    1029      144745 :       OwnedVector<uint8_t>::Of(wire_bytes.module_bytes());
    1030             : 
    1031             :   // Create and compile the native module.
    1032             :   size_t code_size_estimate =
    1033      144745 :       wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module.get());
    1034             : 
    1035             :   // Create a new {NativeModule} first.
    1036             :   auto native_module = isolate->wasm_engine()->NewNativeModule(
    1037             :       isolate, enabled, code_size_estimate,
    1038      289488 :       wasm::NativeModule::kCanAllocateMoreMemory, std::move(module));
    1039      289492 :   native_module->SetWireBytes(std::move(wire_bytes_copy));
    1040      144746 :   native_module->SetRuntimeStubs(isolate);
    1041             : 
    1042      144746 :   CompileNativeModule(isolate, thrower, wasm_module, native_module.get());
    1043      144745 :   if (thrower->error()) return {};
    1044             : 
    1045             :   // Compile JS->wasm wrappers for exported functions.
    1046             :   *export_wrappers_out = isolate->factory()->NewFixedArray(
    1047      137224 :       export_wrapper_size, AllocationType::kOld);
    1048             :   CompileJsToWasmWrappers(isolate, native_module->module(),
    1049      137225 :                           *export_wrappers_out);
    1050             : 
    1051             :   // Log the code within the generated module for profiling.
    1052      137225 :   native_module->LogWasmCodes(isolate);
    1053             : 
    1054             :   return native_module;
    1055             : }
    1056             : 
    1057        2647 : AsyncCompileJob::AsyncCompileJob(
    1058             :     Isolate* isolate, const WasmFeatures& enabled,
    1059             :     std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
    1060             :     std::shared_ptr<CompilationResultResolver> resolver)
    1061             :     : isolate_(isolate),
    1062             :       enabled_features_(enabled),
    1063             :       bytes_copy_(std::move(bytes_copy)),
    1064             :       wire_bytes_(bytes_copy_.get(), bytes_copy_.get() + length),
    1065       10588 :       resolver_(std::move(resolver)) {
    1066             :   v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
    1067        2647 :   v8::Platform* platform = V8::GetCurrentPlatform();
    1068        5294 :   foreground_task_runner_ = platform->GetForegroundTaskRunner(v8_isolate);
    1069             :   native_context_ =
    1070        2647 :       isolate->global_handles()->Create(context->native_context());
    1071             :   DCHECK(native_context_->IsNativeContext());
    1072        2647 : }
    1073             : 
    1074        2195 : void AsyncCompileJob::Start() {
    1075        4390 :   DoAsync<DecodeModule>(isolate_->counters());  // --
    1076        2195 : }
    1077             : 
    1078          72 : void AsyncCompileJob::Abort() {
    1079             :   // Removing this job will trigger the destructor, which will cancel all
    1080             :   // compilation.
    1081          72 :   isolate_->wasm_engine()->RemoveCompileJob(this);
    1082          72 : }
    1083             : 
    1084        1808 : class AsyncStreamingProcessor final : public StreamingProcessor {
    1085             :  public:
    1086             :   explicit AsyncStreamingProcessor(AsyncCompileJob* job);
    1087             : 
    1088             :   bool ProcessModuleHeader(Vector<const uint8_t> bytes,
    1089             :                            uint32_t offset) override;
    1090             : 
    1091             :   bool ProcessSection(SectionCode section_code, Vector<const uint8_t> bytes,
    1092             :                       uint32_t offset) override;
    1093             : 
    1094             :   bool ProcessCodeSectionHeader(int functions_count, uint32_t offset,
    1095             :                                 std::shared_ptr<WireBytesStorage>) override;
    1096             : 
    1097             :   bool ProcessFunctionBody(Vector<const uint8_t> bytes,
    1098             :                            uint32_t offset) override;
    1099             : 
    1100             :   void OnFinishedChunk() override;
    1101             : 
    1102             :   void OnFinishedStream(OwnedVector<uint8_t> bytes) override;
    1103             : 
    1104             :   void OnError(const WasmError&) override;
    1105             : 
    1106             :   void OnAbort() override;
    1107             : 
    1108             :   bool Deserialize(Vector<const uint8_t> wire_bytes,
    1109             :                    Vector<const uint8_t> module_bytes) override;
    1110             : 
    1111             :  private:
    1112             :   // Finishes the AsyncCompileJob with an error.
    1113             :   void FinishAsyncCompileJobWithError(const WasmError&);
    1114             : 
    1115             :   void CommitCompilationUnits();
    1116             : 
    1117             :   ModuleDecoder decoder_;
    1118             :   AsyncCompileJob* job_;
    1119             :   std::unique_ptr<CompilationUnitBuilder> compilation_unit_builder_;
    1120             :   int num_functions_ = 0;
    1121             : };
    1122             : 
    1123         452 : std::shared_ptr<StreamingDecoder> AsyncCompileJob::CreateStreamingDecoder() {
    1124             :   DCHECK_NULL(stream_);
    1125        1356 :   stream_.reset(
    1126        1808 :       new StreamingDecoder(base::make_unique<AsyncStreamingProcessor>(this)));
    1127         452 :   return stream_;
    1128             : }
    1129             : 
    1130        7941 : AsyncCompileJob::~AsyncCompileJob() {
    1131             :   // Note: This destructor always runs on the foreground thread of the isolate.
    1132        2647 :   background_task_manager_.CancelAndWait();
    1133             :   // If the runtime objects were not created yet, then initial compilation did
    1134             :   // not finish yet. In this case we can abort compilation.
    1135        5087 :   if (native_module_ && module_object_.is_null()) {
    1136         172 :     Impl(native_module_->compilation_state())->AbortCompilation();
    1137             :   }
    1138             :   // Tell the streaming decoder that the AsyncCompileJob is not available
    1139             :   // anymore.
    1140             :   // TODO(ahaas): Is this notification really necessary? Check
    1141             :   // https://crbug.com/888170.
    1142        2647 :   if (stream_) stream_->NotifyCompilationEnded();
    1143             :   CancelPendingForegroundTask();
    1144        2647 :   isolate_->global_handles()->Destroy(native_context_.location());
    1145        2647 :   if (!module_object_.is_null()) {
    1146        2268 :     isolate_->global_handles()->Destroy(module_object_.location());
    1147             :   }
    1148        2647 : }
    1149             : 
    1150        2436 : void AsyncCompileJob::CreateNativeModule(
    1151             :     std::shared_ptr<const WasmModule> module) {
    1152             :   // Embedder usage count for declared shared memories.
    1153        2436 :   if (module->has_shared_memory) {
    1154           0 :     isolate_->CountUsage(v8::Isolate::UseCounterFeature::kWasmSharedMemory);
    1155             :   }
    1156             : 
    1157             :   // TODO(wasm): Improve efficiency of storing module wire bytes. Only store
    1158             :   // relevant sections, not function bodies
    1159             : 
    1160             :   // Create the module object and populate with compiled functions and
    1161             :   // information needed at instantiation time.
    1162             :   // TODO(clemensh): For the same module (same bytes / same hash), we should
    1163             :   // only have one {WasmModuleObject}. Otherwise, we might only set
    1164             :   // breakpoints on a (potentially empty) subset of the instances.
    1165             :   // Create the module object.
    1166             : 
    1167             :   size_t code_size_estimate =
    1168        2436 :       wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module.get());
    1169        7308 :   native_module_ = isolate_->wasm_engine()->NewNativeModule(
    1170             :       isolate_, enabled_features_, code_size_estimate,
    1171             :       wasm::NativeModule::kCanAllocateMoreMemory, std::move(module));
    1172        4872 :   native_module_->SetWireBytes({std::move(bytes_copy_), wire_bytes_.length()});
    1173        2436 :   native_module_->SetRuntimeStubs(isolate_);
    1174             : 
    1175        2436 :   if (stream_) stream_->NotifyNativeModuleCreated(native_module_);
    1176        2436 : }
    1177             : 
    1178        2264 : void AsyncCompileJob::PrepareRuntimeObjects() {
    1179             :   // Create heap objects for script and module bytes to be stored in the
    1180             :   // module object. Asm.js is not compiled asynchronously.
    1181             :   const WasmModule* module = native_module_->module();
    1182             :   Handle<Script> script =
    1183        2264 :       CreateWasmScript(isolate_, wire_bytes_, module->source_map_url);
    1184             : 
    1185             :   size_t code_size_estimate =
    1186        2264 :       wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module);
    1187             :   Handle<WasmModuleObject> module_object = WasmModuleObject::New(
    1188        4528 :       isolate_, native_module_, script, code_size_estimate);
    1189             : 
    1190        4528 :   module_object_ = isolate_->global_handles()->Create(*module_object);
    1191        2264 : }
    1192             : 
    1193             : // This function assumes that it is executed in a HandleScope, and that a
    1194             : // context is set on the isolate.
    1195        2268 : void AsyncCompileJob::FinishCompile() {
    1196             :   bool is_after_deserialization = !module_object_.is_null();
    1197        2268 :   if (!is_after_deserialization) {
    1198        2264 :     PrepareRuntimeObjects();
    1199             :   }
    1200             :   DCHECK(!isolate_->context().is_null());
    1201             :   // Finish the wasm script now and make it public to the debugger.
    1202        2268 :   Handle<Script> script(module_object_->script(), isolate_);
    1203        4536 :   if (script->type() == Script::TYPE_WASM &&
    1204             :       module_object_->module()->source_map_url.size() != 0) {
    1205           0 :     MaybeHandle<String> src_map_str = isolate_->factory()->NewStringFromUtf8(
    1206             :         CStrVector(module_object_->module()->source_map_url.c_str()),
    1207           0 :         AllocationType::kOld);
    1208           0 :     script->set_source_mapping_url(*src_map_str.ToHandleChecked());
    1209             :   }
    1210        2268 :   isolate_->debug()->OnAfterCompile(script);
    1211             : 
    1212             :   // We can only update the feature counts once the entire compile is done.
    1213             :   auto compilation_state =
    1214             :       Impl(module_object_->native_module()->compilation_state());
    1215        2268 :   compilation_state->PublishDetectedFeatures(isolate_);
    1216             : 
    1217             :   // TODO(bbudge) Allow deserialization without wrapper compilation, so we can
    1218             :   // just compile wrappers here.
    1219        2268 :   if (!is_after_deserialization) {
    1220             :     // TODO(wasm): compiling wrappers should be made async.
    1221        2264 :     CompileWrappers();
    1222             :   }
    1223        2268 :   FinishModule();
    1224        2268 : }
    1225             : 
    1226         221 : void AsyncCompileJob::DecodeFailed(const WasmError& error) {
    1227         442 :   ErrorThrower thrower(isolate_, "WebAssembly.compile()");
    1228             :   thrower.CompileFailed(error);
    1229             :   // {job} keeps the {this} pointer alive.
    1230             :   std::shared_ptr<AsyncCompileJob> job =
    1231         442 :       isolate_->wasm_engine()->RemoveCompileJob(this);
    1232         221 :   resolver_->OnCompilationFailed(thrower.Reify());
    1233         221 : }
    1234             : 
    1235          76 : void AsyncCompileJob::AsyncCompileFailed() {
    1236         152 :   ErrorThrower thrower(isolate_, "WebAssembly.compile()");
    1237             :   ValidateSequentially(native_module_->module(), native_module_.get(),
    1238          76 :                        isolate_->counters(), isolate_->allocator(), &thrower);
    1239             :   DCHECK(thrower.error());
    1240             :   // {job} keeps the {this} pointer alive.
    1241             :   std::shared_ptr<AsyncCompileJob> job =
    1242         152 :       isolate_->wasm_engine()->RemoveCompileJob(this);
    1243          76 :   resolver_->OnCompilationFailed(thrower.Reify());
    1244          76 : }
    1245             : 
    1246           0 : void AsyncCompileJob::AsyncCompileSucceeded(Handle<WasmModuleObject> result) {
    1247        2268 :   resolver_->OnCompilationSucceeded(result);
    1248           0 : }
    1249             : 
    1250             : class AsyncCompileJob::CompilationStateCallback {
    1251             :  public:
    1252             :   explicit CompilationStateCallback(AsyncCompileJob* job) : job_(job) {}
    1253             : 
    1254        4555 :   void operator()(CompilationEvent event) {
    1255             :     // This callback is only being called from a foreground task.
    1256        4555 :     switch (event) {
    1257             :       case CompilationEvent::kFinishedBaselineCompilation:
    1258             :         DCHECK(!last_event_.has_value());
    1259        4480 :         if (job_->DecrementAndCheckFinisherCount()) {
    1260        2199 :           job_->DoSync<CompileFinished>();
    1261             :         }
    1262             :         break;
    1263             :       case CompilationEvent::kFinishedTopTierCompilation:
    1264             :         DCHECK_EQ(CompilationEvent::kFinishedBaselineCompilation, last_event_);
    1265             :         // At this point, the job will already be gone, thus do not access it
    1266             :         // here.
    1267             :         break;
    1268             :       case CompilationEvent::kFailedCompilation: {
    1269             :         DCHECK(!last_event_.has_value());
    1270         160 :         if (job_->DecrementAndCheckFinisherCount()) {
    1271          59 :           job_->DoSync<CompileFailed>();
    1272             :         }
    1273             :         break;
    1274             :       }
    1275             :       default:
    1276           0 :         UNREACHABLE();
    1277             :     }
    1278             : #ifdef DEBUG
    1279             :     last_event_ = event;
    1280             : #endif
    1281        4555 :   }
    1282             : 
    1283             :  private:
    1284             :   AsyncCompileJob* job_;
    1285             : #ifdef DEBUG
    1286             :   // This will be modified by different threads, but they externally
    1287             :   // synchronize, so no explicit synchronization (currently) needed here.
    1288             :   base::Optional<CompilationEvent> last_event_;
    1289             : #endif
    1290             : };
    1291             : 
    1292             : // A closure to run a compilation step (either as foreground or background
    1293             : // task) and schedule the next step(s), if any.
    1294        7084 : class AsyncCompileJob::CompileStep {
    1295             :  public:
    1296        7083 :   virtual ~CompileStep() = default;
    1297             : 
    1298        7082 :   void Run(AsyncCompileJob* job, bool on_foreground) {
    1299        7082 :     if (on_foreground) {
    1300        4887 :       HandleScope scope(job->isolate_);
    1301        4887 :       SaveAndSwitchContext saved_context(job->isolate_, *job->native_context_);
    1302        4887 :       RunInForeground(job);
    1303             :     } else {
    1304        2195 :       RunInBackground(job);
    1305             :     }
    1306        7082 :   }
    1307             : 
    1308           0 :   virtual void RunInForeground(AsyncCompileJob*) { UNREACHABLE(); }
    1309           0 :   virtual void RunInBackground(AsyncCompileJob*) { UNREACHABLE(); }
    1310             : };
    1311             : 
    1312             : class AsyncCompileJob::CompileTask : public CancelableTask {
    1313             :  public:
    1314             :   CompileTask(AsyncCompileJob* job, bool on_foreground)
    1315             :       // We only manage the background tasks with the {CancelableTaskManager} of
    1316             :       // the {AsyncCompileJob}. Foreground tasks are managed by the system's
    1317             :       // {CancelableTaskManager}. Background tasks cannot spawn tasks managed by
    1318             :       // their own task manager.
    1319        4889 :       : CancelableTask(on_foreground ? job->isolate_->cancelable_task_manager()
    1320             :                                      : &job->background_task_manager_),
    1321             :         job_(job),
    1322        9279 :         on_foreground_(on_foreground) {}
    1323             : 
    1324       21250 :   ~CompileTask() override {
    1325        7083 :     if (job_ != nullptr && on_foreground_) ResetPendingForegroundTask();
    1326       14167 :   }
    1327             : 
    1328        7082 :   void RunInternal() final {
    1329        7082 :     if (!job_) return;
    1330        7082 :     if (on_foreground_) ResetPendingForegroundTask();
    1331       14164 :     job_->step_->Run(job_, on_foreground_);
    1332             :     // After execution, reset {job_} such that we don't try to reset the pending
    1333             :     // foreground task when the task is deleted.
    1334        7082 :     job_ = nullptr;
    1335             :   }
    1336             : 
    1337             :   void Cancel() {
    1338             :     DCHECK_NOT_NULL(job_);
    1339           2 :     job_ = nullptr;
    1340             :   }
    1341             : 
    1342             :  private:
    1343             :   // {job_} will be cleared to cancel a pending task.
    1344             :   AsyncCompileJob* job_;
    1345             :   bool on_foreground_;
    1346             : 
    1347             :   void ResetPendingForegroundTask() const {
    1348             :     DCHECK_EQ(this, job_->pending_foreground_task_);
    1349        4887 :     job_->pending_foreground_task_ = nullptr;
    1350             :   }
    1351             : };
    1352             : 
    1353        4633 : void AsyncCompileJob::StartForegroundTask() {
    1354             :   DCHECK_NULL(pending_foreground_task_);
    1355             : 
    1356        4633 :   auto new_task = base::make_unique<CompileTask>(this, true);
    1357        4633 :   pending_foreground_task_ = new_task.get();
    1358       13899 :   foreground_task_runner_->PostTask(std::move(new_task));
    1359        4633 : }
    1360             : 
    1361         256 : void AsyncCompileJob::ExecuteForegroundTaskImmediately() {
    1362             :   DCHECK_NULL(pending_foreground_task_);
    1363             : 
    1364         256 :   auto new_task = base::make_unique<CompileTask>(this, true);
    1365         256 :   pending_foreground_task_ = new_task.get();
    1366         256 :   new_task->Run();
    1367         256 : }
    1368             : 
    1369           0 : void AsyncCompileJob::CancelPendingForegroundTask() {
    1370        2647 :   if (!pending_foreground_task_) return;
    1371             :   pending_foreground_task_->Cancel();
    1372           2 :   pending_foreground_task_ = nullptr;
    1373             : }
    1374             : 
    1375        2195 : void AsyncCompileJob::StartBackgroundTask() {
    1376        2195 :   auto task = base::make_unique<CompileTask>(this, false);
    1377             : 
    1378             :   // If --wasm-num-compilation-tasks=0 is passed, do only spawn foreground
    1379             :   // tasks. This is used to make timing deterministic.
    1380        2195 :   if (FLAG_wasm_num_compilation_tasks > 0) {
    1381        6585 :     V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
    1382             :   } else {
    1383           0 :     foreground_task_runner_->PostTask(std::move(task));
    1384             :   }
    1385        2195 : }
    1386             : 
    1387             : template <typename Step,
    1388             :           AsyncCompileJob::UseExistingForegroundTask use_existing_fg_task,
    1389             :           typename... Args>
    1390          64 : void AsyncCompileJob::DoSync(Args&&... args) {
    1391        4633 :   NextStep<Step>(std::forward<Args>(args)...);
    1392          64 :   if (use_existing_fg_task && pending_foreground_task_ != nullptr) return;
    1393        4633 :   StartForegroundTask();
    1394             : }
    1395             : 
    1396             : template <typename Step, typename... Args>
    1397             : void AsyncCompileJob::DoImmediately(Args&&... args) {
    1398         256 :   NextStep<Step>(std::forward<Args>(args)...);
    1399         256 :   ExecuteForegroundTaskImmediately();
    1400             : }
    1401             : 
    1402             : template <typename Step, typename... Args>
    1403             : void AsyncCompileJob::DoAsync(Args&&... args) {
    1404        2195 :   NextStep<Step>(std::forward<Args>(args)...);
    1405        2195 :   StartBackgroundTask();
    1406             : }
    1407             : 
    1408             : template <typename Step, typename... Args>
    1409        7084 : void AsyncCompileJob::NextStep(Args&&... args) {
    1410        7561 :   step_.reset(new Step(std::forward<Args>(args)...));
    1411        7084 : }
    1412             : 
    1413             : //==========================================================================
    1414             : // Step 1: (async) Decode the module.
    1415             : //==========================================================================
    1416        4390 : class AsyncCompileJob::DecodeModule : public AsyncCompileJob::CompileStep {
    1417             :  public:
    1418        2195 :   explicit DecodeModule(Counters* counters) : counters_(counters) {}
    1419             : 
    1420        2195 :   void RunInBackground(AsyncCompileJob* job) override {
    1421        2195 :     ModuleResult result;
    1422             :     {
    1423             :       DisallowHandleAllocation no_handle;
    1424             :       DisallowHeapAllocation no_allocation;
    1425             :       // Decode the module bytes.
    1426             :       TRACE_COMPILE("(1) Decoding module...\n");
    1427        6585 :       TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
    1428             :                    "AsyncCompileJob::DecodeModule");
    1429        4390 :       result = DecodeWasmModule(
    1430             :           job->enabled_features_, job->wire_bytes_.start(),
    1431        2195 :           job->wire_bytes_.end(), false, kWasmOrigin, counters_,
    1432        2195 :           job->isolate()->wasm_engine()->allocator());
    1433             : 
    1434             :       // Validate lazy functions here.
    1435        2195 :       auto enabled_features = job->enabled_features_;
    1436        2195 :       if (enabled_features.compilation_hints && result.ok()) {
    1437             :         const WasmModule* module = result.value().get();
    1438             :         auto allocator = job->isolate()->wasm_engine()->allocator();
    1439          24 :         int start = module->num_imported_functions;
    1440          24 :         int end = start + module->num_declared_functions;
    1441             : 
    1442          40 :         for (int func_index = start; func_index < end; func_index++) {
    1443          16 :           const WasmFunction* func = &module->functions[func_index];
    1444          16 :           Vector<const uint8_t> code = job->wire_bytes_.GetFunctionBytes(func);
    1445             : 
    1446          32 :           if (IsLazyCompilation(module, enabled_features, func_index)) {
    1447             :             DecodeResult function_result =
    1448          32 :                 ValidateSingleFunction(module, func_index, code, counters_,
    1449             :                                        allocator, enabled_features);
    1450          16 :             if (function_result.failed()) {
    1451          16 :               result = ModuleResult(function_result.error());
    1452             :               break;
    1453             :             }
    1454             :           }
    1455             :         }
    1456             :       }
    1457             :     }
    1458        2195 :     if (result.failed()) {
    1459             :       // Decoding failure; reject the promise and clean up.
    1460             :       job->DoSync<DecodeFail>(std::move(result).error());
    1461             :     } else {
    1462             :       // Decode passed.
    1463        4308 :       job->DoSync<PrepareAndStartCompile>(std::move(result).value(), true);
    1464             :     }
    1465        2195 :   }
    1466             : 
    1467             :  private:
    1468             :   Counters* const counters_;
    1469             : };
    1470             : 
    1471             : //==========================================================================
    1472             : // Step 1b: (sync) Fail decoding the module.
    1473             : //==========================================================================
    1474         442 : class AsyncCompileJob::DecodeFail : public CompileStep {
    1475             :  public:
    1476         221 :   explicit DecodeFail(WasmError error) : error_(std::move(error)) {}
    1477             : 
    1478             :  private:
    1479             :   WasmError error_;
    1480             : 
    1481         221 :   void RunInForeground(AsyncCompileJob* job) override {
    1482             :     TRACE_COMPILE("(1b) Decoding failed.\n");
    1483             :     // {job_} is deleted in DecodeFailed, therefore the {return}.
    1484         221 :     return job->DecodeFailed(error_);
    1485             :   }
    1486             : };
    1487             : 
    1488             : //==========================================================================
    1489             : // Step 2 (sync): Create heap-allocated data and start compile.
    1490             : //==========================================================================
    1491        4819 : class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
    1492             :  public:
    1493             :   PrepareAndStartCompile(std::shared_ptr<const WasmModule> module,
    1494             :                          bool start_compilation)
    1495        4820 :       : module_(std::move(module)), start_compilation_(start_compilation) {}
    1496             : 
    1497             :  private:
    1498             :   std::shared_ptr<const WasmModule> module_;
    1499             :   bool start_compilation_;
    1500             : 
    1501        2408 :   void RunInForeground(AsyncCompileJob* job) override {
    1502             :     TRACE_COMPILE("(2) Prepare and start compile...\n");
    1503             : 
    1504             :     // Make sure all compilation tasks stopped running. Decoding (async step)
    1505             :     // is done.
    1506        2408 :     job->background_task_manager_.CancelAndWait();
    1507             : 
    1508        4816 :     job->CreateNativeModule(module_);
    1509             : 
    1510             :     CompilationStateImpl* compilation_state =
    1511             :         Impl(job->native_module_->compilation_state());
    1512        4816 :     compilation_state->AddCallback(CompilationStateCallback{job});
    1513        2408 :     if (start_compilation_) {
    1514             :       // TODO(ahaas): Try to remove the {start_compilation_} check when
    1515             :       // streaming decoding is done in the background. If
    1516             :       // InitializeCompilationUnits always returns 0 for streaming compilation,
    1517             :       // then DoAsync would do the same as NextStep already.
    1518             : 
    1519             :       // Add compilation units and kick off compilation.
    1520        2152 :       InitializeCompilationUnits(job->native_module_.get());
    1521             :     }
    1522        2408 :   }
    1523             : };
    1524             : 
    1525             : //==========================================================================
    1526             : // Step 3a (sync): Compilation failed.
    1527             : //==========================================================================
    1528         177 : class AsyncCompileJob::CompileFailed : public CompileStep {
    1529             :  private:
    1530          59 :   void RunInForeground(AsyncCompileJob* job) override {
    1531             :     TRACE_COMPILE("(3a) Compilation failed\n");
    1532             :     DCHECK(job->native_module_->compilation_state()->failed());
    1533             : 
    1534             :     // {job_} is deleted in AsyncCompileFailed, therefore the {return}.
    1535          59 :     return job->AsyncCompileFailed();
    1536             :   }
    1537             : };
    1538             : 
    1539             : namespace {
    1540        2199 : class SampleTopTierCodeSizeCallback {
    1541             :  public:
    1542             :   explicit SampleTopTierCodeSizeCallback(
    1543             :       std::weak_ptr<NativeModule> native_module)
    1544             :       : native_module_(std::move(native_module)) {}
    1545             : 
    1546         171 :   void operator()(CompilationEvent event) {
    1547             :     // This callback is registered after baseline compilation finished, so the
    1548             :     // only possible event to follow is {kFinishedTopTierCompilation}.
    1549             :     DCHECK_EQ(CompilationEvent::kFinishedTopTierCompilation, event);
    1550         171 :     if (std::shared_ptr<NativeModule> native_module = native_module_.lock()) {
    1551             :       native_module->engine()->SampleTopTierCodeSizeInAllIsolates(
    1552         171 :           native_module);
    1553             :     }
    1554         171 :   }
    1555             : 
    1556             :  private:
    1557             :   std::weak_ptr<NativeModule> native_module_;
    1558             : };
    1559             : }  // namespace
    1560             : 
    1561             : //==========================================================================
    1562             : // Step 3b (sync): Compilation finished.
    1563             : //==========================================================================
    1564        6595 : class AsyncCompileJob::CompileFinished : public CompileStep {
    1565             :  private:
    1566        2199 :   void RunInForeground(AsyncCompileJob* job) override {
    1567             :     TRACE_COMPILE("(3b) Compilation finished\n");
    1568             :     DCHECK(!job->native_module_->compilation_state()->failed());
    1569             :     // Sample the generated code size when baseline compilation finished.
    1570        2199 :     job->native_module_->SampleCodeSize(job->isolate_->counters(),
    1571        2199 :                                         NativeModule::kAfterBaseline);
    1572             :     // Also, set a callback to sample the code size after top-tier compilation
    1573             :     // finished. This callback will *not* keep the NativeModule alive.
    1574        4398 :     job->native_module_->compilation_state()->AddCallback(
    1575        2199 :         SampleTopTierCodeSizeCallback{job->native_module_});
    1576             :     // Then finalize and publish the generated module.
    1577        2199 :     job->FinishCompile();
    1578        2199 :   }
    1579             : };
    1580             : 
    1581        2264 : void AsyncCompileJob::CompileWrappers() {
    1582             :   // TODO(wasm): Compile all wrappers here, including the start function wrapper
    1583             :   // and the wrappers for the function table elements.
    1584             :   TRACE_COMPILE("(5) Compile wrappers...\n");
    1585             :   // Compile JS->wasm wrappers for exported functions.
    1586        2264 :   CompileJsToWasmWrappers(isolate_, module_object_->native_module()->module(),
    1587        4528 :                           handle(module_object_->export_wrappers(), isolate_));
    1588        2264 : }
    1589             : 
    1590        2268 : void AsyncCompileJob::FinishModule() {
    1591             :   TRACE_COMPILE("(6) Finish module...\n");
    1592             :   AsyncCompileSucceeded(module_object_);
    1593        2268 :   isolate_->wasm_engine()->RemoveCompileJob(this);
    1594        2268 : }
    1595             : 
    1596           0 : AsyncStreamingProcessor::AsyncStreamingProcessor(AsyncCompileJob* job)
    1597             :     : decoder_(job->enabled_features_),
    1598             :       job_(job),
    1599         904 :       compilation_unit_builder_(nullptr) {}
    1600             : 
    1601         180 : void AsyncStreamingProcessor::FinishAsyncCompileJobWithError(
    1602             :     const WasmError& error) {
    1603             :   DCHECK(error.has_error());
    1604             :   // Make sure all background tasks stopped executing before we change the state
    1605             :   // of the AsyncCompileJob to DecodeFail.
    1606         180 :   job_->background_task_manager_.CancelAndWait();
    1607             : 
    1608             :   // Check if there is already a CompiledModule, in which case we have to clean
    1609             :   // up the CompilationStateImpl as well.
    1610         180 :   if (job_->native_module_) {
    1611          64 :     Impl(job_->native_module_->compilation_state())->AbortCompilation();
    1612             : 
    1613          64 :     job_->DoSync<AsyncCompileJob::DecodeFail,
    1614          64 :                  AsyncCompileJob::kUseExistingForegroundTask>(error);
    1615             : 
    1616             :     // Clear the {compilation_unit_builder_} if it exists. This is needed
    1617             :     // because there is a check in the destructor of the
    1618             :     // {CompilationUnitBuilder} that it is empty.
    1619          64 :     if (compilation_unit_builder_) compilation_unit_builder_->Clear();
    1620             :   } else {
    1621             :     job_->DoSync<AsyncCompileJob::DecodeFail>(error);
    1622             :   }
    1623         180 : }
    1624             : 
    1625             : // Process the module header.
    1626         408 : bool AsyncStreamingProcessor::ProcessModuleHeader(Vector<const uint8_t> bytes,
    1627             :                                                   uint32_t offset) {
    1628             :   TRACE_STREAMING("Process module header...\n");
    1629         408 :   decoder_.StartDecoding(job_->isolate()->counters(),
    1630         816 :                          job_->isolate()->wasm_engine()->allocator());
    1631         408 :   decoder_.DecodeModuleHeader(bytes, offset);
    1632         408 :   if (!decoder_.ok()) {
    1633          16 :     FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());
    1634          16 :     return false;
    1635             :   }
    1636             :   return true;
    1637             : }
    1638             : 
    1639             : // Process all sections except for the code section.
    1640        1060 : bool AsyncStreamingProcessor::ProcessSection(SectionCode section_code,
    1641             :                                              Vector<const uint8_t> bytes,
    1642             :                                              uint32_t offset) {
    1643             :   TRACE_STREAMING("Process section %d ...\n", section_code);
    1644        1060 :   if (compilation_unit_builder_) {
    1645             :     // We reached a section after the code section, we do not need the
    1646             :     // compilation_unit_builder_ anymore.
    1647             :     CommitCompilationUnits();
    1648             :     compilation_unit_builder_.reset();
    1649             :   }
    1650        1060 :   if (section_code == SectionCode::kUnknownSectionCode) {
    1651             :     Decoder decoder(bytes, offset);
    1652             :     section_code = ModuleDecoder::IdentifyUnknownSection(
    1653         136 :         decoder, bytes.start() + bytes.length());
    1654         136 :     if (section_code == SectionCode::kUnknownSectionCode) {
    1655             :       // Skip unknown sections that we do not know how to handle.
    1656             :       return true;
    1657             :     }
    1658             :     // Remove the unknown section tag from the payload bytes.
    1659         136 :     offset += decoder.position();
    1660         136 :     bytes = bytes.SubVector(decoder.position(), bytes.size());
    1661             :   }
    1662             :   constexpr bool verify_functions = false;
    1663        1060 :   decoder_.DecodeSection(section_code, bytes, offset, verify_functions);
    1664        1060 :   if (!decoder_.ok()) {
    1665          28 :     FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());
    1666          28 :     return false;
    1667             :   }
    1668             :   return true;
    1669             : }
    1670             : 
    1671             : // Start the code section.
    1672         268 : bool AsyncStreamingProcessor::ProcessCodeSectionHeader(
    1673             :     int functions_count, uint32_t offset,
    1674             :     std::shared_ptr<WireBytesStorage> wire_bytes_storage) {
    1675             :   TRACE_STREAMING("Start the code section with %d functions...\n",
    1676             :                   functions_count);
    1677         268 :   if (!decoder_.CheckFunctionsCount(static_cast<uint32_t>(functions_count),
    1678             :                                     offset)) {
    1679          12 :     FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());
    1680          12 :     return false;
    1681             :   }
    1682             :   // Execute the PrepareAndStartCompile step immediately and not in a separate
    1683             :   // task.
    1684         768 :   job_->DoImmediately<AsyncCompileJob::PrepareAndStartCompile>(
    1685         256 :       decoder_.shared_module(), false);
    1686         256 :   auto* compilation_state = Impl(job_->native_module_->compilation_state());
    1687         512 :   compilation_state->SetWireBytesStorage(std::move(wire_bytes_storage));
    1688             : 
    1689             :   // Set number of functions that must be compiled to consider the module fully
    1690             :   // compiled.
    1691         256 :   auto wasm_module = job_->native_module_->module();
    1692         256 :   int num_functions = wasm_module->num_declared_functions;
    1693             :   DCHECK_IMPLIES(!job_->native_module_->enabled_features().compilation_hints,
    1694             :                  wasm_module->num_lazy_compilation_hints == 0);
    1695         256 :   int num_lazy_functions = wasm_module->num_lazy_compilation_hints;
    1696             :   compilation_state->SetNumberOfFunctionsToCompile(num_functions,
    1697         256 :                                                    num_lazy_functions);
    1698             : 
    1699             :   // Set outstanding_finishers_ to 2, because both the AsyncCompileJob and the
    1700             :   // AsyncStreamingProcessor have to finish.
    1701         256 :   job_->outstanding_finishers_.store(2);
    1702         256 :   compilation_unit_builder_.reset(
    1703         256 :       new CompilationUnitBuilder(job_->native_module_.get()));
    1704             :   return true;
    1705             : }
    1706             : 
    1707             : // Process a function body.
    1708         508 : bool AsyncStreamingProcessor::ProcessFunctionBody(Vector<const uint8_t> bytes,
    1709             :                                                   uint32_t offset) {
    1710             :   TRACE_STREAMING("Process function body %d ...\n", num_functions_);
    1711             : 
    1712        1016 :   decoder_.DecodeFunctionBody(
    1713        1016 :       num_functions_, static_cast<uint32_t>(bytes.length()), offset, false);
    1714             : 
    1715         508 :   NativeModule* native_module = job_->native_module_.get();
    1716             :   const WasmModule* module = native_module->module();
    1717         508 :   auto enabled_features = native_module->enabled_features();
    1718             :   uint32_t func_index =
    1719        1016 :       num_functions_ + decoder_.module()->num_imported_functions;
    1720             : 
    1721         508 :   if (IsLazyCompilation(module, native_module, enabled_features, func_index)) {
    1722             :     Counters* counters = Impl(native_module->compilation_state())->counters();
    1723             :     AccountingAllocator* allocator = native_module->engine()->allocator();
    1724             : 
    1725             :     // The native module does not own the wire bytes until {SetWireBytes} is
    1726             :     // called in {OnFinishedStream}. Validation must use {bytes} parameter.
    1727          48 :     DecodeResult result = ValidateSingleFunction(
    1728             :         module, func_index, bytes, counters, allocator, enabled_features);
    1729             : 
    1730          24 :     if (result.failed()) {
    1731           8 :       FinishAsyncCompileJobWithError(result.error());
    1732             :       return false;
    1733             :     }
    1734             : 
    1735          16 :     native_module->UseLazyStub(func_index);
    1736             :   } else {
    1737         484 :     compilation_unit_builder_->AddUnits(func_index);
    1738             :   }
    1739             : 
    1740         500 :   ++num_functions_;
    1741             : 
    1742         500 :   return true;
    1743             : }
    1744             : 
    1745           0 : void AsyncStreamingProcessor::CommitCompilationUnits() {
    1746             :   DCHECK(compilation_unit_builder_);
    1747         324 :   compilation_unit_builder_->Commit();
    1748           0 : }
    1749             : 
    1750         396 : void AsyncStreamingProcessor::OnFinishedChunk() {
    1751             :   TRACE_STREAMING("FinishChunk...\n");
    1752         396 :   if (compilation_unit_builder_) CommitCompilationUnits();
    1753         396 : }
    1754             : 
    1755             : // Finish the processing of the stream.
    1756         192 : void AsyncStreamingProcessor::OnFinishedStream(OwnedVector<uint8_t> bytes) {
    1757             :   TRACE_STREAMING("Finish stream...\n");
    1758         572 :   ModuleResult result = decoder_.FinishDecoding(false);
    1759         192 :   if (result.failed()) {
    1760           4 :     FinishAsyncCompileJobWithError(result.error());
    1761           4 :     return;
    1762             :   }
    1763             :   // We have to open a HandleScope and prepare the Context for
    1764             :   // CreateNativeModule, PrepareRuntimeObjects and FinishCompile as this is a
    1765             :   // callback from the embedder.
    1766         188 :   HandleScope scope(job_->isolate_);
    1767         376 :   SaveAndSwitchContext saved_context(job_->isolate_, *job_->native_context_);
    1768             : 
    1769         188 :   bool needs_finish = job_->DecrementAndCheckFinisherCount();
    1770         188 :   if (job_->native_module_ == nullptr) {
    1771             :     // We are processing a WebAssembly module without code section. Create the
    1772             :     // runtime objects now (would otherwise happen in {PrepareAndStartCompile}).
    1773          56 :     job_->CreateNativeModule(std::move(result).value());
    1774             :     DCHECK(needs_finish);
    1775             :   }
    1776         376 :   job_->wire_bytes_ = ModuleWireBytes(bytes.as_vector());
    1777         376 :   job_->native_module_->SetWireBytes(std::move(bytes));
    1778         188 :   if (needs_finish) {
    1779         164 :     if (job_->native_module_->compilation_state()->failed()) {
    1780          17 :       job_->AsyncCompileFailed();
    1781             :     } else {
    1782          65 :       job_->FinishCompile();
    1783             :     }
    1784             :   }
    1785             : }
    1786             : 
    1787             : // Report an error detected in the StreamingDecoder.
    1788         112 : void AsyncStreamingProcessor::OnError(const WasmError& error) {
    1789             :   TRACE_STREAMING("Stream error...\n");
    1790         112 :   FinishAsyncCompileJobWithError(error);
    1791         112 : }
    1792             : 
    1793          72 : void AsyncStreamingProcessor::OnAbort() {
    1794             :   TRACE_STREAMING("Abort stream...\n");
    1795          72 :   job_->Abort();
    1796          72 : }
    1797             : 
    1798           8 : bool AsyncStreamingProcessor::Deserialize(Vector<const uint8_t> module_bytes,
    1799             :                                           Vector<const uint8_t> wire_bytes) {
    1800             :   // DeserializeNativeModule and FinishCompile assume that they are executed in
    1801             :   // a HandleScope, and that a context is set on the isolate.
    1802           8 :   HandleScope scope(job_->isolate_);
    1803          16 :   SaveAndSwitchContext saved_context(job_->isolate_, *job_->native_context_);
    1804             : 
    1805             :   MaybeHandle<WasmModuleObject> result =
    1806           8 :       DeserializeNativeModule(job_->isolate_, module_bytes, wire_bytes);
    1807           8 :   if (result.is_null()) return false;
    1808             : 
    1809             :   job_->module_object_ =
    1810           8 :       job_->isolate_->global_handles()->Create(*result.ToHandleChecked());
    1811           8 :   job_->native_module_ = job_->module_object_->shared_native_module();
    1812           4 :   auto owned_wire_bytes = OwnedVector<uint8_t>::Of(wire_bytes);
    1813           8 :   job_->wire_bytes_ = ModuleWireBytes(owned_wire_bytes.as_vector());
    1814           8 :   job_->native_module_->SetWireBytes(std::move(owned_wire_bytes));
    1815           4 :   job_->FinishCompile();
    1816             :   return true;
    1817             : }
    1818             : 
    1819             : namespace {
    1820     1242379 : int GetMaxBackgroundTasks() {
    1821     1242379 :   if (NeedsDeterministicCompile()) return 1;
    1822     1242371 :   int num_worker_threads = V8::GetCurrentPlatform()->NumberOfWorkerThreads();
    1823             :   int num_compile_tasks =
    1824     1242371 :       std::min(FLAG_wasm_num_compilation_tasks, num_worker_threads);
    1825     2484742 :   return std::max(1, num_compile_tasks);
    1826             : }
    1827             : }  // namespace
    1828             : 
    1829     1242379 : CompilationStateImpl::CompilationStateImpl(
    1830             :     const std::shared_ptr<NativeModule>& native_module,
    1831             :     std::shared_ptr<Counters> async_counters)
    1832             :     : native_module_(native_module.get()),
    1833             :       background_compile_token_(
    1834             :           std::make_shared<BackgroundCompileToken>(native_module)),
    1835      877241 :       compile_mode_(FLAG_wasm_tier_up &&
    1836      877241 :                             native_module->module()->origin == kWasmOrigin
    1837             :                         ? CompileMode::kTiering
    1838             :                         : CompileMode::kRegular),
    1839             :       async_counters_(std::move(async_counters)),
    1840     1242379 :       max_background_tasks_(GetMaxBackgroundTasks()),
    1841             :       compilation_unit_queues_(max_background_tasks_),
    1842     7454274 :       available_task_ids_(max_background_tasks_) {
    1843    18635589 :   for (int i = 0; i < max_background_tasks_; ++i) {
    1844             :     // Ids are popped on task creation, so reverse this list. This ensures that
    1845             :     // the first background task gets id 0.
    1846    17393210 :     available_task_ids_[i] = max_background_tasks_ - 1 - i;
    1847             :   }
    1848     1242379 : }
    1849             : 
    1850     1242613 : void CompilationStateImpl::AbortCompilation() {
    1851     1242613 :   background_compile_token_->Cancel();
    1852             :   // No more callbacks after abort.
    1853     1242614 :   base::MutexGuard callbacks_guard(&callbacks_mutex_);
    1854             :   callbacks_.clear();
    1855     1242615 : }
    1856             : 
    1857      144502 : void CompilationStateImpl::SetNumberOfFunctionsToCompile(
    1858             :     int num_functions, int num_lazy_functions) {
    1859             :   DCHECK(!failed());
    1860      144502 :   base::MutexGuard guard(&callbacks_mutex_);
    1861             : 
    1862      144503 :   int num_functions_to_compile = num_functions - num_lazy_functions;
    1863      144503 :   outstanding_baseline_functions_ = num_functions_to_compile;
    1864      144503 :   outstanding_top_tier_functions_ = num_functions_to_compile;
    1865      289006 :   highest_execution_tier_.assign(num_functions, ExecutionTier::kNone);
    1866             : 
    1867             :   // Degenerate case of an empty module. Trigger callbacks immediately.
    1868      144503 :   if (num_functions_to_compile == 0) {
    1869       12592 :     for (auto& callback : callbacks_) {
    1870             :       callback(CompilationEvent::kFinishedBaselineCompilation);
    1871             :     }
    1872       12592 :     for (auto& callback : callbacks_) {
    1873             :       callback(CompilationEvent::kFinishedTopTierCompilation);
    1874             :     }
    1875             :     // Clear the callbacks because no more events will be delivered.
    1876             :     callbacks_.clear();
    1877             :   }
    1878      144502 : }
    1879             : 
    1880      146705 : void CompilationStateImpl::AddCallback(CompilationState::callback_t callback) {
    1881      146705 :   base::MutexGuard callbacks_guard(&callbacks_mutex_);
    1882      146706 :   callbacks_.emplace_back(std::move(callback));
    1883      146706 : }
    1884             : 
    1885             : void CompilationStateImpl::AddCompilationUnits(
    1886             :     Vector<std::unique_ptr<WasmCompilationUnit>> baseline_units,
    1887             :     Vector<std::unique_ptr<WasmCompilationUnit>> top_tier_units) {
    1888      138275 :   compilation_unit_queues_.AddUnits(baseline_units, top_tier_units);
    1889             : 
    1890      138275 :   RestartBackgroundTasks();
    1891             : }
    1892             : 
    1893          66 : void CompilationStateImpl::AddTopTierCompilationUnit(
    1894             :     std::unique_ptr<WasmCompilationUnit> unit) {
    1895             :   AddCompilationUnits({}, {&unit, 1});
    1896          66 : }
    1897             : 
    1898             : std::unique_ptr<WasmCompilationUnit>
    1899             : CompilationStateImpl::GetNextCompilationUnit(
    1900             :     int task_id, CompileBaselineOnly baseline_only) {
    1901      779769 :   return compilation_unit_queues_.GetNextUnit(task_id, baseline_only);
    1902             : }
    1903             : 
    1904             : void CompilationStateImpl::OnFinishedUnit(WasmCode* code) {
    1905           0 :   OnFinishedUnits({&code, 1});
    1906             : }
    1907             : 
    1908      268306 : void CompilationStateImpl::OnFinishedUnits(Vector<WasmCode*> code_vector) {
    1909      268306 :   base::MutexGuard guard(&callbacks_mutex_);
    1910             : 
    1911             :   // Assume an order of execution tiers that represents the quality of their
    1912             :   // generated code.
    1913             :   static_assert(ExecutionTier::kNone < ExecutionTier::kInterpreter &&
    1914             :                     ExecutionTier::kInterpreter < ExecutionTier::kLiftoff &&
    1915             :                     ExecutionTier::kLiftoff < ExecutionTier::kTurbofan,
    1916             :                 "Assume an order on execution tiers");
    1917             : 
    1918      268475 :   auto module = native_module_->module();
    1919      268475 :   auto enabled_features = native_module_->enabled_features();
    1920      915779 :   for (WasmCode* code : code_vector) {
    1921             :     DCHECK_NOT_NULL(code);
    1922             :     DCHECK_NE(code->tier(), ExecutionTier::kNone);
    1923      323652 :     native_module_->engine()->LogCode(code);
    1924             : 
    1925             :     // Skip lazily compiled code as we do not consider this for the completion
    1926             :     // of baseline respectively top tier compilation.
    1927             :     int func_index = code->index();
    1928      647324 :     if (IsLazyCompilation(module, native_module_, enabled_features,
    1929             :                           func_index)) {
    1930          53 :       continue;
    1931             :     }
    1932             : 
    1933             :     // Determine whether we are reaching baseline or top tier with the given
    1934             :     // code.
    1935      323609 :     uint32_t slot_index = code->index() - module->num_imported_functions;
    1936             :     ExecutionTierPair requested_tiers = GetRequestedExecutionTiers(
    1937      323609 :         module, compile_mode(), enabled_features, func_index);
    1938             :     DCHECK_EQ(highest_execution_tier_.size(), module->num_declared_functions);
    1939      647218 :     ExecutionTier prior_tier = highest_execution_tier_[slot_index];
    1940      323609 :     bool had_reached_baseline = prior_tier >= requested_tiers.baseline_tier;
    1941             :     bool had_reached_top_tier = prior_tier >= requested_tiers.top_tier;
    1942             :     DCHECK_IMPLIES(had_reached_baseline, prior_tier > ExecutionTier::kNone);
    1943             :     bool reaches_baseline = !had_reached_baseline;
    1944             :     bool reaches_top_tier =
    1945      323609 :         !had_reached_top_tier && code->tier() >= requested_tiers.top_tier;
    1946             :     DCHECK_IMPLIES(reaches_baseline,
    1947             :                    code->tier() >= requested_tiers.baseline_tier);
    1948             :     DCHECK_IMPLIES(reaches_top_tier, had_reached_baseline || reaches_baseline);
    1949             : 
    1950             :     // Remember compilation state before update.
    1951             :     bool had_completed_baseline_compilation =
    1952      323609 :         outstanding_baseline_functions_ == 0;
    1953             :     bool had_completed_top_tier_compilation =
    1954      323609 :         outstanding_top_tier_functions_ == 0;
    1955             : 
    1956             :     // Update compilation state.
    1957      323609 :     if (code->tier() > prior_tier) {
    1958      304094 :       highest_execution_tier_[slot_index] = code->tier();
    1959             :     }
    1960      323609 :     if (reaches_baseline) outstanding_baseline_functions_--;
    1961      323609 :     if (reaches_top_tier) outstanding_top_tier_functions_--;
    1962             :     DCHECK_LE(0, outstanding_baseline_functions_);
    1963             :     DCHECK_LE(outstanding_baseline_functions_, outstanding_top_tier_functions_);
    1964             : 
    1965             :     // Conclude if we are completing baseline or top tier compilation.
    1966      560779 :     bool completes_baseline_compilation = !had_completed_baseline_compilation &&
    1967      237170 :                                           outstanding_baseline_functions_ == 0;
    1968      633887 :     bool completes_top_tier_compilation = !had_completed_top_tier_compilation &&
    1969      310278 :                                           outstanding_top_tier_functions_ == 0;
    1970             :     DCHECK_IMPLIES(
    1971             :         completes_top_tier_compilation,
    1972             :         had_completed_baseline_compilation || completes_baseline_compilation);
    1973             : 
    1974             :     // Trigger callbacks.
    1975      323609 :     if (completes_baseline_compilation) {
    1976      261072 :       for (auto& callback : callbacks_) {
    1977             :         callback(CompilationEvent::kFinishedBaselineCompilation);
    1978             :       }
    1979             :     }
    1980      323609 :     if (completes_top_tier_compilation) {
    1981      261024 :       for (auto& callback : callbacks_) {
    1982             :         callback(CompilationEvent::kFinishedTopTierCompilation);
    1983             :       }
    1984             :       // Clear the callbacks because no more events will be delivered.
    1985             :       callbacks_.clear();
    1986             :     }
    1987             :   }
    1988      268482 : }
    1989             : 
    1990      206489 : void CompilationStateImpl::OnBackgroundTaskStopped(
    1991             :     int task_id, const WasmFeatures& detected) {
    1992             :   {
    1993      206489 :     base::MutexGuard guard(&mutex_);
    1994             :     DCHECK_EQ(0, std::count(available_task_ids_.begin(),
    1995             :                             available_task_ids_.end(), task_id));
    1996             :     DCHECK_GT(max_background_tasks_, available_task_ids_.size());
    1997      206560 :     available_task_ids_.push_back(task_id);
    1998      206548 :     UnionFeaturesInto(&detected_features_, detected);
    1999             :   }
    2000             : 
    2001             :   // The background task could have stopped while we were adding new units, or
    2002             :   // because it reached its deadline. In both cases we need to restart tasks to
    2003             :   // avoid a potential deadlock.
    2004      206563 :   RestartBackgroundTasks();
    2005      206531 : }
    2006             : 
    2007      248099 : void CompilationStateImpl::UpdateDetectedFeatures(
    2008             :     const WasmFeatures& detected) {
    2009      248099 :   base::MutexGuard guard(&mutex_);
    2010      248102 :   UnionFeaturesInto(&detected_features_, detected);
    2011      248102 : }
    2012             : 
    2013      144363 : void CompilationStateImpl::PublishDetectedFeatures(Isolate* isolate) {
    2014             :   // Notifying the isolate of the feature counts must take place under
    2015             :   // the mutex, because even if we have finished baseline compilation,
    2016             :   // tiering compilations may still occur in the background.
    2017      144363 :   base::MutexGuard guard(&mutex_);
    2018             :   UpdateFeatureUseCounts(isolate, detected_features_);
    2019      144363 : }
    2020             : 
    2021      344791 : void CompilationStateImpl::RestartBackgroundTasks() {
    2022             :   // Create new tasks, but only spawn them after releasing the mutex, because
    2023             :   // some platforms (e.g. the predictable platform) might execute tasks right
    2024             :   // away.
    2025      340836 :   std::vector<std::unique_ptr<Task>> new_tasks;
    2026             :   {
    2027      344791 :     base::MutexGuard guard(&mutex_);
    2028             :     // Explicit fast path (quite common): If no more task ids are available
    2029             :     // (i.e. {max_background_tasks_} tasks are already running), spawn nothing.
    2030      344834 :     if (available_task_ids_.empty()) return;
    2031             :     // No need to restart tasks if compilation already failed.
    2032      344831 :     if (failed()) return;
    2033             : 
    2034             :     size_t max_num_restart = compilation_unit_queues_.GetTotalSize();
    2035             : 
    2036      764226 :     while (!available_task_ids_.empty() && max_num_restart-- > 0) {
    2037      211698 :       int task_id = available_task_ids_.back();
    2038             :       available_task_ids_.pop_back();
    2039             :       new_tasks.emplace_back(
    2040      211698 :           native_module_->engine()
    2041      423397 :               ->NewBackgroundCompileTask<BackgroundCompileTask>(
    2042      211699 :                   background_compile_token_, async_counters_, task_id));
    2043             :     }
    2044             :   }
    2045             : 
    2046      340836 :   if (baseline_compilation_finished()) {
    2047      123108 :     for (auto& task : new_tasks) {
    2048        2222 :       V8::GetCurrentPlatform()->CallLowPriorityTaskOnWorkerThread(
    2049        2222 :           std::move(task));
    2050             :     }
    2051             :   } else {
    2052      429427 :     for (auto& task : new_tasks) {
    2053      631764 :       V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
    2054             :     }
    2055             :   }
    2056             : }
    2057             : 
    2058        7849 : void CompilationStateImpl::SetError() {
    2059        7849 :   bool expected = false;
    2060        7849 :   if (!compile_failed_.compare_exchange_strong(expected, true,
    2061             :                                                std::memory_order_relaxed)) {
    2062         264 :     return;  // Already failed before.
    2063             :   }
    2064             : 
    2065        7585 :   base::MutexGuard callbacks_guard(&callbacks_mutex_);
    2066       15170 :   for (auto& callback : callbacks_) {
    2067             :     callback(CompilationEvent::kFailedCompilation);
    2068             :   }
    2069             :   // No more callbacks after an error.
    2070             :   callbacks_.clear();
    2071             : }
    2072             : 
    2073      139833 : void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module,
    2074             :                              Handle<FixedArray> export_wrappers) {
    2075             :   JSToWasmWrapperCache js_to_wasm_cache;
    2076             :   int wrapper_index = 0;
    2077             : 
    2078             :   // TODO(6792): Wrappers below are allocated with {Factory::NewCode}. As an
    2079             :   // optimization we keep the code space unlocked to avoid repeated unlocking
    2080             :   // because many such wrapper are allocated in sequence below.
    2081      279666 :   CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
    2082      363790 :   for (auto exp : module->export_table) {
    2083      223957 :     if (exp.kind != kExternalFunction) continue;
    2084      221071 :     auto& function = module->functions[exp.index];
    2085             :     Handle<Code> wrapper_code = js_to_wasm_cache.GetOrCompileJSToWasmWrapper(
    2086      221071 :         isolate, function.sig, function.imported);
    2087      442138 :     export_wrappers->set(wrapper_index, *wrapper_code);
    2088      221069 :     RecordStats(*wrapper_code, isolate->counters());
    2089      221071 :     ++wrapper_index;
    2090             :   }
    2091      139833 : }
    2092             : 
    2093      137362 : Handle<Script> CreateWasmScript(Isolate* isolate,
    2094             :                                 const ModuleWireBytes& wire_bytes,
    2095             :                                 const std::string& source_map_url) {
    2096             :   Handle<Script> script =
    2097      137362 :       isolate->factory()->NewScript(isolate->factory()->empty_string());
    2098      412086 :   script->set_context_data(isolate->native_context()->debug_context_id());
    2099             :   script->set_type(Script::TYPE_WASM);
    2100             : 
    2101      137362 :   int hash = StringHasher::HashSequentialString(
    2102             :       reinterpret_cast<const char*>(wire_bytes.start()),
    2103      137362 :       static_cast<int>(wire_bytes.length()), kZeroHashSeed);
    2104             : 
    2105             :   const int kBufferSize = 32;
    2106             :   char buffer[kBufferSize];
    2107             : 
    2108      137362 :   int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash);
    2109             :   DCHECK(name_chars >= 0 && name_chars < kBufferSize);
    2110             :   MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte(
    2111      137362 :       VectorOf(reinterpret_cast<uint8_t*>(buffer), name_chars),
    2112      137362 :       AllocationType::kOld);
    2113      274722 :   script->set_name(*name_str.ToHandleChecked());
    2114             : 
    2115      137361 :   if (source_map_url.size() != 0) {
    2116             :     MaybeHandle<String> src_map_str = isolate->factory()->NewStringFromUtf8(
    2117           4 :         CStrVector(source_map_url.c_str()), AllocationType::kOld);
    2118           8 :     script->set_source_mapping_url(*src_map_str.ToHandleChecked());
    2119             :   }
    2120      137361 :   return script;
    2121             : }
    2122             : 
    2123             : }  // namespace wasm
    2124             : }  // namespace internal
    2125      121996 : }  // namespace v8
    2126             : 
    2127             : #undef TRACE_COMPILE
    2128             : #undef TRACE_STREAMING
    2129             : #undef TRACE_LAZY

Generated by: LCOV version 1.10