• 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 #include "src/codegen/safepoint-table.h"
6 
7 #include <iomanip>
8 
9 #include "src/codegen/assembler-inl.h"
10 #include "src/codegen/macro-assembler.h"
11 #include "src/deoptimizer/deoptimizer.h"
12 #include "src/diagnostics/disasm.h"
13 #include "src/execution/frames-inl.h"
14 #include "src/utils/ostreams.h"
15 
16 #if V8_ENABLE_WEBASSEMBLY
17 #include "src/wasm/wasm-code-manager.h"
18 #endif  // V8_ENABLE_WEBASSEMBLY
19 
20 namespace v8 {
21 namespace internal {
22 
SafepointTable(Isolate * isolate,Address pc,Code code)23 SafepointTable::SafepointTable(Isolate* isolate, Address pc, Code code)
24     : SafepointTable(code.InstructionStart(isolate, pc),
25                      code.SafepointTableAddress()) {}
26 
27 #if V8_ENABLE_WEBASSEMBLY
SafepointTable(const wasm::WasmCode * code)28 SafepointTable::SafepointTable(const wasm::WasmCode* code)
29     : SafepointTable(
30           code->instruction_start(),
31           code->instruction_start() + code->safepoint_table_offset()) {}
32 #endif  // V8_ENABLE_WEBASSEMBLY
33 
SafepointTable(Address instruction_start,Address safepoint_table_address)34 SafepointTable::SafepointTable(Address instruction_start,
35                                Address safepoint_table_address)
36     : instruction_start_(instruction_start),
37       safepoint_table_address_(safepoint_table_address),
38       length_(base::Memory<int>(safepoint_table_address + kLengthOffset)),
39       entry_configuration_(base::Memory<uint32_t>(safepoint_table_address +
40                                                   kEntryConfigurationOffset)) {}
41 
find_return_pc(int pc_offset)42 int SafepointTable::find_return_pc(int pc_offset) {
43   for (int i = 0; i < length(); i++) {
44     SafepointEntry entry = GetEntry(i);
45     if (entry.trampoline_pc() == pc_offset || entry.pc() == pc_offset) {
46       return entry.pc();
47     }
48   }
49   UNREACHABLE();
50 }
51 
FindEntry(Address pc) const52 SafepointEntry SafepointTable::FindEntry(Address pc) const {
53   int pc_offset = static_cast<int>(pc - instruction_start_);
54 
55   // Check if the PC is pointing at a trampoline.
56   if (has_deopt_data()) {
57     int candidate = -1;
58     for (int i = 0; i < length_; ++i) {
59       int trampoline_pc = GetEntry(i).trampoline_pc();
60       if (trampoline_pc != -1 && trampoline_pc <= pc_offset) candidate = i;
61       if (trampoline_pc > pc_offset) break;
62     }
63     if (candidate != -1) return GetEntry(candidate);
64   }
65 
66   for (int i = 0; i < length_; ++i) {
67     SafepointEntry entry = GetEntry(i);
68     if (i == length_ - 1 || GetEntry(i + 1).pc() > pc_offset) {
69       DCHECK_LE(entry.pc(), pc_offset);
70       return entry;
71     }
72   }
73   UNREACHABLE();
74 }
75 
Print(std::ostream & os) const76 void SafepointTable::Print(std::ostream& os) const {
77   os << "Safepoints (entries = " << length_ << ", byte size = " << byte_size()
78      << ")\n";
79 
80   for (int index = 0; index < length_; index++) {
81     SafepointEntry entry = GetEntry(index);
82     os << reinterpret_cast<const void*>(instruction_start_ + entry.pc()) << " "
83        << std::setw(6) << std::hex << entry.pc() << std::dec;
84 
85     if (!entry.tagged_slots().empty()) {
86       os << "  slots (sp->fp): ";
87       for (uint8_t bits : entry.tagged_slots()) {
88         for (int bit = 0; bit < kBitsPerByte; ++bit) {
89           os << ((bits >> bit) & 1);
90         }
91       }
92     }
93 
94     if (entry.tagged_register_indexes() != 0) {
95       os << "  registers: ";
96       uint32_t register_bits = entry.tagged_register_indexes();
97       int bits = 32 - base::bits::CountLeadingZeros32(register_bits);
98       for (int j = bits - 1; j >= 0; --j) {
99         os << ((register_bits >> j) & 1);
100       }
101     }
102 
103     if (entry.has_deoptimization_index()) {
104       os << "  deopt " << std::setw(6) << entry.deoptimization_index()
105          << " trampoline: " << std::setw(6) << std::hex
106          << entry.trampoline_pc();
107     }
108     os << "\n";
109   }
110 }
111 
DefineSafepoint(Assembler * assembler)112 SafepointTableBuilder::Safepoint SafepointTableBuilder::DefineSafepoint(
113     Assembler* assembler) {
114   entries_.push_back(EntryBuilder(zone_, assembler->pc_offset_for_safepoint()));
115   return SafepointTableBuilder::Safepoint(&entries_.back(), this);
116 }
117 
UpdateDeoptimizationInfo(int pc,int trampoline,int start,int deopt_index)118 int SafepointTableBuilder::UpdateDeoptimizationInfo(int pc, int trampoline,
119                                                     int start,
120                                                     int deopt_index) {
121   DCHECK_NE(SafepointEntry::kNoTrampolinePC, trampoline);
122   DCHECK_NE(SafepointEntry::kNoDeoptIndex, deopt_index);
123   auto it = entries_.Find(start);
124   DCHECK(std::any_of(it, entries_.end(),
125                      [pc](auto& entry) { return entry.pc == pc; }));
126   int index = start;
127   while (it->pc != pc) ++it, ++index;
128   it->trampoline = trampoline;
129   it->deopt_index = deopt_index;
130   return index;
131 }
132 
Emit(Assembler * assembler,int tagged_slots_size)133 void SafepointTableBuilder::Emit(Assembler* assembler, int tagged_slots_size) {
134   DCHECK_LT(max_stack_index_, tagged_slots_size);
135 
136 #ifdef DEBUG
137   int last_pc = -1;
138   int last_trampoline = -1;
139   for (const EntryBuilder& entry : entries_) {
140     // Entries are ordered by PC.
141     DCHECK_LT(last_pc, entry.pc);
142     last_pc = entry.pc;
143     // Trampoline PCs are increasing, and larger than regular PCs.
144     if (entry.trampoline != SafepointEntry::kNoTrampolinePC) {
145       DCHECK_LT(last_trampoline, entry.trampoline);
146       DCHECK_LT(entries_.back().pc, entry.trampoline);
147       last_trampoline = entry.trampoline;
148     }
149     // An entry either has trampoline and deopt index, or none of the two.
150     DCHECK_EQ(entry.trampoline == SafepointEntry::kNoTrampolinePC,
151               entry.deopt_index == SafepointEntry::kNoDeoptIndex);
152   }
153 #endif  // DEBUG
154 
155   RemoveDuplicates();
156 
157   // The encoding is compacted by translating stack slot indices s.t. they
158   // start at 0. See also below.
159   tagged_slots_size -= min_stack_index();
160 
161 #if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_ARM64
162   // We cannot emit a const pool within the safepoint table.
163   Assembler::BlockConstPoolScope block_const_pool(assembler);
164 #endif
165 
166   // Make sure the safepoint table is properly aligned. Pad with nops.
167   assembler->Align(Code::kMetadataAlignment);
168   assembler->RecordComment(";;; Safepoint table.");
169   safepoint_table_offset_ = assembler->pc_offset();
170 
171   // Compute the required sizes of the fields.
172   int used_register_indexes = 0;
173   STATIC_ASSERT(SafepointEntry::kNoTrampolinePC == -1);
174   int max_pc = SafepointEntry::kNoTrampolinePC;
175   STATIC_ASSERT(SafepointEntry::kNoDeoptIndex == -1);
176   int max_deopt_index = SafepointEntry::kNoDeoptIndex;
177   for (const EntryBuilder& entry : entries_) {
178     used_register_indexes |= entry.register_indexes;
179     max_pc = std::max(max_pc, std::max(entry.pc, entry.trampoline));
180     max_deopt_index = std::max(max_deopt_index, entry.deopt_index);
181   }
182 
183   // Derive the bytes and bools for the entry configuration from the values.
184   auto value_to_bytes = [](int value) {
185     DCHECK_LE(0, value);
186     if (value == 0) return 0;
187     if (value <= 0xff) return 1;
188     if (value <= 0xffff) return 2;
189     if (value <= 0xffffff) return 3;
190     return 4;
191   };
192   bool has_deopt_data = max_deopt_index != -1;
193   int register_indexes_size = value_to_bytes(used_register_indexes);
194   // Add 1 so all values (including kNoDeoptIndex and kNoTrampolinePC) are
195   // non-negative.
196   STATIC_ASSERT(SafepointEntry::kNoDeoptIndex == -1);
197   STATIC_ASSERT(SafepointEntry::kNoTrampolinePC == -1);
198   int pc_size = value_to_bytes(max_pc + 1);
199   int deopt_index_size = value_to_bytes(max_deopt_index + 1);
200   int tagged_slots_bytes =
201       (tagged_slots_size + kBitsPerByte - 1) / kBitsPerByte;
202 
203   // Add a CHECK to ensure we never overflow the space in the bitfield, even for
204   // huge functions which might not be covered by tests.
205   CHECK(SafepointTable::RegisterIndexesSizeField::is_valid(
206             register_indexes_size) &&
207         SafepointTable::PcSizeField::is_valid(pc_size) &&
208         SafepointTable::DeoptIndexSizeField::is_valid(deopt_index_size) &&
209         SafepointTable::TaggedSlotsBytesField::is_valid(tagged_slots_bytes));
210 
211   uint32_t entry_configuration =
212       SafepointTable::HasDeoptDataField::encode(has_deopt_data) |
213       SafepointTable::RegisterIndexesSizeField::encode(register_indexes_size) |
214       SafepointTable::PcSizeField::encode(pc_size) |
215       SafepointTable::DeoptIndexSizeField::encode(deopt_index_size) |
216       SafepointTable::TaggedSlotsBytesField::encode(tagged_slots_bytes);
217 
218   // Emit the table header.
219   STATIC_ASSERT(SafepointTable::kLengthOffset == 0 * kIntSize);
220   STATIC_ASSERT(SafepointTable::kEntryConfigurationOffset == 1 * kIntSize);
221   STATIC_ASSERT(SafepointTable::kHeaderSize == 2 * kIntSize);
222   int length = static_cast<int>(entries_.size());
223   assembler->dd(length);
224   assembler->dd(entry_configuration);
225 
226   auto emit_bytes = [assembler](int value, int bytes) {
227     DCHECK_LE(0, value);
228     for (; bytes > 0; --bytes, value >>= 8) assembler->db(value);
229     DCHECK_EQ(0, value);
230   };
231   // Emit entries, sorted by pc offsets.
232   for (const EntryBuilder& entry : entries_) {
233     emit_bytes(entry.pc, pc_size);
234     if (has_deopt_data) {
235       // Add 1 so all values (including kNoDeoptIndex and kNoTrampolinePC) are
236       // non-negative.
237       STATIC_ASSERT(SafepointEntry::kNoDeoptIndex == -1);
238       STATIC_ASSERT(SafepointEntry::kNoTrampolinePC == -1);
239       emit_bytes(entry.deopt_index + 1, deopt_index_size);
240       emit_bytes(entry.trampoline + 1, pc_size);
241     }
242     emit_bytes(entry.register_indexes, register_indexes_size);
243   }
244 
245   // Emit bitmaps of tagged stack slots. Note the slot list is reversed in the
246   // encoding.
247   // TODO(jgruber): Avoid building a reversed copy of the bit vector.
248   ZoneVector<uint8_t> bits(tagged_slots_bytes, 0, zone_);
249   for (const EntryBuilder& entry : entries_) {
250     std::fill(bits.begin(), bits.end(), 0);
251 
252     // Run through the indexes and build a bitmap.
253     for (int idx : *entry.stack_indexes) {
254       // The encoding is compacted by translating stack slot indices s.t. they
255       // start at 0. See also above.
256       const int adjusted_idx = idx - min_stack_index();
257       DCHECK_GT(tagged_slots_size, adjusted_idx);
258       int index = tagged_slots_size - 1 - adjusted_idx;
259       int byte_index = index >> kBitsPerByteLog2;
260       int bit_index = index & (kBitsPerByte - 1);
261       bits[byte_index] |= (1u << bit_index);
262     }
263 
264     // Emit the bitmap for the current entry.
265     for (uint8_t byte : bits) assembler->db(byte);
266   }
267 }
268 
RemoveDuplicates()269 void SafepointTableBuilder::RemoveDuplicates() {
270   // Remove any duplicate entries, i.e. succeeding entries that are identical
271   // except for the PC. During lookup, we will find the first entry whose PC is
272   // not larger than the PC at hand, and find the first non-duplicate.
273 
274   if (entries_.size() < 2) return;
275 
276   auto is_identical_except_for_pc = [](const EntryBuilder& entry1,
277                                        const EntryBuilder& entry2) {
278     if (entry1.deopt_index != entry2.deopt_index) return false;
279     DCHECK_EQ(entry1.trampoline, entry2.trampoline);
280     return entry1.register_indexes == entry2.register_indexes &&
281            entry1.stack_indexes->Equals(*entry2.stack_indexes);
282   };
283 
284   auto remaining_it = entries_.begin();
285   size_t remaining = 0;
286 
287   for (auto it = entries_.begin(), end = entries_.end(); it != end;
288        ++remaining_it, ++remaining) {
289     if (remaining_it != it) *remaining_it = *it;
290     // Merge identical entries.
291     do {
292       ++it;
293     } while (it != end && is_identical_except_for_pc(*it, *remaining_it));
294   }
295 
296   entries_.Rewind(remaining);
297 }
298 
299 }  // namespace internal
300 }  // namespace v8
301