• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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