v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
safepoint-table.cc
Go to the documentation of this file.
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
6
7#include <iomanip>
8
14#include "src/utils/ostreams.h"
15
16#if V8_ENABLE_WEBASSEMBLY
18#endif // V8_ENABLE_WEBASSEMBLY
19
20namespace v8 {
21namespace internal {
22
24 : SafepointTable(code->InstructionStart(isolate, pc),
25 code->safepoint_table_address()) {
26 DCHECK(code->is_turbofanned());
27}
28
31 : SafepointTable(code->InstructionStart(isolate, pc),
32 code->safepoint_table_address()) {
33 DCHECK(code->is_turbofanned());
34}
35
36#if V8_ENABLE_WEBASSEMBLY
39 code->instruction_start(),
40 code->instruction_start() + code->safepoint_table_offset()) {}
41#endif // V8_ENABLE_WEBASSEMBLY
42
44 Address safepoint_table_address)
45 : instruction_start_(instruction_start),
46 safepoint_table_address_(safepoint_table_address),
47 stack_slots_(base::Memory<SafepointTableStackSlotsField_t>(
48 safepoint_table_address + kStackSlotsOffset)),
49 length_(base::Memory<int>(safepoint_table_address + kLengthOffset)),
50 entry_configuration_(base::Memory<uint32_t>(safepoint_table_address +
51 kEntryConfigurationOffset)) {}
52
54 for (int i = 0; i < length(); i++) {
55 SafepointEntry entry = GetEntry(i);
56 if (entry.trampoline_pc() == pc_offset || entry.pc() == pc_offset) {
57 return entry.pc();
58 }
59 }
61}
62
64 int pc_offset = static_cast<int>(pc - instruction_start_);
65
66 // Check if the PC is pointing at a trampoline.
67 if (has_deopt_data()) {
68 int candidate = -1;
69 for (int i = 0; i < length_; ++i) {
70 int trampoline_pc = GetEntry(i).trampoline_pc();
71 if (trampoline_pc != -1 && trampoline_pc <= pc_offset) candidate = i;
72 if (trampoline_pc > pc_offset) break;
73 }
74 if (candidate != -1) return GetEntry(candidate);
75 }
76
77 for (int i = 0; i < length_; ++i) {
78 SafepointEntry entry = GetEntry(i);
79 if (i == length_ - 1 || GetEntry(i + 1).pc() > pc_offset) {
80 if (entry.pc() > pc_offset) return {};
81 return entry;
82 }
83 }
84 return {};
85}
86
92
93// static
96 SafepointTable table(isolate, pc, code);
97 return table.FindEntry(pc);
98}
99
100void SafepointTable::Print(std::ostream& os) const {
101 os << "Safepoints (stack slots = " << stack_slots_
102 << ", entries = " << length_ << ", byte size = " << byte_size() << ")\n";
103
104 for (int index = 0; index < length_; index++) {
105 SafepointEntry entry = GetEntry(index);
106 os << reinterpret_cast<const void*>(instruction_start_ + entry.pc()) << " "
107 << std::setw(6) << std::hex << entry.pc() << std::dec;
108
109 if (!entry.tagged_slots().empty()) {
110 os << " slots (sp->fp): ";
111 uint32_t i = 0;
112 for (uint8_t bits : entry.tagged_slots()) {
113 for (int bit = 0; bit < kBitsPerByte && i < stack_slots_; ++bit, ++i) {
114 os << ((bits >> bit) & 1);
115 }
116 }
117 // The tagged slots bitfield ends at the min stack slot (rounded up to the
118 // nearest byte) -- we might have some slots left over in the stack frame
119 // before the fp, so print zeros for those.
120 for (; i < stack_slots_; ++i) {
121 os << 0;
122 }
123 }
124
125 if (entry.tagged_register_indexes() != 0) {
126 os << " registers: ";
127 uint32_t register_bits = entry.tagged_register_indexes();
128 int bits = 32 - base::bits::CountLeadingZeros32(register_bits);
129 for (int j = bits - 1; j >= 0; --j) {
130 os << ((register_bits >> j) & 1);
131 }
132 }
133
134 if (entry.has_deoptimization_index()) {
135 os << " deopt " << std::setw(6) << entry.deoptimization_index()
136 << " trampoline: " << std::setw(6) << std::hex
137 << entry.trampoline_pc();
138 }
139 os << "\n";
140 }
141}
142
144 Assembler* assembler, int pc_offset) {
145 pc_offset = pc_offset ? pc_offset : assembler->pc_offset_for_safepoint();
146 entries_.emplace_back(zone_, pc_offset);
147 return SafepointTableBuilder::Safepoint(&entries_.back(), this);
148}
149
151 int start,
152 int deopt_index) {
155 auto it = entries_.begin() + start;
156 DCHECK(std::any_of(it, entries_.end(),
157 [pc](auto& entry) { return entry.pc == pc; }));
158 int index = start;
159 while (it->pc != pc) ++it, ++index;
160 it->trampoline = trampoline;
161 it->deopt_index = deopt_index;
162 return index;
163}
164
165void SafepointTableBuilder::Emit(Assembler* assembler, int stack_slot_count) {
166 DCHECK_LT(max_stack_index_, stack_slot_count);
167
168#ifdef DEBUG
169 int last_pc = -1;
170 int last_trampoline = -1;
171 for (const EntryBuilder& entry : entries_) {
172 // Entries are ordered by PC.
173 DCHECK_LT(last_pc, entry.pc);
174 last_pc = entry.pc;
175 // Trampoline PCs are increasing, and larger than regular PCs.
176 if (entry.trampoline != SafepointEntry::kNoTrampolinePC) {
177 DCHECK_LT(last_trampoline, entry.trampoline);
178 DCHECK_LT(entries_.back().pc, entry.trampoline);
179 last_trampoline = entry.trampoline;
180 }
181 // An entry either has trampoline and deopt index, or none of the two.
182 DCHECK_EQ(entry.trampoline == SafepointEntry::kNoTrampolinePC,
183 entry.deopt_index == SafepointEntry::kNoDeoptIndex);
184 }
185#endif // DEBUG
186
188
189 // The encoding is compacted by translating stack slot indices s.t. they
190 // start at 0. See also below.
191 int tagged_slots_size = stack_slot_count - min_stack_index();
192
193#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_ARM64
194 // We cannot emit a const pool within the safepoint table.
195 Assembler::BlockConstPoolScope block_const_pool(assembler);
196#endif
197
198 // Make sure the safepoint table is properly aligned. Pad with nops.
200 assembler->RecordComment(";;; Safepoint table.");
201 set_safepoint_table_offset(assembler->pc_offset());
202
203 // Compute the required sizes of the fields.
204 int used_register_indexes = 0;
205 static_assert(SafepointEntry::kNoTrampolinePC == -1);
207 static_assert(SafepointEntry::kNoDeoptIndex == -1);
208 int max_deopt_index = SafepointEntry::kNoDeoptIndex;
209 for (const EntryBuilder& entry : entries_) {
210 used_register_indexes |= entry.register_indexes;
211 max_pc = std::max(max_pc, std::max(entry.pc, entry.trampoline));
212 max_deopt_index = std::max(max_deopt_index, entry.deopt_index);
213 }
214
215 // Derive the bytes and bools for the entry configuration from the values.
216 auto value_to_bytes = [](int value) {
217 DCHECK_LE(0, value);
218 if (value == 0) return 0;
219 if (value <= 0xff) return 1;
220 if (value <= 0xffff) return 2;
221 if (value <= 0xffffff) return 3;
222 return 4;
223 };
224 bool has_deopt_data = max_deopt_index != -1;
225 int register_indexes_size = value_to_bytes(used_register_indexes);
226 // Add 1 so all values (including kNoDeoptIndex and kNoTrampolinePC) are
227 // non-negative.
228 static_assert(SafepointEntry::kNoDeoptIndex == -1);
229 static_assert(SafepointEntry::kNoTrampolinePC == -1);
230 int pc_size = value_to_bytes(max_pc + 1);
231 int deopt_index_size = value_to_bytes(max_deopt_index + 1);
232 int tagged_slots_bytes =
233 (tagged_slots_size + kBitsPerByte - 1) / kBitsPerByte;
234
235 // Add a CHECK to ensure we never overflow the space in the bitfield, even for
236 // huge functions which might not be covered by tests.
238 register_indexes_size));
242
243 uint32_t entry_configuration =
249
250 // Emit the table header.
251 static_assert(SafepointTable::kStackSlotsOffset == 0 * kIntSize);
252 static_assert(SafepointTable::kLengthOffset == 1 * kIntSize);
253 static_assert(SafepointTable::kEntryConfigurationOffset == 2 * kIntSize);
254 static_assert(SafepointTable::kHeaderSize == 3 * kIntSize);
255 int length = static_cast<int>(entries_.size());
256 assembler->dd(stack_slot_count);
257 assembler->dd(length);
258 assembler->dd(entry_configuration);
259
260 auto emit_bytes = [assembler](int value, int bytes) {
261 DCHECK_LE(0, value);
262 for (; bytes > 0; --bytes, value >>= 8) assembler->db(value);
263 DCHECK_EQ(0, value);
264 };
265 // Emit entries, sorted by pc offsets.
266 for (const EntryBuilder& entry : entries_) {
267 emit_bytes(entry.pc, pc_size);
268 if (has_deopt_data) {
269 // Add 1 so all values (including kNoDeoptIndex and kNoTrampolinePC) are
270 // non-negative.
271 static_assert(SafepointEntry::kNoDeoptIndex == -1);
272 static_assert(SafepointEntry::kNoTrampolinePC == -1);
273 emit_bytes(entry.deopt_index + 1, deopt_index_size);
274 emit_bytes(entry.trampoline + 1, pc_size);
275 }
276 emit_bytes(entry.register_indexes, register_indexes_size);
277 }
278
279 // Emit bitmaps of tagged stack slots. Note the slot list is reversed in the
280 // encoding.
281 // TODO(jgruber): Avoid building a reversed copy of the bit vector.
282 ZoneVector<uint8_t> bits(tagged_slots_bytes, 0, zone_);
283 for (const EntryBuilder& entry : entries_) {
284 std::fill(bits.begin(), bits.end(), 0);
285
286 // Run through the indexes and build a bitmap.
287 for (int idx : *entry.stack_indexes) {
288 // The encoding is compacted by translating stack slot indices s.t. they
289 // start at 0. See also above.
290 const int adjusted_idx = idx - min_stack_index();
291 DCHECK_GT(tagged_slots_size, adjusted_idx);
292 int index = tagged_slots_size - 1 - adjusted_idx;
293 int byte_index = index >> kBitsPerByteLog2;
294 int bit_index = index & (kBitsPerByte - 1);
295 bits[byte_index] |= (1u << bit_index);
296 }
297
298 // Emit the bitmap for the current entry.
299 for (uint8_t byte : bits) assembler->db(byte);
300 }
301}
302
304 // Remove any duplicate entries, i.e. succeeding entries that are identical
305 // except for the PC. During lookup, we will find the first entry whose PC is
306 // not larger than the PC at hand, and find the first non-duplicate.
307
308 if (entries_.size() < 2) return;
309
310 auto is_identical_except_for_pc = [](const EntryBuilder& entry1,
311 const EntryBuilder& entry2) {
312 if (entry1.deopt_index != entry2.deopt_index) return false;
313 DCHECK_EQ(entry1.trampoline, entry2.trampoline);
314 return entry1.register_indexes == entry2.register_indexes &&
315 entry1.stack_indexes->Equals(*entry2.stack_indexes);
316 };
317
318 auto remaining_it = entries_.begin();
319 auto end = entries_.end();
320
321 for (auto it = entries_.begin(); it != end; ++remaining_it) {
322 if (remaining_it != it) *remaining_it = *it;
323 // Merge identical entries.
324 do {
325 ++it;
326 } while (it != end && is_identical_except_for_pc(*it, *remaining_it));
327 }
328
329 entries_.erase(remaining_it, end);
330}
331
332} // namespace internal
333} // namespace v8
static constexpr bool is_valid(T value)
Definition bit-field.h:50
static constexpr U encode(T value)
Definition bit-field.h:55
constexpr bool empty() const
Definition vector.h:73
bool Equals(const GrowableBitVector &other) const
Definition bit-vector.h:313
static constexpr int kMetadataAlignment
base::Vector< const uint8_t > tagged_slots() const
uint32_t tagged_register_indexes() const
int UpdateDeoptimizationInfo(int pc, int trampoline, int start, int deopt_index)
ZoneDeque< EntryBuilder > entries_
V8_EXPORT_PRIVATE void Emit(Assembler *assembler, int stack_slot_count)
Safepoint DefineSafepoint(Assembler *assembler, int pc_offset=0)
void Print(std::ostream &) const
SafepointEntry TryFindEntry(Address pc) const
const SafepointTableStackSlotsField_t stack_slots_
SafepointEntry FindEntry(Address pc) const
int find_return_pc(int pc_offset)
SafepointTable(Isolate *isolate, Address pc, Tagged< InstructionStream > code)
SafepointEntry GetEntry(int index) const
int start
int end
AssemblerT assembler
ZoneVector< RpoNumber > & result
int pc_offset
const int length_
Definition mul-fft.cc:473
constexpr unsigned CountLeadingZeros32(uint32_t value)
Definition bits.h:122
constexpr int kIntSize
Definition globals.h:400
constexpr int kBitsPerByte
Definition globals.h:682
uint32_t SafepointTableStackSlotsField_t
return value
Definition map-inl.h:893
constexpr int kBitsPerByteLog2
Definition globals.h:683
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
const uint8_t * instruction_start_