Line data Source code
1 : // Copyright 2018 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/wasm-engine.h"
6 :
7 : #include "src/code-tracer.h"
8 : #include "src/compilation-statistics.h"
9 : #include "src/counters.h"
10 : #include "src/objects-inl.h"
11 : #include "src/objects/heap-number.h"
12 : #include "src/objects/js-promise.h"
13 : #include "src/ostreams.h"
14 : #include "src/wasm/function-compiler.h"
15 : #include "src/wasm/module-compiler.h"
16 : #include "src/wasm/module-decoder.h"
17 : #include "src/wasm/module-instantiate.h"
18 : #include "src/wasm/streaming-decoder.h"
19 : #include "src/wasm/wasm-objects-inl.h"
20 :
21 : namespace v8 {
22 : namespace internal {
23 : namespace wasm {
24 :
25 : namespace {
26 : // A task to log a set of {WasmCode} objects in an isolate. It does not own any
27 : // data itself, since it is owned by the platform, so lifetime is not really
28 : // bound to the wasm engine.
29 : class LogCodesTask : public Task {
30 : public:
31 : LogCodesTask(base::Mutex* mutex, LogCodesTask** task_slot, Isolate* isolate,
32 : WasmEngine* engine)
33 : : mutex_(mutex),
34 : task_slot_(task_slot),
35 : isolate_(isolate),
36 4 : engine_(engine) {
37 : DCHECK_NOT_NULL(task_slot);
38 : DCHECK_NOT_NULL(isolate);
39 : }
40 :
41 8 : ~LogCodesTask() {
42 : // If the platform deletes this task before executing it, we also deregister
43 : // it to avoid use-after-free from still-running background threads.
44 4 : if (!cancelled()) DeregisterTask();
45 4 : }
46 :
47 0 : void Run() override {
48 0 : if (cancelled()) return;
49 0 : DeregisterTask();
50 0 : engine_->LogOutstandingCodesForIsolate(isolate_);
51 : }
52 :
53 : void Cancel() {
54 : // Cancel will only be called on Isolate shutdown, which happens on the
55 : // Isolate's foreground thread. Thus no synchronization needed.
56 4 : isolate_ = nullptr;
57 : }
58 :
59 : bool cancelled() const { return isolate_ == nullptr; }
60 :
61 0 : void DeregisterTask() {
62 : // The task will only be deregistered from the foreground thread (executing
63 : // this task or calling its destructor), thus we do not need synchronization
64 : // on this field access.
65 0 : if (task_slot_ == nullptr) return; // already deregistered.
66 : // Remove this task from the {IsolateInfo} in the engine. The next
67 : // logging request will allocate and schedule a new task.
68 0 : base::MutexGuard guard(mutex_);
69 : DCHECK_EQ(this, *task_slot_);
70 0 : *task_slot_ = nullptr;
71 0 : task_slot_ = nullptr;
72 : }
73 :
74 : private:
75 : // The mutex of the WasmEngine.
76 : base::Mutex* const mutex_;
77 : // The slot in the WasmEngine where this LogCodesTask is stored. This is
78 : // cleared by this task before execution or on task destruction.
79 : LogCodesTask** task_slot_;
80 : Isolate* isolate_;
81 : WasmEngine* const engine_;
82 : };
83 :
84 0 : class WasmGCForegroundTask : public Task {
85 : public:
86 0 : explicit WasmGCForegroundTask(Isolate* isolate) : isolate_(isolate) {
87 : DCHECK_NOT_NULL(isolate);
88 : }
89 :
90 0 : void Run() final {
91 0 : if (isolate_ == nullptr) return; // cancelled.
92 : WasmEngine* engine = isolate_->wasm_engine();
93 : // If the foreground task is executing, there is no wasm code active. Just
94 : // report an empty set of live wasm code.
95 0 : engine->ReportLiveCodeForGC(isolate_, Vector<WasmCode*>{});
96 : }
97 :
98 0 : void Cancel() { isolate_ = nullptr; }
99 :
100 : private:
101 : Isolate* isolate_;
102 : };
103 :
104 : } // namespace
105 :
106 0 : struct WasmEngine::CurrentGCInfo {
107 : // Set of isolates that did not scan their stack yet for used WasmCode, and
108 : // their scheduled foreground task.
109 : std::unordered_map<Isolate*, WasmGCForegroundTask*> outstanding_isolates;
110 :
111 : // Set of dead code. Filled with all potentially dead code on initialization.
112 : // Code that is still in-use is removed by the individual isolates.
113 : std::unordered_set<WasmCode*> dead_code;
114 : };
115 :
116 124822 : struct WasmEngine::IsolateInfo {
117 62426 : explicit IsolateInfo(Isolate* isolate)
118 62426 : : log_codes(WasmCode::ShouldBeLogged(isolate)) {
119 : v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
120 62426 : v8::Platform* platform = V8::GetCurrentPlatform();
121 124852 : foreground_task_runner = platform->GetForegroundTaskRunner(v8_isolate);
122 62426 : }
123 :
124 : #ifdef DEBUG
125 : ~IsolateInfo() {
126 : // Before destructing, the {WasmEngine} must have cleared outstanding code
127 : // to log.
128 : DCHECK_EQ(0, code_to_log.size());
129 : }
130 : #endif
131 :
132 : // All native modules that are being used by this Isolate (currently only
133 : // grows, never shrinks).
134 : std::set<NativeModule*> native_modules;
135 :
136 : // Caches whether code needs to be logged on this isolate.
137 : bool log_codes;
138 :
139 : // The currently scheduled LogCodesTask.
140 : LogCodesTask* log_codes_task = nullptr;
141 :
142 : // The vector of code objects that still need to be logged in this isolate.
143 : std::vector<WasmCode*> code_to_log;
144 :
145 : // The foreground task runner of the isolate (can be called from background).
146 : std::shared_ptr<v8::TaskRunner> foreground_task_runner;
147 : };
148 :
149 1242981 : struct WasmEngine::NativeModuleInfo {
150 : // Set of isolates using this NativeModule.
151 : std::unordered_set<Isolate*> isolates;
152 :
153 : // Set of potentially dead code. The ref-count of these code objects was
154 : // incremented for each Isolate that might still execute the code, and is
155 : // decremented on {RemoveIsolate} or on a GC.
156 : std::unordered_set<WasmCode*> potentially_dead_code;
157 : };
158 :
159 61028 : WasmEngine::WasmEngine()
160 244112 : : code_manager_(&memory_tracker_, FLAG_wasm_max_code_space * MB) {}
161 :
162 239492 : WasmEngine::~WasmEngine() {
163 : // Synchronize on all background compile tasks.
164 59873 : background_compile_task_manager_.CancelAndWait();
165 : // All AsyncCompileJobs have been canceled.
166 : DCHECK(async_compile_jobs_.empty());
167 : // All Isolates have been deregistered.
168 : DCHECK(isolates_.empty());
169 : // All NativeModules did die.
170 : DCHECK(native_modules_.empty());
171 59873 : }
172 :
173 127850 : bool WasmEngine::SyncValidate(Isolate* isolate, const WasmFeatures& enabled,
174 : const ModuleWireBytes& bytes) {
175 : // TODO(titzer): remove dependency on the isolate.
176 255700 : if (bytes.start() == nullptr || bytes.length() == 0) return false;
177 : ModuleResult result =
178 255700 : DecodeWasmModule(enabled, bytes.start(), bytes.end(), true, kWasmOrigin,
179 255700 : isolate->counters(), allocator());
180 : return result.ok();
181 : }
182 :
183 2469 : MaybeHandle<AsmWasmData> WasmEngine::SyncCompileTranslatedAsmJs(
184 : Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
185 : Vector<const byte> asm_js_offset_table_bytes,
186 : Handle<HeapNumber> uses_bitset) {
187 : ModuleResult result =
188 4938 : DecodeWasmModule(kAsmjsWasmFeatures, bytes.start(), bytes.end(), false,
189 4938 : kAsmJsOrigin, isolate->counters(), allocator());
190 2469 : if (result.failed()) {
191 : // This happens once in a while when we have missed some limit check
192 : // in the asm parser. Output an error message to help diagnose, but crash.
193 : std::cout << result.error().message();
194 0 : UNREACHABLE();
195 : }
196 :
197 : // Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
198 : // in {CompileToNativeModule}.
199 : Handle<FixedArray> export_wrappers;
200 : std::shared_ptr<NativeModule> native_module =
201 : CompileToNativeModule(isolate, kAsmjsWasmFeatures, thrower,
202 4938 : std::move(result).value(), bytes, &export_wrappers);
203 2469 : if (!native_module) return {};
204 :
205 : // Create heap objects for asm.js offset table to be stored in the module
206 : // object.
207 : Handle<ByteArray> asm_js_offset_table =
208 2469 : isolate->factory()->NewByteArray(asm_js_offset_table_bytes.length());
209 : asm_js_offset_table->copy_in(0, asm_js_offset_table_bytes.start(),
210 : asm_js_offset_table_bytes.length());
211 :
212 : return AsmWasmData::New(isolate, std::move(native_module), export_wrappers,
213 4938 : asm_js_offset_table, uses_bitset);
214 : }
215 :
216 5018 : Handle<WasmModuleObject> WasmEngine::FinalizeTranslatedAsmJs(
217 : Isolate* isolate, Handle<AsmWasmData> asm_wasm_data,
218 : Handle<Script> script) {
219 : std::shared_ptr<NativeModule> native_module =
220 : asm_wasm_data->managed_native_module()->get();
221 : Handle<FixedArray> export_wrappers =
222 5018 : handle(asm_wasm_data->export_wrappers(), isolate);
223 : size_t code_size_estimate =
224 : wasm::WasmCodeManager::EstimateNativeModuleCodeSize(
225 5018 : native_module->module());
226 :
227 : Handle<WasmModuleObject> module_object =
228 : WasmModuleObject::New(isolate, std::move(native_module), script,
229 10036 : export_wrappers, code_size_estimate);
230 5018 : module_object->set_asm_js_offset_table(asm_wasm_data->asm_js_offset_table());
231 10036 : return module_object;
232 : }
233 :
234 151958 : MaybeHandle<WasmModuleObject> WasmEngine::SyncCompile(
235 : Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
236 : const ModuleWireBytes& bytes) {
237 : ModuleResult result =
238 303915 : DecodeWasmModule(enabled, bytes.start(), bytes.end(), false, kWasmOrigin,
239 303919 : isolate->counters(), allocator());
240 151957 : if (result.failed()) {
241 : thrower->CompileFailed(result.error());
242 9082 : return {};
243 : }
244 :
245 : // Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
246 : // in {CompileToModuleObject}.
247 : Handle<FixedArray> export_wrappers;
248 : std::shared_ptr<NativeModule> native_module =
249 : CompileToNativeModule(isolate, enabled, thrower,
250 285754 : std::move(result).value(), bytes, &export_wrappers);
251 142879 : if (!native_module) return {};
252 :
253 : Handle<Script> script =
254 135358 : CreateWasmScript(isolate, bytes, native_module->module()->source_map_url);
255 : size_t code_size_estimate =
256 : wasm::WasmCodeManager::EstimateNativeModuleCodeSize(
257 135358 : native_module->module());
258 :
259 : // Create the module object.
260 : // TODO(clemensh): For the same module (same bytes / same hash), we should
261 : // only have one WasmModuleObject. Otherwise, we might only set
262 : // breakpoints on a (potentially empty) subset of the instances.
263 :
264 : // Create the compiled module object and populate with compiled functions
265 : // and information needed at instantiation time. This object needs to be
266 : // serializable. Instantiation may occur off a deserialized version of this
267 : // object.
268 : Handle<WasmModuleObject> module_object =
269 : WasmModuleObject::New(isolate, std::move(native_module), script,
270 270716 : export_wrappers, code_size_estimate);
271 :
272 : // Finish the Wasm script now and make it public to the debugger.
273 135358 : isolate->debug()->OnAfterCompile(script);
274 135358 : return module_object;
275 : }
276 :
277 134923 : MaybeHandle<WasmInstanceObject> WasmEngine::SyncInstantiate(
278 : Isolate* isolate, ErrorThrower* thrower,
279 : Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
280 : MaybeHandle<JSArrayBuffer> memory) {
281 : return InstantiateToInstanceObject(isolate, thrower, module_object, imports,
282 137375 : memory);
283 : }
284 :
285 2452 : void WasmEngine::AsyncInstantiate(
286 : Isolate* isolate, std::unique_ptr<InstantiationResultResolver> resolver,
287 : Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports) {
288 1120 : ErrorThrower thrower(isolate, "WebAssembly.instantiate()");
289 : // Instantiate a TryCatch so that caught exceptions won't progagate out.
290 : // They will still be set as pending exceptions on the isolate.
291 : // TODO(clemensh): Avoid TryCatch, use Execution::TryCall internally to invoke
292 : // start function and report thrown exception explicitly via out argument.
293 3572 : v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
294 2452 : catcher.SetVerbose(false);
295 2452 : catcher.SetCaptureMessage(false);
296 :
297 : MaybeHandle<WasmInstanceObject> instance_object = SyncInstantiate(
298 : isolate, &thrower, module_object, imports, Handle<JSArrayBuffer>::null());
299 :
300 2452 : if (!instance_object.is_null()) {
301 2664 : resolver->OnInstantiationSucceeded(instance_object.ToHandleChecked());
302 1332 : return;
303 : }
304 :
305 1120 : if (isolate->has_pending_exception()) {
306 : // The JS code executed during instantiation has thrown an exception.
307 : // We have to move the exception to the promise chain.
308 : Handle<Object> exception(isolate->pending_exception(), isolate);
309 : isolate->clear_pending_exception();
310 48 : *isolate->external_caught_exception_address() = false;
311 48 : resolver->OnInstantiationFailed(exception);
312 48 : thrower.Reset();
313 : } else {
314 : DCHECK(thrower.error());
315 1072 : resolver->OnInstantiationFailed(thrower.Reify());
316 : }
317 : }
318 :
319 2395 : void WasmEngine::AsyncCompile(
320 : Isolate* isolate, const WasmFeatures& enabled,
321 : std::shared_ptr<CompilationResultResolver> resolver,
322 : const ModuleWireBytes& bytes, bool is_shared) {
323 2395 : if (!FLAG_wasm_async_compilation) {
324 : // Asynchronous compilation disabled; fall back on synchronous compilation.
325 0 : ErrorThrower thrower(isolate, "WasmCompile");
326 : MaybeHandle<WasmModuleObject> module_object;
327 0 : if (is_shared) {
328 : // Make a copy of the wire bytes to avoid concurrent modification.
329 0 : std::unique_ptr<uint8_t[]> copy(new uint8_t[bytes.length()]);
330 : memcpy(copy.get(), bytes.start(), bytes.length());
331 0 : ModuleWireBytes bytes_copy(copy.get(), copy.get() + bytes.length());
332 0 : module_object = SyncCompile(isolate, enabled, &thrower, bytes_copy);
333 : } else {
334 : // The wire bytes are not shared, OK to use them directly.
335 0 : module_object = SyncCompile(isolate, enabled, &thrower, bytes);
336 : }
337 0 : if (thrower.error()) {
338 0 : resolver->OnCompilationFailed(thrower.Reify());
339 0 : return;
340 : }
341 0 : Handle<WasmModuleObject> module = module_object.ToHandleChecked();
342 0 : resolver->OnCompilationSucceeded(module);
343 0 : return;
344 : }
345 :
346 2395 : if (FLAG_wasm_test_streaming) {
347 : std::shared_ptr<StreamingDecoder> streaming_decoder =
348 : StartStreamingCompilation(isolate, enabled,
349 : handle(isolate->context(), isolate),
350 400 : std::move(resolver));
351 200 : streaming_decoder->OnBytesReceived(bytes.module_bytes());
352 200 : streaming_decoder->Finish();
353 : return;
354 : }
355 : // Make a copy of the wire bytes in case the user program changes them
356 : // during asynchronous compilation.
357 2195 : std::unique_ptr<byte[]> copy(new byte[bytes.length()]);
358 : memcpy(copy.get(), bytes.start(), bytes.length());
359 :
360 4390 : AsyncCompileJob* job = CreateAsyncCompileJob(
361 : isolate, enabled, std::move(copy), bytes.length(),
362 2195 : handle(isolate->context(), isolate), std::move(resolver));
363 2195 : job->Start();
364 : }
365 :
366 452 : std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation(
367 : Isolate* isolate, const WasmFeatures& enabled, Handle<Context> context,
368 : std::shared_ptr<CompilationResultResolver> resolver) {
369 : AsyncCompileJob* job =
370 904 : CreateAsyncCompileJob(isolate, enabled, std::unique_ptr<byte[]>(nullptr),
371 452 : 0, context, std::move(resolver));
372 452 : return job->CreateStreamingDecoder();
373 : }
374 :
375 16 : void WasmEngine::CompileFunction(Isolate* isolate, NativeModule* native_module,
376 : uint32_t function_index, ExecutionTier tier) {
377 : // Note we assume that "one-off" compilations can discard detected features.
378 16 : WasmFeatures detected = kNoWasmFeatures;
379 : WasmCompilationUnit::CompileWasmFunction(
380 : isolate, native_module, &detected,
381 32 : &native_module->module()->functions[function_index], tier);
382 16 : }
383 :
384 0 : std::shared_ptr<NativeModule> WasmEngine::ExportNativeModule(
385 : Handle<WasmModuleObject> module_object) {
386 0 : return module_object->shared_native_module();
387 : }
388 :
389 156 : Handle<WasmModuleObject> WasmEngine::ImportNativeModule(
390 : Isolate* isolate, std::shared_ptr<NativeModule> shared_native_module) {
391 156 : NativeModule* native_module = shared_native_module.get();
392 : ModuleWireBytes wire_bytes(native_module->wire_bytes());
393 : const WasmModule* module = native_module->module();
394 : Handle<Script> script =
395 156 : CreateWasmScript(isolate, wire_bytes, module->source_map_url);
396 156 : size_t code_size = native_module->committed_code_space();
397 : Handle<WasmModuleObject> module_object = WasmModuleObject::New(
398 312 : isolate, std::move(shared_native_module), script, code_size);
399 312 : CompileJsToWasmWrappers(isolate, native_module->module(),
400 156 : handle(module_object->export_wrappers(), isolate));
401 : {
402 156 : base::MutexGuard lock(&mutex_);
403 : DCHECK_EQ(1, isolates_.count(isolate));
404 : isolates_[isolate]->native_modules.insert(native_module);
405 : DCHECK_EQ(1, native_modules_.count(native_module));
406 : native_modules_[native_module]->isolates.insert(isolate);
407 : }
408 156 : return module_object;
409 : }
410 :
411 0 : CompilationStatistics* WasmEngine::GetOrCreateTurboStatistics() {
412 0 : base::MutexGuard guard(&mutex_);
413 0 : if (compilation_stats_ == nullptr) {
414 0 : compilation_stats_.reset(new CompilationStatistics());
415 : }
416 0 : return compilation_stats_.get();
417 : }
418 :
419 0 : void WasmEngine::DumpAndResetTurboStatistics() {
420 0 : base::MutexGuard guard(&mutex_);
421 0 : if (compilation_stats_ != nullptr) {
422 0 : StdoutStream os;
423 0 : os << AsPrintableStatistics{*compilation_stats_.get(), false} << std::endl;
424 : }
425 0 : compilation_stats_.reset();
426 0 : }
427 :
428 64 : CodeTracer* WasmEngine::GetCodeTracer() {
429 64 : base::MutexGuard guard(&mutex_);
430 68 : if (code_tracer_ == nullptr) code_tracer_.reset(new CodeTracer(-1));
431 64 : return code_tracer_.get();
432 : }
433 :
434 2647 : AsyncCompileJob* WasmEngine::CreateAsyncCompileJob(
435 : Isolate* isolate, const WasmFeatures& enabled,
436 : std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
437 : std::shared_ptr<CompilationResultResolver> resolver) {
438 : AsyncCompileJob* job =
439 : new AsyncCompileJob(isolate, enabled, std::move(bytes_copy), length,
440 10588 : context, std::move(resolver));
441 : // Pass ownership to the unique_ptr in {async_compile_jobs_}.
442 2647 : base::MutexGuard guard(&mutex_);
443 5294 : async_compile_jobs_[job] = std::unique_ptr<AsyncCompileJob>(job);
444 5294 : return job;
445 : }
446 :
447 2636 : std::unique_ptr<AsyncCompileJob> WasmEngine::RemoveCompileJob(
448 : AsyncCompileJob* job) {
449 2636 : base::MutexGuard guard(&mutex_);
450 : auto item = async_compile_jobs_.find(job);
451 : DCHECK(item != async_compile_jobs_.end());
452 : std::unique_ptr<AsyncCompileJob> result = std::move(item->second);
453 : async_compile_jobs_.erase(item);
454 2636 : return result;
455 : }
456 :
457 56015 : bool WasmEngine::HasRunningCompileJob(Isolate* isolate) {
458 56015 : base::MutexGuard guard(&mutex_);
459 : DCHECK_EQ(1, isolates_.count(isolate));
460 56015 : for (auto& entry : async_compile_jobs_) {
461 3118 : if (entry.first->isolate() == isolate) return true;
462 : }
463 : return false;
464 : }
465 :
466 62420 : void WasmEngine::DeleteCompileJobsOnIsolate(Isolate* isolate) {
467 : // Under the mutex get all jobs to delete. Then delete them without holding
468 : // the mutex, such that deletion can reenter the WasmEngine.
469 62421 : std::vector<std::unique_ptr<AsyncCompileJob>> jobs_to_delete;
470 : {
471 62420 : base::MutexGuard guard(&mutex_);
472 : DCHECK_EQ(1, isolates_.count(isolate));
473 62435 : for (auto it = async_compile_jobs_.begin();
474 : it != async_compile_jobs_.end();) {
475 14 : if (it->first->isolate() != isolate) {
476 : ++it;
477 : continue;
478 : }
479 11 : jobs_to_delete.push_back(std::move(it->second));
480 : it = async_compile_jobs_.erase(it);
481 : }
482 : }
483 62421 : }
484 :
485 62426 : void WasmEngine::AddIsolate(Isolate* isolate) {
486 62426 : base::MutexGuard guard(&mutex_);
487 : DCHECK_EQ(0, isolates_.count(isolate));
488 62426 : isolates_.emplace(isolate, base::make_unique<IsolateInfo>(isolate));
489 :
490 : // Install sampling GC callback.
491 : // TODO(v8:7424): For now we sample module sizes in a GC callback. This will
492 : // bias samples towards apps with high memory pressure. We should switch to
493 : // using sampling based on regular intervals independent of the GC.
494 : auto callback = [](v8::Isolate* v8_isolate, v8::GCType type,
495 150650 : v8::GCCallbackFlags flags, void* data) {
496 75325 : Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
497 : Counters* counters = isolate->counters();
498 : WasmEngine* engine = isolate->wasm_engine();
499 75325 : base::MutexGuard lock(&engine->mutex_);
500 : DCHECK_EQ(1, engine->isolates_.count(isolate));
501 770149 : for (auto* native_module : engine->isolates_[isolate]->native_modules) {
502 694824 : native_module->SampleCodeSize(counters, NativeModule::kSampling);
503 : }
504 150650 : };
505 62426 : isolate->heap()->AddGCEpilogueCallback(callback, v8::kGCTypeMarkSweepCompact,
506 62426 : nullptr);
507 62426 : }
508 :
509 62411 : void WasmEngine::RemoveIsolate(Isolate* isolate) {
510 62411 : base::MutexGuard guard(&mutex_);
511 : auto it = isolates_.find(isolate);
512 : DCHECK_NE(isolates_.end(), it);
513 62411 : std::unique_ptr<IsolateInfo> info = std::move(it->second);
514 : isolates_.erase(it);
515 62593 : for (NativeModule* native_module : info->native_modules) {
516 : DCHECK_EQ(1, native_modules_.count(native_module));
517 : DCHECK_EQ(1, native_modules_[native_module]->isolates.count(isolate));
518 : auto* info = native_modules_[native_module].get();
519 : info->isolates.erase(isolate);
520 182 : if (current_gc_info_) {
521 : auto it = current_gc_info_->outstanding_isolates.find(isolate);
522 0 : if (it != current_gc_info_->outstanding_isolates.end()) {
523 0 : if (auto* gc_task = it->second) gc_task->Cancel();
524 : current_gc_info_->outstanding_isolates.erase(it);
525 : }
526 0 : for (WasmCode* code : info->potentially_dead_code) {
527 : current_gc_info_->dead_code.erase(code);
528 : }
529 : }
530 : }
531 62411 : if (auto* task = info->log_codes_task) task->Cancel();
532 62411 : if (!info->code_to_log.empty()) {
533 0 : WasmCode::DecrementRefCount(VectorOf(info->code_to_log));
534 : info->code_to_log.clear();
535 : }
536 62411 : }
537 :
538 323612 : void WasmEngine::LogCode(WasmCode* code) {
539 323612 : base::MutexGuard guard(&mutex_);
540 323674 : NativeModule* native_module = code->native_module();
541 : DCHECK_EQ(1, native_modules_.count(native_module));
542 646337 : for (Isolate* isolate : native_modules_[native_module]->isolates) {
543 : DCHECK_EQ(1, isolates_.count(isolate));
544 : IsolateInfo* info = isolates_[isolate].get();
545 322663 : if (info->log_codes == false) continue;
546 7 : if (info->log_codes_task == nullptr) {
547 : auto new_task = base::make_unique<LogCodesTask>(
548 4 : &mutex_, &info->log_codes_task, isolate, this);
549 4 : info->log_codes_task = new_task.get();
550 12 : info->foreground_task_runner->PostTask(std::move(new_task));
551 4 : isolate->stack_guard()->RequestLogWasmCode();
552 : }
553 7 : info->code_to_log.push_back(code);
554 7 : code->IncRef();
555 : }
556 323673 : }
557 :
558 812 : void WasmEngine::EnableCodeLogging(Isolate* isolate) {
559 812 : base::MutexGuard guard(&mutex_);
560 : auto it = isolates_.find(isolate);
561 : DCHECK_NE(isolates_.end(), it);
562 813 : it->second->log_codes = true;
563 813 : }
564 :
565 4 : void WasmEngine::LogOutstandingCodesForIsolate(Isolate* isolate) {
566 : // If by now we should not log code any more, do not log it.
567 4 : if (!WasmCode::ShouldBeLogged(isolate)) return;
568 :
569 : // Under the mutex, get the vector of wasm code to log. Then log and decrement
570 : // the ref count without holding the mutex.
571 : std::vector<WasmCode*> code_to_log;
572 : {
573 4 : base::MutexGuard guard(&mutex_);
574 : DCHECK_EQ(1, isolates_.count(isolate));
575 : code_to_log.swap(isolates_[isolate]->code_to_log);
576 : }
577 4 : if (code_to_log.empty()) return;
578 11 : for (WasmCode* code : code_to_log) {
579 7 : code->LogCode(isolate);
580 : }
581 4 : WasmCode::DecrementRefCount(VectorOf(code_to_log));
582 : }
583 :
584 1242977 : std::shared_ptr<NativeModule> WasmEngine::NewNativeModule(
585 : Isolate* isolate, const WasmFeatures& enabled, size_t code_size_estimate,
586 : bool can_request_more, std::shared_ptr<const WasmModule> module) {
587 : std::shared_ptr<NativeModule> native_module =
588 : code_manager_.NewNativeModule(this, isolate, enabled, code_size_estimate,
589 2485958 : can_request_more, std::move(module));
590 1242981 : base::MutexGuard lock(&mutex_);
591 1242981 : auto pair = native_modules_.insert(std::make_pair(
592 2485962 : native_module.get(), base::make_unique<NativeModuleInfo>()));
593 : DCHECK(pair.second); // inserted new entry.
594 : pair.first->second.get()->isolates.insert(isolate);
595 2485962 : isolates_[isolate]->native_modules.insert(native_module.get());
596 1242981 : return native_module;
597 : }
598 :
599 1242981 : void WasmEngine::FreeNativeModule(NativeModule* native_module) {
600 : {
601 1242981 : base::MutexGuard guard(&mutex_);
602 : auto it = native_modules_.find(native_module);
603 : DCHECK_NE(native_modules_.end(), it);
604 2485932 : for (Isolate* isolate : it->second->isolates) {
605 : DCHECK_EQ(1, isolates_.count(isolate));
606 : DCHECK_EQ(1, isolates_[isolate]->native_modules.count(native_module));
607 : isolates_[isolate]->native_modules.erase(native_module);
608 : }
609 : native_modules_.erase(it);
610 : }
611 1242981 : code_manager_.FreeNativeModule(native_module);
612 1242981 : }
613 :
614 : namespace {
615 480 : class SampleTopTierCodeSizeTask : public CancelableTask {
616 : public:
617 : SampleTopTierCodeSizeTask(Isolate* isolate,
618 : std::weak_ptr<NativeModule> native_module)
619 : : CancelableTask(isolate),
620 : isolate_(isolate),
621 160 : native_module_(std::move(native_module)) {}
622 :
623 158 : void RunInternal() override {
624 158 : if (std::shared_ptr<NativeModule> native_module = native_module_.lock()) {
625 154 : native_module->SampleCodeSize(isolate_->counters(),
626 154 : NativeModule::kAfterTopTier);
627 : }
628 158 : }
629 :
630 : private:
631 : Isolate* const isolate_;
632 : const std::weak_ptr<NativeModule> native_module_;
633 : };
634 : } // namespace
635 :
636 163 : void WasmEngine::SampleTopTierCodeSizeInAllIsolates(
637 : const std::shared_ptr<NativeModule>& native_module) {
638 163 : base::MutexGuard lock(&mutex_);
639 : DCHECK_EQ(1, native_modules_.count(native_module.get()));
640 326 : for (Isolate* isolate : native_modules_[native_module.get()]->isolates) {
641 : DCHECK_EQ(1, isolates_.count(isolate));
642 : IsolateInfo* info = isolates_[isolate].get();
643 160 : info->foreground_task_runner->PostTask(
644 480 : base::make_unique<SampleTopTierCodeSizeTask>(isolate, native_module));
645 : }
646 163 : }
647 :
648 0 : void WasmEngine::ReportLiveCodeForGC(Isolate* isolate,
649 : Vector<WasmCode*> live_code) {
650 0 : base::MutexGuard guard(&mutex_);
651 : DCHECK_NOT_NULL(current_gc_info_);
652 : auto outstanding_isolate_it =
653 : current_gc_info_->outstanding_isolates.find(isolate);
654 : DCHECK_NE(current_gc_info_->outstanding_isolates.end(),
655 : outstanding_isolate_it);
656 0 : auto* fg_task = outstanding_isolate_it->second;
657 0 : if (fg_task) fg_task->Cancel();
658 : current_gc_info_->outstanding_isolates.erase(outstanding_isolate_it);
659 0 : for (WasmCode* code : live_code) current_gc_info_->dead_code.erase(code);
660 :
661 0 : if (current_gc_info_->outstanding_isolates.empty()) {
662 : std::unordered_map<NativeModule*, std::vector<WasmCode*>>
663 : dead_code_per_native_module;
664 0 : for (WasmCode* code : current_gc_info_->dead_code) {
665 0 : dead_code_per_native_module[code->native_module()].push_back(code);
666 : }
667 0 : for (auto& entry : dead_code_per_native_module) {
668 0 : entry.first->FreeCode(VectorOf(entry.second));
669 : }
670 : current_gc_info_.reset();
671 : }
672 0 : }
673 :
674 76792 : bool WasmEngine::AddPotentiallyDeadCode(WasmCode* code) {
675 76792 : base::MutexGuard guard(&mutex_);
676 153644 : auto it = native_modules_.find(code->native_module());
677 : DCHECK_NE(native_modules_.end(), it);
678 : auto added = it->second->potentially_dead_code.insert(code);
679 76822 : if (!added.second) return false; // An entry already existed.
680 76822 : new_potentially_dead_code_size_ += code->instructions().size();
681 : // Trigger a GC if 1MiB plus 10% of committed code are potentially dead.
682 76822 : size_t dead_code_limit = 1 * MB + code_manager_.committed_code_space() / 10;
683 76822 : if (FLAG_wasm_code_gc && new_potentially_dead_code_size_ > dead_code_limit &&
684 : !current_gc_info_) {
685 0 : TriggerGC();
686 : }
687 : return true;
688 : }
689 :
690 0 : void WasmEngine::TriggerGC() {
691 : DCHECK_NULL(current_gc_info_);
692 : DCHECK(FLAG_wasm_code_gc);
693 0 : current_gc_info_.reset(new CurrentGCInfo());
694 : // Add all potentially dead code to this GC, and trigger a GC task in each
695 : // isolate.
696 : // TODO(clemensh): Also trigger a stack check interrupt.
697 0 : for (auto& entry : native_modules_) {
698 : NativeModuleInfo* info = entry.second.get();
699 0 : if (info->potentially_dead_code.empty()) continue;
700 0 : for (auto* isolate : native_modules_[entry.first]->isolates) {
701 : auto& gc_task = current_gc_info_->outstanding_isolates[isolate];
702 0 : if (!gc_task) {
703 : auto new_task = base::make_unique<WasmGCForegroundTask>(isolate);
704 0 : gc_task = new_task.get();
705 : DCHECK_EQ(1, isolates_.count(isolate));
706 0 : isolates_[isolate]->foreground_task_runner->PostTask(
707 0 : std::move(new_task));
708 : }
709 : }
710 0 : for (WasmCode* code : info->potentially_dead_code) {
711 : current_gc_info_->dead_code.insert(code);
712 : }
713 : }
714 0 : }
715 :
716 : namespace {
717 :
718 244221 : DEFINE_LAZY_LEAKY_OBJECT_GETTER(std::shared_ptr<WasmEngine>,
719 : GetSharedWasmEngine)
720 :
721 : } // namespace
722 :
723 : // static
724 61000 : void WasmEngine::InitializeOncePerProcess() {
725 61000 : if (!FLAG_wasm_shared_engine) return;
726 122000 : *GetSharedWasmEngine() = std::make_shared<WasmEngine>();
727 : }
728 :
729 : // static
730 59887 : void WasmEngine::GlobalTearDown() {
731 59887 : if (!FLAG_wasm_shared_engine) return;
732 59887 : GetSharedWasmEngine()->reset();
733 : }
734 :
735 : // static
736 62342 : std::shared_ptr<WasmEngine> WasmEngine::GetWasmEngine() {
737 62342 : if (FLAG_wasm_shared_engine) return *GetSharedWasmEngine();
738 : return std::make_shared<WasmEngine>();
739 : }
740 :
741 : // {max_mem_pages} is declared in wasm-limits.h.
742 1656994 : uint32_t max_mem_pages() {
743 : STATIC_ASSERT(kV8MaxWasmMemoryPages <= kMaxUInt32);
744 3313988 : return std::min(uint32_t{kV8MaxWasmMemoryPages}, FLAG_wasm_max_mem_pages);
745 : }
746 :
747 : // {max_table_init_entries} is declared in wasm-limits.h.
748 6431 : uint32_t max_table_init_entries() {
749 : return std::min(uint32_t{kV8MaxWasmTableInitEntries},
750 12862 : FLAG_wasm_max_table_size);
751 : }
752 :
753 : } // namespace wasm
754 : } // namespace internal
755 122004 : } // namespace v8
|