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