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 { ...@@ -80,7 +80,7 @@ void SafepointTable::Print(std::ostream& os) const {
for (int index = 0; index < length_; index++) { for (int index = 0; index < length_; index++) {
SafepointEntry entry = GetEntry(index); SafepointEntry entry = GetEntry(index);
os << reinterpret_cast<const void*>(instruction_start_ + entry.pc()) << " " 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()) { if (!entry.tagged_slots().empty()) {
os << " slots (sp->fp): "; os << " slots (sp->fp): ";
...@@ -163,21 +163,47 @@ void SafepointTableBuilder::Emit(Assembler* assembler, int tagged_slots_size) { ...@@ -163,21 +163,47 @@ void SafepointTableBuilder::Emit(Assembler* assembler, int tagged_slots_size) {
assembler->RecordComment(";;; Safepoint table."); assembler->RecordComment(";;; Safepoint table.");
offset_ = assembler->pc_offset(); 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 = int tagged_slots_bytes =
RoundUp(tagged_slots_size, kBitsPerByte) >> kBitsPerByteLog2; (tagged_slots_size + kBitsPerByte - 1) / kBitsPerByte;
bool has_deopt_data =
std::any_of(entries_.begin(), entries_.end(), [](auto& entry) { // Add a CHECK to ensure we never overflow the space in the bitfield, even for
return entry.deopt_index != SafepointEntry::kNoDeoptIndex; // huge functions which might not be covered by tests.
}); CHECK(SafepointTable::RegisterIndexesSizeField::is_valid(
bool has_register_indexes = register_indexes_size) &&
std::any_of(entries_.begin(), entries_.end(), SafepointTable::PcSizeField::is_valid(pc_size) &&
[](auto& entry) { return entry.register_indexes != 0; }); SafepointTable::DeoptIndexSizeField::is_valid(deopt_index_size) &&
SafepointTable::TaggedSlotsBytesField::is_valid(tagged_slots_bytes));
uint32_t entry_configuration = uint32_t entry_configuration =
SafepointTable::TaggedSlotsBytesField::encode(tagged_slots_bytes) |
SafepointTable::HasDeoptDataField::encode(has_deopt_data) | 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. // Emit the table header.
STATIC_ASSERT(SafepointTable::kLengthOffset == 0 * kIntSize); STATIC_ASSERT(SafepointTable::kLengthOffset == 0 * kIntSize);
...@@ -187,16 +213,19 @@ void SafepointTableBuilder::Emit(Assembler* assembler, int tagged_slots_size) { ...@@ -187,16 +213,19 @@ void SafepointTableBuilder::Emit(Assembler* assembler, int tagged_slots_size) {
assembler->dd(length); assembler->dd(length);
assembler->dd(entry_configuration); 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. // Emit entries, sorted by pc offsets.
for (const EntryBuilder& entry : entries_) { for (const EntryBuilder& entry : entries_) {
assembler->dd(entry.pc); emit_bytes(entry.pc, pc_size);
if (has_deopt_data) { if (has_deopt_data) {
assembler->dd(entry.deopt_index); emit_bytes(entry.deopt_index, deopt_index_size);
assembler->dd(entry.trampoline); emit_bytes(entry.trampoline, pc_size);
}
if (has_register_indexes) {
assembler->dd(entry.register_indexes);
} }
emit_bytes(entry.register_indexes, register_indexes_size);
} }
// Emit bitmaps of tagged stack slots. // Emit bitmaps of tagged stack slots.
......
...@@ -109,20 +109,15 @@ class SafepointTable { ...@@ -109,20 +109,15 @@ class SafepointTable {
Address entry_ptr = Address entry_ptr =
safepoint_table_address_ + kHeaderSize + index * entry_size(); safepoint_table_address_ + kHeaderSize + index * entry_size();
int pc = base::Memory<int>(entry_ptr); int pc = read_bytes(&entry_ptr, pc_size());
entry_ptr += kPcSize;
int deopt_index = SafepointEntry::kNoDeoptIndex; int deopt_index = SafepointEntry::kNoDeoptIndex;
int trampoline_pc = SafepointEntry::kNoTrampolinePC; int trampoline_pc = SafepointEntry::kNoTrampolinePC;
if (has_deopt_data()) { if (has_deopt_data()) {
deopt_index = base::Memory<int>(entry_ptr); deopt_index = read_bytes(&entry_ptr, deopt_index_size());
trampoline_pc = base::Memory<int>(entry_ptr + kIntSize); trampoline_pc = read_bytes(&entry_ptr, pc_size());
entry_ptr += kDeoptDataSize;
}
int tagged_register_indexes = 0;
if (has_register_indexes()) {
tagged_register_indexes = base::Memory<int>(entry_ptr);
entry_ptr += kRegisterIndexesSize;
} }
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 // Entry bits start after the the vector of entries (thus the pc offset of
// the non-existing entry after the last one). // the non-existing entry after the last one).
...@@ -147,21 +142,20 @@ class SafepointTable { ...@@ -147,21 +142,20 @@ class SafepointTable {
static constexpr int kEntryConfigurationOffset = kLengthOffset + kIntSize; static constexpr int kEntryConfigurationOffset = kLengthOffset + kIntSize;
static constexpr int kHeaderSize = kEntryConfigurationOffset + kUInt32Size; static constexpr int kHeaderSize = kEntryConfigurationOffset + kUInt32Size;
// An entry consists of the pc, plus optional deopt data (deopt index and using HasDeoptDataField = base::BitField<bool, 0, 1>;
// trampoline PC), plus optional register indexes. using RegisterIndexesSizeField = HasDeoptDataField::Next<int, 3>;
static constexpr int kPcSize = kIntSize; using PcSizeField = RegisterIndexesSizeField::Next<int, 3>;
static constexpr int kDeoptDataSize = 2 * kIntSize; using DeoptIndexSizeField = PcSizeField::Next<int, 3>;
static constexpr int kRegisterIndexesSize = kIntSize; // 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
using TaggedSlotsBytesField = base::BitField<int, 0, 30>; // limited to a bit below 1MB anyway (see FLAG_stack_size).
using HasDeoptDataField = TaggedSlotsBytesField::Next<bool, 1>; using TaggedSlotsBytesField = DeoptIndexSizeField::Next<int, 22>;
using HasRegisterIndexesField = HasDeoptDataField::Next<bool, 1>;
SafepointTable(Address instruction_start, Address safepoint_table_address); SafepointTable(Address instruction_start, Address safepoint_table_address);
int entry_size() const { int entry_size() const {
return kPcSize + (has_deopt_data() ? kDeoptDataSize : 0) + int deopt_data_size = has_deopt_data() ? pc_size() + deopt_index_size() : 0;
(has_register_indexes() ? kRegisterIndexesSize : 0); return pc_size() + deopt_data_size + register_indexes_size();
} }
int tagged_slots_bytes() const { int tagged_slots_bytes() const {
...@@ -170,8 +164,23 @@ class SafepointTable { ...@@ -170,8 +164,23 @@ class SafepointTable {
bool has_deopt_data() const { bool has_deopt_data() const {
return HasDeoptDataField::decode(entry_configuration_); return HasDeoptDataField::decode(entry_configuration_);
} }
bool has_register_indexes() const { int pc_size() const { return PcSizeField::decode(entry_configuration_); }
return HasRegisterIndexesField::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_) 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