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#include <queue>
9
10#include "src/api/api-inl.h"
11#include "src/asmjs/asm-js.h"
12#include "src/base/enum-set.h"
13#include "src/base/optional.h"
14#include "src/base/platform/mutex.h"
15#include "src/base/platform/semaphore.h"
16#include "src/base/platform/time.h"
17#include "src/base/utils/random-number-generator.h"
18#include "src/compiler/wasm-compiler.h"
19#include "src/handles/global-handles-inl.h"
20#include "src/heap/heap-inl.h"  // For CodePageCollectionMemoryModificationScope.
21#include "src/logging/counters-scopes.h"
22#include "src/logging/metrics.h"
23#include "src/objects/property-descriptor.h"
24#include "src/tasks/task-utils.h"
25#include "src/tracing/trace-event.h"
26#include "src/trap-handler/trap-handler.h"
27#include "src/utils/identity-map.h"
28#include "src/wasm/code-space-access.h"
29#include "src/wasm/module-decoder.h"
30#include "src/wasm/streaming-decoder.h"
31#include "src/wasm/wasm-code-manager.h"
32#include "src/wasm/wasm-engine.h"
33#include "src/wasm/wasm-import-wrapper-cache.h"
34#include "src/wasm/wasm-js.h"
35#include "src/wasm/wasm-limits.h"
36#include "src/wasm/wasm-objects-inl.h"
37#include "src/wasm/wasm-result.h"
38#include "src/wasm/wasm-serialization.h"
39
40#define TRACE_COMPILE(...)                             \
41  do {                                                 \
42    if (FLAG_trace_wasm_compiler) PrintF(__VA_ARGS__); \
43  } while (false)
44
45#define TRACE_STREAMING(...)                            \
46  do {                                                  \
47    if (FLAG_trace_wasm_streaming) PrintF(__VA_ARGS__); \
48  } while (false)
49
50#define TRACE_LAZY(...)                                        \
51  do {                                                         \
52    if (FLAG_trace_wasm_lazy_compilation) PrintF(__VA_ARGS__); \
53  } while (false)
54
55namespace v8 {
56namespace internal {
57namespace wasm {
58
59namespace {
60
61enum class CompileStrategy : uint8_t {
62  // Compiles functions on first use. In this case, execution will block until
63  // the function's baseline is reached and top tier compilation starts in
64  // background (if applicable).
65  // Lazy compilation can help to reduce startup time and code size at the risk
66  // of blocking execution.
67  kLazy,
68  // Compiles baseline ahead of execution and starts top tier compilation in
69  // background (if applicable).
70  kEager,
71  // Triggers baseline compilation on first use (just like {kLazy}) with the
72  // difference that top tier compilation is started eagerly.
73  // This strategy can help to reduce startup time at the risk of blocking
74  // execution, but only in its early phase (until top tier compilation
75  // finishes).
76  kLazyBaselineEagerTopTier,
77  // Marker for default strategy.
78  kDefault = kEager,
79};
80
81class CompilationStateImpl;
82class CompilationUnitBuilder;
83
84class V8_NODISCARD BackgroundCompileScope {
85 public:
86  explicit BackgroundCompileScope(std::weak_ptr<NativeModule> native_module)
87      : native_module_(native_module.lock()) {}
88
89  NativeModule* native_module() const {
90    DCHECK(native_module_);
91    return native_module_.get();
92  }
93  inline CompilationStateImpl* compilation_state() const;
94
95  bool cancelled() const;
96
97 private:
98  // Keep the native module alive while in this scope.
99  std::shared_ptr<NativeModule> native_module_;
100};
101
102enum CompileBaselineOnly : bool {
103  kBaselineOnly = true,
104  kBaselineOrTopTier = false
105};
106
107// A set of work-stealing queues (vectors of units). Each background compile
108// task owns one of the queues and steals from all others once its own queue
109// runs empty.
110class CompilationUnitQueues {
111 public:
112  // Public API for QueueImpl.
113  struct Queue {
114    bool ShouldPublish(int num_processed_units) const;
115  };
116
117  explicit CompilationUnitQueues(int num_declared_functions)
118      : num_declared_functions_(num_declared_functions) {
119    // Add one first queue, to add units to.
120    queues_.emplace_back(std::make_unique<QueueImpl>(0));
121
122    for (auto& atomic_counter : num_units_) {
123      std::atomic_init(&atomic_counter, size_t{0});
124    }
125
126    top_tier_compiled_ =
127        std::make_unique<std::atomic<bool>[]>(num_declared_functions);
128
129    for (int i = 0; i < num_declared_functions; i++) {
130      std::atomic_init(&top_tier_compiled_.get()[i], false);
131    }
132  }
133
134  Queue* GetQueueForTask(int task_id) {
135    int required_queues = task_id + 1;
136    {
137      base::SharedMutexGuard<base::kShared> queues_guard(&queues_mutex_);
138      if (V8_LIKELY(static_cast<int>(queues_.size()) >= required_queues)) {
139        return queues_[task_id].get();
140      }
141    }
142
143    // Otherwise increase the number of queues.
144    base::SharedMutexGuard<base::kExclusive> queues_guard(&queues_mutex_);
145    int num_queues = static_cast<int>(queues_.size());
146    while (num_queues < required_queues) {
147      int steal_from = num_queues + 1;
148      queues_.emplace_back(std::make_unique<QueueImpl>(steal_from));
149      ++num_queues;
150    }
151
152    // Update the {publish_limit}s of all queues.
153
154    // We want background threads to publish regularly (to avoid contention when
155    // they are all publishing at the end). On the other side, each publishing
156    // has some overhead (part of it for synchronizing between threads), so it
157    // should not happen *too* often. Thus aim for 4-8 publishes per thread, but
158    // distribute it such that publishing is likely to happen at different
159    // times.
160    int units_per_thread = num_declared_functions_ / num_queues;
161    int min = std::max(10, units_per_thread / 8);
162    int queue_id = 0;
163    for (auto& queue : queues_) {
164      // Set a limit between {min} and {2*min}, but not smaller than {10}.
165      int limit = min + (min * queue_id / num_queues);
166      queue->publish_limit.store(limit, std::memory_order_relaxed);
167      ++queue_id;
168    }
169
170    return queues_[task_id].get();
171  }
172
173  base::Optional<WasmCompilationUnit> GetNextUnit(
174      Queue* queue, CompileBaselineOnly baseline_only) {
175    // As long as any lower-tier units are outstanding we need to steal them
176    // before executing own higher-tier units.
177    int max_tier = baseline_only ? kBaseline : kTopTier;
178    for (int tier = GetLowestTierWithUnits(); tier <= max_tier; ++tier) {
179      if (auto unit = GetNextUnitOfTier(queue, tier)) {
180        size_t old_units_count =
181            num_units_[tier].fetch_sub(1, std::memory_order_relaxed);
182        DCHECK_LE(1, old_units_count);
183        USE(old_units_count);
184        return unit;
185      }
186    }
187    return {};
188  }
189
190  void AddUnits(base::Vector<WasmCompilationUnit> baseline_units,
191                base::Vector<WasmCompilationUnit> top_tier_units,
192                const WasmModule* module) {
193    DCHECK_LT(0, baseline_units.size() + top_tier_units.size());
194    // Add to the individual queues in a round-robin fashion. No special care is
195    // taken to balance them; they will be balanced by work stealing.
196    QueueImpl* queue;
197    {
198      int queue_to_add = next_queue_to_add.load(std::memory_order_relaxed);
199      base::SharedMutexGuard<base::kShared> queues_guard(&queues_mutex_);
200      while (!next_queue_to_add.compare_exchange_weak(
201          queue_to_add, next_task_id(queue_to_add, queues_.size()),
202          std::memory_order_relaxed)) {
203        // Retry with updated {queue_to_add}.
204      }
205      queue = queues_[queue_to_add].get();
206    }
207
208    base::MutexGuard guard(&queue->mutex);
209    base::Optional<base::MutexGuard> big_units_guard;
210    for (auto pair : {std::make_pair(int{kBaseline}, baseline_units),
211                      std::make_pair(int{kTopTier}, top_tier_units)}) {
212      int tier = pair.first;
213      base::Vector<WasmCompilationUnit> units = pair.second;
214      if (units.empty()) continue;
215      num_units_[tier].fetch_add(units.size(), std::memory_order_relaxed);
216      for (WasmCompilationUnit unit : units) {
217        size_t func_size = module->functions[unit.func_index()].code.length();
218        if (func_size <= kBigUnitsLimit) {
219          queue->units[tier].push_back(unit);
220        } else {
221          if (!big_units_guard) {
222            big_units_guard.emplace(&big_units_queue_.mutex);
223          }
224          big_units_queue_.has_units[tier].store(true,
225                                                 std::memory_order_relaxed);
226          big_units_queue_.units[tier].emplace(func_size, unit);
227        }
228      }
229    }
230  }
231
232  void AddTopTierPriorityUnit(WasmCompilationUnit unit, size_t priority) {
233    base::SharedMutexGuard<base::kShared> queues_guard(&queues_mutex_);
234    // Add to the individual queues in a round-robin fashion. No special care is
235    // taken to balance them; they will be balanced by work stealing. We use
236    // the same counter for this reason.
237    int queue_to_add = next_queue_to_add.load(std::memory_order_relaxed);
238    while (!next_queue_to_add.compare_exchange_weak(
239        queue_to_add, next_task_id(queue_to_add, queues_.size()),
240        std::memory_order_relaxed)) {
241      // Retry with updated {queue_to_add}.
242    }
243
244    {
245      auto* queue = queues_[queue_to_add].get();
246      base::MutexGuard guard(&queue->mutex);
247      queue->top_tier_priority_units.emplace(priority, unit);
248    }
249    num_priority_units_.fetch_add(1, std::memory_order_relaxed);
250    num_units_[kTopTier].fetch_add(1, std::memory_order_relaxed);
251  }
252
253  // Get the current total number of units in all queues. This is only a
254  // momentary snapshot, it's not guaranteed that {GetNextUnit} returns a unit
255  // if this method returns non-zero.
256  size_t GetTotalSize() const {
257    size_t total = 0;
258    for (auto& atomic_counter : num_units_) {
259      total += atomic_counter.load(std::memory_order_relaxed);
260    }
261    return total;
262  }
263
264 private:
265  // Store tier in int so we can easily loop over it:
266  static constexpr int kBaseline = 0;
267  static constexpr int kTopTier = 1;
268  static constexpr int kNumTiers = kTopTier + 1;
269
270  // Functions bigger than {kBigUnitsLimit} will be compiled first, in ascending
271  // order of their function body size.
272  static constexpr size_t kBigUnitsLimit = 4096;
273
274  struct BigUnit {
275    BigUnit(size_t func_size, WasmCompilationUnit unit)
276        : func_size{func_size}, unit(unit) {}
277
278    size_t func_size;
279    WasmCompilationUnit unit;
280
281    bool operator<(const BigUnit& other) const {
282      return func_size < other.func_size;
283    }
284  };
285
286  struct TopTierPriorityUnit {
287    TopTierPriorityUnit(int priority, WasmCompilationUnit unit)
288        : priority(priority), unit(unit) {}
289
290    size_t priority;
291    WasmCompilationUnit unit;
292
293    bool operator<(const TopTierPriorityUnit& other) const {
294      return priority < other.priority;
295    }
296  };
297
298  struct BigUnitsQueue {
299    BigUnitsQueue() {
300      for (auto& atomic : has_units) std::atomic_init(&atomic, false);
301    }
302
303    base::Mutex mutex;
304
305    // Can be read concurrently to check whether any elements are in the queue.
306    std::atomic<bool> has_units[kNumTiers];
307
308    // Protected by {mutex}:
309    std::priority_queue<BigUnit> units[kNumTiers];
310  };
311
312  struct QueueImpl : public Queue {
313    explicit QueueImpl(int next_steal_task_id)
314        : next_steal_task_id(next_steal_task_id) {}
315
316    // Number of units after which the task processing this queue should publish
317    // compilation results. Updated (reduced, using relaxed ordering) when new
318    // queues are allocated. If there is only one thread running, we can delay
319    // publishing arbitrarily.
320    std::atomic<int> publish_limit{kMaxInt};
321
322    base::Mutex mutex;
323
324    // All fields below are protected by {mutex}.
325    std::vector<WasmCompilationUnit> units[kNumTiers];
326    std::priority_queue<TopTierPriorityUnit> top_tier_priority_units;
327    int next_steal_task_id;
328  };
329
330  int next_task_id(int task_id, size_t num_queues) const {
331    int next = task_id + 1;
332    return next == static_cast<int>(num_queues) ? 0 : next;
333  }
334
335  int GetLowestTierWithUnits() const {
336    for (int tier = 0; tier < kNumTiers; ++tier) {
337      if (num_units_[tier].load(std::memory_order_relaxed) > 0) return tier;
338    }
339    return kNumTiers;
340  }
341
342  base::Optional<WasmCompilationUnit> GetNextUnitOfTier(Queue* public_queue,
343                                                        int tier) {
344    QueueImpl* queue = static_cast<QueueImpl*>(public_queue);
345
346    // First check whether there is a priority unit. Execute that first.
347    if (tier == kTopTier) {
348      if (auto unit = GetTopTierPriorityUnit(queue)) {
349        return unit;
350      }
351    }
352
353    // Then check whether there is a big unit of that tier.
354    if (auto unit = GetBigUnitOfTier(tier)) return unit;
355
356    // Finally check whether our own queue has a unit of the wanted tier. If
357    // so, return it, otherwise get the task id to steal from.
358    int steal_task_id;
359    {
360      base::MutexGuard mutex_guard(&queue->mutex);
361      if (!queue->units[tier].empty()) {
362        auto unit = queue->units[tier].back();
363        queue->units[tier].pop_back();
364        return unit;
365      }
366      steal_task_id = queue->next_steal_task_id;
367    }
368
369    // Try to steal from all other queues. If this succeeds, return one of the
370    // stolen units.
371    {
372      base::SharedMutexGuard<base::kShared> guard(&queues_mutex_);
373      for (size_t steal_trials = 0; steal_trials < queues_.size();
374           ++steal_trials, ++steal_task_id) {
375        if (steal_task_id >= static_cast<int>(queues_.size())) {
376          steal_task_id = 0;
377        }
378        if (auto unit = StealUnitsAndGetFirst(queue, steal_task_id, tier)) {
379          return unit;
380        }
381      }
382    }
383
384    // If we reach here, we didn't find any unit of the requested tier.
385    return {};
386  }
387
388  base::Optional<WasmCompilationUnit> GetBigUnitOfTier(int tier) {
389    // Fast path without locking.
390    if (!big_units_queue_.has_units[tier].load(std::memory_order_relaxed)) {
391      return {};
392    }
393    base::MutexGuard guard(&big_units_queue_.mutex);
394    if (big_units_queue_.units[tier].empty()) return {};
395    WasmCompilationUnit unit = big_units_queue_.units[tier].top().unit;
396    big_units_queue_.units[tier].pop();
397    if (big_units_queue_.units[tier].empty()) {
398      big_units_queue_.has_units[tier].store(false, std::memory_order_relaxed);
399    }
400    return unit;
401  }
402
403  base::Optional<WasmCompilationUnit> GetTopTierPriorityUnit(QueueImpl* queue) {
404    // Fast path without locking.
405    if (num_priority_units_.load(std::memory_order_relaxed) == 0) {
406      return {};
407    }
408
409    int steal_task_id;
410    {
411      base::MutexGuard mutex_guard(&queue->mutex);
412      while (!queue->top_tier_priority_units.empty()) {
413        auto unit = queue->top_tier_priority_units.top().unit;
414        queue->top_tier_priority_units.pop();
415        num_priority_units_.fetch_sub(1, std::memory_order_relaxed);
416
417        if (!top_tier_compiled_[unit.func_index()].exchange(
418                true, std::memory_order_relaxed)) {
419          return unit;
420        }
421        num_units_[kTopTier].fetch_sub(1, std::memory_order_relaxed);
422      }
423      steal_task_id = queue->next_steal_task_id;
424    }
425
426    // Try to steal from all other queues. If this succeeds, return one of the
427    // stolen units.
428    {
429      base::SharedMutexGuard<base::kShared> guard(&queues_mutex_);
430      for (size_t steal_trials = 0; steal_trials < queues_.size();
431           ++steal_trials, ++steal_task_id) {
432        if (steal_task_id >= static_cast<int>(queues_.size())) {
433          steal_task_id = 0;
434        }
435        if (auto unit = StealTopTierPriorityUnit(queue, steal_task_id)) {
436          return unit;
437        }
438      }
439    }
440
441    return {};
442  }
443
444  // Steal units of {wanted_tier} from {steal_from_task_id} to {queue}. Return
445  // first stolen unit (rest put in queue of {task_id}), or {nullopt} if
446  // {steal_from_task_id} had no units of {wanted_tier}.
447  // Hold a shared lock on {queues_mutex_} when calling this method.
448  base::Optional<WasmCompilationUnit> StealUnitsAndGetFirst(
449      QueueImpl* queue, int steal_from_task_id, int wanted_tier) {
450    auto* steal_queue = queues_[steal_from_task_id].get();
451    // Cannot steal from own queue.
452    if (steal_queue == queue) return {};
453    std::vector<WasmCompilationUnit> stolen;
454    base::Optional<WasmCompilationUnit> returned_unit;
455    {
456      base::MutexGuard guard(&steal_queue->mutex);
457      auto* steal_from_vector = &steal_queue->units[wanted_tier];
458      if (steal_from_vector->empty()) return {};
459      size_t remaining = steal_from_vector->size() / 2;
460      auto steal_begin = steal_from_vector->begin() + remaining;
461      returned_unit = *steal_begin;
462      stolen.assign(steal_begin + 1, steal_from_vector->end());
463      steal_from_vector->erase(steal_begin, steal_from_vector->end());
464    }
465    base::MutexGuard guard(&queue->mutex);
466    auto* target_queue = &queue->units[wanted_tier];
467    target_queue->insert(target_queue->end(), stolen.begin(), stolen.end());
468    queue->next_steal_task_id = steal_from_task_id + 1;
469    return returned_unit;
470  }
471
472  // Steal one priority unit from {steal_from_task_id} to {task_id}. Return
473  // stolen unit, or {nullopt} if {steal_from_task_id} had no priority units.
474  // Hold a shared lock on {queues_mutex_} when calling this method.
475  base::Optional<WasmCompilationUnit> StealTopTierPriorityUnit(
476      QueueImpl* queue, int steal_from_task_id) {
477    auto* steal_queue = queues_[steal_from_task_id].get();
478    // Cannot steal from own queue.
479    if (steal_queue == queue) return {};
480    base::Optional<WasmCompilationUnit> returned_unit;
481    {
482      base::MutexGuard guard(&steal_queue->mutex);
483      while (true) {
484        if (steal_queue->top_tier_priority_units.empty()) return {};
485
486        auto unit = steal_queue->top_tier_priority_units.top().unit;
487        steal_queue->top_tier_priority_units.pop();
488        num_priority_units_.fetch_sub(1, std::memory_order_relaxed);
489
490        if (!top_tier_compiled_[unit.func_index()].exchange(
491                true, std::memory_order_relaxed)) {
492          returned_unit = unit;
493          break;
494        }
495        num_units_[kTopTier].fetch_sub(1, std::memory_order_relaxed);
496      }
497    }
498    base::MutexGuard guard(&queue->mutex);
499    queue->next_steal_task_id = steal_from_task_id + 1;
500    return returned_unit;
501  }
502
503  // {queues_mutex_} protectes {queues_};
504  base::SharedMutex queues_mutex_;
505  std::vector<std::unique_ptr<QueueImpl>> queues_;
506
507  const int num_declared_functions_;
508
509  BigUnitsQueue big_units_queue_;
510
511  std::atomic<size_t> num_units_[kNumTiers];
512  std::atomic<size_t> num_priority_units_{0};
513  std::unique_ptr<std::atomic<bool>[]> top_tier_compiled_;
514  std::atomic<int> next_queue_to_add{0};
515};
516
517bool CompilationUnitQueues::Queue::ShouldPublish(
518    int num_processed_units) const {
519  auto* queue = static_cast<const QueueImpl*>(this);
520  return num_processed_units >=
521         queue->publish_limit.load(std::memory_order_relaxed);
522}
523
524// The {CompilationStateImpl} keeps track of the compilation state of the
525// owning NativeModule, i.e. which functions are left to be compiled.
526// It contains a task manager to allow parallel and asynchronous background
527// compilation of functions.
528// Its public interface {CompilationState} lives in compilation-environment.h.
529class CompilationStateImpl {
530 public:
531  CompilationStateImpl(const std::shared_ptr<NativeModule>& native_module,
532                       std::shared_ptr<Counters> async_counters,
533                       DynamicTiering dynamic_tiering);
534  ~CompilationStateImpl() {
535    if (compile_job_->IsValid()) compile_job_->CancelAndDetach();
536  }
537
538  // Call right after the constructor, after the {compilation_state_} field in
539  // the {NativeModule} has been initialized.
540  void InitCompileJob();
541
542  // {kCancelUnconditionally}: Cancel all compilation.
543  // {kCancelInitialCompilation}: Cancel all compilation if initial (baseline)
544  // compilation is not finished yet.
545  enum CancellationPolicy { kCancelUnconditionally, kCancelInitialCompilation };
546  void CancelCompilation(CancellationPolicy);
547
548  bool cancelled() const;
549
550  // Initialize compilation progress. Set compilation tiers to expect for
551  // baseline and top tier compilation. Must be set before
552  // {CommitCompilationUnits} is invoked which triggers background compilation.
553  void InitializeCompilationProgress(bool lazy_module, int num_import_wrappers,
554                                     int num_export_wrappers);
555
556  // Initialize the compilation progress after deserialization. This is needed
557  // for recompilation (e.g. for tier down) to work later.
558  void InitializeCompilationProgressAfterDeserialization(
559      base::Vector<const int> lazy_functions,
560      base::Vector<const int> liftoff_functions);
561
562  // Initializes compilation units based on the information encoded in the
563  // {compilation_progress_}.
564  void InitializeCompilationUnits(
565      std::unique_ptr<CompilationUnitBuilder> builder);
566
567  // Adds compilation units for another function to the
568  // {CompilationUnitBuilder}. This function is the streaming compilation
569  // equivalent to {InitializeCompilationUnits}.
570  void AddCompilationUnit(CompilationUnitBuilder* builder, int func_index);
571
572  // Initialize recompilation of the whole module: Setup compilation progress
573  // for recompilation and add the respective compilation units. The callback is
574  // called immediately if no recompilation is needed, or called later
575  // otherwise.
576  void InitializeRecompilation(TieringState new_tiering_state,
577                               std::unique_ptr<CompilationEventCallback>
578                                   recompilation_finished_callback);
579
580  // Add the callback to be called on compilation events. Needs to be
581  // set before {CommitCompilationUnits} is run to ensure that it receives all
582  // events. The callback object must support being deleted from any thread.
583  void AddCallback(std::unique_ptr<CompilationEventCallback> callback);
584
585  // Inserts new functions to compile and kicks off compilation.
586  void CommitCompilationUnits(
587      base::Vector<WasmCompilationUnit> baseline_units,
588      base::Vector<WasmCompilationUnit> top_tier_units,
589      base::Vector<std::shared_ptr<JSToWasmWrapperCompilationUnit>>
590          js_to_wasm_wrapper_units);
591  void CommitTopTierCompilationUnit(WasmCompilationUnit);
592  void AddTopTierPriorityCompilationUnit(WasmCompilationUnit, size_t);
593
594  CompilationUnitQueues::Queue* GetQueueForCompileTask(int task_id);
595
596  base::Optional<WasmCompilationUnit> GetNextCompilationUnit(
597      CompilationUnitQueues::Queue*, CompileBaselineOnly);
598
599  std::shared_ptr<JSToWasmWrapperCompilationUnit>
600  GetNextJSToWasmWrapperCompilationUnit();
601  void FinalizeJSToWasmWrappers(Isolate* isolate, const WasmModule* module,
602                                Handle<FixedArray>* export_wrappers_out);
603
604  void OnFinishedUnits(base::Vector<WasmCode*>);
605  void OnFinishedJSToWasmWrapperUnits(int num);
606
607  void OnCompilationStopped(WasmFeatures detected);
608  void PublishDetectedFeatures(Isolate*);
609  void SchedulePublishCompilationResults(
610      std::vector<std::unique_ptr<WasmCode>> unpublished_code);
611
612  size_t NumOutstandingCompilations() const;
613
614  void SetError();
615
616  void WaitForCompilationEvent(CompilationEvent event);
617
618  void SetHighPriority() {
619    // TODO(wasm): Keep a lower priority for TurboFan-only jobs.
620    compile_job_->UpdatePriority(TaskPriority::kUserBlocking);
621  }
622
623  bool failed() const {
624    return compile_failed_.load(std::memory_order_relaxed);
625  }
626
627  bool baseline_compilation_finished() const {
628    base::MutexGuard guard(&callbacks_mutex_);
629    return outstanding_baseline_units_ == 0 &&
630           outstanding_export_wrappers_ == 0;
631  }
632
633  bool top_tier_compilation_finished() const {
634    base::MutexGuard guard(&callbacks_mutex_);
635    return outstanding_top_tier_functions_ == 0;
636  }
637
638  bool recompilation_finished() const {
639    base::MutexGuard guard(&callbacks_mutex_);
640    return outstanding_recompilation_functions_ == 0;
641  }
642
643  DynamicTiering dynamic_tiering() const { return dynamic_tiering_; }
644
645  Counters* counters() const { return async_counters_.get(); }
646
647  void SetWireBytesStorage(
648      std::shared_ptr<WireBytesStorage> wire_bytes_storage) {
649    base::MutexGuard guard(&mutex_);
650    wire_bytes_storage_ = std::move(wire_bytes_storage);
651  }
652
653  std::shared_ptr<WireBytesStorage> GetWireBytesStorage() const {
654    base::MutexGuard guard(&mutex_);
655    DCHECK_NOT_NULL(wire_bytes_storage_);
656    return wire_bytes_storage_;
657  }
658
659  void set_compilation_id(int compilation_id) {
660    DCHECK_EQ(compilation_id_, kInvalidCompilationID);
661    compilation_id_ = compilation_id;
662  }
663
664  std::weak_ptr<NativeModule> const native_module_weak() const {
665    return native_module_weak_;
666  }
667
668 private:
669  uint8_t SetupCompilationProgressForFunction(
670      bool lazy_function, NativeModule* module,
671      const WasmFeatures& enabled_features, int func_index);
672
673  // Returns the potentially-updated {function_progress}.
674  uint8_t AddCompilationUnitInternal(CompilationUnitBuilder* builder,
675                                     int function_index,
676                                     uint8_t function_progress);
677
678  // Trigger callbacks according to the internal counters below
679  // (outstanding_...), plus the given events.
680  // Hold the {callbacks_mutex_} when calling this method.
681  void TriggerCallbacks(base::EnumSet<CompilationEvent> additional_events = {});
682
683  void PublishCompilationResults(
684      std::vector<std::unique_ptr<WasmCode>> unpublished_code);
685  void PublishCode(base::Vector<std::unique_ptr<WasmCode>> codes);
686
687  NativeModule* const native_module_;
688  std::weak_ptr<NativeModule> const native_module_weak_;
689  const std::shared_ptr<Counters> async_counters_;
690
691  // Compilation error, atomically updated. This flag can be updated and read
692  // using relaxed semantics.
693  std::atomic<bool> compile_failed_{false};
694
695  // True if compilation was cancelled and worker threads should return. This
696  // flag can be updated and read using relaxed semantics.
697  std::atomic<bool> compile_cancelled_{false};
698
699  CompilationUnitQueues compilation_unit_queues_;
700
701  // Number of wrappers to be compiled. Initialized once, counted down in
702  // {GetNextJSToWasmWrapperCompilationUnit}.
703  std::atomic<size_t> outstanding_js_to_wasm_wrappers_{0};
704  // Wrapper compilation units are stored in shared_ptrs so that they are kept
705  // alive by the tasks even if the NativeModule dies.
706  std::vector<std::shared_ptr<JSToWasmWrapperCompilationUnit>>
707      js_to_wasm_wrapper_units_;
708
709  // Cache the dynamic tiering configuration to be consistent for the whole
710  // compilation.
711  const DynamicTiering dynamic_tiering_;
712
713  // This mutex protects all information of this {CompilationStateImpl} which is
714  // being accessed concurrently.
715  mutable base::Mutex mutex_;
716
717  // The compile job handle, initialized right after construction of
718  // {CompilationStateImpl}.
719  std::unique_ptr<JobHandle> compile_job_;
720
721  // The compilation id to identify trace events linked to this compilation.
722  static constexpr int kInvalidCompilationID = -1;
723  int compilation_id_ = kInvalidCompilationID;
724
725  //////////////////////////////////////////////////////////////////////////////
726  // Protected by {mutex_}:
727
728  // Features detected to be used in this module. Features can be detected
729  // as a module is being compiled.
730  WasmFeatures detected_features_ = WasmFeatures::None();
731
732  // Abstraction over the storage of the wire bytes. Held in a shared_ptr so
733  // that background compilation jobs can keep the storage alive while
734  // compiling.
735  std::shared_ptr<WireBytesStorage> wire_bytes_storage_;
736
737  // End of fields protected by {mutex_}.
738  //////////////////////////////////////////////////////////////////////////////
739
740  // This mutex protects the callbacks vector, and the counters used to
741  // determine which callbacks to call. The counters plus the callbacks
742  // themselves need to be synchronized to ensure correct order of events.
743  mutable base::Mutex callbacks_mutex_;
744
745  //////////////////////////////////////////////////////////////////////////////
746  // Protected by {callbacks_mutex_}:
747
748  // Callbacks to be called on compilation events.
749  std::vector<std::unique_ptr<CompilationEventCallback>> callbacks_;
750
751  // Events that already happened.
752  base::EnumSet<CompilationEvent> finished_events_;
753
754  int outstanding_baseline_units_ = 0;
755  int outstanding_export_wrappers_ = 0;
756  int outstanding_top_tier_functions_ = 0;
757  // The amount of generated top tier code since the last
758  // {kFinishedCompilationChunk} event.
759  size_t bytes_since_last_chunk_ = 0;
760  std::vector<uint8_t> compilation_progress_;
761
762  int outstanding_recompilation_functions_ = 0;
763  TieringState tiering_state_ = kTieredUp;
764
765  // End of fields protected by {callbacks_mutex_}.
766  //////////////////////////////////////////////////////////////////////////////
767
768  // {publish_mutex_} protects {publish_queue_} and {publisher_running_}.
769  base::Mutex publish_mutex_;
770  std::vector<std::unique_ptr<WasmCode>> publish_queue_;
771  bool publisher_running_ = false;
772
773  // Encoding of fields in the {compilation_progress_} vector.
774  using RequiredBaselineTierField = base::BitField8<ExecutionTier, 0, 2>;
775  using RequiredTopTierField = base::BitField8<ExecutionTier, 2, 2>;
776  using ReachedTierField = base::BitField8<ExecutionTier, 4, 2>;
777  using MissingRecompilationField = base::BitField8<bool, 6, 1>;
778};
779
780CompilationStateImpl* Impl(CompilationState* compilation_state) {
781  return reinterpret_cast<CompilationStateImpl*>(compilation_state);
782}
783const CompilationStateImpl* Impl(const CompilationState* compilation_state) {
784  return reinterpret_cast<const CompilationStateImpl*>(compilation_state);
785}
786
787CompilationStateImpl* BackgroundCompileScope::compilation_state() const {
788  DCHECK(native_module_);
789  return Impl(native_module_->compilation_state());
790}
791
792bool BackgroundCompileScope::cancelled() const {
793  return native_module_ == nullptr ||
794         Impl(native_module_->compilation_state())->cancelled();
795}
796
797void UpdateFeatureUseCounts(Isolate* isolate, const WasmFeatures& detected) {
798  using Feature = v8::Isolate::UseCounterFeature;
799  constexpr static std::pair<WasmFeature, Feature> kUseCounters[] = {
800      {kFeature_reftypes, Feature::kWasmRefTypes},
801      {kFeature_simd, Feature::kWasmSimdOpcodes},
802      {kFeature_threads, Feature::kWasmThreadOpcodes},
803      {kFeature_eh, Feature::kWasmExceptionHandling}};
804
805  for (auto& feature : kUseCounters) {
806    if (detected.contains(feature.first)) isolate->CountUsage(feature.second);
807  }
808}
809
810}  // namespace
811
812//////////////////////////////////////////////////////
813// PIMPL implementation of {CompilationState}.
814
815CompilationState::~CompilationState() { Impl(this)->~CompilationStateImpl(); }
816
817void CompilationState::InitCompileJob() { Impl(this)->InitCompileJob(); }
818
819void CompilationState::CancelCompilation() {
820  Impl(this)->CancelCompilation(CompilationStateImpl::kCancelUnconditionally);
821}
822
823void CompilationState::CancelInitialCompilation() {
824  Impl(this)->CancelCompilation(
825      CompilationStateImpl::kCancelInitialCompilation);
826}
827
828void CompilationState::SetError() { Impl(this)->SetError(); }
829
830void CompilationState::SetWireBytesStorage(
831    std::shared_ptr<WireBytesStorage> wire_bytes_storage) {
832  Impl(this)->SetWireBytesStorage(std::move(wire_bytes_storage));
833}
834
835std::shared_ptr<WireBytesStorage> CompilationState::GetWireBytesStorage()
836    const {
837  return Impl(this)->GetWireBytesStorage();
838}
839
840void CompilationState::AddCallback(
841    std::unique_ptr<CompilationEventCallback> callback) {
842  return Impl(this)->AddCallback(std::move(callback));
843}
844
845void CompilationState::WaitForTopTierFinished() {
846  Impl(this)->WaitForCompilationEvent(
847      CompilationEvent::kFinishedTopTierCompilation);
848}
849
850void CompilationState::SetHighPriority() { Impl(this)->SetHighPriority(); }
851
852void CompilationState::InitializeAfterDeserialization(
853    base::Vector<const int> lazy_functions,
854    base::Vector<const int> liftoff_functions) {
855  Impl(this)->InitializeCompilationProgressAfterDeserialization(
856      lazy_functions, liftoff_functions);
857}
858
859bool CompilationState::failed() const { return Impl(this)->failed(); }
860
861bool CompilationState::baseline_compilation_finished() const {
862  return Impl(this)->baseline_compilation_finished();
863}
864
865bool CompilationState::top_tier_compilation_finished() const {
866  return Impl(this)->top_tier_compilation_finished();
867}
868
869bool CompilationState::recompilation_finished() const {
870  return Impl(this)->recompilation_finished();
871}
872
873void CompilationState::set_compilation_id(int compilation_id) {
874  Impl(this)->set_compilation_id(compilation_id);
875}
876
877DynamicTiering CompilationState::dynamic_tiering() const {
878  return Impl(this)->dynamic_tiering();
879}
880
881// static
882std::unique_ptr<CompilationState> CompilationState::New(
883    const std::shared_ptr<NativeModule>& native_module,
884    std::shared_ptr<Counters> async_counters, DynamicTiering dynamic_tiering) {
885  return std::unique_ptr<CompilationState>(reinterpret_cast<CompilationState*>(
886      new CompilationStateImpl(std::move(native_module),
887                               std::move(async_counters), dynamic_tiering)));
888}
889
890// End of PIMPL implementation of {CompilationState}.
891//////////////////////////////////////////////////////
892
893namespace {
894
895ExecutionTier ApplyHintToExecutionTier(WasmCompilationHintTier hint,
896                                       ExecutionTier default_tier) {
897  switch (hint) {
898    case WasmCompilationHintTier::kDefault:
899      return default_tier;
900    case WasmCompilationHintTier::kBaseline:
901      return ExecutionTier::kLiftoff;
902    case WasmCompilationHintTier::kOptimized:
903      return ExecutionTier::kTurbofan;
904  }
905  UNREACHABLE();
906}
907
908const WasmCompilationHint* GetCompilationHint(const WasmModule* module,
909                                              uint32_t func_index) {
910  DCHECK_LE(module->num_imported_functions, func_index);
911  uint32_t hint_index = declared_function_index(module, func_index);
912  const std::vector<WasmCompilationHint>& compilation_hints =
913      module->compilation_hints;
914  if (hint_index < compilation_hints.size()) {
915    return &compilation_hints[hint_index];
916  }
917  return nullptr;
918}
919
920CompileStrategy GetCompileStrategy(const WasmModule* module,
921                                   const WasmFeatures& enabled_features,
922                                   uint32_t func_index, bool lazy_module) {
923  if (lazy_module) return CompileStrategy::kLazy;
924  if (!enabled_features.has_compilation_hints()) {
925    return CompileStrategy::kDefault;
926  }
927  auto* hint = GetCompilationHint(module, func_index);
928  if (hint == nullptr) return CompileStrategy::kDefault;
929  switch (hint->strategy) {
930    case WasmCompilationHintStrategy::kLazy:
931      return CompileStrategy::kLazy;
932    case WasmCompilationHintStrategy::kEager:
933      return CompileStrategy::kEager;
934    case WasmCompilationHintStrategy::kLazyBaselineEagerTopTier:
935      return CompileStrategy::kLazyBaselineEagerTopTier;
936    case WasmCompilationHintStrategy::kDefault:
937      return CompileStrategy::kDefault;
938  }
939}
940
941struct ExecutionTierPair {
942  ExecutionTier baseline_tier;
943  ExecutionTier top_tier;
944};
945
946ExecutionTierPair GetRequestedExecutionTiers(
947    NativeModule* native_module, const WasmFeatures& enabled_features,
948    uint32_t func_index) {
949  const WasmModule* module = native_module->module();
950  ExecutionTierPair result;
951
952  result.baseline_tier = WasmCompilationUnit::GetBaselineExecutionTier(module);
953
954  bool dynamic_tiering =
955      Impl(native_module->compilation_state())->dynamic_tiering() ==
956      DynamicTiering::kEnabled;
957  bool tier_up_enabled = !dynamic_tiering && FLAG_wasm_tier_up;
958  if (module->origin != kWasmOrigin || !tier_up_enabled ||
959      V8_UNLIKELY(FLAG_wasm_tier_up_filter >= 0 &&
960                  func_index !=
961                      static_cast<uint32_t>(FLAG_wasm_tier_up_filter))) {
962    result.top_tier = result.baseline_tier;
963    return result;
964  }
965
966  // Default tiering behaviour.
967  result.top_tier = ExecutionTier::kTurbofan;
968
969  // Check if compilation hints override default tiering behaviour.
970  if (enabled_features.has_compilation_hints()) {
971    const WasmCompilationHint* hint = GetCompilationHint(module, func_index);
972    if (hint != nullptr) {
973      result.baseline_tier =
974          ApplyHintToExecutionTier(hint->baseline_tier, result.baseline_tier);
975      result.top_tier =
976          ApplyHintToExecutionTier(hint->top_tier, result.top_tier);
977    }
978  }
979
980  // Correct top tier if necessary.
981  static_assert(ExecutionTier::kLiftoff < ExecutionTier::kTurbofan,
982                "Assume an order on execution tiers");
983  if (result.baseline_tier > result.top_tier) {
984    result.top_tier = result.baseline_tier;
985  }
986  return result;
987}
988
989// The {CompilationUnitBuilder} builds compilation units and stores them in an
990// internal buffer. The buffer is moved into the working queue of the
991// {CompilationStateImpl} when {Commit} is called.
992class CompilationUnitBuilder {
993 public:
994  explicit CompilationUnitBuilder(NativeModule* native_module)
995      : native_module_(native_module) {}
996
997  void AddUnits(uint32_t func_index) {
998    if (func_index < native_module_->module()->num_imported_functions) {
999      baseline_units_.emplace_back(func_index, ExecutionTier::kNone,
1000                                   kNoDebugging);
1001      return;
1002    }
1003    ExecutionTierPair tiers = GetRequestedExecutionTiers(
1004        native_module_, native_module_->enabled_features(), func_index);
1005    // Compile everything for non-debugging initially. If needed, we will tier
1006    // down when the module is fully compiled. Synchronization would be pretty
1007    // difficult otherwise.
1008    baseline_units_.emplace_back(func_index, tiers.baseline_tier, kNoDebugging);
1009    if (tiers.baseline_tier != tiers.top_tier) {
1010      tiering_units_.emplace_back(func_index, tiers.top_tier, kNoDebugging);
1011    }
1012  }
1013
1014  void AddJSToWasmWrapperUnit(
1015      std::shared_ptr<JSToWasmWrapperCompilationUnit> unit) {
1016    js_to_wasm_wrapper_units_.emplace_back(std::move(unit));
1017  }
1018
1019  void AddBaselineUnit(int func_index, ExecutionTier tier) {
1020    baseline_units_.emplace_back(func_index, tier, kNoDebugging);
1021  }
1022
1023  void AddTopTierUnit(int func_index, ExecutionTier tier) {
1024    tiering_units_.emplace_back(func_index, tier, kNoDebugging);
1025  }
1026
1027  void AddDebugUnit(int func_index) {
1028    baseline_units_.emplace_back(func_index, ExecutionTier::kLiftoff,
1029                                 kForDebugging);
1030  }
1031
1032  void AddRecompilationUnit(int func_index, ExecutionTier tier) {
1033    // For recompilation, just treat all units like baseline units.
1034    baseline_units_.emplace_back(
1035        func_index, tier,
1036        tier == ExecutionTier::kLiftoff ? kForDebugging : kNoDebugging);
1037  }
1038
1039  bool Commit() {
1040    if (baseline_units_.empty() && tiering_units_.empty() &&
1041        js_to_wasm_wrapper_units_.empty()) {
1042      return false;
1043    }
1044    compilation_state()->CommitCompilationUnits(
1045        base::VectorOf(baseline_units_), base::VectorOf(tiering_units_),
1046        base::VectorOf(js_to_wasm_wrapper_units_));
1047    Clear();
1048    return true;
1049  }
1050
1051  void Clear() {
1052    baseline_units_.clear();
1053    tiering_units_.clear();
1054    js_to_wasm_wrapper_units_.clear();
1055  }
1056
1057  const WasmModule* module() { return native_module_->module(); }
1058
1059 private:
1060  CompilationStateImpl* compilation_state() const {
1061    return Impl(native_module_->compilation_state());
1062  }
1063
1064  NativeModule* const native_module_;
1065  std::vector<WasmCompilationUnit> baseline_units_;
1066  std::vector<WasmCompilationUnit> tiering_units_;
1067  std::vector<std::shared_ptr<JSToWasmWrapperCompilationUnit>>
1068      js_to_wasm_wrapper_units_;
1069};
1070
1071void SetCompileError(ErrorThrower* thrower, ModuleWireBytes wire_bytes,
1072                     const WasmFunction* func, const WasmModule* module,
1073                     WasmError error) {
1074  WasmName name = wire_bytes.GetNameOrNull(func, module);
1075  if (name.begin() == nullptr) {
1076    thrower->CompileError("Compiling function #%d failed: %s @+%u",
1077                          func->func_index, error.message().c_str(),
1078                          error.offset());
1079  } else {
1080    TruncatedUserString<> truncated_name(name);
1081    thrower->CompileError("Compiling function #%d:\"%.*s\" failed: %s @+%u",
1082                          func->func_index, truncated_name.length(),
1083                          truncated_name.start(), error.message().c_str(),
1084                          error.offset());
1085  }
1086}
1087
1088DecodeResult ValidateSingleFunction(const WasmModule* module, int func_index,
1089                                    base::Vector<const uint8_t> code,
1090                                    Counters* counters,
1091                                    AccountingAllocator* allocator,
1092                                    WasmFeatures enabled_features) {
1093  const WasmFunction* func = &module->functions[func_index];
1094  FunctionBody body{func->sig, func->code.offset(), code.begin(), code.end()};
1095  DecodeResult result;
1096
1097  WasmFeatures detected;
1098  return VerifyWasmCode(allocator, enabled_features, module, &detected, body);
1099}
1100
1101enum OnlyLazyFunctions : bool {
1102  kAllFunctions = false,
1103  kOnlyLazyFunctions = true,
1104};
1105
1106void ValidateSequentially(
1107    const WasmModule* module, NativeModule* native_module, Counters* counters,
1108    AccountingAllocator* allocator, ErrorThrower* thrower, bool lazy_module,
1109    OnlyLazyFunctions only_lazy_functions = kAllFunctions) {
1110  DCHECK(!thrower->error());
1111  uint32_t start = module->num_imported_functions;
1112  uint32_t end = start + module->num_declared_functions;
1113  auto enabled_features = native_module->enabled_features();
1114  for (uint32_t func_index = start; func_index < end; func_index++) {
1115    // Skip non-lazy functions if requested.
1116    if (only_lazy_functions) {
1117      CompileStrategy strategy =
1118          GetCompileStrategy(module, enabled_features, func_index, lazy_module);
1119      if (strategy != CompileStrategy::kLazy &&
1120          strategy != CompileStrategy::kLazyBaselineEagerTopTier) {
1121        continue;
1122      }
1123    }
1124
1125    ModuleWireBytes wire_bytes{native_module->wire_bytes()};
1126    const WasmFunction* func = &module->functions[func_index];
1127    base::Vector<const uint8_t> code = wire_bytes.GetFunctionBytes(func);
1128    DecodeResult result = ValidateSingleFunction(
1129        module, func_index, code, counters, allocator, enabled_features);
1130    if (result.failed()) {
1131      SetCompileError(thrower, wire_bytes, func, module, result.error());
1132    }
1133  }
1134}
1135
1136bool IsLazyModule(const WasmModule* module) {
1137  return FLAG_wasm_lazy_compilation ||
1138         (FLAG_asm_wasm_lazy_compilation && is_asmjs_module(module));
1139}
1140
1141}  // namespace
1142
1143bool CompileLazy(Isolate* isolate, Handle<WasmInstanceObject> instance,
1144                 int func_index) {
1145  Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
1146  NativeModule* native_module = module_object->native_module();
1147  const WasmModule* module = native_module->module();
1148  auto enabled_features = native_module->enabled_features();
1149  Counters* counters = isolate->counters();
1150
1151  // Put the timer scope around everything, including the {CodeSpaceWriteScope}
1152  // and its destruction, to measure complete overhead (apart from the runtime
1153  // function itself, which has constant overhead).
1154  base::Optional<TimedHistogramScope> lazy_compile_time_scope;
1155  if (base::TimeTicks::IsHighResolution()) {
1156    lazy_compile_time_scope.emplace(counters->wasm_lazy_compile_time());
1157  }
1158
1159  DCHECK(!native_module->lazy_compile_frozen());
1160
1161  TRACE_LAZY("Compiling wasm-function#%d.\n", func_index);
1162
1163  base::ThreadTicks thread_ticks = base::ThreadTicks::IsSupported()
1164                                       ? base::ThreadTicks::Now()
1165                                       : base::ThreadTicks();
1166
1167  CompilationStateImpl* compilation_state =
1168      Impl(native_module->compilation_state());
1169  ExecutionTierPair tiers =
1170      GetRequestedExecutionTiers(native_module, enabled_features, func_index);
1171
1172  DCHECK_LE(native_module->num_imported_functions(), func_index);
1173  DCHECK_LT(func_index, native_module->num_functions());
1174  WasmCompilationUnit baseline_unit{func_index, tiers.baseline_tier,
1175                                    kNoDebugging};
1176  CompilationEnv env = native_module->CreateCompilationEnv();
1177  WasmEngine* engine = GetWasmEngine();
1178  WasmFeatures detected_features;
1179  WasmCompilationResult result = baseline_unit.ExecuteCompilation(
1180      &env, compilation_state->GetWireBytesStorage().get(), counters,
1181      &detected_features);
1182  compilation_state->OnCompilationStopped(detected_features);
1183  if (!thread_ticks.IsNull()) {
1184    native_module->UpdateCPUDuration(
1185        (base::ThreadTicks::Now() - thread_ticks).InMicroseconds(),
1186        tiers.baseline_tier);
1187  }
1188
1189  // During lazy compilation, we can only get compilation errors when
1190  // {--wasm-lazy-validation} is enabled. Otherwise, the module was fully
1191  // verified before starting its execution.
1192  CHECK_IMPLIES(result.failed(), FLAG_wasm_lazy_validation);
1193  const WasmFunction* func = &module->functions[func_index];
1194  if (result.failed()) {
1195    ErrorThrower thrower(isolate, nullptr);
1196    base::Vector<const uint8_t> code =
1197        compilation_state->GetWireBytesStorage()->GetCode(func->code);
1198    DecodeResult decode_result =
1199        ValidateSingleFunction(module, func_index, code, counters,
1200                               engine->allocator(), enabled_features);
1201    CHECK(decode_result.failed());
1202    SetCompileError(&thrower, ModuleWireBytes(native_module->wire_bytes()),
1203                    func, module, decode_result.error());
1204    return false;
1205  }
1206
1207  // Allocate feedback vector if needed.
1208  if (result.feedback_vector_slots > 0) {
1209    DCHECK(FLAG_wasm_speculative_inlining);
1210    Handle<FixedArray> vector = isolate->factory()->NewFixedArrayWithZeroes(
1211        result.feedback_vector_slots);
1212    instance->feedback_vectors().set(
1213        declared_function_index(module, func_index), *vector);
1214  }
1215
1216  WasmCodeRefScope code_ref_scope;
1217  WasmCode* code;
1218  {
1219    CodeSpaceWriteScope code_space_write_scope(native_module);
1220    code = native_module->PublishCode(
1221        native_module->AddCompiledCode(std::move(result)));
1222  }
1223  DCHECK_EQ(func_index, code->index());
1224
1225  if (WasmCode::ShouldBeLogged(isolate)) {
1226    DisallowGarbageCollection no_gc;
1227    Object url_obj = module_object->script().name();
1228    DCHECK(url_obj.IsString() || url_obj.IsUndefined());
1229    std::unique_ptr<char[]> url =
1230        url_obj.IsString() ? String::cast(url_obj).ToCString() : nullptr;
1231    code->LogCode(isolate, url.get(), module_object->script().id());
1232  }
1233
1234  counters->wasm_lazily_compiled_functions()->Increment();
1235
1236  const bool lazy_module = IsLazyModule(module);
1237  if (GetCompileStrategy(module, enabled_features, func_index, lazy_module) ==
1238          CompileStrategy::kLazy &&
1239      tiers.baseline_tier < tiers.top_tier) {
1240    WasmCompilationUnit tiering_unit{func_index, tiers.top_tier, kNoDebugging};
1241    compilation_state->CommitTopTierCompilationUnit(tiering_unit);
1242  }
1243
1244  return true;
1245}
1246
1247class TransitiveTypeFeedbackProcessor {
1248 public:
1249  TransitiveTypeFeedbackProcessor(const WasmModule* module,
1250                                  Handle<WasmInstanceObject> instance,
1251                                  int func_index)
1252      : instance_(instance),
1253        feedback_for_function_(module->type_feedback.feedback_for_function) {
1254    base::MutexGuard mutex_guard(&module->type_feedback.mutex);
1255    queue_.insert(func_index);
1256    while (!queue_.empty()) {
1257      auto next = queue_.cbegin();
1258      Process(*next);
1259      queue_.erase(next);
1260    }
1261  }
1262
1263 private:
1264  void Process(int func_index);
1265
1266  void EnqueueCallees(std::vector<CallSiteFeedback> feedback) {
1267    for (size_t i = 0; i < feedback.size(); i++) {
1268      int func = feedback[i].function_index;
1269      // TODO(jkummerow): Find a way to get the target function ID for
1270      // direct calls (which currently requires decoding the function).
1271      if (func == -1) continue;
1272      // Don't spend time on calls that have never been executed.
1273      if (feedback[i].absolute_call_frequency == 0) continue;
1274      // Don't recompute feedback that has already been processed.
1275      auto existing = feedback_for_function_.find(func);
1276      if (existing != feedback_for_function_.end() &&
1277          existing->second.feedback_vector.size() > 0) {
1278        continue;
1279      }
1280      queue_.insert(func);
1281    }
1282  }
1283
1284  Handle<WasmInstanceObject> instance_;
1285  std::map<uint32_t, FunctionTypeFeedback>& feedback_for_function_;
1286  std::unordered_set<int> queue_;
1287};
1288
1289void TransitiveTypeFeedbackProcessor::Process(int func_index) {
1290  int which_vector = declared_function_index(instance_->module(), func_index);
1291  Object maybe_feedback = instance_->feedback_vectors().get(which_vector);
1292  if (!maybe_feedback.IsFixedArray()) return;
1293  FixedArray feedback = FixedArray::cast(maybe_feedback);
1294  std::vector<CallSiteFeedback> result(feedback.length() / 2);
1295  int imported_functions =
1296      static_cast<int>(instance_->module()->num_imported_functions);
1297  for (int i = 0; i < feedback.length(); i += 2) {
1298    Object value = feedback.get(i);
1299    if (value.IsWasmInternalFunction() &&
1300        WasmExportedFunction::IsWasmExportedFunction(
1301            WasmInternalFunction::cast(value).external())) {
1302      // Monomorphic, and the internal function points to a wasm-generated
1303      // external function (WasmExportedFunction). Mark the target for inlining
1304      // if it's defined in the same module.
1305      WasmExportedFunction target = WasmExportedFunction::cast(
1306          WasmInternalFunction::cast(value).external());
1307      if (target.instance() == *instance_ &&
1308          target.function_index() >= imported_functions) {
1309        if (FLAG_trace_wasm_speculative_inlining) {
1310          PrintF("[Function #%d call_ref #%d inlineable (monomorphic)]\n",
1311                 func_index, i / 2);
1312        }
1313        int32_t count = Smi::cast(feedback.get(i + 1)).value();
1314        result[i / 2] = {target.function_index(), count};
1315        continue;
1316      }
1317    } else if (value.IsFixedArray()) {
1318      // Polymorphic. Pick a target for inlining if there is one that was
1319      // seen for most calls, and matches the requirements of the monomorphic
1320      // case.
1321      FixedArray polymorphic = FixedArray::cast(value);
1322      size_t total_count = 0;
1323      for (int j = 0; j < polymorphic.length(); j += 2) {
1324        total_count += Smi::cast(polymorphic.get(j + 1)).value();
1325      }
1326      int found_target = -1;
1327      int found_count = -1;
1328      double best_frequency = 0;
1329      for (int j = 0; j < polymorphic.length(); j += 2) {
1330        int32_t this_count = Smi::cast(polymorphic.get(j + 1)).value();
1331        double frequency = static_cast<double>(this_count) / total_count;
1332        if (frequency > best_frequency) best_frequency = frequency;
1333        if (frequency < 0.8) continue;
1334
1335        // We reject this polymorphic entry if:
1336        // - it is not defined,
1337        // - it is not a wasm-defined function (WasmExportedFunction)
1338        // - it was not defined in this module.
1339        if (!polymorphic.get(j).IsWasmInternalFunction()) continue;
1340        WasmInternalFunction internal =
1341            WasmInternalFunction::cast(polymorphic.get(j));
1342        if (!WasmExportedFunction::IsWasmExportedFunction(
1343                internal.external())) {
1344          continue;
1345        }
1346        WasmExportedFunction target =
1347            WasmExportedFunction::cast(internal.external());
1348        if (target.instance() != *instance_ ||
1349            target.function_index() < imported_functions) {
1350          continue;
1351        }
1352
1353        found_target = target.function_index();
1354        found_count = static_cast<int>(this_count);
1355        if (FLAG_trace_wasm_speculative_inlining) {
1356          PrintF("[Function #%d call_ref #%d inlineable (polymorphic %f)]\n",
1357                 func_index, i / 2, frequency);
1358        }
1359        break;
1360      }
1361      if (found_target >= 0) {
1362        result[i / 2] = {found_target, found_count};
1363        continue;
1364      } else if (FLAG_trace_wasm_speculative_inlining) {
1365        PrintF("[Function #%d call_ref #%d: best frequency %f]\n", func_index,
1366               i / 2, best_frequency);
1367      }
1368    } else if (value.IsSmi()) {
1369      // Direct call, just collecting call count.
1370      int count = Smi::cast(value).value();
1371      if (FLAG_trace_wasm_speculative_inlining) {
1372        PrintF("[Function #%d call_direct #%d: frequency %d]\n", func_index,
1373               i / 2, count);
1374      }
1375      result[i / 2] = {-1, count};
1376      continue;
1377    }
1378    // If we fall through to here, then this call isn't eligible for inlining.
1379    // Possible reasons: uninitialized or megamorphic feedback; or monomorphic
1380    // or polymorphic that didn't meet our requirements.
1381    if (FLAG_trace_wasm_speculative_inlining) {
1382      PrintF("[Function #%d call_ref #%d *not* inlineable]\n", func_index,
1383             i / 2);
1384    }
1385    result[i / 2] = {-1, -1};
1386  }
1387  EnqueueCallees(result);
1388  feedback_for_function_[func_index].feedback_vector = std::move(result);
1389}
1390
1391void TriggerTierUp(Isolate* isolate, NativeModule* native_module,
1392                   int func_index, Handle<WasmInstanceObject> instance) {
1393  CompilationStateImpl* compilation_state =
1394      Impl(native_module->compilation_state());
1395  WasmCompilationUnit tiering_unit{func_index, ExecutionTier::kTurbofan,
1396                                   kNoDebugging};
1397
1398  const WasmModule* module = native_module->module();
1399  size_t priority;
1400  {
1401    base::MutexGuard mutex_guard(&module->type_feedback.mutex);
1402    int saved_priority =
1403        module->type_feedback.feedback_for_function[func_index].tierup_priority;
1404    saved_priority++;
1405    module->type_feedback.feedback_for_function[func_index].tierup_priority =
1406        saved_priority;
1407    // Continue to creating a compilation unit if this is the first time
1408    // we detect this function as hot, and create a new higher-priority unit
1409    // if the number of tierup checks is a power of two (at least 4).
1410    if (saved_priority > 1 &&
1411        (saved_priority < 4 || (saved_priority & (saved_priority - 1)) != 0)) {
1412      return;
1413    }
1414    priority = saved_priority;
1415  }
1416  if (FLAG_wasm_speculative_inlining) {
1417    // TODO(jkummerow): we could have collisions here if different instances
1418    // of the same module have collected different feedback. If that ever
1419    // becomes a problem, figure out a solution.
1420    TransitiveTypeFeedbackProcessor process(module, instance, func_index);
1421  }
1422
1423  compilation_state->AddTopTierPriorityCompilationUnit(tiering_unit, priority);
1424}
1425
1426namespace {
1427
1428void RecordStats(const Code code, Counters* counters) {
1429  counters->wasm_generated_code_size()->Increment(code.raw_body_size());
1430  counters->wasm_reloc_size()->Increment(code.relocation_info().length());
1431}
1432
1433enum CompilationExecutionResult : int8_t { kNoMoreUnits, kYield };
1434
1435CompilationExecutionResult ExecuteJSToWasmWrapperCompilationUnits(
1436    std::weak_ptr<NativeModule> native_module, JobDelegate* delegate) {
1437  std::shared_ptr<JSToWasmWrapperCompilationUnit> wrapper_unit = nullptr;
1438  int num_processed_wrappers = 0;
1439
1440  OperationsBarrier::Token wrapper_compilation_token;
1441  Isolate* isolate;
1442
1443  {
1444    BackgroundCompileScope compile_scope(native_module);
1445    if (compile_scope.cancelled()) return kYield;
1446    wrapper_unit = compile_scope.compilation_state()
1447                       ->GetNextJSToWasmWrapperCompilationUnit();
1448    if (!wrapper_unit) return kNoMoreUnits;
1449    isolate = wrapper_unit->isolate();
1450    wrapper_compilation_token =
1451        wasm::GetWasmEngine()->StartWrapperCompilation(isolate);
1452    if (!wrapper_compilation_token) return kNoMoreUnits;
1453  }
1454
1455  TRACE_EVENT0("v8.wasm", "wasm.JSToWasmWrapperCompilation");
1456  while (true) {
1457    DCHECK_EQ(isolate, wrapper_unit->isolate());
1458    wrapper_unit->Execute();
1459    ++num_processed_wrappers;
1460    bool yield = delegate && delegate->ShouldYield();
1461    BackgroundCompileScope compile_scope(native_module);
1462    if (compile_scope.cancelled()) return kYield;
1463    if (yield ||
1464        !(wrapper_unit = compile_scope.compilation_state()
1465                             ->GetNextJSToWasmWrapperCompilationUnit())) {
1466      compile_scope.compilation_state()->OnFinishedJSToWasmWrapperUnits(
1467          num_processed_wrappers);
1468      return yield ? kYield : kNoMoreUnits;
1469    }
1470  }
1471}
1472
1473namespace {
1474const char* GetCompilationEventName(const WasmCompilationUnit& unit,
1475                                    const CompilationEnv& env) {
1476  ExecutionTier tier = unit.tier();
1477  if (tier == ExecutionTier::kLiftoff) {
1478    return "wasm.BaselineCompilation";
1479  }
1480  if (tier == ExecutionTier::kTurbofan) {
1481    return "wasm.TopTierCompilation";
1482  }
1483  if (unit.func_index() <
1484      static_cast<int>(env.module->num_imported_functions)) {
1485    return "wasm.WasmToJSWrapperCompilation";
1486  }
1487  return "wasm.OtherCompilation";
1488}
1489}  // namespace
1490
1491constexpr uint8_t kMainTaskId = 0;
1492
1493// Run by the {BackgroundCompileJob} (on any thread).
1494CompilationExecutionResult ExecuteCompilationUnits(
1495    std::weak_ptr<NativeModule> native_module, Counters* counters,
1496    JobDelegate* delegate, CompileBaselineOnly baseline_only) {
1497  TRACE_EVENT0("v8.wasm", "wasm.ExecuteCompilationUnits");
1498
1499  // Execute JS to Wasm wrapper units first, so that they are ready to be
1500  // finalized by the main thread when the kFinishedBaselineCompilation event is
1501  // triggered.
1502  if (ExecuteJSToWasmWrapperCompilationUnits(native_module, delegate) ==
1503      kYield) {
1504    return kYield;
1505  }
1506
1507  // These fields are initialized in a {BackgroundCompileScope} before
1508  // starting compilation.
1509  base::Optional<CompilationEnv> env;
1510  std::shared_ptr<WireBytesStorage> wire_bytes;
1511  std::shared_ptr<const WasmModule> module;
1512  // Task 0 is any main thread (there might be multiple from multiple isolates),
1513  // worker threads start at 1 (thus the "+ 1").
1514  STATIC_ASSERT(kMainTaskId == 0);
1515  int task_id = delegate ? (int{delegate->GetTaskId()} + 1) : kMainTaskId;
1516  DCHECK_LE(0, task_id);
1517  CompilationUnitQueues::Queue* queue;
1518  base::Optional<WasmCompilationUnit> unit;
1519
1520  WasmFeatures detected_features = WasmFeatures::None();
1521
1522  base::ThreadTicks thread_ticks = base::ThreadTicks::IsSupported()
1523                                       ? base::ThreadTicks::Now()
1524                                       : base::ThreadTicks();
1525
1526  // Preparation (synchronized): Initialize the fields above and get the first
1527  // compilation unit.
1528  {
1529    BackgroundCompileScope compile_scope(native_module);
1530    if (compile_scope.cancelled()) return kYield;
1531    env.emplace(compile_scope.native_module()->CreateCompilationEnv());
1532    wire_bytes = compile_scope.compilation_state()->GetWireBytesStorage();
1533    module = compile_scope.native_module()->shared_module();
1534    queue = compile_scope.compilation_state()->GetQueueForCompileTask(task_id);
1535    unit = compile_scope.compilation_state()->GetNextCompilationUnit(
1536        queue, baseline_only);
1537    if (!unit) return kNoMoreUnits;
1538  }
1539  TRACE_COMPILE("ExecuteCompilationUnits (task id %d)\n", task_id);
1540
1541  std::vector<WasmCompilationResult> results_to_publish;
1542  while (true) {
1543    ExecutionTier current_tier = unit->tier();
1544    const char* event_name = GetCompilationEventName(unit.value(), env.value());
1545    TRACE_EVENT0("v8.wasm", event_name);
1546    while (unit->tier() == current_tier) {
1547      // (asynchronous): Execute the compilation.
1548      WasmCompilationResult result = unit->ExecuteCompilation(
1549          &env.value(), wire_bytes.get(), counters, &detected_features);
1550      results_to_publish.emplace_back(std::move(result));
1551
1552      bool yield = delegate && delegate->ShouldYield();
1553
1554      // (synchronized): Publish the compilation result and get the next unit.
1555      BackgroundCompileScope compile_scope(native_module);
1556      if (compile_scope.cancelled()) return kYield;
1557
1558      if (!results_to_publish.back().succeeded()) {
1559        compile_scope.compilation_state()->SetError();
1560        return kNoMoreUnits;
1561      }
1562
1563      if (!unit->for_debugging() && result.result_tier != current_tier) {
1564        compile_scope.native_module()->AddLiftoffBailout();
1565      }
1566
1567      // Yield or get next unit.
1568      if (yield ||
1569          !(unit = compile_scope.compilation_state()->GetNextCompilationUnit(
1570                queue, baseline_only))) {
1571        if (!thread_ticks.IsNull()) {
1572          compile_scope.native_module()->UpdateCPUDuration(
1573              (base::ThreadTicks::Now() - thread_ticks).InMicroseconds(),
1574              current_tier);
1575        }
1576        std::vector<std::unique_ptr<WasmCode>> unpublished_code =
1577            compile_scope.native_module()->AddCompiledCode(
1578                base::VectorOf(std::move(results_to_publish)));
1579        results_to_publish.clear();
1580        compile_scope.compilation_state()->SchedulePublishCompilationResults(
1581            std::move(unpublished_code));
1582        compile_scope.compilation_state()->OnCompilationStopped(
1583            detected_features);
1584        return yield ? kYield : kNoMoreUnits;
1585      }
1586
1587      // Publish after finishing a certain amount of units, to avoid contention
1588      // when all threads publish at the end.
1589      bool batch_full =
1590          queue->ShouldPublish(static_cast<int>(results_to_publish.size()));
1591      // Also publish each time the compilation tier changes from Liftoff to
1592      // TurboFan, such that we immediately publish the baseline compilation
1593      // results to start execution, and do not wait for a batch to fill up.
1594      bool liftoff_finished = unit->tier() != current_tier &&
1595                              unit->tier() == ExecutionTier::kTurbofan;
1596      if (batch_full || liftoff_finished) {
1597        if (!thread_ticks.IsNull()) {
1598          base::ThreadTicks thread_ticks_now = base::ThreadTicks::Now();
1599          compile_scope.native_module()->UpdateCPUDuration(
1600              (thread_ticks_now - thread_ticks).InMicroseconds(), current_tier);
1601          thread_ticks = thread_ticks_now;
1602        }
1603        std::vector<std::unique_ptr<WasmCode>> unpublished_code =
1604            compile_scope.native_module()->AddCompiledCode(
1605                base::VectorOf(std::move(results_to_publish)));
1606        results_to_publish.clear();
1607        compile_scope.compilation_state()->SchedulePublishCompilationResults(
1608            std::move(unpublished_code));
1609      }
1610    }
1611  }
1612  UNREACHABLE();
1613}
1614
1615using JSToWasmWrapperKey = std::pair<bool, FunctionSig>;
1616
1617// Returns the number of units added.
1618int AddExportWrapperUnits(Isolate* isolate, NativeModule* native_module,
1619                          CompilationUnitBuilder* builder) {
1620  std::unordered_set<JSToWasmWrapperKey, base::hash<JSToWasmWrapperKey>> keys;
1621  for (auto exp : native_module->module()->export_table) {
1622    if (exp.kind != kExternalFunction) continue;
1623    auto& function = native_module->module()->functions[exp.index];
1624    JSToWasmWrapperKey key(function.imported, *function.sig);
1625    if (keys.insert(key).second) {
1626      auto unit = std::make_shared<JSToWasmWrapperCompilationUnit>(
1627          isolate, function.sig, native_module->module(), function.imported,
1628          native_module->enabled_features(),
1629          JSToWasmWrapperCompilationUnit::kAllowGeneric);
1630      builder->AddJSToWasmWrapperUnit(std::move(unit));
1631    }
1632  }
1633
1634  return static_cast<int>(keys.size());
1635}
1636
1637// Returns the number of units added.
1638int AddImportWrapperUnits(NativeModule* native_module,
1639                          CompilationUnitBuilder* builder) {
1640  std::unordered_set<WasmImportWrapperCache::CacheKey,
1641                     WasmImportWrapperCache::CacheKeyHash>
1642      keys;
1643  int num_imported_functions = native_module->num_imported_functions();
1644  for (int func_index = 0; func_index < num_imported_functions; func_index++) {
1645    const FunctionSig* sig = native_module->module()->functions[func_index].sig;
1646    if (!IsJSCompatibleSignature(sig, native_module->module(),
1647                                 native_module->enabled_features())) {
1648      continue;
1649    }
1650    WasmImportWrapperCache::CacheKey key(
1651        compiler::kDefaultImportCallKind, sig,
1652        static_cast<int>(sig->parameter_count()), kNoSuspend);
1653    auto it = keys.insert(key);
1654    if (it.second) {
1655      // Ensure that all keys exist in the cache, so that we can populate the
1656      // cache later without locking.
1657      (*native_module->import_wrapper_cache())[key] = nullptr;
1658      builder->AddUnits(func_index);
1659    }
1660  }
1661  return static_cast<int>(keys.size());
1662}
1663
1664void InitializeLazyCompilation(NativeModule* native_module) {
1665  const bool lazy_module = IsLazyModule(native_module->module());
1666  auto* module = native_module->module();
1667
1668  uint32_t start = module->num_imported_functions;
1669  uint32_t end = start + module->num_declared_functions;
1670  base::Optional<CodeSpaceWriteScope> lazy_code_space_write_scope;
1671  for (uint32_t func_index = start; func_index < end; func_index++) {
1672    CompileStrategy strategy = GetCompileStrategy(
1673        module, native_module->enabled_features(), func_index, lazy_module);
1674    if (strategy == CompileStrategy::kLazy ||
1675        strategy == CompileStrategy::kLazyBaselineEagerTopTier) {
1676      // Open a single scope for all following calls to {UseLazyStub()}, instead
1677      // of flipping page permissions for each {func_index} individually.
1678      if (!lazy_code_space_write_scope.has_value()) {
1679        lazy_code_space_write_scope.emplace(native_module);
1680      }
1681      native_module->UseLazyStub(func_index);
1682    }
1683  }
1684}
1685
1686std::unique_ptr<CompilationUnitBuilder> InitializeCompilation(
1687    Isolate* isolate, NativeModule* native_module) {
1688  InitializeLazyCompilation(native_module);
1689  CompilationStateImpl* compilation_state =
1690      Impl(native_module->compilation_state());
1691  const bool lazy_module = IsLazyModule(native_module->module());
1692  auto builder = std::make_unique<CompilationUnitBuilder>(native_module);
1693  int num_import_wrappers = AddImportWrapperUnits(native_module, builder.get());
1694  int num_export_wrappers =
1695      AddExportWrapperUnits(isolate, native_module, builder.get());
1696  compilation_state->InitializeCompilationProgress(
1697      lazy_module, num_import_wrappers, num_export_wrappers);
1698  return builder;
1699}
1700
1701bool MayCompriseLazyFunctions(const WasmModule* module,
1702                              const WasmFeatures& enabled_features,
1703                              bool lazy_module) {
1704  if (lazy_module || enabled_features.has_compilation_hints()) return true;
1705#ifdef ENABLE_SLOW_DCHECKS
1706  int start = module->num_imported_functions;
1707  int end = start + module->num_declared_functions;
1708  for (int func_index = start; func_index < end; func_index++) {
1709    SLOW_DCHECK(GetCompileStrategy(module, enabled_features, func_index,
1710                                   lazy_module) != CompileStrategy::kLazy);
1711  }
1712#endif
1713  return false;
1714}
1715
1716class CompilationTimeCallback : public CompilationEventCallback {
1717 public:
1718  enum CompileMode { kSynchronous, kAsync, kStreaming };
1719  explicit CompilationTimeCallback(
1720      std::shared_ptr<Counters> async_counters,
1721      std::shared_ptr<metrics::Recorder> metrics_recorder,
1722      v8::metrics::Recorder::ContextId context_id,
1723      std::weak_ptr<NativeModule> native_module, CompileMode compile_mode)
1724      : start_time_(base::TimeTicks::Now()),
1725        async_counters_(std::move(async_counters)),
1726        metrics_recorder_(std::move(metrics_recorder)),
1727        context_id_(context_id),
1728        native_module_(std::move(native_module)),
1729        compile_mode_(compile_mode) {}
1730
1731  // Keep this callback alive to be able to record caching metrics.
1732  ReleaseAfterFinalEvent release_after_final_event() override {
1733    return CompilationEventCallback::ReleaseAfterFinalEvent::kKeep;
1734  }
1735
1736  void call(CompilationEvent compilation_event) override {
1737    DCHECK(base::TimeTicks::IsHighResolution());
1738    std::shared_ptr<NativeModule> native_module = native_module_.lock();
1739    if (!native_module) return;
1740    auto now = base::TimeTicks::Now();
1741    auto duration = now - start_time_;
1742    if (compilation_event == CompilationEvent::kFinishedBaselineCompilation) {
1743      // Reset {start_time_} to measure tier-up time.
1744      start_time_ = now;
1745      if (compile_mode_ != kSynchronous) {
1746        TimedHistogram* histogram =
1747            compile_mode_ == kAsync
1748                ? async_counters_->wasm_async_compile_wasm_module_time()
1749                : async_counters_->wasm_streaming_compile_wasm_module_time();
1750        histogram->AddSample(static_cast<int>(duration.InMicroseconds()));
1751      }
1752
1753      v8::metrics::WasmModuleCompiled event{
1754          (compile_mode_ != kSynchronous),         // async
1755          (compile_mode_ == kStreaming),           // streamed
1756          false,                                   // cached
1757          false,                                   // deserialized
1758          FLAG_wasm_lazy_compilation,              // lazy
1759          true,                                    // success
1760          native_module->liftoff_code_size(),      // code_size_in_bytes
1761          native_module->liftoff_bailout_count(),  // liftoff_bailout_count
1762          duration.InMicroseconds(),               // wall_clock_duration_in_us
1763          static_cast<int64_t>(                    // cpu_time_duration_in_us
1764              native_module->baseline_compilation_cpu_duration())};
1765      metrics_recorder_->DelayMainThreadEvent(event, context_id_);
1766    }
1767    if (compilation_event == CompilationEvent::kFinishedTopTierCompilation) {
1768      TimedHistogram* histogram = async_counters_->wasm_tier_up_module_time();
1769      histogram->AddSample(static_cast<int>(duration.InMicroseconds()));
1770
1771      v8::metrics::WasmModuleTieredUp event{
1772          FLAG_wasm_lazy_compilation,           // lazy
1773          native_module->turbofan_code_size(),  // code_size_in_bytes
1774          duration.InMicroseconds(),            // wall_clock_duration_in_us
1775          static_cast<int64_t>(                 // cpu_time_duration_in_us
1776              native_module->tier_up_cpu_duration())};
1777      metrics_recorder_->DelayMainThreadEvent(event, context_id_);
1778    }
1779    if (compilation_event == CompilationEvent::kFailedCompilation) {
1780      v8::metrics::WasmModuleCompiled event{
1781          (compile_mode_ != kSynchronous),         // async
1782          (compile_mode_ == kStreaming),           // streamed
1783          false,                                   // cached
1784          false,                                   // deserialized
1785          FLAG_wasm_lazy_compilation,              // lazy
1786          false,                                   // success
1787          native_module->liftoff_code_size(),      // code_size_in_bytes
1788          native_module->liftoff_bailout_count(),  // liftoff_bailout_count
1789          duration.InMicroseconds(),               // wall_clock_duration_in_us
1790          static_cast<int64_t>(                    // cpu_time_duration_in_us
1791              native_module->baseline_compilation_cpu_duration())};
1792      metrics_recorder_->DelayMainThreadEvent(event, context_id_);
1793    }
1794  }
1795
1796 private:
1797  base::TimeTicks start_time_;
1798  const std::shared_ptr<Counters> async_counters_;
1799  std::shared_ptr<metrics::Recorder> metrics_recorder_;
1800  v8::metrics::Recorder::ContextId context_id_;
1801  std::weak_ptr<NativeModule> native_module_;
1802  const CompileMode compile_mode_;
1803};
1804
1805void CompileNativeModule(Isolate* isolate,
1806                         v8::metrics::Recorder::ContextId context_id,
1807                         ErrorThrower* thrower, const WasmModule* wasm_module,
1808                         std::shared_ptr<NativeModule> native_module,
1809                         Handle<FixedArray>* export_wrappers_out) {
1810  CHECK(!FLAG_jitless);
1811  ModuleWireBytes wire_bytes(native_module->wire_bytes());
1812  const bool lazy_module = IsLazyModule(wasm_module);
1813  if (!FLAG_wasm_lazy_validation && wasm_module->origin == kWasmOrigin &&
1814      MayCompriseLazyFunctions(wasm_module, native_module->enabled_features(),
1815                               lazy_module)) {
1816    // Validate wasm modules for lazy compilation if requested. Never validate
1817    // asm.js modules as these are valid by construction (additionally a CHECK
1818    // will catch this during lazy compilation).
1819    ValidateSequentially(wasm_module, native_module.get(), isolate->counters(),
1820                         isolate->allocator(), thrower, lazy_module,
1821                         kOnlyLazyFunctions);
1822    // On error: Return and leave the module in an unexecutable state.
1823    if (thrower->error()) return;
1824  }
1825
1826  DCHECK_GE(kMaxInt, native_module->module()->num_declared_functions);
1827
1828  // The callback captures a shared ptr to the semaphore.
1829  auto* compilation_state = Impl(native_module->compilation_state());
1830  if (base::TimeTicks::IsHighResolution()) {
1831    compilation_state->AddCallback(std::make_unique<CompilationTimeCallback>(
1832        isolate->async_counters(), isolate->metrics_recorder(), context_id,
1833        native_module, CompilationTimeCallback::kSynchronous));
1834  }
1835
1836  // Initialize the compilation units and kick off background compile tasks.
1837  std::unique_ptr<CompilationUnitBuilder> builder =
1838      InitializeCompilation(isolate, native_module.get());
1839  compilation_state->InitializeCompilationUnits(std::move(builder));
1840
1841  compilation_state->WaitForCompilationEvent(
1842      CompilationEvent::kFinishedExportWrappers);
1843
1844  if (compilation_state->failed()) {
1845    DCHECK_IMPLIES(lazy_module, !FLAG_wasm_lazy_validation);
1846    ValidateSequentially(wasm_module, native_module.get(), isolate->counters(),
1847                         isolate->allocator(), thrower, lazy_module);
1848    CHECK(thrower->error());
1849    return;
1850  }
1851
1852  compilation_state->FinalizeJSToWasmWrappers(isolate, native_module->module(),
1853                                              export_wrappers_out);
1854
1855  compilation_state->WaitForCompilationEvent(
1856      CompilationEvent::kFinishedBaselineCompilation);
1857
1858  compilation_state->PublishDetectedFeatures(isolate);
1859
1860  if (compilation_state->failed()) {
1861    DCHECK_IMPLIES(lazy_module, !FLAG_wasm_lazy_validation);
1862    ValidateSequentially(wasm_module, native_module.get(), isolate->counters(),
1863                         isolate->allocator(), thrower, lazy_module);
1864    CHECK(thrower->error());
1865  }
1866}
1867
1868class BackgroundCompileJob final : public JobTask {
1869 public:
1870  explicit BackgroundCompileJob(std::weak_ptr<NativeModule> native_module,
1871                                std::shared_ptr<Counters> async_counters)
1872      : native_module_(std::move(native_module)),
1873        engine_barrier_(GetWasmEngine()->GetBarrierForBackgroundCompile()),
1874        async_counters_(std::move(async_counters)) {}
1875
1876  void Run(JobDelegate* delegate) override {
1877    auto engine_scope = engine_barrier_->TryLock();
1878    if (!engine_scope) return;
1879    ExecuteCompilationUnits(native_module_, async_counters_.get(), delegate,
1880                            kBaselineOrTopTier);
1881  }
1882
1883  size_t GetMaxConcurrency(size_t worker_count) const override {
1884    BackgroundCompileScope compile_scope(native_module_);
1885    if (compile_scope.cancelled()) return 0;
1886    // NumOutstandingCompilations() does not reflect the units that running
1887    // workers are processing, thus add the current worker count to that number.
1888    return std::min(
1889        static_cast<size_t>(FLAG_wasm_num_compilation_tasks),
1890        worker_count +
1891            compile_scope.compilation_state()->NumOutstandingCompilations());
1892  }
1893
1894 private:
1895  std::weak_ptr<NativeModule> native_module_;
1896  std::shared_ptr<OperationsBarrier> engine_barrier_;
1897  const std::shared_ptr<Counters> async_counters_;
1898};
1899
1900}  // namespace
1901
1902std::shared_ptr<NativeModule> CompileToNativeModule(
1903    Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
1904    std::shared_ptr<const WasmModule> module, const ModuleWireBytes& wire_bytes,
1905    Handle<FixedArray>* export_wrappers_out, int compilation_id,
1906    v8::metrics::Recorder::ContextId context_id) {
1907  const WasmModule* wasm_module = module.get();
1908  WasmEngine* engine = GetWasmEngine();
1909  base::OwnedVector<uint8_t> wire_bytes_copy =
1910      base::OwnedVector<uint8_t>::Of(wire_bytes.module_bytes());
1911  // Prefer {wire_bytes_copy} to {wire_bytes.module_bytes()} for the temporary
1912  // cache key. When we eventually install the module in the cache, the wire
1913  // bytes of the temporary key and the new key have the same base pointer and
1914  // we can skip the full bytes comparison.
1915  std::shared_ptr<NativeModule> native_module = engine->MaybeGetNativeModule(
1916      wasm_module->origin, wire_bytes_copy.as_vector(), isolate);
1917  if (native_module) {
1918    // TODO(thibaudm): Look into sharing export wrappers.
1919    CompileJsToWasmWrappers(isolate, wasm_module, export_wrappers_out);
1920    return native_module;
1921  }
1922
1923  TimedHistogramScope wasm_compile_module_time_scope(SELECT_WASM_COUNTER(
1924      isolate->counters(), wasm_module->origin, wasm_compile, module_time));
1925
1926  // Embedder usage count for declared shared memories.
1927  if (wasm_module->has_shared_memory) {
1928    isolate->CountUsage(v8::Isolate::UseCounterFeature::kWasmSharedMemory);
1929  }
1930
1931  // Create a new {NativeModule} first.
1932  const bool include_liftoff = module->origin == kWasmOrigin && FLAG_liftoff;
1933  DynamicTiering dynamic_tiering = isolate->IsWasmDynamicTieringEnabled()
1934                                       ? DynamicTiering::kEnabled
1935                                       : DynamicTiering::kDisabled;
1936  size_t code_size_estimate =
1937      wasm::WasmCodeManager::EstimateNativeModuleCodeSize(
1938          module.get(), include_liftoff, dynamic_tiering);
1939  native_module =
1940      engine->NewNativeModule(isolate, enabled, module, code_size_estimate);
1941  native_module->SetWireBytes(std::move(wire_bytes_copy));
1942  native_module->compilation_state()->set_compilation_id(compilation_id);
1943  // Sync compilation is user blocking, so we increase the priority.
1944  native_module->compilation_state()->SetHighPriority();
1945
1946  CompileNativeModule(isolate, context_id, thrower, wasm_module, native_module,
1947                      export_wrappers_out);
1948  bool cache_hit = !engine->UpdateNativeModuleCache(thrower->error(),
1949                                                    &native_module, isolate);
1950  if (thrower->error()) return {};
1951
1952  if (cache_hit) {
1953    CompileJsToWasmWrappers(isolate, wasm_module, export_wrappers_out);
1954    return native_module;
1955  }
1956
1957  // Ensure that the code objects are logged before returning.
1958  engine->LogOutstandingCodesForIsolate(isolate);
1959
1960  return native_module;
1961}
1962
1963void RecompileNativeModule(NativeModule* native_module,
1964                           TieringState tiering_state) {
1965  // Install a callback to notify us once background recompilation finished.
1966  auto recompilation_finished_semaphore = std::make_shared<base::Semaphore>(0);
1967  auto* compilation_state = Impl(native_module->compilation_state());
1968
1969  class RecompilationFinishedCallback : public CompilationEventCallback {
1970   public:
1971    explicit RecompilationFinishedCallback(
1972        std::shared_ptr<base::Semaphore> recompilation_finished_semaphore)
1973        : recompilation_finished_semaphore_(
1974              std::move(recompilation_finished_semaphore)) {}
1975
1976    void call(CompilationEvent event) override {
1977      DCHECK_NE(CompilationEvent::kFailedCompilation, event);
1978      if (event == CompilationEvent::kFinishedRecompilation) {
1979        recompilation_finished_semaphore_->Signal();
1980      }
1981    }
1982
1983   private:
1984    std::shared_ptr<base::Semaphore> recompilation_finished_semaphore_;
1985  };
1986
1987  // The callback captures a shared ptr to the semaphore.
1988  // Initialize the compilation units and kick off background compile tasks.
1989  compilation_state->InitializeRecompilation(
1990      tiering_state, std::make_unique<RecompilationFinishedCallback>(
1991                         recompilation_finished_semaphore));
1992
1993  constexpr JobDelegate* kNoDelegate = nullptr;
1994  ExecuteCompilationUnits(compilation_state->native_module_weak(),
1995                          compilation_state->counters(), kNoDelegate,
1996                          kBaselineOnly);
1997  recompilation_finished_semaphore->Wait();
1998  DCHECK(!compilation_state->failed());
1999}
2000
2001AsyncCompileJob::AsyncCompileJob(
2002    Isolate* isolate, const WasmFeatures& enabled,
2003    std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
2004    Handle<Context> incumbent_context, const char* api_method_name,
2005    std::shared_ptr<CompilationResultResolver> resolver, int compilation_id)
2006    : isolate_(isolate),
2007      api_method_name_(api_method_name),
2008      enabled_features_(enabled),
2009      dynamic_tiering_(isolate_->IsWasmDynamicTieringEnabled()
2010                           ? DynamicTiering::kEnabled
2011                           : DynamicTiering::kDisabled),
2012      wasm_lazy_compilation_(FLAG_wasm_lazy_compilation),
2013      start_time_(base::TimeTicks::Now()),
2014      bytes_copy_(std::move(bytes_copy)),
2015      wire_bytes_(bytes_copy_.get(), bytes_copy_.get() + length),
2016      resolver_(std::move(resolver)),
2017      compilation_id_(compilation_id) {
2018  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
2019               "wasm.AsyncCompileJob");
2020  CHECK(FLAG_wasm_async_compilation);
2021  CHECK(!FLAG_jitless);
2022  v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
2023  v8::Platform* platform = V8::GetCurrentPlatform();
2024  foreground_task_runner_ = platform->GetForegroundTaskRunner(v8_isolate);
2025  native_context_ =
2026      isolate->global_handles()->Create(context->native_context());
2027  incumbent_context_ = isolate->global_handles()->Create(*incumbent_context);
2028  DCHECK(native_context_->IsNativeContext());
2029  context_id_ = isolate->GetOrRegisterRecorderContextId(native_context_);
2030  metrics_event_.async = true;
2031}
2032
2033void AsyncCompileJob::Start() {
2034  DoAsync<DecodeModule>(isolate_->counters(),
2035                        isolate_->metrics_recorder());  // --
2036}
2037
2038void AsyncCompileJob::Abort() {
2039  // Removing this job will trigger the destructor, which will cancel all
2040  // compilation.
2041  GetWasmEngine()->RemoveCompileJob(this);
2042}
2043
2044class AsyncStreamingProcessor final : public StreamingProcessor {
2045 public:
2046  explicit AsyncStreamingProcessor(AsyncCompileJob* job,
2047                                   std::shared_ptr<Counters> counters,
2048                                   AccountingAllocator* allocator);
2049
2050  ~AsyncStreamingProcessor() override;
2051
2052  bool ProcessModuleHeader(base::Vector<const uint8_t> bytes,
2053                           uint32_t offset) override;
2054
2055  bool ProcessSection(SectionCode section_code,
2056                      base::Vector<const uint8_t> bytes,
2057                      uint32_t offset) override;
2058
2059  bool ProcessCodeSectionHeader(int num_functions,
2060                                uint32_t functions_mismatch_error_offset,
2061                                std::shared_ptr<WireBytesStorage>,
2062                                int code_section_start,
2063                                int code_section_length) override;
2064
2065  bool ProcessFunctionBody(base::Vector<const uint8_t> bytes,
2066                           uint32_t offset) override;
2067
2068  void OnFinishedChunk() override;
2069
2070  void OnFinishedStream(base::OwnedVector<uint8_t> bytes) override;
2071
2072  void OnError(const WasmError&) override;
2073
2074  void OnAbort() override;
2075
2076  bool Deserialize(base::Vector<const uint8_t> wire_bytes,
2077                   base::Vector<const uint8_t> module_bytes) override;
2078
2079 private:
2080  // Finishes the AsyncCompileJob with an error.
2081  void FinishAsyncCompileJobWithError(const WasmError&);
2082
2083  void CommitCompilationUnits();
2084
2085  ModuleDecoder decoder_;
2086  AsyncCompileJob* job_;
2087  std::unique_ptr<CompilationUnitBuilder> compilation_unit_builder_;
2088  int num_functions_ = 0;
2089  bool prefix_cache_hit_ = false;
2090  bool before_code_section_ = true;
2091  std::shared_ptr<Counters> async_counters_;
2092  AccountingAllocator* allocator_;
2093
2094  // Running hash of the wire bytes up to code section size, but excluding the
2095  // code section itself. Used by the {NativeModuleCache} to detect potential
2096  // duplicate modules.
2097  size_t prefix_hash_;
2098};
2099
2100std::shared_ptr<StreamingDecoder> AsyncCompileJob::CreateStreamingDecoder() {
2101  DCHECK_NULL(stream_);
2102  stream_ = StreamingDecoder::CreateAsyncStreamingDecoder(
2103      std::make_unique<AsyncStreamingProcessor>(
2104          this, isolate_->async_counters(), isolate_->allocator()));
2105  return stream_;
2106}
2107
2108AsyncCompileJob::~AsyncCompileJob() {
2109  // Note: This destructor always runs on the foreground thread of the isolate.
2110  background_task_manager_.CancelAndWait();
2111  // If initial compilation did not finish yet we can abort it.
2112  if (native_module_) {
2113    Impl(native_module_->compilation_state())
2114        ->CancelCompilation(CompilationStateImpl::kCancelInitialCompilation);
2115  }
2116  // Tell the streaming decoder that the AsyncCompileJob is not available
2117  // anymore.
2118  // TODO(ahaas): Is this notification really necessary? Check
2119  // https://crbug.com/888170.
2120  if (stream_) stream_->NotifyCompilationEnded();
2121  CancelPendingForegroundTask();
2122  isolate_->global_handles()->Destroy(native_context_.location());
2123  isolate_->global_handles()->Destroy(incumbent_context_.location());
2124  if (!module_object_.is_null()) {
2125    isolate_->global_handles()->Destroy(module_object_.location());
2126  }
2127}
2128
2129void AsyncCompileJob::CreateNativeModule(
2130    std::shared_ptr<const WasmModule> module, size_t code_size_estimate) {
2131  // Embedder usage count for declared shared memories.
2132  if (module->has_shared_memory) {
2133    isolate_->CountUsage(v8::Isolate::UseCounterFeature::kWasmSharedMemory);
2134  }
2135
2136  // TODO(wasm): Improve efficiency of storing module wire bytes. Only store
2137  // relevant sections, not function bodies
2138
2139  // Create the module object and populate with compiled functions and
2140  // information needed at instantiation time.
2141
2142  native_module_ = GetWasmEngine()->NewNativeModule(
2143      isolate_, enabled_features_, std::move(module), code_size_estimate);
2144  native_module_->SetWireBytes({std::move(bytes_copy_), wire_bytes_.length()});
2145  native_module_->compilation_state()->set_compilation_id(compilation_id_);
2146}
2147
2148bool AsyncCompileJob::GetOrCreateNativeModule(
2149    std::shared_ptr<const WasmModule> module, size_t code_size_estimate) {
2150  native_module_ = GetWasmEngine()->MaybeGetNativeModule(
2151      module->origin, wire_bytes_.module_bytes(), isolate_);
2152  if (native_module_ == nullptr) {
2153    CreateNativeModule(std::move(module), code_size_estimate);
2154    return false;
2155  }
2156  return true;
2157}
2158
2159void AsyncCompileJob::PrepareRuntimeObjects() {
2160  // Create heap objects for script and module bytes to be stored in the
2161  // module object. Asm.js is not compiled asynchronously.
2162  DCHECK(module_object_.is_null());
2163  auto source_url = stream_ ? stream_->url() : base::Vector<const char>();
2164  auto script =
2165      GetWasmEngine()->GetOrCreateScript(isolate_, native_module_, source_url);
2166  Handle<WasmModuleObject> module_object =
2167      WasmModuleObject::New(isolate_, native_module_, script);
2168
2169  module_object_ = isolate_->global_handles()->Create(*module_object);
2170}
2171
2172// This function assumes that it is executed in a HandleScope, and that a
2173// context is set on the isolate.
2174void AsyncCompileJob::FinishCompile(bool is_after_cache_hit) {
2175  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
2176               "wasm.FinishAsyncCompile");
2177  bool is_after_deserialization = !module_object_.is_null();
2178  auto compilation_state = Impl(native_module_->compilation_state());
2179  if (!is_after_deserialization) {
2180    if (stream_) {
2181      stream_->NotifyNativeModuleCreated(native_module_);
2182    }
2183    PrepareRuntimeObjects();
2184  }
2185
2186  // Measure duration of baseline compilation or deserialization from cache.
2187  if (base::TimeTicks::IsHighResolution()) {
2188    base::TimeDelta duration = base::TimeTicks::Now() - start_time_;
2189    int duration_usecs = static_cast<int>(duration.InMicroseconds());
2190    isolate_->counters()->wasm_streaming_finish_wasm_module_time()->AddSample(
2191        duration_usecs);
2192
2193    if (is_after_cache_hit || is_after_deserialization) {
2194      v8::metrics::WasmModuleCompiled event{
2195          true,                                     // async
2196          true,                                     // streamed
2197          is_after_cache_hit,                       // cached
2198          is_after_deserialization,                 // deserialized
2199          wasm_lazy_compilation_,                   // lazy
2200          !compilation_state->failed(),             // success
2201          native_module_->turbofan_code_size(),     // code_size_in_bytes
2202          native_module_->liftoff_bailout_count(),  // liftoff_bailout_count
2203          duration.InMicroseconds(),                // wall_clock_duration_in_us
2204          static_cast<int64_t>(                     // cpu_time_duration_in_us
2205              native_module_->baseline_compilation_cpu_duration())};
2206      isolate_->metrics_recorder()->DelayMainThreadEvent(event, context_id_);
2207    }
2208  }
2209
2210  DCHECK(!isolate_->context().is_null());
2211  // Finish the wasm script now and make it public to the debugger.
2212  Handle<Script> script(module_object_->script(), isolate_);
2213  const WasmModule* module = module_object_->module();
2214  if (script->type() == Script::TYPE_WASM &&
2215      module->debug_symbols.type == WasmDebugSymbols::Type::SourceMap &&
2216      !module->debug_symbols.external_url.is_empty()) {
2217    ModuleWireBytes wire_bytes(module_object_->native_module()->wire_bytes());
2218    MaybeHandle<String> src_map_str = isolate_->factory()->NewStringFromUtf8(
2219        wire_bytes.GetNameOrNull(module->debug_symbols.external_url),
2220        AllocationType::kOld);
2221    script->set_source_mapping_url(*src_map_str.ToHandleChecked());
2222  }
2223  {
2224    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
2225                 "wasm.Debug.OnAfterCompile");
2226    isolate_->debug()->OnAfterCompile(script);
2227  }
2228
2229  // TODO(bbudge) Allow deserialization without wrapper compilation, so we can
2230  // just compile wrappers here.
2231  if (!is_after_deserialization) {
2232    Handle<FixedArray> export_wrappers;
2233    if (is_after_cache_hit) {
2234      // TODO(thibaudm): Look into sharing wrappers.
2235      CompileJsToWasmWrappers(isolate_, module, &export_wrappers);
2236    } else {
2237      compilation_state->FinalizeJSToWasmWrappers(isolate_, module,
2238                                                  &export_wrappers);
2239    }
2240    module_object_->set_export_wrappers(*export_wrappers);
2241  }
2242  // We can only update the feature counts once the entire compile is done.
2243  compilation_state->PublishDetectedFeatures(isolate_);
2244
2245  // We might need to recompile the module for debugging, if the debugger was
2246  // enabled while streaming compilation was running. Since handling this while
2247  // compiling via streaming is tricky, we just tier down now, before publishing
2248  // the module.
2249  if (native_module_->IsTieredDown()) native_module_->RecompileForTiering();
2250
2251  // Finally, log all generated code (it does not matter if this happens
2252  // repeatedly in case the script is shared).
2253  native_module_->LogWasmCodes(isolate_, module_object_->script());
2254
2255  FinishModule();
2256}
2257
2258void AsyncCompileJob::DecodeFailed(const WasmError& error) {
2259  ErrorThrower thrower(isolate_, api_method_name_);
2260  thrower.CompileFailed(error);
2261  // {job} keeps the {this} pointer alive.
2262  std::shared_ptr<AsyncCompileJob> job =
2263      GetWasmEngine()->RemoveCompileJob(this);
2264  resolver_->OnCompilationFailed(thrower.Reify());
2265}
2266
2267void AsyncCompileJob::AsyncCompileFailed() {
2268  ErrorThrower thrower(isolate_, api_method_name_);
2269  DCHECK_EQ(native_module_->module()->origin, kWasmOrigin);
2270  const bool lazy_module = wasm_lazy_compilation_;
2271  ValidateSequentially(native_module_->module(), native_module_.get(),
2272                       isolate_->counters(), isolate_->allocator(), &thrower,
2273                       lazy_module);
2274  DCHECK(thrower.error());
2275  // {job} keeps the {this} pointer alive.
2276  std::shared_ptr<AsyncCompileJob> job =
2277      GetWasmEngine()->RemoveCompileJob(this);
2278  resolver_->OnCompilationFailed(thrower.Reify());
2279}
2280
2281void AsyncCompileJob::AsyncCompileSucceeded(Handle<WasmModuleObject> result) {
2282  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
2283               "wasm.OnCompilationSucceeded");
2284  // We have to make sure that an "incumbent context" is available in case
2285  // the module's start function calls out to Blink.
2286  Local<v8::Context> backup_incumbent_context =
2287      Utils::ToLocal(incumbent_context_);
2288  v8::Context::BackupIncumbentScope incumbent(backup_incumbent_context);
2289  resolver_->OnCompilationSucceeded(result);
2290}
2291
2292class AsyncCompileJob::CompilationStateCallback
2293    : public CompilationEventCallback {
2294 public:
2295  explicit CompilationStateCallback(AsyncCompileJob* job) : job_(job) {}
2296
2297  void call(CompilationEvent event) override {
2298    // This callback is only being called from a foreground task.
2299    switch (event) {
2300      case CompilationEvent::kFinishedExportWrappers:
2301        // Even if baseline compilation units finish first, we trigger the
2302        // "kFinishedExportWrappers" event first.
2303        DCHECK(!last_event_.has_value());
2304        break;
2305      case CompilationEvent::kFinishedBaselineCompilation:
2306        DCHECK_EQ(CompilationEvent::kFinishedExportWrappers, last_event_);
2307        if (job_->DecrementAndCheckFinisherCount()) {
2308          // Install the native module in the cache, or reuse a conflicting one.
2309          // If we get a conflicting module, wait until we are back in the
2310          // main thread to update {job_->native_module_} to avoid a data race.
2311          std::shared_ptr<NativeModule> native_module = job_->native_module_;
2312          bool cache_hit = !GetWasmEngine()->UpdateNativeModuleCache(
2313              false, &native_module, job_->isolate_);
2314          DCHECK_EQ(cache_hit, native_module != job_->native_module_);
2315          job_->DoSync<CompileFinished>(cache_hit ? std::move(native_module)
2316                                                  : nullptr);
2317        }
2318        break;
2319      case CompilationEvent::kFinishedCompilationChunk:
2320        DCHECK(CompilationEvent::kFinishedBaselineCompilation == last_event_ ||
2321               CompilationEvent::kFinishedCompilationChunk == last_event_);
2322        break;
2323      case CompilationEvent::kFinishedTopTierCompilation:
2324        DCHECK(CompilationEvent::kFinishedBaselineCompilation == last_event_);
2325        // At this point, the job will already be gone, thus do not access it
2326        // here.
2327        break;
2328      case CompilationEvent::kFailedCompilation:
2329        DCHECK(!last_event_.has_value() ||
2330               last_event_ == CompilationEvent::kFinishedExportWrappers);
2331        if (job_->DecrementAndCheckFinisherCount()) {
2332          // Don't update {job_->native_module_} to avoid data races with other
2333          // compilation threads. Use a copy of the shared pointer instead.
2334          std::shared_ptr<NativeModule> native_module = job_->native_module_;
2335          GetWasmEngine()->UpdateNativeModuleCache(true, &native_module,
2336                                                   job_->isolate_);
2337          job_->DoSync<CompileFailed>();
2338        }
2339        break;
2340      case CompilationEvent::kFinishedRecompilation:
2341        // This event can happen either before or after
2342        // {kFinishedTopTierCompilation}, hence don't remember this in
2343        // {last_event_}.
2344        return;
2345    }
2346#ifdef DEBUG
2347    last_event_ = event;
2348#endif
2349  }
2350
2351 private:
2352  AsyncCompileJob* job_;
2353#ifdef DEBUG
2354  // This will be modified by different threads, but they externally
2355  // synchronize, so no explicit synchronization (currently) needed here.
2356  base::Optional<CompilationEvent> last_event_;
2357#endif
2358};
2359
2360// A closure to run a compilation step (either as foreground or background
2361// task) and schedule the next step(s), if any.
2362class AsyncCompileJob::CompileStep {
2363 public:
2364  virtual ~CompileStep() = default;
2365
2366  void Run(AsyncCompileJob* job, bool on_foreground) {
2367    if (on_foreground) {
2368      HandleScope scope(job->isolate_);
2369      SaveAndSwitchContext saved_context(job->isolate_, *job->native_context_);
2370      RunInForeground(job);
2371    } else {
2372      RunInBackground(job);
2373    }
2374  }
2375
2376  virtual void RunInForeground(AsyncCompileJob*) { UNREACHABLE(); }
2377  virtual void RunInBackground(AsyncCompileJob*) { UNREACHABLE(); }
2378};
2379
2380class AsyncCompileJob::CompileTask : public CancelableTask {
2381 public:
2382  CompileTask(AsyncCompileJob* job, bool on_foreground)
2383      // We only manage the background tasks with the {CancelableTaskManager} of
2384      // the {AsyncCompileJob}. Foreground tasks are managed by the system's
2385      // {CancelableTaskManager}. Background tasks cannot spawn tasks managed by
2386      // their own task manager.
2387      : CancelableTask(on_foreground ? job->isolate_->cancelable_task_manager()
2388                                     : &job->background_task_manager_),
2389        job_(job),
2390        on_foreground_(on_foreground) {}
2391
2392  ~CompileTask() override {
2393    if (job_ != nullptr && on_foreground_) ResetPendingForegroundTask();
2394  }
2395
2396  void RunInternal() final {
2397    if (!job_) return;
2398    if (on_foreground_) ResetPendingForegroundTask();
2399    job_->step_->Run(job_, on_foreground_);
2400    // After execution, reset {job_} such that we don't try to reset the pending
2401    // foreground task when the task is deleted.
2402    job_ = nullptr;
2403  }
2404
2405  void Cancel() {
2406    DCHECK_NOT_NULL(job_);
2407    job_ = nullptr;
2408  }
2409
2410 private:
2411  // {job_} will be cleared to cancel a pending task.
2412  AsyncCompileJob* job_;
2413  bool on_foreground_;
2414
2415  void ResetPendingForegroundTask() const {
2416    DCHECK_EQ(this, job_->pending_foreground_task_);
2417    job_->pending_foreground_task_ = nullptr;
2418  }
2419};
2420
2421void AsyncCompileJob::StartForegroundTask() {
2422  DCHECK_NULL(pending_foreground_task_);
2423
2424  auto new_task = std::make_unique<CompileTask>(this, true);
2425  pending_foreground_task_ = new_task.get();
2426  foreground_task_runner_->PostTask(std::move(new_task));
2427}
2428
2429void AsyncCompileJob::ExecuteForegroundTaskImmediately() {
2430  DCHECK_NULL(pending_foreground_task_);
2431
2432  auto new_task = std::make_unique<CompileTask>(this, true);
2433  pending_foreground_task_ = new_task.get();
2434  new_task->Run();
2435}
2436
2437void AsyncCompileJob::CancelPendingForegroundTask() {
2438  if (!pending_foreground_task_) return;
2439  pending_foreground_task_->Cancel();
2440  pending_foreground_task_ = nullptr;
2441}
2442
2443void AsyncCompileJob::StartBackgroundTask() {
2444  auto task = std::make_unique<CompileTask>(this, false);
2445
2446  // If --wasm-num-compilation-tasks=0 is passed, do only spawn foreground
2447  // tasks. This is used to make timing deterministic.
2448  if (FLAG_wasm_num_compilation_tasks > 0) {
2449    V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
2450  } else {
2451    foreground_task_runner_->PostTask(std::move(task));
2452  }
2453}
2454
2455template <typename Step,
2456          AsyncCompileJob::UseExistingForegroundTask use_existing_fg_task,
2457          typename... Args>
2458void AsyncCompileJob::DoSync(Args&&... args) {
2459  NextStep<Step>(std::forward<Args>(args)...);
2460  if (use_existing_fg_task && pending_foreground_task_ != nullptr) return;
2461  StartForegroundTask();
2462}
2463
2464template <typename Step, typename... Args>
2465void AsyncCompileJob::DoImmediately(Args&&... args) {
2466  NextStep<Step>(std::forward<Args>(args)...);
2467  ExecuteForegroundTaskImmediately();
2468}
2469
2470template <typename Step, typename... Args>
2471void AsyncCompileJob::DoAsync(Args&&... args) {
2472  NextStep<Step>(std::forward<Args>(args)...);
2473  StartBackgroundTask();
2474}
2475
2476template <typename Step, typename... Args>
2477void AsyncCompileJob::NextStep(Args&&... args) {
2478  step_.reset(new Step(std::forward<Args>(args)...));
2479}
2480
2481//==========================================================================
2482// Step 1: (async) Decode the module.
2483//==========================================================================
2484class AsyncCompileJob::DecodeModule : public AsyncCompileJob::CompileStep {
2485 public:
2486  explicit DecodeModule(Counters* counters,
2487                        std::shared_ptr<metrics::Recorder> metrics_recorder)
2488      : counters_(counters), metrics_recorder_(std::move(metrics_recorder)) {}
2489
2490  void RunInBackground(AsyncCompileJob* job) override {
2491    ModuleResult result;
2492    {
2493      DisallowHandleAllocation no_handle;
2494      DisallowGarbageCollection no_gc;
2495      // Decode the module bytes.
2496      TRACE_COMPILE("(1) Decoding module...\n");
2497      TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
2498                   "wasm.DecodeModule");
2499      auto enabled_features = job->enabled_features_;
2500      result = DecodeWasmModule(
2501          enabled_features, job->wire_bytes_.start(), job->wire_bytes_.end(),
2502          false, kWasmOrigin, counters_, metrics_recorder_, job->context_id(),
2503          DecodingMethod::kAsync, GetWasmEngine()->allocator());
2504
2505      // Validate lazy functions here if requested.
2506      if (!FLAG_wasm_lazy_validation && result.ok()) {
2507        const WasmModule* module = result.value().get();
2508        DCHECK_EQ(module->origin, kWasmOrigin);
2509        const bool lazy_module = job->wasm_lazy_compilation_;
2510        if (MayCompriseLazyFunctions(module, enabled_features, lazy_module)) {
2511          auto allocator = GetWasmEngine()->allocator();
2512          int start = module->num_imported_functions;
2513          int end = start + module->num_declared_functions;
2514
2515          for (int func_index = start; func_index < end; func_index++) {
2516            const WasmFunction* func = &module->functions[func_index];
2517            base::Vector<const uint8_t> code =
2518                job->wire_bytes_.GetFunctionBytes(func);
2519
2520            CompileStrategy strategy = GetCompileStrategy(
2521                module, enabled_features, func_index, lazy_module);
2522            bool validate_lazily_compiled_function =
2523                strategy == CompileStrategy::kLazy ||
2524                strategy == CompileStrategy::kLazyBaselineEagerTopTier;
2525            if (validate_lazily_compiled_function) {
2526              DecodeResult function_result =
2527                  ValidateSingleFunction(module, func_index, code, counters_,
2528                                         allocator, enabled_features);
2529              if (function_result.failed()) {
2530                result = ModuleResult(function_result.error());
2531                break;
2532              }
2533            }
2534          }
2535        }
2536      }
2537    }
2538    if (result.failed()) {
2539      // Decoding failure; reject the promise and clean up.
2540      job->DoSync<DecodeFail>(std::move(result).error());
2541    } else {
2542      // Decode passed.
2543      std::shared_ptr<WasmModule> module = std::move(result).value();
2544      const bool include_liftoff = FLAG_liftoff;
2545      size_t code_size_estimate =
2546          wasm::WasmCodeManager::EstimateNativeModuleCodeSize(
2547              module.get(), include_liftoff, job->dynamic_tiering_);
2548      job->DoSync<PrepareAndStartCompile>(std::move(module), true,
2549                                          code_size_estimate);
2550    }
2551  }
2552
2553 private:
2554  Counters* const counters_;
2555  std::shared_ptr<metrics::Recorder> metrics_recorder_;
2556};
2557
2558//==========================================================================
2559// Step 1b: (sync) Fail decoding the module.
2560//==========================================================================
2561class AsyncCompileJob::DecodeFail : public CompileStep {
2562 public:
2563  explicit DecodeFail(WasmError error) : error_(std::move(error)) {}
2564
2565 private:
2566  WasmError error_;
2567
2568  void RunInForeground(AsyncCompileJob* job) override {
2569    TRACE_COMPILE("(1b) Decoding failed.\n");
2570    // {job_} is deleted in DecodeFailed, therefore the {return}.
2571    return job->DecodeFailed(error_);
2572  }
2573};
2574
2575//==========================================================================
2576// Step 2 (sync): Create heap-allocated data and start compile.
2577//==========================================================================
2578class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
2579 public:
2580  PrepareAndStartCompile(std::shared_ptr<const WasmModule> module,
2581                         bool start_compilation, size_t code_size_estimate)
2582      : module_(std::move(module)),
2583        start_compilation_(start_compilation),
2584        code_size_estimate_(code_size_estimate) {}
2585
2586 private:
2587  void RunInForeground(AsyncCompileJob* job) override {
2588    TRACE_COMPILE("(2) Prepare and start compile...\n");
2589
2590    const bool streaming = job->wire_bytes_.length() == 0;
2591    if (streaming) {
2592      // Streaming compilation already checked for cache hits.
2593      job->CreateNativeModule(module_, code_size_estimate_);
2594    } else if (job->GetOrCreateNativeModule(std::move(module_),
2595                                            code_size_estimate_)) {
2596      job->FinishCompile(true);
2597      return;
2598    }
2599
2600    // Make sure all compilation tasks stopped running. Decoding (async step)
2601    // is done.
2602    job->background_task_manager_.CancelAndWait();
2603
2604    CompilationStateImpl* compilation_state =
2605        Impl(job->native_module_->compilation_state());
2606    compilation_state->AddCallback(
2607        std::make_unique<CompilationStateCallback>(job));
2608    if (base::TimeTicks::IsHighResolution()) {
2609      auto compile_mode = job->stream_ == nullptr
2610                              ? CompilationTimeCallback::kAsync
2611                              : CompilationTimeCallback::kStreaming;
2612      compilation_state->AddCallback(std::make_unique<CompilationTimeCallback>(
2613          job->isolate_->async_counters(), job->isolate_->metrics_recorder(),
2614          job->context_id_, job->native_module_, compile_mode));
2615    }
2616
2617    if (start_compilation_) {
2618      std::unique_ptr<CompilationUnitBuilder> builder =
2619          InitializeCompilation(job->isolate(), job->native_module_.get());
2620      compilation_state->InitializeCompilationUnits(std::move(builder));
2621      // We are in single-threaded mode, so there are no worker tasks that will
2622      // do the compilation. We call {WaitForCompilationEvent} here so that the
2623      // main thread paticipates and finishes the compilation.
2624      if (FLAG_wasm_num_compilation_tasks == 0) {
2625        compilation_state->WaitForCompilationEvent(
2626            CompilationEvent::kFinishedBaselineCompilation);
2627      }
2628    }
2629  }
2630
2631  const std::shared_ptr<const WasmModule> module_;
2632  const bool start_compilation_;
2633  const size_t code_size_estimate_;
2634};
2635
2636//==========================================================================
2637// Step 3a (sync): Compilation failed.
2638//==========================================================================
2639class AsyncCompileJob::CompileFailed : public CompileStep {
2640 private:
2641  void RunInForeground(AsyncCompileJob* job) override {
2642    TRACE_COMPILE("(3a) Compilation failed\n");
2643    DCHECK(job->native_module_->compilation_state()->failed());
2644
2645    // {job_} is deleted in AsyncCompileFailed, therefore the {return}.
2646    return job->AsyncCompileFailed();
2647  }
2648};
2649
2650namespace {
2651class SampleTopTierCodeSizeCallback : public CompilationEventCallback {
2652 public:
2653  explicit SampleTopTierCodeSizeCallback(
2654      std::weak_ptr<NativeModule> native_module)
2655      : native_module_(std::move(native_module)) {}
2656
2657  void call(CompilationEvent event) override {
2658    if (event != CompilationEvent::kFinishedTopTierCompilation) return;
2659    if (std::shared_ptr<NativeModule> native_module = native_module_.lock()) {
2660      GetWasmEngine()->SampleTopTierCodeSizeInAllIsolates(native_module);
2661    }
2662  }
2663
2664 private:
2665  std::weak_ptr<NativeModule> native_module_;
2666};
2667}  // namespace
2668
2669//==========================================================================
2670// Step 3b (sync): Compilation finished.
2671//==========================================================================
2672class AsyncCompileJob::CompileFinished : public CompileStep {
2673 public:
2674  explicit CompileFinished(std::shared_ptr<NativeModule> cached_native_module)
2675      : cached_native_module_(std::move(cached_native_module)) {}
2676
2677 private:
2678  void RunInForeground(AsyncCompileJob* job) override {
2679    TRACE_COMPILE("(3b) Compilation finished\n");
2680    if (cached_native_module_) {
2681      job->native_module_ = cached_native_module_;
2682    } else {
2683      DCHECK(!job->native_module_->compilation_state()->failed());
2684      // Sample the generated code size when baseline compilation finished.
2685      job->native_module_->SampleCodeSize(job->isolate_->counters(),
2686                                          NativeModule::kAfterBaseline);
2687      // Also, set a callback to sample the code size after top-tier compilation
2688      // finished. This callback will *not* keep the NativeModule alive.
2689      job->native_module_->compilation_state()->AddCallback(
2690          std::make_unique<SampleTopTierCodeSizeCallback>(job->native_module_));
2691    }
2692    // Then finalize and publish the generated module.
2693    job->FinishCompile(cached_native_module_ != nullptr);
2694  }
2695
2696  std::shared_ptr<NativeModule> cached_native_module_;
2697};
2698
2699void AsyncCompileJob::FinishModule() {
2700  TRACE_COMPILE("(4) Finish module...\n");
2701  AsyncCompileSucceeded(module_object_);
2702  GetWasmEngine()->RemoveCompileJob(this);
2703}
2704
2705AsyncStreamingProcessor::AsyncStreamingProcessor(
2706    AsyncCompileJob* job, std::shared_ptr<Counters> async_counters,
2707    AccountingAllocator* allocator)
2708    : decoder_(job->enabled_features_),
2709      job_(job),
2710      compilation_unit_builder_(nullptr),
2711      async_counters_(async_counters),
2712      allocator_(allocator) {}
2713
2714AsyncStreamingProcessor::~AsyncStreamingProcessor() {
2715  if (job_->native_module_ && job_->native_module_->wire_bytes().empty()) {
2716    // Clean up the temporary cache entry.
2717    GetWasmEngine()->StreamingCompilationFailed(prefix_hash_);
2718  }
2719}
2720
2721void AsyncStreamingProcessor::FinishAsyncCompileJobWithError(
2722    const WasmError& error) {
2723  DCHECK(error.has_error());
2724  // Make sure all background tasks stopped executing before we change the state
2725  // of the AsyncCompileJob to DecodeFail.
2726  job_->background_task_manager_.CancelAndWait();
2727
2728  // Record event metrics.
2729  auto duration = base::TimeTicks::Now() - job_->start_time_;
2730  job_->metrics_event_.success = false;
2731  job_->metrics_event_.streamed = true;
2732  job_->metrics_event_.module_size_in_bytes = job_->wire_bytes_.length();
2733  job_->metrics_event_.function_count = num_functions_;
2734  job_->metrics_event_.wall_clock_duration_in_us = duration.InMicroseconds();
2735  job_->isolate_->metrics_recorder()->DelayMainThreadEvent(job_->metrics_event_,
2736                                                           job_->context_id_);
2737
2738  // Check if there is already a CompiledModule, in which case we have to clean
2739  // up the CompilationStateImpl as well.
2740  if (job_->native_module_) {
2741    Impl(job_->native_module_->compilation_state())
2742        ->CancelCompilation(CompilationStateImpl::kCancelUnconditionally);
2743
2744    job_->DoSync<AsyncCompileJob::DecodeFail,
2745                 AsyncCompileJob::kUseExistingForegroundTask>(error);
2746
2747    // Clear the {compilation_unit_builder_} if it exists. This is needed
2748    // because there is a check in the destructor of the
2749    // {CompilationUnitBuilder} that it is empty.
2750    if (compilation_unit_builder_) compilation_unit_builder_->Clear();
2751  } else {
2752    job_->DoSync<AsyncCompileJob::DecodeFail>(error);
2753  }
2754}
2755
2756// Process the module header.
2757bool AsyncStreamingProcessor::ProcessModuleHeader(
2758    base::Vector<const uint8_t> bytes, uint32_t offset) {
2759  TRACE_STREAMING("Process module header...\n");
2760  decoder_.StartDecoding(job_->isolate()->counters(),
2761                         job_->isolate()->metrics_recorder(),
2762                         job_->context_id(), GetWasmEngine()->allocator());
2763  decoder_.DecodeModuleHeader(bytes, offset);
2764  if (!decoder_.ok()) {
2765    FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());
2766    return false;
2767  }
2768  prefix_hash_ = NativeModuleCache::WireBytesHash(bytes);
2769  return true;
2770}
2771
2772// Process all sections except for the code section.
2773bool AsyncStreamingProcessor::ProcessSection(SectionCode section_code,
2774                                             base::Vector<const uint8_t> bytes,
2775                                             uint32_t offset) {
2776  TRACE_STREAMING("Process section %d ...\n", section_code);
2777  if (compilation_unit_builder_) {
2778    // We reached a section after the code section, we do not need the
2779    // compilation_unit_builder_ anymore.
2780    CommitCompilationUnits();
2781    compilation_unit_builder_.reset();
2782  }
2783  if (before_code_section_) {
2784    // Combine section hashes until code section.
2785    prefix_hash_ = base::hash_combine(prefix_hash_,
2786                                      NativeModuleCache::WireBytesHash(bytes));
2787  }
2788  if (section_code == SectionCode::kUnknownSectionCode) {
2789    size_t bytes_consumed = ModuleDecoder::IdentifyUnknownSection(
2790        &decoder_, bytes, offset, &section_code);
2791    if (!decoder_.ok()) {
2792      FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());
2793      return false;
2794    }
2795    if (section_code == SectionCode::kUnknownSectionCode) {
2796      // Skip unknown sections that we do not know how to handle.
2797      return true;
2798    }
2799    // Remove the unknown section tag from the payload bytes.
2800    offset += bytes_consumed;
2801    bytes = bytes.SubVector(bytes_consumed, bytes.size());
2802  }
2803  constexpr bool verify_functions = false;
2804  decoder_.DecodeSection(section_code, bytes, offset, verify_functions);
2805  if (!decoder_.ok()) {
2806    FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());
2807    return false;
2808  }
2809  return true;
2810}
2811
2812// Start the code section.
2813bool AsyncStreamingProcessor::ProcessCodeSectionHeader(
2814    int num_functions, uint32_t functions_mismatch_error_offset,
2815    std::shared_ptr<WireBytesStorage> wire_bytes_storage,
2816    int code_section_start, int code_section_length) {
2817  DCHECK_LE(0, code_section_length);
2818  before_code_section_ = false;
2819  TRACE_STREAMING("Start the code section with %d functions...\n",
2820                  num_functions);
2821  decoder_.StartCodeSection();
2822  if (!decoder_.CheckFunctionsCount(static_cast<uint32_t>(num_functions),
2823                                    functions_mismatch_error_offset)) {
2824    FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());
2825    return false;
2826  }
2827
2828  decoder_.set_code_section(code_section_start,
2829                            static_cast<uint32_t>(code_section_length));
2830
2831  prefix_hash_ = base::hash_combine(prefix_hash_,
2832                                    static_cast<uint32_t>(code_section_length));
2833  if (!GetWasmEngine()->GetStreamingCompilationOwnership(prefix_hash_)) {
2834    // Known prefix, wait until the end of the stream and check the cache.
2835    prefix_cache_hit_ = true;
2836    return true;
2837  }
2838
2839  // Execute the PrepareAndStartCompile step immediately and not in a separate
2840  // task.
2841  int num_imported_functions =
2842      static_cast<int>(decoder_.module()->num_imported_functions);
2843  DCHECK_EQ(kWasmOrigin, decoder_.module()->origin);
2844  const bool include_liftoff = FLAG_liftoff;
2845  size_t code_size_estimate =
2846      wasm::WasmCodeManager::EstimateNativeModuleCodeSize(
2847          num_functions, num_imported_functions, code_section_length,
2848          include_liftoff, job_->dynamic_tiering_);
2849  job_->DoImmediately<AsyncCompileJob::PrepareAndStartCompile>(
2850      decoder_.shared_module(), false, code_size_estimate);
2851
2852  auto* compilation_state = Impl(job_->native_module_->compilation_state());
2853  compilation_state->SetWireBytesStorage(std::move(wire_bytes_storage));
2854  DCHECK_EQ(job_->native_module_->module()->origin, kWasmOrigin);
2855
2856  // Set outstanding_finishers_ to 2, because both the AsyncCompileJob and the
2857  // AsyncStreamingProcessor have to finish.
2858  job_->outstanding_finishers_.store(2);
2859  compilation_unit_builder_ =
2860      InitializeCompilation(job_->isolate(), job_->native_module_.get());
2861  return true;
2862}
2863
2864// Process a function body.
2865bool AsyncStreamingProcessor::ProcessFunctionBody(
2866    base::Vector<const uint8_t> bytes, uint32_t offset) {
2867  TRACE_STREAMING("Process function body %d ...\n", num_functions_);
2868
2869  decoder_.DecodeFunctionBody(
2870      num_functions_, static_cast<uint32_t>(bytes.length()), offset, false);
2871
2872  const WasmModule* module = decoder_.module();
2873  auto enabled_features = job_->enabled_features_;
2874  uint32_t func_index =
2875      num_functions_ + decoder_.module()->num_imported_functions;
2876  DCHECK_EQ(module->origin, kWasmOrigin);
2877  const bool lazy_module = job_->wasm_lazy_compilation_;
2878  CompileStrategy strategy =
2879      GetCompileStrategy(module, enabled_features, func_index, lazy_module);
2880  bool validate_lazily_compiled_function =
2881      !FLAG_wasm_lazy_validation &&
2882      (strategy == CompileStrategy::kLazy ||
2883       strategy == CompileStrategy::kLazyBaselineEagerTopTier);
2884  if (validate_lazily_compiled_function) {
2885    // The native module does not own the wire bytes until {SetWireBytes} is
2886    // called in {OnFinishedStream}. Validation must use {bytes} parameter.
2887    DecodeResult result =
2888        ValidateSingleFunction(module, func_index, bytes, async_counters_.get(),
2889                               allocator_, enabled_features);
2890
2891    if (result.failed()) {
2892      FinishAsyncCompileJobWithError(result.error());
2893      return false;
2894    }
2895  }
2896
2897  // Don't compile yet if we might have a cache hit.
2898  if (prefix_cache_hit_) {
2899    num_functions_++;
2900    return true;
2901  }
2902
2903  auto* compilation_state = Impl(job_->native_module_->compilation_state());
2904  compilation_state->AddCompilationUnit(compilation_unit_builder_.get(),
2905                                        func_index);
2906  ++num_functions_;
2907
2908  return true;
2909}
2910
2911void AsyncStreamingProcessor::CommitCompilationUnits() {
2912  DCHECK(compilation_unit_builder_);
2913  compilation_unit_builder_->Commit();
2914}
2915
2916void AsyncStreamingProcessor::OnFinishedChunk() {
2917  TRACE_STREAMING("FinishChunk...\n");
2918  if (compilation_unit_builder_) CommitCompilationUnits();
2919}
2920
2921// Finish the processing of the stream.
2922void AsyncStreamingProcessor::OnFinishedStream(
2923    base::OwnedVector<uint8_t> bytes) {
2924  TRACE_STREAMING("Finish stream...\n");
2925  DCHECK_EQ(NativeModuleCache::PrefixHash(bytes.as_vector()), prefix_hash_);
2926  ModuleResult result = decoder_.FinishDecoding(false);
2927  if (result.failed()) {
2928    FinishAsyncCompileJobWithError(result.error());
2929    return;
2930  }
2931
2932  job_->wire_bytes_ = ModuleWireBytes(bytes.as_vector());
2933  job_->bytes_copy_ = bytes.ReleaseData();
2934
2935  // Record event metrics.
2936  auto duration = base::TimeTicks::Now() - job_->start_time_;
2937  job_->metrics_event_.success = true;
2938  job_->metrics_event_.streamed = true;
2939  job_->metrics_event_.module_size_in_bytes = job_->wire_bytes_.length();
2940  job_->metrics_event_.function_count = num_functions_;
2941  job_->metrics_event_.wall_clock_duration_in_us = duration.InMicroseconds();
2942  job_->isolate_->metrics_recorder()->DelayMainThreadEvent(job_->metrics_event_,
2943                                                           job_->context_id_);
2944
2945  if (prefix_cache_hit_) {
2946    // Restart as an asynchronous, non-streaming compilation. Most likely
2947    // {PrepareAndStartCompile} will get the native module from the cache.
2948    const bool include_liftoff = FLAG_liftoff;
2949    size_t code_size_estimate =
2950        wasm::WasmCodeManager::EstimateNativeModuleCodeSize(
2951            result.value().get(), include_liftoff, job_->dynamic_tiering_);
2952    job_->DoSync<AsyncCompileJob::PrepareAndStartCompile>(
2953        std::move(result).value(), true, code_size_estimate);
2954    return;
2955  }
2956
2957  // We have to open a HandleScope and prepare the Context for
2958  // CreateNativeModule, PrepareRuntimeObjects and FinishCompile as this is a
2959  // callback from the embedder.
2960  HandleScope scope(job_->isolate_);
2961  SaveAndSwitchContext saved_context(job_->isolate_, *job_->native_context_);
2962
2963  // Record the size of the wire bytes. In synchronous and asynchronous
2964  // (non-streaming) compilation, this happens in {DecodeWasmModule}.
2965  auto* histogram = job_->isolate_->counters()->wasm_wasm_module_size_bytes();
2966  histogram->AddSample(job_->wire_bytes_.module_bytes().length());
2967
2968  const bool has_code_section = job_->native_module_ != nullptr;
2969  bool cache_hit = false;
2970  if (!has_code_section) {
2971    // We are processing a WebAssembly module without code section. Create the
2972    // native module now (would otherwise happen in {PrepareAndStartCompile} or
2973    // {ProcessCodeSectionHeader}).
2974    constexpr size_t kCodeSizeEstimate = 0;
2975    cache_hit = job_->GetOrCreateNativeModule(std::move(result).value(),
2976                                              kCodeSizeEstimate);
2977  } else {
2978    job_->native_module_->SetWireBytes(
2979        {std::move(job_->bytes_copy_), job_->wire_bytes_.length()});
2980  }
2981  const bool needs_finish = job_->DecrementAndCheckFinisherCount();
2982  DCHECK_IMPLIES(!has_code_section, needs_finish);
2983  if (needs_finish) {
2984    const bool failed = job_->native_module_->compilation_state()->failed();
2985    if (!cache_hit) {
2986      cache_hit = !GetWasmEngine()->UpdateNativeModuleCache(
2987          failed, &job_->native_module_, job_->isolate_);
2988    }
2989    if (failed) {
2990      job_->AsyncCompileFailed();
2991    } else {
2992      job_->FinishCompile(cache_hit);
2993    }
2994  }
2995}
2996
2997// Report an error detected in the StreamingDecoder.
2998void AsyncStreamingProcessor::OnError(const WasmError& error) {
2999  TRACE_STREAMING("Stream error...\n");
3000  FinishAsyncCompileJobWithError(error);
3001}
3002
3003void AsyncStreamingProcessor::OnAbort() {
3004  TRACE_STREAMING("Abort stream...\n");
3005  job_->Abort();
3006}
3007
3008bool AsyncStreamingProcessor::Deserialize(
3009    base::Vector<const uint8_t> module_bytes,
3010    base::Vector<const uint8_t> wire_bytes) {
3011  TRACE_EVENT0("v8.wasm", "wasm.Deserialize");
3012  TimedHistogramScope time_scope(
3013      job_->isolate()->counters()->wasm_deserialization_time(),
3014      job_->isolate());
3015  // DeserializeNativeModule and FinishCompile assume that they are executed in
3016  // a HandleScope, and that a context is set on the isolate.
3017  HandleScope scope(job_->isolate_);
3018  SaveAndSwitchContext saved_context(job_->isolate_, *job_->native_context_);
3019
3020  MaybeHandle<WasmModuleObject> result = DeserializeNativeModule(
3021      job_->isolate_, module_bytes, wire_bytes, job_->stream_->url());
3022
3023  if (result.is_null()) return false;
3024
3025  job_->module_object_ =
3026      job_->isolate_->global_handles()->Create(*result.ToHandleChecked());
3027  job_->native_module_ = job_->module_object_->shared_native_module();
3028  job_->wire_bytes_ = ModuleWireBytes(job_->native_module_->wire_bytes());
3029  job_->FinishCompile(false);
3030  return true;
3031}
3032
3033CompilationStateImpl::CompilationStateImpl(
3034    const std::shared_ptr<NativeModule>& native_module,
3035    std::shared_ptr<Counters> async_counters, DynamicTiering dynamic_tiering)
3036    : native_module_(native_module.get()),
3037      native_module_weak_(std::move(native_module)),
3038      async_counters_(std::move(async_counters)),
3039      compilation_unit_queues_(native_module->num_functions()),
3040      dynamic_tiering_(dynamic_tiering) {}
3041
3042void CompilationStateImpl::InitCompileJob() {
3043  DCHECK_NULL(compile_job_);
3044  compile_job_ = V8::GetCurrentPlatform()->PostJob(
3045      TaskPriority::kUserVisible, std::make_unique<BackgroundCompileJob>(
3046                                      native_module_weak_, async_counters_));
3047}
3048
3049void CompilationStateImpl::CancelCompilation(
3050    CompilationStateImpl::CancellationPolicy cancellation_policy) {
3051  base::MutexGuard callbacks_guard(&callbacks_mutex_);
3052
3053  if (cancellation_policy == kCancelInitialCompilation &&
3054      finished_events_.contains(
3055          CompilationEvent::kFinishedBaselineCompilation)) {
3056    // Initial compilation already finished; cannot be cancelled.
3057    return;
3058  }
3059
3060  // std::memory_order_relaxed is sufficient because no other state is
3061  // synchronized with |compile_cancelled_|.
3062  compile_cancelled_.store(true, std::memory_order_relaxed);
3063
3064  // No more callbacks after abort.
3065  callbacks_.clear();
3066}
3067
3068bool CompilationStateImpl::cancelled() const {
3069  return compile_cancelled_.load(std::memory_order_relaxed);
3070}
3071
3072uint8_t CompilationStateImpl::SetupCompilationProgressForFunction(
3073    bool lazy_function, NativeModule* native_module,
3074    const WasmFeatures& enabled_features, int func_index) {
3075  ExecutionTierPair requested_tiers =
3076      GetRequestedExecutionTiers(native_module, enabled_features, func_index);
3077  CompileStrategy strategy = GetCompileStrategy(
3078      native_module->module(), enabled_features, func_index, lazy_function);
3079
3080  bool required_for_baseline = strategy == CompileStrategy::kEager;
3081  bool required_for_top_tier = strategy != CompileStrategy::kLazy;
3082  DCHECK_EQ(required_for_top_tier,
3083            strategy == CompileStrategy::kEager ||
3084                strategy == CompileStrategy::kLazyBaselineEagerTopTier);
3085
3086  // Count functions to complete baseline and top tier compilation.
3087  if (required_for_baseline) outstanding_baseline_units_++;
3088  if (required_for_top_tier) outstanding_top_tier_functions_++;
3089
3090  // Initialize function's compilation progress.
3091  ExecutionTier required_baseline_tier = required_for_baseline
3092                                             ? requested_tiers.baseline_tier
3093                                             : ExecutionTier::kNone;
3094  ExecutionTier required_top_tier =
3095      required_for_top_tier ? requested_tiers.top_tier : ExecutionTier::kNone;
3096  uint8_t function_progress =
3097      ReachedTierField::encode(ExecutionTier::kNone) |
3098      RequiredBaselineTierField::encode(required_baseline_tier) |
3099      RequiredTopTierField::encode(required_top_tier);
3100
3101  return function_progress;
3102}
3103
3104void CompilationStateImpl::InitializeCompilationProgress(
3105    bool lazy_module, int num_import_wrappers, int num_export_wrappers) {
3106  DCHECK(!failed());
3107  auto enabled_features = native_module_->enabled_features();
3108  auto* module = native_module_->module();
3109
3110  base::MutexGuard guard(&callbacks_mutex_);
3111  DCHECK_EQ(0, outstanding_baseline_units_);
3112  DCHECK_EQ(0, outstanding_export_wrappers_);
3113  DCHECK_EQ(0, outstanding_top_tier_functions_);
3114  compilation_progress_.reserve(module->num_declared_functions);
3115  int start = module->num_imported_functions;
3116  int end = start + module->num_declared_functions;
3117
3118  const bool prefer_liftoff = native_module_->IsTieredDown();
3119  for (int func_index = start; func_index < end; func_index++) {
3120    if (prefer_liftoff) {
3121      constexpr uint8_t kLiftoffOnlyFunctionProgress =
3122          RequiredTopTierField::encode(ExecutionTier::kLiftoff) |
3123          RequiredBaselineTierField::encode(ExecutionTier::kLiftoff) |
3124          ReachedTierField::encode(ExecutionTier::kNone);
3125      compilation_progress_.push_back(kLiftoffOnlyFunctionProgress);
3126      outstanding_baseline_units_++;
3127      outstanding_top_tier_functions_++;
3128      continue;
3129    }
3130    uint8_t function_progress = SetupCompilationProgressForFunction(
3131        lazy_module, native_module_, enabled_features, func_index);
3132    compilation_progress_.push_back(function_progress);
3133  }
3134  DCHECK_IMPLIES(lazy_module, outstanding_baseline_units_ == 0);
3135  DCHECK_IMPLIES(lazy_module, outstanding_top_tier_functions_ == 0);
3136  DCHECK_LE(0, outstanding_baseline_units_);
3137  DCHECK_LE(outstanding_baseline_units_, outstanding_top_tier_functions_);
3138  outstanding_baseline_units_ += num_import_wrappers;
3139  outstanding_export_wrappers_ = num_export_wrappers;
3140
3141  // Trigger callbacks if module needs no baseline or top tier compilation. This
3142  // can be the case for an empty or fully lazy module.
3143  TriggerCallbacks();
3144}
3145
3146uint8_t CompilationStateImpl::AddCompilationUnitInternal(
3147    CompilationUnitBuilder* builder, int function_index,
3148    uint8_t function_progress) {
3149  ExecutionTier required_baseline_tier =
3150      CompilationStateImpl::RequiredBaselineTierField::decode(
3151          function_progress);
3152  ExecutionTier required_top_tier =
3153      CompilationStateImpl::RequiredTopTierField::decode(function_progress);
3154  ExecutionTier reached_tier =
3155      CompilationStateImpl::ReachedTierField::decode(function_progress);
3156
3157  if (FLAG_experimental_wasm_gc) {
3158    // The Turbofan optimizations we enable for WasmGC code can (for now)
3159    // take a very long time, so skip Turbofan compilation for super-large
3160    // functions.
3161    // Besides, module serialization currently requires that all functions
3162    // have been TF-compiled. By enabling this limit only for WasmGC, we
3163    // make sure that non-experimental modules can be serialize as usual.
3164    // TODO(jkummerow): This is a stop-gap solution to avoid excessive
3165    // compile times. We would like to replace this hard threshold with
3166    // a better solution (TBD) eventually.
3167    constexpr uint32_t kMaxWasmFunctionSizeForTurbofan = 500 * KB;
3168    uint32_t size = builder->module()->functions[function_index].code.length();
3169    if (size > kMaxWasmFunctionSizeForTurbofan) {
3170      required_baseline_tier = ExecutionTier::kLiftoff;
3171      if (required_top_tier == ExecutionTier::kTurbofan) {
3172        required_top_tier = ExecutionTier::kLiftoff;
3173        outstanding_top_tier_functions_--;
3174      }
3175    }
3176  }
3177
3178  if (reached_tier < required_baseline_tier) {
3179    builder->AddBaselineUnit(function_index, required_baseline_tier);
3180  }
3181  if (reached_tier < required_top_tier &&
3182      required_baseline_tier != required_top_tier) {
3183    builder->AddTopTierUnit(function_index, required_top_tier);
3184  }
3185  return CompilationStateImpl::RequiredBaselineTierField::encode(
3186             required_baseline_tier) |
3187         CompilationStateImpl::RequiredTopTierField::encode(required_top_tier) |
3188         CompilationStateImpl::ReachedTierField::encode(reached_tier);
3189}
3190
3191void CompilationStateImpl::InitializeCompilationUnits(
3192    std::unique_ptr<CompilationUnitBuilder> builder) {
3193  int offset = native_module_->module()->num_imported_functions;
3194  if (native_module_->IsTieredDown()) {
3195    for (size_t i = 0; i < compilation_progress_.size(); ++i) {
3196      int func_index = offset + static_cast<int>(i);
3197      builder->AddDebugUnit(func_index);
3198    }
3199  } else {
3200    base::MutexGuard guard(&callbacks_mutex_);
3201
3202    for (size_t i = 0; i < compilation_progress_.size(); ++i) {
3203      uint8_t function_progress = compilation_progress_[i];
3204      int func_index = offset + static_cast<int>(i);
3205      compilation_progress_[i] = AddCompilationUnitInternal(
3206          builder.get(), func_index, function_progress);
3207    }
3208  }
3209  builder->Commit();
3210}
3211
3212void CompilationStateImpl::AddCompilationUnit(CompilationUnitBuilder* builder,
3213                                              int func_index) {
3214  if (native_module_->IsTieredDown()) {
3215    builder->AddDebugUnit(func_index);
3216    return;
3217  }
3218  int offset = native_module_->module()->num_imported_functions;
3219  int progress_index = func_index - offset;
3220  uint8_t function_progress;
3221  {
3222    // TODO(ahaas): This lock may cause overhead. If so, we could get rid of the
3223    // lock as follows:
3224    // 1) Make compilation_progress_ an array of atomic<uint8_t>, and access it
3225    // lock-free.
3226    // 2) Have a copy of compilation_progress_ that we use for initialization.
3227    // 3) Just re-calculate the content of compilation_progress_.
3228    base::MutexGuard guard(&callbacks_mutex_);
3229    function_progress = compilation_progress_[progress_index];
3230  }
3231  uint8_t updated_function_progress =
3232      AddCompilationUnitInternal(builder, func_index, function_progress);
3233  if (updated_function_progress != function_progress) {
3234    // This should happen very rarely (only for super-large functions), so we're
3235    // not worried about overhead.
3236    base::MutexGuard guard(&callbacks_mutex_);
3237    compilation_progress_[progress_index] = updated_function_progress;
3238  }
3239}
3240
3241void CompilationStateImpl::InitializeCompilationProgressAfterDeserialization(
3242    base::Vector<const int> lazy_functions,
3243    base::Vector<const int> liftoff_functions) {
3244  TRACE_EVENT2("v8.wasm", "wasm.CompilationAfterDeserialization",
3245               "num_lazy_functions", lazy_functions.size(),
3246               "num_liftoff_functions", liftoff_functions.size());
3247  TimedHistogramScope lazy_compile_time_scope(
3248      counters()->wasm_compile_after_deserialize());
3249
3250  auto* module = native_module_->module();
3251  auto enabled_features = native_module_->enabled_features();
3252  const bool lazy_module = IsLazyModule(module);
3253  base::Optional<CodeSpaceWriteScope> lazy_code_space_write_scope;
3254  if (lazy_module || !lazy_functions.empty()) {
3255    lazy_code_space_write_scope.emplace(native_module_);
3256  }
3257  {
3258    base::MutexGuard guard(&callbacks_mutex_);
3259    DCHECK(compilation_progress_.empty());
3260    constexpr uint8_t kProgressAfterTurbofanDeserialization =
3261        RequiredBaselineTierField::encode(ExecutionTier::kTurbofan) |
3262        RequiredTopTierField::encode(ExecutionTier::kTurbofan) |
3263        ReachedTierField::encode(ExecutionTier::kTurbofan);
3264    finished_events_.Add(CompilationEvent::kFinishedExportWrappers);
3265    if (liftoff_functions.empty() || lazy_module) {
3266      // We have to trigger the compilation events to finish compilation.
3267      // Typically the events get triggered when a CompilationUnit finishes, but
3268      // with lazy compilation there are no compilation units.
3269      // The {kFinishedBaselineCompilation} event is needed for module
3270      // compilation to finish.
3271      finished_events_.Add(CompilationEvent::kFinishedBaselineCompilation);
3272      if (liftoff_functions.empty() && lazy_functions.empty()) {
3273        // All functions exist now as TurboFan functions, so we can trigger the
3274        // {kFinishedTopTierCompilation} event.
3275        // The {kFinishedTopTierCompilation} event is needed for the C-API so
3276        // that {serialize()} works after {deserialize()}.
3277        finished_events_.Add(CompilationEvent::kFinishedTopTierCompilation);
3278      }
3279    }
3280    compilation_progress_.assign(module->num_declared_functions,
3281                                 kProgressAfterTurbofanDeserialization);
3282    for (auto func_index : lazy_functions) {
3283      native_module_->UseLazyStub(func_index);
3284
3285      compilation_progress_[declared_function_index(module, func_index)] =
3286          SetupCompilationProgressForFunction(/*lazy_function =*/true,
3287                                              native_module_, enabled_features,
3288                                              func_index);
3289    }
3290    for (auto func_index : liftoff_functions) {
3291      if (lazy_module) {
3292        native_module_->UseLazyStub(func_index);
3293      }
3294      // Check that {func_index} is not contained in {lazy_functions}.
3295      DCHECK_EQ(
3296          compilation_progress_[declared_function_index(module, func_index)],
3297          kProgressAfterTurbofanDeserialization);
3298      compilation_progress_[declared_function_index(module, func_index)] =
3299          SetupCompilationProgressForFunction(lazy_module, native_module_,
3300                                              enabled_features, func_index);
3301    }
3302  }
3303  auto builder = std::make_unique<CompilationUnitBuilder>(native_module_);
3304  InitializeCompilationUnits(std::move(builder));
3305  WaitForCompilationEvent(CompilationEvent::kFinishedBaselineCompilation);
3306}
3307
3308void CompilationStateImpl::InitializeRecompilation(
3309    TieringState new_tiering_state,
3310    std::unique_ptr<CompilationEventCallback> recompilation_finished_callback) {
3311  DCHECK(!failed());
3312
3313  // Hold the mutex as long as possible, to synchronize between multiple
3314  // recompilations that are triggered at the same time (e.g. when the profiler
3315  // is disabled).
3316  base::Optional<base::MutexGuard> guard(&callbacks_mutex_);
3317
3318  // As long as there are outstanding recompilation functions, take part in
3319  // compilation. This is to avoid recompiling for the same tier or for
3320  // different tiers concurrently. Note that the compilation unit queues can run
3321  // empty before {outstanding_recompilation_functions_} drops to zero. In this
3322  // case, we do not wait for the last running compilation threads to finish
3323  // their units, but just start our own recompilation already.
3324  while (outstanding_recompilation_functions_ > 0 &&
3325         compilation_unit_queues_.GetTotalSize() > 0) {
3326    guard.reset();
3327    constexpr JobDelegate* kNoDelegate = nullptr;
3328    ExecuteCompilationUnits(native_module_weak_, async_counters_.get(),
3329                            kNoDelegate, kBaselineOrTopTier);
3330    guard.emplace(&callbacks_mutex_);
3331  }
3332
3333  // Information about compilation progress is shared between this class and the
3334  // NativeModule. Before updating information here, consult the NativeModule to
3335  // find all functions that need recompilation.
3336  // Since the current tiering state is updated on the NativeModule before
3337  // triggering recompilation, it's OK if the information is slightly outdated.
3338  // If we compile functions twice, the NativeModule will ignore all redundant
3339  // code (or code compiled for the wrong tier).
3340  std::vector<int> recompile_function_indexes =
3341      native_module_->FindFunctionsToRecompile(new_tiering_state);
3342
3343  callbacks_.emplace_back(std::move(recompilation_finished_callback));
3344  tiering_state_ = new_tiering_state;
3345
3346  // If compilation progress is not initialized yet, then compilation didn't
3347  // start yet, and new code will be kept tiered-down from the start. For
3348  // streaming compilation, there is a special path to tier down later, when
3349  // the module is complete. In any case, we don't need to recompile here.
3350  base::Optional<CompilationUnitBuilder> builder;
3351  if (compilation_progress_.size() > 0) {
3352    builder.emplace(native_module_);
3353    const WasmModule* module = native_module_->module();
3354    DCHECK_EQ(module->num_declared_functions, compilation_progress_.size());
3355    DCHECK_GE(module->num_declared_functions,
3356              recompile_function_indexes.size());
3357    outstanding_recompilation_functions_ =
3358        static_cast<int>(recompile_function_indexes.size());
3359    // Restart recompilation if another recompilation is already happening.
3360    for (auto& progress : compilation_progress_) {
3361      progress = MissingRecompilationField::update(progress, false);
3362    }
3363    auto new_tier = new_tiering_state == kTieredDown ? ExecutionTier::kLiftoff
3364                                                     : ExecutionTier::kTurbofan;
3365    int imported = module->num_imported_functions;
3366    // Generate necessary compilation units on the fly.
3367    for (int function_index : recompile_function_indexes) {
3368      DCHECK_LE(imported, function_index);
3369      int slot_index = function_index - imported;
3370      auto& progress = compilation_progress_[slot_index];
3371      progress = MissingRecompilationField::update(progress, true);
3372      builder->AddRecompilationUnit(function_index, new_tier);
3373    }
3374  }
3375
3376  // Trigger callback if module needs no recompilation.
3377  if (outstanding_recompilation_functions_ == 0) {
3378    TriggerCallbacks(base::EnumSet<CompilationEvent>(
3379        {CompilationEvent::kFinishedRecompilation}));
3380  }
3381
3382  if (builder.has_value()) {
3383    // Avoid holding lock while scheduling a compile job.
3384    guard.reset();
3385    builder->Commit();
3386  }
3387}
3388
3389void CompilationStateImpl::AddCallback(
3390    std::unique_ptr<CompilationEventCallback> callback) {
3391  base::MutexGuard callbacks_guard(&callbacks_mutex_);
3392  // Immediately trigger events that already happened.
3393  for (auto event : {CompilationEvent::kFinishedExportWrappers,
3394                     CompilationEvent::kFinishedBaselineCompilation,
3395                     CompilationEvent::kFinishedTopTierCompilation,
3396                     CompilationEvent::kFailedCompilation}) {
3397    if (finished_events_.contains(event)) {
3398      callback->call(event);
3399    }
3400  }
3401  constexpr base::EnumSet<CompilationEvent> kFinalEvents{
3402      CompilationEvent::kFinishedTopTierCompilation,
3403      CompilationEvent::kFailedCompilation};
3404  if (!finished_events_.contains_any(kFinalEvents)) {
3405    callbacks_.emplace_back(std::move(callback));
3406  }
3407}
3408
3409void CompilationStateImpl::CommitCompilationUnits(
3410    base::Vector<WasmCompilationUnit> baseline_units,
3411    base::Vector<WasmCompilationUnit> top_tier_units,
3412    base::Vector<std::shared_ptr<JSToWasmWrapperCompilationUnit>>
3413        js_to_wasm_wrapper_units) {
3414  if (!js_to_wasm_wrapper_units.empty()) {
3415    // |js_to_wasm_wrapper_units_| will only be initialized once.
3416    DCHECK_EQ(0, outstanding_js_to_wasm_wrappers_.load());
3417    js_to_wasm_wrapper_units_.insert(js_to_wasm_wrapper_units_.end(),
3418                                     js_to_wasm_wrapper_units.begin(),
3419                                     js_to_wasm_wrapper_units.end());
3420    // Use release semantics such that updates to {js_to_wasm_wrapper_units_}
3421    // are available to other threads doing an acquire load.
3422    outstanding_js_to_wasm_wrappers_.store(js_to_wasm_wrapper_units.size(),
3423                                           std::memory_order_release);
3424  }
3425  if (!baseline_units.empty() || !top_tier_units.empty()) {
3426    compilation_unit_queues_.AddUnits(baseline_units, top_tier_units,
3427                                      native_module_->module());
3428  }
3429  compile_job_->NotifyConcurrencyIncrease();
3430}
3431
3432void CompilationStateImpl::CommitTopTierCompilationUnit(
3433    WasmCompilationUnit unit) {
3434  CommitCompilationUnits({}, {&unit, 1}, {});
3435}
3436
3437void CompilationStateImpl::AddTopTierPriorityCompilationUnit(
3438    WasmCompilationUnit unit, size_t priority) {
3439  compilation_unit_queues_.AddTopTierPriorityUnit(unit, priority);
3440  {
3441    base::MutexGuard guard(&callbacks_mutex_);
3442    outstanding_top_tier_functions_++;
3443  }
3444  compile_job_->NotifyConcurrencyIncrease();
3445}
3446
3447std::shared_ptr<JSToWasmWrapperCompilationUnit>
3448CompilationStateImpl::GetNextJSToWasmWrapperCompilationUnit() {
3449  size_t outstanding_units =
3450      outstanding_js_to_wasm_wrappers_.load(std::memory_order_relaxed);
3451  // Use acquire semantics such that initialization of
3452  // {js_to_wasm_wrapper_units_} is available.
3453  while (outstanding_units &&
3454         !outstanding_js_to_wasm_wrappers_.compare_exchange_weak(
3455             outstanding_units, outstanding_units - 1,
3456             std::memory_order_acquire)) {
3457    // Retry with updated {outstanding_units}.
3458  }
3459  if (outstanding_units == 0) return nullptr;
3460  return js_to_wasm_wrapper_units_[outstanding_units - 1];
3461}
3462
3463void CompilationStateImpl::FinalizeJSToWasmWrappers(
3464    Isolate* isolate, const WasmModule* module,
3465    Handle<FixedArray>* export_wrappers_out) {
3466  *export_wrappers_out = isolate->factory()->NewFixedArray(
3467      MaxNumExportWrappers(module), AllocationType::kOld);
3468  // TODO(6792): Wrappers below are allocated with {Factory::NewCode}. As an
3469  // optimization we create a code memory modification scope that avoids
3470  // changing the page permissions back-and-forth between RWX and RX, because
3471  // many such wrapper are allocated in sequence below.
3472  TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
3473               "wasm.FinalizeJSToWasmWrappers", "wrappers",
3474               js_to_wasm_wrapper_units_.size());
3475  CodePageCollectionMemoryModificationScope modification_scope(isolate->heap());
3476  for (auto& unit : js_to_wasm_wrapper_units_) {
3477    DCHECK_EQ(isolate, unit->isolate());
3478    Handle<Code> code = unit->Finalize();
3479    int wrapper_index =
3480        GetExportWrapperIndex(module, unit->sig(), unit->is_import());
3481    (*export_wrappers_out)->set(wrapper_index, ToCodeT(*code));
3482    RecordStats(*code, isolate->counters());
3483  }
3484}
3485
3486CompilationUnitQueues::Queue* CompilationStateImpl::GetQueueForCompileTask(
3487    int task_id) {
3488  return compilation_unit_queues_.GetQueueForTask(task_id);
3489}
3490
3491base::Optional<WasmCompilationUnit>
3492CompilationStateImpl::GetNextCompilationUnit(
3493    CompilationUnitQueues::Queue* queue, CompileBaselineOnly baseline_only) {
3494  return compilation_unit_queues_.GetNextUnit(queue, baseline_only);
3495}
3496
3497void CompilationStateImpl::OnFinishedUnits(
3498    base::Vector<WasmCode*> code_vector) {
3499  TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
3500               "wasm.OnFinishedUnits", "units", code_vector.size());
3501
3502  base::MutexGuard guard(&callbacks_mutex_);
3503
3504  // In case of no outstanding compilation units we can return early.
3505  // This is especially important for lazy modules that were deserialized.
3506  // Compilation progress was not set up in these cases.
3507  if (outstanding_baseline_units_ == 0 && outstanding_export_wrappers_ == 0 &&
3508      outstanding_top_tier_functions_ == 0 &&
3509      outstanding_recompilation_functions_ == 0) {
3510    return;
3511  }
3512
3513  // Assume an order of execution tiers that represents the quality of their
3514  // generated code.
3515  static_assert(ExecutionTier::kNone < ExecutionTier::kLiftoff &&
3516                    ExecutionTier::kLiftoff < ExecutionTier::kTurbofan,
3517                "Assume an order on execution tiers");
3518
3519  DCHECK_EQ(compilation_progress_.size(),
3520            native_module_->module()->num_declared_functions);
3521
3522  base::EnumSet<CompilationEvent> triggered_events;
3523
3524  for (size_t i = 0; i < code_vector.size(); i++) {
3525    WasmCode* code = code_vector[i];
3526    DCHECK_NOT_NULL(code);
3527    DCHECK_LT(code->index(), native_module_->num_functions());
3528
3529    if (code->index() <
3530        static_cast<int>(native_module_->num_imported_functions())) {
3531      // Import wrapper.
3532      DCHECK_EQ(code->tier(), ExecutionTier::kTurbofan);
3533      outstanding_baseline_units_--;
3534    } else {
3535      // Function.
3536      DCHECK_NE(code->tier(), ExecutionTier::kNone);
3537
3538      // Read function's compilation progress.
3539      // This view on the compilation progress may differ from the actually
3540      // compiled code. Any lazily compiled function does not contribute to the
3541      // compilation progress but may publish code to the code manager.
3542      int slot_index =
3543          declared_function_index(native_module_->module(), code->index());
3544      uint8_t function_progress = compilation_progress_[slot_index];
3545      ExecutionTier required_baseline_tier =
3546          RequiredBaselineTierField::decode(function_progress);
3547      ExecutionTier required_top_tier =
3548          RequiredTopTierField::decode(function_progress);
3549      ExecutionTier reached_tier = ReachedTierField::decode(function_progress);
3550
3551      // Check whether required baseline or top tier are reached.
3552      if (reached_tier < required_baseline_tier &&
3553          required_baseline_tier <= code->tier()) {
3554        DCHECK_GT(outstanding_baseline_units_, 0);
3555        outstanding_baseline_units_--;
3556      }
3557      if (code->tier() == ExecutionTier::kTurbofan) {
3558        bytes_since_last_chunk_ += code->instructions().size();
3559      }
3560      if (reached_tier < required_top_tier &&
3561          required_top_tier <= code->tier()) {
3562        DCHECK_GT(outstanding_top_tier_functions_, 0);
3563        outstanding_top_tier_functions_--;
3564      }
3565
3566      if (V8_UNLIKELY(MissingRecompilationField::decode(function_progress))) {
3567        DCHECK_LT(0, outstanding_recompilation_functions_);
3568        // If tiering up, accept any TurboFan code. For tiering down, look at
3569        // the {for_debugging} flag. The tier can be Liftoff or TurboFan and is
3570        // irrelevant here. In particular, we want to ignore any outstanding
3571        // non-debugging units.
3572        bool matches = tiering_state_ == kTieredDown
3573                           ? code->for_debugging()
3574                           : code->tier() == ExecutionTier::kTurbofan;
3575        if (matches) {
3576          outstanding_recompilation_functions_--;
3577          compilation_progress_[slot_index] = MissingRecompilationField::update(
3578              compilation_progress_[slot_index], false);
3579          if (outstanding_recompilation_functions_ == 0) {
3580            triggered_events.Add(CompilationEvent::kFinishedRecompilation);
3581          }
3582        }
3583      }
3584
3585      // Update function's compilation progress.
3586      if (code->tier() > reached_tier) {
3587        compilation_progress_[slot_index] = ReachedTierField::update(
3588            compilation_progress_[slot_index], code->tier());
3589      }
3590      DCHECK_LE(0, outstanding_baseline_units_);
3591    }
3592  }
3593
3594  TriggerCallbacks(triggered_events);
3595}
3596
3597void CompilationStateImpl::OnFinishedJSToWasmWrapperUnits(int num) {
3598  if (num == 0) return;
3599  base::MutexGuard guard(&callbacks_mutex_);
3600  DCHECK_GE(outstanding_export_wrappers_, num);
3601  outstanding_export_wrappers_ -= num;
3602  TriggerCallbacks();
3603}
3604
3605void CompilationStateImpl::TriggerCallbacks(
3606    base::EnumSet<CompilationEvent> triggered_events) {
3607  DCHECK(!callbacks_mutex_.TryLock());
3608
3609  if (outstanding_export_wrappers_ == 0) {
3610    triggered_events.Add(CompilationEvent::kFinishedExportWrappers);
3611    if (outstanding_baseline_units_ == 0) {
3612      triggered_events.Add(CompilationEvent::kFinishedBaselineCompilation);
3613      if (dynamic_tiering_ == DynamicTiering::kDisabled &&
3614          outstanding_top_tier_functions_ == 0) {
3615        triggered_events.Add(CompilationEvent::kFinishedTopTierCompilation);
3616      }
3617    }
3618  }
3619
3620  if (dynamic_tiering_ == DynamicTiering::kEnabled &&
3621      static_cast<size_t>(FLAG_wasm_caching_threshold) <
3622          bytes_since_last_chunk_) {
3623    triggered_events.Add(CompilationEvent::kFinishedCompilationChunk);
3624    bytes_since_last_chunk_ = 0;
3625  }
3626  if (compile_failed_.load(std::memory_order_relaxed)) {
3627    // *Only* trigger the "failed" event.
3628    triggered_events =
3629        base::EnumSet<CompilationEvent>({CompilationEvent::kFailedCompilation});
3630  }
3631
3632  if (triggered_events.empty()) return;
3633
3634  // Don't trigger past events again.
3635  triggered_events -= finished_events_;
3636  // Recompilation can happen multiple times, thus do not store this. There can
3637  // also be multiple compilation chunks.
3638  finished_events_ |= triggered_events -
3639                      CompilationEvent::kFinishedRecompilation -
3640                      CompilationEvent::kFinishedCompilationChunk;
3641
3642  for (auto event :
3643       {std::make_pair(CompilationEvent::kFailedCompilation,
3644                       "wasm.CompilationFailed"),
3645        std::make_pair(CompilationEvent::kFinishedExportWrappers,
3646                       "wasm.ExportWrappersFinished"),
3647        std::make_pair(CompilationEvent::kFinishedBaselineCompilation,
3648                       "wasm.BaselineFinished"),
3649        std::make_pair(CompilationEvent::kFinishedTopTierCompilation,
3650                       "wasm.TopTierFinished"),
3651        std::make_pair(CompilationEvent::kFinishedCompilationChunk,
3652                       "wasm.CompilationChunkFinished"),
3653        std::make_pair(CompilationEvent::kFinishedRecompilation,
3654                       "wasm.RecompilationFinished")}) {
3655    if (!triggered_events.contains(event.first)) continue;
3656    DCHECK_NE(compilation_id_, kInvalidCompilationID);
3657    TRACE_EVENT1("v8.wasm", event.second, "id", compilation_id_);
3658    for (auto& callback : callbacks_) {
3659      callback->call(event.first);
3660    }
3661  }
3662
3663  if (outstanding_baseline_units_ == 0 && outstanding_export_wrappers_ == 0 &&
3664      outstanding_top_tier_functions_ == 0 &&
3665      outstanding_recompilation_functions_ == 0) {
3666    callbacks_.erase(
3667        std::remove_if(
3668            callbacks_.begin(), callbacks_.end(),
3669            [](std::unique_ptr<CompilationEventCallback>& event) {
3670              return event->release_after_final_event() ==
3671                     CompilationEventCallback::ReleaseAfterFinalEvent::kRelease;
3672            }),
3673        callbacks_.end());
3674  }
3675}
3676
3677void CompilationStateImpl::OnCompilationStopped(WasmFeatures detected) {
3678  base::MutexGuard guard(&mutex_);
3679  detected_features_.Add(detected);
3680}
3681
3682void CompilationStateImpl::PublishDetectedFeatures(Isolate* isolate) {
3683  // Notifying the isolate of the feature counts must take place under
3684  // the mutex, because even if we have finished baseline compilation,
3685  // tiering compilations may still occur in the background.
3686  base::MutexGuard guard(&mutex_);
3687  UpdateFeatureUseCounts(isolate, detected_features_);
3688}
3689
3690void CompilationStateImpl::PublishCompilationResults(
3691    std::vector<std::unique_ptr<WasmCode>> unpublished_code) {
3692  if (unpublished_code.empty()) return;
3693
3694  // For import wrapper compilation units, add result to the cache.
3695  int num_imported_functions = native_module_->num_imported_functions();
3696  WasmImportWrapperCache* cache = native_module_->import_wrapper_cache();
3697  for (const auto& code : unpublished_code) {
3698    int func_index = code->index();
3699    DCHECK_LE(0, func_index);
3700    DCHECK_LT(func_index, native_module_->num_functions());
3701    if (func_index < num_imported_functions) {
3702      const FunctionSig* sig =
3703          native_module_->module()->functions[func_index].sig;
3704      WasmImportWrapperCache::CacheKey key(
3705          compiler::kDefaultImportCallKind, sig,
3706          static_cast<int>(sig->parameter_count()), kNoSuspend);
3707      // If two imported functions have the same key, only one of them should
3708      // have been added as a compilation unit. So it is always the first time
3709      // we compile a wrapper for this key here.
3710      DCHECK_NULL((*cache)[key]);
3711      (*cache)[key] = code.get();
3712      code->IncRef();
3713    }
3714  }
3715  PublishCode(base::VectorOf(unpublished_code));
3716}
3717
3718void CompilationStateImpl::PublishCode(
3719    base::Vector<std::unique_ptr<WasmCode>> code) {
3720  WasmCodeRefScope code_ref_scope;
3721  std::vector<WasmCode*> published_code =
3722      native_module_->PublishCode(std::move(code));
3723  // Defer logging code in case wire bytes were not fully received yet.
3724  if (native_module_->HasWireBytes()) {
3725    GetWasmEngine()->LogCode(base::VectorOf(published_code));
3726  }
3727
3728  OnFinishedUnits(base::VectorOf(std::move(published_code)));
3729}
3730
3731void CompilationStateImpl::SchedulePublishCompilationResults(
3732    std::vector<std::unique_ptr<WasmCode>> unpublished_code) {
3733  {
3734    base::MutexGuard guard(&publish_mutex_);
3735    if (publisher_running_) {
3736      // Add new code to the queue and return.
3737      publish_queue_.reserve(publish_queue_.size() + unpublished_code.size());
3738      for (auto& c : unpublished_code) {
3739        publish_queue_.emplace_back(std::move(c));
3740      }
3741      return;
3742    }
3743    publisher_running_ = true;
3744  }
3745  CodeSpaceWriteScope code_space_write_scope(native_module_);
3746  while (true) {
3747    PublishCompilationResults(std::move(unpublished_code));
3748    unpublished_code.clear();
3749
3750    // Keep publishing new code that came in.
3751    base::MutexGuard guard(&publish_mutex_);
3752    DCHECK(publisher_running_);
3753    if (publish_queue_.empty()) {
3754      publisher_running_ = false;
3755      return;
3756    }
3757    unpublished_code.swap(publish_queue_);
3758  }
3759}
3760
3761size_t CompilationStateImpl::NumOutstandingCompilations() const {
3762  size_t outstanding_wrappers =
3763      outstanding_js_to_wasm_wrappers_.load(std::memory_order_relaxed);
3764  size_t outstanding_functions = compilation_unit_queues_.GetTotalSize();
3765  return outstanding_wrappers + outstanding_functions;
3766}
3767
3768void CompilationStateImpl::SetError() {
3769  compile_cancelled_.store(true, std::memory_order_relaxed);
3770  if (compile_failed_.exchange(true, std::memory_order_relaxed)) {
3771    return;  // Already failed before.
3772  }
3773
3774  base::MutexGuard callbacks_guard(&callbacks_mutex_);
3775  TriggerCallbacks();
3776  callbacks_.clear();
3777}
3778
3779void CompilationStateImpl::WaitForCompilationEvent(
3780    CompilationEvent expect_event) {
3781  class WaitForCompilationEventCallback : public CompilationEventCallback {
3782   public:
3783    WaitForCompilationEventCallback(std::shared_ptr<base::Semaphore> semaphore,
3784                                    std::shared_ptr<std::atomic<bool>> done,
3785                                    base::EnumSet<CompilationEvent> events)
3786        : semaphore_(std::move(semaphore)),
3787          done_(std::move(done)),
3788          events_(events) {}
3789
3790    void call(CompilationEvent event) override {
3791      if (!events_.contains(event)) return;
3792      done_->store(true, std::memory_order_relaxed);
3793      semaphore_->Signal();
3794    }
3795
3796   private:
3797    std::shared_ptr<base::Semaphore> semaphore_;
3798    std::shared_ptr<std::atomic<bool>> done_;
3799    base::EnumSet<CompilationEvent> events_;
3800  };
3801
3802  auto semaphore = std::make_shared<base::Semaphore>(0);
3803  auto done = std::make_shared<std::atomic<bool>>(false);
3804  base::EnumSet<CompilationEvent> events{expect_event,
3805                                         CompilationEvent::kFailedCompilation};
3806  {
3807    base::MutexGuard callbacks_guard(&callbacks_mutex_);
3808    if (finished_events_.contains_any(events)) return;
3809    callbacks_.emplace_back(std::make_unique<WaitForCompilationEventCallback>(
3810        semaphore, done, events));
3811  }
3812
3813  class WaitForEventDelegate final : public JobDelegate {
3814   public:
3815    explicit WaitForEventDelegate(std::shared_ptr<std::atomic<bool>> done)
3816        : done_(std::move(done)) {}
3817
3818    bool ShouldYield() override {
3819      return done_->load(std::memory_order_relaxed);
3820    }
3821
3822    bool IsJoiningThread() const override { return true; }
3823
3824    void NotifyConcurrencyIncrease() override { UNIMPLEMENTED(); }
3825
3826    uint8_t GetTaskId() override { return kMainTaskId; }
3827
3828   private:
3829    std::shared_ptr<std::atomic<bool>> done_;
3830  };
3831
3832  WaitForEventDelegate delegate{done};
3833  // Everything except for top-tier units will be processed with kBaselineOnly
3834  // (including wrappers). Hence we choose this for any event except
3835  // {kFinishedTopTierCompilation}.
3836  auto compile_tiers =
3837      expect_event == CompilationEvent::kFinishedTopTierCompilation
3838          ? kBaselineOrTopTier
3839          : kBaselineOnly;
3840  ExecuteCompilationUnits(native_module_weak_, async_counters_.get(), &delegate,
3841                          compile_tiers);
3842  semaphore->Wait();
3843}
3844
3845namespace {
3846using JSToWasmWrapperQueue =
3847    WrapperQueue<JSToWasmWrapperKey, base::hash<JSToWasmWrapperKey>>;
3848using JSToWasmWrapperUnitMap =
3849    std::unordered_map<JSToWasmWrapperKey,
3850                       std::unique_ptr<JSToWasmWrapperCompilationUnit>,
3851                       base::hash<JSToWasmWrapperKey>>;
3852
3853class CompileJSToWasmWrapperJob final : public JobTask {
3854 public:
3855  CompileJSToWasmWrapperJob(JSToWasmWrapperQueue* queue,
3856                            JSToWasmWrapperUnitMap* compilation_units)
3857      : queue_(queue),
3858        compilation_units_(compilation_units),
3859        outstanding_units_(queue->size()) {}
3860
3861  void Run(JobDelegate* delegate) override {
3862    while (base::Optional<JSToWasmWrapperKey> key = queue_->pop()) {
3863      JSToWasmWrapperCompilationUnit* unit = (*compilation_units_)[*key].get();
3864      unit->Execute();
3865      outstanding_units_.fetch_sub(1, std::memory_order_relaxed);
3866      if (delegate && delegate->ShouldYield()) return;
3867    }
3868  }
3869
3870  size_t GetMaxConcurrency(size_t /* worker_count */) const override {
3871    DCHECK_GE(FLAG_wasm_num_compilation_tasks, 1);
3872    // {outstanding_units_} includes the units that other workers are currently
3873    // working on, so we can safely ignore the {worker_count} and just return
3874    // the current number of outstanding units.
3875    return std::min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks),
3876                    outstanding_units_.load(std::memory_order_relaxed));
3877  }
3878
3879 private:
3880  JSToWasmWrapperQueue* const queue_;
3881  JSToWasmWrapperUnitMap* const compilation_units_;
3882  std::atomic<size_t> outstanding_units_;
3883};
3884}  // namespace
3885
3886void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module,
3887                             Handle<FixedArray>* export_wrappers_out) {
3888  TRACE_EVENT0("v8.wasm", "wasm.CompileJsToWasmWrappers");
3889  *export_wrappers_out = isolate->factory()->NewFixedArray(
3890      MaxNumExportWrappers(module), AllocationType::kOld);
3891
3892  JSToWasmWrapperQueue queue;
3893  JSToWasmWrapperUnitMap compilation_units;
3894  WasmFeatures enabled_features = WasmFeatures::FromIsolate(isolate);
3895
3896  // Prepare compilation units in the main thread.
3897  for (auto exp : module->export_table) {
3898    if (exp.kind != kExternalFunction) continue;
3899    auto& function = module->functions[exp.index];
3900    JSToWasmWrapperKey key(function.imported, *function.sig);
3901    if (queue.insert(key)) {
3902      auto unit = std::make_unique<JSToWasmWrapperCompilationUnit>(
3903          isolate, function.sig, module, function.imported, enabled_features,
3904          JSToWasmWrapperCompilationUnit::kAllowGeneric);
3905      compilation_units.emplace(key, std::move(unit));
3906    }
3907  }
3908
3909  {
3910    // This is nested inside the event above, so the name can be less
3911    // descriptive. It's mainly to log the number of wrappers.
3912    TRACE_EVENT1("v8.wasm", "wasm.JsToWasmWrapperCompilation", "num_wrappers",
3913                 compilation_units.size());
3914    auto job =
3915        std::make_unique<CompileJSToWasmWrapperJob>(&queue, &compilation_units);
3916    if (FLAG_wasm_num_compilation_tasks > 0) {
3917      auto job_handle = V8::GetCurrentPlatform()->PostJob(
3918          TaskPriority::kUserVisible, std::move(job));
3919
3920      // Wait for completion, while contributing to the work.
3921      job_handle->Join();
3922    } else {
3923      job->Run(nullptr);
3924    }
3925  }
3926
3927  // Finalize compilation jobs in the main thread.
3928  // TODO(6792): Wrappers below are allocated with {Factory::NewCode}. As an
3929  // optimization we create a code memory modification scope that avoids
3930  // changing the page permissions back-and-forth between RWX and RX, because
3931  // many such wrapper are allocated in sequence below.
3932  CodePageCollectionMemoryModificationScope modification_scope(isolate->heap());
3933  for (auto& pair : compilation_units) {
3934    JSToWasmWrapperKey key = pair.first;
3935    JSToWasmWrapperCompilationUnit* unit = pair.second.get();
3936    DCHECK_EQ(isolate, unit->isolate());
3937    Handle<Code> code = unit->Finalize();
3938    int wrapper_index = GetExportWrapperIndex(module, &key.second, key.first);
3939    (*export_wrappers_out)->set(wrapper_index, ToCodeT(*code));
3940    RecordStats(*code, isolate->counters());
3941  }
3942}
3943
3944WasmCode* CompileImportWrapper(
3945    NativeModule* native_module, Counters* counters,
3946    compiler::WasmImportCallKind kind, const FunctionSig* sig,
3947    int expected_arity, Suspend suspend,
3948    WasmImportWrapperCache::ModificationScope* cache_scope) {
3949  // Entry should exist, so that we don't insert a new one and invalidate
3950  // other threads' iterators/references, but it should not have been compiled
3951  // yet.
3952  WasmImportWrapperCache::CacheKey key(kind, sig, expected_arity, suspend);
3953  DCHECK_NULL((*cache_scope)[key]);
3954  bool source_positions = is_asmjs_module(native_module->module());
3955  // Keep the {WasmCode} alive until we explicitly call {IncRef}.
3956  WasmCodeRefScope code_ref_scope;
3957  CompilationEnv env = native_module->CreateCompilationEnv();
3958  WasmCompilationResult result = compiler::CompileWasmImportCallWrapper(
3959      &env, kind, sig, source_positions, expected_arity, suspend);
3960  WasmCode* published_code;
3961  {
3962    CodeSpaceWriteScope code_space_write_scope(native_module);
3963    std::unique_ptr<WasmCode> wasm_code = native_module->AddCode(
3964        result.func_index, result.code_desc, result.frame_slot_count,
3965        result.tagged_parameter_slots,
3966        result.protected_instructions_data.as_vector(),
3967        result.source_positions.as_vector(), GetCodeKind(result),
3968        ExecutionTier::kNone, kNoDebugging);
3969    published_code = native_module->PublishCode(std::move(wasm_code));
3970  }
3971  (*cache_scope)[key] = published_code;
3972  published_code->IncRef();
3973  counters->wasm_generated_code_size()->Increment(
3974      published_code->instructions().length());
3975  counters->wasm_reloc_size()->Increment(published_code->reloc_info().length());
3976  return published_code;
3977}
3978
3979}  // namespace wasm
3980}  // namespace internal
3981}  // namespace v8
3982
3983#undef TRACE_COMPILE
3984#undef TRACE_STREAMING
3985#undef TRACE_LAZY
3986