| |
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| |
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
| |
3 * This Source Code Form is subject to the terms of the Mozilla Public |
| |
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
| |
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
6 |
| |
7 #include "jit/Safepoints.h" |
| |
8 |
| |
9 #include "mozilla/MathAlgorithms.h" |
| |
10 |
| |
11 #include "jit/BitSet.h" |
| |
12 #include "jit/IonSpewer.h" |
| |
13 #include "jit/LIR.h" |
| |
14 |
| |
15 using namespace js; |
| |
16 using namespace jit; |
| |
17 |
| |
18 using mozilla::FloorLog2; |
| |
19 |
| |
20 bool |
| |
21 SafepointWriter::init(TempAllocator &alloc, uint32_t slotCount) |
| |
22 { |
| |
23 frameSlots_ = BitSet::New(alloc, slotCount / sizeof(intptr_t)); |
| |
24 if (!frameSlots_) |
| |
25 return false; |
| |
26 |
| |
27 return true; |
| |
28 } |
| |
29 |
| |
30 uint32_t |
| |
31 SafepointWriter::startEntry() |
| |
32 { |
| |
33 IonSpew(IonSpew_Safepoints, "Encoding safepoint (position %d):", stream_.length()); |
| |
34 return uint32_t(stream_.length()); |
| |
35 } |
| |
36 |
| |
37 void |
| |
38 SafepointWriter::writeOsiCallPointOffset(uint32_t osiCallPointOffset) |
| |
39 { |
| |
40 stream_.writeUnsigned(osiCallPointOffset); |
| |
41 } |
| |
42 |
| |
43 static void |
| |
44 WriteRegisterMask(CompactBufferWriter &stream, uint32_t bits) |
| |
45 { |
| |
46 if (sizeof(PackedRegisterMask) == 1) |
| |
47 stream.writeByte(bits); |
| |
48 else |
| |
49 stream.writeUnsigned(bits); |
| |
50 } |
| |
51 |
| |
52 static int32_t |
| |
53 ReadRegisterMask(CompactBufferReader &stream) |
| |
54 { |
| |
55 if (sizeof(PackedRegisterMask) == 1) |
| |
56 return stream.readByte(); |
| |
57 return stream.readUnsigned(); |
| |
58 } |
| |
59 |
| |
60 void |
| |
61 SafepointWriter::writeGcRegs(LSafepoint *safepoint) |
| |
62 { |
| |
63 GeneralRegisterSet gc = safepoint->gcRegs(); |
| |
64 GeneralRegisterSet spilledGpr = safepoint->liveRegs().gprs(); |
| |
65 FloatRegisterSet spilledFloat = safepoint->liveRegs().fpus(); |
| |
66 GeneralRegisterSet slots = safepoint->slotsOrElementsRegs(); |
| |
67 GeneralRegisterSet valueRegs; |
| |
68 |
| |
69 WriteRegisterMask(stream_, spilledGpr.bits()); |
| |
70 if (!spilledGpr.empty()) { |
| |
71 WriteRegisterMask(stream_, gc.bits()); |
| |
72 WriteRegisterMask(stream_, slots.bits()); |
| |
73 |
| |
74 #ifdef JS_PUNBOX64 |
| |
75 valueRegs = safepoint->valueRegs(); |
| |
76 WriteRegisterMask(stream_, valueRegs.bits()); |
| |
77 #endif |
| |
78 } |
| |
79 |
| |
80 // GC registers are a subset of the spilled registers. |
| |
81 JS_ASSERT((valueRegs.bits() & ~spilledGpr.bits()) == 0); |
| |
82 JS_ASSERT((gc.bits() & ~spilledGpr.bits()) == 0); |
| |
83 |
| |
84 WriteRegisterMask(stream_, spilledFloat.bits()); |
| |
85 |
| |
86 #ifdef DEBUG |
| |
87 if (IonSpewEnabled(IonSpew_Safepoints)) { |
| |
88 for (GeneralRegisterForwardIterator iter(spilledGpr); iter.more(); iter++) { |
| |
89 const char *type = gc.has(*iter) |
| |
90 ? "gc" |
| |
91 : slots.has(*iter) |
| |
92 ? "slots" |
| |
93 : valueRegs.has(*iter) |
| |
94 ? "value" |
| |
95 : "any"; |
| |
96 IonSpew(IonSpew_Safepoints, " %s reg: %s", type, (*iter).name()); |
| |
97 } |
| |
98 for (FloatRegisterForwardIterator iter(spilledFloat); iter.more(); iter++) |
| |
99 IonSpew(IonSpew_Safepoints, " float reg: %s", (*iter).name()); |
| |
100 } |
| |
101 #endif |
| |
102 } |
| |
103 |
| |
104 static void |
| |
105 MapSlotsToBitset(BitSet *set, CompactBufferWriter &stream, uint32_t nslots, uint32_t *slots) |
| |
106 { |
| |
107 set->clear(); |
| |
108 |
| |
109 for (uint32_t i = 0; i < nslots; i++) { |
| |
110 // Slots are represented at a distance from |fp|. We divide by the |
| |
111 // pointer size, since we only care about pointer-sized/aligned slots |
| |
112 // here. Since the stack grows down, this means slots start at index 1, |
| |
113 // so we subtract 1 to pack the bitset. |
| |
114 JS_ASSERT(slots[i] % sizeof(intptr_t) == 0); |
| |
115 JS_ASSERT(slots[i] / sizeof(intptr_t) > 0); |
| |
116 set->insert(slots[i] / sizeof(intptr_t) - 1); |
| |
117 } |
| |
118 |
| |
119 size_t count = set->rawLength(); |
| |
120 const uint32_t *words = set->raw(); |
| |
121 for (size_t i = 0; i < count; i++) |
| |
122 stream.writeUnsigned(words[i]); |
| |
123 } |
| |
124 |
| |
125 void |
| |
126 SafepointWriter::writeGcSlots(LSafepoint *safepoint) |
| |
127 { |
| |
128 LSafepoint::SlotList &slots = safepoint->gcSlots(); |
| |
129 |
| |
130 #ifdef DEBUG |
| |
131 for (uint32_t i = 0; i < slots.length(); i++) |
| |
132 IonSpew(IonSpew_Safepoints, " gc slot: %d", slots[i]); |
| |
133 #endif |
| |
134 |
| |
135 MapSlotsToBitset(frameSlots_, |
| |
136 stream_, |
| |
137 slots.length(), |
| |
138 slots.begin()); |
| |
139 } |
| |
140 |
| |
141 void |
| |
142 SafepointWriter::writeSlotsOrElementsSlots(LSafepoint *safepoint) |
| |
143 { |
| |
144 LSafepoint::SlotList &slots = safepoint->slotsOrElementsSlots(); |
| |
145 |
| |
146 stream_.writeUnsigned(slots.length()); |
| |
147 |
| |
148 for (uint32_t i = 0; i < slots.length(); i++) { |
| |
149 #ifdef DEBUG |
| |
150 IonSpew(IonSpew_Safepoints, " slots/elements slot: %d", slots[i]); |
| |
151 #endif |
| |
152 stream_.writeUnsigned(slots[i]); |
| |
153 } |
| |
154 } |
| |
155 |
| |
156 void |
| |
157 SafepointWriter::writeValueSlots(LSafepoint *safepoint) |
| |
158 { |
| |
159 LSafepoint::SlotList &slots = safepoint->valueSlots(); |
| |
160 |
| |
161 #ifdef DEBUG |
| |
162 for (uint32_t i = 0; i < slots.length(); i++) |
| |
163 IonSpew(IonSpew_Safepoints, " gc value: %d", slots[i]); |
| |
164 #endif |
| |
165 |
| |
166 MapSlotsToBitset(frameSlots_, stream_, slots.length(), slots.begin()); |
| |
167 } |
| |
168 |
| |
169 #if defined(DEBUG) && defined(JS_NUNBOX32) |
| |
170 static void |
| |
171 DumpNunboxPart(const LAllocation &a) |
| |
172 { |
| |
173 if (a.isStackSlot()) { |
| |
174 fprintf(IonSpewFile, "stack %d", a.toStackSlot()->slot()); |
| |
175 } else if (a.isArgument()) { |
| |
176 fprintf(IonSpewFile, "arg %d", a.toArgument()->index()); |
| |
177 } else { |
| |
178 fprintf(IonSpewFile, "reg %s", a.toGeneralReg()->reg().name()); |
| |
179 } |
| |
180 } |
| |
181 #endif // DEBUG |
| |
182 |
| |
183 // Nunbox part encoding: |
| |
184 // |
| |
185 // Reg = 000 |
| |
186 // Stack = 001 |
| |
187 // Arg = 010 |
| |
188 // |
| |
189 // [vwu] nentries: |
| |
190 // uint16_t: tttp ppXX XXXY YYYY |
| |
191 // |
| |
192 // If ttt = Reg, type is reg XXXXX |
| |
193 // If ppp = Reg, payload is reg YYYYY |
| |
194 // |
| |
195 // If ttt != Reg, type is: |
| |
196 // XXXXX if not 11111, otherwise followed by [vwu] |
| |
197 // If ppp != Reg, payload is: |
| |
198 // YYYYY if not 11111, otherwise followed by [vwu] |
| |
199 // |
| |
200 enum NunboxPartKind { |
| |
201 Part_Reg, |
| |
202 Part_Stack, |
| |
203 Part_Arg |
| |
204 }; |
| |
205 |
| |
206 static const uint32_t PART_KIND_BITS = 3; |
| |
207 static const uint32_t PART_KIND_MASK = (1 << PART_KIND_BITS) - 1; |
| |
208 static const uint32_t PART_INFO_BITS = 5; |
| |
209 static const uint32_t PART_INFO_MASK = (1 << PART_INFO_BITS) - 1; |
| |
210 |
| |
211 static const uint32_t MAX_INFO_VALUE = (1 << PART_INFO_BITS) - 1; |
| |
212 static const uint32_t TYPE_KIND_SHIFT = 16 - PART_KIND_BITS; |
| |
213 static const uint32_t PAYLOAD_KIND_SHIFT = TYPE_KIND_SHIFT - PART_KIND_BITS; |
| |
214 static const uint32_t TYPE_INFO_SHIFT = PAYLOAD_KIND_SHIFT - PART_INFO_BITS; |
| |
215 static const uint32_t PAYLOAD_INFO_SHIFT = TYPE_INFO_SHIFT - PART_INFO_BITS; |
| |
216 |
| |
217 JS_STATIC_ASSERT(PAYLOAD_INFO_SHIFT == 0); |
| |
218 |
| |
219 #ifdef JS_NUNBOX32 |
| |
220 static inline NunboxPartKind |
| |
221 AllocationToPartKind(const LAllocation &a) |
| |
222 { |
| |
223 if (a.isRegister()) |
| |
224 return Part_Reg; |
| |
225 if (a.isStackSlot()) |
| |
226 return Part_Stack; |
| |
227 JS_ASSERT(a.isArgument()); |
| |
228 return Part_Arg; |
| |
229 } |
| |
230 |
| |
231 // gcc 4.5 doesn't actually inline CanEncodeInfoInHeader when only |
| |
232 // using the "inline" keyword, and miscompiles the function as well |
| |
233 // when doing block reordering with branch prediction information. |
| |
234 // See bug 799295 comment 71. |
| |
235 static MOZ_ALWAYS_INLINE bool |
| |
236 CanEncodeInfoInHeader(const LAllocation &a, uint32_t *out) |
| |
237 { |
| |
238 if (a.isGeneralReg()) { |
| |
239 *out = a.toGeneralReg()->reg().code(); |
| |
240 return true; |
| |
241 } |
| |
242 |
| |
243 if (a.isStackSlot()) |
| |
244 *out = a.toStackSlot()->slot(); |
| |
245 else |
| |
246 *out = a.toArgument()->index(); |
| |
247 |
| |
248 return *out < MAX_INFO_VALUE; |
| |
249 } |
| |
250 |
| |
251 void |
| |
252 SafepointWriter::writeNunboxParts(LSafepoint *safepoint) |
| |
253 { |
| |
254 LSafepoint::NunboxList &entries = safepoint->nunboxParts(); |
| |
255 |
| |
256 # ifdef DEBUG |
| |
257 if (IonSpewEnabled(IonSpew_Safepoints)) { |
| |
258 for (uint32_t i = 0; i < entries.length(); i++) { |
| |
259 SafepointNunboxEntry &entry = entries[i]; |
| |
260 if (entry.type.isUse() || entry.payload.isUse()) |
| |
261 continue; |
| |
262 IonSpewHeader(IonSpew_Safepoints); |
| |
263 fprintf(IonSpewFile, " nunbox (type in "); |
| |
264 DumpNunboxPart(entry.type); |
| |
265 fprintf(IonSpewFile, ", payload in "); |
| |
266 DumpNunboxPart(entry.payload); |
| |
267 fprintf(IonSpewFile, ")\n"); |
| |
268 } |
| |
269 } |
| |
270 # endif |
| |
271 |
| |
272 // Safepoints are permitted to have partially filled in entries for nunboxes, |
| |
273 // provided that only the type is live and not the payload. Omit these from |
| |
274 // the written safepoint. |
| |
275 uint32_t partials = safepoint->partialNunboxes(); |
| |
276 |
| |
277 stream_.writeUnsigned(entries.length() - partials); |
| |
278 |
| |
279 for (size_t i = 0; i < entries.length(); i++) { |
| |
280 SafepointNunboxEntry &entry = entries[i]; |
| |
281 |
| |
282 if (entry.type.isUse() || entry.payload.isUse()) { |
| |
283 partials--; |
| |
284 continue; |
| |
285 } |
| |
286 |
| |
287 uint16_t header = 0; |
| |
288 |
| |
289 header |= (AllocationToPartKind(entry.type) << TYPE_KIND_SHIFT); |
| |
290 header |= (AllocationToPartKind(entry.payload) << PAYLOAD_KIND_SHIFT); |
| |
291 |
| |
292 uint32_t typeVal; |
| |
293 bool typeExtra = !CanEncodeInfoInHeader(entry.type, &typeVal); |
| |
294 if (!typeExtra) |
| |
295 header |= (typeVal << TYPE_INFO_SHIFT); |
| |
296 else |
| |
297 header |= (MAX_INFO_VALUE << TYPE_INFO_SHIFT); |
| |
298 |
| |
299 uint32_t payloadVal; |
| |
300 bool payloadExtra = !CanEncodeInfoInHeader(entry.payload, &payloadVal); |
| |
301 if (!payloadExtra) |
| |
302 header |= (payloadVal << PAYLOAD_INFO_SHIFT); |
| |
303 else |
| |
304 header |= (MAX_INFO_VALUE << PAYLOAD_INFO_SHIFT); |
| |
305 |
| |
306 stream_.writeFixedUint16_t(header); |
| |
307 if (typeExtra) |
| |
308 stream_.writeUnsigned(typeVal); |
| |
309 if (payloadExtra) |
| |
310 stream_.writeUnsigned(payloadVal); |
| |
311 } |
| |
312 |
| |
313 JS_ASSERT(partials == 0); |
| |
314 } |
| |
315 #endif |
| |
316 |
| |
317 void |
| |
318 SafepointWriter::encode(LSafepoint *safepoint) |
| |
319 { |
| |
320 uint32_t safepointOffset = startEntry(); |
| |
321 |
| |
322 JS_ASSERT(safepoint->osiCallPointOffset()); |
| |
323 |
| |
324 writeOsiCallPointOffset(safepoint->osiCallPointOffset()); |
| |
325 writeGcRegs(safepoint); |
| |
326 writeGcSlots(safepoint); |
| |
327 writeValueSlots(safepoint); |
| |
328 |
| |
329 #ifdef JS_NUNBOX32 |
| |
330 writeNunboxParts(safepoint); |
| |
331 #endif |
| |
332 |
| |
333 writeSlotsOrElementsSlots(safepoint); |
| |
334 |
| |
335 endEntry(); |
| |
336 safepoint->setOffset(safepointOffset); |
| |
337 } |
| |
338 |
| |
339 void |
| |
340 SafepointWriter::endEntry() |
| |
341 { |
| |
342 IonSpew(IonSpew_Safepoints, " -- entry ended at %d", uint32_t(stream_.length())); |
| |
343 } |
| |
344 |
| |
345 SafepointReader::SafepointReader(IonScript *script, const SafepointIndex *si) |
| |
346 : stream_(script->safepoints() + si->safepointOffset(), |
| |
347 script->safepoints() + script->safepointsSize()), |
| |
348 frameSlots_(script->frameSlots() / sizeof(intptr_t)) |
| |
349 { |
| |
350 osiCallPointOffset_ = stream_.readUnsigned(); |
| |
351 |
| |
352 // gcSpills is a subset of allGprSpills. |
| |
353 allGprSpills_ = GeneralRegisterSet(ReadRegisterMask(stream_)); |
| |
354 if (allGprSpills_.empty()) { |
| |
355 gcSpills_ = allGprSpills_; |
| |
356 valueSpills_ = allGprSpills_; |
| |
357 slotsOrElementsSpills_ = allGprSpills_; |
| |
358 } else { |
| |
359 gcSpills_ = GeneralRegisterSet(ReadRegisterMask(stream_)); |
| |
360 slotsOrElementsSpills_ = GeneralRegisterSet(ReadRegisterMask(stream_)); |
| |
361 #ifdef JS_PUNBOX64 |
| |
362 valueSpills_ = GeneralRegisterSet(ReadRegisterMask(stream_)); |
| |
363 #endif |
| |
364 } |
| |
365 |
| |
366 allFloatSpills_ = FloatRegisterSet(ReadRegisterMask(stream_)); |
| |
367 |
| |
368 advanceFromGcRegs(); |
| |
369 } |
| |
370 |
| |
371 uint32_t |
| |
372 SafepointReader::osiReturnPointOffset() const |
| |
373 { |
| |
374 return osiCallPointOffset_ + Assembler::patchWrite_NearCallSize(); |
| |
375 } |
| |
376 |
| |
377 CodeLocationLabel |
| |
378 SafepointReader::InvalidationPatchPoint(IonScript *script, const SafepointIndex *si) |
| |
379 { |
| |
380 SafepointReader reader(script, si); |
| |
381 |
| |
382 return CodeLocationLabel(script->method(), reader.osiCallPointOffset()); |
| |
383 } |
| |
384 |
| |
385 void |
| |
386 SafepointReader::advanceFromGcRegs() |
| |
387 { |
| |
388 currentSlotChunk_ = 0; |
| |
389 nextSlotChunkNumber_ = 0; |
| |
390 } |
| |
391 |
| |
392 bool |
| |
393 SafepointReader::getSlotFromBitmap(uint32_t *slot) |
| |
394 { |
| |
395 while (currentSlotChunk_ == 0) { |
| |
396 // Are there any more chunks to read? |
| |
397 if (nextSlotChunkNumber_ == BitSet::RawLengthForBits(frameSlots_)) |
| |
398 return false; |
| |
399 |
| |
400 // Yes, read the next chunk. |
| |
401 currentSlotChunk_ = stream_.readUnsigned(); |
| |
402 nextSlotChunkNumber_++; |
| |
403 } |
| |
404 |
| |
405 // The current chunk still has bits in it, so get the next bit, then mask |
| |
406 // it out of the slot chunk. |
| |
407 uint32_t bit = FloorLog2(currentSlotChunk_); |
| |
408 currentSlotChunk_ &= ~(1 << bit); |
| |
409 |
| |
410 // Return the slot, taking care to add 1 back in since it was subtracted |
| |
411 // when added in the original bitset, and re-scale it by the pointer size, |
| |
412 // reversing the transformation in MapSlotsToBitset. |
| |
413 *slot = (((nextSlotChunkNumber_ - 1) * BitSet::BitsPerWord) + bit + 1) * sizeof(intptr_t); |
| |
414 return true; |
| |
415 } |
| |
416 |
| |
417 bool |
| |
418 SafepointReader::getGcSlot(uint32_t *slot) |
| |
419 { |
| |
420 if (getSlotFromBitmap(slot)) |
| |
421 return true; |
| |
422 advanceFromGcSlots(); |
| |
423 return false; |
| |
424 } |
| |
425 |
| |
426 void |
| |
427 SafepointReader::advanceFromGcSlots() |
| |
428 { |
| |
429 // No, reset the counter. |
| |
430 currentSlotChunk_ = 0; |
| |
431 nextSlotChunkNumber_ = 0; |
| |
432 } |
| |
433 |
| |
434 bool |
| |
435 SafepointReader::getValueSlot(uint32_t *slot) |
| |
436 { |
| |
437 if (getSlotFromBitmap(slot)) |
| |
438 return true; |
| |
439 advanceFromValueSlots(); |
| |
440 return false; |
| |
441 } |
| |
442 |
| |
443 void |
| |
444 SafepointReader::advanceFromValueSlots() |
| |
445 { |
| |
446 #ifdef JS_NUNBOX32 |
| |
447 nunboxSlotsRemaining_ = stream_.readUnsigned(); |
| |
448 #else |
| |
449 nunboxSlotsRemaining_ = 0; |
| |
450 advanceFromNunboxSlots(); |
| |
451 #endif |
| |
452 } |
| |
453 |
| |
454 static inline LAllocation |
| |
455 PartFromStream(CompactBufferReader &stream, NunboxPartKind kind, uint32_t info) |
| |
456 { |
| |
457 if (kind == Part_Reg) |
| |
458 return LGeneralReg(Register::FromCode(info)); |
| |
459 |
| |
460 if (info == MAX_INFO_VALUE) |
| |
461 info = stream.readUnsigned(); |
| |
462 |
| |
463 if (kind == Part_Stack) |
| |
464 return LStackSlot(info); |
| |
465 |
| |
466 JS_ASSERT(kind == Part_Arg); |
| |
467 return LArgument(info); |
| |
468 } |
| |
469 |
| |
470 bool |
| |
471 SafepointReader::getNunboxSlot(LAllocation *type, LAllocation *payload) |
| |
472 { |
| |
473 if (!nunboxSlotsRemaining_--) { |
| |
474 advanceFromNunboxSlots(); |
| |
475 return false; |
| |
476 } |
| |
477 |
| |
478 uint16_t header = stream_.readFixedUint16_t(); |
| |
479 NunboxPartKind typeKind = (NunboxPartKind)((header >> TYPE_KIND_SHIFT) & PART_KIND_MASK); |
| |
480 NunboxPartKind payloadKind = (NunboxPartKind)((header >> PAYLOAD_KIND_SHIFT) & PART_KIND_MASK); |
| |
481 uint32_t typeInfo = (header >> TYPE_INFO_SHIFT) & PART_INFO_MASK; |
| |
482 uint32_t payloadInfo = (header >> PAYLOAD_INFO_SHIFT) & PART_INFO_MASK; |
| |
483 |
| |
484 *type = PartFromStream(stream_, typeKind, typeInfo); |
| |
485 *payload = PartFromStream(stream_, payloadKind, payloadInfo); |
| |
486 return true; |
| |
487 } |
| |
488 |
| |
489 void |
| |
490 SafepointReader::advanceFromNunboxSlots() |
| |
491 { |
| |
492 slotsOrElementsSlotsRemaining_ = stream_.readUnsigned(); |
| |
493 } |
| |
494 |
| |
495 bool |
| |
496 SafepointReader::getSlotsOrElementsSlot(uint32_t *slot) |
| |
497 { |
| |
498 if (!slotsOrElementsSlotsRemaining_--) |
| |
499 return false; |
| |
500 *slot = stream_.readUnsigned(); |
| |
501 return true; |
| |
502 } |