Line data Source code
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/safepoint-table.h"
6 :
7 : #include "src/assembler-inl.h"
8 : #include "src/deoptimizer.h"
9 : #include "src/disasm.h"
10 : #include "src/frames-inl.h"
11 : #include "src/macro-assembler.h"
12 : #include "src/ostreams.h"
13 :
14 : namespace v8 {
15 : namespace internal {
16 :
17 :
18 1061973 : bool SafepointEntry::HasRegisters() const {
19 : DCHECK(is_valid());
20 : DCHECK(IsAligned(kNumSafepointRegisters, kBitsPerByte));
21 : const int num_reg_bytes = kNumSafepointRegisters >> kBitsPerByteLog2;
22 3185919 : for (int i = 0; i < num_reg_bytes; i++) {
23 2123946 : if (bits_[i] != SafepointTable::kNoRegisters) return true;
24 : }
25 : return false;
26 : }
27 :
28 :
29 0 : bool SafepointEntry::HasRegisterAt(int reg_index) const {
30 : DCHECK(is_valid());
31 : DCHECK(reg_index >= 0 && reg_index < kNumSafepointRegisters);
32 0 : int byte_index = reg_index >> kBitsPerByteLog2;
33 0 : int bit_index = reg_index & (kBitsPerByte - 1);
34 0 : return (bits_[byte_index] & (1 << bit_index)) != 0;
35 : }
36 :
37 12649 : SafepointTable::SafepointTable(Address instruction_start,
38 : size_t safepoint_table_offset,
39 : uint32_t stack_slots, bool has_deopt)
40 : : instruction_start_(instruction_start),
41 : stack_slots_(stack_slots),
42 3035404 : has_deopt_(has_deopt) {
43 3035404 : Address header = instruction_start_ + safepoint_table_offset;
44 3035404 : length_ = Memory<uint32_t>(header + kLengthOffset);
45 6070808 : entry_size_ = Memory<uint32_t>(header + kEntrySizeOffset);
46 3035404 : pc_and_deoptimization_indexes_ = header + kHeaderSize;
47 3035404 : entries_ = pc_and_deoptimization_indexes_ + (length_ * kFixedEntrySize);
48 : DCHECK_GT(entry_size_, 0);
49 : STATIC_ASSERT(SafepointEntry::DeoptimizationIndexOrArgumentsField::kMax ==
50 : Safepoint::kNoDeoptimizationIndex);
51 12649 : }
52 :
53 3022755 : SafepointTable::SafepointTable(Code code)
54 : : SafepointTable(code->InstructionStart(), code->safepoint_table_offset(),
55 6045510 : code->stack_slots(), true) {}
56 :
57 51463 : unsigned SafepointTable::find_return_pc(unsigned pc_offset) {
58 92028 : for (unsigned i = 0; i < length(); i++) {
59 46014 : if (GetTrampolinePcOffset(i) == static_cast<int>(pc_offset)) {
60 5449 : return GetPcOffset(i);
61 40565 : } else if (GetPcOffset(i) == pc_offset) {
62 : return pc_offset;
63 : }
64 : }
65 0 : UNREACHABLE();
66 : return 0;
67 : }
68 :
69 6059910 : SafepointEntry SafepointTable::FindEntry(Address pc) const {
70 3029955 : unsigned pc_offset = static_cast<unsigned>(pc - instruction_start_);
71 : // We use kMaxUInt32 as sentinel value, so check that we don't hit that.
72 : DCHECK_NE(kMaxUInt32, pc_offset);
73 : unsigned len = length();
74 3029955 : CHECK_GT(len, 0);
75 : // If pc == kMaxUInt32, then this entry covers all call sites in the function.
76 3060834 : if (len == 1 && GetPcOffset(0) == kMaxUInt32) return GetEntry(0);
77 28655397 : for (unsigned i = 0; i < len; i++) {
78 : // TODO(kasperl): Replace the linear search with binary search.
79 63348946 : if (GetPcOffset(i) == pc_offset ||
80 28791947 : (has_deopt_ &&
81 28791947 : GetTrampolinePcOffset(i) == static_cast<int>(pc_offset))) {
82 : return GetEntry(i);
83 : }
84 : }
85 0 : UNREACHABLE();
86 : return SafepointEntry();
87 : }
88 :
89 :
90 0 : void SafepointTable::PrintEntry(unsigned index,
91 : std::ostream& os) const { // NOLINT
92 : disasm::NameConverter converter;
93 : SafepointEntry entry = GetEntry(index);
94 : uint8_t* bits = entry.bits();
95 :
96 : // Print the stack slot bits.
97 0 : if (entry_size_ > 0) {
98 : DCHECK(IsAligned(kNumSafepointRegisters, kBitsPerByte));
99 : const int first = kNumSafepointRegisters >> kBitsPerByteLog2;
100 0 : int last = entry_size_ - 1;
101 0 : for (int i = first; i < last; i++) PrintBits(os, bits[i], kBitsPerByte);
102 0 : int last_bits = stack_slots_ - ((last - first) * kBitsPerByte);
103 0 : PrintBits(os, bits[last], last_bits);
104 :
105 : // Print the registers (if any).
106 0 : if (!entry.HasRegisters()) return;
107 0 : for (int j = 0; j < kNumSafepointRegisters; j++) {
108 0 : if (entry.HasRegisterAt(j)) {
109 0 : os << " | " << converter.NameOfCPURegister(j);
110 : }
111 : }
112 : }
113 : }
114 :
115 :
116 0 : void SafepointTable::PrintBits(std::ostream& os, // NOLINT
117 : uint8_t byte, int digits) {
118 : DCHECK(digits >= 0 && digits <= kBitsPerByte);
119 0 : for (int i = 0; i < digits; i++) {
120 0 : os << (((byte & (1 << i)) == 0) ? "0" : "1");
121 : }
122 0 : }
123 :
124 0 : void Safepoint::DefinePointerRegister(Register reg) {
125 0 : registers_->push_back(reg.code());
126 0 : }
127 :
128 :
129 5991333 : Safepoint SafepointTableBuilder::DefineSafepoint(
130 : Assembler* assembler,
131 : Safepoint::Kind kind,
132 : int arguments,
133 : Safepoint::DeoptMode deopt_mode) {
134 : DCHECK_GE(arguments, 0);
135 : deoptimization_info_.push_back(
136 21354439 : DeoptimizationInfo(zone_, assembler->pc_offset(), arguments, kind));
137 5991325 : if (deopt_mode == Safepoint::kNoLazyDeopt) {
138 3380448 : last_lazy_safepoint_ = deoptimization_info_.size();
139 : }
140 : DeoptimizationInfo& new_info = deoptimization_info_.back();
141 5991325 : return Safepoint(new_info.indexes, new_info.registers);
142 : }
143 :
144 2610903 : void SafepointTableBuilder::RecordLazyDeoptimizationIndex(int index) {
145 13054475 : for (auto it = deoptimization_info_.Find(last_lazy_safepoint_);
146 : it != deoptimization_info_.end(); it++, last_lazy_safepoint_++) {
147 2610883 : it->deopt_index = index;
148 : }
149 2610903 : }
150 :
151 3636529 : unsigned SafepointTableBuilder::GetCodeOffset() const {
152 : DCHECK(emitted_);
153 3636529 : return offset_;
154 : }
155 :
156 2348759 : int SafepointTableBuilder::UpdateDeoptimizationInfo(int pc, int trampoline,
157 : int start) {
158 : int index = start;
159 12194467 : for (auto it = deoptimization_info_.Find(start);
160 : it != deoptimization_info_.end(); it++, index++) {
161 4922854 : if (static_cast<int>(it->pc) == pc) {
162 2348759 : it->trampoline = trampoline;
163 2348759 : return index;
164 : }
165 : }
166 0 : UNREACHABLE();
167 : }
168 :
169 3636391 : void SafepointTableBuilder::Emit(Assembler* assembler, int bits_per_entry) {
170 3636391 : RemoveDuplicates();
171 :
172 : // Make sure the safepoint table is properly aligned. Pad with nops.
173 3636432 : assembler->Align(kIntSize);
174 3636421 : assembler->RecordComment(";;; Safepoint table.");
175 3636439 : offset_ = assembler->pc_offset();
176 :
177 : // Take the register bits into account.
178 3636439 : bits_per_entry += kNumSafepointRegisters;
179 :
180 : // Compute the number of bytes per safepoint entry.
181 : int bytes_per_entry =
182 3636439 : RoundUp(bits_per_entry, kBitsPerByte) >> kBitsPerByteLog2;
183 :
184 : // Emit the table header.
185 : STATIC_ASSERT(SafepointTable::kLengthOffset == 0 * kIntSize);
186 : STATIC_ASSERT(SafepointTable::kEntrySizeOffset == 1 * kIntSize);
187 : STATIC_ASSERT(SafepointTable::kHeaderSize == 2 * kIntSize);
188 3636439 : int length = static_cast<int>(deoptimization_info_.size());
189 3636439 : assembler->dd(length);
190 3636355 : assembler->dd(bytes_per_entry);
191 :
192 : // Emit sorted table of pc offsets together with additional info (i.e. the
193 : // deoptimization index or arguments count) and trampoline offsets.
194 : STATIC_ASSERT(SafepointTable::kPcOffset == 0 * kIntSize);
195 : STATIC_ASSERT(SafepointTable::kEncodedInfoOffset == 1 * kIntSize);
196 : STATIC_ASSERT(SafepointTable::kTrampolinePcOffset == 2 * kIntSize);
197 : STATIC_ASSERT(SafepointTable::kFixedEntrySize == 3 * kIntSize);
198 12515202 : for (const DeoptimizationInfo& info : deoptimization_info_) {
199 5241888 : assembler->dd(info.pc);
200 5241865 : assembler->dd(EncodeExceptPC(info));
201 5241937 : assembler->dd(info.trampoline);
202 : }
203 :
204 : // Emit table of bitmaps.
205 3636691 : ZoneVector<uint8_t> bits(bytes_per_entry, 0, zone_);
206 12515212 : for (const DeoptimizationInfo& info : deoptimization_info_) {
207 5241839 : ZoneChunkList<int>* indexes = info.indexes;
208 5241839 : ZoneChunkList<int>* registers = info.registers;
209 : std::fill(bits.begin(), bits.end(), 0);
210 :
211 : // Run through the registers (if any).
212 : DCHECK(IsAligned(kNumSafepointRegisters, kBitsPerByte));
213 5241839 : if (registers == nullptr) {
214 : const int num_reg_bytes = kNumSafepointRegisters >> kBitsPerByteLog2;
215 10483795 : for (int j = 0; j < num_reg_bytes; j++) {
216 20967590 : bits[j] = SafepointTable::kNoRegisters;
217 : }
218 : } else {
219 0 : for (int index : *registers) {
220 : DCHECK(index >= 0 && index < kNumSafepointRegisters);
221 0 : int byte_index = index >> kBitsPerByteLog2;
222 0 : int bit_index = index & (kBitsPerByte - 1);
223 0 : bits[byte_index] |= (1 << bit_index);
224 : }
225 : }
226 :
227 : // Run through the indexes and build a bitmap.
228 37801636 : for (int idx : *indexes) {
229 13658979 : int index = bits_per_entry - 1 - idx;
230 13658979 : int byte_index = index >> kBitsPerByteLog2;
231 13658979 : int bit_index = index & (kBitsPerByte - 1);
232 27317958 : bits[byte_index] |= (1U << bit_index);
233 : }
234 :
235 : // Emit the bitmap for the current entry.
236 19496397 : for (int k = 0; k < bytes_per_entry; k++) {
237 38992284 : assembler->db(bits[k]);
238 : }
239 : }
240 3636814 : emitted_ = true;
241 3636814 : }
242 :
243 0 : uint32_t SafepointTableBuilder::EncodeExceptPC(const DeoptimizationInfo& info) {
244 5241865 : bool has_argc = info.arguments != 0;
245 5241865 : int argc = has_argc ? info.arguments : info.deopt_index;
246 : DCHECK(info.deopt_index == Safepoint::kNoDeoptimizationIndex || !has_argc);
247 5241865 : return SafepointEntry::DeoptimizationIndexOrArgumentsField::encode(argc) |
248 : SafepointEntry::HasArgumentsField::encode(has_argc) |
249 10483730 : SafepointEntry::SaveDoublesField::encode(info.has_doubles);
250 : }
251 :
252 3636249 : void SafepointTableBuilder::RemoveDuplicates() {
253 : // If the table contains more than one entry, and all entries are identical
254 : // (except for the pc), replace the whole table by a single entry with pc =
255 : // kMaxUInt32. This especially compacts the table for wasm code without tagged
256 : // pointers and without deoptimization info.
257 :
258 3831080 : if (deoptimization_info_.size() < 2) return;
259 :
260 : // Check that all entries (1, size] are identical to entry 0.
261 635617 : const DeoptimizationInfo& first_info = deoptimization_info_.front();
262 1839315 : for (auto it = deoptimization_info_.Find(1); it != deoptimization_info_.end();
263 : it++) {
264 1008880 : if (!IsIdenticalExceptForPc(first_info, *it)) return;
265 : }
266 :
267 : // If we get here, all entries were identical. Rewind the list to just one
268 : // entry, and set the pc to kMaxUInt32.
269 194818 : deoptimization_info_.Rewind(1);
270 194831 : deoptimization_info_.front().pc = kMaxUInt32;
271 : }
272 :
273 1008917 : bool SafepointTableBuilder::IsIdenticalExceptForPc(
274 : const DeoptimizationInfo& info1, const DeoptimizationInfo& info2) const {
275 1008917 : if (info1.arguments != info2.arguments) return false;
276 1008966 : if (info1.has_doubles != info2.has_doubles) return false;
277 :
278 1008990 : if (info1.deopt_index != info2.deopt_index) return false;
279 :
280 1358702 : ZoneChunkList<int>* indexes1 = info1.indexes;
281 1358702 : ZoneChunkList<int>* indexes2 = info2.indexes;
282 679351 : if (indexes1->size() != indexes2->size()) return false;
283 572515 : if (!std::equal(indexes1->begin(), indexes1->end(), indexes2->begin())) {
284 : return false;
285 : }
286 :
287 568082 : ZoneChunkList<int>* registers1 = info1.registers;
288 568082 : ZoneChunkList<int>* registers2 = info2.registers;
289 568082 : if (registers1) {
290 0 : if (!registers2) return false;
291 0 : if (registers1->size() != registers2->size()) return false;
292 0 : if (!std::equal(registers1->begin(), registers1->end(),
293 0 : registers2->begin())) {
294 : return false;
295 : }
296 568082 : } else if (registers2) {
297 : return false;
298 : }
299 :
300 : return true;
301 : }
302 :
303 : } // namespace internal
304 183867 : } // namespace v8
|