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/memory.h" 9 #include "src/common/assert-scope.h" 10 #include "src/utils/allocation.h" 11 #include "src/utils/utils.h" 12 #include "src/zone/zone-chunk-list.h" 13 #include "src/zone/zone.h" 14 15 namespace v8 { 16 namespace internal { 17 18 namespace wasm { 19 class WasmCode; 20 } // namespace wasm 21 22 class SafepointEntry { 23 public: SafepointEntry()24 SafepointEntry() : deopt_index_(0), bits_(nullptr), trampoline_pc_(-1) {} 25 SafepointEntry(unsigned deopt_index,uint8_t * bits,int trampoline_pc)26 SafepointEntry(unsigned deopt_index, uint8_t* bits, int trampoline_pc) 27 : deopt_index_(deopt_index), bits_(bits), trampoline_pc_(trampoline_pc) { 28 DCHECK(is_valid()); 29 } 30 is_valid()31 bool is_valid() const { return bits_ != nullptr; } 32 Equals(const SafepointEntry & other)33 bool Equals(const SafepointEntry& other) const { 34 return deopt_index_ == other.deopt_index_ && bits_ == other.bits_; 35 } 36 Reset()37 void Reset() { 38 deopt_index_ = 0; 39 bits_ = nullptr; 40 } 41 trampoline_pc()42 int trampoline_pc() { return trampoline_pc_; } 43 44 static const unsigned kNoDeoptIndex = kMaxUInt32; 45 deoptimization_index()46 int deoptimization_index() const { 47 DCHECK(is_valid() && has_deoptimization_index()); 48 return deopt_index_; 49 } 50 has_deoptimization_index()51 bool has_deoptimization_index() const { 52 DCHECK(is_valid()); 53 return deopt_index_ != kNoDeoptIndex; 54 } 55 bits()56 uint8_t* bits() { 57 DCHECK(is_valid()); 58 return bits_; 59 } 60 61 private: 62 unsigned deopt_index_; 63 uint8_t* bits_; 64 // It needs to be an integer as it is -1 for eager deoptimizations. 65 int trampoline_pc_; 66 }; 67 68 class SafepointTable { 69 public: 70 explicit SafepointTable(Code code); 71 explicit SafepointTable(const wasm::WasmCode* code); 72 size()73 int size() const { 74 return kHeaderSize + (length_ * (kFixedEntrySize + entry_size_)); 75 } length()76 unsigned length() const { return length_; } entry_size()77 unsigned entry_size() const { return entry_size_; } 78 GetPcOffset(unsigned index)79 unsigned GetPcOffset(unsigned index) const { 80 DCHECK(index < length_); 81 return base::Memory<uint32_t>(GetPcOffsetLocation(index)); 82 } 83 GetTrampolinePcOffset(unsigned index)84 int GetTrampolinePcOffset(unsigned index) const { 85 DCHECK(index < length_); 86 return base::Memory<int>(GetTrampolineLocation(index)); 87 } 88 89 unsigned find_return_pc(unsigned pc_offset); 90 GetEntry(unsigned index)91 SafepointEntry GetEntry(unsigned index) const { 92 DCHECK(index < length_); 93 unsigned deopt_index = 94 base::Memory<uint32_t>(GetEncodedInfoLocation(index)); 95 uint8_t* bits = &base::Memory<uint8_t>(entries() + (index * entry_size_)); 96 int trampoline_pc = 97 has_deopt_ ? base::Memory<int>(GetTrampolineLocation(index)) : -1; 98 return SafepointEntry(deopt_index, bits, trampoline_pc); 99 } 100 101 // Returns the entry for the given pc. 102 SafepointEntry FindEntry(Address pc) const; 103 104 void PrintEntry(unsigned index, std::ostream& os) const; // NOLINT 105 106 private: 107 SafepointTable(Address instruction_start, Address safepoint_table_address, 108 uint32_t stack_slots, bool has_deopt); 109 110 static const uint8_t kNoRegisters = 0xFF; 111 112 // Layout information. 113 static const int kLengthOffset = 0; 114 static const int kEntrySizeOffset = kLengthOffset + kIntSize; 115 static const int kHeaderSize = kEntrySizeOffset + kIntSize; 116 static const int kPcOffset = 0; 117 static const int kEncodedInfoOffset = kPcOffset + kIntSize; 118 static const int kTrampolinePcOffset = kEncodedInfoOffset + kIntSize; 119 static const int kFixedEntrySize = kTrampolinePcOffset + kIntSize; 120 ReadLength(Address table)121 static uint32_t ReadLength(Address table) { 122 return base::Memory<uint32_t>(table + kLengthOffset); 123 } ReadEntrySize(Address table)124 static uint32_t ReadEntrySize(Address table) { 125 return base::Memory<uint32_t>(table + kEntrySizeOffset); 126 } pc_and_deoptimization_indexes()127 Address pc_and_deoptimization_indexes() const { 128 return safepoint_table_address_ + kHeaderSize; 129 } entries()130 Address entries() const { 131 return safepoint_table_address_ + kHeaderSize + (length_ * kFixedEntrySize); 132 } 133 GetPcOffsetLocation(unsigned index)134 Address GetPcOffsetLocation(unsigned index) const { 135 return pc_and_deoptimization_indexes() + (index * kFixedEntrySize); 136 } 137 GetEncodedInfoLocation(unsigned index)138 Address GetEncodedInfoLocation(unsigned index) const { 139 return GetPcOffsetLocation(index) + kEncodedInfoOffset; 140 } 141 GetTrampolineLocation(unsigned index)142 Address GetTrampolineLocation(unsigned index) const { 143 return GetPcOffsetLocation(index) + kTrampolinePcOffset; 144 } 145 146 static void PrintBits(std::ostream& os, uint8_t byte, int digits); 147 148 DISALLOW_HEAP_ALLOCATION(no_allocation_) 149 150 const Address instruction_start_; 151 const uint32_t stack_slots_; 152 const bool has_deopt_; 153 154 // Safepoint table layout. 155 const Address safepoint_table_address_; 156 const uint32_t length_; 157 const uint32_t entry_size_; 158 159 friend class SafepointTableBuilder; 160 friend class SafepointEntry; 161 162 DISALLOW_COPY_AND_ASSIGN(SafepointTable); 163 }; 164 165 class Safepoint { 166 public: 167 enum DeoptMode { kNoLazyDeopt, kLazyDeopt }; 168 169 static const int kNoDeoptimizationIndex = SafepointEntry::kNoDeoptIndex; 170 DefinePointerSlot(int index)171 void DefinePointerSlot(int index) { indexes_->push_back(index); } 172 173 private: Safepoint(ZoneChunkList<int> * indexes)174 explicit Safepoint(ZoneChunkList<int>* indexes) : indexes_(indexes) {} 175 ZoneChunkList<int>* const indexes_; 176 177 friend class SafepointTableBuilder; 178 }; 179 180 class SafepointTableBuilder { 181 public: SafepointTableBuilder(Zone * zone)182 explicit SafepointTableBuilder(Zone* zone) 183 : deoptimization_info_(zone), 184 emitted_(false), 185 zone_(zone) {} 186 187 // Get the offset of the emitted safepoint table in the code. 188 unsigned GetCodeOffset() const; 189 190 // Define a new safepoint for the current position in the body. 191 Safepoint DefineSafepoint(Assembler* assembler, Safepoint::DeoptMode mode); 192 193 // Emit the safepoint table after the body. The number of bits per 194 // entry must be enough to hold all the pointer indexes. 195 V8_EXPORT_PRIVATE void Emit(Assembler* assembler, int bits_per_entry); 196 197 // Find the Deoptimization Info with pc offset {pc} and update its 198 // trampoline field. Calling this function ensures that the safepoint 199 // table contains the trampoline PC {trampoline} that replaced the 200 // return PC {pc} on the stack. 201 int UpdateDeoptimizationInfo(int pc, int trampoline, int start, 202 unsigned deopt_index); 203 204 private: 205 struct DeoptimizationInfo { 206 unsigned pc; 207 unsigned deopt_index; 208 int trampoline; 209 ZoneChunkList<int>* indexes; DeoptimizationInfoDeoptimizationInfo210 DeoptimizationInfo(Zone* zone, unsigned pc) 211 : pc(pc), 212 deopt_index(Safepoint::kNoDeoptimizationIndex), 213 trampoline(-1), 214 indexes(zone->New<ZoneChunkList<int>>( 215 zone, ZoneChunkList<int>::StartMode::kSmall)) {} 216 }; 217 218 // Compares all fields of a {DeoptimizationInfo} except {pc} and {trampoline}. 219 bool IsIdenticalExceptForPc(const DeoptimizationInfo&, 220 const DeoptimizationInfo&) const; 221 222 // If all entries are identical, replace them by 1 entry with pc = kMaxUInt32. 223 void RemoveDuplicates(); 224 225 ZoneChunkList<DeoptimizationInfo> deoptimization_info_; 226 227 unsigned offset_; 228 bool emitted_; 229 230 Zone* zone_; 231 232 DISALLOW_COPY_AND_ASSIGN(SafepointTableBuilder); 233 }; 234 235 } // namespace internal 236 } // namespace v8 237 238 #endif // V8_CODEGEN_SAFEPOINT_TABLE_H_ 239