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