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 2485960 : class BackgroundCompileToken {
67 : public:
68 : explicit BackgroundCompileToken(
69 : const std::shared_ptr<NativeModule>& native_module)
70 1242981 : : native_module_(native_module) {}
71 :
72 1251100 : void Cancel() {
73 1251100 : base::SharedMutexGuard<base::kExclusive> mutex_guard(&mutex_);
74 : native_module_.reset();
75 1251100 : }
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 812117 : mutex_.LockShared();
84 : return native_module_.lock();
85 : }
86 :
87 815985 : 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 812117 : explicit BackgroundCompileScope(
98 : const std::shared_ptr<BackgroundCompileToken>& token)
99 1628250 : : token_(token.get()), native_module_(token->StartScope()) {}
100 :
101 1632059 : ~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 1242981 : class CompilationUnitQueues {
127 : public:
128 1242981 : explicit CompilationUnitQueues(int max_tasks) : queues_(max_tasks) {
129 : DCHECK_LT(0, max_tasks);
130 18644619 : for (int task_id = 0; task_id < max_tasks; ++task_id) {
131 17401638 : queues_[task_id].next_steal_task_id_ = next_task_id(task_id);
132 : }
133 3728943 : for (auto& atomic_counter : num_units_) {
134 : std::atomic_init(&atomic_counter, size_t{0});
135 : }
136 1242981 : }
137 :
138 794575 : 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 794575 : int max_tier = baseline_only ? kBaseline : kTopTier;
146 833661 : for (int tier = GetLowestTierWithUnits(); tier <= max_tier; ++tier) {
147 352611 : 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 352611 : base::MutexGuard mutex_guard(&queue->mutex_);
153 352797 : if (!queue->units_[tier].empty()) {
154 314852 : auto unit = std::move(queue->units_[tier].back());
155 : queue->units_[tier].pop_back();
156 : DecrementUnitCount(tier);
157 : return unit;
158 : }
159 37888 : 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 609778 : for (; steal_trials > 0;
166 : --steal_trials, steal_task_id = next_task_id(steal_task_id)) {
167 208947 : if (steal_task_id == task_id) continue;
168 538212 : if (auto unit = StealUnitsAndGetFirst(task_id, steal_task_id, tier)) {
169 : DecrementUnitCount(tier);
170 18344 : return unit;
171 : }
172 : }
173 : }
174 : return {};
175 : }
176 :
177 138919 : 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 138919 : int queue_to_add = next_queue_to_add.load(std::memory_order_relaxed);
183 277838 : 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 138919 : Queue* queue = &queues_[queue_to_add];
189 138919 : base::MutexGuard guard(&queue->mutex_);
190 138918 : if (!baseline_units.empty()) {
191 : queue->units_[kBaseline].insert(
192 : queue->units_[kBaseline].end(),
193 : std::make_move_iterator(baseline_units.begin()),
194 138851 : std::make_move_iterator(baseline_units.end()));
195 : num_units_[kBaseline].fetch_add(baseline_units.size(),
196 : std::memory_order_relaxed);
197 : }
198 138920 : 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 52073 : 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 138919 : }
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 1061245 : for (auto& atomic_counter : num_units_) {
214 707504 : 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 34803276 : 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 9048720 : int next = task_id + 1;
241 9048720 : return next == static_cast<int>(queues_.size()) ? 0 : next;
242 : }
243 :
244 : int GetLowestTierWithUnits() const {
245 2743241 : for (int tier = 0; tier < kNumTiers; ++tier) {
246 1350922 : 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 185461 : 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 185555 : std::vector<std::unique_ptr<WasmCompilationUnit>> stolen;
264 : {
265 185461 : Queue* steal_queue = &queues_[steal_from_task_id];
266 185461 : base::MutexGuard guard(&steal_queue->mutex_);
267 185564 : if (steal_queue->units_[wanted_tier].empty()) return {};
268 : auto* steal_from_vector = &steal_queue->units_[wanted_tier];
269 18344 : 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 18343 : steal_from_vector->resize(remaining);
274 : }
275 : DCHECK(!stolen.empty());
276 18343 : auto returned_unit = std::move(stolen.back());
277 : stolen.pop_back();
278 18344 : Queue* queue = &queues_[task_id];
279 18344 : base::MutexGuard guard(&queue->mutex_);
280 18343 : auto* target_queue = &queue->units_[wanted_tier];
281 : target_queue->insert(target_queue->end(),
282 : std::make_move_iterator(stolen.begin()),
283 18343 : std::make_move_iterator(stolen.end()));
284 18338 : 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 4971924 : 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 353743 : base::MutexGuard guard(&callbacks_mutex_);
337 : DCHECK_LE(outstanding_baseline_functions_, outstanding_top_tier_functions_);
338 353730 : return outstanding_baseline_functions_ == 0;
339 : }
340 :
341 : CompileMode compile_mode() const { return compile_mode_; }
342 : Counters* counters() const { return async_counters_.get(); }
343 8126 : WasmFeatures* detected_features() { return &detected_features_; }
344 :
345 2340784 : void SetWireBytesStorage(
346 : std::shared_ptr<WireBytesStorage> wire_bytes_storage) {
347 2340784 : base::MutexGuard guard(&mutex_);
348 : wire_bytes_storage_ = wire_bytes_storage;
349 2340784 : }
350 :
351 : std::shared_ptr<WireBytesStorage> GetWireBytesStorage() const {
352 1210755 : 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 258883 : 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 145004 : if (detected.threads) {
430 1436 : isolate->CountUsage(v8::Isolate::UseCounterFeature::kWasmThreadOpcodes);
431 : }
432 : }
433 :
434 : } // namespace
435 :
436 : //////////////////////////////////////////////////////
437 : // PIMPL implementation of {CompilationState}.
438 :
439 1242981 : CompilationState::~CompilationState() { Impl(this)->~CompilationStateImpl(); }
440 :
441 1242981 : void CompilationState::AbortCompilation() { Impl(this)->AbortCompilation(); }
442 :
443 0 : void CompilationState::SetError() { Impl(this)->SetError(); }
444 :
445 2340528 : void CompilationState::SetWireBytesStorage(
446 : std::shared_ptr<WireBytesStorage> wire_bytes_storage) {
447 4681056 : Impl(this)->SetWireBytesStorage(std::move(wire_bytes_storage));
448 2340528 : }
449 :
450 731780 : std::shared_ptr<WireBytesStorage> CompilationState::GetWireBytesStorage()
451 : const {
452 731780 : return Impl(this)->GetWireBytesStorage();
453 : }
454 :
455 2201 : void CompilationState::AddCallback(CompilationState::callback_t callback) {
456 4402 : 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 1242981 : 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 2485962 : 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 1268 : uint32_t hint_index = func_index - module->num_imported_functions;
501 : const std::vector<WasmCompilationHint>& compilation_hints =
502 : module->compilation_hints;
503 2536 : 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 559482 : if (enabled_features.compilation_hints) {
513 : const WasmCompilationHint* hint = GetCompilationHint(module, func_index);
514 1143 : return hint != nullptr &&
515 343 : 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 559506 : 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 567450 : ExecutionTierPair GetRequestedExecutionTiers(
534 : const WasmModule* module, CompileMode compile_mode,
535 : const WasmFeatures& enabled_features, uint32_t func_index) {
536 : ExecutionTierPair result;
537 567450 : switch (compile_mode) {
538 : case CompileMode::kRegular:
539 : result.baseline_tier =
540 275084 : WasmCompilationUnit::GetDefaultExecutionTier(module);
541 : result.top_tier = result.baseline_tier;
542 275085 : 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 292366 : 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 292365 : if (result.baseline_tier > result.top_tier) {
566 : result.top_tier = result.baseline_tier;
567 : }
568 292365 : 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 145145 : class CompilationUnitBuilder {
577 : public:
578 : explicit CompilationUnitBuilder(NativeModule* native_module)
579 : : native_module_(native_module),
580 145145 : default_tier_(WasmCompilationUnit::GetDefaultExecutionTier(
581 290290 : native_module->module())) {}
582 :
583 235704 : void AddUnits(uint32_t func_index) {
584 : ExecutionTierPair tiers = GetRequestedExecutionTiers(
585 : native_module_->module(), compilation_state()->compile_mode(),
586 471408 : native_module_->enabled_features(), func_index);
587 235704 : baseline_units_.emplace_back(CreateUnit(func_index, tiers.baseline_tier));
588 235701 : if (tiers.baseline_tier != tiers.top_tier) {
589 99650 : tiering_units_.emplace_back(CreateUnit(func_index, tiers.top_tier));
590 : }
591 235702 : }
592 :
593 145211 : bool Commit() {
594 145211 : if (baseline_units_.empty() && tiering_units_.empty()) return false;
595 : compilation_state()->AddCompilationUnits(VectorOf(baseline_units_),
596 : VectorOf(tiering_units_));
597 138853 : Clear();
598 138853 : return true;
599 : }
600 :
601 138913 : void Clear() {
602 : baseline_units_.clear();
603 : tiering_units_.clear();
604 138913 : }
605 :
606 : private:
607 : std::unique_ptr<WasmCompilationUnit> CreateUnit(uint32_t func_index,
608 : ExecutionTier tier) {
609 335353 : 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 8126 : 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 16252 : 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 8126 : 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 8126 : native_module->enabled_features(), func_index);
653 :
654 16252 : WasmCompilationUnit baseline_unit(func_index, tiers.baseline_tier);
655 8126 : CompilationEnv env = native_module->CreateCompilationEnv();
656 : WasmCompilationResult result = baseline_unit.ExecuteCompilation(
657 8126 : isolate->wasm_engine(), &env, compilation_state->GetWireBytesStorage(),
658 16252 : isolate->counters(), compilation_state->detected_features());
659 16252 : WasmCodeRefScope code_ref_scope;
660 8126 : WasmCode* code = native_module->AddCompiledCode(std::move(result));
661 :
662 8126 : 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 8126 : 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 8126 : if (WasmCode::ShouldBeLogged(isolate)) code->LogCode(isolate);
679 :
680 8126 : double func_kb = 1e-3 * func->code.length();
681 8126 : double compilation_seconds = compilation_timer.Elapsed().InSecondsF();
682 :
683 8126 : counters->wasm_lazily_compiled_functions()->Increment();
684 :
685 8126 : int throughput_sample = static_cast<int>(func_kb / compilation_seconds);
686 8126 : counters->wasm_lazy_compilation_throughput()->AddSample(throughput_sample);
687 8126 : }
688 :
689 : namespace {
690 :
691 221122 : void RecordStats(const Code code, Counters* counters) {
692 442245 : counters->wasm_generated_code_size()->Increment(code->body_size());
693 221124 : counters->wasm_reloc_size()->Increment(code->relocation_info()->length());
694 221123 : }
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 482815 : 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 1448583 : TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "ExecuteCompilationUnits");
705 :
706 482819 : 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 482819 : if (is_foreground) task_id = 0;
711 :
712 482819 : 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 482729 : platform->MonotonicallyIncreasingTime() + kBackgroundCompileTimeLimit;
718 :
719 : // These fields are initialized in a {BackgroundCompileScope} before
720 : // starting compilation.
721 482849 : base::Optional<CompilationEnv> env;
722 482849 : std::shared_ptr<WireBytesStorage> wire_bytes;
723 : std::shared_ptr<const WasmModule> module;
724 : WasmEngine* wasm_engine = nullptr;
725 965835 : std::unique_ptr<WasmCompilationUnit> unit;
726 482849 : WasmFeatures detected_features = kNoWasmFeatures;
727 :
728 : auto stop = [is_foreground, task_id,
729 938922 : &detected_features](BackgroundCompileScope& compile_scope) {
730 469461 : if (is_foreground) {
731 : compile_scope.compilation_state()->UpdateDetectedFeatures(
732 250841 : detected_features);
733 : } else {
734 : compile_scope.compilation_state()->OnBackgroundTaskStopped(
735 218620 : task_id, detected_features);
736 : }
737 469491 : };
738 :
739 : // Preparation (synchronized): Initialize the fields above and get the first
740 : // compilation unit.
741 : {
742 671942 : BackgroundCompileScope compile_scope(token);
743 776838 : if (compile_scope.cancelled()) return false;
744 941842 : env.emplace(compile_scope.native_module()->CreateCompilationEnv());
745 471041 : wire_bytes = compile_scope.compilation_state()->GetWireBytesStorage();
746 471039 : module = compile_scope.native_module()->shared_module();
747 : wasm_engine = compile_scope.native_module()->engine();
748 470859 : unit = compile_scope.compilation_state()->GetNextCompilationUnit(
749 : task_id, baseline_only);
750 471021 : if (unit == nullptr) {
751 281928 : stop(compile_scope);
752 281939 : return false;
753 : }
754 : }
755 :
756 189079 : std::vector<WasmCompilationResult> results_to_publish;
757 :
758 : auto publish_results = [&results_to_publish](
759 942396 : BackgroundCompileScope* compile_scope) {
760 541558 : if (results_to_publish.empty()) return;
761 537444 : WasmCodeRefScope code_ref_scope;
762 : std::vector<WasmCode*> code_vector =
763 : compile_scope->native_module()->AddCompiledCode(
764 268471 : VectorOf(results_to_publish));
765 268833 : compile_scope->compilation_state()->OnFinishedUnits(VectorOf(code_vector));
766 : results_to_publish.clear();
767 189067 : };
768 :
769 : bool compilation_failed = false;
770 : while (true) {
771 : // (asynchronous): Execute the compilation.
772 : WasmCompilationResult result = unit->ExecuteCompilation(
773 477178 : wasm_engine, &env.value(), wire_bytes, counters, &detected_features);
774 329793 : results_to_publish.emplace_back(std::move(result));
775 :
776 : // (synchronized): Publish the compilation result and get the next unit.
777 : {
778 473749 : BackgroundCompileScope compile_scope(token);
779 514353 : if (compile_scope.cancelled()) return true;
780 331710 : if (!results_to_publish.back().succeeded()) {
781 : // Compile error.
782 7880 : compile_scope.compilation_state()->SetError();
783 7876 : stop(compile_scope);
784 : compilation_failed = true;
785 7880 : break;
786 : }
787 : // Publish TurboFan units immediately to reduce peak memory consumption.
788 323830 : if (result.requested_tier == ExecutionTier::kTurbofan) {
789 225540 : publish_results(&compile_scope);
790 : }
791 :
792 : // Get next unit.
793 323941 : if (deadline < platform->MonotonicallyIncreasingTime()) {
794 : unit = nullptr;
795 : } else {
796 323639 : unit = compile_scope.compilation_state()->GetNextCompilationUnit(
797 : task_id, baseline_only);
798 : }
799 :
800 323725 : if (unit == nullptr) {
801 179671 : publish_results(&compile_scope);
802 179676 : stop(compile_scope);
803 179649 : 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 7882 : token->Cancel();
811 7880 : 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 144887 : 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 144887 : 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 144887 : 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 144887 : 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 144889 : uint32_t start = module->num_imported_functions;
890 144889 : uint32_t end = start + module->num_declared_functions;
891 615359 : for (uint32_t func_index = start; func_index < end; func_index++) {
892 235236 : if (IsLazyCompilation(module, native_module,
893 : native_module->enabled_features(), func_index)) {
894 16 : native_module->UseLazyStub(func_index);
895 : } else {
896 235220 : builder.AddUnits(func_index);
897 : }
898 : }
899 144888 : builder.Commit();
900 144889 : }
901 :
902 : bool NeedsDeterministicCompile() {
903 1385718 : return FLAG_trace_wasm_decoder || FLAG_wasm_num_compilation_tasks <= 1;
904 : }
905 :
906 145385 : 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 145385 : if (FLAG_wasm_lazy_compilation ||
912 144082 : (FLAG_asm_wasm_lazy_compilation && wasm_module->origin == kAsmJsOrigin)) {
913 2641 : 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 2825 : if (thrower->error()) return;
924 : }
925 : native_module->set_lazy_compilation(true);
926 2633 : native_module->UseLazyStubs();
927 2633 : return;
928 : }
929 :
930 142744 : 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 285473 : 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 285474 : auto baseline_finished_semaphore = std::make_shared<base::Semaphore>(0);
948 : // The callback captures a shared ptr to the semaphore.
949 285472 : compilation_state->AddCallback(
950 285472 : [baseline_finished_semaphore](CompilationEvent event) {
951 555638 : if (event == CompilationEvent::kFinishedBaselineCompilation ||
952 277819 : event == CompilationEvent::kFailedCompilation) {
953 142737 : baseline_finished_semaphore->Signal();
954 : }
955 142737 : });
956 :
957 : // Initialize the compilation units and kick off background compile tasks.
958 142736 : 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 142737 : 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 142737 : if (!NeedsDeterministicCompile()) {
968 517766 : 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 142737 : baseline_finished_semaphore->Wait();
977 :
978 142737 : compilation_state->PublishDetectedFeatures(isolate);
979 :
980 142737 : 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 672439 : 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 448452 : task_id_(task_id) {}
998 :
999 224092 : void RunInternal() override {
1000 224092 : ExecuteCompilationUnits(token_, async_counters_.get(), task_id_,
1001 224092 : kBaselineOrTopTier);
1002 224094 : }
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 145341 : 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 145341 : TimedHistogramScope wasm_compile_module_time_scope(SELECT_WASM_COUNTER(
1018 145341 : isolate->counters(), wasm_module->origin, wasm_compile, module_time));
1019 :
1020 : // Embedder usage count for declared shared memories.
1021 145344 : if (wasm_module->has_shared_memory) {
1022 1482 : isolate->CountUsage(v8::Isolate::UseCounterFeature::kWasmSharedMemory);
1023 : }
1024 145346 : 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 145346 : OwnedVector<uint8_t>::Of(wire_bytes.module_bytes());
1030 :
1031 : // Create and compile the native module.
1032 : size_t code_size_estimate =
1033 145347 : 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 290694 : wasm::NativeModule::kCanAllocateMoreMemory, std::move(module));
1039 290696 : native_module->SetWireBytes(std::move(wire_bytes_copy));
1040 145348 : native_module->SetRuntimeStubs(isolate);
1041 :
1042 145348 : CompileNativeModule(isolate, thrower, wasm_module, native_module.get());
1043 145347 : if (thrower->error()) return {};
1044 :
1045 : // Compile JS->wasm wrappers for exported functions.
1046 : *export_wrappers_out = isolate->factory()->NewFixedArray(
1047 137826 : export_wrapper_size, AllocationType::kOld);
1048 : CompileJsToWasmWrappers(isolate, native_module->module(),
1049 137827 : *export_wrappers_out);
1050 :
1051 : // Log the code within the generated module for profiling.
1052 137827 : native_module->LogWasmCodes(isolate);
1053 :
1054 : return native_module;
1055 : }
1056 :
1057 38 : void CompileNativeModuleWithExplicitBoundsChecks(Isolate* isolate,
1058 : ErrorThrower* thrower,
1059 : const WasmModule* wasm_module,
1060 : NativeModule* native_module) {
1061 38 : native_module->DisableTrapHandler();
1062 38 : CompileNativeModule(isolate, thrower, wasm_module, native_module);
1063 38 : }
1064 :
1065 2647 : AsyncCompileJob::AsyncCompileJob(
1066 : Isolate* isolate, const WasmFeatures& enabled,
1067 : std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
1068 : std::shared_ptr<CompilationResultResolver> resolver)
1069 : : isolate_(isolate),
1070 : enabled_features_(enabled),
1071 : bytes_copy_(std::move(bytes_copy)),
1072 : wire_bytes_(bytes_copy_.get(), bytes_copy_.get() + length),
1073 10588 : resolver_(std::move(resolver)) {
1074 : v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
1075 2647 : v8::Platform* platform = V8::GetCurrentPlatform();
1076 5294 : foreground_task_runner_ = platform->GetForegroundTaskRunner(v8_isolate);
1077 : native_context_ =
1078 2647 : isolate->global_handles()->Create(context->native_context());
1079 : DCHECK(native_context_->IsNativeContext());
1080 2647 : }
1081 :
1082 2195 : void AsyncCompileJob::Start() {
1083 4390 : DoAsync<DecodeModule>(isolate_->counters()); // --
1084 2195 : }
1085 :
1086 72 : void AsyncCompileJob::Abort() {
1087 : // Removing this job will trigger the destructor, which will cancel all
1088 : // compilation.
1089 72 : isolate_->wasm_engine()->RemoveCompileJob(this);
1090 72 : }
1091 :
1092 1808 : class AsyncStreamingProcessor final : public StreamingProcessor {
1093 : public:
1094 : explicit AsyncStreamingProcessor(AsyncCompileJob* job);
1095 :
1096 : bool ProcessModuleHeader(Vector<const uint8_t> bytes,
1097 : uint32_t offset) override;
1098 :
1099 : bool ProcessSection(SectionCode section_code, Vector<const uint8_t> bytes,
1100 : uint32_t offset) override;
1101 :
1102 : bool ProcessCodeSectionHeader(int functions_count, uint32_t offset,
1103 : std::shared_ptr<WireBytesStorage>) override;
1104 :
1105 : bool ProcessFunctionBody(Vector<const uint8_t> bytes,
1106 : uint32_t offset) override;
1107 :
1108 : void OnFinishedChunk() override;
1109 :
1110 : void OnFinishedStream(OwnedVector<uint8_t> bytes) override;
1111 :
1112 : void OnError(const WasmError&) override;
1113 :
1114 : void OnAbort() override;
1115 :
1116 : bool Deserialize(Vector<const uint8_t> wire_bytes,
1117 : Vector<const uint8_t> module_bytes) override;
1118 :
1119 : private:
1120 : // Finishes the AsyncCompileJob with an error.
1121 : void FinishAsyncCompileJobWithError(const WasmError&);
1122 :
1123 : void CommitCompilationUnits();
1124 :
1125 : ModuleDecoder decoder_;
1126 : AsyncCompileJob* job_;
1127 : std::unique_ptr<CompilationUnitBuilder> compilation_unit_builder_;
1128 : int num_functions_ = 0;
1129 : };
1130 :
1131 452 : std::shared_ptr<StreamingDecoder> AsyncCompileJob::CreateStreamingDecoder() {
1132 : DCHECK_NULL(stream_);
1133 1356 : stream_.reset(
1134 1808 : new StreamingDecoder(base::make_unique<AsyncStreamingProcessor>(this)));
1135 452 : return stream_;
1136 : }
1137 :
1138 7941 : AsyncCompileJob::~AsyncCompileJob() {
1139 : // Note: This destructor always runs on the foreground thread of the isolate.
1140 2647 : background_task_manager_.CancelAndWait();
1141 : // If the runtime objects were not created yet, then initial compilation did
1142 : // not finish yet. In this case we can abort compilation.
1143 5087 : if (native_module_ && module_object_.is_null()) {
1144 173 : Impl(native_module_->compilation_state())->AbortCompilation();
1145 : }
1146 : // Tell the streaming decoder that the AsyncCompileJob is not available
1147 : // anymore.
1148 : // TODO(ahaas): Is this notification really necessary? Check
1149 : // https://crbug.com/888170.
1150 2647 : if (stream_) stream_->NotifyCompilationEnded();
1151 : CancelPendingForegroundTask();
1152 2647 : isolate_->global_handles()->Destroy(native_context_.location());
1153 2647 : if (!module_object_.is_null()) {
1154 2267 : isolate_->global_handles()->Destroy(module_object_.location());
1155 : }
1156 2647 : }
1157 :
1158 2436 : void AsyncCompileJob::CreateNativeModule(
1159 : std::shared_ptr<const WasmModule> module) {
1160 : // Embedder usage count for declared shared memories.
1161 2436 : if (module->has_shared_memory) {
1162 0 : isolate_->CountUsage(v8::Isolate::UseCounterFeature::kWasmSharedMemory);
1163 : }
1164 :
1165 : // TODO(wasm): Improve efficiency of storing module wire bytes. Only store
1166 : // relevant sections, not function bodies
1167 :
1168 : // Create the module object and populate with compiled functions and
1169 : // information needed at instantiation time.
1170 : // TODO(clemensh): For the same module (same bytes / same hash), we should
1171 : // only have one {WasmModuleObject}. Otherwise, we might only set
1172 : // breakpoints on a (potentially empty) subset of the instances.
1173 : // Create the module object.
1174 :
1175 : size_t code_size_estimate =
1176 2436 : wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module.get());
1177 7308 : native_module_ = isolate_->wasm_engine()->NewNativeModule(
1178 : isolate_, enabled_features_, code_size_estimate,
1179 : wasm::NativeModule::kCanAllocateMoreMemory, std::move(module));
1180 4872 : native_module_->SetWireBytes({std::move(bytes_copy_), wire_bytes_.length()});
1181 2436 : native_module_->SetRuntimeStubs(isolate_);
1182 :
1183 2436 : if (stream_) stream_->NotifyNativeModuleCreated(native_module_);
1184 2436 : }
1185 :
1186 2263 : void AsyncCompileJob::PrepareRuntimeObjects() {
1187 : // Create heap objects for script and module bytes to be stored in the
1188 : // module object. Asm.js is not compiled asynchronously.
1189 : const WasmModule* module = native_module_->module();
1190 : Handle<Script> script =
1191 2263 : CreateWasmScript(isolate_, wire_bytes_, module->source_map_url);
1192 :
1193 : size_t code_size_estimate =
1194 2263 : wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module);
1195 : Handle<WasmModuleObject> module_object = WasmModuleObject::New(
1196 4526 : isolate_, native_module_, script, code_size_estimate);
1197 :
1198 4526 : module_object_ = isolate_->global_handles()->Create(*module_object);
1199 2263 : }
1200 :
1201 : // This function assumes that it is executed in a HandleScope, and that a
1202 : // context is set on the isolate.
1203 2267 : void AsyncCompileJob::FinishCompile() {
1204 : bool is_after_deserialization = !module_object_.is_null();
1205 2267 : if (!is_after_deserialization) {
1206 2263 : PrepareRuntimeObjects();
1207 : }
1208 : DCHECK(!isolate_->context().is_null());
1209 : // Finish the wasm script now and make it public to the debugger.
1210 2267 : Handle<Script> script(module_object_->script(), isolate_);
1211 4534 : if (script->type() == Script::TYPE_WASM &&
1212 : module_object_->module()->source_map_url.size() != 0) {
1213 0 : MaybeHandle<String> src_map_str = isolate_->factory()->NewStringFromUtf8(
1214 : CStrVector(module_object_->module()->source_map_url.c_str()),
1215 0 : AllocationType::kOld);
1216 0 : script->set_source_mapping_url(*src_map_str.ToHandleChecked());
1217 : }
1218 2267 : isolate_->debug()->OnAfterCompile(script);
1219 :
1220 : // We can only update the feature counts once the entire compile is done.
1221 : auto compilation_state =
1222 : Impl(module_object_->native_module()->compilation_state());
1223 2267 : compilation_state->PublishDetectedFeatures(isolate_);
1224 :
1225 : // TODO(bbudge) Allow deserialization without wrapper compilation, so we can
1226 : // just compile wrappers here.
1227 2267 : if (!is_after_deserialization) {
1228 : // TODO(wasm): compiling wrappers should be made async.
1229 2263 : CompileWrappers();
1230 : }
1231 2267 : FinishModule();
1232 2267 : }
1233 :
1234 221 : void AsyncCompileJob::DecodeFailed(const WasmError& error) {
1235 442 : ErrorThrower thrower(isolate_, "WebAssembly.compile()");
1236 : thrower.CompileFailed(error);
1237 : // {job} keeps the {this} pointer alive.
1238 : std::shared_ptr<AsyncCompileJob> job =
1239 442 : isolate_->wasm_engine()->RemoveCompileJob(this);
1240 221 : resolver_->OnCompilationFailed(thrower.Reify());
1241 221 : }
1242 :
1243 76 : void AsyncCompileJob::AsyncCompileFailed() {
1244 152 : ErrorThrower thrower(isolate_, "WebAssembly.compile()");
1245 : ValidateSequentially(native_module_->module(), native_module_.get(),
1246 76 : isolate_->counters(), isolate_->allocator(), &thrower);
1247 : DCHECK(thrower.error());
1248 : // {job} keeps the {this} pointer alive.
1249 : std::shared_ptr<AsyncCompileJob> job =
1250 152 : isolate_->wasm_engine()->RemoveCompileJob(this);
1251 76 : resolver_->OnCompilationFailed(thrower.Reify());
1252 76 : }
1253 :
1254 0 : void AsyncCompileJob::AsyncCompileSucceeded(Handle<WasmModuleObject> result) {
1255 2267 : resolver_->OnCompilationSucceeded(result);
1256 0 : }
1257 :
1258 : class AsyncCompileJob::CompilationStateCallback {
1259 : public:
1260 : explicit CompilationStateCallback(AsyncCompileJob* job) : job_(job) {}
1261 :
1262 4554 : void operator()(CompilationEvent event) {
1263 : // This callback is only being called from a foreground task.
1264 4554 : switch (event) {
1265 : case CompilationEvent::kFinishedBaselineCompilation:
1266 : DCHECK(!last_event_.has_value());
1267 4478 : if (job_->DecrementAndCheckFinisherCount()) {
1268 2197 : job_->DoSync<CompileFinished>();
1269 : }
1270 : break;
1271 : case CompilationEvent::kFinishedTopTierCompilation:
1272 : DCHECK_EQ(CompilationEvent::kFinishedBaselineCompilation, last_event_);
1273 : // At this point, the job will already be gone, thus do not access it
1274 : // here.
1275 : break;
1276 : case CompilationEvent::kFailedCompilation: {
1277 : DCHECK(!last_event_.has_value());
1278 160 : if (job_->DecrementAndCheckFinisherCount()) {
1279 62 : job_->DoSync<CompileFailed>();
1280 : }
1281 : break;
1282 : }
1283 : default:
1284 0 : UNREACHABLE();
1285 : }
1286 : #ifdef DEBUG
1287 : last_event_ = event;
1288 : #endif
1289 4554 : }
1290 :
1291 : private:
1292 : AsyncCompileJob* job_;
1293 : #ifdef DEBUG
1294 : // This will be modified by different threads, but they externally
1295 : // synchronize, so no explicit synchronization (currently) needed here.
1296 : base::Optional<CompilationEvent> last_event_;
1297 : #endif
1298 : };
1299 :
1300 : // A closure to run a compilation step (either as foreground or background
1301 : // task) and schedule the next step(s), if any.
1302 7083 : class AsyncCompileJob::CompileStep {
1303 : public:
1304 7082 : virtual ~CompileStep() = default;
1305 :
1306 7082 : void Run(AsyncCompileJob* job, bool on_foreground) {
1307 7082 : if (on_foreground) {
1308 4888 : HandleScope scope(job->isolate_);
1309 4888 : SaveAndSwitchContext saved_context(job->isolate_, *job->native_context_);
1310 4888 : RunInForeground(job);
1311 : } else {
1312 2194 : RunInBackground(job);
1313 : }
1314 7082 : }
1315 :
1316 0 : virtual void RunInForeground(AsyncCompileJob*) { UNREACHABLE(); }
1317 0 : virtual void RunInBackground(AsyncCompileJob*) { UNREACHABLE(); }
1318 : };
1319 :
1320 : class AsyncCompileJob::CompileTask : public CancelableTask {
1321 : public:
1322 : CompileTask(AsyncCompileJob* job, bool on_foreground)
1323 : // We only manage the background tasks with the {CancelableTaskManager} of
1324 : // the {AsyncCompileJob}. Foreground tasks are managed by the system's
1325 : // {CancelableTaskManager}. Background tasks cannot spawn tasks managed by
1326 : // their own task manager.
1327 4889 : : CancelableTask(on_foreground ? job->isolate_->cancelable_task_manager()
1328 : : &job->background_task_manager_),
1329 : job_(job),
1330 9279 : on_foreground_(on_foreground) {}
1331 :
1332 21252 : ~CompileTask() override {
1333 7084 : if (job_ != nullptr && on_foreground_) ResetPendingForegroundTask();
1334 14168 : }
1335 :
1336 7082 : void RunInternal() final {
1337 7082 : if (!job_) return;
1338 7082 : if (on_foreground_) ResetPendingForegroundTask();
1339 14164 : job_->step_->Run(job_, on_foreground_);
1340 : // After execution, reset {job_} such that we don't try to reset the pending
1341 : // foreground task when the task is deleted.
1342 7082 : job_ = nullptr;
1343 : }
1344 :
1345 : void Cancel() {
1346 : DCHECK_NOT_NULL(job_);
1347 1 : job_ = nullptr;
1348 : }
1349 :
1350 : private:
1351 : // {job_} will be cleared to cancel a pending task.
1352 : AsyncCompileJob* job_;
1353 : bool on_foreground_;
1354 :
1355 : void ResetPendingForegroundTask() const {
1356 : DCHECK_EQ(this, job_->pending_foreground_task_);
1357 4888 : job_->pending_foreground_task_ = nullptr;
1358 : }
1359 : };
1360 :
1361 4633 : void AsyncCompileJob::StartForegroundTask() {
1362 : DCHECK_NULL(pending_foreground_task_);
1363 :
1364 4633 : auto new_task = base::make_unique<CompileTask>(this, true);
1365 4633 : pending_foreground_task_ = new_task.get();
1366 13899 : foreground_task_runner_->PostTask(std::move(new_task));
1367 4633 : }
1368 :
1369 256 : void AsyncCompileJob::ExecuteForegroundTaskImmediately() {
1370 : DCHECK_NULL(pending_foreground_task_);
1371 :
1372 256 : auto new_task = base::make_unique<CompileTask>(this, true);
1373 256 : pending_foreground_task_ = new_task.get();
1374 256 : new_task->Run();
1375 256 : }
1376 :
1377 0 : void AsyncCompileJob::CancelPendingForegroundTask() {
1378 2647 : if (!pending_foreground_task_) return;
1379 : pending_foreground_task_->Cancel();
1380 1 : pending_foreground_task_ = nullptr;
1381 : }
1382 :
1383 2195 : void AsyncCompileJob::StartBackgroundTask() {
1384 2195 : auto task = base::make_unique<CompileTask>(this, false);
1385 :
1386 : // If --wasm-num-compilation-tasks=0 is passed, do only spawn foreground
1387 : // tasks. This is used to make timing deterministic.
1388 2195 : if (FLAG_wasm_num_compilation_tasks > 0) {
1389 6585 : V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
1390 : } else {
1391 0 : foreground_task_runner_->PostTask(std::move(task));
1392 : }
1393 2195 : }
1394 :
1395 : template <typename Step,
1396 : AsyncCompileJob::UseExistingForegroundTask use_existing_fg_task,
1397 : typename... Args>
1398 64 : void AsyncCompileJob::DoSync(Args&&... args) {
1399 4631 : NextStep<Step>(std::forward<Args>(args)...);
1400 64 : if (use_existing_fg_task && pending_foreground_task_ != nullptr) return;
1401 4633 : StartForegroundTask();
1402 : }
1403 :
1404 : template <typename Step, typename... Args>
1405 : void AsyncCompileJob::DoImmediately(Args&&... args) {
1406 256 : NextStep<Step>(std::forward<Args>(args)...);
1407 256 : ExecuteForegroundTaskImmediately();
1408 : }
1409 :
1410 : template <typename Step, typename... Args>
1411 : void AsyncCompileJob::DoAsync(Args&&... args) {
1412 2195 : NextStep<Step>(std::forward<Args>(args)...);
1413 2195 : StartBackgroundTask();
1414 : }
1415 :
1416 : template <typename Step, typename... Args>
1417 7082 : void AsyncCompileJob::NextStep(Args&&... args) {
1418 7559 : step_.reset(new Step(std::forward<Args>(args)...));
1419 7084 : }
1420 :
1421 : //==========================================================================
1422 : // Step 1: (async) Decode the module.
1423 : //==========================================================================
1424 4386 : class AsyncCompileJob::DecodeModule : public AsyncCompileJob::CompileStep {
1425 : public:
1426 2195 : explicit DecodeModule(Counters* counters) : counters_(counters) {}
1427 :
1428 2194 : void RunInBackground(AsyncCompileJob* job) override {
1429 2194 : ModuleResult result;
1430 : {
1431 : DisallowHandleAllocation no_handle;
1432 : DisallowHeapAllocation no_allocation;
1433 : // Decode the module bytes.
1434 : TRACE_COMPILE("(1) Decoding module...\n");
1435 6580 : TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
1436 : "AsyncCompileJob::DecodeModule");
1437 4386 : result = DecodeWasmModule(
1438 : job->enabled_features_, job->wire_bytes_.start(),
1439 2194 : job->wire_bytes_.end(), false, kWasmOrigin, counters_,
1440 2192 : job->isolate()->wasm_engine()->allocator());
1441 :
1442 : // Validate lazy functions here.
1443 2192 : auto enabled_features = job->enabled_features_;
1444 2192 : if (enabled_features.compilation_hints && result.ok()) {
1445 : const WasmModule* module = result.value().get();
1446 : auto allocator = job->isolate()->wasm_engine()->allocator();
1447 24 : int start = module->num_imported_functions;
1448 24 : int end = start + module->num_declared_functions;
1449 :
1450 40 : for (int func_index = start; func_index < end; func_index++) {
1451 16 : const WasmFunction* func = &module->functions[func_index];
1452 16 : Vector<const uint8_t> code = job->wire_bytes_.GetFunctionBytes(func);
1453 :
1454 32 : if (IsLazyCompilation(module, enabled_features, func_index)) {
1455 : DecodeResult function_result =
1456 32 : ValidateSingleFunction(module, func_index, code, counters_,
1457 : allocator, enabled_features);
1458 16 : if (function_result.failed()) {
1459 16 : result = ModuleResult(function_result.error());
1460 : break;
1461 : }
1462 : }
1463 : }
1464 : }
1465 : }
1466 2192 : if (result.failed()) {
1467 : // Decoding failure; reject the promise and clean up.
1468 : job->DoSync<DecodeFail>(std::move(result).error());
1469 : } else {
1470 : // Decode passed.
1471 4304 : job->DoSync<PrepareAndStartCompile>(std::move(result).value(), true);
1472 : }
1473 2194 : }
1474 :
1475 : private:
1476 : Counters* const counters_;
1477 : };
1478 :
1479 : //==========================================================================
1480 : // Step 1b: (sync) Fail decoding the module.
1481 : //==========================================================================
1482 442 : class AsyncCompileJob::DecodeFail : public CompileStep {
1483 : public:
1484 221 : explicit DecodeFail(WasmError error) : error_(std::move(error)) {}
1485 :
1486 : private:
1487 : WasmError error_;
1488 :
1489 221 : void RunInForeground(AsyncCompileJob* job) override {
1490 : TRACE_COMPILE("(1b) Decoding failed.\n");
1491 : // {job_} is deleted in DecodeFailed, therefore the {return}.
1492 221 : return job->DecodeFailed(error_);
1493 : }
1494 : };
1495 :
1496 : //==========================================================================
1497 : // Step 2 (sync): Create heap-allocated data and start compile.
1498 : //==========================================================================
1499 4818 : class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
1500 : public:
1501 : PrepareAndStartCompile(std::shared_ptr<const WasmModule> module,
1502 : bool start_compilation)
1503 4816 : : module_(std::move(module)), start_compilation_(start_compilation) {}
1504 :
1505 : private:
1506 : std::shared_ptr<const WasmModule> module_;
1507 : bool start_compilation_;
1508 :
1509 2408 : void RunInForeground(AsyncCompileJob* job) override {
1510 : TRACE_COMPILE("(2) Prepare and start compile...\n");
1511 :
1512 : // Make sure all compilation tasks stopped running. Decoding (async step)
1513 : // is done.
1514 2408 : job->background_task_manager_.CancelAndWait();
1515 :
1516 4816 : job->CreateNativeModule(module_);
1517 :
1518 : CompilationStateImpl* compilation_state =
1519 : Impl(job->native_module_->compilation_state());
1520 4816 : compilation_state->AddCallback(CompilationStateCallback{job});
1521 2408 : if (start_compilation_) {
1522 : // TODO(ahaas): Try to remove the {start_compilation_} check when
1523 : // streaming decoding is done in the background. If
1524 : // InitializeCompilationUnits always returns 0 for streaming compilation,
1525 : // then DoAsync would do the same as NextStep already.
1526 :
1527 : // Add compilation units and kick off compilation.
1528 2152 : InitializeCompilationUnits(job->native_module_.get());
1529 : }
1530 2408 : }
1531 : };
1532 :
1533 : //==========================================================================
1534 : // Step 3a (sync): Compilation failed.
1535 : //==========================================================================
1536 186 : class AsyncCompileJob::CompileFailed : public CompileStep {
1537 : private:
1538 62 : void RunInForeground(AsyncCompileJob* job) override {
1539 : TRACE_COMPILE("(3a) Compilation failed\n");
1540 : DCHECK(job->native_module_->compilation_state()->failed());
1541 :
1542 : // {job_} is deleted in AsyncCompileFailed, therefore the {return}.
1543 62 : return job->AsyncCompileFailed();
1544 : }
1545 : };
1546 :
1547 : namespace {
1548 2197 : class SampleTopTierCodeSizeCallback {
1549 : public:
1550 : explicit SampleTopTierCodeSizeCallback(
1551 : std::weak_ptr<NativeModule> native_module)
1552 : : native_module_(std::move(native_module)) {}
1553 :
1554 163 : void operator()(CompilationEvent event) {
1555 : // This callback is registered after baseline compilation finished, so the
1556 : // only possible event to follow is {kFinishedTopTierCompilation}.
1557 : DCHECK_EQ(CompilationEvent::kFinishedTopTierCompilation, event);
1558 163 : if (std::shared_ptr<NativeModule> native_module = native_module_.lock()) {
1559 : native_module->engine()->SampleTopTierCodeSizeInAllIsolates(
1560 163 : native_module);
1561 : }
1562 163 : }
1563 :
1564 : private:
1565 : std::weak_ptr<NativeModule> native_module_;
1566 : };
1567 : } // namespace
1568 :
1569 : //==========================================================================
1570 : // Step 3b (sync): Compilation finished.
1571 : //==========================================================================
1572 6591 : class AsyncCompileJob::CompileFinished : public CompileStep {
1573 : private:
1574 2197 : void RunInForeground(AsyncCompileJob* job) override {
1575 : TRACE_COMPILE("(3b) Compilation finished\n");
1576 : DCHECK(!job->native_module_->compilation_state()->failed());
1577 : // Sample the generated code size when baseline compilation finished.
1578 2197 : job->native_module_->SampleCodeSize(job->isolate_->counters(),
1579 2197 : NativeModule::kAfterBaseline);
1580 : // Also, set a callback to sample the code size after top-tier compilation
1581 : // finished. This callback will *not* keep the NativeModule alive.
1582 4394 : job->native_module_->compilation_state()->AddCallback(
1583 2197 : SampleTopTierCodeSizeCallback{job->native_module_});
1584 : // Then finalize and publish the generated module.
1585 2197 : job->FinishCompile();
1586 2197 : }
1587 : };
1588 :
1589 2263 : void AsyncCompileJob::CompileWrappers() {
1590 : // TODO(wasm): Compile all wrappers here, including the start function wrapper
1591 : // and the wrappers for the function table elements.
1592 : TRACE_COMPILE("(5) Compile wrappers...\n");
1593 : // Compile JS->wasm wrappers for exported functions.
1594 2263 : CompileJsToWasmWrappers(isolate_, module_object_->native_module()->module(),
1595 4526 : handle(module_object_->export_wrappers(), isolate_));
1596 2263 : }
1597 :
1598 2267 : void AsyncCompileJob::FinishModule() {
1599 : TRACE_COMPILE("(6) Finish module...\n");
1600 : AsyncCompileSucceeded(module_object_);
1601 2267 : isolate_->wasm_engine()->RemoveCompileJob(this);
1602 2267 : }
1603 :
1604 0 : AsyncStreamingProcessor::AsyncStreamingProcessor(AsyncCompileJob* job)
1605 : : decoder_(job->enabled_features_),
1606 : job_(job),
1607 904 : compilation_unit_builder_(nullptr) {}
1608 :
1609 180 : void AsyncStreamingProcessor::FinishAsyncCompileJobWithError(
1610 : const WasmError& error) {
1611 : DCHECK(error.has_error());
1612 : // Make sure all background tasks stopped executing before we change the state
1613 : // of the AsyncCompileJob to DecodeFail.
1614 180 : job_->background_task_manager_.CancelAndWait();
1615 :
1616 : // Check if there is already a CompiledModule, in which case we have to clean
1617 : // up the CompilationStateImpl as well.
1618 180 : if (job_->native_module_) {
1619 64 : Impl(job_->native_module_->compilation_state())->AbortCompilation();
1620 :
1621 64 : job_->DoSync<AsyncCompileJob::DecodeFail,
1622 64 : AsyncCompileJob::kUseExistingForegroundTask>(error);
1623 :
1624 : // Clear the {compilation_unit_builder_} if it exists. This is needed
1625 : // because there is a check in the destructor of the
1626 : // {CompilationUnitBuilder} that it is empty.
1627 64 : if (compilation_unit_builder_) compilation_unit_builder_->Clear();
1628 : } else {
1629 : job_->DoSync<AsyncCompileJob::DecodeFail>(error);
1630 : }
1631 180 : }
1632 :
1633 : // Process the module header.
1634 408 : bool AsyncStreamingProcessor::ProcessModuleHeader(Vector<const uint8_t> bytes,
1635 : uint32_t offset) {
1636 : TRACE_STREAMING("Process module header...\n");
1637 408 : decoder_.StartDecoding(job_->isolate()->counters(),
1638 816 : job_->isolate()->wasm_engine()->allocator());
1639 408 : decoder_.DecodeModuleHeader(bytes, offset);
1640 408 : if (!decoder_.ok()) {
1641 16 : FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());
1642 16 : return false;
1643 : }
1644 : return true;
1645 : }
1646 :
1647 : // Process all sections except for the code section.
1648 1060 : bool AsyncStreamingProcessor::ProcessSection(SectionCode section_code,
1649 : Vector<const uint8_t> bytes,
1650 : uint32_t offset) {
1651 : TRACE_STREAMING("Process section %d ...\n", section_code);
1652 1060 : if (compilation_unit_builder_) {
1653 : // We reached a section after the code section, we do not need the
1654 : // compilation_unit_builder_ anymore.
1655 : CommitCompilationUnits();
1656 : compilation_unit_builder_.reset();
1657 : }
1658 1060 : if (section_code == SectionCode::kUnknownSectionCode) {
1659 : Decoder decoder(bytes, offset);
1660 : section_code = ModuleDecoder::IdentifyUnknownSection(
1661 136 : decoder, bytes.start() + bytes.length());
1662 136 : if (section_code == SectionCode::kUnknownSectionCode) {
1663 : // Skip unknown sections that we do not know how to handle.
1664 : return true;
1665 : }
1666 : // Remove the unknown section tag from the payload bytes.
1667 136 : offset += decoder.position();
1668 136 : bytes = bytes.SubVector(decoder.position(), bytes.size());
1669 : }
1670 : constexpr bool verify_functions = false;
1671 1060 : decoder_.DecodeSection(section_code, bytes, offset, verify_functions);
1672 1060 : if (!decoder_.ok()) {
1673 28 : FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());
1674 28 : return false;
1675 : }
1676 : return true;
1677 : }
1678 :
1679 : // Start the code section.
1680 268 : bool AsyncStreamingProcessor::ProcessCodeSectionHeader(
1681 : int functions_count, uint32_t offset,
1682 : std::shared_ptr<WireBytesStorage> wire_bytes_storage) {
1683 : TRACE_STREAMING("Start the code section with %d functions...\n",
1684 : functions_count);
1685 268 : if (!decoder_.CheckFunctionsCount(static_cast<uint32_t>(functions_count),
1686 : offset)) {
1687 12 : FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());
1688 12 : return false;
1689 : }
1690 : // Execute the PrepareAndStartCompile step immediately and not in a separate
1691 : // task.
1692 768 : job_->DoImmediately<AsyncCompileJob::PrepareAndStartCompile>(
1693 256 : decoder_.shared_module(), false);
1694 256 : auto* compilation_state = Impl(job_->native_module_->compilation_state());
1695 512 : compilation_state->SetWireBytesStorage(std::move(wire_bytes_storage));
1696 :
1697 : // Set number of functions that must be compiled to consider the module fully
1698 : // compiled.
1699 256 : auto wasm_module = job_->native_module_->module();
1700 256 : int num_functions = wasm_module->num_declared_functions;
1701 : DCHECK_IMPLIES(!job_->native_module_->enabled_features().compilation_hints,
1702 : wasm_module->num_lazy_compilation_hints == 0);
1703 256 : int num_lazy_functions = wasm_module->num_lazy_compilation_hints;
1704 : compilation_state->SetNumberOfFunctionsToCompile(num_functions,
1705 256 : num_lazy_functions);
1706 :
1707 : // Set outstanding_finishers_ to 2, because both the AsyncCompileJob and the
1708 : // AsyncStreamingProcessor have to finish.
1709 256 : job_->outstanding_finishers_.store(2);
1710 256 : compilation_unit_builder_.reset(
1711 256 : new CompilationUnitBuilder(job_->native_module_.get()));
1712 : return true;
1713 : }
1714 :
1715 : // Process a function body.
1716 508 : bool AsyncStreamingProcessor::ProcessFunctionBody(Vector<const uint8_t> bytes,
1717 : uint32_t offset) {
1718 : TRACE_STREAMING("Process function body %d ...\n", num_functions_);
1719 :
1720 1016 : decoder_.DecodeFunctionBody(
1721 1016 : num_functions_, static_cast<uint32_t>(bytes.length()), offset, false);
1722 :
1723 508 : NativeModule* native_module = job_->native_module_.get();
1724 : const WasmModule* module = native_module->module();
1725 508 : auto enabled_features = native_module->enabled_features();
1726 : uint32_t func_index =
1727 1016 : num_functions_ + decoder_.module()->num_imported_functions;
1728 :
1729 508 : if (IsLazyCompilation(module, native_module, enabled_features, func_index)) {
1730 : Counters* counters = Impl(native_module->compilation_state())->counters();
1731 : AccountingAllocator* allocator = native_module->engine()->allocator();
1732 :
1733 : // The native module does not own the wire bytes until {SetWireBytes} is
1734 : // called in {OnFinishedStream}. Validation must use {bytes} parameter.
1735 48 : DecodeResult result = ValidateSingleFunction(
1736 : module, func_index, bytes, counters, allocator, enabled_features);
1737 :
1738 24 : if (result.failed()) {
1739 8 : FinishAsyncCompileJobWithError(result.error());
1740 : return false;
1741 : }
1742 :
1743 16 : native_module->UseLazyStub(func_index);
1744 : } else {
1745 484 : compilation_unit_builder_->AddUnits(func_index);
1746 : }
1747 :
1748 500 : ++num_functions_;
1749 :
1750 500 : return true;
1751 : }
1752 :
1753 0 : void AsyncStreamingProcessor::CommitCompilationUnits() {
1754 : DCHECK(compilation_unit_builder_);
1755 324 : compilation_unit_builder_->Commit();
1756 0 : }
1757 :
1758 396 : void AsyncStreamingProcessor::OnFinishedChunk() {
1759 : TRACE_STREAMING("FinishChunk...\n");
1760 396 : if (compilation_unit_builder_) CommitCompilationUnits();
1761 396 : }
1762 :
1763 : // Finish the processing of the stream.
1764 192 : void AsyncStreamingProcessor::OnFinishedStream(OwnedVector<uint8_t> bytes) {
1765 : TRACE_STREAMING("Finish stream...\n");
1766 572 : ModuleResult result = decoder_.FinishDecoding(false);
1767 192 : if (result.failed()) {
1768 4 : FinishAsyncCompileJobWithError(result.error());
1769 4 : return;
1770 : }
1771 : // We have to open a HandleScope and prepare the Context for
1772 : // CreateNativeModule, PrepareRuntimeObjects and FinishCompile as this is a
1773 : // callback from the embedder.
1774 188 : HandleScope scope(job_->isolate_);
1775 376 : SaveAndSwitchContext saved_context(job_->isolate_, *job_->native_context_);
1776 :
1777 188 : bool needs_finish = job_->DecrementAndCheckFinisherCount();
1778 188 : if (job_->native_module_ == nullptr) {
1779 : // We are processing a WebAssembly module without code section. Create the
1780 : // runtime objects now (would otherwise happen in {PrepareAndStartCompile}).
1781 56 : job_->CreateNativeModule(std::move(result).value());
1782 : DCHECK(needs_finish);
1783 : }
1784 376 : job_->wire_bytes_ = ModuleWireBytes(bytes.as_vector());
1785 376 : job_->native_module_->SetWireBytes(std::move(bytes));
1786 188 : if (needs_finish) {
1787 160 : if (job_->native_module_->compilation_state()->failed()) {
1788 14 : job_->AsyncCompileFailed();
1789 : } else {
1790 66 : job_->FinishCompile();
1791 : }
1792 : }
1793 : }
1794 :
1795 : // Report an error detected in the StreamingDecoder.
1796 112 : void AsyncStreamingProcessor::OnError(const WasmError& error) {
1797 : TRACE_STREAMING("Stream error...\n");
1798 112 : FinishAsyncCompileJobWithError(error);
1799 112 : }
1800 :
1801 72 : void AsyncStreamingProcessor::OnAbort() {
1802 : TRACE_STREAMING("Abort stream...\n");
1803 72 : job_->Abort();
1804 72 : }
1805 :
1806 8 : bool AsyncStreamingProcessor::Deserialize(Vector<const uint8_t> module_bytes,
1807 : Vector<const uint8_t> wire_bytes) {
1808 : // DeserializeNativeModule and FinishCompile assume that they are executed in
1809 : // a HandleScope, and that a context is set on the isolate.
1810 8 : HandleScope scope(job_->isolate_);
1811 16 : SaveAndSwitchContext saved_context(job_->isolate_, *job_->native_context_);
1812 :
1813 : MaybeHandle<WasmModuleObject> result =
1814 8 : DeserializeNativeModule(job_->isolate_, module_bytes, wire_bytes);
1815 8 : if (result.is_null()) return false;
1816 :
1817 : job_->module_object_ =
1818 8 : job_->isolate_->global_handles()->Create(*result.ToHandleChecked());
1819 8 : job_->native_module_ = job_->module_object_->shared_native_module();
1820 4 : auto owned_wire_bytes = OwnedVector<uint8_t>::Of(wire_bytes);
1821 8 : job_->wire_bytes_ = ModuleWireBytes(owned_wire_bytes.as_vector());
1822 8 : job_->native_module_->SetWireBytes(std::move(owned_wire_bytes));
1823 4 : job_->FinishCompile();
1824 : return true;
1825 : }
1826 :
1827 : namespace {
1828 1242981 : int GetMaxBackgroundTasks() {
1829 1242981 : if (NeedsDeterministicCompile()) return 1;
1830 1242973 : int num_worker_threads = V8::GetCurrentPlatform()->NumberOfWorkerThreads();
1831 : int num_compile_tasks =
1832 1242973 : std::min(FLAG_wasm_num_compilation_tasks, num_worker_threads);
1833 2485946 : return std::max(1, num_compile_tasks);
1834 : }
1835 : } // namespace
1836 :
1837 1242981 : CompilationStateImpl::CompilationStateImpl(
1838 : const std::shared_ptr<NativeModule>& native_module,
1839 : std::shared_ptr<Counters> async_counters)
1840 : : native_module_(native_module.get()),
1841 : background_compile_token_(
1842 : std::make_shared<BackgroundCompileToken>(native_module)),
1843 877465 : compile_mode_(FLAG_wasm_tier_up &&
1844 877465 : native_module->module()->origin == kWasmOrigin
1845 : ? CompileMode::kTiering
1846 : : CompileMode::kRegular),
1847 : async_counters_(std::move(async_counters)),
1848 1242981 : max_background_tasks_(GetMaxBackgroundTasks()),
1849 : compilation_unit_queues_(max_background_tasks_),
1850 7457886 : available_task_ids_(max_background_tasks_) {
1851 18644619 : for (int i = 0; i < max_background_tasks_; ++i) {
1852 : // Ids are popped on task creation, so reverse this list. This ensures that
1853 : // the first background task gets id 0.
1854 17401638 : available_task_ids_[i] = max_background_tasks_ - 1 - i;
1855 : }
1856 1242981 : }
1857 :
1858 1243218 : void CompilationStateImpl::AbortCompilation() {
1859 1243218 : background_compile_token_->Cancel();
1860 : // No more callbacks after abort.
1861 1243218 : base::MutexGuard callbacks_guard(&callbacks_mutex_);
1862 : callbacks_.clear();
1863 1243218 : }
1864 :
1865 145141 : void CompilationStateImpl::SetNumberOfFunctionsToCompile(
1866 : int num_functions, int num_lazy_functions) {
1867 : DCHECK(!failed());
1868 145141 : base::MutexGuard guard(&callbacks_mutex_);
1869 :
1870 145145 : int num_functions_to_compile = num_functions - num_lazy_functions;
1871 145145 : outstanding_baseline_functions_ = num_functions_to_compile;
1872 145145 : outstanding_top_tier_functions_ = num_functions_to_compile;
1873 290290 : highest_execution_tier_.assign(num_functions, ExecutionTier::kNone);
1874 :
1875 : // Degenerate case of an empty module. Trigger callbacks immediately.
1876 145145 : if (num_functions_to_compile == 0) {
1877 12592 : for (auto& callback : callbacks_) {
1878 : callback(CompilationEvent::kFinishedBaselineCompilation);
1879 : }
1880 12592 : for (auto& callback : callbacks_) {
1881 : callback(CompilationEvent::kFinishedTopTierCompilation);
1882 : }
1883 : // Clear the callbacks because no more events will be delivered.
1884 : callbacks_.clear();
1885 : }
1886 145144 : }
1887 :
1888 147343 : void CompilationStateImpl::AddCallback(CompilationState::callback_t callback) {
1889 147343 : base::MutexGuard callbacks_guard(&callbacks_mutex_);
1890 147345 : callbacks_.emplace_back(std::move(callback));
1891 147346 : }
1892 :
1893 : void CompilationStateImpl::AddCompilationUnits(
1894 : Vector<std::unique_ptr<WasmCompilationUnit>> baseline_units,
1895 : Vector<std::unique_ptr<WasmCompilationUnit>> top_tier_units) {
1896 138917 : compilation_unit_queues_.AddUnits(baseline_units, top_tier_units);
1897 :
1898 138919 : RestartBackgroundTasks();
1899 : }
1900 :
1901 66 : void CompilationStateImpl::AddTopTierCompilationUnit(
1902 : std::unique_ptr<WasmCompilationUnit> unit) {
1903 : AddCompilationUnits({}, {&unit, 1});
1904 66 : }
1905 :
1906 : std::unique_ptr<WasmCompilationUnit>
1907 : CompilationStateImpl::GetNextCompilationUnit(
1908 : int task_id, CompileBaselineOnly baseline_only) {
1909 794714 : return compilation_unit_queues_.GetNextUnit(task_id, baseline_only);
1910 : }
1911 :
1912 : void CompilationStateImpl::OnFinishedUnit(WasmCode* code) {
1913 0 : OnFinishedUnits({&code, 1});
1914 : }
1915 :
1916 268695 : void CompilationStateImpl::OnFinishedUnits(Vector<WasmCode*> code_vector) {
1917 268695 : base::MutexGuard guard(&callbacks_mutex_);
1918 :
1919 : // Assume an order of execution tiers that represents the quality of their
1920 : // generated code.
1921 : static_assert(ExecutionTier::kNone < ExecutionTier::kInterpreter &&
1922 : ExecutionTier::kInterpreter < ExecutionTier::kLiftoff &&
1923 : ExecutionTier::kLiftoff < ExecutionTier::kTurbofan,
1924 : "Assume an order on execution tiers");
1925 :
1926 268843 : auto module = native_module_->module();
1927 268843 : auto enabled_features = native_module_->enabled_features();
1928 916175 : for (WasmCode* code : code_vector) {
1929 : DCHECK_NOT_NULL(code);
1930 : DCHECK_NE(code->tier(), ExecutionTier::kNone);
1931 323654 : native_module_->engine()->LogCode(code);
1932 :
1933 : // Skip lazily compiled code as we do not consider this for the completion
1934 : // of baseline respectively top tier compilation.
1935 : int func_index = code->index();
1936 647348 : if (IsLazyCompilation(module, native_module_, enabled_features,
1937 : func_index)) {
1938 47 : continue;
1939 : }
1940 :
1941 : // Determine whether we are reaching baseline or top tier with the given
1942 : // code.
1943 323627 : uint32_t slot_index = code->index() - module->num_imported_functions;
1944 : ExecutionTierPair requested_tiers = GetRequestedExecutionTiers(
1945 323627 : module, compile_mode(), enabled_features, func_index);
1946 : DCHECK_EQ(highest_execution_tier_.size(), module->num_declared_functions);
1947 647252 : ExecutionTier prior_tier = highest_execution_tier_[slot_index];
1948 323626 : bool had_reached_baseline = prior_tier >= requested_tiers.baseline_tier;
1949 : bool had_reached_top_tier = prior_tier >= requested_tiers.top_tier;
1950 : DCHECK_IMPLIES(had_reached_baseline, prior_tier > ExecutionTier::kNone);
1951 : bool reaches_baseline = !had_reached_baseline;
1952 : bool reaches_top_tier =
1953 323626 : !had_reached_top_tier && code->tier() >= requested_tiers.top_tier;
1954 : DCHECK_IMPLIES(reaches_baseline,
1955 : code->tier() >= requested_tiers.baseline_tier);
1956 : DCHECK_IMPLIES(reaches_top_tier, had_reached_baseline || reaches_baseline);
1957 :
1958 : // Remember compilation state before update.
1959 : bool had_completed_baseline_compilation =
1960 323626 : outstanding_baseline_functions_ == 0;
1961 : bool had_completed_top_tier_compilation =
1962 323626 : outstanding_top_tier_functions_ == 0;
1963 :
1964 : // Update compilation state.
1965 323626 : if (code->tier() > prior_tier) {
1966 304016 : highest_execution_tier_[slot_index] = code->tier();
1967 : }
1968 323626 : if (reaches_baseline) outstanding_baseline_functions_--;
1969 323626 : if (reaches_top_tier) outstanding_top_tier_functions_--;
1970 : DCHECK_LE(0, outstanding_baseline_functions_);
1971 : DCHECK_LE(outstanding_baseline_functions_, outstanding_top_tier_functions_);
1972 :
1973 : // Conclude if we are completing baseline or top tier compilation.
1974 562464 : bool completes_baseline_compilation = !had_completed_baseline_compilation &&
1975 238838 : outstanding_baseline_functions_ == 0;
1976 634581 : bool completes_top_tier_compilation = !had_completed_top_tier_compilation &&
1977 310955 : outstanding_top_tier_functions_ == 0;
1978 : DCHECK_IMPLIES(
1979 : completes_top_tier_compilation,
1980 : had_completed_baseline_compilation || completes_baseline_compilation);
1981 :
1982 : // Trigger callbacks.
1983 323626 : if (completes_baseline_compilation) {
1984 262354 : for (auto& callback : callbacks_) {
1985 : callback(CompilationEvent::kFinishedBaselineCompilation);
1986 : }
1987 : }
1988 323626 : if (completes_top_tier_compilation) {
1989 262210 : for (auto& callback : callbacks_) {
1990 : callback(CompilationEvent::kFinishedTopTierCompilation);
1991 : }
1992 : // Clear the callbacks because no more events will be delivered.
1993 : callbacks_.clear();
1994 : }
1995 : }
1996 268858 : }
1997 :
1998 218621 : void CompilationStateImpl::OnBackgroundTaskStopped(
1999 : int task_id, const WasmFeatures& detected) {
2000 : {
2001 218621 : base::MutexGuard guard(&mutex_);
2002 : DCHECK_EQ(0, std::count(available_task_ids_.begin(),
2003 : available_task_ids_.end(), task_id));
2004 : DCHECK_GT(max_background_tasks_, available_task_ids_.size());
2005 218680 : available_task_ids_.push_back(task_id);
2006 218672 : UnionFeaturesInto(&detected_features_, detected);
2007 : }
2008 :
2009 : // The background task could have stopped while we were adding new units, or
2010 : // because it reached its deadline. In both cases we need to restart tasks to
2011 : // avoid a potential deadlock.
2012 218676 : RestartBackgroundTasks();
2013 218649 : }
2014 :
2015 250841 : void CompilationStateImpl::UpdateDetectedFeatures(
2016 : const WasmFeatures& detected) {
2017 250841 : base::MutexGuard guard(&mutex_);
2018 250841 : UnionFeaturesInto(&detected_features_, detected);
2019 250842 : }
2020 :
2021 145004 : void CompilationStateImpl::PublishDetectedFeatures(Isolate* isolate) {
2022 : // Notifying the isolate of the feature counts must take place under
2023 : // the mutex, because even if we have finished baseline compilation,
2024 : // tiering compilations may still occur in the background.
2025 145004 : base::MutexGuard guard(&mutex_);
2026 : UpdateFeatureUseCounts(isolate, detected_features_);
2027 145004 : }
2028 :
2029 357550 : void CompilationStateImpl::RestartBackgroundTasks() {
2030 : // Create new tasks, but only spawn them after releasing the mutex, because
2031 : // some platforms (e.g. the predictable platform) might execute tasks right
2032 : // away.
2033 353759 : std::vector<std::unique_ptr<Task>> new_tasks;
2034 : {
2035 357550 : base::MutexGuard guard(&mutex_);
2036 : // Explicit fast path (quite common): If no more task ids are available
2037 : // (i.e. {max_background_tasks_} tasks are already running), spawn nothing.
2038 357592 : if (available_task_ids_.empty()) return;
2039 : // No need to restart tasks if compilation already failed.
2040 357589 : if (failed()) return;
2041 :
2042 : size_t max_num_restart = compilation_unit_queues_.GetTotalSize();
2043 :
2044 802191 : while (!available_task_ids_.empty() && max_num_restart-- > 0) {
2045 224224 : int task_id = available_task_ids_.back();
2046 : available_task_ids_.pop_back();
2047 : new_tasks.emplace_back(
2048 224224 : native_module_->engine()
2049 448449 : ->NewBackgroundCompileTask<BackgroundCompileTask>(
2050 224226 : background_compile_token_, async_counters_, task_id));
2051 : }
2052 : }
2053 :
2054 353759 : if (baseline_compilation_finished()) {
2055 152539 : for (auto& task : new_tasks) {
2056 28894 : V8::GetCurrentPlatform()->CallLowPriorityTaskOnWorkerThread(
2057 28894 : std::move(task));
2058 : }
2059 : } else {
2060 425446 : for (auto& task : new_tasks) {
2061 629337 : V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
2062 : }
2063 : }
2064 : }
2065 :
2066 7882 : void CompilationStateImpl::SetError() {
2067 7882 : bool expected = false;
2068 7882 : if (!compile_failed_.compare_exchange_strong(expected, true,
2069 : std::memory_order_relaxed)) {
2070 297 : return; // Already failed before.
2071 : }
2072 :
2073 7585 : base::MutexGuard callbacks_guard(&callbacks_mutex_);
2074 15170 : for (auto& callback : callbacks_) {
2075 : callback(CompilationEvent::kFailedCompilation);
2076 : }
2077 : // No more callbacks after an error.
2078 : callbacks_.clear();
2079 : }
2080 :
2081 140434 : void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module,
2082 : Handle<FixedArray> export_wrappers) {
2083 : JSToWasmWrapperCache js_to_wasm_cache;
2084 : int wrapper_index = 0;
2085 :
2086 : // TODO(6792): Wrappers below are allocated with {Factory::NewCode}. As an
2087 : // optimization we keep the code space unlocked to avoid repeated unlocking
2088 : // because many such wrapper are allocated in sequence below.
2089 280868 : CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
2090 364435 : for (auto exp : module->export_table) {
2091 224001 : if (exp.kind != kExternalFunction) continue;
2092 221124 : auto& function = module->functions[exp.index];
2093 : Handle<Code> wrapper_code = js_to_wasm_cache.GetOrCompileJSToWasmWrapper(
2094 221124 : isolate, function.sig, function.imported);
2095 442244 : export_wrappers->set(wrapper_index, *wrapper_code);
2096 221123 : RecordStats(*wrapper_code, isolate->counters());
2097 221124 : ++wrapper_index;
2098 : }
2099 140434 : }
2100 :
2101 137965 : Handle<Script> CreateWasmScript(Isolate* isolate,
2102 : const ModuleWireBytes& wire_bytes,
2103 : const std::string& source_map_url) {
2104 : Handle<Script> script =
2105 137965 : isolate->factory()->NewScript(isolate->factory()->empty_string());
2106 413895 : script->set_context_data(isolate->native_context()->debug_context_id());
2107 : script->set_type(Script::TYPE_WASM);
2108 :
2109 137965 : int hash = StringHasher::HashSequentialString(
2110 : reinterpret_cast<const char*>(wire_bytes.start()),
2111 137965 : static_cast<int>(wire_bytes.length()), kZeroHashSeed);
2112 :
2113 : const int kBufferSize = 32;
2114 : char buffer[kBufferSize];
2115 :
2116 137965 : int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash);
2117 : DCHECK(name_chars >= 0 && name_chars < kBufferSize);
2118 : MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte(
2119 137965 : VectorOf(reinterpret_cast<uint8_t*>(buffer), name_chars),
2120 137965 : AllocationType::kOld);
2121 275930 : script->set_name(*name_str.ToHandleChecked());
2122 :
2123 137965 : if (source_map_url.size() != 0) {
2124 : MaybeHandle<String> src_map_str = isolate->factory()->NewStringFromUtf8(
2125 4 : CStrVector(source_map_url.c_str()), AllocationType::kOld);
2126 8 : script->set_source_mapping_url(*src_map_str.ToHandleChecked());
2127 : }
2128 137965 : return script;
2129 : }
2130 :
2131 : } // namespace wasm
2132 : } // namespace internal
2133 122004 : } // namespace v8
2134 :
2135 : #undef TRACE_COMPILE
2136 : #undef TRACE_STREAMING
2137 : #undef TRACE_LAZY
|