Commit 7e213102 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[wasm][gc] Add data structure for running GC

This adds the {CurrentGCInfo} data structure to the wasm engine. It
holds all information needed for the current GC cycle, which is
currently only the set of Isolates that still need to report their live
code, and the set of dead wasm code (which is potentially reduced when
Isolates report live code).

Running the GC is guarded by the new '--wasm-code-gc' flag. I will add
this to the --future variant in a follow-up CL.

R=mstarzinger@chromium.org

Bug: v8:8217
Change-Id: I82e96d986cf5a758bc0f94e49e13ad78fae4e935
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1559738
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60721}
parent e12ab4c3
......@@ -689,6 +689,7 @@ DEFINE_DEBUG_BOOL(trace_wasm_lazy_compilation, false,
DEFINE_NEG_IMPLICATION(wasm_interpret_all, asm_wasm_lazy_compilation)
DEFINE_NEG_IMPLICATION(wasm_interpret_all, wasm_lazy_compilation)
DEFINE_NEG_IMPLICATION(wasm_interpret_all, wasm_tier_up)
DEFINE_BOOL(wasm_code_gc, false, "enable garbage collection of wasm code")
// Profiler flags.
DEFINE_INT(frame_count, 1, "number of stack frames inspected by the profiler")
......
......@@ -80,8 +80,39 @@ class LogCodesTask : public Task {
Isolate* isolate_;
std::vector<WasmCode*> code_to_log_;
};
class WasmGCForegroundTask : public Task {
public:
explicit WasmGCForegroundTask(Isolate* isolate) : isolate_(isolate) {
DCHECK_NOT_NULL(isolate);
}
void Run() final {
if (isolate_ == nullptr) return; // cancelled.
WasmEngine* engine = isolate_->wasm_engine();
// If the foreground task is executing, there is no wasm code active. Just
// report an empty set of live wasm code.
engine->ReportLiveCodeForGC(isolate_, Vector<WasmCode*>{});
}
void Cancel() { isolate_ = nullptr; }
private:
Isolate* isolate_;
};
} // namespace
struct WasmEngine::CurrentGCInfo {
// Set of isolates that did not scan their stack yet for used WasmCode, and
// their scheduled foreground task.
std::unordered_map<Isolate*, WasmGCForegroundTask*> outstanding_isolates;
// Set of dead code. Filled with all potentially dead code on initialization.
// Code that is still in-use is removed by the individual isolates.
std::unordered_set<WasmCode*> dead_code;
};
struct WasmEngine::IsolateInfo {
explicit IsolateInfo(Isolate* isolate)
: log_codes(WasmCode::ShouldBeLogged(isolate)) {
......@@ -473,6 +504,16 @@ void WasmEngine::RemoveIsolate(Isolate* isolate) {
DCHECK_EQ(1, native_modules_[native_module]->isolates.count(isolate));
auto* info = native_modules_[native_module].get();
info->isolates.erase(isolate);
if (current_gc_info_) {
auto it = current_gc_info_->outstanding_isolates.find(isolate);
if (it != current_gc_info_->outstanding_isolates.end()) {
if (it->second) it->second->Cancel();
current_gc_info_->outstanding_isolates.erase(it);
}
for (WasmCode* code : info->potentially_dead_code) {
current_gc_info_->dead_code.erase(code);
}
}
}
if (auto* task = it->second->log_codes_task) task->Cancel();
isolates_.erase(it);
......@@ -567,6 +608,32 @@ void WasmEngine::SampleTopTierCodeSizeInAllIsolates(
}
}
void WasmEngine::ReportLiveCodeForGC(Isolate* isolate,
Vector<WasmCode*> live_code) {
base::MutexGuard guard(&mutex_);
DCHECK_NOT_NULL(current_gc_info_);
auto outstanding_isolate_it =
current_gc_info_->outstanding_isolates.find(isolate);
DCHECK_NE(current_gc_info_->outstanding_isolates.end(),
outstanding_isolate_it);
auto* fg_task = outstanding_isolate_it->second;
if (fg_task) fg_task->Cancel();
current_gc_info_->outstanding_isolates.erase(outstanding_isolate_it);
for (WasmCode* code : live_code) current_gc_info_->dead_code.erase(code);
if (current_gc_info_->outstanding_isolates.empty()) {
std::unordered_map<NativeModule*, std::vector<WasmCode*>>
dead_code_per_native_module;
for (WasmCode* code : current_gc_info_->dead_code) {
dead_code_per_native_module[code->native_module()].push_back(code);
}
for (auto& entry : dead_code_per_native_module) {
entry.first->FreeCode(VectorOf(entry.second));
}
current_gc_info_.reset();
}
}
bool WasmEngine::AddPotentiallyDeadCode(WasmCode* code) {
base::MutexGuard guard(&mutex_);
auto it = native_modules_.find(code->native_module());
......@@ -574,10 +641,41 @@ bool WasmEngine::AddPotentiallyDeadCode(WasmCode* code) {
auto added = it->second->potentially_dead_code.insert(code);
if (!added.second) return false; // An entry already existed.
new_potentially_dead_code_size_ += code->instructions().size();
// TODO(clemensh): Trigger GC if the size exceeds a certain threshold.
// Trigger a GC if 1MiB plus 10% of committed code are potentially dead.
size_t dead_code_limit = 1 * MB + code_manager_.committed_code_space() / 10;
if (FLAG_wasm_code_gc && new_potentially_dead_code_size_ > dead_code_limit &&
!current_gc_info_) {
TriggerGC();
}
return true;
}
void WasmEngine::TriggerGC() {
DCHECK_NULL(current_gc_info_);
DCHECK(FLAG_wasm_code_gc);
current_gc_info_.reset(new CurrentGCInfo());
// Add all potentially dead code to this GC, and trigger a GC task in each
// isolate.
// TODO(clemensh): Also trigger a stack check interrupt.
for (auto& entry : native_modules_) {
NativeModuleInfo* info = entry.second.get();
if (info->potentially_dead_code.empty()) continue;
for (auto* isolate : native_modules_[entry.first]->isolates) {
auto& gc_task = current_gc_info_->outstanding_isolates[isolate];
if (!gc_task) {
auto new_task = base::make_unique<WasmGCForegroundTask>(isolate);
gc_task = new_task.get();
DCHECK_EQ(1, isolates_.count(isolate));
isolates_[isolate]->foreground_task_runner->PostTask(
std::move(new_task));
}
}
for (WasmCode* code : info->potentially_dead_code) {
current_gc_info_->dead_code.insert(code);
}
}
}
namespace {
DEFINE_LAZY_LEAKY_OBJECT_GETTER(std::shared_ptr<WasmEngine>,
......
......@@ -181,6 +181,9 @@ class V8_EXPORT_PRIVATE WasmEngine {
// This will spawn foreground tasks that do *not* keep the NativeModule alive.
void SampleTopTierCodeSizeInAllIsolates(const std::shared_ptr<NativeModule>&);
// Called by each Isolate to report its live code for a GC cycle.
void ReportLiveCodeForGC(Isolate*, Vector<WasmCode*> live_code);
// Add potentially dead code. The occurrence in the set of potentially dead
// code counts as a reference, and is decremented on the next GC.
// Returns {true} if the code was added to the set of potentially dead code,
......@@ -197,6 +200,7 @@ class V8_EXPORT_PRIVATE WasmEngine {
static std::shared_ptr<WasmEngine> GetWasmEngine();
private:
struct CurrentGCInfo;
struct IsolateInfo;
struct NativeModuleInfo;
......@@ -206,6 +210,8 @@ class V8_EXPORT_PRIVATE WasmEngine {
Handle<Context> context,
std::shared_ptr<CompilationResultResolver> resolver);
void TriggerGC();
WasmMemoryTracker memory_tracker_;
WasmCodeManager code_manager_;
AccountingAllocator allocator_;
......@@ -240,6 +246,10 @@ class V8_EXPORT_PRIVATE WasmEngine {
// threshold, a new GC is triggered.
size_t new_potentially_dead_code_size_ = 0;
// If an engine-wide GC is currently running, this pointer stores information
// about that.
std::unique_ptr<CurrentGCInfo> current_gc_info_;
// End of fields protected by {mutex_}.
//////////////////////////////////////////////////////////////////////////////
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment