#include "src/sandbox/cppheap-pointer-table.h"
#include "src/execution/isolate.h"
#include "src/logging/counters.h"
#include "src/sandbox/cppheap-pointer-table-inl.h"
#ifdef V8_COMPRESS_POINTERS
namespace v8 {
namespace internal {
uint32_t CppHeapPointerTable::SweepAndCompact(Space* space,
Counters* counters) {
DCHECK(space->BelongsTo(this));
base::MutexGuard guard(&space->mutex_);
base::MutexGuard invalidated_fields_guard(&space->invalidated_fields_mutex_);
space->freelist_head_.store(kEntryAllocationIsForbiddenMarker,
std::memory_order_relaxed);
uint32_t start_of_evacuation_area =
space->start_of_evacuation_area_.load(std::memory_order_relaxed);
bool evacuation_was_successful = false;
if (space->IsCompacting()) {
if (space->CompactingWasAborted()) {
start_of_evacuation_area &= ~Space::kCompactionAbortedMarker;
} else {
evacuation_was_successful = true;
}
DCHECK(IsAligned(start_of_evacuation_area, kEntriesPerSegment));
space->StopCompacting();
}
uint32_t current_freelist_head = 0;
uint32_t current_freelist_length = 0;
auto AddToFreelist = [&](uint32_t entry_index) {
at(entry_index).MakeFreelistEntry(current_freelist_head);
current_freelist_head = entry_index;
current_freelist_length++;
};
std::vector<Segment> segments_to_deallocate;
for (auto segment : base::Reversed(space->segments_)) {
bool segment_will_be_evacuated =
evacuation_was_successful &&
segment.first_entry() >= start_of_evacuation_area;
uint32_t previous_freelist_head = current_freelist_head;
uint32_t previous_freelist_length = current_freelist_length;
for (uint32_t i = segment.last_entry(); i >= segment.first_entry(); i--) {
auto payload = at(i).GetRawPayload();
if (payload.ContainsEvacuationEntry()) {
DCHECK(!segment_will_be_evacuated);
Address handle_location =
payload.ExtractEvacuationEntryHandleLocation();
DCHECK(!space->FieldWasInvalidated(handle_location));
ResolveEvacuationEntryDuringSweeping(
i, reinterpret_cast<CppHeapPointerHandle*>(handle_location),
start_of_evacuation_area);
DCHECK(at(i).GetRawPayload().ContainsPointer());
DCHECK(!at(i).GetRawPayload().HasMarkBitSet());
} else if (!payload.HasMarkBitSet()) {
AddToFreelist(i);
} else {
auto new_payload = payload;
new_payload.ClearMarkBit();
at(i).SetRawPayload(new_payload);
}
DCHECK(!at(i).HasEvacuationEntry());
}
uint32_t free_entries = current_freelist_length - previous_freelist_length;
bool segment_is_empty = free_entries == kEntriesPerSegment;
if (segment_is_empty || segment_will_be_evacuated) {
segments_to_deallocate.push_back(segment);
current_freelist_head = previous_freelist_head;
current_freelist_length = previous_freelist_length;
}
}
for (auto segment : segments_to_deallocate) {
FreeTableSegment(segment);
space->segments_.erase(segment);
}
FreelistHead new_freelist(current_freelist_head, current_freelist_length);
space->freelist_head_.store(new_freelist, std::memory_order_release);
DCHECK_EQ(space->freelist_length(), current_freelist_length);
uint32_t num_live_entries = space->capacity() - current_freelist_length;
counters->cppheap_pointers_count()->AddSample(num_live_entries);
return num_live_entries;
}
void CppHeapPointerTable::ResolveEvacuationEntryDuringSweeping(
uint32_t new_index, CppHeapPointerHandle* handle_location,
uint32_t start_of_evacuation_area) {
CppHeapPointerHandle old_handle = *handle_location;
CHECK(IsValidHandle(old_handle));
uint32_t old_index = HandleToIndex(old_handle);
CppHeapPointerHandle new_handle = IndexToHandle(new_index);
DCHECK_GE(old_index, start_of_evacuation_area);
DCHECK_LT(new_index, start_of_evacuation_area);
auto& new_entry = at(new_index);
at(old_index).Evacuate(new_entry);
*handle_location = new_handle;
}
#ifdef OBJECT_PRINT
namespace {
constexpr std::string_view entry_spacer =
"+-----------------------------------------+\n";
}
void CppHeapPointerTableEntryPrinter::PrintHeader(const char* space_name) {
PrintF(stderr, "%s", entry_spacer.data());
PrintF(stderr, "| %*s |\n", static_cast<int>(entry_spacer.size() - 5),
space_name);
PrintF(stderr, "%s", entry_spacer.data());
PrintF(stderr, "| handle | tag | CppHeap pointer |\n");
PrintF(stderr, "%s", entry_spacer.data());
}
void CppHeapPointerTableEntryPrinter::PrintIfInUse(
CppHeapPointerHandle handle, const CppHeapPointerTableEntry& entry,
std::function<bool(CppHeapPointerTag)> entry_callback) {
const auto payload = entry.GetRawPayload();
const CppHeapPointerTag tag = payload.ExtractTag();
if (tag == CppHeapPointerTag::kFreeEntryTag ||
tag == CppHeapPointerTag::kZappedEntryTag) {
return;
}
if (!entry_callback(tag)) {
return;
}
Address address = payload.Untag(tag);
PrintF(stderr, "| %10" PRIu32 " | %5" PRIu16 " | 0x%016" PRIxPTR " |\n",
handle, tag, address);
}
void CppHeapPointerTableEntryPrinter::PrintFooter() {
PrintF(stderr, "%s", entry_spacer.data());
}
#endif
}
}
#endif