Commit c52d2854 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[wasm][gc] Track potentially dead code per engine

This adds data structures to track potentially dead code in the wasm
engine. The engine will then trigger an engine-wide GC once the
potentially dead code reaches a certain threshold.

R=mstarzinger@chromium.org

Bug: v8:8217
Change-Id: I13216a66bb8e8e1594b165a65708e53057e9e535
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1559736
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60718}
parent 5abd06f3
......@@ -371,6 +371,26 @@ WasmCode::~WasmCode() {
}
}
V8_WARN_UNUSED_RESULT bool WasmCode::DecRefOnPotentiallyDeadCode() {
if (native_module_->engine()->AddPotentiallyDeadCode(this)) {
// The code just became potentially dead. The ref count we wanted to
// decrement is now transferred to the set of potentially dead code, and
// will be decremented when the next GC is run.
return false;
}
// If we reach here, the code was already potentially dead. Decrement the ref
// count, and return true if it drops to zero.
int old_count = ref_count_.load(std::memory_order_relaxed);
while (true) {
DCHECK_LE(1, old_count);
if (ref_count_.compare_exchange_weak(old_count, old_count - 1,
std::memory_order_relaxed)) {
return old_count == 1;
}
}
}
// static
void WasmCode::DecrementRefCount(Vector<WasmCode*> code_vec) {
// Decrement the ref counter of all given code objects. Keep the ones whose
// ref count drops to zero.
......
......@@ -151,14 +151,22 @@ class V8_EXPORT_PRIVATE WasmCode final {
USE(old_val);
}
// Decrement the ref count. Returns whether this code becomes dead and needs
// to be freed.
V8_WARN_UNUSED_RESULT bool DecRef() {
int old_count = ref_count_.fetch_sub(1, std::memory_order_relaxed);
DCHECK_LE(1, old_count);
return old_count == 1;
int old_count = ref_count_.load(std::memory_order_relaxed);
while (true) {
DCHECK_LE(1, old_count);
if (V8_UNLIKELY(old_count == 1)) return DecRefOnPotentiallyDeadCode();
if (ref_count_.compare_exchange_weak(old_count, old_count - 1,
std::memory_order_relaxed)) {
return false;
}
}
}
// Decrement the ref count on set of {WasmCode} objects, potentially belonging
// to different {NativeModule}s.
// Decrement the ref count on a set of {WasmCode} objects, potentially
// belonging to different {NativeModule}s. Dead code will be deleted.
static void DecrementRefCount(Vector<WasmCode*>);
enum FlushICache : bool { kFlushICache = true, kNoFlushICache = false };
......@@ -210,6 +218,10 @@ class V8_EXPORT_PRIVATE WasmCode final {
// trap_handler_index.
void RegisterTrapHandlerData();
// Slow path for {DecRef}: The code becomes potentially dead.
// Returns whether this code becomes dead and needs to be freed.
bool DecRefOnPotentiallyDeadCode();
Vector<byte> instructions_;
OwnedVector<const byte> reloc_info_;
OwnedVector<const byte> source_position_table_;
......@@ -236,7 +248,7 @@ class V8_EXPORT_PRIVATE WasmCode final {
// 1) The jump table.
// 2) Function tables.
// 3) {WasmCodeRefScope}s.
// 4) Threads currently executing this code.
// 4) The set of potentially dead code in the {WasmEngine}.
// If a decrement of (1) or (2) would drop the ref count to 0, that code
// becomes a candidate for garbage collection. At that point, we add
// ref counts for (4) *before* decrementing the counter to ensure the code
......
......@@ -104,6 +104,16 @@ struct WasmEngine::IsolateInfo {
std::shared_ptr<v8::TaskRunner> foreground_task_runner;
};
struct WasmEngine::NativeModuleInfo {
// Set of isolates using this NativeModule.
std::unordered_set<Isolate*> isolates;
// Set of potentially dead code. The ref-count of these code objects was
// incremented for each Isolate that might still execute the code, and is
// decremented on {RemoveIsolate} or on a GC.
std::unordered_set<WasmCode*> potentially_dead_code;
};
WasmEngine::WasmEngine()
: code_manager_(&memory_tracker_, FLAG_wasm_max_code_space * MB) {}
......@@ -115,7 +125,7 @@ WasmEngine::~WasmEngine() {
// All Isolates have been deregistered.
DCHECK(isolates_.empty());
// All NativeModules did die.
DCHECK(isolates_per_native_module_.empty());
DCHECK(native_modules_.empty());
}
bool WasmEngine::SyncValidate(Isolate* isolate, const WasmFeatures& enabled,
......@@ -350,8 +360,8 @@ Handle<WasmModuleObject> WasmEngine::ImportNativeModule(
base::MutexGuard lock(&mutex_);
DCHECK_EQ(1, isolates_.count(isolate));
isolates_[isolate]->native_modules.insert(native_module);
DCHECK_EQ(1, isolates_per_native_module_.count(native_module));
isolates_per_native_module_[native_module].insert(isolate);
DCHECK_EQ(1, native_modules_.count(native_module));
native_modules_[native_module]->isolates.insert(isolate);
}
return module_object;
}
......@@ -459,8 +469,10 @@ void WasmEngine::RemoveIsolate(Isolate* isolate) {
auto it = isolates_.find(isolate);
DCHECK_NE(isolates_.end(), it);
for (NativeModule* native_module : it->second->native_modules) {
DCHECK_EQ(1, isolates_per_native_module_[native_module].count(isolate));
isolates_per_native_module_[native_module].erase(isolate);
DCHECK_EQ(1, native_modules_.count(native_module));
DCHECK_EQ(1, native_modules_[native_module]->isolates.count(isolate));
auto* info = native_modules_[native_module].get();
info->isolates.erase(isolate);
}
if (auto* task = it->second->log_codes_task) task->Cancel();
isolates_.erase(it);
......@@ -469,8 +481,8 @@ void WasmEngine::RemoveIsolate(Isolate* isolate) {
void WasmEngine::LogCode(WasmCode* code) {
base::MutexGuard guard(&mutex_);
NativeModule* native_module = code->native_module();
DCHECK_EQ(1, isolates_per_native_module_.count(native_module));
for (Isolate* isolate : isolates_per_native_module_[native_module]) {
DCHECK_EQ(1, native_modules_.count(native_module));
for (Isolate* isolate : native_modules_[native_module]->isolates) {
DCHECK_EQ(1, isolates_.count(isolate));
IsolateInfo* info = isolates_[isolate].get();
if (info->log_codes == false) continue;
......@@ -498,8 +510,10 @@ std::shared_ptr<NativeModule> WasmEngine::NewNativeModule(
code_manager_.NewNativeModule(this, isolate, enabled, code_size_estimate,
can_request_more, std::move(module));
base::MutexGuard lock(&mutex_);
isolates_per_native_module_[native_module.get()].insert(isolate);
DCHECK_EQ(1, isolates_.count(isolate));
auto pair = native_modules_.insert(std::make_pair(
native_module.get(), base::make_unique<NativeModuleInfo>()));
DCHECK(pair.second); // inserted new entry.
pair.first->second.get()->isolates.insert(isolate);
isolates_[isolate]->native_modules.insert(native_module.get());
return native_module;
}
......@@ -507,14 +521,14 @@ std::shared_ptr<NativeModule> WasmEngine::NewNativeModule(
void WasmEngine::FreeNativeModule(NativeModule* native_module) {
{
base::MutexGuard guard(&mutex_);
auto it = isolates_per_native_module_.find(native_module);
DCHECK_NE(isolates_per_native_module_.end(), it);
for (Isolate* isolate : it->second) {
auto it = native_modules_.find(native_module);
DCHECK_NE(native_modules_.end(), it);
for (Isolate* isolate : it->second->isolates) {
DCHECK_EQ(1, isolates_.count(isolate));
DCHECK_EQ(1, isolates_[isolate]->native_modules.count(native_module));
isolates_[isolate]->native_modules.erase(native_module);
}
isolates_per_native_module_.erase(it);
native_modules_.erase(it);
}
code_manager_.FreeNativeModule(native_module);
}
......@@ -544,8 +558,8 @@ class SampleTopTierCodeSizeTask : public CancelableTask {
void WasmEngine::SampleTopTierCodeSizeInAllIsolates(
const std::shared_ptr<NativeModule>& native_module) {
base::MutexGuard lock(&mutex_);
DCHECK_EQ(1, isolates_per_native_module_.count(native_module.get()));
for (Isolate* isolate : isolates_per_native_module_[native_module.get()]) {
DCHECK_EQ(1, native_modules_.count(native_module.get()));
for (Isolate* isolate : native_modules_[native_module.get()]->isolates) {
DCHECK_EQ(1, isolates_.count(isolate));
IsolateInfo* info = isolates_[isolate].get();
info->foreground_task_runner->PostTask(
......@@ -553,6 +567,17 @@ void WasmEngine::SampleTopTierCodeSizeInAllIsolates(
}
}
bool WasmEngine::AddPotentiallyDeadCode(WasmCode* code) {
base::MutexGuard guard(&mutex_);
auto it = native_modules_.find(code->native_module());
DCHECK_NE(native_modules_.end(), it);
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.
return true;
}
namespace {
DEFINE_LAZY_LEAKY_OBJECT_GETTER(std::shared_ptr<WasmEngine>,
......
......@@ -181,6 +181,13 @@ class V8_EXPORT_PRIVATE WasmEngine {
// This will spawn foreground tasks that do *not* keep the NativeModule alive.
void SampleTopTierCodeSizeInAllIsolates(const std::shared_ptr<NativeModule>&);
// 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,
// {false} if an entry already exists. The ref count is *unchanged* in any
// case.
V8_WARN_UNUSED_RESULT bool AddPotentiallyDeadCode(WasmCode*);
// Call on process start and exit.
static void InitializeOncePerProcess();
static void GlobalTearDown();
......@@ -191,6 +198,7 @@ class V8_EXPORT_PRIVATE WasmEngine {
private:
struct IsolateInfo;
struct NativeModuleInfo;
AsyncCompileJob* CreateAsyncCompileJob(
Isolate* isolate, const WasmFeatures& enabled,
......@@ -224,10 +232,13 @@ class V8_EXPORT_PRIVATE WasmEngine {
// Set of isolates which use this WasmEngine.
std::unordered_map<Isolate*, std::unique_ptr<IsolateInfo>> isolates_;
// Maps each NativeModule to the set of Isolates that have access to that
// NativeModule. The isolate sets currently only grow, they never shrink.
std::unordered_map<NativeModule*, std::unordered_set<Isolate*>>
isolates_per_native_module_;
// Set of native modules managed by this engine.
std::unordered_map<NativeModule*, std::unique_ptr<NativeModuleInfo>>
native_modules_;
// Size of code that became dead since the last GC. If this exceeds a certain
// threshold, a new GC is triggered.
size_t new_potentially_dead_code_size_ = 0;
// 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