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 "src/api.h"
8 : #include "src/asmjs/asm-js.h"
9 : #include "src/base/enum-set.h"
10 : #include "src/base/optional.h"
11 : #include "src/base/template-utils.h"
12 : #include "src/base/utils/random-number-generator.h"
13 : #include "src/compiler/wasm-compiler.h"
14 : #include "src/counters.h"
15 : #include "src/heap/heap-inl.h" // For CodeSpaceMemoryModificationScope.
16 : #include "src/identity-map.h"
17 : #include "src/property-descriptor.h"
18 : #include "src/task-utils.h"
19 : #include "src/tracing/trace-event.h"
20 : #include "src/trap-handler/trap-handler.h"
21 : #include "src/wasm/js-to-wasm-wrapper-cache-inl.h"
22 : #include "src/wasm/module-decoder.h"
23 : #include "src/wasm/streaming-decoder.h"
24 : #include "src/wasm/wasm-code-manager.h"
25 : #include "src/wasm/wasm-engine.h"
26 : #include "src/wasm/wasm-import-wrapper-cache-inl.h"
27 : #include "src/wasm/wasm-js.h"
28 : #include "src/wasm/wasm-limits.h"
29 : #include "src/wasm/wasm-memory.h"
30 : #include "src/wasm/wasm-objects-inl.h"
31 : #include "src/wasm/wasm-result.h"
32 : #include "src/wasm/wasm-serialization.h"
33 :
34 : #define TRACE_COMPILE(...) \
35 : do { \
36 : if (FLAG_trace_wasm_compiler) PrintF(__VA_ARGS__); \
37 : } while (false)
38 :
39 : #define TRACE_STREAMING(...) \
40 : do { \
41 : if (FLAG_trace_wasm_streaming) PrintF(__VA_ARGS__); \
42 : } while (false)
43 :
44 : #define TRACE_LAZY(...) \
45 : do { \
46 : if (FLAG_trace_wasm_lazy_compilation) PrintF(__VA_ARGS__); \
47 : } while (false)
48 :
49 : namespace v8 {
50 : namespace internal {
51 : namespace wasm {
52 :
53 : namespace {
54 :
55 : enum class CompileMode : uint8_t { kRegular, kTiering };
56 :
57 : // Background compile jobs hold a shared pointer to this token. The token is
58 : // used to notify them that they should stop. As soon as they see this (after
59 : // finishing their current compilation unit), they will stop.
60 : // This allows to already remove the NativeModule without having to synchronize
61 : // on background compile jobs.
62 1240023 : class BackgroundCompileToken {
63 : public:
64 : explicit BackgroundCompileToken(NativeModule* native_module)
65 1240023 : : native_module_(native_module) {}
66 :
67 : void Cancel() {
68 1240212 : base::MutexGuard mutex_guard(&mutex_);
69 1240212 : native_module_ = nullptr;
70 : }
71 :
72 : // Only call this while holding the {mutex_}.
73 243 : void CancelLocked() { native_module_ = nullptr; }
74 :
75 : private:
76 : friend class BackgroundCompileScope;
77 : base::Mutex mutex_;
78 : NativeModule* native_module_;
79 :
80 : NativeModule* StartScope() {
81 426506 : mutex_.Lock();
82 426663 : return native_module_;
83 : }
84 :
85 426663 : void ExitScope() { mutex_.Unlock(); }
86 : };
87 :
88 : class CompilationStateImpl;
89 :
90 : // Keep these scopes short, as they hold the mutex of the token, which
91 : // sequentializes all these scopes. The mutex is also acquired from foreground
92 : // tasks, which should not be blocked for a long time.
93 : class BackgroundCompileScope {
94 : public:
95 : explicit BackgroundCompileScope(
96 : const std::shared_ptr<BackgroundCompileToken>& token)
97 426506 : : token_(token.get()), native_module_(token->StartScope()) {}
98 :
99 : ~BackgroundCompileScope() { token_->ExitScope(); }
100 :
101 : bool cancelled() const { return native_module_ == nullptr; }
102 :
103 : NativeModule* native_module() {
104 : DCHECK(!cancelled());
105 : return native_module_;
106 : }
107 :
108 : inline CompilationStateImpl* compilation_state();
109 :
110 : private:
111 : BackgroundCompileToken* const token_;
112 : NativeModule* const native_module_;
113 : };
114 :
115 : // The {CompilationStateImpl} keeps track of the compilation state of the
116 : // owning NativeModule, i.e. which functions are left to be compiled.
117 : // It contains a task manager to allow parallel and asynchronous background
118 : // compilation of functions.
119 : // It's public interface {CompilationState} lives in compilation-environment.h.
120 : class CompilationStateImpl {
121 : public:
122 : CompilationStateImpl(NativeModule*, std::shared_ptr<Counters> async_counters);
123 : ~CompilationStateImpl();
124 :
125 : // Cancel all background compilation and wait for all tasks to finish. Call
126 : // this before destructing this object.
127 : void AbortCompilation();
128 :
129 : // Set the number of compilations unit expected to be executed. Needs to be
130 : // set before {AddCompilationUnits} is run, which triggers background
131 : // compilation.
132 : void SetNumberOfFunctionsToCompile(size_t num_functions);
133 :
134 : // Add the callback function to be called on compilation events. Needs to be
135 : // set before {AddCompilationUnits} is run.
136 : void AddCallback(CompilationState::callback_t);
137 :
138 : // Inserts new functions to compile and kicks off compilation.
139 : void AddCompilationUnits(
140 : std::vector<std::unique_ptr<WasmCompilationUnit>>& baseline_units,
141 : std::vector<std::unique_ptr<WasmCompilationUnit>>& tiering_units);
142 : std::unique_ptr<WasmCompilationUnit> GetNextCompilationUnit();
143 :
144 : void OnFinishedUnit(ExecutionTier, WasmCode*);
145 :
146 : void ReportDetectedFeatures(const WasmFeatures& detected);
147 : void OnBackgroundTaskStopped(const WasmFeatures& detected);
148 : void PublishDetectedFeatures(Isolate* isolate, const WasmFeatures& detected);
149 : void RestartBackgroundCompileTask();
150 : void RestartBackgroundTasks();
151 :
152 : void SetError(uint32_t func_index, const WasmError& error);
153 :
154 : bool failed() const {
155 8056 : return compile_error_.load(std::memory_order_relaxed) != nullptr;
156 : }
157 :
158 919861 : bool baseline_compilation_finished() const {
159 919861 : base::MutexGuard guard(&mutex_);
160 1819725 : return outstanding_baseline_units_ == 0 ||
161 1797184 : (compile_mode_ == CompileMode::kTiering &&
162 1817181 : outstanding_tiering_units_ == 0);
163 : }
164 :
165 : CompileMode compile_mode() const { return compile_mode_; }
166 : WasmFeatures* detected_features() { return &detected_features_; }
167 :
168 : // Call {GetCompileError} from foreground threads only, since we access
169 : // NativeModule::wire_bytes, which is set from the foreground thread once the
170 : // stream has finished.
171 13018 : WasmError GetCompileError() {
172 : CompilationError* error = compile_error_.load(std::memory_order_acquire);
173 : DCHECK_NOT_NULL(error);
174 13018 : std::ostringstream error_msg;
175 13018 : error_msg << "Compiling wasm function \"";
176 13018 : wasm::ModuleWireBytes wire_bytes(native_module_->wire_bytes());
177 : wasm::WireBytesRef name_ref = native_module_->module()->LookupFunctionName(
178 26036 : wire_bytes, error->func_index);
179 13018 : if (name_ref.is_set()) {
180 272 : wasm::WasmName name = wire_bytes.GetNameOrNull(name_ref);
181 544 : error_msg.write(name.start(), name.length());
182 : } else {
183 25492 : error_msg << "wasm-function[" << error->func_index << "]";
184 : }
185 26036 : error_msg << "\" failed: " << error->error.message();
186 13018 : return WasmError{error->error.offset(), error_msg.str()};
187 : }
188 :
189 2336214 : void SetWireBytesStorage(
190 : std::shared_ptr<WireBytesStorage> wire_bytes_storage) {
191 2336214 : base::MutexGuard guard(&mutex_);
192 : wire_bytes_storage_ = wire_bytes_storage;
193 2336214 : }
194 :
195 906332 : std::shared_ptr<WireBytesStorage> GetWireBytesStorage() const {
196 906332 : base::MutexGuard guard(&mutex_);
197 : DCHECK_NOT_NULL(wire_bytes_storage_);
198 906339 : return wire_bytes_storage_;
199 : }
200 :
201 : private:
202 : struct CompilationError {
203 : uint32_t const func_index;
204 : WasmError const error;
205 : CompilationError(uint32_t func_index, WasmError error)
206 6646 : : func_index(func_index), error(std::move(error)) {}
207 : };
208 :
209 : void NotifyOnEvent(CompilationEvent event);
210 :
211 : NativeModule* const native_module_;
212 : const std::shared_ptr<BackgroundCompileToken> background_compile_token_;
213 : const CompileMode compile_mode_;
214 : const std::shared_ptr<Counters> async_counters_;
215 :
216 : // Compilation error, atomically updated, but at most once (nullptr -> error).
217 : // Uses acquire-release semantics (acquire on load, release on update).
218 : // For checking whether an error is set, relaxed semantics can be used.
219 : std::atomic<CompilationError*> compile_error_{nullptr};
220 :
221 : // This mutex protects all information of this {CompilationStateImpl} which is
222 : // being accessed concurrently.
223 : mutable base::Mutex mutex_;
224 :
225 : //////////////////////////////////////////////////////////////////////////////
226 : // Protected by {mutex_}:
227 :
228 : std::vector<std::unique_ptr<WasmCompilationUnit>> baseline_compilation_units_;
229 : std::vector<std::unique_ptr<WasmCompilationUnit>> tiering_compilation_units_;
230 :
231 : int num_background_tasks_ = 0;
232 :
233 : // Features detected to be used in this module. Features can be detected
234 : // as a module is being compiled.
235 : WasmFeatures detected_features_ = kNoWasmFeatures;
236 :
237 : // Abstraction over the storage of the wire bytes. Held in a shared_ptr so
238 : // that background compilation jobs can keep the storage alive while
239 : // compiling.
240 : std::shared_ptr<WireBytesStorage> wire_bytes_storage_;
241 :
242 : size_t outstanding_baseline_units_ = 0;
243 : size_t outstanding_tiering_units_ = 0;
244 :
245 : // End of fields protected by {mutex_}.
246 : //////////////////////////////////////////////////////////////////////////////
247 :
248 : // Callback functions to be called on compilation events. Only accessible from
249 : // the foreground thread.
250 : std::vector<CompilationState::callback_t> callbacks_;
251 :
252 : const int max_background_tasks_ = 0;
253 : };
254 :
255 : CompilationStateImpl* Impl(CompilationState* compilation_state) {
256 : return reinterpret_cast<CompilationStateImpl*>(compilation_state);
257 : }
258 : const CompilationStateImpl* Impl(const CompilationState* compilation_state) {
259 : return reinterpret_cast<const CompilationStateImpl*>(compilation_state);
260 : }
261 :
262 : CompilationStateImpl* BackgroundCompileScope::compilation_state() {
263 : return Impl(native_module()->compilation_state());
264 : }
265 :
266 : void UpdateFeatureUseCounts(Isolate* isolate, const WasmFeatures& detected) {
267 142733 : if (detected.threads) {
268 1232 : isolate->CountUsage(v8::Isolate::UseCounterFeature::kWasmThreadOpcodes);
269 : }
270 : }
271 :
272 : } // namespace
273 :
274 : //////////////////////////////////////////////////////
275 : // PIMPL implementation of {CompilationState}.
276 :
277 1240023 : CompilationState::~CompilationState() { Impl(this)->~CompilationStateImpl(); }
278 :
279 1240023 : void CompilationState::AbortCompilation() { Impl(this)->AbortCompilation(); }
280 :
281 6646 : void CompilationState::SetError(uint32_t func_index, const WasmError& error) {
282 6646 : Impl(this)->SetError(func_index, error);
283 6646 : }
284 :
285 2336214 : void CompilationState::SetWireBytesStorage(
286 : std::shared_ptr<WireBytesStorage> wire_bytes_storage) {
287 4672428 : Impl(this)->SetWireBytesStorage(std::move(wire_bytes_storage));
288 2336214 : }
289 :
290 861489 : std::shared_ptr<WireBytesStorage> CompilationState::GetWireBytesStorage()
291 : const {
292 869529 : return Impl(this)->GetWireBytesStorage();
293 : }
294 :
295 4 : void CompilationState::AddCallback(CompilationState::callback_t callback) {
296 8 : return Impl(this)->AddCallback(std::move(callback));
297 : }
298 :
299 32 : bool CompilationState::failed() const { return Impl(this)->failed(); }
300 :
301 0 : void CompilationState::OnFinishedUnit(ExecutionTier tier, WasmCode* code) {
302 0 : Impl(this)->OnFinishedUnit(tier, code);
303 0 : }
304 :
305 : // static
306 1240023 : std::unique_ptr<CompilationState> CompilationState::New(
307 : NativeModule* native_module, std::shared_ptr<Counters> async_counters) {
308 : return std::unique_ptr<CompilationState>(reinterpret_cast<CompilationState*>(
309 2480046 : new CompilationStateImpl(native_module, std::move(async_counters))));
310 : }
311 :
312 : // End of PIMPL implementation of {CompilationState}.
313 : //////////////////////////////////////////////////////
314 :
315 8040 : WasmCode* LazyCompileFunction(Isolate* isolate, NativeModule* native_module,
316 : int func_index) {
317 : base::ElapsedTimer compilation_timer;
318 : DCHECK(!native_module->has_code(static_cast<uint32_t>(func_index)));
319 :
320 : compilation_timer.Start();
321 :
322 : TRACE_LAZY("Compiling wasm-function#%d.\n", func_index);
323 :
324 : const uint8_t* module_start = native_module->wire_bytes().start();
325 :
326 16080 : const WasmFunction* func = &native_module->module()->functions[func_index];
327 : FunctionBody func_body{func->sig, func->code.offset(),
328 : module_start + func->code.offset(),
329 : module_start + func->code.end_offset()};
330 :
331 : ExecutionTier tier =
332 8040 : WasmCompilationUnit::GetDefaultExecutionTier(native_module->module());
333 8040 : WasmCompilationUnit unit(isolate->wasm_engine(), func_index, tier);
334 8040 : CompilationEnv env = native_module->CreateCompilationEnv();
335 : WasmCompilationResult result = unit.ExecuteCompilation(
336 : &env, native_module->compilation_state()->GetWireBytesStorage(),
337 : isolate->counters(),
338 32160 : Impl(native_module->compilation_state())->detected_features());
339 8040 : WasmCode* code = unit.Publish(std::move(result), native_module);
340 :
341 : // During lazy compilation, we should never get compilation errors. The module
342 : // was verified before starting execution with lazy compilation.
343 : // This might be OOM, but then we cannot continue execution anyway.
344 : // TODO(clemensh): According to the spec, we can actually skip validation at
345 : // module creation time, and return a function that always traps here.
346 8040 : CHECK(!native_module->compilation_state()->failed());
347 :
348 8040 : if (WasmCode::ShouldBeLogged(isolate)) code->LogCode(isolate);
349 :
350 : int64_t func_size =
351 8040 : static_cast<int64_t>(func->code.end_offset() - func->code.offset());
352 8040 : int64_t compilation_time = compilation_timer.Elapsed().InMicroseconds();
353 :
354 : auto counters = isolate->counters();
355 8040 : counters->wasm_lazily_compiled_functions()->Increment();
356 :
357 : counters->wasm_lazy_compilation_throughput()->AddSample(
358 8040 : compilation_time != 0 ? static_cast<int>(func_size / compilation_time)
359 16080 : : 0);
360 :
361 8040 : return code;
362 : }
363 :
364 8040 : Address CompileLazy(Isolate* isolate, NativeModule* native_module,
365 : uint32_t func_index) {
366 : HistogramTimerScope lazy_time_scope(
367 8040 : isolate->counters()->wasm_lazy_compilation_time());
368 :
369 : DCHECK(!native_module->lazy_compile_frozen());
370 :
371 16080 : NativeModuleModificationScope native_module_modification_scope(native_module);
372 :
373 8040 : WasmCode* result = LazyCompileFunction(isolate, native_module, func_index);
374 : DCHECK_NOT_NULL(result);
375 : DCHECK_EQ(func_index, result->index());
376 :
377 8040 : return result->instruction_start();
378 : }
379 :
380 : namespace {
381 :
382 : // The {CompilationUnitBuilder} builds compilation units and stores them in an
383 : // internal buffer. The buffer is moved into the working queue of the
384 : // {CompilationStateImpl} when {Commit} is called.
385 6243 : class CompilationUnitBuilder {
386 : public:
387 : explicit CompilationUnitBuilder(NativeModule* native_module,
388 : WasmEngine* wasm_engine)
389 : : native_module_(native_module),
390 : wasm_engine_(wasm_engine),
391 : default_tier_(WasmCompilationUnit::GetDefaultExecutionTier(
392 6435 : native_module->module())) {}
393 :
394 102713 : void AddUnit(uint32_t func_index) {
395 102713 : switch (compilation_state()->compile_mode()) {
396 : case CompileMode::kTiering:
397 : tiering_units_.emplace_back(
398 205106 : CreateUnit(func_index, ExecutionTier::kOptimized));
399 : baseline_units_.emplace_back(
400 205106 : CreateUnit(func_index, ExecutionTier::kBaseline));
401 102553 : return;
402 : case CompileMode::kRegular:
403 480 : baseline_units_.emplace_back(CreateUnit(func_index, default_tier_));
404 160 : return;
405 : }
406 0 : UNREACHABLE();
407 : }
408 :
409 12574 : bool Commit() {
410 6371 : if (baseline_units_.empty() && tiering_units_.empty()) return false;
411 12518 : compilation_state()->AddCompilationUnits(baseline_units_, tiering_units_);
412 6259 : Clear();
413 6259 : return true;
414 : }
415 :
416 6303 : void Clear() {
417 : baseline_units_.clear();
418 : tiering_units_.clear();
419 6303 : }
420 :
421 : private:
422 : std::unique_ptr<WasmCompilationUnit> CreateUnit(uint32_t func_index,
423 : ExecutionTier tier) {
424 : return base::make_unique<WasmCompilationUnit>(wasm_engine_, func_index,
425 205266 : tier);
426 : }
427 :
428 : CompilationStateImpl* compilation_state() const {
429 : return Impl(native_module_->compilation_state());
430 : }
431 :
432 : NativeModule* const native_module_;
433 : WasmEngine* const wasm_engine_;
434 : const ExecutionTier default_tier_;
435 : std::vector<std::unique_ptr<WasmCompilationUnit>> baseline_units_;
436 : std::vector<std::unique_ptr<WasmCompilationUnit>> tiering_units_;
437 : };
438 :
439 : bool compile_lazy(const WasmModule* module) {
440 143151 : return FLAG_wasm_lazy_compilation ||
441 142663 : (FLAG_asm_wasm_lazy_compilation && module->origin == kAsmJsOrigin);
442 : }
443 :
444 219835 : void RecordStats(const Code code, Counters* counters) {
445 219835 : counters->wasm_generated_code_size()->Increment(code->body_size());
446 439673 : counters->wasm_reloc_size()->Increment(code->relocation_info()->length());
447 219836 : }
448 :
449 228803 : double MonotonicallyIncreasingTimeInMs() {
450 228803 : return V8::GetCurrentPlatform()->MonotonicallyIncreasingTime() *
451 228802 : base::Time::kMillisecondsPerSecond;
452 : }
453 :
454 : // Run by each compilation task and by the main thread (i.e. in both
455 : // foreground and background threads).
456 882554 : bool FetchAndExecuteCompilationUnit(CompilationEnv* env,
457 : NativeModule* native_module,
458 : CompilationStateImpl* compilation_state,
459 : WasmFeatures* detected,
460 : Counters* counters) {
461 : DisallowHeapAccess no_heap_access;
462 :
463 : std::unique_ptr<WasmCompilationUnit> unit =
464 882554 : compilation_state->GetNextCompilationUnit();
465 882554 : if (unit == nullptr) return false;
466 :
467 : WasmCompilationResult result = unit->ExecuteCompilation(
468 24576 : env, compilation_state->GetWireBytesStorage(), counters, detected);
469 :
470 12288 : WasmCode* code = unit->Publish(std::move(result), native_module);
471 6144 : compilation_state->OnFinishedUnit(unit->requested_tier(), code);
472 :
473 : return true;
474 : }
475 :
476 6051 : void InitializeCompilationUnits(NativeModule* native_module,
477 : WasmEngine* wasm_engine) {
478 : ModuleWireBytes wire_bytes(native_module->wire_bytes());
479 : const WasmModule* module = native_module->module();
480 : CompilationUnitBuilder builder(native_module, wasm_engine);
481 6051 : uint32_t start = module->num_imported_functions;
482 6051 : uint32_t end = start + module->num_declared_functions;
483 108340 : for (uint32_t i = start; i < end; ++i) {
484 102289 : builder.AddUnit(i);
485 : }
486 6051 : builder.Commit();
487 6051 : }
488 :
489 4818 : void CompileInParallel(Isolate* isolate, NativeModule* native_module) {
490 : // Data structures for the parallel compilation.
491 :
492 : //-----------------------------------------------------------------------
493 : // For parallel compilation:
494 : // 1) The main thread allocates a compilation unit for each wasm function
495 : // and stores them in the vector {compilation_units} within the
496 : // {compilation_state}. By adding units to the {compilation_state}, new
497 : // {BackgroundCompileTasks} instances are spawned which run on
498 : // the background threads.
499 : // 2) The background threads and the main thread pick one compilation unit at
500 : // a time and execute the parallel phase of the compilation unit.
501 :
502 : // Turn on the {CanonicalHandleScope} so that the background threads can
503 : // use the node cache.
504 4818 : CanonicalHandleScope canonical(isolate);
505 :
506 : CompilationStateImpl* compilation_state =
507 : Impl(native_module->compilation_state());
508 : uint32_t num_wasm_functions =
509 4818 : native_module->num_functions() - native_module->num_imported_functions();
510 4818 : compilation_state->SetNumberOfFunctionsToCompile(num_wasm_functions);
511 :
512 : // 1) The main thread allocates a compilation unit for each wasm function
513 : // and stores them in the vector {compilation_units} within the
514 : // {compilation_state}. By adding units to the {compilation_state}, new
515 : // {BackgroundCompileTask} instances are spawned which run on
516 : // background threads.
517 4818 : InitializeCompilationUnits(native_module, isolate->wasm_engine());
518 :
519 : // 2) The background threads and the main thread pick one compilation unit at
520 : // a time and execute the parallel phase of the compilation unit.
521 4818 : WasmFeatures detected_features;
522 4818 : CompilationEnv env = native_module->CreateCompilationEnv();
523 : // TODO(wasm): This might already execute TurboFan units on the main thread,
524 : // while waiting for baseline compilation to finish. This can introduce
525 : // additional delay.
526 : // TODO(wasm): This is a busy-wait loop once all units have started executing
527 : // in background threads. Replace by a semaphore / barrier.
528 1779362 : while (!compilation_state->failed() &&
529 887172 : !compilation_state->baseline_compilation_finished()) {
530 : FetchAndExecuteCompilationUnit(&env, native_module, compilation_state,
531 882554 : &detected_features, isolate->counters());
532 : }
533 :
534 : // Publish features from the foreground and background tasks.
535 4818 : compilation_state->PublishDetectedFeatures(isolate, detected_features);
536 4818 : }
537 :
538 135722 : void CompileSequentially(Isolate* isolate, NativeModule* native_module,
539 : ErrorThrower* thrower) {
540 : DCHECK(!thrower->error());
541 :
542 : ModuleWireBytes wire_bytes(native_module->wire_bytes());
543 : const WasmModule* module = native_module->module();
544 135722 : WasmFeatures detected = kNoWasmFeatures;
545 : auto* comp_state = Impl(native_module->compilation_state());
546 : ExecutionTier tier =
547 135722 : WasmCompilationUnit::GetDefaultExecutionTier(native_module->module());
548 506718 : for (const WasmFunction& func : module->functions) {
549 241654 : if (func.imported) continue; // Imports are compiled at instantiation time.
550 :
551 : // Compile the function.
552 : WasmCompilationUnit::CompileWasmFunction(isolate, native_module, &detected,
553 130366 : &func, tier);
554 130363 : if (comp_state->failed()) {
555 12754 : thrower->CompileFailed(comp_state->GetCompileError());
556 6377 : break;
557 : }
558 : }
559 : UpdateFeatureUseCounts(isolate, detected);
560 135719 : }
561 :
562 624 : void ValidateSequentially(Isolate* isolate, NativeModule* native_module,
563 : ErrorThrower* thrower) {
564 : DCHECK(!thrower->error());
565 :
566 : ModuleWireBytes wire_bytes(native_module->wire_bytes());
567 : const WasmModule* module = native_module->module();
568 176 : uint32_t start = module->num_imported_functions;
569 176 : uint32_t end = start + module->num_declared_functions;
570 616 : for (uint32_t i = start; i < end; ++i) {
571 448 : const WasmFunction& func = module->functions[i];
572 :
573 : const byte* base = wire_bytes.start();
574 : FunctionBody body{func.sig, func.code.offset(), base + func.code.offset(),
575 448 : base + func.code.end_offset()};
576 : DecodeResult result;
577 : {
578 896 : auto time_counter = SELECT_WASM_COUNTER(
579 : isolate->counters(), module->origin, wasm_decode, function_time);
580 :
581 : TimedHistogramScope wasm_decode_function_time_scope(time_counter);
582 448 : WasmFeatures detected;
583 896 : result = VerifyWasmCode(isolate->allocator(),
584 448 : native_module->enabled_features(), module,
585 : &detected, body);
586 : }
587 448 : if (result.failed()) {
588 8 : TruncatedUserString<> name(wire_bytes.GetNameOrNull(&func, module));
589 : thrower->CompileError("Compiling function #%d:%.*s failed: %s @+%u", i,
590 : name.length(), name.start(),
591 : result.error().message().c_str(),
592 16 : result.error().offset());
593 : break;
594 : }
595 : }
596 176 : }
597 :
598 143327 : void CompileNativeModule(Isolate* isolate, ErrorThrower* thrower,
599 : const WasmModule* wasm_module,
600 : NativeModule* native_module) {
601 : ModuleWireBytes wire_bytes(native_module->wire_bytes());
602 :
603 143151 : if (compile_lazy(wasm_module)) {
604 2611 : if (wasm_module->origin == kWasmOrigin) {
605 : // Validate wasm modules for lazy compilation. Don't validate asm.js
606 : // modules, they are valid by construction (otherwise a CHECK will fail
607 : // during lazy compilation).
608 : // TODO(clemensh): According to the spec, we can actually skip validation
609 : // at module creation time, and return a function that always traps at
610 : // (lazy) compilation time.
611 176 : ValidateSequentially(isolate, native_module, thrower);
612 143325 : if (thrower->error()) return;
613 : }
614 :
615 2603 : native_module->SetLazyBuiltin(BUILTIN_CODE(isolate, WasmCompileLazy));
616 : } else {
617 : size_t funcs_to_compile =
618 281080 : wasm_module->functions.size() - wasm_module->num_imported_functions;
619 : bool compile_parallel =
620 281072 : !FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks > 0 &&
621 145358 : funcs_to_compile > 1 &&
622 4818 : V8::GetCurrentPlatform()->NumberOfWorkerThreads() > 0;
623 :
624 140540 : if (compile_parallel) {
625 4818 : CompileInParallel(isolate, native_module);
626 : } else {
627 135722 : CompileSequentially(isolate, native_module, thrower);
628 : }
629 : auto* compilation_state = Impl(native_module->compilation_state());
630 140539 : if (compilation_state->failed()) {
631 13154 : thrower->CompileFailed(compilation_state->GetCompileError());
632 : }
633 : }
634 : }
635 :
636 : // The runnable task that performs compilations in the background.
637 94232 : class BackgroundCompileTask : public CancelableTask {
638 : public:
639 31417 : explicit BackgroundCompileTask(CancelableTaskManager* manager,
640 : std::shared_ptr<BackgroundCompileToken> token,
641 : std::shared_ptr<Counters> async_counters)
642 : : CancelableTask(manager),
643 : token_(std::move(token)),
644 62834 : async_counters_(std::move(async_counters)) {}
645 :
646 31386 : void RunInternal() override {
647 : TRACE_COMPILE("(3b) Compiling...\n");
648 62773 : TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
649 : "BackgroundCompileTask::RunInternal");
650 :
651 : // These fields are initialized before getting the first unit of work.
652 31387 : base::Optional<CompilationEnv> env;
653 31387 : std::shared_ptr<WireBytesStorage> wire_bytes;
654 : std::shared_ptr<const WasmModule> module;
655 :
656 31387 : WasmFeatures detected_features = kNoWasmFeatures;
657 31387 : double deadline = MonotonicallyIncreasingTimeInMs() + 50.0;
658 : while (true) {
659 : // Step 1 (synchronized): Get a WasmCompilationUnit, and initialize some
660 : // fields if this is the first unit executed by this task.
661 : std::unique_ptr<WasmCompilationUnit> unit;
662 : {
663 : BackgroundCompileScope compile_scope(token_);
664 228821 : if (compile_scope.cancelled()) return;
665 228075 : if (!env.has_value()) {
666 61332 : env.emplace(compile_scope.native_module()->CreateCompilationEnv());
667 61332 : wire_bytes = compile_scope.compilation_state()->GetWireBytesStorage();
668 : module = compile_scope.native_module()->shared_module();
669 : }
670 456149 : unit = compile_scope.compilation_state()->GetNextCompilationUnit();
671 228074 : if (unit == nullptr) {
672 : compile_scope.compilation_state()->OnBackgroundTaskStopped(
673 30232 : detected_features);
674 30233 : return;
675 : }
676 : }
677 :
678 : // Step 2: Execute the compilation.
679 :
680 : WasmCompilationResult result = unit->ExecuteCompilation(
681 395257 : &env.value(), wire_bytes, async_counters_.get(), &detected_features);
682 :
683 : // Step 3 (synchronized): Publish the compilation result.
684 : {
685 : BackgroundCompileScope compile_scope(token_);
686 197842 : if (compile_scope.cancelled()) return;
687 : WasmCode* code =
688 197689 : unit->Publish(std::move(result), compile_scope.native_module());
689 197689 : if (code == nullptr) {
690 : compile_scope.compilation_state()->OnBackgroundTaskStopped(
691 243 : detected_features);
692 : // Also, cancel all remaining compilation.
693 243 : token_->CancelLocked();
694 : return;
695 : }
696 : compile_scope.compilation_state()->OnFinishedUnit(
697 394892 : unit->requested_tier(), code);
698 197446 : if (deadline < MonotonicallyIncreasingTimeInMs()) {
699 : compile_scope.compilation_state()->ReportDetectedFeatures(
700 26 : detected_features);
701 26 : compile_scope.compilation_state()->RestartBackgroundCompileTask();
702 26 : return;
703 : }
704 : }
705 : }
706 31399 : UNREACHABLE(); // Loop exits via explicit return.
707 : }
708 :
709 : private:
710 : std::shared_ptr<BackgroundCompileToken> token_;
711 : std::shared_ptr<Counters> async_counters_;
712 : };
713 :
714 : } // namespace
715 :
716 143110 : std::unique_ptr<NativeModule> CompileToNativeModule(
717 143111 : Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
718 : std::shared_ptr<const WasmModule> module, const ModuleWireBytes& wire_bytes,
719 : Handle<FixedArray>* export_wrappers_out) {
720 429335 : const WasmModule* wasm_module = module.get();
721 143110 : TimedHistogramScope wasm_compile_module_time_scope(SELECT_WASM_COUNTER(
722 286220 : isolate->counters(), wasm_module->origin, wasm_compile, module_time));
723 :
724 : // Embedder usage count for declared shared memories.
725 143113 : if (wasm_module->has_shared_memory) {
726 1259 : isolate->CountUsage(v8::Isolate::UseCounterFeature::kWasmSharedMemory);
727 : }
728 143112 : int export_wrapper_size = static_cast<int>(module->num_exported_functions);
729 :
730 : // TODO(wasm): only save the sections necessary to deserialize a
731 : // {WasmModule}. E.g. function bodies could be omitted.
732 : OwnedVector<uint8_t> wire_bytes_copy =
733 143112 : OwnedVector<uint8_t>::Of(wire_bytes.module_bytes());
734 :
735 : // Create and compile the native module.
736 : size_t code_size_estimate =
737 143113 : wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module.get());
738 :
739 : // Create a new {NativeModule} first.
740 : auto native_module = isolate->wasm_engine()->NewNativeModule(
741 : isolate, enabled, code_size_estimate,
742 286224 : wasm::NativeModule::kCanAllocateMoreMemory, std::move(module));
743 286226 : native_module->SetWireBytes(std::move(wire_bytes_copy));
744 143113 : native_module->SetRuntimeStubs(isolate);
745 :
746 143113 : CompileNativeModule(isolate, thrower, wasm_module, native_module.get());
747 143111 : if (thrower->error()) return {};
748 :
749 : // Compile JS->wasm wrappers for exported functions.
750 : *export_wrappers_out =
751 136526 : isolate->factory()->NewFixedArray(export_wrapper_size, TENURED);
752 : CompileJsToWasmWrappers(isolate, native_module->module(),
753 136528 : *export_wrappers_out);
754 :
755 : // Log the code within the generated module for profiling.
756 136528 : native_module->LogWasmCodes(isolate);
757 :
758 : return native_module;
759 : }
760 :
761 38 : void CompileNativeModuleWithExplicitBoundsChecks(Isolate* isolate,
762 : ErrorThrower* thrower,
763 : const WasmModule* wasm_module,
764 : NativeModule* native_module) {
765 38 : native_module->DisableTrapHandler();
766 38 : CompileNativeModule(isolate, thrower, wasm_module, native_module);
767 38 : }
768 :
769 2503 : AsyncCompileJob::AsyncCompileJob(
770 2503 : Isolate* isolate, const WasmFeatures& enabled,
771 : std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
772 : std::shared_ptr<CompilationResultResolver> resolver)
773 : : isolate_(isolate),
774 : enabled_features_(enabled),
775 : bytes_copy_(std::move(bytes_copy)),
776 : wire_bytes_(bytes_copy_.get(), bytes_copy_.get() + length),
777 10012 : resolver_(std::move(resolver)) {
778 : v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
779 2503 : v8::Platform* platform = V8::GetCurrentPlatform();
780 5006 : foreground_task_runner_ = platform->GetForegroundTaskRunner(v8_isolate);
781 : native_context_ =
782 5006 : isolate->global_handles()->Create(context->native_context());
783 : DCHECK(native_context_->IsNativeContext());
784 2503 : }
785 :
786 2147 : void AsyncCompileJob::Start() {
787 6441 : DoAsync<DecodeModule>(isolate_->counters()); // --
788 2147 : }
789 :
790 52 : void AsyncCompileJob::Abort() {
791 : // Removing this job will trigger the destructor, which will cancel all
792 : // compilation.
793 156 : isolate_->wasm_engine()->RemoveCompileJob(this);
794 52 : }
795 :
796 1424 : class AsyncStreamingProcessor final : public StreamingProcessor {
797 : public:
798 : explicit AsyncStreamingProcessor(AsyncCompileJob* job);
799 :
800 : bool ProcessModuleHeader(Vector<const uint8_t> bytes,
801 : uint32_t offset) override;
802 :
803 : bool ProcessSection(SectionCode section_code, Vector<const uint8_t> bytes,
804 : uint32_t offset) override;
805 :
806 : bool ProcessCodeSectionHeader(size_t functions_count, uint32_t offset,
807 : std::shared_ptr<WireBytesStorage>) override;
808 :
809 : bool ProcessFunctionBody(Vector<const uint8_t> bytes,
810 : uint32_t offset) override;
811 :
812 : void OnFinishedChunk() override;
813 :
814 : void OnFinishedStream(OwnedVector<uint8_t> bytes) override;
815 :
816 : void OnError(const WasmError&) override;
817 :
818 : void OnAbort() override;
819 :
820 : bool Deserialize(Vector<const uint8_t> wire_bytes,
821 : Vector<const uint8_t> module_bytes) override;
822 :
823 : private:
824 : // Finishes the AsyncCompileJob with an error.
825 : void FinishAsyncCompileJobWithError(const WasmError&);
826 :
827 : void CommitCompilationUnits();
828 :
829 : ModuleDecoder decoder_;
830 : AsyncCompileJob* job_;
831 : std::unique_ptr<CompilationUnitBuilder> compilation_unit_builder_;
832 : uint32_t next_function_ = 0;
833 : };
834 :
835 356 : std::shared_ptr<StreamingDecoder> AsyncCompileJob::CreateStreamingDecoder() {
836 : DCHECK_NULL(stream_);
837 : stream_.reset(
838 1424 : new StreamingDecoder(base::make_unique<AsyncStreamingProcessor>(this)));
839 356 : return stream_;
840 : }
841 :
842 5006 : AsyncCompileJob::~AsyncCompileJob() {
843 2503 : background_task_manager_.CancelAndWait();
844 : // If the runtime objects were not created yet, then initial compilation did
845 : // not finish yet. In this case we can abort compilation.
846 4840 : if (native_module_ && module_object_.is_null()) {
847 141 : Impl(native_module_->compilation_state())->AbortCompilation();
848 : }
849 : // Tell the streaming decoder that the AsyncCompileJob is not available
850 : // anymore.
851 : // TODO(ahaas): Is this notification really necessary? Check
852 : // https://crbug.com/888170.
853 2503 : if (stream_) stream_->NotifyCompilationEnded();
854 : CancelPendingForegroundTask();
855 2503 : isolate_->global_handles()->Destroy(native_context_.location());
856 2503 : if (!module_object_.is_null()) {
857 2196 : isolate_->global_handles()->Destroy(module_object_.location());
858 : }
859 2503 : }
860 :
861 2333 : void AsyncCompileJob::CreateNativeModule(
862 : std::shared_ptr<const WasmModule> module) {
863 : // Embedder usage count for declared shared memories.
864 4666 : if (module->has_shared_memory) {
865 0 : isolate_->CountUsage(v8::Isolate::UseCounterFeature::kWasmSharedMemory);
866 : }
867 :
868 : // TODO(wasm): Improve efficiency of storing module wire bytes. Only store
869 : // relevant sections, not function bodies
870 :
871 : // Create the module object and populate with compiled functions and
872 : // information needed at instantiation time.
873 : // TODO(clemensh): For the same module (same bytes / same hash), we should
874 : // only have one {WasmModuleObject}. Otherwise, we might only set
875 : // breakpoints on a (potentially empty) subset of the instances.
876 : // Create the module object.
877 :
878 : size_t code_size_estimate =
879 2333 : wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module.get());
880 9332 : native_module_ = isolate_->wasm_engine()->NewNativeModule(
881 : isolate_, enabled_features_, code_size_estimate,
882 : wasm::NativeModule::kCanAllocateMoreMemory, std::move(module));
883 6999 : native_module_->SetWireBytes({std::move(bytes_copy_), wire_bytes_.length()});
884 4666 : native_module_->SetRuntimeStubs(isolate_);
885 :
886 2333 : if (stream_) stream_->NotifyNativeModuleCreated(native_module_);
887 2333 : }
888 :
889 2192 : void AsyncCompileJob::PrepareRuntimeObjects() {
890 : // Create heap objects for script and module bytes to be stored in the
891 : // module object. Asm.js is not compiled asynchronously.
892 2192 : const WasmModule* module = native_module_->module();
893 : Handle<Script> script =
894 4384 : CreateWasmScript(isolate_, wire_bytes_, module->source_map_url);
895 :
896 : size_t code_size_estimate =
897 2192 : wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module);
898 : Handle<WasmModuleObject> module_object = WasmModuleObject::New(
899 4384 : isolate_, native_module_, script, code_size_estimate);
900 :
901 4384 : module_object_ = isolate_->global_handles()->Create(*module_object);
902 2192 : }
903 :
904 : // This function assumes that it is executed in a HandleScope, and that a
905 : // context is set on the isolate.
906 2196 : void AsyncCompileJob::FinishCompile() {
907 2196 : bool is_after_deserialization = !module_object_.is_null();
908 2196 : if (!is_after_deserialization) {
909 2192 : PrepareRuntimeObjects();
910 : }
911 : DCHECK(!isolate_->context().is_null());
912 : // Finish the wasm script now and make it public to the debugger.
913 8784 : Handle<Script> script(module_object_->script(), isolate_);
914 6588 : if (script->type() == Script::TYPE_WASM &&
915 4392 : module_object_->module()->source_map_url.size() != 0) {
916 : MaybeHandle<String> src_map_str = isolate_->factory()->NewStringFromUtf8(
917 0 : CStrVector(module_object_->module()->source_map_url.c_str()), TENURED);
918 0 : script->set_source_mapping_url(*src_map_str.ToHandleChecked());
919 : }
920 4392 : isolate_->debug()->OnAfterCompile(script);
921 :
922 : // We can only update the feature counts once the entire compile is done.
923 : auto compilation_state =
924 4392 : Impl(module_object_->native_module()->compilation_state());
925 : compilation_state->PublishDetectedFeatures(
926 2196 : isolate_, *compilation_state->detected_features());
927 :
928 : // TODO(bbudge) Allow deserialization without wrapper compilation, so we can
929 : // just compile wrappers here.
930 2196 : if (!is_after_deserialization) {
931 : // TODO(wasm): compiling wrappers should be made async.
932 2192 : CompileWrappers();
933 : }
934 2196 : FinishModule();
935 2196 : }
936 :
937 245 : void AsyncCompileJob::AsyncCompileFailed(const WasmError& error) {
938 245 : ErrorThrower thrower(isolate_, "WebAssembly.compile()");
939 : thrower.CompileFailed(error);
940 : // {job} keeps the {this} pointer alive.
941 : std::shared_ptr<AsyncCompileJob> job =
942 735 : isolate_->wasm_engine()->RemoveCompileJob(this);
943 490 : resolver_->OnCompilationFailed(thrower.Reify());
944 245 : }
945 :
946 0 : void AsyncCompileJob::AsyncCompileSucceeded(Handle<WasmModuleObject> result) {
947 2196 : resolver_->OnCompilationSucceeded(result);
948 0 : }
949 :
950 : class AsyncCompileJob::CompilationStateCallback {
951 : public:
952 : explicit CompilationStateCallback(AsyncCompileJob* job) : job_(job) {}
953 :
954 2635 : void operator()(CompilationEvent event) {
955 : // This callback is only being called from a foreground task.
956 2635 : switch (event) {
957 : case CompilationEvent::kFinishedBaselineCompilation:
958 : DCHECK(!last_event_.has_value());
959 2576 : if (job_->DecrementAndCheckFinisherCount()) {
960 1239 : AsyncCompileJob* job = job_;
961 1239 : job->foreground_task_runner_->PostTask(
962 1240 : MakeCancelableTask(job->isolate_, [job] {
963 1240 : HandleScope scope(job->isolate_);
964 : SaveAndSwitchContext saved_context(job->isolate_,
965 2480 : *job->native_context_);
966 1240 : job->FinishCompile();
967 8679 : }));
968 : }
969 : break;
970 : case CompilationEvent::kFinishedTopTierCompilation:
971 : DCHECK_EQ(CompilationEvent::kFinishedBaselineCompilation, last_event_);
972 : // This callback should not react to top tier finished callbacks, since
973 : // the job might already be gone then.
974 : break;
975 : case CompilationEvent::kFailedCompilation: {
976 : DCHECK(!last_event_.has_value());
977 : // Tier-up compilation should not fail if baseline compilation
978 : // did not fail.
979 : DCHECK(!Impl(job_->native_module_->compilation_state())
980 : ->baseline_compilation_finished());
981 :
982 64 : AsyncCompileJob* job = job_;
983 64 : job->foreground_task_runner_->PostTask(
984 64 : MakeCancelableTask(job->isolate_, [job] {
985 64 : HandleScope scope(job->isolate_);
986 : SaveAndSwitchContext saved_context(job->isolate_,
987 128 : *job->native_context_);
988 : WasmError error = Impl(job->native_module_->compilation_state())
989 128 : ->GetCompileError();
990 128 : return job->AsyncCompileFailed(error);
991 384 : }));
992 :
993 64 : break;
994 : }
995 : default:
996 0 : UNREACHABLE();
997 : }
998 : #ifdef DEBUG
999 : last_event_ = event;
1000 : #endif
1001 2636 : }
1002 :
1003 : private:
1004 : AsyncCompileJob* job_;
1005 : #ifdef DEBUG
1006 : // This will be modified by different threads, but they externally
1007 : // synchronize, so no explicit synchronization (currently) needed here.
1008 : base::Optional<CompilationEvent> last_event_;
1009 : #endif
1010 : };
1011 :
1012 : // A closure to run a compilation step (either as foreground or background
1013 : // task) and schedule the next step(s), if any.
1014 4642 : class AsyncCompileJob::CompileStep {
1015 : public:
1016 4642 : virtual ~CompileStep() = default;
1017 :
1018 4641 : void Run(AsyncCompileJob* job, bool on_foreground) {
1019 4641 : if (on_foreground) {
1020 2494 : HandleScope scope(job->isolate_);
1021 2494 : SaveAndSwitchContext saved_context(job->isolate_, *job->native_context_);
1022 2494 : RunInForeground(job);
1023 : } else {
1024 2147 : RunInBackground(job);
1025 : }
1026 4641 : }
1027 :
1028 0 : virtual void RunInForeground(AsyncCompileJob*) { UNREACHABLE(); }
1029 0 : virtual void RunInBackground(AsyncCompileJob*) { UNREACHABLE(); }
1030 : };
1031 :
1032 : class AsyncCompileJob::CompileTask : public CancelableTask {
1033 : public:
1034 : CompileTask(AsyncCompileJob* job, bool on_foreground)
1035 : // We only manage the background tasks with the {CancelableTaskManager} of
1036 : // the {AsyncCompileJob}. Foreground tasks are managed by the system's
1037 : // {CancelableTaskManager}. Background tasks cannot spawn tasks managed by
1038 : // their own task manager.
1039 2494 : : CancelableTask(on_foreground ? job->isolate_->cancelable_task_manager()
1040 : : &job->background_task_manager_),
1041 : job_(job),
1042 6788 : on_foreground_(on_foreground) {}
1043 :
1044 9284 : ~CompileTask() override {
1045 4642 : if (job_ != nullptr && on_foreground_) ResetPendingForegroundTask();
1046 9284 : }
1047 :
1048 4641 : void RunInternal() final {
1049 9282 : if (!job_) return;
1050 4641 : if (on_foreground_) ResetPendingForegroundTask();
1051 9282 : job_->step_->Run(job_, on_foreground_);
1052 : // After execution, reset {job_} such that we don't try to reset the pending
1053 : // foreground task when the task is deleted.
1054 4641 : job_ = nullptr;
1055 : }
1056 :
1057 : void Cancel() {
1058 : DCHECK_NOT_NULL(job_);
1059 1 : job_ = nullptr;
1060 : }
1061 :
1062 : private:
1063 : // {job_} will be cleared to cancel a pending task.
1064 : AsyncCompileJob* job_;
1065 : bool on_foreground_;
1066 :
1067 : void ResetPendingForegroundTask() const {
1068 : DCHECK_EQ(this, job_->pending_foreground_task_);
1069 2494 : job_->pending_foreground_task_ = nullptr;
1070 : }
1071 : };
1072 :
1073 2302 : void AsyncCompileJob::StartForegroundTask() {
1074 : DCHECK_NULL(pending_foreground_task_);
1075 :
1076 2302 : auto new_task = base::make_unique<CompileTask>(this, true);
1077 2303 : pending_foreground_task_ = new_task.get();
1078 6909 : foreground_task_runner_->PostTask(std::move(new_task));
1079 2303 : }
1080 :
1081 192 : void AsyncCompileJob::ExecuteForegroundTaskImmediately() {
1082 : DCHECK_NULL(pending_foreground_task_);
1083 :
1084 192 : auto new_task = base::make_unique<CompileTask>(this, true);
1085 192 : pending_foreground_task_ = new_task.get();
1086 192 : new_task->Run();
1087 192 : }
1088 :
1089 0 : void AsyncCompileJob::CancelPendingForegroundTask() {
1090 2503 : if (!pending_foreground_task_) return;
1091 : pending_foreground_task_->Cancel();
1092 1 : pending_foreground_task_ = nullptr;
1093 : }
1094 :
1095 2147 : void AsyncCompileJob::StartBackgroundTask() {
1096 2147 : auto task = base::make_unique<CompileTask>(this, false);
1097 :
1098 : // If --wasm-num-compilation-tasks=0 is passed, do only spawn foreground
1099 : // tasks. This is used to make timing deterministic.
1100 2147 : if (FLAG_wasm_num_compilation_tasks > 0) {
1101 6441 : V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
1102 : } else {
1103 0 : foreground_task_runner_->PostTask(std::move(task));
1104 : }
1105 2147 : }
1106 :
1107 : template <typename Step,
1108 : AsyncCompileJob::UseExistingForegroundTask use_existing_fg_task,
1109 : typename... Args>
1110 48 : void AsyncCompileJob::DoSync(Args&&... args) {
1111 2303 : NextStep<Step>(std::forward<Args>(args)...);
1112 96 : if (use_existing_fg_task && pending_foreground_task_ != nullptr) return;
1113 2302 : StartForegroundTask();
1114 : }
1115 :
1116 : template <typename Step, typename... Args>
1117 : void AsyncCompileJob::DoImmediately(Args&&... args) {
1118 192 : NextStep<Step>(std::forward<Args>(args)...);
1119 192 : ExecuteForegroundTaskImmediately();
1120 : }
1121 :
1122 : template <typename Step, typename... Args>
1123 : void AsyncCompileJob::DoAsync(Args&&... args) {
1124 2147 : NextStep<Step>(std::forward<Args>(args)...);
1125 2147 : StartBackgroundTask();
1126 : }
1127 :
1128 : template <typename Step, typename... Args>
1129 4642 : void AsyncCompileJob::NextStep(Args&&... args) {
1130 4798 : step_.reset(new Step(std::forward<Args>(args)...));
1131 4641 : }
1132 :
1133 : //==========================================================================
1134 : // Step 1: (async) Decode the module.
1135 : //==========================================================================
1136 4294 : class AsyncCompileJob::DecodeModule : public AsyncCompileJob::CompileStep {
1137 : public:
1138 2147 : explicit DecodeModule(Counters* counters) : counters_(counters) {}
1139 :
1140 4294 : void RunInBackground(AsyncCompileJob* job) override {
1141 : ModuleResult result;
1142 : {
1143 : DisallowHandleAllocation no_handle;
1144 : DisallowHeapAllocation no_allocation;
1145 : // Decode the module bytes.
1146 : TRACE_COMPILE("(1) Decoding module...\n");
1147 4294 : TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
1148 : "AsyncCompileJob::DecodeModule");
1149 6441 : result = DecodeWasmModule(
1150 : job->enabled_features_, job->wire_bytes_.start(),
1151 : job->wire_bytes_.end(), false, kWasmOrigin, counters_,
1152 4294 : job->isolate()->wasm_engine()->allocator());
1153 : }
1154 2147 : if (result.failed()) {
1155 : // Decoding failure; reject the promise and clean up.
1156 : job->DoSync<DecodeFail>(std::move(result).error());
1157 : } else {
1158 : // Decode passed.
1159 4244 : job->DoSync<PrepareAndStartCompile>(std::move(result).value(), true);
1160 2147 : }
1161 2147 : }
1162 :
1163 : private:
1164 : Counters* const counters_;
1165 : };
1166 :
1167 : //==========================================================================
1168 : // Step 1b: (sync) Fail decoding the module.
1169 : //==========================================================================
1170 362 : class AsyncCompileJob::DecodeFail : public CompileStep {
1171 : public:
1172 181 : explicit DecodeFail(WasmError error) : error_(std::move(error)) {}
1173 :
1174 : private:
1175 : WasmError error_;
1176 :
1177 181 : void RunInForeground(AsyncCompileJob* job) override {
1178 : TRACE_COMPILE("(1b) Decoding failed.\n");
1179 : // {job_} is deleted in AsyncCompileFailed, therefore the {return}.
1180 181 : return job->AsyncCompileFailed(error_);
1181 : }
1182 : };
1183 :
1184 : //==========================================================================
1185 : // Step 2 (sync): Create heap-allocated data and start compile.
1186 : //==========================================================================
1187 4628 : class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
1188 : public:
1189 : PrepareAndStartCompile(std::shared_ptr<const WasmModule> module,
1190 : bool start_compilation)
1191 4628 : : module_(std::move(module)), start_compilation_(start_compilation) {}
1192 :
1193 : private:
1194 : std::shared_ptr<const WasmModule> module_;
1195 : bool start_compilation_;
1196 :
1197 3546 : void RunInForeground(AsyncCompileJob* job) override {
1198 : TRACE_COMPILE("(2) Prepare and start compile...\n");
1199 :
1200 : // Make sure all compilation tasks stopped running. Decoding (async step)
1201 : // is done.
1202 2313 : job->background_task_manager_.CancelAndWait();
1203 :
1204 4626 : job->CreateNativeModule(module_);
1205 :
1206 : size_t num_functions =
1207 5859 : module_->functions.size() - module_->num_imported_functions;
1208 :
1209 2313 : if (num_functions == 0) {
1210 : // Degenerate case of an empty module.
1211 888 : job->FinishCompile();
1212 3201 : return;
1213 : }
1214 :
1215 : CompilationStateImpl* compilation_state =
1216 2658 : Impl(job->native_module_->compilation_state());
1217 1425 : compilation_state->AddCallback(CompilationStateCallback{job});
1218 1425 : if (start_compilation_) {
1219 : // TODO(ahaas): Try to remove the {start_compilation_} check when
1220 : // streaming decoding is done in the background. If
1221 : // InitializeCompilationUnits always returns 0 for streaming compilation,
1222 : // then DoAsync would do the same as NextStep already.
1223 :
1224 : compilation_state->SetNumberOfFunctionsToCompile(
1225 1233 : module_->num_declared_functions);
1226 : // Add compilation units and kick off compilation.
1227 : InitializeCompilationUnits(job->native_module_.get(),
1228 1233 : job->isolate()->wasm_engine());
1229 : }
1230 : }
1231 : };
1232 :
1233 2192 : void AsyncCompileJob::CompileWrappers() {
1234 : // TODO(wasm): Compile all wrappers here, including the start function wrapper
1235 : // and the wrappers for the function table elements.
1236 : TRACE_COMPILE("(5) Compile wrappers...\n");
1237 : // Compile JS->wasm wrappers for exported functions.
1238 : CompileJsToWasmWrappers(isolate_, module_object_->native_module()->module(),
1239 8768 : handle(module_object_->export_wrappers(), isolate_));
1240 2192 : }
1241 :
1242 2196 : void AsyncCompileJob::FinishModule() {
1243 : TRACE_COMPILE("(6) Finish module...\n");
1244 : AsyncCompileSucceeded(module_object_);
1245 :
1246 2196 : size_t num_functions = native_module_->num_functions() -
1247 : native_module_->num_imported_functions();
1248 2196 : auto* compilation_state = Impl(native_module_->compilation_state());
1249 2196 : if (compilation_state->compile_mode() == CompileMode::kRegular ||
1250 : num_functions == 0) {
1251 : // If we do not tier up, the async compile job is done here and
1252 : // can be deleted.
1253 2772 : isolate_->wasm_engine()->RemoveCompileJob(this);
1254 3120 : return;
1255 : }
1256 : DCHECK_EQ(CompileMode::kTiering, compilation_state->compile_mode());
1257 1272 : if (compilation_state->baseline_compilation_finished()) {
1258 3816 : isolate_->wasm_engine()->RemoveCompileJob(this);
1259 : }
1260 : }
1261 :
1262 0 : AsyncStreamingProcessor::AsyncStreamingProcessor(AsyncCompileJob* job)
1263 : : decoder_(job->enabled_features_),
1264 : job_(job),
1265 356 : compilation_unit_builder_(nullptr) {}
1266 :
1267 156 : void AsyncStreamingProcessor::FinishAsyncCompileJobWithError(
1268 : const WasmError& error) {
1269 : DCHECK(error.has_error());
1270 : // Make sure all background tasks stopped executing before we change the state
1271 : // of the AsyncCompileJob to DecodeFail.
1272 156 : job_->background_task_manager_.CancelAndWait();
1273 :
1274 : // Check if there is already a CompiledModule, in which case we have to clean
1275 : // up the CompilationStateImpl as well.
1276 156 : if (job_->native_module_) {
1277 48 : Impl(job_->native_module_->compilation_state())->AbortCompilation();
1278 :
1279 : job_->DoSync<AsyncCompileJob::DecodeFail,
1280 48 : AsyncCompileJob::kUseExistingForegroundTask>(error);
1281 :
1282 : // Clear the {compilation_unit_builder_} if it exists. This is needed
1283 : // because there is a check in the destructor of the
1284 : // {CompilationUnitBuilder} that it is empty.
1285 48 : if (compilation_unit_builder_) compilation_unit_builder_->Clear();
1286 : } else {
1287 : job_->DoSync<AsyncCompileJob::DecodeFail>(error);
1288 : }
1289 156 : }
1290 :
1291 : // Process the module header.
1292 328 : bool AsyncStreamingProcessor::ProcessModuleHeader(Vector<const uint8_t> bytes,
1293 : uint32_t offset) {
1294 : TRACE_STREAMING("Process module header...\n");
1295 : decoder_.StartDecoding(job_->isolate()->counters(),
1296 984 : job_->isolate()->wasm_engine()->allocator());
1297 328 : decoder_.DecodeModuleHeader(bytes, offset);
1298 328 : if (!decoder_.ok()) {
1299 16 : FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());
1300 16 : return false;
1301 : }
1302 : return true;
1303 : }
1304 :
1305 : // Process all sections except for the code section.
1306 724 : bool AsyncStreamingProcessor::ProcessSection(SectionCode section_code,
1307 : Vector<const uint8_t> bytes,
1308 : uint32_t offset) {
1309 : TRACE_STREAMING("Process section %d ...\n", section_code);
1310 724 : if (compilation_unit_builder_) {
1311 : // We reached a section after the code section, we do not need the
1312 : // compilation_unit_builder_ anymore.
1313 : CommitCompilationUnits();
1314 : compilation_unit_builder_.reset();
1315 : }
1316 724 : if (section_code == SectionCode::kUnknownSectionCode) {
1317 : Decoder decoder(bytes, offset);
1318 : section_code = ModuleDecoder::IdentifyUnknownSection(
1319 56 : decoder, bytes.start() + bytes.length());
1320 56 : if (section_code == SectionCode::kUnknownSectionCode) {
1321 : // Skip unknown sections that we do not know how to handle.
1322 : return true;
1323 : }
1324 : // Remove the unknown section tag from the payload bytes.
1325 112 : offset += decoder.position();
1326 56 : bytes = bytes.SubVector(decoder.position(), bytes.size());
1327 : }
1328 : constexpr bool verify_functions = false;
1329 724 : decoder_.DecodeSection(section_code, bytes, offset, verify_functions);
1330 724 : if (!decoder_.ok()) {
1331 20 : FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());
1332 20 : return false;
1333 : }
1334 : return true;
1335 : }
1336 :
1337 : // Start the code section.
1338 204 : bool AsyncStreamingProcessor::ProcessCodeSectionHeader(
1339 : size_t functions_count, uint32_t offset,
1340 : std::shared_ptr<WireBytesStorage> wire_bytes_storage) {
1341 : TRACE_STREAMING("Start the code section with %zu functions...\n",
1342 : functions_count);
1343 204 : if (!decoder_.CheckFunctionsCount(static_cast<uint32_t>(functions_count),
1344 204 : offset)) {
1345 12 : FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());
1346 12 : return false;
1347 : }
1348 : // Execute the PrepareAndStartCompile step immediately and not in a separate
1349 : // task.
1350 : job_->DoImmediately<AsyncCompileJob::PrepareAndStartCompile>(
1351 576 : decoder_.shared_module(), false);
1352 : job_->native_module_->compilation_state()->SetWireBytesStorage(
1353 960 : std::move(wire_bytes_storage));
1354 :
1355 192 : auto* compilation_state = Impl(job_->native_module_->compilation_state());
1356 192 : compilation_state->SetNumberOfFunctionsToCompile(functions_count);
1357 :
1358 : // Set outstanding_finishers_ to 2, because both the AsyncCompileJob and the
1359 : // AsyncStreamingProcessor have to finish.
1360 192 : job_->outstanding_finishers_.store(2);
1361 : compilation_unit_builder_.reset(new CompilationUnitBuilder(
1362 384 : job_->native_module_.get(), job_->isolate()->wasm_engine()));
1363 : return true;
1364 : }
1365 :
1366 : // Process a function body.
1367 424 : bool AsyncStreamingProcessor::ProcessFunctionBody(Vector<const uint8_t> bytes,
1368 : uint32_t offset) {
1369 : TRACE_STREAMING("Process function body %d ...\n", next_function_);
1370 :
1371 : decoder_.DecodeFunctionBody(
1372 848 : next_function_, static_cast<uint32_t>(bytes.length()), offset, false);
1373 :
1374 848 : uint32_t index = next_function_ + decoder_.module()->num_imported_functions;
1375 424 : compilation_unit_builder_->AddUnit(index);
1376 424 : ++next_function_;
1377 : // This method always succeeds. The return value is necessary to comply with
1378 : // the StreamingProcessor interface.
1379 424 : return true;
1380 : }
1381 :
1382 0 : void AsyncStreamingProcessor::CommitCompilationUnits() {
1383 : DCHECK(compilation_unit_builder_);
1384 264 : compilation_unit_builder_->Commit();
1385 0 : }
1386 :
1387 328 : void AsyncStreamingProcessor::OnFinishedChunk() {
1388 : TRACE_STREAMING("FinishChunk...\n");
1389 328 : if (compilation_unit_builder_) CommitCompilationUnits();
1390 328 : }
1391 :
1392 : // Finish the processing of the stream.
1393 132 : void AsyncStreamingProcessor::OnFinishedStream(OwnedVector<uint8_t> bytes) {
1394 : TRACE_STREAMING("Finish stream...\n");
1395 264 : ModuleResult result = decoder_.FinishDecoding(false);
1396 132 : if (result.failed()) {
1397 4 : FinishAsyncCompileJobWithError(result.error());
1398 136 : return;
1399 : }
1400 : // We have to open a HandleScope and prepare the Context for
1401 : // CreateNativeModule, PrepareRuntimeObjects and FinishCompile as this is a
1402 : // callback from the embedder.
1403 128 : HandleScope scope(job_->isolate_);
1404 256 : SaveAndSwitchContext saved_context(job_->isolate_, *job_->native_context_);
1405 :
1406 128 : bool needs_finish = job_->DecrementAndCheckFinisherCount();
1407 256 : if (job_->native_module_ == nullptr) {
1408 : // We are processing a WebAssembly module without code section. Create the
1409 : // runtime objects now (would otherwise happen in {PrepareAndStartCompile}).
1410 40 : job_->CreateNativeModule(std::move(result).value());
1411 : DCHECK(needs_finish);
1412 : }
1413 256 : job_->wire_bytes_ = ModuleWireBytes(bytes.as_vector());
1414 256 : job_->native_module_->SetWireBytes(std::move(bytes));
1415 128 : if (needs_finish) {
1416 64 : job_->FinishCompile();
1417 128 : }
1418 : }
1419 :
1420 : // Report an error detected in the StreamingDecoder.
1421 104 : void AsyncStreamingProcessor::OnError(const WasmError& error) {
1422 : TRACE_STREAMING("Stream error...\n");
1423 104 : FinishAsyncCompileJobWithError(error);
1424 104 : }
1425 :
1426 52 : void AsyncStreamingProcessor::OnAbort() {
1427 : TRACE_STREAMING("Abort stream...\n");
1428 52 : job_->Abort();
1429 52 : }
1430 :
1431 8 : bool AsyncStreamingProcessor::Deserialize(Vector<const uint8_t> module_bytes,
1432 : Vector<const uint8_t> wire_bytes) {
1433 : // DeserializeNativeModule and FinishCompile assume that they are executed in
1434 : // a HandleScope, and that a context is set on the isolate.
1435 12 : HandleScope scope(job_->isolate_);
1436 16 : SaveAndSwitchContext saved_context(job_->isolate_, *job_->native_context_);
1437 :
1438 : MaybeHandle<WasmModuleObject> result =
1439 8 : DeserializeNativeModule(job_->isolate_, module_bytes, wire_bytes);
1440 8 : if (result.is_null()) return false;
1441 :
1442 : job_->module_object_ =
1443 8 : job_->isolate_->global_handles()->Create(*result.ToHandleChecked());
1444 12 : job_->native_module_ = job_->module_object_->shared_native_module();
1445 4 : auto owned_wire_bytes = OwnedVector<uint8_t>::Of(wire_bytes);
1446 8 : job_->wire_bytes_ = ModuleWireBytes(owned_wire_bytes.as_vector());
1447 8 : job_->native_module_->SetWireBytes(std::move(owned_wire_bytes));
1448 4 : job_->FinishCompile();
1449 : return true;
1450 : }
1451 :
1452 1240023 : CompilationStateImpl::CompilationStateImpl(
1453 : NativeModule* native_module, std::shared_ptr<Counters> async_counters)
1454 : : native_module_(native_module),
1455 : background_compile_token_(
1456 : std::make_shared<BackgroundCompileToken>(native_module)),
1457 1239933 : compile_mode_(FLAG_wasm_tier_up &&
1458 2479866 : native_module->module()->origin == kWasmOrigin
1459 : ? CompileMode::kTiering
1460 : : CompileMode::kRegular),
1461 : async_counters_(std::move(async_counters)),
1462 : max_background_tasks_(std::max(
1463 : 1, std::min(FLAG_wasm_num_compilation_tasks,
1464 9920184 : V8::GetCurrentPlatform()->NumberOfWorkerThreads()))) {}
1465 :
1466 3720069 : CompilationStateImpl::~CompilationStateImpl() {
1467 : CompilationError* error = compile_error_.load(std::memory_order_acquire);
1468 1246664 : if (error != nullptr) delete error;
1469 1240023 : }
1470 :
1471 1240212 : void CompilationStateImpl::AbortCompilation() {
1472 1240212 : background_compile_token_->Cancel();
1473 : // No more callbacks after abort.
1474 1240212 : callbacks_.clear();
1475 1240211 : }
1476 :
1477 6243 : void CompilationStateImpl::SetNumberOfFunctionsToCompile(size_t num_functions) {
1478 : DCHECK(!failed());
1479 6243 : base::MutexGuard guard(&mutex_);
1480 6243 : outstanding_baseline_units_ = num_functions;
1481 :
1482 6243 : if (compile_mode_ == CompileMode::kTiering) {
1483 6199 : outstanding_tiering_units_ = num_functions;
1484 : }
1485 6243 : }
1486 :
1487 : void CompilationStateImpl::AddCallback(CompilationState::callback_t callback) {
1488 1429 : callbacks_.emplace_back(std::move(callback));
1489 : }
1490 :
1491 6259 : void CompilationStateImpl::AddCompilationUnits(
1492 : std::vector<std::unique_ptr<WasmCompilationUnit>>& baseline_units,
1493 : std::vector<std::unique_ptr<WasmCompilationUnit>>& tiering_units) {
1494 : {
1495 6259 : base::MutexGuard guard(&mutex_);
1496 :
1497 6259 : if (compile_mode_ == CompileMode::kTiering) {
1498 : DCHECK_EQ(baseline_units.size(), tiering_units.size());
1499 : DCHECK_EQ(tiering_units.back()->requested_tier(),
1500 : ExecutionTier::kOptimized);
1501 : tiering_compilation_units_.insert(
1502 : tiering_compilation_units_.end(),
1503 : std::make_move_iterator(tiering_units.begin()),
1504 6215 : std::make_move_iterator(tiering_units.end()));
1505 : } else {
1506 : DCHECK(tiering_compilation_units_.empty());
1507 : }
1508 :
1509 : baseline_compilation_units_.insert(
1510 : baseline_compilation_units_.end(),
1511 : std::make_move_iterator(baseline_units.begin()),
1512 6259 : std::make_move_iterator(baseline_units.end()));
1513 : }
1514 :
1515 6259 : RestartBackgroundTasks();
1516 6259 : }
1517 :
1518 : std::unique_ptr<WasmCompilationUnit>
1519 1110615 : CompilationStateImpl::GetNextCompilationUnit() {
1520 1110615 : base::MutexGuard guard(&mutex_);
1521 :
1522 : std::vector<std::unique_ptr<WasmCompilationUnit>>& units =
1523 : baseline_compilation_units_.empty() ? tiering_compilation_units_
1524 1110629 : : baseline_compilation_units_;
1525 :
1526 1110629 : if (!units.empty()) {
1527 : std::unique_ptr<WasmCompilationUnit> unit = std::move(units.back());
1528 203986 : units.pop_back();
1529 : return unit;
1530 : }
1531 :
1532 : return std::unique_ptr<WasmCompilationUnit>();
1533 : }
1534 :
1535 203587 : void CompilationStateImpl::OnFinishedUnit(ExecutionTier tier, WasmCode* code) {
1536 : // This mutex guarantees that events happen in the right order.
1537 203587 : base::MutexGuard guard(&mutex_);
1538 :
1539 : // If we are *not* compiling in tiering mode, then all units are counted as
1540 : // baseline units.
1541 203589 : bool is_tiering_mode = compile_mode_ == CompileMode::kTiering;
1542 203589 : bool is_tiering_unit = is_tiering_mode && tier == ExecutionTier::kOptimized;
1543 :
1544 : // Sanity check: If we are not in tiering mode, there cannot be outstanding
1545 : // tiering units.
1546 : DCHECK_IMPLIES(!is_tiering_mode, outstanding_tiering_units_ == 0);
1547 :
1548 203589 : if (is_tiering_unit) {
1549 : DCHECK_LT(0, outstanding_tiering_units_);
1550 101468 : --outstanding_tiering_units_;
1551 101468 : if (outstanding_tiering_units_ == 0) {
1552 : // If baseline compilation has not finished yet, then also trigger
1553 : // {kFinishedBaselineCompilation}.
1554 5839 : if (outstanding_baseline_units_ > 0) {
1555 442 : NotifyOnEvent(CompilationEvent::kFinishedBaselineCompilation);
1556 : }
1557 5839 : NotifyOnEvent(CompilationEvent::kFinishedTopTierCompilation);
1558 : }
1559 : } else {
1560 : DCHECK_LT(0, outstanding_baseline_units_);
1561 102121 : --outstanding_baseline_units_;
1562 102121 : if (outstanding_baseline_units_ == 0) {
1563 5928 : NotifyOnEvent(CompilationEvent::kFinishedBaselineCompilation);
1564 : // If we are not tiering, then we also trigger the "top tier finished"
1565 : // event when baseline compilation is finished.
1566 5928 : if (!is_tiering_mode) {
1567 44 : NotifyOnEvent(CompilationEvent::kFinishedTopTierCompilation);
1568 : }
1569 : }
1570 : }
1571 :
1572 203590 : if (code != nullptr) native_module_->engine()->LogCode(code);
1573 203590 : }
1574 :
1575 31417 : void CompilationStateImpl::RestartBackgroundCompileTask() {
1576 : auto task =
1577 : native_module_->engine()->NewBackgroundCompileTask<BackgroundCompileTask>(
1578 31417 : background_compile_token_, async_counters_);
1579 :
1580 31417 : if (baseline_compilation_finished()) {
1581 14226 : V8::GetCurrentPlatform()->CallLowPriorityTaskOnWorkerThread(
1582 42678 : std::move(task));
1583 : } else {
1584 51573 : V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
1585 : }
1586 31417 : }
1587 :
1588 26 : void CompilationStateImpl::ReportDetectedFeatures(
1589 : const WasmFeatures& detected) {
1590 26 : base::MutexGuard guard(&mutex_);
1591 26 : UnionFeaturesInto(&detected_features_, detected);
1592 26 : }
1593 :
1594 30474 : void CompilationStateImpl::OnBackgroundTaskStopped(
1595 : const WasmFeatures& detected) {
1596 30474 : base::MutexGuard guard(&mutex_);
1597 : DCHECK_LE(1, num_background_tasks_);
1598 30475 : --num_background_tasks_;
1599 30475 : UnionFeaturesInto(&detected_features_, detected);
1600 30476 : }
1601 :
1602 7014 : void CompilationStateImpl::PublishDetectedFeatures(
1603 : Isolate* isolate, const WasmFeatures& detected) {
1604 : // Notifying the isolate of the feature counts must take place under
1605 : // the mutex, because even if we have finished baseline compilation,
1606 : // tiering compilations may still occur in the background.
1607 7014 : base::MutexGuard guard(&mutex_);
1608 7014 : UnionFeaturesInto(&detected_features_, detected);
1609 : UpdateFeatureUseCounts(isolate, detected_features_);
1610 7014 : }
1611 :
1612 6259 : void CompilationStateImpl::RestartBackgroundTasks() {
1613 : int num_restart;
1614 : {
1615 6259 : base::MutexGuard guard(&mutex_);
1616 : // No need to restart tasks if compilation already failed.
1617 6259 : if (failed()) return;
1618 :
1619 : DCHECK_LE(num_background_tasks_, max_background_tasks_);
1620 6259 : if (num_background_tasks_ == max_background_tasks_) return;
1621 : size_t num_compilation_units =
1622 18777 : baseline_compilation_units_.size() + tiering_compilation_units_.size();
1623 6259 : num_restart = max_background_tasks_ - num_background_tasks_;
1624 : DCHECK_LE(0, num_restart);
1625 6259 : if (num_compilation_units < static_cast<size_t>(num_restart)) {
1626 4066 : num_restart = static_cast<int>(num_compilation_units);
1627 : }
1628 6259 : num_background_tasks_ += num_restart;
1629 : }
1630 :
1631 31391 : for (; num_restart > 0; --num_restart) {
1632 31391 : RestartBackgroundCompileTask();
1633 : }
1634 : }
1635 :
1636 6646 : void CompilationStateImpl::SetError(uint32_t func_index,
1637 : const WasmError& error) {
1638 : DCHECK(error.has_error());
1639 : std::unique_ptr<CompilationError> compile_error =
1640 6646 : base::make_unique<CompilationError>(func_index, error);
1641 6646 : CompilationError* expected = nullptr;
1642 : bool set = compile_error_.compare_exchange_strong(
1643 6646 : expected, compile_error.get(), std::memory_order_acq_rel);
1644 : // Ignore all but the first error. If the previous value is not nullptr, just
1645 : // return (and free the allocated error).
1646 13292 : if (!set) return;
1647 : // If set successfully, give up ownership.
1648 : compile_error.release();
1649 : // Schedule a foreground task to call the callback and notify users about the
1650 : // compile error.
1651 6641 : NotifyOnEvent(CompilationEvent::kFailedCompilation);
1652 : }
1653 :
1654 18893 : void CompilationStateImpl::NotifyOnEvent(CompilationEvent event) {
1655 40431 : for (auto& callback : callbacks_) callback(event);
1656 : // If no more events are expected after this one, clear the callbacks to free
1657 : // memory. We can safely do this here, as this method is only called from
1658 : // foreground tasks.
1659 18894 : if (event >= CompilationEvent::kFirstFinalEvent) callbacks_.clear();
1660 18895 : }
1661 :
1662 139036 : void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module,
1663 : Handle<FixedArray> export_wrappers) {
1664 : JSToWasmWrapperCache js_to_wasm_cache;
1665 : int wrapper_index = 0;
1666 :
1667 : // TODO(6792): Wrappers below are allocated with {Factory::NewCode}. As an
1668 : // optimization we keep the code space unlocked to avoid repeated unlocking
1669 : // because many such wrapper are allocated in sequence below.
1670 278068 : CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
1671 500470 : for (auto exp : module->export_table) {
1672 222399 : if (exp.kind != kExternalFunction) continue;
1673 219837 : auto& function = module->functions[exp.index];
1674 : Handle<Code> wrapper_code = js_to_wasm_cache.GetOrCompileJSToWasmWrapper(
1675 219837 : isolate, function.sig, function.imported);
1676 439674 : export_wrappers->set(wrapper_index, *wrapper_code);
1677 219837 : RecordStats(*wrapper_code, isolate->counters());
1678 219836 : ++wrapper_index;
1679 : }
1680 139036 : }
1681 :
1682 136597 : Handle<Script> CreateWasmScript(Isolate* isolate,
1683 : const ModuleWireBytes& wire_bytes,
1684 : const std::string& source_map_url) {
1685 : Handle<Script> script =
1686 136597 : isolate->factory()->NewScript(isolate->factory()->empty_string());
1687 409791 : script->set_context_data(isolate->native_context()->debug_context_id());
1688 : script->set_type(Script::TYPE_WASM);
1689 :
1690 : int hash = StringHasher::HashSequentialString(
1691 : reinterpret_cast<const char*>(wire_bytes.start()),
1692 136597 : static_cast<int>(wire_bytes.length()), kZeroHashSeed);
1693 :
1694 : const int kBufferSize = 32;
1695 : char buffer[kBufferSize];
1696 :
1697 136597 : int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash);
1698 : DCHECK(name_chars >= 0 && name_chars < kBufferSize);
1699 : MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte(
1700 136597 : VectorOf(reinterpret_cast<uint8_t*>(buffer), name_chars), TENURED);
1701 273194 : script->set_name(*name_str.ToHandleChecked());
1702 :
1703 136597 : if (source_map_url.size() != 0) {
1704 : MaybeHandle<String> src_map_str = isolate->factory()->NewStringFromUtf8(
1705 4 : CStrVector(source_map_url.c_str()), TENURED);
1706 8 : script->set_source_mapping_url(*src_map_str.ToHandleChecked());
1707 : }
1708 136597 : return script;
1709 : }
1710 :
1711 : } // namespace wasm
1712 : } // namespace internal
1713 178779 : } // namespace v8
1714 :
1715 : #undef TRACE_COMPILE
1716 : #undef TRACE_STREAMING
1717 : #undef TRACE_LAZY
|