Commit f68242bc authored by Clemens Backes's avatar Clemens Backes Committed by V8 LUCI CQ

[codegen] Reduce size of safepoint table fields

Code objects are often small and do not use the full integer range of PC
offsets and deoptimization indexes. Reducing the size of these fields to
the required size per table reduces the overall size of safepoint tables
by roughly 25%.

R=jkummerow@chromium.org

Bug: v8:12401
Change-Id: Ie6889a70782f5510436a1d05d31d17aac0bfec6e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3306556Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78216}
parent d129b43f
......@@ -80,7 +80,7 @@ void SafepointTable::Print(std::ostream& os) const {
for (int index = 0; index < length_; index++) {
SafepointEntry entry = GetEntry(index);
os << reinterpret_cast<const void*>(instruction_start_ + entry.pc()) << " "
<< std::setw(6) << std::hex << entry.pc();
<< std::setw(6) << std::hex << entry.pc() << std::dec;
if (!entry.tagged_slots().empty()) {
os << " slots (sp->fp): ";
......@@ -163,21 +163,47 @@ void SafepointTableBuilder::Emit(Assembler* assembler, int tagged_slots_size) {
assembler->RecordComment(";;; Safepoint table.");
offset_ = assembler->pc_offset();
// Compute the number of bytes for tagged slots per safepoint entry.
// Compute the required sizes of the fields.
int used_register_indexes = 0;
int max_pc = 0;
STATIC_ASSERT(SafepointEntry::kNoDeoptIndex == -1);
int max_deopt_index = SafepointEntry::kNoDeoptIndex;
for (const EntryBuilder& entry : entries_) {
used_register_indexes |= entry.register_indexes;
max_pc = std::max(max_pc, std::max(entry.pc, entry.trampoline));
max_deopt_index = std::max(max_deopt_index, entry.deopt_index);
}
// Derive the bytes and bools for the entry configuration from the values.
auto value_to_bytes = [](int value) {
if (value == 0) return 0;
for (int bytes = 1;; ++bytes) {
DCHECK_GE(4, bytes);
int shifted_value = (value << (32 - 8 * bytes)) >> (32 - 8 * bytes);
if (shifted_value == value) return bytes;
}
};
bool has_deopt_data = max_deopt_index != SafepointEntry::kNoDeoptIndex;
int register_indexes_size = value_to_bytes(used_register_indexes);
int pc_size = value_to_bytes(max_pc);
int deopt_index_size = value_to_bytes(max_deopt_index);
int tagged_slots_bytes =
RoundUp(tagged_slots_size, kBitsPerByte) >> kBitsPerByteLog2;
bool has_deopt_data =
std::any_of(entries_.begin(), entries_.end(), [](auto& entry) {
return entry.deopt_index != SafepointEntry::kNoDeoptIndex;
});
bool has_register_indexes =
std::any_of(entries_.begin(), entries_.end(),
[](auto& entry) { return entry.register_indexes != 0; });
(tagged_slots_size + kBitsPerByte - 1) / kBitsPerByte;
// Add a CHECK to ensure we never overflow the space in the bitfield, even for
// huge functions which might not be covered by tests.
CHECK(SafepointTable::RegisterIndexesSizeField::is_valid(
register_indexes_size) &&
SafepointTable::PcSizeField::is_valid(pc_size) &&
SafepointTable::DeoptIndexSizeField::is_valid(deopt_index_size) &&
SafepointTable::TaggedSlotsBytesField::is_valid(tagged_slots_bytes));
uint32_t entry_configuration =
SafepointTable::TaggedSlotsBytesField::encode(tagged_slots_bytes) |
SafepointTable::HasDeoptDataField::encode(has_deopt_data) |
SafepointTable::HasRegisterIndexesField::encode(has_register_indexes);
SafepointTable::RegisterIndexesSizeField::encode(register_indexes_size) |
SafepointTable::PcSizeField::encode(pc_size) |
SafepointTable::DeoptIndexSizeField::encode(deopt_index_size) |
SafepointTable::TaggedSlotsBytesField::encode(tagged_slots_bytes);
// Emit the table header.
STATIC_ASSERT(SafepointTable::kLengthOffset == 0 * kIntSize);
......@@ -187,16 +213,19 @@ void SafepointTableBuilder::Emit(Assembler* assembler, int tagged_slots_size) {
assembler->dd(length);
assembler->dd(entry_configuration);
auto emit_bytes = [assembler](int value, int bytes) {
for (int b = bytes - 1; b >= 0; --b) {
assembler->db(value >> (8 * b));
}
};
// Emit entries, sorted by pc offsets.
for (const EntryBuilder& entry : entries_) {
assembler->dd(entry.pc);
emit_bytes(entry.pc, pc_size);
if (has_deopt_data) {
assembler->dd(entry.deopt_index);
assembler->dd(entry.trampoline);
}
if (has_register_indexes) {
assembler->dd(entry.register_indexes);
emit_bytes(entry.deopt_index, deopt_index_size);
emit_bytes(entry.trampoline, pc_size);
}
emit_bytes(entry.register_indexes, register_indexes_size);
}
// Emit bitmaps of tagged stack slots.
......
......@@ -109,20 +109,15 @@ class SafepointTable {
Address entry_ptr =
safepoint_table_address_ + kHeaderSize + index * entry_size();
int pc = base::Memory<int>(entry_ptr);
entry_ptr += kPcSize;
int pc = read_bytes(&entry_ptr, pc_size());
int deopt_index = SafepointEntry::kNoDeoptIndex;
int trampoline_pc = SafepointEntry::kNoTrampolinePC;
if (has_deopt_data()) {
deopt_index = base::Memory<int>(entry_ptr);
trampoline_pc = base::Memory<int>(entry_ptr + kIntSize);
entry_ptr += kDeoptDataSize;
}
int tagged_register_indexes = 0;
if (has_register_indexes()) {
tagged_register_indexes = base::Memory<int>(entry_ptr);
entry_ptr += kRegisterIndexesSize;
deopt_index = read_bytes(&entry_ptr, deopt_index_size());
trampoline_pc = read_bytes(&entry_ptr, pc_size());
}
int tagged_register_indexes =
read_bytes(&entry_ptr, register_indexes_size());
// Entry bits start after the the vector of entries (thus the pc offset of
// the non-existing entry after the last one).
......@@ -147,21 +142,20 @@ class SafepointTable {
static constexpr int kEntryConfigurationOffset = kLengthOffset + kIntSize;
static constexpr int kHeaderSize = kEntryConfigurationOffset + kUInt32Size;
// An entry consists of the pc, plus optional deopt data (deopt index and
// trampoline PC), plus optional register indexes.
static constexpr int kPcSize = kIntSize;
static constexpr int kDeoptDataSize = 2 * kIntSize;
static constexpr int kRegisterIndexesSize = kIntSize;
using TaggedSlotsBytesField = base::BitField<int, 0, 30>;
using HasDeoptDataField = TaggedSlotsBytesField::Next<bool, 1>;
using HasRegisterIndexesField = HasDeoptDataField::Next<bool, 1>;
using HasDeoptDataField = base::BitField<bool, 0, 1>;
using RegisterIndexesSizeField = HasDeoptDataField::Next<int, 3>;
using PcSizeField = RegisterIndexesSizeField::Next<int, 3>;
using DeoptIndexSizeField = PcSizeField::Next<int, 3>;
// In 22 bits, we can encode up to 4M bytes, corresponding to 32M frame slots,
// which is 128MB on 32-bit and 256MB on 64-bit systems. The stack size is
// limited to a bit below 1MB anyway (see FLAG_stack_size).
using TaggedSlotsBytesField = DeoptIndexSizeField::Next<int, 22>;
SafepointTable(Address instruction_start, Address safepoint_table_address);
int entry_size() const {
return kPcSize + (has_deopt_data() ? kDeoptDataSize : 0) +
(has_register_indexes() ? kRegisterIndexesSize : 0);
int deopt_data_size = has_deopt_data() ? pc_size() + deopt_index_size() : 0;
return pc_size() + deopt_data_size + register_indexes_size();
}
int tagged_slots_bytes() const {
......@@ -170,8 +164,23 @@ class SafepointTable {
bool has_deopt_data() const {
return HasDeoptDataField::decode(entry_configuration_);
}
bool has_register_indexes() const {
return HasRegisterIndexesField::decode(entry_configuration_);
int pc_size() const { return PcSizeField::decode(entry_configuration_); }
int deopt_index_size() const {
return DeoptIndexSizeField::decode(entry_configuration_);
}
int register_indexes_size() const {
return RegisterIndexesSizeField::decode(entry_configuration_);
}
static int read_bytes(Address* ptr, int bytes) {
if (bytes == 0) return 0;
// Sign-extend the first byte.
int result = *reinterpret_cast<int8_t*>(*ptr);
for (int b = 1; b < bytes; ++b) {
result = result << 8 | *reinterpret_cast<uint8_t*>(*ptr + b);
}
*ptr += bytes;
return result;
}
DISALLOW_GARBAGE_COLLECTION(no_gc_)
......
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