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 
HasRegisters() const18 bool SafepointEntry::HasRegisters() const {
19   DCHECK(is_valid());
20   DCHECK(IsAligned(kNumSafepointRegisters, kBitsPerByte));
21   const int num_reg_bytes = kNumSafepointRegisters >> kBitsPerByteLog2;
22   for (int i = 0; i < num_reg_bytes; i++) {
23     if (bits_[i] != SafepointTable::kNoRegisters) return true;
24   }
25   return false;
26 }
27 
28 
HasRegisterAt(int reg_index) const29 bool SafepointEntry::HasRegisterAt(int reg_index) const {
30   DCHECK(is_valid());
31   DCHECK(reg_index >= 0 && reg_index < kNumSafepointRegisters);
32   int byte_index = reg_index >> kBitsPerByteLog2;
33   int bit_index = reg_index & (kBitsPerByte - 1);
34   return (bits_[byte_index] & (1 << bit_index)) != 0;
35 }
36 
SafepointTable(Address instruction_start,size_t safepoint_table_offset,uint32_t stack_slots,bool has_deopt)37 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       has_deopt_(has_deopt) {
43   Address header = instruction_start_ + safepoint_table_offset;
44   length_ = Memory::uint32_at(header + kLengthOffset);
45   entry_size_ = Memory::uint32_at(header + kEntrySizeOffset);
46   pc_and_deoptimization_indexes_ = header + kHeaderSize;
47   entries_ = pc_and_deoptimization_indexes_ + (length_ * kFixedEntrySize);
48   DCHECK_GT(entry_size_, 0);
49   STATIC_ASSERT(SafepointEntry::DeoptimizationIndexField::kMax ==
50                 Safepoint::kNoDeoptimizationIndex);
51 }
52 
SafepointTable(Code * code)53 SafepointTable::SafepointTable(Code* code)
54     : SafepointTable(code->InstructionStart(), code->safepoint_table_offset(),
55                      code->stack_slots(), true) {}
56 
find_return_pc(unsigned pc_offset)57 unsigned SafepointTable::find_return_pc(unsigned pc_offset) {
58   for (unsigned i = 0; i < length(); i++) {
59     if (GetTrampolinePcOffset(i) == static_cast<int>(pc_offset)) {
60       return GetPcOffset(i);
61     } else if (GetPcOffset(i) == pc_offset) {
62       return pc_offset;
63     }
64   }
65   UNREACHABLE();
66   return 0;
67 }
68 
FindEntry(Address pc) const69 SafepointEntry SafepointTable::FindEntry(Address pc) const {
70   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   // If pc == kMaxUInt32, then this entry covers all call sites in the function.
75   if (len == 1 && GetPcOffset(0) == kMaxUInt32) return GetEntry(0);
76   for (unsigned i = 0; i < len; i++) {
77     // TODO(kasperl): Replace the linear search with binary search.
78     if (GetPcOffset(i) == pc_offset ||
79         (has_deopt_ &&
80          GetTrampolinePcOffset(i) == static_cast<int>(pc_offset))) {
81       return GetEntry(i);
82     }
83   }
84   UNREACHABLE();
85   return SafepointEntry();
86 }
87 
88 
PrintEntry(unsigned index,std::ostream & os) const89 void SafepointTable::PrintEntry(unsigned index,
90                                 std::ostream& os) const {  // NOLINT
91   disasm::NameConverter converter;
92   SafepointEntry entry = GetEntry(index);
93   uint8_t* bits = entry.bits();
94 
95   // Print the stack slot bits.
96   if (entry_size_ > 0) {
97     DCHECK(IsAligned(kNumSafepointRegisters, kBitsPerByte));
98     const int first = kNumSafepointRegisters >> kBitsPerByteLog2;
99     int last = entry_size_ - 1;
100     for (int i = first; i < last; i++) PrintBits(os, bits[i], kBitsPerByte);
101     int last_bits = stack_slots_ - ((last - first) * kBitsPerByte);
102     PrintBits(os, bits[last], last_bits);
103 
104     // Print the registers (if any).
105     if (!entry.HasRegisters()) return;
106     for (int j = 0; j < kNumSafepointRegisters; j++) {
107       if (entry.HasRegisterAt(j)) {
108         os << " | " << converter.NameOfCPURegister(j);
109       }
110     }
111   }
112 }
113 
114 
PrintBits(std::ostream & os,uint8_t byte,int digits)115 void SafepointTable::PrintBits(std::ostream& os,  // NOLINT
116                                uint8_t byte, int digits) {
117   DCHECK(digits >= 0 && digits <= kBitsPerByte);
118   for (int i = 0; i < digits; i++) {
119     os << (((byte & (1 << i)) == 0) ? "0" : "1");
120   }
121 }
122 
123 
DefinePointerRegister(Register reg,Zone * zone)124 void Safepoint::DefinePointerRegister(Register reg, Zone* zone) {
125   registers_->Add(reg.code(), zone);
126 }
127 
128 
DefineSafepoint(Assembler * assembler,Safepoint::Kind kind,int arguments,Safepoint::DeoptMode deopt_mode)129 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_.Add(
136       DeoptimizationInfo(zone_, assembler->pc_offset(), arguments, kind),
137       zone_);
138   if (deopt_mode == Safepoint::kNoLazyDeopt) {
139     last_lazy_safepoint_ = deoptimization_info_.length();
140   }
141   DeoptimizationInfo& new_info = deoptimization_info_.last();
142   return Safepoint(new_info.indexes, new_info.registers);
143 }
144 
145 
RecordLazyDeoptimizationIndex(int index)146 void SafepointTableBuilder::RecordLazyDeoptimizationIndex(int index) {
147   while (last_lazy_safepoint_ < deoptimization_info_.length()) {
148     deoptimization_info_[last_lazy_safepoint_++].deopt_index = index;
149   }
150 }
151 
GetCodeOffset() const152 unsigned SafepointTableBuilder::GetCodeOffset() const {
153   DCHECK(emitted_);
154   return offset_;
155 }
156 
UpdateDeoptimizationInfo(int pc,int trampoline,int start)157 int SafepointTableBuilder::UpdateDeoptimizationInfo(int pc, int trampoline,
158                                                     int start) {
159   int index = -1;
160   for (int i = start; i < deoptimization_info_.length(); i++) {
161     if (static_cast<int>(deoptimization_info_[i].pc) == pc) {
162       index = i;
163       break;
164     }
165   }
166   CHECK_GE(index, 0);
167   DCHECK(index < deoptimization_info_.length());
168   deoptimization_info_[index].trampoline = trampoline;
169   return index;
170 }
171 
Emit(Assembler * assembler,int bits_per_entry)172 void SafepointTableBuilder::Emit(Assembler* assembler, int bits_per_entry) {
173   RemoveDuplicates();
174 
175   // Make sure the safepoint table is properly aligned. Pad with nops.
176   assembler->Align(kIntSize);
177   assembler->RecordComment(";;; Safepoint table.");
178   offset_ = assembler->pc_offset();
179 
180   // Take the register bits into account.
181   bits_per_entry += kNumSafepointRegisters;
182 
183   // Compute the number of bytes per safepoint entry.
184   int bytes_per_entry =
185       RoundUp(bits_per_entry, kBitsPerByte) >> kBitsPerByteLog2;
186 
187   // Emit the table header.
188   int length = deoptimization_info_.length();
189   assembler->dd(length);
190   assembler->dd(bytes_per_entry);
191 
192   // Emit sorted table of pc offsets together with deoptimization indexes.
193   for (int i = 0; i < length; i++) {
194     const DeoptimizationInfo& info = deoptimization_info_[i];
195     assembler->dd(info.pc);
196     assembler->dd(EncodeExceptPC(info));
197     assembler->dd(info.trampoline);
198   }
199 
200   // Emit table of bitmaps.
201   ZoneList<uint8_t> bits(bytes_per_entry, zone_);
202   for (int i = 0; i < length; i++) {
203     ZoneList<int>* indexes = deoptimization_info_[i].indexes;
204     ZoneList<int>* registers = deoptimization_info_[i].registers;
205     bits.Clear();
206     bits.AddBlock(0, bytes_per_entry, zone_);
207 
208     // Run through the registers (if any).
209     DCHECK(IsAligned(kNumSafepointRegisters, kBitsPerByte));
210     if (registers == nullptr) {
211       const int num_reg_bytes = kNumSafepointRegisters >> kBitsPerByteLog2;
212       for (int j = 0; j < num_reg_bytes; j++) {
213         bits[j] = SafepointTable::kNoRegisters;
214       }
215     } else {
216       for (int j = 0; j < registers->length(); j++) {
217         int index = registers->at(j);
218         DCHECK(index >= 0 && index < kNumSafepointRegisters);
219         int byte_index = index >> kBitsPerByteLog2;
220         int bit_index = index & (kBitsPerByte - 1);
221         bits[byte_index] |= (1 << bit_index);
222       }
223     }
224 
225     // Run through the indexes and build a bitmap.
226     for (int j = 0; j < indexes->length(); j++) {
227       int index = bits_per_entry - 1 - indexes->at(j);
228       int byte_index = index >> kBitsPerByteLog2;
229       int bit_index = index & (kBitsPerByte - 1);
230       bits[byte_index] |= (1U << bit_index);
231     }
232 
233     // Emit the bitmap for the current entry.
234     for (int k = 0; k < bytes_per_entry; k++) {
235       assembler->db(bits[k]);
236     }
237   }
238   emitted_ = true;
239 }
240 
EncodeExceptPC(const DeoptimizationInfo & info)241 uint32_t SafepointTableBuilder::EncodeExceptPC(const DeoptimizationInfo& info) {
242   return SafepointEntry::DeoptimizationIndexField::encode(info.deopt_index) |
243          SafepointEntry::ArgumentsField::encode(info.arguments) |
244          SafepointEntry::SaveDoublesField::encode(info.has_doubles);
245 }
246 
RemoveDuplicates()247 void SafepointTableBuilder::RemoveDuplicates() {
248   // If the table contains more than one entry, and all entries are identical
249   // (except for the pc), replace the whole table by a single entry with pc =
250   // kMaxUInt32. This especially compacts the table for wasm code without tagged
251   // pointers and without deoptimization info.
252 
253   int length = deoptimization_info_.length();
254   if (length < 2) return;
255 
256   // Check that all entries (1, length] are identical to entry 0.
257   const DeoptimizationInfo& first_info = deoptimization_info_[0];
258   for (int i = 1; i < length; ++i) {
259     if (!IsIdenticalExceptForPc(first_info, deoptimization_info_[i])) return;
260   }
261 
262   // If we get here, all entries were identical. Rewind the list to just one
263   // entry, and set the pc to kMaxUInt32.
264   deoptimization_info_.Rewind(1);
265   deoptimization_info_[0].pc = kMaxUInt32;
266 }
267 
IsIdenticalExceptForPc(const DeoptimizationInfo & info1,const DeoptimizationInfo & info2) const268 bool SafepointTableBuilder::IsIdenticalExceptForPc(
269     const DeoptimizationInfo& info1, const DeoptimizationInfo& info2) const {
270   if (info1.arguments != info2.arguments) return false;
271   if (info1.has_doubles != info2.has_doubles) return false;
272 
273   if (info1.deopt_index != info2.deopt_index) return false;
274 
275   ZoneList<int>* indexes1 = info1.indexes;
276   ZoneList<int>* indexes2 = info2.indexes;
277   if (indexes1->length() != indexes2->length()) return false;
278   for (int i = 0; i < indexes1->length(); ++i) {
279     if (indexes1->at(i) != indexes2->at(i)) return false;
280   }
281 
282   ZoneList<int>* registers1 = info1.registers;
283   ZoneList<int>* registers2 = info2.registers;
284   if (registers1) {
285     if (!registers2) return false;
286     if (registers1->length() != registers2->length()) return false;
287     for (int i = 0; i < registers1->length(); ++i) {
288       if (registers1->at(i) != registers2->at(i)) return false;
289     }
290   } else if (registers2) {
291     return false;
292   }
293 
294   return true;
295 }
296 
297 }  // namespace internal
298 }  // namespace v8
299