blob: 51a20ad8d94e271218be4b993bce7067aaa176fa [file] [log] [blame]
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/wasm/wasm-code-pointer-table.h"
#include "src/sandbox/external-entity-table-inl.h"
#include "src/wasm/wasm-code-pointer-table-inl.h"
namespace v8::internal::wasm {
void WasmCodePointerTable::Initialize() { Base::Initialize(); }
void WasmCodePointerTable::TearDown() {
FreeNativeFunctionHandles();
SweepSegments(0);
DCHECK(freelist_head_.load().is_empty());
Base::TearDown();
}
DEFINE_LAZY_LEAKY_OBJECT_GETTER(WasmCodePointerTable,
GetProcessWideWasmCodePointerTable)
std::vector<uint32_t> WasmCodePointerTable::FreelistToVector(
WasmCodePointerTable::FreelistHead freelist) {
DCHECK(!freelist.is_empty());
std::vector<uint32_t> entries(freelist.length());
uint32_t entry = freelist.next();
for (uint32_t i = 0; i < freelist.length(); i++) {
entries[i] = entry;
entry = at(entry).GetNextFreelistEntryIndex();
}
return entries;
}
WasmCodePointerTable::FreelistHead WasmCodePointerTable::VectorToFreelist(
std::vector<uint32_t> entries) {
if (entries.empty()) {
return FreelistHead();
}
FreelistHead new_freelist =
FreelistHead(entries[0], static_cast<uint32_t>(entries.size()));
WriteScope write_scope("Freelist write");
for (size_t i = 0; i < entries.size() - 1; i++) {
uint32_t entry = entries[i];
uint32_t next_entry = entries[i + 1];
at(entry).MakeFreelistEntry(next_entry);
}
return new_freelist;
}
void WasmCodePointerTable::SweepSegments(size_t threshold) {
if (threshold < kEntriesPerSegment) {
// We need at least a whole empty segment if we want to sweep anything.
threshold = kEntriesPerSegment;
}
FreelistHead initial_head, empty_freelist;
do {
initial_head = ReadFreelistHead();
if (initial_head.length() < threshold) {
return;
}
// Try to unlink the freelist. If it fails, try again.
} while (
!freelist_head_.compare_exchange_strong(initial_head, empty_freelist));
// We unlinked the whole free list, so we have exclusive access to it at
// this point.
// Now search for empty segments (== all entries are freelist entries) and
// unlink them.
std::vector<uint32_t> freelist_entries = FreelistToVector(initial_head);
std::sort(freelist_entries.begin(), freelist_entries.end());
// The minimum threshold is kEntriesPerSegment.
DCHECK_GE(freelist_entries.size(), kEntriesPerSegment);
// We iterate over all freelist entries and copy them over to a new vector,
// while skipping and unmapping empty segments.
std::vector<uint32_t> new_freelist_entries;
for (size_t i = 0; i < freelist_entries.size(); i++) {
uint32_t entry = freelist_entries[i];
Segment segment = Segment::Containing(entry);
if (segment.first_entry() == entry &&
i + kEntriesPerSegment - 1 < freelist_entries.size()) {
uint32_t last_entry = freelist_entries[i + kEntriesPerSegment - 1];
if (segment.last_entry() == last_entry) {
// The whole segment is empty. Delete the segment and skip all
// entries;
FreeTableSegment(segment);
i += kEntriesPerSegment - 1;
continue;
}
}
new_freelist_entries.push_back(entry);
}
DCHECK_LE(new_freelist_entries.size(), freelist_entries.size());
DCHECK(IsAligned(freelist_entries.size() - new_freelist_entries.size(),
kEntriesPerSegment));
if (new_freelist_entries.empty()) {
return;
}
// Finally, add the new freelist back.
uint32_t last_element = new_freelist_entries.back();
FreelistHead new_freelist = VectorToFreelist(new_freelist_entries);
LinkFreelist(new_freelist, last_element);
}
WasmCodePointer WasmCodePointerTable::GetOrCreateHandleForNativeFunction(
Address addr) {
base::MutexGuard guard(&native_function_map_mutex_);
auto it = native_function_map_.find(addr);
if (it != native_function_map_.end()) {
return it->second;
}
WasmCodePointer handle = AllocateAndInitializeEntry(addr, -1);
native_function_map_.insert({addr, handle});
return handle;
}
bool WasmCodePointerTable::EntrypointEqualTo(WasmCodePointer index,
Address address) {
return at(index.value()).GetEntrypointWithoutSignatureCheck() == address;
}
void WasmCodePointerTable::FreeNativeFunctionHandles() {
base::MutexGuard guard(&native_function_map_mutex_);
for (auto const& [address, handle] : native_function_map_) {
FreeEntry(handle);
}
native_function_map_.clear();
}
} // namespace v8::internal::wasm