| // 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); |
| #ifdef V8_EXTERNAL_CODE_SPACE |
| explicit MaglevSafepointTable(Isolate* isolate, Address pc, |
| CodeDataContainer code); |
| #endif |
| 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_ |