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