1 // Copyright 2011 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_CODEGEN_SAFEPOINT_TABLE_H_ 6 #define V8_CODEGEN_SAFEPOINT_TABLE_H_ 7 8 #include "src/base/bit-field.h" 9 #include "src/base/iterator.h" 10 #include "src/base/memory.h" 11 #include "src/common/assert-scope.h" 12 #include "src/utils/allocation.h" 13 #include "src/utils/bit-vector.h" 14 #include "src/utils/utils.h" 15 #include "src/zone/zone-chunk-list.h" 16 #include "src/zone/zone.h" 17 18 namespace v8 { 19 namespace internal { 20 21 namespace wasm { 22 class WasmCode; 23 } // namespace wasm 24 25 class SafepointEntry { 26 public: 27 static constexpr int kNoDeoptIndex = -1; 28 static constexpr int kNoTrampolinePC = -1; 29 30 SafepointEntry() = default; 31 SafepointEntry(int pc,int deopt_index,uint32_t tagged_register_indexes,base::Vector<uint8_t> tagged_slots,int trampoline_pc)32 SafepointEntry(int pc, int deopt_index, uint32_t tagged_register_indexes, 33 base::Vector<uint8_t> tagged_slots, int trampoline_pc) 34 : pc_(pc), 35 deopt_index_(deopt_index), 36 tagged_register_indexes_(tagged_register_indexes), 37 tagged_slots_(tagged_slots), 38 trampoline_pc_(trampoline_pc) { 39 DCHECK(is_initialized()); 40 } 41 is_initialized()42 bool is_initialized() const { return tagged_slots_.begin() != nullptr; } 43 44 bool operator==(const SafepointEntry& other) const { 45 return pc_ == other.pc_ && deopt_index_ == other.deopt_index_ && 46 tagged_register_indexes_ == other.tagged_register_indexes_ && 47 tagged_slots_ == other.tagged_slots_ && 48 trampoline_pc_ == other.trampoline_pc_; 49 } 50 Reset()51 void Reset() { 52 *this = SafepointEntry{}; 53 DCHECK(!is_initialized()); 54 } 55 pc()56 int pc() const { return pc_; } 57 trampoline_pc()58 int trampoline_pc() const { return trampoline_pc_; } 59 has_deoptimization_index()60 bool has_deoptimization_index() const { 61 DCHECK(is_initialized()); 62 return deopt_index_ != kNoDeoptIndex; 63 } 64 deoptimization_index()65 int deoptimization_index() const { 66 DCHECK(is_initialized() && has_deoptimization_index()); 67 return deopt_index_; 68 } 69 tagged_register_indexes()70 uint32_t tagged_register_indexes() const { 71 DCHECK(is_initialized()); 72 return tagged_register_indexes_; 73 } 74 tagged_slots()75 base::Vector<const uint8_t> tagged_slots() const { 76 DCHECK(is_initialized()); 77 return tagged_slots_; 78 } 79 80 private: 81 int pc_ = -1; 82 int deopt_index_ = kNoDeoptIndex; 83 uint32_t tagged_register_indexes_ = 0; 84 base::Vector<uint8_t> tagged_slots_; 85 int trampoline_pc_ = kNoTrampolinePC; 86 }; 87 88 // A wrapper class for accessing the safepoint table embedded into the Code 89 // object. 90 class SafepointTable { 91 public: 92 // The isolate and pc arguments are used for figuring out whether pc 93 // belongs to the embedded or un-embedded code blob. 94 explicit SafepointTable(Isolate* isolate, Address pc, Code code); 95 #if V8_ENABLE_WEBASSEMBLY 96 explicit SafepointTable(const wasm::WasmCode* code); 97 #endif // V8_ENABLE_WEBASSEMBLY 98 99 SafepointTable(const SafepointTable&) = delete; 100 SafepointTable& operator=(const SafepointTable&) = delete; 101 length()102 int length() const { return length_; } 103 byte_size()104 int byte_size() const { 105 return kHeaderSize + length_ * (entry_size() + tagged_slots_bytes()); 106 } 107 108 int find_return_pc(int pc_offset); 109 GetEntry(int index)110 SafepointEntry GetEntry(int index) const { 111 DCHECK_GT(length_, index); 112 Address entry_ptr = 113 safepoint_table_address_ + kHeaderSize + index * entry_size(); 114 115 int pc = read_bytes(&entry_ptr, pc_size()); 116 int deopt_index = SafepointEntry::kNoDeoptIndex; 117 int trampoline_pc = SafepointEntry::kNoTrampolinePC; 118 if (has_deopt_data()) { 119 STATIC_ASSERT(SafepointEntry::kNoDeoptIndex == -1); 120 STATIC_ASSERT(SafepointEntry::kNoTrampolinePC == -1); 121 // `-1` to restore the original value, see also 122 // SafepointTableBuilder::Emit. 123 deopt_index = read_bytes(&entry_ptr, deopt_index_size()) - 1; 124 trampoline_pc = read_bytes(&entry_ptr, pc_size()) - 1; 125 DCHECK(deopt_index >= 0 || deopt_index == SafepointEntry::kNoDeoptIndex); 126 DCHECK(trampoline_pc >= 0 || 127 trampoline_pc == SafepointEntry::kNoTrampolinePC); 128 } 129 int tagged_register_indexes = 130 read_bytes(&entry_ptr, register_indexes_size()); 131 132 // Entry bits start after the the vector of entries (thus the pc offset of 133 // the non-existing entry after the last one). 134 uint8_t* tagged_slots_start = reinterpret_cast<uint8_t*>( 135 safepoint_table_address_ + kHeaderSize + length_ * entry_size()); 136 base::Vector<uint8_t> tagged_slots( 137 tagged_slots_start + index * tagged_slots_bytes(), 138 tagged_slots_bytes()); 139 140 return SafepointEntry(pc, deopt_index, tagged_register_indexes, 141 tagged_slots, trampoline_pc); 142 } 143 144 // Returns the entry for the given pc. 145 SafepointEntry FindEntry(Address pc) const; 146 147 void Print(std::ostream&) const; 148 149 private: 150 // Layout information. 151 static constexpr int kLengthOffset = 0; 152 static constexpr int kEntryConfigurationOffset = kLengthOffset + kIntSize; 153 static constexpr int kHeaderSize = kEntryConfigurationOffset + kUInt32Size; 154 155 using HasDeoptDataField = base::BitField<bool, 0, 1>; 156 using RegisterIndexesSizeField = HasDeoptDataField::Next<int, 3>; 157 using PcSizeField = RegisterIndexesSizeField::Next<int, 3>; 158 using DeoptIndexSizeField = PcSizeField::Next<int, 3>; 159 // In 22 bits, we can encode up to 4M bytes, corresponding to 32M frame slots, 160 // which is 128MB on 32-bit and 256MB on 64-bit systems. The stack size is 161 // limited to a bit below 1MB anyway (see FLAG_stack_size). 162 using TaggedSlotsBytesField = DeoptIndexSizeField::Next<int, 22>; 163 164 SafepointTable(Address instruction_start, Address safepoint_table_address); 165 entry_size()166 int entry_size() const { 167 int deopt_data_size = has_deopt_data() ? pc_size() + deopt_index_size() : 0; 168 return pc_size() + deopt_data_size + register_indexes_size(); 169 } 170 tagged_slots_bytes()171 int tagged_slots_bytes() const { 172 return TaggedSlotsBytesField::decode(entry_configuration_); 173 } has_deopt_data()174 bool has_deopt_data() const { 175 return HasDeoptDataField::decode(entry_configuration_); 176 } pc_size()177 int pc_size() const { return PcSizeField::decode(entry_configuration_); } deopt_index_size()178 int deopt_index_size() const { 179 return DeoptIndexSizeField::decode(entry_configuration_); 180 } register_indexes_size()181 int register_indexes_size() const { 182 return RegisterIndexesSizeField::decode(entry_configuration_); 183 } 184 read_bytes(Address * ptr,int bytes)185 static int read_bytes(Address* ptr, int bytes) { 186 uint32_t result = 0; 187 for (int b = 0; b < bytes; ++b, ++*ptr) { 188 result |= uint32_t{*reinterpret_cast<uint8_t*>(*ptr)} << (8 * b); 189 } 190 return static_cast<int>(result); 191 } 192 193 DISALLOW_GARBAGE_COLLECTION(no_gc_) 194 195 const Address instruction_start_; 196 197 // Safepoint table layout. 198 const Address safepoint_table_address_; 199 const int length_; 200 const uint32_t entry_configuration_; 201 202 friend class SafepointTableBuilder; 203 friend class SafepointEntry; 204 }; 205 206 class SafepointTableBuilder { 207 private: 208 struct EntryBuilder { 209 int pc; 210 int deopt_index = SafepointEntry::kNoDeoptIndex; 211 int trampoline = SafepointEntry::kNoTrampolinePC; 212 GrowableBitVector* stack_indexes; 213 uint32_t register_indexes = 0; EntryBuilderEntryBuilder214 EntryBuilder(Zone* zone, int pc) 215 : pc(pc), stack_indexes(zone->New<GrowableBitVector>()) {} 216 }; 217 218 public: SafepointTableBuilder(Zone * zone)219 explicit SafepointTableBuilder(Zone* zone) : entries_(zone), zone_(zone) {} 220 221 SafepointTableBuilder(const SafepointTableBuilder&) = delete; 222 SafepointTableBuilder& operator=(const SafepointTableBuilder&) = delete; 223 emitted()224 bool emitted() const { 225 return safepoint_table_offset_ != kNoSafepointTableOffset; 226 } 227 safepoint_table_offset()228 int safepoint_table_offset() const { 229 DCHECK(emitted()); 230 return safepoint_table_offset_; 231 } 232 233 class Safepoint { 234 public: DefineTaggedStackSlot(int index)235 void DefineTaggedStackSlot(int index) { 236 // Note it is only valid to specify stack slots here that are *not* in 237 // the fixed part of the frame (e.g. argc, target, context, stored rbp, 238 // return address). Frame iteration handles the fixed part of the frame 239 // with custom code, see CommonFrame::IterateCompiledFrame. 240 entry_->stack_indexes->Add(index, table_->zone_); 241 table_->UpdateMinMaxStackIndex(index); 242 } 243 DefineTaggedRegister(int reg_code)244 void DefineTaggedRegister(int reg_code) { 245 DCHECK_LT(reg_code, 246 kBitsPerByte * sizeof(EntryBuilder::register_indexes)); 247 entry_->register_indexes |= 1u << reg_code; 248 } 249 250 private: 251 friend class SafepointTableBuilder; Safepoint(EntryBuilder * entry,SafepointTableBuilder * table)252 Safepoint(EntryBuilder* entry, SafepointTableBuilder* table) 253 : entry_(entry), table_(table) {} 254 EntryBuilder* const entry_; 255 SafepointTableBuilder* const table_; 256 }; 257 258 // Define a new safepoint for the current position in the body. 259 Safepoint DefineSafepoint(Assembler* assembler); 260 261 // Emit the safepoint table after the body. The number of bits per 262 // entry must be enough to hold all the pointer indexes. 263 V8_EXPORT_PRIVATE void Emit(Assembler* assembler, int bits_per_entry); 264 265 // Find the Deoptimization Info with pc offset {pc} and update its 266 // trampoline field. Calling this function ensures that the safepoint 267 // table contains the trampoline PC {trampoline} that replaced the 268 // return PC {pc} on the stack. 269 int UpdateDeoptimizationInfo(int pc, int trampoline, int start, 270 int deopt_index); 271 272 private: 273 // Remove consecutive identical entries. 274 void RemoveDuplicates(); 275 UpdateMinMaxStackIndex(int index)276 void UpdateMinMaxStackIndex(int index) { 277 #ifdef DEBUG 278 max_stack_index_ = std::max(max_stack_index_, index); 279 #endif // DEBUG 280 min_stack_index_ = std::min(min_stack_index_, index); 281 } 282 min_stack_index()283 int min_stack_index() const { 284 return min_stack_index_ == std::numeric_limits<int>::max() 285 ? 0 286 : min_stack_index_; 287 } 288 289 static constexpr int kNoSafepointTableOffset = -1; 290 291 // Tracks the min/max stack slot index over all entries. We need the minimum 292 // index when encoding the actual table since we shift all unused lower 293 // indices out of the encoding. Tracking the indices during safepoint 294 // construction means we don't have to iterate again later. 295 #ifdef DEBUG 296 int max_stack_index_ = 0; 297 #endif // DEBUG 298 int min_stack_index_ = std::numeric_limits<int>::max(); 299 300 ZoneChunkList<EntryBuilder> entries_; 301 int safepoint_table_offset_ = kNoSafepointTableOffset; 302 Zone* const zone_; 303 }; 304 305 } // namespace internal 306 } // namespace v8 307 308 #endif // V8_CODEGEN_SAFEPOINT_TABLE_H_ 309