// 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 #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; using RegisterIndexesSizeField = HasDeoptDataField::Next; using PcSizeField = RegisterIndexesSizeField::Next; using DeoptIndexSizeField = PcSizeField::Next; 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(result); } static uint8_t read_byte(Address* ptr) { uint8_t result = *reinterpret_cast(*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: const uint32_t num_tagged_slots_; const uint32_t num_untagged_slots_; ZoneChunkList entries_; }; } // namespace internal } // namespace v8 #endif // V8_CODEGEN_MAGLEV_SAFEPOINT_TABLE_H_