v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
wasm-engine.cc
Go to the documentation of this file.
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
6
7#include <optional>
8
9#include "src/base/hashing.h"
13#include "src/common/globals.h"
14#include "src/debug/debug.h"
21#include "src/logging/metrics.h"
25#include "src/objects/objects.h"
27#include "src/utils/ostreams.h"
33#include "src/wasm/pgo.h"
34#include "src/wasm/stacks.h"
38#include "src/wasm/wasm-debug.h"
41
42#if V8_ENABLE_DRUMBRAKE
44#endif // V8_ENABLE_DRUMBRAKE
45
46#ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
48#endif // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
49
50namespace v8::internal::wasm {
51
52#define TRACE_CODE_GC(...) \
53 do { \
54 if (v8_flags.trace_wasm_code_gc) PrintF("[wasm-gc] " __VA_ARGS__); \
55 } while (false)
56
57// This class exists in order to solve a shutdown ordering problem.
58// The basic situation is that the process-global WasmEngine has, for each
59// Isolate that it knows about, a map from NativeModule to Script, using
60// WeakScriptHandles to make sure that the NativeModules, which are shared
61// across the process, don't keep the (Isolate-specific) Scripts alive.
62// In the other direction, the Scripts keep the NativeModule alive, IOW
63// usually the Scripts die first, and the WeakScriptHandles are cleared
64// before being freed.
65// In case of asm.js modules and in case of Isolate shutdown, it can happen
66// that the NativeModule dies first, so the WeakScriptHandles are no longer
67// needed and should be destroyed. That can only happen on the main thread of
68// the Isolate they belong to, whereas the last thread that releases a
69// NativeModule might be any other thread, so we post a
70// ClearWeakScriptHandleTask to that isolate's foreground task runner.
71// In case of Isolate shutdown at an inconvenient moment, this task runner can
72// destroy all waiting tasks; and *afterwards* global handles are freed, which
73// writes to the memory location backing the handle, so this bit of memory must
74// not be owned by (and die with) the ClearWeakScriptHandleTask.
75// The solution is this class here: its instances form a linked list owned by
76// the Isolate to which the referenced Scripts belong. Its name refers to the
77// fact that it stores global handles that used to have a purpose but are now
78// just waiting for the right thread to destroy them.
79// If the ClearWeakScriptHandleTask gets to run (i.e. in the regular case),
80// it destroys the weak global handle and then the WasmOrphanedGlobalHandle
81// container, removing it from the isolate's list.
82// If the ClearWeakScriptHandleTask is destroyed before it runs, the isolate's
83// list of WasmOrphanedGlobalHandles isn't modified, so the indirection cell
84// is still around when all remaining global handles are freed; nevertheless
85// it won't leak because the Isolate owns it and will free it.
87 public:
89
90 void InitializeLocation(std::unique_ptr<Address*> location) {
91 location_ = std::move(location);
92 }
93
94 static void Destroy(WasmOrphanedGlobalHandle* that) {
95 // Destroy the global handle if it still exists.
96 Address** location = that->location_.get();
97 if (location) GlobalHandles::Destroy(*location);
98 that->location_.reset();
99 // Unlink and free the container.
100 *that->prev_ptr_ = that->next_;
101 if (that->next_ != nullptr) that->next_->prev_ptr_ = that->prev_ptr_;
102 // This function could be a non-static method, but then the next line
103 // would read "delete this", which is UB.
104 delete that;
105 }
106
107 private:
108 friend class WasmEngine;
109
110 // This is a doubly linked list with a twist: the {next_} pointer is just
111 // what you would expect, whereas {prev_ptr_} points at the slot inside
112 // the previous element that's pointing at the current element. The purpose
113 // of this design is to make it possible for the previous element to be
114 // the {Isolate::wasm_orphaned_handle_} field, without requiring any
115 // special-casing in the insert and delete operations.
118 std::unique_ptr<Address*> location_;
119};
120
121// static
122std::atomic<int32_t> WasmEngine::had_nondeterminism_{0};
123
124// static
126 WasmOrphanedGlobalHandle** pointer) {
127 // No need for additional locking: this is only ever called indirectly
128 // from {WasmEngine::ClearWeakScriptHandle()}, which holds the engine-wide
129 // {mutex_}.
131 orphan->next_ = *pointer;
132 orphan->prev_ptr_ = pointer;
133 if (orphan->next_ != nullptr) orphan->next_->prev_ptr_ = &orphan->next_;
134 *pointer = orphan;
135 return orphan;
136}
137
138// static
140 // This is meant to be called from ~Isolate, so we no longer care about
141 // maintaining invariants: the only task is to free memory to prevent leaks.
142 while (start != nullptr) {
144 delete start;
145 start = next;
146 }
147}
148
150 base::MutexGuard guard(&mutex_);
151 return native_modules_.size();
152}
153
154// A task to log a set of {WasmCode} objects in an isolate. It does not own any
155// data itself, since it is owned by the platform, so lifetime is not really
156// bound to the wasm engine.
158 friend class WasmEngine;
159
160 public:
161 explicit LogCodesTask(Isolate* isolate)
162 : CancelableTask(isolate), isolate_(isolate) {}
163
167
168 private:
170};
171
172namespace {
173void CheckNoArchivedThreads(Isolate* isolate) {
174 class ArchivedThreadsVisitor : public ThreadVisitor {
175 void VisitThread(Isolate* isolate, ThreadLocalTop* top) override {
176 // Archived threads are rarely used, and not combined with Wasm at the
177 // moment. Implement this and test it properly once we have a use case for
178 // that.
179 FATAL("archived threads in combination with wasm not supported");
180 }
181 } archived_threads_visitor;
182 isolate->thread_manager()->IterateArchivedThreads(&archived_threads_visitor);
183}
184
185class WasmGCForegroundTask : public CancelableTask {
186 public:
187 explicit WasmGCForegroundTask(Isolate* isolate)
188 : CancelableTask(isolate->cancelable_task_manager()), isolate_(isolate) {}
189
190 void RunInternal() final {
191 // The stack can contain live frames, for instance when this is invoked
192 // during a pause or a breakpoint.
193 GetWasmEngine()->ReportLiveCodeFromStackForGC(isolate_);
194 }
195
196 private:
197 Isolate* isolate_;
198};
199
200class ClearWeakScriptHandleTask : public CancelableTask {
201 public:
202 explicit ClearWeakScriptHandleTask(Isolate* isolate,
203 std::unique_ptr<Address*> location)
204 : CancelableTask(isolate->cancelable_task_manager()) {
205 handle_ = isolate->NewWasmOrphanedGlobalHandle();
206 handle_->InitializeLocation(std::move(location));
207 }
208
209 // We don't override the destructor, because there is nothing to do:
210 // if the task is deleted before it was run, then everything is shutting
211 // down anyway, so destroying the GlobalHandle is no longer relevant (and
212 // it might well be too late to do that safely).
213
214 void RunInternal() override {
215 WasmOrphanedGlobalHandle::Destroy(handle_);
216 handle_ = nullptr;
217 }
218
219 private:
220 // This is owned by the Isolate to ensure correct shutdown ordering.
221 WasmOrphanedGlobalHandle* handle_;
222};
223
224class WeakScriptHandle {
225 public:
226 WeakScriptHandle(DirectHandle<Script> script, Isolate* isolate)
227 : script_id_(script->id()), isolate_(isolate) {
228 DCHECK(IsString(script->name()) || IsUndefined(script->name()));
229 if (IsString(script->name())) {
230 source_url_ = Cast<String>(script->name())->ToCString();
231 }
232 auto global_handle = isolate->global_handles()->Create(*script);
233 location_ = std::make_unique<Address*>(global_handle.location());
234 GlobalHandles::MakeWeak(location_.get());
235 }
236
237 ~WeakScriptHandle() {
238 // Usually the destructor of this class is called after the weak callback,
239 // because the Script keeps the NativeModule alive. In that case,
240 // {location_} is already cleared, and there is nothing to do.
241 if (location_ == nullptr || *location_ == nullptr) return;
242 // For asm.js modules, the Script usually outlives the NativeModule.
243 // We must destroy the GlobalHandle before freeing the memory that's
244 // backing {location_}, so that when the Script does die eventually, there
245 // is no lingering weak GlobalHandle that would try to clear {location_}.
246 // We can't do that from arbitrary threads, so we must post a task to the
247 // main thread.
249 }
250
251 WeakScriptHandle(WeakScriptHandle&&) V8_NOEXCEPT = default;
252
253 DirectHandle<Script> handle() const {
254 return DirectHandle<Script>::FromSlot(*location_);
255 }
256
257 // Called by ~IsolateInfo. When the Isolate is shutting down, cleaning
258 // up properly is both no longer necessary and no longer safe to do.
259 void Clear() { location_.reset(); }
260
261 int script_id() const { return script_id_; }
262
263 const std::shared_ptr<const char[]>& source_url() const {
264 return source_url_;
265 }
266
267 private:
268 // Store the location in a unique_ptr so that its address stays the same even
269 // when this object is moved/copied.
270 std::unique_ptr<Address*> location_;
271
272 // Store the script ID independent of the weak handle, such that it's always
273 // available.
275
276 // Similar for the source URL. We cannot dereference the handle from
277 // arbitrary threads, but we need the URL available for code logging.
278 // The shared pointer is kept alive by unlogged code, even if this entry is
279 // collected in the meantime.
280 // TODO(chromium:1132260): Revisit this for huge URLs.
281 std::shared_ptr<const char[]> source_url_;
282
283 // The Isolate that the handled script belongs to.
284 Isolate* isolate_;
285};
286
287// If PGO data is being collected, keep all native modules alive, so repeated
288// runs of a benchmark (with different configuration) all use the same module.
289// This vector is protected by the global WasmEngine's mutex, but not defined in
290// the header because it's a private implementation detail.
291std::vector<std::shared_ptr<NativeModule>>* native_modules_kept_alive_for_pgo;
292
293} // namespace
294
295std::shared_ptr<NativeModule> NativeModuleCache::MaybeGetNativeModule(
296 ModuleOrigin origin, base::Vector<const uint8_t> wire_bytes,
297 const CompileTimeImports& compile_imports) {
298 if (!v8_flags.wasm_native_module_cache) return nullptr;
299 if (origin != kWasmOrigin) return nullptr;
301 size_t prefix_hash = PrefixHash(wire_bytes);
302 NativeModuleCache::Key key{prefix_hash, compile_imports, wire_bytes};
303 while (true) {
304 auto it = map_.find(key);
305 if (it == map_.end()) {
306 // Even though this exact key is not in the cache, there might be a
307 // matching prefix hash indicating that a streaming compilation is
308 // currently compiling a module with the same prefix. {OnFinishedStream}
309 // happens on the main thread too, so waiting for streaming compilation to
310 // finish would create a deadlock. Instead, compile the module twice and
311 // handle the conflict in {UpdateNativeModuleCache}.
312
313 // Insert a {nullopt} entry to let other threads know that this
314 // {NativeModule} is already being created on another thread.
315 [[maybe_unused]] auto [iterator, inserted] =
316 map_.emplace(key, std::nullopt);
317 DCHECK(inserted);
318 return nullptr;
319 }
320 if (it->second.has_value()) {
321 if (auto shared_native_module = it->second.value().lock()) {
322 DCHECK_EQ(
323 shared_native_module->compile_imports().compare(compile_imports),
324 0);
325 DCHECK_EQ(shared_native_module->wire_bytes(), wire_bytes);
326 return shared_native_module;
327 }
328 }
329 // TODO(11858): This deadlocks in predictable mode, because there is only a
330 // single thread.
332 }
333}
334
336 size_t prefix_hash, const CompileTimeImports& compile_imports) {
337 if (!v8_flags.wasm_native_module_cache) return true;
339 auto it = map_.lower_bound(Key{prefix_hash, compile_imports, {}});
340 if (it != map_.end() && it->first.prefix_hash == prefix_hash) {
341 DCHECK_IMPLIES(!it->first.bytes.empty(),
342 PrefixHash(it->first.bytes) == prefix_hash);
343 return false;
344 }
345 Key key{prefix_hash, compile_imports, {}};
346 DCHECK_EQ(0, map_.count(key));
347 map_.emplace(key, std::nullopt);
348 return true;
349}
350
352 size_t prefix_hash, const CompileTimeImports& compile_imports) {
353 if (!v8_flags.wasm_native_module_cache) return;
355 Key key{prefix_hash, compile_imports, {}};
356 map_.erase(key);
358}
359
360std::shared_ptr<NativeModule> NativeModuleCache::Update(
361 std::shared_ptr<NativeModule> native_module, bool error) {
362 DCHECK_NOT_NULL(native_module);
363 if (!v8_flags.wasm_native_module_cache) return native_module;
364 if (native_module->module()->origin != kWasmOrigin) return native_module;
365 base::Vector<const uint8_t> wire_bytes = native_module->wire_bytes();
366 DCHECK(!wire_bytes.empty());
367 size_t prefix_hash = PrefixHash(native_module->wire_bytes());
369 const CompileTimeImports& compile_imports = native_module->compile_imports();
370 map_.erase(Key{prefix_hash, compile_imports, {}});
371 const Key key{prefix_hash, compile_imports, wire_bytes};
372 auto it = map_.find(key);
373 if (it != map_.end()) {
374 if (it->second.has_value()) {
375 auto conflicting_module = it->second.value().lock();
376 if (conflicting_module != nullptr) {
377 DCHECK_EQ(conflicting_module->wire_bytes(), wire_bytes);
378 // This return might delete {native_module} if we were the last holder.
379 // That in turn can call {NativeModuleCache::Erase}, which takes the
380 // mutex. This is not a problem though, since the {MutexGuard} above is
381 // released before the {native_module}, per the definition order.
382 return conflicting_module;
383 }
384 }
385 map_.erase(it);
386 }
387 if (!error) {
388 // The key now points to the new native module's owned copy of the bytes,
389 // so that it stays valid until the native module is freed and erased from
390 // the map.
391 [[maybe_unused]] auto [iterator, inserted] = map_.emplace(
392 key, std::optional<std::weak_ptr<NativeModule>>(native_module));
393 DCHECK(inserted);
394 }
396 return native_module;
397}
398
400 if (!v8_flags.wasm_native_module_cache) return;
401 if (native_module->module()->origin != kWasmOrigin) return;
402 // Happens in some tests where bytes are set directly.
403 if (native_module->wire_bytes().empty()) return;
405 size_t prefix_hash = PrefixHash(native_module->wire_bytes());
406 map_.erase(Key{prefix_hash, native_module->compile_imports(),
407 native_module->wire_bytes()});
409}
410
411// static
413 // Compute the hash as a combined hash of the sections up to the code section
414 // header, to mirror the way streaming compilation does it.
415 Decoder decoder(wire_bytes.begin(), wire_bytes.end());
416 decoder.consume_bytes(8, "module header");
418 base::Hasher hasher;
419 while (decoder.ok() && decoder.more()) {
420 section_id = static_cast<SectionCode>(decoder.consume_u8());
421 uint32_t section_size = decoder.consume_u32v("section size");
422 if (section_id == SectionCode::kCodeSectionCode) {
423 hasher.Add(section_size);
424 break;
425 }
426 const uint8_t* payload_start = decoder.pc();
427 decoder.consume_bytes(section_size, "section payload");
428 hasher.AddRange(base::VectorOf(payload_start, section_size));
429 }
430 return hasher.hash();
431}
432
438
439 // Set of isolates that did not scan their stack yet for used WasmCode, and
440 // their scheduled foreground task.
441 std::unordered_map<Isolate*, WasmGCForegroundTask*> outstanding_isolates;
442
443 // Set of dead code. Filled with all potentially dead code on initialization.
444 // Code that is still in-use is removed by the individual isolates.
445 std::unordered_set<WasmCode*> dead_code;
446
447 // The number of GCs triggered in the native module that triggered this GC.
448 // This is stored in the histogram for each participating isolate during
449 // execution of that isolate's foreground task.
450 const int8_t gc_sequence_index;
451
452 // If during this GC, another GC was requested, we skipped that other GC (we
453 // only run one GC at a time). Remember though to trigger another one once
454 // this one finishes. {next_gc_sequence_index} is 0 if no next GC is needed,
455 // and >0 otherwise. It stores the {num_code_gcs_triggered} of the native
456 // module which triggered the next GC.
458
459 // The start time of this GC; used for tracing and sampled via {Counters}.
460 // Can be null ({TimeTicks::IsNull()}) if timer is not high resolution.
462};
463
465 IsolateInfo(Isolate* isolate, bool log_code)
466 : log_codes(log_code), async_counters(isolate->async_counters()) {
467 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
469 foreground_task_runner = platform->GetForegroundTaskRunner(v8_isolate);
470 }
471
473 // Before destructing, the {WasmEngine} must have cleared outstanding code
474 // to log.
475 DCHECK_EQ(0, code_to_log.size());
476
477 // We need the {~WeakScriptHandle} destructor in {scripts} to behave
478 // differently depending on whether the Isolate is in the process of
479 // being destroyed. That's the only situation where we would run the
480 // {~IsolateInfo} destructor, and in that case, we can no longer post
481 // the task that would destroy the {WeakScriptHandle}'s {GlobalHandle};
482 // whereas if only individual entries of {scripts} get deleted, then
483 // we can and should post such tasks.
484 for (auto& [native_module, script_handle] : scripts) {
485 script_handle.Clear();
486 }
487 }
488
489 // All native modules that are being used by this Isolate.
490 std::unordered_set<NativeModule*> native_modules;
491
492 // Scripts created for each native module in this isolate.
493 std::unordered_map<NativeModule*, WeakScriptHandle> scripts;
494
495 // Caches whether code needs to be logged on this isolate.
497
498 // Maps script ID to vector of code objects that still need to be logged, and
499 // the respective source URL.
501 std::vector<WasmCode*> code;
502 // Keep the NativeModule alive while code logging is outstanding.
503 std::shared_ptr<NativeModule> native_module;
504 std::shared_ptr<const char[]> source_url;
505 };
506 std::unordered_map<int, CodeToLogPerScript> code_to_log;
507
508 // The foreground task runner of the isolate (can be called from background).
509 std::shared_ptr<v8::TaskRunner> foreground_task_runner;
510
511 const std::shared_ptr<Counters> async_counters;
512
513 // Keep new modules in debug state.
515
516 // Keep track whether we already added a sample for PKU support (we only want
517 // one sample per Isolate).
519};
520
522 std::unique_ptr<Address*> location) {
523 // This function is designed for one targeted use case, which always
524 // acquires a lock on {mutex_} before calling here.
526 IsolateInfo* isolate_info = isolates_[isolate].get();
527 std::shared_ptr<TaskRunner> runner = isolate_info->foreground_task_runner;
528 runner->PostTask(std::make_unique<ClearWeakScriptHandleTask>(
529 isolate, std::move(location)));
530}
531
533 explicit NativeModuleInfo(std::weak_ptr<NativeModule> native_module)
534 : weak_ptr(std::move(native_module)) {}
535
536 // Weak pointer, to gain back a shared_ptr if needed.
537 std::weak_ptr<NativeModule> weak_ptr;
538
539 // Set of isolates using this NativeModule.
540 std::unordered_set<Isolate*> isolates;
541};
542
543WasmEngine::WasmEngine() : call_descriptors_(&allocator_) {}
544
546#ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
547 // Synchronize on the GDB-remote thread, if running.
548 gdb_server_.reset();
549#endif // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
550
551 if (V8_UNLIKELY(v8_flags.print_wasm_offheap_memory_size)) {
553 }
554
555 // Free all modules that were kept alive for collecting PGO. This is to avoid
556 // memory leaks.
557 if (V8_UNLIKELY(native_modules_kept_alive_for_pgo)) {
558 delete native_modules_kept_alive_for_pgo;
559 }
560
561 operations_barrier_->CancelAndWait();
562
563 // All code should have been deleted already, but wrappers managed by the
564 // WasmImportWrapperCache are placed in {potentially_dead_code_} when they
565 // are no longer referenced, and we don't want to wait for the next
566 // Wasm Code GC cycle to remove them from that set.
567 for (WasmCode* code : potentially_dead_code_) {
568 code->DcheckRefCountIsOne();
569 // The actual instructions will get thrown out when the global
570 // WasmImportWrapperCache's {code_allocator_} frees its memory region.
571 // Here we just pacify LSan.
572 delete code;
573 }
574
575 // All AsyncCompileJobs have been canceled.
577 // All Isolates have been deregistered.
578 DCHECK(isolates_.empty());
579 // All NativeModules did die.
580 DCHECK(native_modules_.empty());
581 // Native module cache does not leak.
583}
584
586 CompileTimeImports compile_imports,
588 TRACE_EVENT0("v8.wasm", "wasm.SyncValidate");
589 if (bytes.empty()) return false;
590
591 WasmDetectedFeatures unused_detected_features;
593 enabled, bytes, true, kWasmOrigin, isolate->counters(),
594 isolate->metrics_recorder(),
595 isolate->GetOrRegisterRecorderContextId(isolate->native_context()),
596 DecodingMethod::kSync, &unused_detected_features);
597 if (result.failed()) return false;
599 result.value().get(), bytes, compile_imports, &unused_detected_features);
600 return !error.has_error();
601}
602
604 Isolate* isolate, ErrorThrower* thrower,
606 base::Vector<const uint8_t> asm_js_offset_table_bytes,
607 DirectHandle<HeapNumber> uses_bitset, LanguageMode language_mode) {
608 int compilation_id = next_compilation_id_.fetch_add(1);
609 TRACE_EVENT1("v8.wasm", "wasm.SyncCompileTranslatedAsmJs", "id",
610 compilation_id);
611 ModuleOrigin origin = language_mode == LanguageMode::kSloppy
614 // TODO(leszeks): If we want asm.js in UKM, we should figure out a way to pass
615 // the context id in here.
618 WasmDetectedFeatures detected_features;
620 WasmEnabledFeatures::ForAsmjs(), bytes.as_vector(), false, origin,
621 isolate->counters(), isolate->metrics_recorder(), context_id,
622 DecodingMethod::kSync, &detected_features);
623 if (result.failed()) {
624 // This happens once in a while when we have missed some limit check
625 // in the asm parser. Output an error message to help diagnose, but crash.
626 std::cout << result.error().message();
627 UNREACHABLE();
628 }
629
630 result.value()->asm_js_offset_information =
631 std::make_unique<AsmJsOffsetInformation>(asm_js_offset_table_bytes);
632
633 // Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
634 // in {CompileToNativeModule}.
635 constexpr ProfileInformation* kNoProfileInformation = nullptr;
636 std::shared_ptr<NativeModule> native_module = CompileToNativeModule(
637 isolate, WasmEnabledFeatures::ForAsmjs(), detected_features,
638 CompileTimeImports{}, thrower, std::move(result).value(),
639 std::move(bytes), compilation_id, context_id, kNoProfileInformation);
640 if (!native_module) return {};
641
642 native_module->LogWasmCodes(isolate, *script);
643 {
644 // Register the script with the isolate. We do this unconditionally for
645 // consistency; it is in particular required for logging lazy-compiled code.
646 base::MutexGuard guard(&mutex_);
647 DCHECK_EQ(1, isolates_.count(isolate));
648 auto& scripts = isolates_[isolate]->scripts;
649 // If the same asm.js module is instantiated repeatedly, then we
650 // deduplicate the NativeModule, so the script exists already.
651 if (scripts.count(native_module.get()) == 0) {
652 scripts.emplace(native_module.get(), WeakScriptHandle(script, isolate));
653 }
654 }
655
656 return AsmWasmData::New(isolate, std::move(native_module), uses_bitset);
657}
658
660 Isolate* isolate, DirectHandle<AsmWasmData> asm_wasm_data,
661 DirectHandle<Script> script) {
662 std::shared_ptr<NativeModule> native_module =
663 asm_wasm_data->managed_native_module()->get();
664 DirectHandle<WasmModuleObject> module_object =
665 WasmModuleObject::New(isolate, std::move(native_module), script);
666 return module_object;
667}
668
670 Isolate* isolate, WasmEnabledFeatures enabled_features,
671 CompileTimeImports compile_imports, ErrorThrower* thrower,
673 int compilation_id = next_compilation_id_.fetch_add(1);
674 TRACE_EVENT1("v8.wasm", "wasm.SyncCompile", "id", compilation_id);
676 isolate->GetOrRegisterRecorderContextId(isolate->native_context());
677 std::shared_ptr<WasmModule> module;
678 WasmDetectedFeatures detected_features;
679 {
680 // Normally modules are validated in {CompileToNativeModule} but in jitless
681 // mode the only opportunity of validatiom is during decoding.
682 bool validate_module = v8_flags.wasm_jitless;
684 enabled_features, bytes.as_vector(), validate_module, kWasmOrigin,
685 isolate->counters(), isolate->metrics_recorder(), context_id,
686 DecodingMethod::kSync, &detected_features);
687 if (result.failed()) {
688 thrower->CompileFailed(result.error());
689 return {};
690 }
691 module = std::move(result).value();
692 if (WasmError error =
693 ValidateAndSetBuiltinImports(module.get(), bytes.as_vector(),
694 compile_imports, &detected_features)) {
695 thrower->CompileError("%s @+%u", error.message().c_str(), error.offset());
696 return {};
697 }
698 }
699
700 // If experimental PGO via files is enabled, load profile information now.
701 std::unique_ptr<ProfileInformation> pgo_info;
702 if (V8_UNLIKELY(v8_flags.experimental_wasm_pgo_from_file)) {
703 pgo_info = LoadProfileFromFile(module.get(), bytes.as_vector());
704 }
705
706 // Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
707 // in {CompileToNativeModule}.
708 std::shared_ptr<NativeModule> native_module = CompileToNativeModule(
709 isolate, enabled_features, detected_features, std::move(compile_imports),
710 thrower, std::move(module), std::move(bytes), compilation_id, context_id,
711 pgo_info.get());
712 if (!native_module) return {};
713
714#ifdef DEBUG
715 // Ensure that code GC will check this isolate for live code.
716 {
718 DCHECK_EQ(1, isolates_.count(isolate));
719 DCHECK_EQ(1, isolates_[isolate]->native_modules.count(native_module.get()));
720 DCHECK_EQ(1, native_modules_.count(native_module.get()));
721 DCHECK_EQ(1, native_modules_[native_module.get()]->isolates.count(isolate));
722 }
723#endif
724
725 constexpr base::Vector<const char> kNoSourceUrl;
726 DirectHandle<Script> script =
727 GetOrCreateScript(isolate, native_module, kNoSourceUrl);
728
729 native_module->LogWasmCodes(isolate, *script);
730
731 // Create the compiled module object and populate with compiled functions
732 // and information needed at instantiation time. This object needs to be
733 // serializable. Instantiation may occur off a deserialized version of this
734 // object.
735 DirectHandle<WasmModuleObject> module_object =
736 WasmModuleObject::New(isolate, std::move(native_module), script);
737
738 // Finish the Wasm script now and make it public to the debugger.
739 isolate->debug()->OnAfterCompile(script);
740 return module_object;
741}
742
744 Isolate* isolate, ErrorThrower* thrower,
745 DirectHandle<WasmModuleObject> module_object,
748 TRACE_EVENT0("v8.wasm", "wasm.SyncInstantiate");
749 return InstantiateToInstanceObject(isolate, thrower, module_object, imports,
750 memory);
751}
752
754 Isolate* isolate, std::unique_ptr<InstantiationResultResolver> resolver,
755 DirectHandle<WasmModuleObject> module_object,
757 ErrorThrower thrower(isolate, "WebAssembly.instantiate()");
758 TRACE_EVENT0("v8.wasm", "wasm.AsyncInstantiate");
759 // Instantiate a TryCatch so that caught exceptions won't propagate out.
760 // They will still be set as exceptions on the isolate.
761 // TODO(clemensb): Avoid TryCatch, use Execution::TryCall internally to invoke
762 // start function and report thrown exception explicitly via out argument.
763 v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
764 catcher.SetVerbose(false);
765 catcher.SetCaptureMessage(false);
766
768 SyncInstantiate(isolate, &thrower, module_object, imports,
770
771 if (!instance_object.is_null()) {
772 resolver->OnInstantiationSucceeded(instance_object.ToHandleChecked());
773 return;
774 }
775
776 if (isolate->has_exception()) {
777 thrower.Reset();
778 if (isolate->is_execution_terminating()) return;
779 // The JS code executed during instantiation has thrown an exception.
780 // We have to move the exception to the promise chain.
781 DirectHandle<JSAny> exception(Cast<JSAny>(isolate->exception()), isolate);
782 isolate->clear_exception();
783 resolver->OnInstantiationFailed(exception);
784 } else {
785 DCHECK(thrower.error());
786 resolver->OnInstantiationFailed(thrower.Reify());
787 }
788}
789
791 Isolate* isolate, WasmEnabledFeatures enabled,
792 CompileTimeImports compile_imports,
793 std::shared_ptr<CompilationResultResolver> resolver,
795 const char* api_method_name_for_errors) {
796 int compilation_id = next_compilation_id_.fetch_add(1);
797 TRACE_EVENT1("v8.wasm", "wasm.AsyncCompile", "id", compilation_id);
798
799 if (!v8_flags.wasm_async_compilation || v8_flags.wasm_jitless) {
800 // Asynchronous compilation disabled; fall back on synchronous compilation.
801 ErrorThrower thrower(isolate, api_method_name_for_errors);
803 module_object = SyncCompile(isolate, enabled, std::move(compile_imports),
804 &thrower, std::move(bytes));
805 if (thrower.error()) {
806 resolver->OnCompilationFailed(thrower.Reify());
807 return;
808 }
809 DirectHandle<WasmModuleObject> module = module_object.ToHandleChecked();
810 resolver->OnCompilationSucceeded(module);
811 return;
812 }
813
814 if (v8_flags.wasm_test_streaming) {
815 std::shared_ptr<StreamingDecoder> streaming_decoder =
816 StartStreamingCompilation(isolate, enabled, std::move(compile_imports),
817 direct_handle(isolate->context(), isolate),
818 api_method_name_for_errors,
819 std::move(resolver));
820
821 auto* rng = isolate->random_number_generator();
823 if (!bytes.empty()) ranges.push_back(bytes.as_vector());
824 // Split into up to 16 ranges (2^4).
825 for (int round = 0; round < 4; ++round) {
826 for (auto it = ranges.begin(); it != ranges.end(); ++it) {
827 auto range = *it;
828 if (range.size() < 2 || !rng->NextBool()) continue; // Do not split.
829 // Choose split point within [1, range.size() - 1].
830 static_assert(kV8MaxWasmModuleSize <= kMaxInt);
831 size_t split_point =
832 1 + rng->NextInt(static_cast<int>(range.size() - 1));
833 // Insert first sub-range *before* {it} and make {it} point after it.
834 it = ranges.insert(it, range.SubVector(0, split_point)) + 1;
835 *it = range.SubVectorFrom(split_point);
836 }
837 }
838 for (auto range : ranges) {
839 streaming_decoder->OnBytesReceived(range);
840 }
841 streaming_decoder->Finish();
842 return;
843 }
844
846 isolate, enabled, std::move(compile_imports), std::move(bytes),
847 isolate->native_context(), api_method_name_for_errors,
848 std::move(resolver), compilation_id);
849 job->Start();
850}
851
852std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation(
853 Isolate* isolate, WasmEnabledFeatures enabled,
854 CompileTimeImports compile_imports, DirectHandle<Context> context,
855 const char* api_method_name,
856 std::shared_ptr<CompilationResultResolver> resolver) {
857 int compilation_id = next_compilation_id_.fetch_add(1);
858 TRACE_EVENT1("v8.wasm", "wasm.StartStreamingCompilation", "id",
859 compilation_id);
860 if (v8_flags.wasm_async_compilation) {
862 isolate, enabled, std::move(compile_imports), {}, context,
863 api_method_name, std::move(resolver), compilation_id);
864 return job->CreateStreamingDecoder();
865 }
867 isolate, enabled, std::move(compile_imports), context, api_method_name,
868 std::move(resolver));
869}
870
872 NativeModule* native_module,
873 uint32_t function_index, ExecutionTier tier) {
874 DCHECK(!v8_flags.wasm_jitless);
875
876 // Note we assume that "one-off" compilations can discard detected features.
877 WasmDetectedFeatures detected;
879 counters, native_module, &detected,
880 &native_module->module()->functions[function_index], tier);
881}
882
884 if (v8_flags.wasm_jitless) return;
885
886 std::vector<std::shared_ptr<NativeModule>> native_modules;
887 // {mutex_} gets taken both here and in {RemoveCompiledCode} in
888 // {AddPotentiallyDeadCode}. Therefore {RemoveCompiledCode} has to be
889 // called outside the lock.
890 {
892 if (isolates_[isolate]->keep_in_debug_state) return;
893 isolates_[isolate]->keep_in_debug_state = true;
894 for (auto* native_module : isolates_[isolate]->native_modules) {
895 DCHECK_EQ(1, native_modules_.count(native_module));
896 if (auto shared_ptr = native_modules_[native_module]->weak_ptr.lock()) {
897 native_modules.emplace_back(std::move(shared_ptr));
898 }
899 native_module->SetDebugState(kDebugging);
900 }
901 }
902 WasmCodeRefScope ref_scope;
903 for (auto& native_module : native_modules) {
904 native_module->RemoveCompiledCode(
906 }
907}
908
910 // Only trigger recompilation after releasing the mutex, otherwise we risk
911 // deadlocks because of lock inversion. The bool tells whether the module
912 // needs recompilation for tier up.
913 std::vector<std::pair<std::shared_ptr<NativeModule>, bool>> native_modules;
914 {
916 isolates_[isolate]->keep_in_debug_state = false;
917 auto can_remove_debug_code = [this](NativeModule* native_module) {
918 DCHECK_EQ(1, native_modules_.count(native_module));
919 for (auto* isolate : native_modules_[native_module]->isolates) {
920 DCHECK_EQ(1, isolates_.count(isolate));
921 if (isolates_[isolate]->keep_in_debug_state) return false;
922 }
923 return true;
924 };
925 for (auto* native_module : isolates_[isolate]->native_modules) {
926 DCHECK_EQ(1, native_modules_.count(native_module));
927 auto shared_ptr = native_modules_[native_module]->weak_ptr.lock();
928 if (!shared_ptr) continue; // The module is not used any more.
929 if (!native_module->IsInDebugState()) continue;
930 // Only start tier-up if no other isolate needs this module in tiered
931 // down state.
932 bool remove_debug_code = can_remove_debug_code(native_module);
933 if (remove_debug_code) native_module->SetDebugState(kNotDebugging);
934 native_modules.emplace_back(std::move(shared_ptr), remove_debug_code);
935 }
936 }
937 for (auto& entry : native_modules) {
938 auto& native_module = entry.first;
939 bool remove_debug_code = entry.second;
940 // Remove all breakpoints set by this isolate.
941 if (native_module->HasDebugInfo()) {
942 native_module->GetDebugInfo()->RemoveIsolate(isolate);
943 }
944 if (remove_debug_code) {
945 WasmCodeRefScope ref_scope;
946 native_module->RemoveCompiledCode(
948 }
949 }
950}
951
952namespace {
953DirectHandle<Script> CreateWasmScript(
954 Isolate* isolate, std::shared_ptr<NativeModule> native_module,
955 base::Vector<const char> source_url) {
956 base::Vector<const uint8_t> wire_bytes = native_module->wire_bytes();
957
958 // The source URL of the script is
959 // - the original source URL if available (from the streaming API),
960 // - wasm://wasm/<module name>-<hash> if a module name has been set, or
961 // - wasm://wasm/<hash> otherwise.
962 const WasmModule* module = native_module->module();
963 DirectHandle<String> url_str;
964 if (!source_url.empty()) {
965 url_str = isolate->factory()
966 ->NewStringFromUtf8(source_url, AllocationType::kOld)
967 .ToHandleChecked();
968 } else {
969 // Limit the printed hash to 8 characters.
970 uint32_t hash = static_cast<uint32_t>(GetWireBytesHash(wire_bytes));
972 if (module->name.is_empty()) {
973 // Build the URL in the form "wasm://wasm/<hash>".
974 int url_len = SNPrintF(buffer, "wasm://wasm/%08x", hash);
975 DCHECK(url_len >= 0 && url_len < buffer.length());
976 url_str = isolate->factory()
977 ->NewStringFromUtf8(buffer.SubVector(0, url_len),
979 .ToHandleChecked();
980 } else {
981 // Build the URL in the form "wasm://wasm/<module name>-<hash>".
982 int hash_len = SNPrintF(buffer, "-%08x", hash);
983 DCHECK(hash_len >= 0 && hash_len < buffer.length());
984 DirectHandle<String> prefix =
985 isolate->factory()->NewStringFromStaticChars("wasm://wasm/");
986 DirectHandle<String> module_name =
988 isolate, wire_bytes, module->name, kNoInternalize);
989 DirectHandle<String> hash_str =
990 isolate->factory()
991 ->NewStringFromUtf8(buffer.SubVector(0, hash_len))
992 .ToHandleChecked();
993 // Concatenate the three parts.
994 url_str = isolate->factory()
995 ->NewConsString(prefix, module_name)
996 .ToHandleChecked();
997 url_str = isolate->factory()
998 ->NewConsString(url_str, hash_str)
999 .ToHandleChecked();
1000 }
1001 }
1002 DirectHandle<PrimitiveHeapObject> source_map_url =
1003 isolate->factory()->undefined_value();
1006 auto source_map_symbols =
1007 module->debug_symbols[WasmDebugSymbols::Type::SourceMap];
1008 base::Vector<const char> external_url =
1009 ModuleWireBytes(wire_bytes)
1010 .GetNameOrNull(source_map_symbols.external_url);
1011 MaybeDirectHandle<String> src_map_str =
1012 isolate->factory()->NewStringFromUtf8(external_url,
1014 source_map_url = src_map_str.ToHandleChecked();
1015 }
1016
1017 // Use the given shared {NativeModule}, but increase its reference count by
1018 // allocating a new {Managed<T>} that the {Script} references.
1019 size_t code_size_estimate = native_module->committed_code_space();
1020 size_t memory_estimate =
1021 code_size_estimate +
1023 DirectHandle<Managed<wasm::NativeModule>> managed_native_module =
1024 Managed<wasm::NativeModule>::From(isolate, memory_estimate,
1025 std::move(native_module));
1026
1027 DirectHandle<Script> script =
1028 isolate->factory()->NewScript(isolate->factory()->undefined_value());
1029 {
1031 Tagged<Script> raw_script = *script;
1032 raw_script->set_compilation_state(Script::CompilationState::kCompiled);
1033 raw_script->set_context_data(isolate->native_context()->debug_context_id());
1034 raw_script->set_name(*url_str);
1035 raw_script->set_type(Script::Type::kWasm);
1036 raw_script->set_source_mapping_url(*source_map_url);
1037 raw_script->set_line_ends(ReadOnlyRoots(isolate).empty_fixed_array(),
1039 raw_script->set_wasm_managed_native_module(*managed_native_module);
1040 raw_script->set_wasm_breakpoint_infos(
1041 ReadOnlyRoots(isolate).empty_fixed_array(), SKIP_WRITE_BARRIER);
1042 raw_script->set_wasm_weak_instance_list(
1043 ReadOnlyRoots(isolate).empty_weak_array_list(), SKIP_WRITE_BARRIER);
1044
1045 // For correct exception handling (in particular, the onunhandledrejection
1046 // callback), we must set the origin options from the nearest calling JS
1047 // frame.
1048 // Considering all Wasm modules as shared across origins isn't a privacy
1049 // issue, because in order to instantiate and use them, a site needs to
1050 // already have access to their wire bytes anyway.
1051 static constexpr bool kIsSharedCrossOrigin = true;
1052 static constexpr bool kIsOpaque = false;
1053 static constexpr bool kIsWasm = true;
1054 static constexpr bool kIsModule = false;
1055 raw_script->set_origin_options(ScriptOriginOptions(
1056 kIsSharedCrossOrigin, kIsOpaque, kIsWasm, kIsModule));
1057 }
1058
1059 return script;
1060}
1061} // namespace
1062
1064 Isolate* isolate, std::shared_ptr<NativeModule> shared_native_module,
1065 base::Vector<const char> source_url) {
1066 NativeModule* native_module = shared_native_module.get();
1067 ModuleWireBytes wire_bytes(native_module->wire_bytes());
1068 DirectHandle<Script> script =
1069 GetOrCreateScript(isolate, shared_native_module, source_url);
1070 native_module->LogWasmCodes(isolate, *script);
1071 DirectHandle<WasmModuleObject> module_object =
1072 WasmModuleObject::New(isolate, std::move(shared_native_module), script);
1073 {
1074 base::MutexGuard lock(&mutex_);
1075 DCHECK_EQ(1, isolates_.count(isolate));
1076 IsolateInfo* isolate_info = isolates_.find(isolate)->second.get();
1077 isolate_info->native_modules.insert(native_module);
1078 DCHECK_EQ(1, native_modules_.count(native_module));
1079 native_modules_[native_module]->isolates.insert(isolate);
1080 if (isolate_info->log_codes && !native_module->log_code()) {
1081 EnableCodeLogging(native_module);
1082 }
1083 }
1084
1085 // Finish the Wasm script now and make it public to the debugger.
1086 isolate->debug()->OnAfterCompile(script);
1087 return module_object;
1088}
1089
1090std::pair<size_t, size_t> WasmEngine::FlushLiftoffCode() {
1091 // Keep the NativeModules alive until after the destructor of the
1092 // `WasmCodeRefScope`, which still needs to access the code and the
1093 // NativeModule.
1094 std::vector<std::shared_ptr<NativeModule>> native_modules_with_dead_code;
1095 WasmCodeRefScope ref_scope;
1096 base::MutexGuard guard(&mutex_);
1097 size_t removed_code_size = 0;
1098 size_t removed_metadata_size = 0;
1099 for (auto& [native_module, info] : native_modules_) {
1100 std::shared_ptr<NativeModule> shared = info->weak_ptr.lock();
1101 if (!shared) continue; // The NativeModule is dying anyway.
1102 auto [code_size, metadata_size] = native_module->RemoveCompiledCode(
1104 DCHECK_EQ(code_size == 0, metadata_size == 0);
1105 if (code_size == 0) continue;
1106 native_modules_with_dead_code.emplace_back(std::move(shared));
1107 removed_code_size += code_size;
1108 removed_metadata_size += metadata_size;
1109 }
1110 return {removed_code_size, removed_metadata_size};
1111}
1112
1114 base::MutexGuard guard(&mutex_);
1115 size_t codesize_liftoff = 0;
1116 for (auto& [native_module, info] : native_modules_) {
1117 codesize_liftoff += native_module->SumLiftoffCodeSizeForTesting();
1118 }
1119 return codesize_liftoff;
1120}
1121
1122std::shared_ptr<CompilationStatistics>
1130
1132 base::MutexGuard guard(&mutex_);
1133 if (compilation_stats_ != nullptr) {
1134 StdoutStream os;
1135 os << AsPrintableStatistics{"Turbofan Wasm", *compilation_stats_, false}
1136 << std::endl;
1137 }
1138 compilation_stats_.reset();
1139}
1140
1142 base::MutexGuard guard(&mutex_);
1143 if (compilation_stats_ != nullptr) {
1144 StdoutStream os;
1145 os << AsPrintableStatistics{"Turbofan Wasm", *compilation_stats_, false}
1146 << std::endl;
1147 }
1148}
1149
1151 base::MutexGuard guard(&mutex_);
1152 if (code_tracer_ == nullptr) code_tracer_.reset(new CodeTracer(-1));
1153 return code_tracer_.get();
1154}
1155
1157 Isolate* isolate, WasmEnabledFeatures enabled,
1159 DirectHandle<Context> context, const char* api_method_name,
1160 std::shared_ptr<CompilationResultResolver> resolver, int compilation_id) {
1161 DirectHandle<NativeContext> incumbent_context =
1162 isolate->GetIncumbentContext();
1164 isolate, enabled, std::move(compile_imports), std::move(bytes), context,
1165 incumbent_context, api_method_name, std::move(resolver), compilation_id);
1166 // Pass ownership to the unique_ptr in {async_compile_jobs_}.
1167 base::MutexGuard guard(&mutex_);
1168 async_compile_jobs_[job] = std::unique_ptr<AsyncCompileJob>(job);
1169 return job;
1170}
1171
1172std::unique_ptr<AsyncCompileJob> WasmEngine::RemoveCompileJob(
1173 AsyncCompileJob* job) {
1174 base::MutexGuard guard(&mutex_);
1175 auto item = async_compile_jobs_.find(job);
1176 DCHECK(item != async_compile_jobs_.end());
1177 std::unique_ptr<AsyncCompileJob> result = std::move(item->second);
1178 async_compile_jobs_.erase(item);
1179 return result;
1180}
1181
1183 base::MutexGuard guard(&mutex_);
1184 DCHECK_EQ(1, isolates_.count(isolate));
1185 for (auto& entry : async_compile_jobs_) {
1186 if (entry.first->isolate() == isolate) return true;
1187 }
1188 return false;
1189}
1190
1192 // Under the mutex get all jobs to delete. Then delete them without holding
1193 // the mutex, such that deletion can reenter the WasmEngine.
1194 std::vector<std::unique_ptr<AsyncCompileJob>> jobs_to_delete;
1195 {
1196 base::MutexGuard guard(&mutex_);
1197 for (auto it = async_compile_jobs_.begin();
1198 it != async_compile_jobs_.end();) {
1199 if (!it->first->context().is_identical_to(context)) {
1200 ++it;
1201 continue;
1202 }
1203 jobs_to_delete.push_back(std::move(it->second));
1204 it = async_compile_jobs_.erase(it);
1205 }
1206 }
1207}
1208
1210 // Under the mutex get all jobs to delete. Then delete them without holding
1211 // the mutex, such that deletion can reenter the WasmEngine.
1212 std::vector<std::unique_ptr<AsyncCompileJob>> jobs_to_delete;
1213 std::vector<std::weak_ptr<NativeModule>> modules_in_isolate;
1214 {
1215 base::MutexGuard guard(&mutex_);
1216 for (auto it = async_compile_jobs_.begin();
1217 it != async_compile_jobs_.end();) {
1218 if (it->first->isolate() != isolate) {
1219 ++it;
1220 continue;
1221 }
1222 jobs_to_delete.push_back(std::move(it->second));
1223 it = async_compile_jobs_.erase(it);
1224 }
1225 DCHECK_EQ(1, isolates_.count(isolate));
1226 auto* isolate_info = isolates_[isolate].get();
1227 for (auto* native_module : isolate_info->native_modules) {
1228 DCHECK_EQ(1, native_modules_.count(native_module));
1229 modules_in_isolate.emplace_back(native_modules_[native_module]->weak_ptr);
1230 }
1231 }
1232
1233 // All modules that have not finished initial compilation yet cannot be
1234 // shared with other isolates. Hence we cancel their compilation. In
1235 // particular, this will cancel wrapper compilation which is bound to this
1236 // isolate (this would be a UAF otherwise).
1237 for (auto& weak_module : modules_in_isolate) {
1238 if (auto shared_module = weak_module.lock()) {
1239 shared_module->compilation_state()->CancelInitialCompilation();
1240 }
1241 }
1242}
1243
1245 const bool log_code = WasmCode::ShouldBeLogged(isolate);
1246 // Create the IsolateInfo.
1247 {
1248 // Create the IsolateInfo outside the mutex to reduce the size of the
1249 // critical section and to avoid lock-order-inversion issues.
1250 auto isolate_info = std::make_unique<IsolateInfo>(isolate, log_code);
1251 base::MutexGuard guard(&mutex_);
1252 DCHECK_EQ(0, isolates_.count(isolate));
1253 isolates_.emplace(isolate, std::move(isolate_info));
1254 }
1255 if (log_code) {
1256 // Log existing wrappers (which are shared across isolates).
1258 }
1259
1260 // Install sampling GC callback.
1261 // TODO(v8:7424): For now we sample module sizes in a GC callback. This will
1262 // bias samples towards apps with high memory pressure. We should switch to
1263 // using sampling based on regular intervals independent of the GC.
1264 auto callback = [](v8::Isolate* v8_isolate, v8::GCType type,
1266 Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
1267 Counters* counters = isolate->counters();
1268 WasmEngine* engine = GetWasmEngine();
1269 {
1270 base::MutexGuard lock(&engine->mutex_);
1271 DCHECK_EQ(1, engine->isolates_.count(isolate));
1272 for (auto* native_module : engine->isolates_[isolate]->native_modules) {
1273 native_module->SampleCodeSize(counters);
1274 }
1275 }
1276 // Also sample overall metadata size (this includes the metadata size of
1277 // individual NativeModules; we are summing that up twice, which could be
1278 // improved performance-wise).
1279 // The engine-wide metadata also includes global storage e.g. for the type
1280 // canonicalizer.
1281 Histogram* metadata_histogram = counters->wasm_engine_metadata_size_kb();
1282 if (metadata_histogram->Enabled()) {
1283 size_t engine_meta_data = engine->EstimateCurrentMemoryConsumption();
1284 metadata_histogram->AddSample(static_cast<int>(engine_meta_data / KB));
1285 }
1286 };
1287 isolate->heap()->AddGCEpilogueCallback(callback, v8::kGCTypeMarkSweepCompact,
1288 nullptr);
1289
1290#ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
1291 if (gdb_server_) {
1292 gdb_server_->AddIsolate(isolate);
1293 }
1294#endif // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
1295}
1296
1298#ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
1299 if (gdb_server_) {
1300 gdb_server_->RemoveIsolate(isolate);
1301 }
1302#endif // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
1303
1304 // Keep a WasmCodeRefScope which dies after the {mutex_} is released, to avoid
1305 // deadlock when code actually dies, as that requires taking the {mutex_}.
1306 // Also, keep the NativeModules themselves alive. The isolate is shutting
1307 // down, so the heap will not do that any more.
1308 std::set<std::shared_ptr<NativeModule>> native_modules_with_code_to_log;
1309 WasmCodeRefScope code_ref_scope_for_dead_code;
1310
1311 base::MutexGuard guard(&mutex_);
1312
1313 // Lookup the IsolateInfo; do not remove it yet (that happens below).
1314 auto isolates_it = isolates_.find(isolate);
1315 DCHECK_NE(isolates_.end(), isolates_it);
1316 IsolateInfo* isolate_info = isolates_it->second.get();
1317
1318 // Remove the isolate from the per-native-module info, and other cleanup.
1319 for (auto* native_module : isolate_info->native_modules) {
1320 DCHECK_EQ(1, native_modules_.count(native_module));
1321 NativeModuleInfo* native_module_info =
1322 native_modules_.find(native_module)->second.get();
1323
1324 // Check that the {NativeModule::log_code_} field has the expected value,
1325 // and update if the dying isolate was the last one with code logging
1326 // enabled.
1327 auto has_isolate_with_code_logging = [this, native_module_info] {
1328 return std::any_of(native_module_info->isolates.begin(),
1329 native_module_info->isolates.end(),
1330 [this](Isolate* isolate) {
1331 return isolates_.find(isolate)->second->log_codes;
1332 });
1333 };
1334 DCHECK_EQ(native_module->log_code(), has_isolate_with_code_logging());
1335 DCHECK_EQ(1, native_module_info->isolates.count(isolate));
1336 native_module_info->isolates.erase(isolate);
1337 if (native_module->log_code() && !has_isolate_with_code_logging()) {
1338 DisableCodeLogging(native_module);
1339 }
1340
1341 // Remove any debug code and other info for this isolate.
1342 if (native_module->HasDebugInfo()) {
1343 native_module->GetDebugInfo()->RemoveIsolate(isolate);
1344 }
1345 }
1346
1347 // Abort any outstanding GC.
1348 if (current_gc_info_) {
1350 }
1351
1352 // Clear the {code_to_log} vector.
1353 for (auto& [script_id, code_to_log] : isolate_info->code_to_log) {
1354 if (code_to_log.native_module == nullptr) {
1355 // Wrapper code objects have neither Script nor NativeModule.
1356 DCHECK_EQ(script_id, -1);
1357 continue;
1358 }
1359 native_modules_with_code_to_log.insert(code_to_log.native_module);
1360 for (WasmCode* code : code_to_log.code) {
1361 // Keep a reference in the {code_ref_scope_for_dead_code} such that the
1362 // code cannot become dead immediately.
1364 code->DecRefOnLiveCode();
1365 }
1366 }
1367 isolate_info->code_to_log.clear();
1368
1369 // Finally remove the {IsolateInfo} for this isolate.
1370 isolates_.erase(isolates_it);
1371}
1372
1374 if (code_vec.empty()) return;
1375 NativeModule* native_module = code_vec[0]->native_module();
1376 if (!native_module->log_code()) return;
1377 using TaskToSchedule =
1378 std::pair<std::shared_ptr<v8::TaskRunner>, std::unique_ptr<LogCodesTask>>;
1379 std::vector<TaskToSchedule> to_schedule;
1380 {
1381 base::MutexGuard guard(&mutex_);
1382 DCHECK_EQ(1, native_modules_.count(native_module));
1383 NativeModuleInfo* native_module_info =
1384 native_modules_.find(native_module)->second.get();
1385 std::shared_ptr<NativeModule> shared_native_module =
1386 native_module_info->weak_ptr.lock();
1387 // The NativeModule cannot be dying already at this point.
1388 DCHECK_NOT_NULL(shared_native_module);
1389 for (Isolate* isolate : native_module_info->isolates) {
1390 DCHECK_EQ(1, isolates_.count(isolate));
1391 IsolateInfo* info = isolates_[isolate].get();
1392 if (info->log_codes == false) continue;
1393
1394 auto script_it = info->scripts.find(native_module);
1395 // If the script does not yet exist, logging will happen later. If the
1396 // weak handle is cleared already, we also don't need to log any more.
1397 if (script_it == info->scripts.end()) continue;
1398
1399 // If there is no code scheduled to be logged already in that isolate,
1400 // then schedule a new task and also set an interrupt to log the newly
1401 // added code as soon as possible.
1402 if (info->code_to_log.empty()) {
1403 isolate->stack_guard()->RequestLogWasmCode();
1404 to_schedule.emplace_back(info->foreground_task_runner,
1405 std::make_unique<LogCodesTask>(isolate));
1406 }
1407
1408 WeakScriptHandle& weak_script_handle = script_it->second;
1409 auto& log_entry = info->code_to_log[weak_script_handle.script_id()];
1410 if (!log_entry.native_module) {
1411 log_entry.native_module = shared_native_module;
1412 }
1413 if (!log_entry.source_url) {
1414 log_entry.source_url = weak_script_handle.source_url();
1415 }
1416 log_entry.code.insert(log_entry.code.end(), code_vec.begin(),
1417 code_vec.end());
1418
1419 // Increment the reference count for the added {log_entry.code} entries.
1420 for (WasmCode* code : code_vec) {
1421 DCHECK_EQ(native_module, code->native_module());
1422 code->IncRef();
1423 }
1424 }
1425 }
1426 for (auto& [runner, task] : to_schedule) {
1427 runner->PostTask(std::move(task));
1428 }
1429}
1430
1432 // Wrappers don't belong to any particular NativeModule.
1433 DCHECK_NULL(code->native_module());
1434 // Fast path:
1435 if (!num_modules_with_code_logging_.load(std::memory_order_relaxed)) {
1436 return false;
1437 }
1438
1439 using TaskToSchedule =
1440 std::pair<std::shared_ptr<v8::TaskRunner>, std::unique_ptr<LogCodesTask>>;
1441 std::vector<TaskToSchedule> to_schedule;
1442 bool did_trigger_code_logging = false;
1443 {
1444 base::MutexGuard guard(&mutex_);
1445 for (const auto& entry : isolates_) {
1446 Isolate* isolate = entry.first;
1447 IsolateInfo* info = entry.second.get();
1448 if (info->log_codes == false) continue;
1449 did_trigger_code_logging = true;
1450
1451 // If this is the first code to log in that isolate, request an interrupt
1452 // to log the newly added code as soon as possible.
1453 if (info->code_to_log.empty()) {
1454 isolate->stack_guard()->RequestLogWasmCode();
1455 to_schedule.emplace_back(info->foreground_task_runner,
1456 std::make_unique<LogCodesTask>(isolate));
1457 }
1458
1459 constexpr int kNoScriptId = -1;
1460 auto& log_entry = info->code_to_log[kNoScriptId];
1461 log_entry.code.push_back(code);
1462
1463 // Increment the reference count for the added {log_entry.code} entry.
1464 // TODO(jkummerow): It might be nice to have a custom smart pointer
1465 // that manages updating the refcount for the WasmCode it holds.
1466 code->IncRef();
1467 }
1468 DCHECK_EQ(did_trigger_code_logging, num_modules_with_code_logging_.load(
1469 std::memory_order_relaxed) > 0);
1470 }
1471 for (auto& [runner, task] : to_schedule) {
1472 runner->PostTask(std::move(task));
1473 }
1474
1475 return did_trigger_code_logging;
1476}
1477
1479 base::MutexGuard guard(&mutex_);
1480 auto it = isolates_.find(isolate);
1481 DCHECK_NE(isolates_.end(), it);
1482 IsolateInfo* info = it->second.get();
1483 if (info->log_codes) return;
1484 info->log_codes = true;
1485 // Also set {NativeModule::log_code_} for all native modules currently used by
1486 // this isolate.
1487 for (NativeModule* native_module : info->native_modules) {
1488 if (!native_module->log_code()) EnableCodeLogging(native_module);
1489 }
1490}
1491
1493 // The caller should hold the mutex.
1495 DCHECK(!native_module->log_code());
1496 native_module->EnableCodeLogging();
1497 num_modules_with_code_logging_.fetch_add(1, std::memory_order_relaxed);
1498 // Check the accuracy of {num_modules_with_code_logging_}.
1499 DCHECK_EQ(
1500 num_modules_with_code_logging_.load(std::memory_order_relaxed),
1501 std::count_if(
1502 native_modules_.begin(), native_modules_.end(),
1503 [](std::pair<NativeModule* const, std::unique_ptr<NativeModuleInfo>>&
1504 pair) { return pair.first->log_code(); }));
1505}
1506
1508 // The caller should hold the mutex.
1510 DCHECK(native_module->log_code());
1511 native_module->DisableCodeLogging();
1512 num_modules_with_code_logging_.fetch_sub(1, std::memory_order_relaxed);
1513 // Check the accuracy of {num_modules_with_code_logging_}.
1514 DCHECK_EQ(
1515 num_modules_with_code_logging_.load(std::memory_order_relaxed),
1516 std::count_if(
1517 native_modules_.begin(), native_modules_.end(),
1518 [](std::pair<NativeModule* const, std::unique_ptr<NativeModuleInfo>>&
1519 pair) { return pair.first->log_code(); }));
1520}
1521
1523 // Under the mutex, get the vector of wasm code to log. Then log and decrement
1524 // the ref count without holding the mutex.
1525 std::unordered_map<int, IsolateInfo::CodeToLogPerScript> code_to_log_map;
1526 {
1527 base::MutexGuard guard(&mutex_);
1528 DCHECK_EQ(1, isolates_.count(isolate));
1529 code_to_log_map.swap(isolates_[isolate]->code_to_log);
1530 }
1531
1532 // Check again whether we still need to log code.
1533 bool should_log = WasmCode::ShouldBeLogged(isolate);
1534
1535 TRACE_EVENT0("v8.wasm", "wasm.LogCode");
1536 for (auto& [script_id, code_to_log] : code_to_log_map) {
1537 if (should_log) {
1538 for (WasmCode* code : code_to_log.code) {
1539 const char* source_url = code_to_log.source_url.get();
1540 // The source URL can be empty for eval()'ed scripts.
1541 if (!source_url) source_url = "";
1542 code->LogCode(isolate, source_url, script_id);
1543 }
1544 }
1546 }
1547}
1548
1549std::shared_ptr<NativeModule> WasmEngine::NewNativeModule(
1550 Isolate* isolate, WasmEnabledFeatures enabled_features,
1551 WasmDetectedFeatures detected_features, CompileTimeImports compile_imports,
1552 std::shared_ptr<const WasmModule> module, size_t code_size_estimate) {
1553 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
1554 "wasm.NewNativeModule");
1555#ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
1556 if (v8_flags.wasm_gdb_remote && !gdb_server_) {
1557 gdb_server_ = gdb_server::GdbServer::Create();
1558 gdb_server_->AddIsolate(isolate);
1559 }
1560#endif // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
1561
1562 if (!v8_flags.wasm_jitless) {
1563 // Initialize the import wrapper cache if that hasn't happened yet.
1565 }
1566
1567 std::shared_ptr<NativeModule> native_module =
1569 isolate, enabled_features, detected_features,
1570 std::move(compile_imports), code_size_estimate, std::move(module));
1571 base::MutexGuard lock(&mutex_);
1572 if (V8_UNLIKELY(v8_flags.experimental_wasm_pgo_to_file)) {
1573 if (!native_modules_kept_alive_for_pgo) {
1574 native_modules_kept_alive_for_pgo =
1575 new std::vector<std::shared_ptr<NativeModule>>;
1576 }
1577 native_modules_kept_alive_for_pgo->emplace_back(native_module);
1578 }
1579 auto [iterator, inserted] = native_modules_.insert(std::make_pair(
1580 native_module.get(), std::make_unique<NativeModuleInfo>(native_module)));
1581 DCHECK(inserted);
1582 NativeModuleInfo* native_module_info = iterator->second.get();
1583 native_module_info->isolates.insert(isolate);
1584 DCHECK_EQ(1, isolates_.count(isolate));
1585 IsolateInfo* isolate_info = isolates_.find(isolate)->second.get();
1586 isolate_info->native_modules.insert(native_module.get());
1587 if (isolate_info->keep_in_debug_state) {
1588 native_module->SetDebugState(kDebugging);
1589 }
1590 if (isolate_info->log_codes) {
1591 EnableCodeLogging(native_module.get());
1592 }
1593
1594 // Record memory protection key support.
1595 if (!isolate_info->pku_support_sampled) {
1596 isolate_info->pku_support_sampled = true;
1597 auto* histogram =
1598 isolate->counters()->wasm_memory_protection_keys_support();
1600 histogram->AddSample(has_mpk ? 1 : 0);
1601 }
1602
1603 isolate->counters()->wasm_modules_per_isolate()->AddSample(
1604 static_cast<int>(isolate_info->native_modules.size()));
1605 isolate->counters()->wasm_modules_per_engine()->AddSample(
1606 static_cast<int>(native_modules_.size()));
1607 return native_module;
1608}
1609
1610std::shared_ptr<NativeModule> WasmEngine::MaybeGetNativeModule(
1611 ModuleOrigin origin, base::Vector<const uint8_t> wire_bytes,
1612 const CompileTimeImports& compile_imports, Isolate* isolate) {
1613 TRACE_EVENT1("v8.wasm", "wasm.GetNativeModuleFromCache", "wire_bytes",
1614 wire_bytes.size());
1615 std::shared_ptr<NativeModule> native_module =
1616 native_module_cache_.MaybeGetNativeModule(origin, wire_bytes,
1617 compile_imports);
1618 bool remove_all_code = false;
1619 if (native_module) {
1620 TRACE_EVENT0("v8.wasm", "CacheHit");
1621 base::MutexGuard guard(&mutex_);
1622 auto& native_module_info = native_modules_[native_module.get()];
1623 if (!native_module_info) {
1624 native_module_info = std::make_unique<NativeModuleInfo>(native_module);
1625 }
1626 native_module_info->isolates.insert(isolate);
1627 auto* isolate_data = isolates_[isolate].get();
1628 isolate_data->native_modules.insert(native_module.get());
1629 if (isolate_data->keep_in_debug_state && !native_module->IsInDebugState()) {
1630 remove_all_code = true;
1631 native_module->SetDebugState(kDebugging);
1632 }
1633 if (isolate_data->log_codes && !native_module->log_code()) {
1634 EnableCodeLogging(native_module.get());
1635 }
1636 }
1637 if (remove_all_code) {
1638 WasmCodeRefScope ref_scope;
1639 native_module->RemoveCompiledCode(
1641 }
1642 return native_module;
1643}
1644
1645std::shared_ptr<NativeModule> WasmEngine::UpdateNativeModuleCache(
1646 bool has_error, std::shared_ptr<NativeModule> native_module,
1647 Isolate* isolate) {
1648 // Keep the previous pointer, but as a `void*`, because we only want to use it
1649 // later to compare pointers, and never need to dereference it.
1650 void* prev = native_module.get();
1651 native_module =
1652 native_module_cache_.Update(std::move(native_module), has_error);
1653 if (prev == native_module.get()) return native_module;
1654 bool remove_all_code = false;
1655 {
1656 base::MutexGuard guard(&mutex_);
1657 DCHECK_EQ(1, native_modules_.count(native_module.get()));
1658 native_modules_[native_module.get()]->isolates.insert(isolate);
1659 DCHECK_EQ(1, isolates_.count(isolate));
1660 auto* isolate_data = isolates_[isolate].get();
1661 isolate_data->native_modules.insert(native_module.get());
1662 if (isolate_data->keep_in_debug_state && !native_module->IsInDebugState()) {
1663 remove_all_code = true;
1664 native_module->SetDebugState(kDebugging);
1665 }
1666 if (isolate_data->log_codes && !native_module->log_code()) {
1667 EnableCodeLogging(native_module.get());
1668 }
1669 }
1670 if (remove_all_code) {
1671 WasmCodeRefScope ref_scope;
1672 native_module->RemoveCompiledCode(
1674 }
1675 return native_module;
1676}
1677
1679 size_t prefix_hash, const CompileTimeImports& compile_imports) {
1680 TRACE_EVENT0("v8.wasm", "wasm.GetStreamingCompilationOwnership");
1682 compile_imports)) {
1683 return true;
1684 }
1685 // This is only a marker, not for tracing execution time. There should be a
1686 // later "wasm.GetNativeModuleFromCache" event for trying to get the module
1687 // from the cache.
1688 TRACE_EVENT0("v8.wasm", "CacheHit");
1689 return false;
1690}
1691
1693 size_t prefix_hash, const CompileTimeImports& compile_imports) {
1694 native_module_cache_.StreamingCompilationFailed(prefix_hash, compile_imports);
1695}
1696
1698 base::MutexGuard guard(&mutex_);
1699 auto module = native_modules_.find(native_module);
1700 DCHECK_NE(native_modules_.end(), module);
1701 auto part_of_native_module = [native_module](WasmCode* code) {
1702 return code->native_module() == native_module;
1703 };
1704 for (Isolate* isolate : module->second->isolates) {
1705 DCHECK_EQ(1, isolates_.count(isolate));
1706 IsolateInfo* info = isolates_[isolate].get();
1707 DCHECK_EQ(1, info->native_modules.count(native_module));
1708 info->native_modules.erase(native_module);
1709 info->scripts.erase(native_module);
1710
1711 // Flush the Wasm code lookup cache, since it may refer to some
1712 // code within native modules that we are going to release (if a
1713 // Managed<wasm::NativeModule> object is no longer referenced).
1715
1716 // {CodeToLogPerScript} keeps the NativeModule alive, so if it dies, there
1717 // can not be any outstanding code to be logged.
1718#ifdef DEBUG
1719 for (auto& log_entry : info->code_to_log) {
1720 for (WasmCode* code : log_entry.second.code) {
1721 DCHECK_NE(native_module, code->native_module());
1722 }
1723 }
1724#endif
1725 }
1726 // If there is a GC running which has references to code contained in the
1727 // deleted {NativeModule}, remove those references.
1728 if (current_gc_info_) {
1729 for (auto it = current_gc_info_->dead_code.begin(),
1730 end = current_gc_info_->dead_code.end();
1731 it != end;) {
1732 if ((*it)->native_module() == native_module) {
1733 it = current_gc_info_->dead_code.erase(it);
1734 } else {
1735 ++it;
1736 }
1737 }
1738 TRACE_CODE_GC("Native module %p died, reducing dead code objects to %zu.\n",
1739 native_module, current_gc_info_->dead_code.size());
1740 }
1741 // If any code objects are currently tracked as dead or near-dead, remove
1742 // references belonging to the NativeModule that's being deleted.
1743 std::erase_if(potentially_dead_code_, part_of_native_module);
1744
1745 if (native_module->log_code()) DisableCodeLogging(native_module);
1746
1747 native_module_cache_.Erase(native_module);
1748 native_modules_.erase(module);
1749}
1750
1752 std::unordered_set<WasmCode*>& live_code) {
1753 TRACE_EVENT0("v8.wasm", "wasm.ReportLiveCodeForGC");
1754 TRACE_CODE_GC("Isolate %d reporting %zu live code objects.\n", isolate->id(),
1755 live_code.size());
1756 base::MutexGuard guard(&mutex_);
1757 // This report might come in late (note that we trigger both a stack guard and
1758 // a foreground task). In that case, ignore it.
1759 if (current_gc_info_ == nullptr) return;
1760 if (!RemoveIsolateFromCurrentGC(isolate)) return;
1761 isolate->counters()->wasm_module_num_triggered_code_gcs()->AddSample(
1762 current_gc_info_->gc_sequence_index);
1763 for (WasmCode* code : live_code) current_gc_info_->dead_code.erase(code);
1765}
1766
1767namespace {
1768void ReportLiveCodeFromFrameForGC(
1769 Isolate* isolate, StackFrame* frame,
1770 std::unordered_set<wasm::WasmCode*>& live_wasm_code) {
1771 if (frame->type() == StackFrame::WASM) {
1772 WasmFrame* wasm_frame = WasmFrame::cast(frame);
1773 WasmCode* code = wasm_frame->wasm_code();
1774 live_wasm_code.insert(code);
1775#if V8_TARGET_ARCH_X64
1776 if (code->for_debugging()) {
1777 Address osr_target =
1778 base::Memory<Address>(wasm_frame->fp() - kOSRTargetOffset);
1779 if (osr_target) {
1780 WasmCode* osr_code =
1781 GetWasmCodeManager()->LookupCode(isolate, osr_target);
1782 DCHECK_NOT_NULL(osr_code);
1783 live_wasm_code.insert(osr_code);
1784 }
1785 }
1786#endif
1787 } else if (frame->type() == StackFrame::WASM_TO_JS) {
1788 live_wasm_code.insert(static_cast<WasmToJsFrame*>(frame)->wasm_code());
1789 }
1790}
1791} // namespace
1792
1794 wasm::WasmCodeRefScope code_ref_scope;
1795 std::unordered_set<wasm::WasmCode*> live_wasm_code;
1796
1797 for (const std::unique_ptr<StackMemory>& stack : isolate->wasm_stacks()) {
1798 if (stack->IsActive()) {
1799 // The active stack's jump buffer does not match the current state, use
1800 // the thread info below instead.
1801 continue;
1802 }
1803 for (StackFrameIterator it(isolate, stack.get()); !it.done();
1804 it.Advance()) {
1805 StackFrame* const frame = it.frame();
1806 ReportLiveCodeFromFrameForGC(isolate, frame, live_wasm_code);
1807 }
1808 }
1809
1810 for (StackFrameIterator it(isolate, isolate->thread_local_top(),
1811 StackFrameIterator::FirstStackOnly{});
1812 !it.done(); it.Advance()) {
1813 StackFrame* const frame = it.frame();
1814 ReportLiveCodeFromFrameForGC(isolate, frame, live_wasm_code);
1815 }
1816
1817 CheckNoArchivedThreads(isolate);
1818
1819 // Flush the code lookup cache, since it may refer to some code we
1820 // are going to release.
1822
1823 ReportLiveCodeForGC(isolate, live_wasm_code);
1824}
1825
1827 base::MutexGuard guard(&mutex_);
1828 DCHECK(code->is_dying()); // Caller just marked it as such.
1829 auto added = potentially_dead_code_.insert(code);
1830 DCHECK(added.second);
1831 USE(added);
1832 new_potentially_dead_code_size_ += code->instructions().size();
1833 if (v8_flags.wasm_code_gc) {
1834 // Trigger a GC if 64kB plus 10% of committed code are potentially dead.
1835 size_t dead_code_limit =
1836 v8_flags.stress_wasm_code_gc
1837 ? 0
1838 : 64 * KB + GetWasmCodeManager()->committed_code_space() / 10;
1839 if (new_potentially_dead_code_size_ > dead_code_limit) {
1840 TriggerCodeGC_Locked(dead_code_limit);
1841 }
1842 }
1843}
1844
1845void WasmEngine::TriggerCodeGC_Locked(size_t dead_code_limit) {
1846 bool inc_gc_count =
1847 num_code_gcs_triggered_ < std::numeric_limits<int8_t>::max();
1848 if (current_gc_info_ == nullptr) {
1849 if (inc_gc_count) ++num_code_gcs_triggered_;
1851 "Triggering GC (potentially dead: %zu bytes; limit: %zu bytes).\n",
1852 new_potentially_dead_code_size_, dead_code_limit);
1854 } else if (current_gc_info_->next_gc_sequence_index == 0) {
1855 if (inc_gc_count) ++num_code_gcs_triggered_;
1857 "Scheduling another GC after the current one (potentially dead: "
1858 "%zu bytes; limit: %zu bytes).\n",
1859 new_potentially_dead_code_size_, dead_code_limit);
1860 current_gc_info_->next_gc_sequence_index = num_code_gcs_triggered_;
1861 DCHECK_NE(0, current_gc_info_->next_gc_sequence_index);
1862 }
1863}
1864
1866 if (!v8_flags.wasm_code_gc) return;
1867 base::MutexGuard guard(&mutex_);
1868 TRACE_CODE_GC("Wasm Code GC explicitly requested for testing:\n");
1871 // Let's not waste a GC sequence index when there is no code to free.
1872 TRACE_CODE_GC("But there is nothing to do.\n");
1873 return;
1874 }
1876}
1877
1879 std::vector<WasmCode*>& dead_wrappers) {
1880 base::MutexGuard guard(&mutex_);
1881 FreeDeadCodeLocked(dead_code, dead_wrappers);
1882}
1883
1885 std::vector<WasmCode*>& dead_wrappers) {
1886 TRACE_EVENT0("v8.wasm", "wasm.FreeDeadCode");
1888 for (auto& dead_code_entry : dead_code) {
1889 NativeModule* native_module = dead_code_entry.first;
1890 const std::vector<WasmCode*>& code_vec = dead_code_entry.second;
1891 TRACE_CODE_GC("Freeing %zu code object%s of module %p.\n", code_vec.size(),
1892 code_vec.size() == 1 ? "" : "s", native_module);
1893 native_module->FreeCode(base::VectorOf(code_vec));
1894 }
1895 if (dead_wrappers.size()) {
1896 TRACE_CODE_GC("Freeing %zu wrapper%s.\n", dead_wrappers.size(),
1897 dead_wrappers.size() == 1 ? "" : "s");
1898 GetWasmImportWrapperCache()->Free(dead_wrappers);
1899 }
1900}
1901
1903 Isolate* isolate, const std::shared_ptr<NativeModule>& native_module,
1904 base::Vector<const char> source_url) {
1905 {
1906 base::MutexGuard guard(&mutex_);
1907 DCHECK_EQ(1, isolates_.count(isolate));
1908 auto& scripts = isolates_[isolate]->scripts;
1909 auto it = scripts.find(native_module.get());
1910 if (it != scripts.end()) {
1911 DirectHandle<Script> weak_global_handle = it->second.handle();
1912 if (weak_global_handle.is_null()) {
1913 scripts.erase(it);
1914 } else {
1915 return DirectHandle<Script>::New(*weak_global_handle, isolate);
1916 }
1917 }
1918 }
1919 // Temporarily release the mutex to let the GC collect native modules.
1920 auto script = CreateWasmScript(isolate, native_module, source_url);
1921 {
1922 base::MutexGuard guard(&mutex_);
1923 DCHECK_EQ(1, isolates_.count(isolate));
1924 auto& scripts = isolates_[isolate]->scripts;
1925 DCHECK_EQ(0, scripts.count(native_module.get()));
1926 scripts.emplace(native_module.get(), WeakScriptHandle(script, isolate));
1927 return script;
1928 }
1929}
1930
1931std::shared_ptr<OperationsBarrier>
1935
1936void WasmEngine::TriggerGC(int8_t gc_sequence_index) {
1939 DCHECK(v8_flags.wasm_code_gc);
1941 current_gc_info_.reset(new CurrentGCInfo(gc_sequence_index));
1942 // Add all potentially dead code to this GC, and trigger a GC task in each
1943 // known isolate. We can't limit the isolates to those that contributed
1944 // potentially-dead WasmCode objects, because wrappers don't point back
1945 // at a NativeModule or Isolate.
1946 for (WasmCode* code : potentially_dead_code_) {
1947 current_gc_info_->dead_code.insert(code);
1948 }
1949 for (const auto& entry : isolates_) {
1950 Isolate* isolate = entry.first;
1951 auto& gc_task = current_gc_info_->outstanding_isolates[isolate];
1952 if (!gc_task) {
1953 auto new_task = std::make_unique<WasmGCForegroundTask>(isolate);
1954 gc_task = new_task.get();
1955 DCHECK_EQ(1, isolates_.count(isolate));
1956 isolates_[isolate]->foreground_task_runner->PostTask(std::move(new_task));
1957 }
1958 isolate->stack_guard()->RequestWasmCodeGC();
1959 }
1961 "Starting GC (nr %d). Number of potentially dead code objects: %zu\n",
1962 current_gc_info_->gc_sequence_index, current_gc_info_->dead_code.size());
1963 // Ensure that there are outstanding isolates that will eventually finish this
1964 // GC. If there are no outstanding isolates, we finish the GC immediately.
1966 DCHECK(current_gc_info_ == nullptr ||
1967 !current_gc_info_->outstanding_isolates.empty());
1968}
1969
1973 return current_gc_info_->outstanding_isolates.erase(isolate) != 0;
1974}
1975
1979 "Remaining dead code objects: %zu; outstanding isolates: %zu.\n",
1980 current_gc_info_->dead_code.size(),
1981 current_gc_info_->outstanding_isolates.size());
1982
1983 // If there are more outstanding isolates, return immediately.
1984 if (!current_gc_info_->outstanding_isolates.empty()) return;
1985
1986 // All remaining code in {current_gc_info->dead_code} is really dead.
1987 // Remove it from the set of potentially dead code, and decrement its
1988 // ref count.
1989 size_t num_freed = 0;
1990 DeadCodeMap dead_code;
1991 std::vector<WasmCode*> dead_wrappers;
1992 for (WasmCode* code : current_gc_info_->dead_code) {
1993 DCHECK(potentially_dead_code_.contains(code));
1994 DCHECK(code->is_dying());
1995 potentially_dead_code_.erase(code);
1996 if (code->DecRefOnDeadCode()) {
1997 NativeModule* native_module = code->native_module();
1998 if (native_module) {
1999 dead_code[native_module].push_back(code);
2000 } else {
2001 dead_wrappers.push_back(code);
2002 }
2003 ++num_freed;
2004 }
2005 }
2006
2007 FreeDeadCodeLocked(dead_code, dead_wrappers);
2008
2009 TRACE_CODE_GC("Found %zu dead code objects, freed %zu.\n",
2010 current_gc_info_->dead_code.size(), num_freed);
2011 USE(num_freed);
2012
2013 int8_t next_gc_sequence_index = current_gc_info_->next_gc_sequence_index;
2014 current_gc_info_.reset();
2015 if (next_gc_sequence_index != 0) TriggerGC(next_gc_sequence_index);
2016}
2017
2019 base::MutexGuard lock(&mutex_);
2020 for (const auto& [native_module, native_module_info] : native_modules_) {
2021 target->DecodeNames(native_module);
2022 }
2023}
2024
2030 size_t result = sizeof(WasmEngine);
2033 {
2034 base::MutexGuard lock(&mutex_);
2036 result += async_compile_jobs_.size() * sizeof(AsyncCompileJob);
2038
2039 // TODO(14106): Do we care about {compilation_stats_}?
2040 // TODO(14106): Do we care about {code_tracer_}?
2041
2043 result += isolates_.size() * sizeof(IsolateInfo);
2044 for (const auto& [isolate, isolate_info] : isolates_) {
2045 result += ContentSize(isolate_info->native_modules);
2046 result += ContentSize(isolate_info->scripts);
2047 result += ContentSize(isolate_info->code_to_log);
2048 }
2049
2051 result += native_modules_.size() * sizeof(NativeModuleInfo);
2052 for (const auto& [native_module, native_module_info] : native_modules_) {
2053 result += native_module->EstimateCurrentMemoryConsumption();
2054 result += ContentSize(native_module_info->isolates);
2055 }
2056
2057 if (current_gc_info_) {
2058 result += sizeof(CurrentGCInfo);
2059 result += ContentSize(current_gc_info_->outstanding_isolates);
2060 result += ContentSize(current_gc_info_->dead_code);
2061 }
2062 }
2063 if (v8_flags.trace_wasm_offheap_memory) {
2064 PrintF("WasmEngine: %zu\n", result);
2065 }
2066 return result;
2067}
2068
2070 DCHECK(v8_flags.print_wasm_offheap_memory_size);
2071 PrintF("Off-heap memory size of WasmEngine: %zu\n",
2073}
2074
2076 return deopts_executed_.load(std::memory_order::relaxed);
2077}
2078
2080 int previous_value = deopts_executed_.fetch_add(1, std::memory_order_relaxed);
2081 return previous_value + 1;
2082}
2083
2084namespace {
2085
2086struct GlobalWasmState {
2087 // Note: The order of fields is important here, as the WasmEngine's destructor
2088 // must run first. It contains a barrier which ensures that background threads
2089 // finished, and that has to happen before the WasmCodeManager gets destroyed.
2090 WasmCodeManager code_manager;
2091 WasmImportWrapperCache import_wrapper_cache;
2092 WasmEngine engine;
2093 CanonicalTypeNamesProvider type_names_provider;
2094};
2095
2096GlobalWasmState* global_wasm_state = nullptr;
2097
2098} // namespace
2099
2100// static
2102 DCHECK_NULL(global_wasm_state);
2103 global_wasm_state = new GlobalWasmState();
2104
2105#ifdef V8_ENABLE_DRUMBRAKE
2106 if (v8_flags.wasm_jitless) {
2108 }
2109#endif // V8_ENABLE_DRUMBRAKE
2110
2112}
2113
2114// static
2116#ifdef V8_ENABLE_DRUMBRAKE
2117 if (v8_flags.wasm_jitless) {
2119 }
2120#endif // V8_ENABLE_DRUMBRAKE
2121
2122 // Note: This can be called multiple times in a row (see
2123 // test-api/InitializeAndDisposeMultiple). This is fine, as
2124 // {global_wasm_state} will be nullptr then.
2125 delete global_wasm_state;
2126 global_wasm_state = nullptr;
2127
2129}
2130
2132 DCHECK_NOT_NULL(global_wasm_state);
2133 return &global_wasm_state->engine;
2134}
2135
2137 DCHECK_NOT_NULL(global_wasm_state);
2138 return &global_wasm_state->code_manager;
2139}
2140
2142 DCHECK_NOT_NULL(global_wasm_state);
2143 return &global_wasm_state->import_wrapper_cache;
2144}
2145
2147 DCHECK_NOT_NULL(global_wasm_state);
2148 return &global_wasm_state->type_names_provider;
2149}
2150
2151// {max_mem_pages} is declared in wasm-limits.h.
2153 static_assert(
2155 "Wasm memories must not be bigger than JSArrayBuffers");
2156 static_assert(kV8MaxWasmMemory32Pages <= kMaxUInt32);
2157 return std::min(uint32_t{kV8MaxWasmMemory32Pages},
2158 v8_flags.wasm_max_mem_pages.value());
2159}
2160
2162 static_assert(
2164 "Wasm memories must not be bigger than JSArrayBuffers");
2165 static_assert(kV8MaxWasmMemory64Pages <= kMaxUInt32);
2166 return std::min(uint32_t{kV8MaxWasmMemory64Pages},
2167 v8_flags.wasm_max_mem_pages.value());
2168}
2169
2170// {max_table_size} is declared in wasm-limits.h.
2171uint32_t max_table_size() {
2172 return std::min(uint32_t{kV8MaxWasmTableSize},
2173 v8_flags.wasm_max_table_size.value());
2174}
2175
2176// {max_table_init_entries} is declared in wasm-limits.h.
2178 return std::min(uint32_t{kV8MaxWasmTableInitEntries},
2179 v8_flags.wasm_max_table_size.value());
2180}
2181
2182// {max_module_size} is declared in wasm-limits.h.
2184 // Clamp the value of --wasm-max-module-size between 16 and the maximum
2185 // that the implementation supports.
2186 constexpr size_t kMin = 16;
2187 constexpr size_t kMax = kV8MaxWasmModuleSize;
2188 static_assert(kMin <= kV8MaxWasmModuleSize);
2189 return std::clamp(v8_flags.wasm_max_module_size.value(), kMin, kMax);
2190}
2191
2192#undef TRACE_CODE_GC
2193
2194} // namespace v8::internal::wasm
Isolate * isolate_
RegisterAllocator * allocator_
union v8::internal::@341::BuiltinMetadata::KindSpecificData data
std::shared_ptr< v8::TaskRunner > GetForegroundTaskRunner(Isolate *isolate)
void PostTask(std::unique_ptr< Task > task, const SourceLocation &location=SourceLocation::Current())
Definition v8-platform.h:82
void SetVerbose(bool value)
Definition api.cc:2856
void SetCaptureMessage(bool value)
Definition api.cc:2860
Hasher & AddRange(Iterator first, Iterator last)
Definition hashing.h:127
Hasher & Add(const T &t)
Definition hashing.h:121
constexpr size_t hash() const
Definition hashing.h:111
V8_INLINE void AssertHeld() const
Definition mutex.h:58
int length() const
Definition vector.h:64
Vector< T > SubVector(size_t from, size_t to) const
Definition vector.h:41
constexpr bool empty() const
Definition vector.h:73
constexpr size_t size() const
Definition vector.h:70
constexpr T * begin() const
Definition vector.h:96
constexpr T * end() const
Definition vector.h:103
static Handle< AsmWasmData > New(Isolate *isolate, std::shared_ptr< wasm::NativeModule > native_module, DirectHandle< HeapNumber > uses_bitset)
V8_INLINE bool is_null() const
Definition handles.h:693
static V8_INLINE DirectHandle< T > New(Tagged< T > object, Isolate *isolate)
Definition handles.h:664
static void Destroy(Address *location)
V8_EXPORT_PRIVATE void AddSample(int sample)
Definition counters.cc:50
StackGuard * stack_guard()
Definition isolate.h:1198
static constexpr size_t kMaxByteLength
static DirectHandle< Managed< CppType > > From(Isolate *isolate, size_t estimated_size, std::shared_ptr< CppType > shared_ptr, AllocationType allocation_type=AllocationType::kYoung)
Definition managed-inl.h:27
V8_INLINE DirectHandle< T > ToHandleChecked() const
virtual Type type() const =0
static V8_EXPORT_PRIVATE v8::Platform * GetCurrentPlatform()
Definition v8.cc:282
static V8_EXPORT_PRIVATE DirectHandle< WasmModuleObject > New(Isolate *isolate, std::shared_ptr< wasm::NativeModule > native_module, DirectHandle< Script > script)
static DirectHandle< String > ExtractUtf8StringFromModuleBytes(Isolate *, DirectHandle< WasmModuleObject >, wasm::WireBytesRef, InternalizeString)
std::shared_ptr< StreamingDecoder > CreateStreamingDecoder()
const uint8_t * pc() const
Definition decoder.h:408
void consume_bytes(uint32_t size, const char *name="skip")
Definition decoder.h:297
uint32_t consume_u32v(const char *name="var_uint32")
Definition decoder.h:251
uint8_t consume_u8(const char *name="uint8_t")
Definition decoder.h:225
void CompileFailed(const WasmError &error)
V8_WARN_UNUSED_RESULT DirectHandle< JSObject > Reify()
void StreamingCompilationFailed(size_t prefix_hash, const CompileTimeImports &compile_imports)
void Erase(NativeModule *native_module)
std::shared_ptr< NativeModule > MaybeGetNativeModule(ModuleOrigin origin, base::Vector< const uint8_t > wire_bytes, const CompileTimeImports &compile_imports)
std::map< Key, std::optional< std::weak_ptr< NativeModule > > > map_
base::ConditionVariable cache_cv_
static size_t PrefixHash(base::Vector< const uint8_t > wire_bytes)
std::shared_ptr< NativeModule > Update(std::shared_ptr< NativeModule > native_module, bool error)
bool GetStreamingCompilationOwnership(size_t prefix_hash, const CompileTimeImports &compile_imports)
void FreeCode(base::Vector< WasmCode *const >)
const WasmModule * module() const
base::Vector< const uint8_t > wire_bytes() const
void LogWasmCodes(Isolate *, Tagged< Script >)
const CompileTimeImports & compile_imports() const
static std::unique_ptr< StreamingDecoder > CreateSyncStreamingDecoder(Isolate *isolate, WasmEnabledFeatures enabled, CompileTimeImports compile_imports, DirectHandle< Context > context, const char *api_method_name_for_errors, std::shared_ptr< CompilationResultResolver > resolver)
static size_t EstimateNativeModuleMetaDataSize(const WasmModule *)
WasmCode * LookupCode(Isolate *isolate, Address pc) const
std::shared_ptr< NativeModule > NewNativeModule(Isolate *isolate, WasmEnabledFeatures enabled_features, WasmDetectedFeatures detected_features, CompileTimeImports compile_imports, size_t code_size_estimate, std::shared_ptr< const WasmModule > module)
static void DecrementRefCount(base::Vector< WasmCode *const >)
static bool ShouldBeLogged(Isolate *isolate)
static void CompileWasmFunction(Counters *, NativeModule *, WasmDetectedFeatures *detected, const WasmFunction *, ExecutionTier)
static constexpr WasmEnabledFeatures ForAsmjs()
std::atomic< size_t > num_modules_with_code_logging_
std::unique_ptr< CurrentGCInfo > current_gc_info_
void DisableCodeLogging(NativeModule *)
std::unordered_set< WasmCode * > potentially_dead_code_
std::shared_ptr< NativeModule > MaybeGetNativeModule(ModuleOrigin origin, base::Vector< const uint8_t > wire_bytes, const CompileTimeImports &compile_imports, Isolate *isolate)
void LogCode(base::Vector< WasmCode * >)
std::shared_ptr< OperationsBarrier > GetBarrierForBackgroundCompile()
void PrintCurrentMemoryConsumptionEstimate() const
static void FreeAllOrphanedGlobalHandles(WasmOrphanedGlobalHandle *start)
bool GetStreamingCompilationOwnership(size_t prefix_hash, const CompileTimeImports &compile_imports)
MaybeDirectHandle< WasmModuleObject > SyncCompile(Isolate *isolate, WasmEnabledFeatures enabled, CompileTimeImports compile_imports, ErrorThrower *thrower, base::OwnedVector< const uint8_t > bytes)
void ReportLiveCodeFromStackForGC(Isolate *)
std::atomic< int > deopts_executed_
DirectHandle< WasmModuleObject > ImportNativeModule(Isolate *isolate, std::shared_ptr< NativeModule > shared_module, base::Vector< const char > source_url)
void AddPotentiallyDeadCode(WasmCode *)
static std::atomic< int32_t > had_nondeterminism_
void FreeDeadCode(const DeadCodeMap &, std::vector< WasmCode * > &)
std::unordered_map< Isolate *, std::unique_ptr< IsolateInfo > > isolates_
std::unique_ptr< CodeTracer > code_tracer_
void ReportLiveCodeForGC(Isolate *, std::unordered_set< WasmCode * > &live_code)
void LogOutstandingCodesForIsolate(Isolate *)
std::unique_ptr< AsyncCompileJob > RemoveCompileJob(AsyncCompileJob *job)
DirectHandle< Script > GetOrCreateScript(Isolate *, const std::shared_ptr< NativeModule > &, base::Vector< const char > source_url)
AsyncCompileJob * CreateAsyncCompileJob(Isolate *isolate, WasmEnabledFeatures enabled, CompileTimeImports compile_imports, base::OwnedVector< const uint8_t > bytes, DirectHandle< Context > context, const char *api_method_name, std::shared_ptr< CompilationResultResolver > resolver, int compilation_id)
std::shared_ptr< OperationsBarrier > operations_barrier_
void CompileFunction(Counters *counters, NativeModule *native_module, uint32_t function_index, ExecutionTier tier)
std::unordered_map< NativeModule *, std::unique_ptr< NativeModuleInfo > > native_modules_
static WasmOrphanedGlobalHandle * NewOrphanedGlobalHandle(WasmOrphanedGlobalHandle **pointer)
void StreamingCompilationFailed(size_t prefix_hash, const CompileTimeImports &compile_imports)
void DeleteCompileJobsOnContext(DirectHandle< Context > context)
void DeleteCompileJobsOnIsolate(Isolate *isolate)
void AsyncCompile(Isolate *isolate, WasmEnabledFeatures enabled, CompileTimeImports compile_imports, std::shared_ptr< CompilationResultResolver > resolver, base::OwnedVector< const uint8_t > bytes, const char *api_method_name_for_errors)
std::unordered_map< AsyncCompileJob *, std::unique_ptr< AsyncCompileJob > > async_compile_jobs_
void EnterDebuggingForIsolate(Isolate *isolate)
bool SyncValidate(Isolate *isolate, WasmEnabledFeatures enabled, CompileTimeImports compile_imports, base::Vector< const uint8_t > bytes)
void TriggerCodeGC_Locked(size_t dead_code_limit)
void FreeNativeModule(NativeModule *)
void DecodeAllNameSections(CanonicalTypeNamesProvider *target)
DirectHandle< WasmModuleObject > FinalizeTranslatedAsmJs(Isolate *isolate, DirectHandle< AsmWasmData > asm_wasm_data, DirectHandle< Script > script)
std::atomic< int > next_compilation_id_
size_t EstimateCurrentMemoryConsumption() const
bool HasRunningCompileJob(Isolate *isolate)
std::shared_ptr< CompilationStatistics > compilation_stats_
void LeaveDebuggingForIsolate(Isolate *isolate)
std::shared_ptr< NativeModule > NewNativeModule(Isolate *isolate, WasmEnabledFeatures enabled_features, WasmDetectedFeatures detected_features, CompileTimeImports compile_imports, std::shared_ptr< const WasmModule > module, size_t code_size_estimate)
void ClearWeakScriptHandle(Isolate *isolate, std::unique_ptr< Address * > location)
MaybeHandle< AsmWasmData > SyncCompileTranslatedAsmJs(Isolate *isolate, ErrorThrower *thrower, base::OwnedVector< const uint8_t > bytes, DirectHandle< Script > script, base::Vector< const uint8_t > asm_js_offset_table_bytes, DirectHandle< HeapNumber > uses_bitset, LanguageMode language_mode)
NativeModuleCache native_module_cache_
bool RemoveIsolateFromCurrentGC(Isolate *)
void RemoveIsolate(Isolate *isolate)
std::shared_ptr< NativeModule > UpdateNativeModuleCache(bool has_error, std::shared_ptr< NativeModule > native_module, Isolate *isolate)
std::shared_ptr< CompilationStatistics > GetOrCreateTurboStatistics()
std::unordered_map< NativeModule *, std::vector< WasmCode * > > DeadCodeMap
void FreeDeadCodeLocked(const DeadCodeMap &, std::vector< WasmCode * > &)
std::shared_ptr< StreamingDecoder > StartStreamingCompilation(Isolate *isolate, WasmEnabledFeatures enabled, CompileTimeImports compile_imports, DirectHandle< Context > context, const char *api_method_name, std::shared_ptr< CompilationResultResolver > resolver)
void TriggerGC(int8_t gc_sequence_index)
void AsyncInstantiate(Isolate *isolate, std::unique_ptr< InstantiationResultResolver > resolver, DirectHandle< WasmModuleObject > module_object, MaybeDirectHandle< JSReceiver > imports)
MaybeDirectHandle< WasmInstanceObject > SyncInstantiate(Isolate *isolate, ErrorThrower *thrower, DirectHandle< WasmModuleObject > module_object, MaybeDirectHandle< JSReceiver > imports, MaybeDirectHandle< JSArrayBuffer > memory)
void AddIsolate(Isolate *isolate)
std::pair< size_t, size_t > FlushLiftoffCode()
TypeCanonicalizer type_canonicalizer_
void Free(std::vector< WasmCode * > &wrappers)
static void Destroy(WasmOrphanedGlobalHandle *that)
void InitializeLocation(std::unique_ptr< Address * > location)
std::unique_ptr< Address * > location_
static std::unique_ptr< GdbServer > Create()
static const ContextId Empty()
Definition v8-metrics.h:195
Handle< Code > code
int start
int end
Isolate * isolate
TNode< Context > context
TNode< Object > callback
ZoneVector< RpoNumber > & result
LinkageLocation location_
STL namespace.
T & Memory(Address addr)
Definition memory.h:18
constexpr Vector< T > VectorOf(T *start, size_t size)
Definition vector.h:360
constexpr size_t kV8MaxWasmTableInitEntries
Definition wasm-limits.h:59
V8_EXPORT_PRIVATE WasmCodePointerTable * GetProcessWideWasmCodePointerTable()
uint32_t max_mem32_pages()
WasmImportWrapperCache * GetWasmImportWrapperCache()
uint32_t max_table_size()
uint32_t max_mem64_pages()
WasmCodeManager * GetWasmCodeManager()
constexpr size_t kV8MaxWasmMemory64Pages
Definition wasm-limits.h:46
CanonicalTypeNamesProvider * GetCanonicalTypeNamesProvider()
size_t GetWireBytesHash(base::Vector< const uint8_t > wire_bytes)
constexpr size_t kV8MaxWasmModuleSize
Definition wasm-limits.h:50
MaybeDirectHandle< WasmInstanceObject > InstantiateToInstanceObject(Isolate *isolate, ErrorThrower *thrower, DirectHandle< WasmModuleObject > module_object, MaybeDirectHandle< JSReceiver > imports, MaybeDirectHandle< JSArrayBuffer > memory_buffer)
ModuleResult DecodeWasmModule(WasmEnabledFeatures enabled_features, base::Vector< const uint8_t > wire_bytes, bool validate_functions, ModuleOrigin origin, Counters *counters, std::shared_ptr< metrics::Recorder > metrics_recorder, v8::metrics::Recorder::ContextId context_id, DecodingMethod decoding_method, WasmDetectedFeatures *detected_features)
std::shared_ptr< NativeModule > CompileToNativeModule(Isolate *isolate, WasmEnabledFeatures enabled_features, WasmDetectedFeatures detected_features, CompileTimeImports compile_imports, ErrorThrower *thrower, std::shared_ptr< const WasmModule > module, base::OwnedVector< const uint8_t > wire_bytes, int compilation_id, v8::metrics::Recorder::ContextId context_id, ProfileInformation *pgo_info)
WasmError ValidateAndSetBuiltinImports(const WasmModule *module, base::Vector< const uint8_t > wire_bytes, const CompileTimeImports &imports, WasmDetectedFeatures *detected)
uint32_t max_table_init_entries()
size_t ContentSize(const std::vector< T > &vector)
WasmEngine * GetWasmEngine()
constexpr size_t kWasmPageSize
std::unique_ptr< ProfileInformation > LoadProfileFromFile(const WasmModule *module, base::Vector< const uint8_t > wire_bytes)
Definition pgo.cc:222
constexpr size_t kV8MaxWasmTableSize
Definition wasm-limits.h:58
constexpr size_t kV8MaxWasmMemory32Pages
Definition wasm-limits.h:43
PerThreadAssertScopeDebugOnly< false, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > DisallowGarbageCollection
@ SKIP_WRITE_BARRIER
Definition objects.h:52
void PrintF(const char *format,...)
Definition utils.cc:39
Tagged(T object) -> Tagged< T >
V8_INLINE DirectHandle< T > direct_handle(Tagged< T > object, Isolate *isolate)
Flag flags[]
Definition flags.cc:3797
V8_EXPORT_PRIVATE FlagValues v8_flags
constexpr int kMaxInt
Definition globals.h:374
kMemory0SizeOffset Address kNewAllocationLimitAddressOffset Address kOldAllocationLimitAddressOffset uint8_t kGlobalsStartOffset kJumpTableStartOffset std::atomic< uint32_t > kTieringBudgetArrayOffset kDataSegmentStartsOffset kElementSegmentsOffset instance_object
constexpr uint32_t kMaxUInt32
Definition globals.h:387
kInterpreterTrampolineOffset script
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
GCCallbackFlags
@ kGCTypeMarkSweepCompact
#define V8_NOEXCEPT
#define FATAL(...)
Definition logging.h:47
#define DCHECK_NULL(val)
Definition logging.h:491
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define USE(...)
Definition macros.h:293
#define UPDATE_WHEN_CLASS_CHANGES(classname, size)
std::unordered_map< Isolate *, WasmGCForegroundTask * > outstanding_isolates
std::unordered_set< WasmCode * > dead_code
std::unordered_set< NativeModule * > native_modules
const std::shared_ptr< Counters > async_counters
IsolateInfo(Isolate *isolate, bool log_code)
std::unordered_map< NativeModule *, WeakScriptHandle > scripts
std::shared_ptr< v8::TaskRunner > foreground_task_runner
std::unordered_map< int, CodeToLogPerScript > code_to_log
NativeModuleInfo(std::weak_ptr< NativeModule > native_module)
std::unordered_set< Isolate * > isolates
std::array< WasmDebugSymbols, WasmDebugSymbols::kNumTypes > debug_symbols
#define TRACE_EVENT0(category_group, name)
#define TRACE_DISABLED_BY_DEFAULT(name)
#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val)
#define V8_UNLIKELY(condition)
Definition v8config.h:660
int script_id_
CanonicalTypeNamesProvider type_names_provider
WasmImportWrapperCache import_wrapper_cache
WasmCodeManager code_manager
std::shared_ptr< const char[]> source_url_
#define TRACE_CODE_GC(...)
WasmOrphanedGlobalHandle * handle_
wasm::ValueType type