[maglev] Add a maglev-specific safepoint mechanism
Maglev groups all its tagged spill slots together, and the number of them doesn't change. This means that the generality of the existing safepoint mechanism is massive overkill for maglev code. This patch adds a maglev-specific safepoint table, which is the safepoint of a code object if-and-only-if that code object has maglev code. This safepoint stores the number of tagged and untagged slots once, globally, and individual entries are just used for deopts and for storing the state of pushed registers (this is currently unused, but will be used in the future for pushing registers in deferred calls). Bug: v8:7700 Change-Id: I15f84a6e957357825e84e33238f8a36f2e0b3012 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3747858 Commit-Queue: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Cr-Commit-Position: refs/heads/main@{#81564}
This commit is contained in:
parent
c04fba9354
commit
76356780aa
3
BUILD.gn
3
BUILD.gn
@ -2779,6 +2779,7 @@ v8_header_set("v8_internal_headers") {
|
||||
"src/codegen/machine-type.h",
|
||||
"src/codegen/macro-assembler-inl.h",
|
||||
"src/codegen/macro-assembler.h",
|
||||
"src/codegen/maglev-safepoint-table.h",
|
||||
"src/codegen/optimized-compilation-info.h",
|
||||
"src/codegen/pending-optimization-table.h",
|
||||
"src/codegen/register-arch.h",
|
||||
@ -2788,6 +2789,7 @@ v8_header_set("v8_internal_headers") {
|
||||
"src/codegen/reglist-base.h",
|
||||
"src/codegen/reglist.h",
|
||||
"src/codegen/reloc-info.h",
|
||||
"src/codegen/safepoint-table-base.h",
|
||||
"src/codegen/safepoint-table.h",
|
||||
"src/codegen/script-details.h",
|
||||
"src/codegen/signature.h",
|
||||
@ -4288,6 +4290,7 @@ v8_source_set("v8_base_without_compiler") {
|
||||
"src/codegen/handler-table.cc",
|
||||
"src/codegen/interface-descriptors.cc",
|
||||
"src/codegen/machine-type.cc",
|
||||
"src/codegen/maglev-safepoint-table.cc",
|
||||
"src/codegen/optimized-compilation-info.cc",
|
||||
"src/codegen/pending-optimization-table.cc",
|
||||
"src/codegen/register-configuration.cc",
|
||||
|
262
src/codegen/maglev-safepoint-table.cc
Normal file
262
src/codegen/maglev-safepoint-table.cc
Normal file
@ -0,0 +1,262 @@
|
||||
// Copyright 2022 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/codegen/maglev-safepoint-table.h"
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include "src/codegen/macro-assembler.h"
|
||||
#include "src/objects/code-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
MaglevSafepointTable::MaglevSafepointTable(Isolate* isolate, Address pc,
|
||||
Code code)
|
||||
: MaglevSafepointTable(code.InstructionStart(isolate, pc),
|
||||
code.SafepointTableAddress()) {
|
||||
DCHECK(code.is_maglevved());
|
||||
}
|
||||
|
||||
MaglevSafepointTable::MaglevSafepointTable(Address instruction_start,
|
||||
Address safepoint_table_address)
|
||||
: instruction_start_(instruction_start),
|
||||
safepoint_table_address_(safepoint_table_address),
|
||||
length_(base::Memory<int>(safepoint_table_address + kLengthOffset)),
|
||||
entry_configuration_(base::Memory<uint32_t>(safepoint_table_address +
|
||||
kEntryConfigurationOffset)),
|
||||
num_tagged_slots_(base::Memory<uint32_t>(safepoint_table_address +
|
||||
kNumTaggedSlotsOffset)),
|
||||
num_untagged_slots_(base::Memory<uint32_t>(safepoint_table_address +
|
||||
kNumUntaggedSlotsOffset)) {}
|
||||
|
||||
int MaglevSafepointTable::find_return_pc(int pc_offset) {
|
||||
for (int i = 0; i < length(); i++) {
|
||||
MaglevSafepointEntry entry = GetEntry(i);
|
||||
if (entry.trampoline_pc() == pc_offset || entry.pc() == pc_offset) {
|
||||
return entry.pc();
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
MaglevSafepointEntry MaglevSafepointTable::FindEntry(Address pc) const {
|
||||
int pc_offset = static_cast<int>(pc - instruction_start_);
|
||||
|
||||
// Check if the PC is pointing at a trampoline.
|
||||
if (has_deopt_data()) {
|
||||
int candidate = -1;
|
||||
for (int i = 0; i < length_; ++i) {
|
||||
int trampoline_pc = GetEntry(i).trampoline_pc();
|
||||
if (trampoline_pc != -1 && trampoline_pc <= pc_offset) candidate = i;
|
||||
if (trampoline_pc > pc_offset) break;
|
||||
}
|
||||
if (candidate != -1) return GetEntry(candidate);
|
||||
}
|
||||
|
||||
for (int i = 0; i < length_; ++i) {
|
||||
MaglevSafepointEntry entry = GetEntry(i);
|
||||
if (i == length_ - 1 || GetEntry(i + 1).pc() > pc_offset) {
|
||||
DCHECK_LE(entry.pc(), pc_offset);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void MaglevSafepointTable::Print(std::ostream& os) const {
|
||||
os << "Safepoints (entries = " << length_ << ", byte size = " << byte_size()
|
||||
<< ", tagged slots = " << num_tagged_slots_
|
||||
<< ", untagged slots = " << num_untagged_slots_ << ")\n";
|
||||
|
||||
for (int index = 0; index < length_; index++) {
|
||||
MaglevSafepointEntry entry = GetEntry(index);
|
||||
os << reinterpret_cast<const void*>(instruction_start_ + entry.pc()) << " "
|
||||
<< std::setw(6) << std::hex << entry.pc() << std::dec;
|
||||
|
||||
os << " num pushed registers: "
|
||||
<< static_cast<int>(entry.num_pushed_registers());
|
||||
|
||||
if (entry.tagged_register_indexes() != 0) {
|
||||
os << " registers: ";
|
||||
uint32_t register_bits = entry.tagged_register_indexes();
|
||||
int bits = 32 - base::bits::CountLeadingZeros32(register_bits);
|
||||
for (int j = bits - 1; j >= 0; --j) {
|
||||
os << ((register_bits >> j) & 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.has_deoptimization_index()) {
|
||||
os << " deopt " << std::setw(6) << entry.deoptimization_index()
|
||||
<< " trampoline: " << std::setw(6) << std::hex
|
||||
<< entry.trampoline_pc();
|
||||
}
|
||||
os << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
MaglevSafepointTableBuilder::Safepoint
|
||||
MaglevSafepointTableBuilder::DefineSafepoint(Assembler* assembler) {
|
||||
entries_.push_back(EntryBuilder(assembler->pc_offset_for_safepoint()));
|
||||
return MaglevSafepointTableBuilder::Safepoint(&entries_.back());
|
||||
}
|
||||
|
||||
int MaglevSafepointTableBuilder::UpdateDeoptimizationInfo(int pc,
|
||||
int trampoline,
|
||||
int start,
|
||||
int deopt_index) {
|
||||
DCHECK_NE(MaglevSafepointEntry::kNoTrampolinePC, trampoline);
|
||||
DCHECK_NE(MaglevSafepointEntry::kNoDeoptIndex, deopt_index);
|
||||
auto it = entries_.Find(start);
|
||||
DCHECK(std::any_of(it, entries_.end(),
|
||||
[pc](auto& entry) { return entry.pc == pc; }));
|
||||
int index = start;
|
||||
while (it->pc != pc) ++it, ++index;
|
||||
it->trampoline = trampoline;
|
||||
it->deopt_index = deopt_index;
|
||||
return index;
|
||||
}
|
||||
|
||||
void MaglevSafepointTableBuilder::Emit(Assembler* assembler) {
|
||||
#ifdef DEBUG
|
||||
int last_pc = -1;
|
||||
int last_trampoline = -1;
|
||||
for (const EntryBuilder& entry : entries_) {
|
||||
// Entries are ordered by PC.
|
||||
DCHECK_LT(last_pc, entry.pc);
|
||||
last_pc = entry.pc;
|
||||
// Trampoline PCs are increasing, and larger than regular PCs.
|
||||
if (entry.trampoline != MaglevSafepointEntry::kNoTrampolinePC) {
|
||||
DCHECK_LT(last_trampoline, entry.trampoline);
|
||||
DCHECK_LT(entries_.back().pc, entry.trampoline);
|
||||
last_trampoline = entry.trampoline;
|
||||
}
|
||||
// An entry either has trampoline and deopt index, or none of the two.
|
||||
DCHECK_EQ(entry.trampoline == MaglevSafepointEntry::kNoTrampolinePC,
|
||||
entry.deopt_index == MaglevSafepointEntry::kNoDeoptIndex);
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
RemoveDuplicates();
|
||||
|
||||
#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_ARM64
|
||||
// We cannot emit a const pool within the safepoint table.
|
||||
Assembler::BlockConstPoolScope block_const_pool(assembler);
|
||||
#endif
|
||||
|
||||
// Make sure the safepoint table is properly aligned. Pad with nops.
|
||||
assembler->Align(Code::kMetadataAlignment);
|
||||
assembler->RecordComment(";;; Maglev safepoint table.");
|
||||
set_safepoint_table_offset(assembler->pc_offset());
|
||||
|
||||
// Compute the required sizes of the fields.
|
||||
int used_register_indexes = 0;
|
||||
static_assert(MaglevSafepointEntry::kNoTrampolinePC == -1);
|
||||
int max_pc = MaglevSafepointEntry::kNoTrampolinePC;
|
||||
static_assert(MaglevSafepointEntry::kNoDeoptIndex == -1);
|
||||
int max_deopt_index = MaglevSafepointEntry::kNoDeoptIndex;
|
||||
for (const EntryBuilder& entry : entries_) {
|
||||
used_register_indexes |= entry.tagged_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) {
|
||||
DCHECK_LE(0, value);
|
||||
if (value == 0) return 0;
|
||||
if (value <= 0xff) return 1;
|
||||
if (value <= 0xffff) return 2;
|
||||
if (value <= 0xffffff) return 3;
|
||||
return 4;
|
||||
};
|
||||
bool has_deopt_data = max_deopt_index != -1;
|
||||
int register_indexes_size = value_to_bytes(used_register_indexes);
|
||||
// Add 1 so all values (including kNoDeoptIndex and kNoTrampolinePC) are
|
||||
// non-negative.
|
||||
static_assert(MaglevSafepointEntry::kNoDeoptIndex == -1);
|
||||
static_assert(MaglevSafepointEntry::kNoTrampolinePC == -1);
|
||||
int pc_size = value_to_bytes(max_pc + 1);
|
||||
int deopt_index_size = value_to_bytes(max_deopt_index + 1);
|
||||
|
||||
// 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(MaglevSafepointTable::RegisterIndexesSizeField::is_valid(
|
||||
register_indexes_size));
|
||||
CHECK(MaglevSafepointTable::PcSizeField::is_valid(pc_size));
|
||||
CHECK(MaglevSafepointTable::DeoptIndexSizeField::is_valid(deopt_index_size));
|
||||
|
||||
uint32_t entry_configuration =
|
||||
MaglevSafepointTable::HasDeoptDataField::encode(has_deopt_data) |
|
||||
MaglevSafepointTable::RegisterIndexesSizeField::encode(
|
||||
register_indexes_size) |
|
||||
MaglevSafepointTable::PcSizeField::encode(pc_size) |
|
||||
MaglevSafepointTable::DeoptIndexSizeField::encode(deopt_index_size);
|
||||
|
||||
// Emit the table header.
|
||||
static_assert(MaglevSafepointTable::kLengthOffset == 0 * kIntSize);
|
||||
static_assert(MaglevSafepointTable::kEntryConfigurationOffset ==
|
||||
1 * kIntSize);
|
||||
static_assert(MaglevSafepointTable::kNumTaggedSlotsOffset == 2 * kIntSize);
|
||||
static_assert(MaglevSafepointTable::kNumUntaggedSlotsOffset == 3 * kIntSize);
|
||||
static_assert(MaglevSafepointTable::kHeaderSize == 4 * kIntSize);
|
||||
int length = static_cast<int>(entries_.size());
|
||||
assembler->dd(length);
|
||||
assembler->dd(entry_configuration);
|
||||
assembler->dd(num_tagged_slots_);
|
||||
assembler->dd(num_untagged_slots_);
|
||||
|
||||
auto emit_bytes = [assembler](int value, int bytes) {
|
||||
DCHECK_LE(0, value);
|
||||
for (; bytes > 0; --bytes, value >>= 8) assembler->db(value);
|
||||
DCHECK_EQ(0, value);
|
||||
};
|
||||
// Emit entries, sorted by pc offsets.
|
||||
for (const EntryBuilder& entry : entries_) {
|
||||
emit_bytes(entry.pc, pc_size);
|
||||
if (has_deopt_data) {
|
||||
// Add 1 so all values (including kNoDeoptIndex and kNoTrampolinePC) are
|
||||
// non-negative.
|
||||
static_assert(MaglevSafepointEntry::kNoDeoptIndex == -1);
|
||||
static_assert(MaglevSafepointEntry::kNoTrampolinePC == -1);
|
||||
emit_bytes(entry.deopt_index + 1, deopt_index_size);
|
||||
emit_bytes(entry.trampoline + 1, pc_size);
|
||||
}
|
||||
assembler->db(entry.num_pushed_registers);
|
||||
emit_bytes(entry.tagged_register_indexes, register_indexes_size);
|
||||
}
|
||||
}
|
||||
|
||||
void MaglevSafepointTableBuilder::RemoveDuplicates() {
|
||||
// Remove any duplicate entries, i.e. succeeding entries that are identical
|
||||
// except for the PC. During lookup, we will find the first entry whose PC is
|
||||
// not larger than the PC at hand, and find the first non-duplicate.
|
||||
|
||||
if (entries_.size() < 2) return;
|
||||
|
||||
auto is_identical_except_for_pc = [](const EntryBuilder& entry1,
|
||||
const EntryBuilder& entry2) {
|
||||
if (entry1.deopt_index != entry2.deopt_index) return false;
|
||||
DCHECK_EQ(entry1.trampoline, entry2.trampoline);
|
||||
return entry1.num_pushed_registers == entry2.num_pushed_registers &&
|
||||
entry1.tagged_register_indexes == entry2.tagged_register_indexes;
|
||||
};
|
||||
|
||||
auto remaining_it = entries_.begin();
|
||||
size_t remaining = 0;
|
||||
|
||||
for (auto it = entries_.begin(), end = entries_.end(); it != end;
|
||||
++remaining_it, ++remaining) {
|
||||
if (remaining_it != it) *remaining_it = *it;
|
||||
// Merge identical entries.
|
||||
do {
|
||||
++it;
|
||||
} while (it != end && is_identical_except_for_pc(*it, *remaining_it));
|
||||
}
|
||||
|
||||
entries_.Rewind(remaining);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
241
src/codegen/maglev-safepoint-table.h
Normal file
241
src/codegen/maglev-safepoint-table.h
Normal file
@ -0,0 +1,241 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
#ifndef V8_CODEGEN_MAGLEV_SAFEPOINT_TABLE_H_
|
||||
#define V8_CODEGEN_MAGLEV_SAFEPOINT_TABLE_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/base/bit-field.h"
|
||||
#include "src/codegen/safepoint-table-base.h"
|
||||
#include "src/common/assert-scope.h"
|
||||
#include "src/utils/allocation.h"
|
||||
#include "src/zone/zone-chunk-list.h"
|
||||
#include "src/zone/zone.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
class MaglevSafepointEntry : public SafepointEntryBase {
|
||||
public:
|
||||
static constexpr int kNoDeoptIndex = -1;
|
||||
static constexpr int kNoTrampolinePC = -1;
|
||||
|
||||
MaglevSafepointEntry() = default;
|
||||
|
||||
MaglevSafepointEntry(int pc, int deopt_index, uint32_t num_tagged_slots,
|
||||
uint32_t num_untagged_slots,
|
||||
uint8_t num_pushed_registers,
|
||||
uint32_t tagged_register_indexes, int trampoline_pc)
|
||||
: SafepointEntryBase(pc, deopt_index, trampoline_pc),
|
||||
num_tagged_slots_(num_tagged_slots),
|
||||
num_untagged_slots_(num_untagged_slots),
|
||||
num_pushed_registers_(num_pushed_registers),
|
||||
tagged_register_indexes_(tagged_register_indexes) {
|
||||
DCHECK(is_initialized());
|
||||
}
|
||||
|
||||
bool operator==(const MaglevSafepointEntry& other) const {
|
||||
return this->SafepointEntryBase::operator==(other) &&
|
||||
num_tagged_slots_ == other.num_tagged_slots_ &&
|
||||
num_untagged_slots_ == other.num_untagged_slots_ &&
|
||||
num_pushed_registers_ == other.num_pushed_registers_ &&
|
||||
tagged_register_indexes_ == other.tagged_register_indexes_;
|
||||
}
|
||||
|
||||
uint32_t num_tagged_slots() const { return num_tagged_slots_; }
|
||||
uint32_t num_untagged_slots() const { return num_untagged_slots_; }
|
||||
uint8_t num_pushed_registers() const { return num_pushed_registers_; }
|
||||
uint32_t tagged_register_indexes() const { return tagged_register_indexes_; }
|
||||
|
||||
private:
|
||||
uint32_t num_tagged_slots_ = 0;
|
||||
uint32_t num_untagged_slots_ = 0;
|
||||
uint8_t num_pushed_registers_ = 0;
|
||||
uint32_t tagged_register_indexes_ = 0;
|
||||
};
|
||||
|
||||
// A wrapper class for accessing the safepoint table embedded into the Code
|
||||
// object.
|
||||
class MaglevSafepointTable {
|
||||
public:
|
||||
// The isolate and pc arguments are used for figuring out whether pc
|
||||
// belongs to the embedded or un-embedded code blob.
|
||||
explicit MaglevSafepointTable(Isolate* isolate, Address pc, Code code);
|
||||
|
||||
MaglevSafepointTable(const MaglevSafepointTable&) = delete;
|
||||
MaglevSafepointTable& operator=(const MaglevSafepointTable&) = delete;
|
||||
|
||||
int length() const { return length_; }
|
||||
|
||||
int byte_size() const { return kHeaderSize + length_ * entry_size(); }
|
||||
|
||||
int find_return_pc(int pc_offset);
|
||||
|
||||
MaglevSafepointEntry GetEntry(int index) const {
|
||||
DCHECK_GT(length_, index);
|
||||
Address entry_ptr =
|
||||
safepoint_table_address_ + kHeaderSize + index * entry_size();
|
||||
|
||||
int pc = read_bytes(&entry_ptr, pc_size());
|
||||
int deopt_index = MaglevSafepointEntry::kNoDeoptIndex;
|
||||
int trampoline_pc = MaglevSafepointEntry::kNoTrampolinePC;
|
||||
if (has_deopt_data()) {
|
||||
static_assert(MaglevSafepointEntry::kNoDeoptIndex == -1);
|
||||
static_assert(MaglevSafepointEntry::kNoTrampolinePC == -1);
|
||||
// `-1` to restore the original value, see also
|
||||
// MaglevSafepointTableBuilder::Emit.
|
||||
deopt_index = read_bytes(&entry_ptr, deopt_index_size()) - 1;
|
||||
trampoline_pc = read_bytes(&entry_ptr, pc_size()) - 1;
|
||||
DCHECK(deopt_index >= 0 ||
|
||||
deopt_index == MaglevSafepointEntry::kNoDeoptIndex);
|
||||
DCHECK(trampoline_pc >= 0 ||
|
||||
trampoline_pc == MaglevSafepointEntry::kNoTrampolinePC);
|
||||
}
|
||||
uint8_t num_pushed_registers = read_byte(&entry_ptr);
|
||||
int tagged_register_indexes =
|
||||
read_bytes(&entry_ptr, register_indexes_size());
|
||||
|
||||
return MaglevSafepointEntry(pc, deopt_index, num_tagged_slots_,
|
||||
num_untagged_slots_, num_pushed_registers,
|
||||
tagged_register_indexes, trampoline_pc);
|
||||
}
|
||||
|
||||
// Returns the entry for the given pc.
|
||||
MaglevSafepointEntry FindEntry(Address pc) const;
|
||||
|
||||
void Print(std::ostream&) const;
|
||||
|
||||
private:
|
||||
// Layout information.
|
||||
static constexpr int kLengthOffset = 0;
|
||||
static constexpr int kEntryConfigurationOffset = kLengthOffset + kIntSize;
|
||||
// The number of tagged/untagged slots is constant for the whole code so just
|
||||
// store it in the header.
|
||||
static constexpr int kNumTaggedSlotsOffset =
|
||||
kEntryConfigurationOffset + kUInt32Size;
|
||||
static constexpr int kNumUntaggedSlotsOffset =
|
||||
kNumTaggedSlotsOffset + kUInt32Size;
|
||||
static constexpr int kHeaderSize = kNumUntaggedSlotsOffset + kUInt32Size;
|
||||
|
||||
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>;
|
||||
|
||||
MaglevSafepointTable(Address instruction_start,
|
||||
Address safepoint_table_address);
|
||||
|
||||
int entry_size() const {
|
||||
int deopt_data_size = has_deopt_data() ? pc_size() + deopt_index_size() : 0;
|
||||
const int num_pushed_registers_size = 1;
|
||||
return pc_size() + deopt_data_size + num_pushed_registers_size +
|
||||
register_indexes_size();
|
||||
}
|
||||
|
||||
bool has_deopt_data() const {
|
||||
return HasDeoptDataField::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) {
|
||||
uint32_t result = 0;
|
||||
for (int b = 0; b < bytes; ++b) {
|
||||
result |= uint32_t{read_byte(ptr)} << (8 * b);
|
||||
}
|
||||
return static_cast<int>(result);
|
||||
}
|
||||
|
||||
static uint8_t read_byte(Address* ptr) {
|
||||
uint8_t result = *reinterpret_cast<uint8_t*>(*ptr);
|
||||
++*ptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
DISALLOW_GARBAGE_COLLECTION(no_gc_)
|
||||
|
||||
const Address instruction_start_;
|
||||
|
||||
// Safepoint table layout.
|
||||
const Address safepoint_table_address_;
|
||||
const int length_;
|
||||
const uint32_t entry_configuration_;
|
||||
const uint32_t num_tagged_slots_;
|
||||
const uint32_t num_untagged_slots_;
|
||||
|
||||
friend class MaglevSafepointTableBuilder;
|
||||
friend class MaglevSafepointEntry;
|
||||
};
|
||||
|
||||
class MaglevSafepointTableBuilder : public SafepointTableBuilderBase {
|
||||
private:
|
||||
struct EntryBuilder {
|
||||
int pc;
|
||||
int deopt_index = MaglevSafepointEntry::kNoDeoptIndex;
|
||||
int trampoline = MaglevSafepointEntry::kNoTrampolinePC;
|
||||
uint8_t num_pushed_registers = 0;
|
||||
uint32_t tagged_register_indexes = 0;
|
||||
explicit EntryBuilder(int pc) : pc(pc) {}
|
||||
};
|
||||
|
||||
public:
|
||||
explicit MaglevSafepointTableBuilder(Zone* zone, uint32_t num_tagged_slots,
|
||||
uint32_t num_untagged_slots)
|
||||
: num_tagged_slots_(num_tagged_slots),
|
||||
num_untagged_slots_(num_untagged_slots),
|
||||
entries_(zone) {}
|
||||
|
||||
MaglevSafepointTableBuilder(const MaglevSafepointTableBuilder&) = delete;
|
||||
MaglevSafepointTableBuilder& operator=(const MaglevSafepointTableBuilder&) =
|
||||
delete;
|
||||
|
||||
class Safepoint {
|
||||
public:
|
||||
void DefineTaggedRegister(int reg_code) {
|
||||
DCHECK_LT(reg_code,
|
||||
kBitsPerByte * sizeof(EntryBuilder::tagged_register_indexes));
|
||||
entry_->tagged_register_indexes |= 1u << reg_code;
|
||||
}
|
||||
void SetNumPushedRegisters(uint8_t num_registers) {
|
||||
entry_->num_pushed_registers = num_registers;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class MaglevSafepointTableBuilder;
|
||||
explicit Safepoint(EntryBuilder* entry) : entry_(entry) {}
|
||||
EntryBuilder* const entry_;
|
||||
};
|
||||
|
||||
// Define a new safepoint for the current position in the body.
|
||||
Safepoint DefineSafepoint(Assembler* assembler);
|
||||
|
||||
// Emit the safepoint table after the body.
|
||||
V8_EXPORT_PRIVATE void Emit(Assembler* assembler);
|
||||
|
||||
// Find the Deoptimization Info with pc offset {pc} and update its
|
||||
// trampoline field. Calling this function ensures that the safepoint
|
||||
// table contains the trampoline PC {trampoline} that replaced the
|
||||
// return PC {pc} on the stack.
|
||||
int UpdateDeoptimizationInfo(int pc, int trampoline, int start,
|
||||
int deopt_index);
|
||||
|
||||
private:
|
||||
// Remove consecutive identical entries.
|
||||
void RemoveDuplicates();
|
||||
|
||||
const uint32_t num_tagged_slots_;
|
||||
const uint32_t num_untagged_slots_;
|
||||
ZoneChunkList<EntryBuilder> entries_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_CODEGEN_MAGLEV_SAFEPOINT_TABLE_H_
|
85
src/codegen/safepoint-table-base.h
Normal file
85
src/codegen/safepoint-table-base.h
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
#ifndef V8_CODEGEN_SAFEPOINT_TABLE_BASE_H_
|
||||
#define V8_CODEGEN_SAFEPOINT_TABLE_BASE_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/base/logging.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
class SafepointEntryBase {
|
||||
public:
|
||||
static constexpr int kNoDeoptIndex = -1;
|
||||
static constexpr int kNoTrampolinePC = -1;
|
||||
|
||||
SafepointEntryBase() = default;
|
||||
|
||||
SafepointEntryBase(int pc, int deopt_index, int trampoline_pc)
|
||||
: pc_(pc), deopt_index_(deopt_index), trampoline_pc_(trampoline_pc) {
|
||||
DCHECK(is_initialized());
|
||||
}
|
||||
|
||||
bool is_initialized() const { return pc_ != 0; }
|
||||
|
||||
int pc() const {
|
||||
DCHECK(is_initialized());
|
||||
return pc_;
|
||||
}
|
||||
|
||||
int trampoline_pc() const { return trampoline_pc_; }
|
||||
|
||||
bool has_deoptimization_index() const {
|
||||
return deopt_index_ != kNoDeoptIndex;
|
||||
}
|
||||
|
||||
int deoptimization_index() const {
|
||||
DCHECK(has_deoptimization_index());
|
||||
return deopt_index_;
|
||||
}
|
||||
|
||||
void Reset() { pc_ = 0; }
|
||||
|
||||
protected:
|
||||
bool operator==(const SafepointEntryBase& other) const {
|
||||
return pc_ == other.pc_ && deopt_index_ == other.deopt_index_ &&
|
||||
trampoline_pc_ == other.trampoline_pc_;
|
||||
}
|
||||
|
||||
private:
|
||||
int pc_ = 0;
|
||||
int deopt_index_ = kNoDeoptIndex;
|
||||
int trampoline_pc_ = kNoTrampolinePC;
|
||||
};
|
||||
|
||||
class SafepointTableBuilderBase {
|
||||
public:
|
||||
bool emitted() const {
|
||||
return safepoint_table_offset_ != kNoSafepointTableOffset;
|
||||
}
|
||||
|
||||
int safepoint_table_offset() const {
|
||||
DCHECK(emitted());
|
||||
return safepoint_table_offset_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void set_safepoint_table_offset(int offset) {
|
||||
DCHECK(!emitted());
|
||||
safepoint_table_offset_ = offset;
|
||||
DCHECK(emitted());
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int kNoSafepointTableOffset = -1;
|
||||
int safepoint_table_offset_ = kNoSafepointTableOffset;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_CODEGEN_SAFEPOINT_TABLE_BASE_H_
|
@ -166,7 +166,7 @@ void SafepointTableBuilder::Emit(Assembler* assembler, int tagged_slots_size) {
|
||||
// Make sure the safepoint table is properly aligned. Pad with nops.
|
||||
assembler->Align(Code::kMetadataAlignment);
|
||||
assembler->RecordComment(";;; Safepoint table.");
|
||||
safepoint_table_offset_ = assembler->pc_offset();
|
||||
set_safepoint_table_offset(assembler->pc_offset());
|
||||
|
||||
// Compute the required sizes of the fields.
|
||||
int used_register_indexes = 0;
|
||||
@ -203,10 +203,10 @@ void SafepointTableBuilder::Emit(Assembler* assembler, int tagged_slots_size) {
|
||||
// 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));
|
||||
register_indexes_size));
|
||||
CHECK(SafepointTable::PcSizeField::is_valid(pc_size));
|
||||
CHECK(SafepointTable::DeoptIndexSizeField::is_valid(deopt_index_size));
|
||||
CHECK(SafepointTable::TaggedSlotsBytesField::is_valid(tagged_slots_bytes));
|
||||
|
||||
uint32_t entry_configuration =
|
||||
SafepointTable::HasDeoptDataField::encode(has_deopt_data) |
|
||||
|
@ -6,12 +6,10 @@
|
||||
#define V8_CODEGEN_SAFEPOINT_TABLE_H_
|
||||
|
||||
#include "src/base/bit-field.h"
|
||||
#include "src/base/iterator.h"
|
||||
#include "src/base/memory.h"
|
||||
#include "src/codegen/safepoint-table-base.h"
|
||||
#include "src/common/assert-scope.h"
|
||||
#include "src/utils/allocation.h"
|
||||
#include "src/utils/bit-vector.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/zone/zone-chunk-list.h"
|
||||
#include "src/zone/zone.h"
|
||||
|
||||
@ -22,49 +20,22 @@ namespace wasm {
|
||||
class WasmCode;
|
||||
} // namespace wasm
|
||||
|
||||
class SafepointEntry {
|
||||
class SafepointEntry : public SafepointEntryBase {
|
||||
public:
|
||||
static constexpr int kNoDeoptIndex = -1;
|
||||
static constexpr int kNoTrampolinePC = -1;
|
||||
|
||||
SafepointEntry() = default;
|
||||
|
||||
SafepointEntry(int pc, int deopt_index, uint32_t tagged_register_indexes,
|
||||
base::Vector<uint8_t> tagged_slots, int trampoline_pc)
|
||||
: pc_(pc),
|
||||
deopt_index_(deopt_index),
|
||||
: SafepointEntryBase(pc, deopt_index, trampoline_pc),
|
||||
tagged_register_indexes_(tagged_register_indexes),
|
||||
tagged_slots_(tagged_slots),
|
||||
trampoline_pc_(trampoline_pc) {
|
||||
tagged_slots_(tagged_slots) {
|
||||
DCHECK(is_initialized());
|
||||
}
|
||||
|
||||
bool is_initialized() const { return tagged_slots_.begin() != nullptr; }
|
||||
|
||||
bool operator==(const SafepointEntry& other) const {
|
||||
return pc_ == other.pc_ && deopt_index_ == other.deopt_index_ &&
|
||||
return this->SafepointEntryBase::operator==(other) &&
|
||||
tagged_register_indexes_ == other.tagged_register_indexes_ &&
|
||||
tagged_slots_ == other.tagged_slots_ &&
|
||||
trampoline_pc_ == other.trampoline_pc_;
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
*this = SafepointEntry{};
|
||||
DCHECK(!is_initialized());
|
||||
}
|
||||
|
||||
int pc() const { return pc_; }
|
||||
|
||||
int trampoline_pc() const { return trampoline_pc_; }
|
||||
|
||||
bool has_deoptimization_index() const {
|
||||
DCHECK(is_initialized());
|
||||
return deopt_index_ != kNoDeoptIndex;
|
||||
}
|
||||
|
||||
int deoptimization_index() const {
|
||||
DCHECK(is_initialized() && has_deoptimization_index());
|
||||
return deopt_index_;
|
||||
tagged_slots_ == other.tagged_slots_;
|
||||
}
|
||||
|
||||
uint32_t tagged_register_indexes() const {
|
||||
@ -74,15 +45,13 @@ class SafepointEntry {
|
||||
|
||||
base::Vector<const uint8_t> tagged_slots() const {
|
||||
DCHECK(is_initialized());
|
||||
DCHECK_NOT_NULL(tagged_slots_.data());
|
||||
return tagged_slots_;
|
||||
}
|
||||
|
||||
private:
|
||||
int pc_ = -1;
|
||||
int deopt_index_ = kNoDeoptIndex;
|
||||
uint32_t tagged_register_indexes_ = 0;
|
||||
base::Vector<uint8_t> tagged_slots_;
|
||||
int trampoline_pc_ = kNoTrampolinePC;
|
||||
};
|
||||
|
||||
// A wrapper class for accessing the safepoint table embedded into the Code
|
||||
@ -203,7 +172,7 @@ class SafepointTable {
|
||||
friend class SafepointEntry;
|
||||
};
|
||||
|
||||
class SafepointTableBuilder {
|
||||
class SafepointTableBuilder : public SafepointTableBuilderBase {
|
||||
private:
|
||||
struct EntryBuilder {
|
||||
int pc;
|
||||
@ -221,15 +190,6 @@ class SafepointTableBuilder {
|
||||
SafepointTableBuilder(const SafepointTableBuilder&) = delete;
|
||||
SafepointTableBuilder& operator=(const SafepointTableBuilder&) = delete;
|
||||
|
||||
bool emitted() const {
|
||||
return safepoint_table_offset_ != kNoSafepointTableOffset;
|
||||
}
|
||||
|
||||
int safepoint_table_offset() const {
|
||||
DCHECK(emitted());
|
||||
return safepoint_table_offset_;
|
||||
}
|
||||
|
||||
class Safepoint {
|
||||
public:
|
||||
void DefineTaggedStackSlot(int index) {
|
||||
@ -286,8 +246,6 @@ class SafepointTableBuilder {
|
||||
: min_stack_index_;
|
||||
}
|
||||
|
||||
static constexpr int kNoSafepointTableOffset = -1;
|
||||
|
||||
// Tracks the min/max stack slot index over all entries. We need the minimum
|
||||
// index when encoding the actual table since we shift all unused lower
|
||||
// indices out of the encoding. Tracking the indices during safepoint
|
||||
@ -298,8 +256,7 @@ class SafepointTableBuilder {
|
||||
int min_stack_index_ = std::numeric_limits<int>::max();
|
||||
|
||||
ZoneChunkList<EntryBuilder> entries_;
|
||||
int safepoint_table_offset_ = kNoSafepointTableOffset;
|
||||
Zone* const zone_;
|
||||
Zone* zone_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -371,7 +371,7 @@ Assembler::Assembler(const AssemblerOptions& options,
|
||||
}
|
||||
|
||||
void Assembler::GetCode(Isolate* isolate, CodeDesc* desc,
|
||||
SafepointTableBuilder* safepoint_table_builder,
|
||||
SafepointTableBuilderBase* safepoint_table_builder,
|
||||
int handler_table_offset) {
|
||||
// As a crutch to avoid having to add manual Align calls wherever we use a
|
||||
// raw workflow to create Code objects (mostly in tests), add another Align
|
||||
@ -408,6 +408,7 @@ void Assembler::GetCode(Isolate* isolate, CodeDesc* desc,
|
||||
(safepoint_table_builder == kNoSafepointTable)
|
||||
? handler_table_offset2
|
||||
: safepoint_table_builder->safepoint_table_offset();
|
||||
|
||||
const int reloc_info_offset =
|
||||
static_cast<int>(reloc_info_writer.pos() - buffer_->start());
|
||||
CodeDesc::Initialize(desc, this, safepoint_table_offset,
|
||||
|
@ -59,6 +59,7 @@ namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
class SafepointTableBuilder;
|
||||
class MaglevSafepointTableBuilder;
|
||||
|
||||
// Utility functions
|
||||
|
||||
@ -424,9 +425,10 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
|
||||
|
||||
// GetCode emits any pending (non-emitted) code and fills the descriptor desc.
|
||||
static constexpr int kNoHandlerTable = 0;
|
||||
static constexpr SafepointTableBuilder* kNoSafepointTable = nullptr;
|
||||
static constexpr SafepointTableBuilderBase* kNoSafepointTable = nullptr;
|
||||
|
||||
void GetCode(Isolate* isolate, CodeDesc* desc,
|
||||
SafepointTableBuilder* safepoint_table_builder,
|
||||
SafepointTableBuilderBase* safepoint_table_builder,
|
||||
int handler_table_offset);
|
||||
|
||||
// Convenience wrapper for code without safepoint or handler tables.
|
||||
@ -2554,6 +2556,9 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
|
||||
|
||||
int WriteCodeComments();
|
||||
|
||||
void GetCode(Isolate* isolate, CodeDesc* desc, int safepoint_table_offset,
|
||||
int handler_table_offset);
|
||||
|
||||
friend class EnsureSpace;
|
||||
friend class RegExpMacroAssemblerX64;
|
||||
|
||||
|
@ -265,9 +265,16 @@ class ActivationsFinder : public ThreadVisitor {
|
||||
code.marked_for_deoptimization()) {
|
||||
codes_->erase(code);
|
||||
// Obtain the trampoline to the deoptimizer call.
|
||||
SafepointEntry safepoint =
|
||||
code.GetSafepointEntry(isolate, it.frame()->pc());
|
||||
int trampoline_pc = safepoint.trampoline_pc();
|
||||
int trampoline_pc;
|
||||
if (code.is_maglevved()) {
|
||||
MaglevSafepointEntry safepoint =
|
||||
code.GetMaglevSafepointEntry(isolate, it.frame()->pc());
|
||||
trampoline_pc = safepoint.trampoline_pc();
|
||||
} else {
|
||||
SafepointEntry safepoint =
|
||||
code.GetSafepointEntry(isolate, it.frame()->pc());
|
||||
trampoline_pc = safepoint.trampoline_pc();
|
||||
}
|
||||
DCHECK_IMPLIES(code == topmost_, safe_to_deopt_);
|
||||
static_assert(SafepointEntry::kNoTrampolinePC == -1);
|
||||
CHECK_GE(trampoline_pc, 0);
|
||||
@ -311,11 +318,18 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(NativeContext native_context) {
|
||||
JSFunction function =
|
||||
static_cast<OptimizedFrame*>(it.frame())->function();
|
||||
TraceFoundActivation(isolate, function);
|
||||
SafepointEntry safepoint =
|
||||
code.GetSafepointEntry(isolate, it.frame()->pc());
|
||||
bool safe_if_deopt_triggered;
|
||||
if (code.is_maglevved()) {
|
||||
MaglevSafepointEntry safepoint =
|
||||
code.GetMaglevSafepointEntry(isolate, it.frame()->pc());
|
||||
safe_if_deopt_triggered = safepoint.has_deoptimization_index();
|
||||
} else {
|
||||
SafepointEntry safepoint =
|
||||
code.GetSafepointEntry(isolate, it.frame()->pc());
|
||||
safe_if_deopt_triggered = safepoint.has_deoptimization_index();
|
||||
}
|
||||
|
||||
// Deopt is checked when we are patching addresses on stack.
|
||||
bool safe_if_deopt_triggered = safepoint.has_deoptimization_index();
|
||||
bool is_builtin_code = code.kind() == CodeKind::BUILTIN;
|
||||
DCHECK(topmost_optimized_code.is_null() || safe_if_deopt_triggered ||
|
||||
is_builtin_code);
|
||||
|
@ -20,7 +20,11 @@ class InnerPointerToCodeCache {
|
||||
struct InnerPointerToCodeCacheEntry {
|
||||
Address inner_pointer;
|
||||
CodeLookupResult code;
|
||||
SafepointEntry safepoint_entry;
|
||||
union {
|
||||
SafepointEntry safepoint_entry;
|
||||
MaglevSafepointEntry maglev_safepoint_entry;
|
||||
};
|
||||
InnerPointerToCodeCacheEntry() : safepoint_entry() {}
|
||||
};
|
||||
|
||||
static void FlushCallback(v8::Isolate* isolate, v8::GCType type,
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "src/base/platform/wrappers.h"
|
||||
#include "src/codegen/interface-descriptors.h"
|
||||
#include "src/codegen/macro-assembler.h"
|
||||
#include "src/codegen/maglev-safepoint-table.h"
|
||||
#include "src/codegen/register-configuration.h"
|
||||
#include "src/codegen/safepoint-table.h"
|
||||
#include "src/common/globals.h"
|
||||
@ -1061,12 +1062,14 @@ void CommonFrame::IterateCompiledFrame(RootVisitor* v) const {
|
||||
// Find the code and compute the safepoint information.
|
||||
Address inner_pointer = pc();
|
||||
SafepointEntry safepoint_entry;
|
||||
MaglevSafepointEntry maglev_safepoint_entry;
|
||||
uint32_t stack_slots = 0;
|
||||
CodeLookupResult code_lookup_result;
|
||||
bool has_tagged_outgoing_params = false;
|
||||
uint16_t first_tagged_parameter_slot = 0;
|
||||
uint16_t num_tagged_parameter_slots = 0;
|
||||
bool is_wasm = false;
|
||||
bool is_maglev = false;
|
||||
|
||||
#if V8_ENABLE_WEBASSEMBLY
|
||||
bool has_wasm_feedback_slot = false;
|
||||
@ -1089,19 +1092,36 @@ void CommonFrame::IterateCompiledFrame(RootVisitor* v) const {
|
||||
if (!is_wasm) {
|
||||
InnerPointerToCodeCache::InnerPointerToCodeCacheEntry* entry =
|
||||
isolate()->inner_pointer_to_code_cache()->GetCacheEntry(inner_pointer);
|
||||
if (!entry->safepoint_entry.is_initialized()) {
|
||||
entry->safepoint_entry =
|
||||
entry->code.ToCode().GetSafepointEntry(isolate(), inner_pointer);
|
||||
DCHECK(entry->safepoint_entry.is_initialized());
|
||||
} else {
|
||||
DCHECK_EQ(entry->safepoint_entry, entry->code.ToCode().GetSafepointEntry(
|
||||
isolate(), inner_pointer));
|
||||
}
|
||||
|
||||
CHECK(entry->code.IsFound());
|
||||
code_lookup_result = entry->code;
|
||||
Code code = entry->code.ToCode();
|
||||
safepoint_entry = entry->safepoint_entry;
|
||||
is_maglev = code.is_maglevved();
|
||||
|
||||
if (is_maglev) {
|
||||
if (!entry->maglev_safepoint_entry.is_initialized()) {
|
||||
entry->maglev_safepoint_entry =
|
||||
entry->code.ToCode().GetMaglevSafepointEntry(isolate(),
|
||||
inner_pointer);
|
||||
DCHECK(entry->maglev_safepoint_entry.is_initialized());
|
||||
} else {
|
||||
DCHECK_EQ(entry->maglev_safepoint_entry,
|
||||
entry->code.ToCode().GetMaglevSafepointEntry(isolate(),
|
||||
inner_pointer));
|
||||
}
|
||||
maglev_safepoint_entry = entry->maglev_safepoint_entry;
|
||||
} else {
|
||||
if (!entry->safepoint_entry.is_initialized()) {
|
||||
entry->safepoint_entry =
|
||||
entry->code.ToCode().GetSafepointEntry(isolate(), inner_pointer);
|
||||
DCHECK(entry->safepoint_entry.is_initialized());
|
||||
} else {
|
||||
DCHECK_EQ(
|
||||
entry->safepoint_entry,
|
||||
entry->code.ToCode().GetSafepointEntry(isolate(), inner_pointer));
|
||||
}
|
||||
safepoint_entry = entry->safepoint_entry;
|
||||
}
|
||||
stack_slots = code.stack_slots();
|
||||
|
||||
has_tagged_outgoing_params = code.has_tagged_outgoing_params();
|
||||
@ -1198,76 +1218,111 @@ void CommonFrame::IterateCompiledFrame(RootVisitor* v) const {
|
||||
}
|
||||
|
||||
// Visit pointer spill slots and locals.
|
||||
DCHECK_GE((stack_slots + kBitsPerByte) / kBitsPerByte,
|
||||
safepoint_entry.tagged_slots().size());
|
||||
int slot_offset = 0;
|
||||
PtrComprCageBase cage_base(isolate());
|
||||
for (uint8_t bits : safepoint_entry.tagged_slots()) {
|
||||
while (bits) {
|
||||
const int bit = base::bits::CountTrailingZeros(bits);
|
||||
bits &= ~(1 << bit);
|
||||
FullObjectSlot spill_slot = parameters_limit + slot_offset + bit;
|
||||
#ifdef V8_COMPRESS_POINTERS
|
||||
// Spill slots may contain compressed values in which case the upper
|
||||
// 32-bits will contain zeros. In order to simplify handling of such
|
||||
// slots in GC we ensure that the slot always contains full value.
|
||||
auto visit_spill_slot = [&](FullObjectSlot spill_slot) {
|
||||
|
||||
// The spill slot may actually contain weak references so we load/store
|
||||
// values using spill_slot.location() in order to avoid dealing with
|
||||
// FullMaybeObjectSlots here.
|
||||
if (V8_EXTERNAL_CODE_SPACE_BOOL) {
|
||||
// When external code space is enabled the spill slot could contain both
|
||||
// Code and non-Code references, which have different cage bases. So
|
||||
// unconditional decompression of the value might corrupt Code pointers.
|
||||
// However, given that
|
||||
// 1) the Code pointers are never compressed by design (because
|
||||
// otherwise we wouldn't know which cage base to apply for
|
||||
// decompression, see respective DCHECKs in
|
||||
// RelocInfo::target_object()),
|
||||
// 2) there's no need to update the upper part of the full pointer
|
||||
// because if it was there then it'll stay the same,
|
||||
// we can avoid updating upper part of the spill slot if it already
|
||||
// contains full value.
|
||||
// TODO(v8:11880): Remove this special handling by enforcing builtins
|
||||
// to use CodeTs instead of Code objects.
|
||||
Address value = *spill_slot.location();
|
||||
if (!HAS_SMI_TAG(value) && value <= 0xffffffff) {
|
||||
// We don't need to update smi values or full pointers.
|
||||
*spill_slot.location() =
|
||||
DecompressTaggedPointer(cage_base, static_cast<Tagged_t>(value));
|
||||
if (DEBUG_BOOL) {
|
||||
// Ensure that the spill slot contains correct heap object.
|
||||
HeapObject raw = HeapObject::cast(Object(*spill_slot.location()));
|
||||
MapWord map_word = raw.map_word(cage_base, kRelaxedLoad);
|
||||
HeapObject forwarded = map_word.IsForwardingAddress()
|
||||
? map_word.ToForwardingAddress()
|
||||
: raw;
|
||||
bool is_self_forwarded =
|
||||
forwarded.map_word(cage_base, kRelaxedLoad).ptr() ==
|
||||
forwarded.address();
|
||||
if (is_self_forwarded) {
|
||||
// The object might be in a self-forwarding state if it's located
|
||||
// in new large object space. GC will fix this at a later stage.
|
||||
CHECK(BasicMemoryChunk::FromHeapObject(forwarded)
|
||||
->InNewLargeObjectSpace());
|
||||
} else {
|
||||
CHECK(forwarded.map(cage_base).IsMap(cage_base));
|
||||
}
|
||||
#ifdef V8_COMPRESS_POINTERS
|
||||
// Spill slots may contain compressed values in which case the upper
|
||||
// 32-bits will contain zeros. In order to simplify handling of such
|
||||
// slots in GC we ensure that the slot always contains full value.
|
||||
|
||||
// The spill slot may actually contain weak references so we load/store
|
||||
// values using spill_slot.location() in order to avoid dealing with
|
||||
// FullMaybeObjectSlots here.
|
||||
if (V8_EXTERNAL_CODE_SPACE_BOOL) {
|
||||
// When external code space is enabled the spill slot could contain both
|
||||
// Code and non-Code references, which have different cage bases. So
|
||||
// unconditional decompression of the value might corrupt Code pointers.
|
||||
// However, given that
|
||||
// 1) the Code pointers are never compressed by design (because
|
||||
// otherwise we wouldn't know which cage base to apply for
|
||||
// decompression, see respective DCHECKs in
|
||||
// RelocInfo::target_object()),
|
||||
// 2) there's no need to update the upper part of the full pointer
|
||||
// because if it was there then it'll stay the same,
|
||||
// we can avoid updating upper part of the spill slot if it already
|
||||
// contains full value.
|
||||
// TODO(v8:11880): Remove this special handling by enforcing builtins
|
||||
// to use CodeTs instead of Code objects.
|
||||
Address value = *spill_slot.location();
|
||||
if (!HAS_SMI_TAG(value) && value <= 0xffffffff) {
|
||||
// We don't need to update smi values or full pointers.
|
||||
*spill_slot.location() =
|
||||
DecompressTaggedPointer(cage_base, static_cast<Tagged_t>(value));
|
||||
if (DEBUG_BOOL) {
|
||||
// Ensure that the spill slot contains correct heap object.
|
||||
HeapObject raw = HeapObject::cast(Object(*spill_slot.location()));
|
||||
MapWord map_word = raw.map_word(cage_base, kRelaxedLoad);
|
||||
HeapObject forwarded = map_word.IsForwardingAddress()
|
||||
? map_word.ToForwardingAddress()
|
||||
: raw;
|
||||
bool is_self_forwarded =
|
||||
forwarded.map_word(cage_base, kRelaxedLoad).ptr() ==
|
||||
forwarded.address();
|
||||
if (is_self_forwarded) {
|
||||
// The object might be in a self-forwarding state if it's located
|
||||
// in new large object space. GC will fix this at a later stage.
|
||||
CHECK(BasicMemoryChunk::FromHeapObject(forwarded)
|
||||
->InNewLargeObjectSpace());
|
||||
} else {
|
||||
CHECK(forwarded.map(cage_base).IsMap(cage_base));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Tagged_t compressed_value =
|
||||
static_cast<Tagged_t>(*spill_slot.location());
|
||||
if (!HAS_SMI_TAG(compressed_value)) {
|
||||
// We don't need to update smi values.
|
||||
*spill_slot.location() =
|
||||
DecompressTaggedPointer(cage_base, compressed_value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
v->VisitRootPointer(Root::kStackRoots, nullptr, spill_slot);
|
||||
} else {
|
||||
Tagged_t compressed_value = static_cast<Tagged_t>(*spill_slot.location());
|
||||
if (!HAS_SMI_TAG(compressed_value)) {
|
||||
// We don't need to update smi values.
|
||||
*spill_slot.location() =
|
||||
DecompressTaggedPointer(cage_base, compressed_value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
v->VisitRootPointer(Root::kStackRoots, nullptr, spill_slot);
|
||||
};
|
||||
|
||||
if (is_maglev) {
|
||||
DCHECK_EQ(stack_slots, StandardFrameConstants::kFixedSlotCount +
|
||||
maglev_safepoint_entry.num_tagged_slots() +
|
||||
maglev_safepoint_entry.num_untagged_slots());
|
||||
|
||||
for (uint32_t i = 0; i < maglev_safepoint_entry.num_tagged_slots(); ++i) {
|
||||
FullObjectSlot spill_slot = frame_header_base - 1 - i;
|
||||
visit_spill_slot(spill_slot);
|
||||
}
|
||||
|
||||
// Maglev can also spill registers, tagged and untagged, just before making
|
||||
// a call. These are distinct from normal spill slots and live between the
|
||||
// normal spill slots and the pushed parameters. Some of these are tagged,
|
||||
// as indicated by the tagged register indexes, and should be visited too.
|
||||
if (maglev_safepoint_entry.num_pushed_registers() > 0) {
|
||||
FullObjectSlot pushed_register_base = parameters_limit;
|
||||
|
||||
uint32_t tagged_register_indexes =
|
||||
maglev_safepoint_entry.tagged_register_indexes();
|
||||
while (tagged_register_indexes != 0) {
|
||||
int index = base::bits::CountTrailingZeros(tagged_register_indexes);
|
||||
tagged_register_indexes &= ~(1 << index);
|
||||
FullObjectSlot spill_slot = pushed_register_base - index;
|
||||
visit_spill_slot(spill_slot);
|
||||
}
|
||||
|
||||
parameters_limit =
|
||||
pushed_register_base - maglev_safepoint_entry.num_pushed_registers();
|
||||
}
|
||||
} else {
|
||||
DCHECK_GE((stack_slots + kBitsPerByte) / kBitsPerByte,
|
||||
safepoint_entry.tagged_slots().size());
|
||||
int slot_offset = 0;
|
||||
for (uint8_t bits : safepoint_entry.tagged_slots()) {
|
||||
while (bits) {
|
||||
const int bit = base::bits::CountTrailingZeros(bits);
|
||||
bits &= ~(1 << bit);
|
||||
FullObjectSlot spill_slot = parameters_limit + slot_offset + bit;
|
||||
visit_spill_slot(spill_slot);
|
||||
}
|
||||
slot_offset += kBitsPerByte;
|
||||
}
|
||||
slot_offset += kBitsPerByte;
|
||||
}
|
||||
|
||||
// Visit tagged parameters that have been passed to the function of this
|
||||
@ -1907,10 +1962,19 @@ DeoptimizationData OptimizedFrame::GetDeoptimizationData(
|
||||
DCHECK(!code.is_null());
|
||||
DCHECK(CodeKindCanDeoptimize(code.kind()));
|
||||
|
||||
SafepointEntry safepoint_entry = code.GetSafepointEntry(isolate(), pc());
|
||||
if (safepoint_entry.has_deoptimization_index()) {
|
||||
*deopt_index = safepoint_entry.deoptimization_index();
|
||||
return DeoptimizationData::cast(code.deoptimization_data());
|
||||
if (code.is_maglevved()) {
|
||||
MaglevSafepointEntry safepoint_entry =
|
||||
code.GetMaglevSafepointEntry(isolate(), pc());
|
||||
if (safepoint_entry.has_deoptimization_index()) {
|
||||
*deopt_index = safepoint_entry.deoptimization_index();
|
||||
return DeoptimizationData::cast(code.deoptimization_data());
|
||||
}
|
||||
} else {
|
||||
SafepointEntry safepoint_entry = code.GetSafepointEntry(isolate(), pc());
|
||||
if (safepoint_entry.has_deoptimization_index()) {
|
||||
*deopt_index = safepoint_entry.deoptimization_index();
|
||||
return DeoptimizationData::cast(code.deoptimization_data());
|
||||
}
|
||||
}
|
||||
*deopt_index = SafepointEntry::kNoDeoptIndex;
|
||||
return DeoptimizationData();
|
||||
@ -2502,7 +2566,11 @@ InnerPointerToCodeCache::GetCacheEntry(Address inner_pointer) {
|
||||
// the code has been computed.
|
||||
entry->code =
|
||||
isolate_->heap()->GcSafeFindCodeForInnerPointer(inner_pointer);
|
||||
entry->safepoint_entry.Reset();
|
||||
if (entry->code.IsCode() && entry->code.code().is_maglevved()) {
|
||||
entry->maglev_safepoint_entry.Reset();
|
||||
} else {
|
||||
entry->safepoint_entry.Reset();
|
||||
}
|
||||
entry->inner_pointer = inner_pointer;
|
||||
}
|
||||
return entry;
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "src/codegen/label.h"
|
||||
#include "src/codegen/machine-type.h"
|
||||
#include "src/codegen/macro-assembler.h"
|
||||
#include "src/codegen/safepoint-table.h"
|
||||
#include "src/codegen/maglev-safepoint-table.h"
|
||||
#include "src/common/globals.h"
|
||||
#include "src/compiler/backend/instruction.h"
|
||||
#include "src/compiler/js-heap-broker.h"
|
||||
@ -33,7 +33,7 @@ class DeferredCodeInfo {
|
||||
class MaglevCodeGenState {
|
||||
public:
|
||||
MaglevCodeGenState(MaglevCompilationInfo* compilation_info,
|
||||
SafepointTableBuilder* safepoint_table_builder)
|
||||
MaglevSafepointTableBuilder* safepoint_table_builder)
|
||||
: compilation_info_(compilation_info),
|
||||
safepoint_table_builder_(safepoint_table_builder),
|
||||
masm_(isolate(), CodeObjectRequired::kNo) {}
|
||||
@ -55,8 +55,6 @@ class MaglevCodeGenState {
|
||||
const std::vector<LazyDeoptInfo*>& lazy_deopts() const {
|
||||
return lazy_deopts_;
|
||||
}
|
||||
inline void DefineSafepointStackSlots(
|
||||
SafepointTableBuilder::Safepoint& safepoint) const;
|
||||
inline void DefineLazyDeoptPoint(LazyDeoptInfo* info);
|
||||
|
||||
compiler::NativeContextRef native_context() const {
|
||||
@ -69,7 +67,7 @@ class MaglevCodeGenState {
|
||||
}
|
||||
MacroAssembler* masm() { return &masm_; }
|
||||
int stack_slots() const { return untagged_slots_ + tagged_slots_; }
|
||||
SafepointTableBuilder* safepoint_table_builder() const {
|
||||
MaglevSafepointTableBuilder* safepoint_table_builder() const {
|
||||
return safepoint_table_builder_;
|
||||
}
|
||||
MaglevCompilationInfo* compilation_info() const { return compilation_info_; }
|
||||
@ -115,7 +113,7 @@ class MaglevCodeGenState {
|
||||
}
|
||||
|
||||
MaglevCompilationInfo* const compilation_info_;
|
||||
SafepointTableBuilder* const safepoint_table_builder_;
|
||||
MaglevSafepointTableBuilder* const safepoint_table_builder_;
|
||||
|
||||
MacroAssembler masm_;
|
||||
std::vector<DeferredCodeInfo*> deferred_code_;
|
||||
@ -156,19 +154,10 @@ inline DoubleRegister ToDoubleRegister(const ValueLocation& location) {
|
||||
return ToDoubleRegister(location.operand());
|
||||
}
|
||||
|
||||
inline void MaglevCodeGenState::DefineSafepointStackSlots(
|
||||
SafepointTableBuilder::Safepoint& safepoint) const {
|
||||
for (int stack_slot = 0; stack_slot < tagged_slots_; stack_slot++) {
|
||||
safepoint.DefineTaggedStackSlot(GetSafepointIndexForStackSlot(stack_slot));
|
||||
}
|
||||
}
|
||||
|
||||
inline void MaglevCodeGenState::DefineLazyDeoptPoint(LazyDeoptInfo* info) {
|
||||
info->deopting_call_return_pc = masm()->pc_offset_for_safepoint();
|
||||
PushLazyDeopt(info);
|
||||
SafepointTableBuilder::Safepoint safepoint =
|
||||
safepoint_table_builder()->DefineSafepoint(masm());
|
||||
DefineSafepointStackSlots(safepoint);
|
||||
safepoint_table_builder()->DefineSafepoint(masm());
|
||||
}
|
||||
|
||||
} // namespace maglev
|
||||
|
@ -104,11 +104,8 @@ class MaglevCodeGeneratingNodeProcessor {
|
||||
Immediate(graph->untagged_stack_slots() * kSystemPointerSize));
|
||||
}
|
||||
|
||||
// We don't emit proper safepoint data yet; instead, define a single
|
||||
// safepoint at the end of the code object.
|
||||
SafepointTableBuilder::Safepoint safepoint =
|
||||
safepoint_table_builder()->DefineSafepoint(masm());
|
||||
code_gen_state_->DefineSafepointStackSlots(safepoint);
|
||||
// Define a single safepoint at the end of the code object.
|
||||
safepoint_table_builder()->DefineSafepoint(masm());
|
||||
}
|
||||
|
||||
void PostProcessGraph(MaglevCompilationInfo*, Graph*) {}
|
||||
@ -334,7 +331,7 @@ class MaglevCodeGeneratingNodeProcessor {
|
||||
MaglevGraphLabeller* graph_labeller() const {
|
||||
return code_gen_state_->graph_labeller();
|
||||
}
|
||||
SafepointTableBuilder* safepoint_table_builder() const {
|
||||
MaglevSafepointTableBuilder* safepoint_table_builder() const {
|
||||
return code_gen_state_->safepoint_table_builder();
|
||||
}
|
||||
|
||||
@ -355,7 +352,9 @@ class MaglevCodeGeneratorImpl final {
|
||||
static constexpr int kOptimizedOutConstantIndex = 0;
|
||||
|
||||
MaglevCodeGeneratorImpl(MaglevCompilationInfo* compilation_info, Graph* graph)
|
||||
: safepoint_table_builder_(compilation_info->zone()),
|
||||
: safepoint_table_builder_(compilation_info->zone(),
|
||||
graph->tagged_stack_slots(),
|
||||
graph->untagged_stack_slots()),
|
||||
translation_array_builder_(compilation_info->zone()),
|
||||
code_gen_state_(compilation_info, safepoint_table_builder()),
|
||||
processor_(compilation_info, &code_gen_state_),
|
||||
@ -656,8 +655,7 @@ class MaglevCodeGeneratorImpl final {
|
||||
// Final alignment before starting on the metadata section.
|
||||
masm()->Align(Code::kMetadataAlignment);
|
||||
|
||||
safepoint_table_builder()->Emit(masm(),
|
||||
stack_slot_count_with_fixed_frame());
|
||||
safepoint_table_builder()->Emit(masm());
|
||||
}
|
||||
|
||||
MaybeHandle<Code> BuildCodeObject() {
|
||||
@ -757,7 +755,7 @@ class MaglevCodeGeneratorImpl final {
|
||||
return code_gen_state_.compilation_info()->isolate();
|
||||
}
|
||||
MacroAssembler* masm() { return code_gen_state_.masm(); }
|
||||
SafepointTableBuilder* safepoint_table_builder() {
|
||||
MaglevSafepointTableBuilder* safepoint_table_builder() {
|
||||
return &safepoint_table_builder_;
|
||||
}
|
||||
TranslationArrayBuilder* translation_array_builder() {
|
||||
@ -773,7 +771,7 @@ class MaglevCodeGeneratorImpl final {
|
||||
return *res.entry;
|
||||
}
|
||||
|
||||
SafepointTableBuilder safepoint_table_builder_;
|
||||
MaglevSafepointTableBuilder safepoint_table_builder_;
|
||||
TranslationArrayBuilder translation_array_builder_;
|
||||
MaglevCodeGenState code_gen_state_;
|
||||
GraphProcessor<MaglevCodeGeneratingNodeProcessor> processor_;
|
||||
|
@ -215,10 +215,18 @@ void Code::RelocateFromDesc(ByteArray reloc_info, Heap* heap,
|
||||
}
|
||||
|
||||
SafepointEntry Code::GetSafepointEntry(Isolate* isolate, Address pc) {
|
||||
DCHECK(!is_maglevved());
|
||||
SafepointTable table(isolate, pc, *this);
|
||||
return table.FindEntry(pc);
|
||||
}
|
||||
|
||||
MaglevSafepointEntry Code::GetMaglevSafepointEntry(Isolate* isolate,
|
||||
Address pc) {
|
||||
DCHECK(is_maglevved());
|
||||
MaglevSafepointTable table(isolate, pc, *this);
|
||||
return table.FindEntry(pc);
|
||||
}
|
||||
|
||||
Address Code::OffHeapInstructionStart(Isolate* isolate, Address pc) const {
|
||||
DCHECK(is_off_heap_trampoline());
|
||||
EmbeddedData d = EmbeddedData::GetEmbeddedDataForPC(isolate, pc);
|
||||
@ -591,8 +599,13 @@ void Code::Disassemble(const char* name, std::ostream& os, Isolate* isolate,
|
||||
os << "\n";
|
||||
|
||||
if (uses_safepoint_table()) {
|
||||
SafepointTable table(isolate, current_pc, *this);
|
||||
table.Print(os);
|
||||
if (is_maglevved()) {
|
||||
MaglevSafepointTable table(isolate, current_pc, *this);
|
||||
table.Print(os);
|
||||
} else {
|
||||
SafepointTable table(isolate, current_pc, *this);
|
||||
table.Print(os);
|
||||
}
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "src/base/bit-field.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/codegen/handler-table.h"
|
||||
#include "src/codegen/maglev-safepoint-table.h"
|
||||
#include "src/deoptimizer/translation-array.h"
|
||||
#include "src/objects/code-kind.h"
|
||||
#include "src/objects/contexts.h"
|
||||
@ -508,6 +509,9 @@ class Code : public HeapObject {
|
||||
// Get the safepoint entry for the given pc.
|
||||
SafepointEntry GetSafepointEntry(Isolate* isolate, Address pc);
|
||||
|
||||
// Get the maglev safepoint entry for the given pc.
|
||||
MaglevSafepointEntry GetMaglevSafepointEntry(Isolate* isolate, Address pc);
|
||||
|
||||
// The entire code object including its header is copied verbatim to the
|
||||
// snapshot so that it can be written in one, fast, memcpy during
|
||||
// deserialization. The deserializer will overwrite some pointers, rather
|
||||
|
Loading…
Reference in New Issue
Block a user