1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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/. */
7 #include "jit/Safepoints.h"
9 #include "mozilla/MathAlgorithms.h"
11 #include "jit/BitSet.h"
12 #include "jit/IonScript.h"
13 #include "jit/JitSpewer.h"
15 #include "jit/SafepointIndex.h"
20 using mozilla::FloorLog2
;
22 SafepointWriter::SafepointWriter(uint32_t localSlotsSize
,
23 uint32_t argumentsSize
)
24 : localSlots_((localSlotsSize
/ sizeof(intptr_t)) +
25 1), // Stack slot counts are inclusive.
26 argumentSlots_(argumentsSize
/ sizeof(intptr_t)) {}
28 bool SafepointWriter::init(TempAllocator
& alloc
) {
29 return localSlots_
.init(alloc
) && argumentSlots_
.init(alloc
);
32 uint32_t SafepointWriter::startEntry() {
33 JitSpew(JitSpew_Safepoints
,
34 "Encoding safepoint (position %zu):", stream_
.length());
35 return uint32_t(stream_
.length());
38 void SafepointWriter::writeOsiCallPointOffset(uint32_t osiCallPointOffset
) {
39 stream_
.writeUnsigned(osiCallPointOffset
);
42 static void WriteRegisterMask(CompactBufferWriter
& stream
,
43 PackedRegisterMask bits
) {
44 if (sizeof(PackedRegisterMask
) == 1) {
45 stream
.writeByte(bits
);
47 MOZ_ASSERT(sizeof(PackedRegisterMask
) <= 4);
48 stream
.writeUnsigned(bits
);
52 static PackedRegisterMask
ReadRegisterMask(CompactBufferReader
& stream
) {
53 if (sizeof(PackedRegisterMask
) == 1) {
54 return stream
.readByte();
56 MOZ_ASSERT(sizeof(PackedRegisterMask
) <= 4);
57 return stream
.readUnsigned();
60 static void WriteFloatRegisterMask(CompactBufferWriter
& stream
,
61 FloatRegisters::SetType bits
) {
62 switch (sizeof(FloatRegisters::SetType
)) {
63 #ifdef JS_CODEGEN_ARM64
65 stream
.writeUnsigned64(bits
.low());
66 stream
.writeUnsigned64(bits
.high());
70 stream
.writeByte(bits
);
73 stream
.writeUnsigned(bits
);
76 stream
.writeUnsigned64(bits
);
80 MOZ_CRASH("WriteFloatRegisterMask: unexpected size");
84 static FloatRegisters::SetType
ReadFloatRegisterMask(
85 CompactBufferReader
& stream
) {
86 switch (sizeof(FloatRegisters::SetType
)) {
87 #ifdef JS_CODEGEN_ARM64
89 uint64_t low
= stream
.readUnsigned64();
90 uint64_t high
= stream
.readUnsigned64();
91 return Bitset128(high
, low
);
95 return stream
.readByte();
99 return stream
.readUnsigned();
101 return stream
.readUnsigned64();
104 MOZ_CRASH("ReadFloatRegisterMask: unexpected size");
108 void SafepointWriter::writeGcRegs(LSafepoint
* safepoint
) {
109 LiveGeneralRegisterSet
gc(safepoint
->gcRegs());
110 LiveGeneralRegisterSet
spilledGpr(safepoint
->liveRegs().gprs());
111 LiveFloatRegisterSet
spilledFloat(safepoint
->liveRegs().fpus());
112 LiveGeneralRegisterSet
slots(safepoint
->slotsOrElementsRegs());
113 LiveGeneralRegisterSet
wasmAnyRef(safepoint
->wasmAnyRefRegs());
114 LiveGeneralRegisterSet valueRegs
;
116 WriteRegisterMask(stream_
, spilledGpr
.bits());
117 if (!spilledGpr
.empty()) {
118 WriteRegisterMask(stream_
, gc
.bits());
119 WriteRegisterMask(stream_
, slots
.bits());
120 WriteRegisterMask(stream_
, wasmAnyRef
.bits());
123 valueRegs
= safepoint
->valueRegs();
124 WriteRegisterMask(stream_
, valueRegs
.bits());
128 // GC registers are a subset of the spilled registers.
129 MOZ_ASSERT((valueRegs
.bits() & ~spilledGpr
.bits()) == 0);
130 MOZ_ASSERT((gc
.bits() & ~spilledGpr
.bits()) == 0);
132 WriteFloatRegisterMask(stream_
, spilledFloat
.bits());
135 if (JitSpewEnabled(JitSpew_Safepoints
)) {
136 for (GeneralRegisterForwardIterator
iter(spilledGpr
); iter
.more(); ++iter
) {
137 const char* type
= gc
.has(*iter
) ? "gc"
138 : slots
.has(*iter
) ? "slots"
139 : valueRegs
.has(*iter
) ? "value"
141 JitSpew(JitSpew_Safepoints
, " %s reg: %s", type
, (*iter
).name());
143 for (FloatRegisterForwardIterator
iter(spilledFloat
); iter
.more(); ++iter
) {
144 JitSpew(JitSpew_Safepoints
, " float reg: %s", (*iter
).name());
150 static void WriteBitset(const BitSet
& set
, CompactBufferWriter
& stream
) {
151 size_t count
= set
.rawLength();
152 const uint32_t* words
= set
.raw();
153 for (size_t i
= 0; i
< count
; i
++) {
154 stream
.writeUnsigned(words
[i
]);
158 static void MapSlotsToBitset(BitSet
& stackSet
, BitSet
& argumentSet
,
159 CompactBufferWriter
& stream
,
160 const LSafepoint::SlotList
& slots
) {
164 for (uint32_t i
= 0; i
< slots
.length(); i
++) {
165 // Slots are represented at a distance from |fp|. We divide by the
166 // pointer size, since we only care about pointer-sized/aligned slots
168 MOZ_ASSERT(slots
[i
].slot
% sizeof(intptr_t) == 0);
169 size_t index
= slots
[i
].slot
/ sizeof(intptr_t);
170 (slots
[i
].stack
? stackSet
: argumentSet
).insert(index
);
173 WriteBitset(stackSet
, stream
);
174 WriteBitset(argumentSet
, stream
);
177 void SafepointWriter::writeGcSlots(LSafepoint
* safepoint
) {
178 LSafepoint::SlotList
& slots
= safepoint
->gcSlots();
181 for (uint32_t i
= 0; i
< slots
.length(); i
++) {
182 JitSpew(JitSpew_Safepoints
, " gc slot: %u", slots
[i
].slot
);
186 MapSlotsToBitset(localSlots_
, argumentSlots_
, stream_
, slots
);
189 void SafepointWriter::writeSlotsOrElementsSlots(LSafepoint
* safepoint
) {
190 LSafepoint::SlotList
& slots
= safepoint
->slotsOrElementsSlots();
192 stream_
.writeUnsigned(slots
.length());
194 for (uint32_t i
= 0; i
< slots
.length(); i
++) {
195 if (!slots
[i
].stack
) {
199 JitSpew(JitSpew_Safepoints
, " slots/elements slot: %u", slots
[i
].slot
);
201 stream_
.writeUnsigned(slots
[i
].slot
);
205 void SafepointWriter::writeWasmAnyRefSlots(LSafepoint
* safepoint
) {
206 LSafepoint::SlotList
& slots
= safepoint
->wasmAnyRefSlots();
208 stream_
.writeUnsigned(slots
.length());
210 for (uint32_t i
= 0; i
< slots
.length(); i
++) {
211 if (!slots
[i
].stack
) {
215 JitSpew(JitSpew_Safepoints
, " wasm_anyref slot: %u", slots
[i
].slot
);
217 stream_
.writeUnsigned(slots
[i
].slot
);
222 void SafepointWriter::writeValueSlots(LSafepoint
* safepoint
) {
223 LSafepoint::SlotList
& slots
= safepoint
->valueSlots();
226 for (uint32_t i
= 0; i
< slots
.length(); i
++) {
227 JitSpew(JitSpew_Safepoints
, " gc value: %u", slots
[i
].slot
);
231 MapSlotsToBitset(localSlots_
, argumentSlots_
, stream_
, slots
);
235 #if defined(JS_JITSPEW) && defined(JS_NUNBOX32)
236 static void DumpNunboxPart(const LAllocation
& a
) {
237 Fprinter
& out
= JitSpewPrinter();
238 if (a
.isStackSlot()) {
239 out
.printf("stack %d", a
.toStackSlot()->slot());
240 } else if (a
.isArgument()) {
241 out
.printf("arg %d", a
.toArgument()->index());
243 out
.printf("reg %s", a
.toGeneralReg()->reg().name());
248 // Nunbox part encoding:
255 // uint16_t: tttp ppXX XXXY YYYY
257 // If ttt = Reg, type is reg XXXXX
258 // If ppp = Reg, payload is reg YYYYY
260 // If ttt != Reg, type is:
261 // XXXXX if not 11111, otherwise followed by [vwu]
262 // If ppp != Reg, payload is:
263 // YYYYY if not 11111, otherwise followed by [vwu]
265 enum NunboxPartKind
{ Part_Reg
, Part_Stack
, Part_Arg
};
267 static const uint32_t PART_KIND_BITS
= 3;
268 static const uint32_t PART_KIND_MASK
= (1 << PART_KIND_BITS
) - 1;
269 static const uint32_t PART_INFO_BITS
= 5;
270 static const uint32_t PART_INFO_MASK
= (1 << PART_INFO_BITS
) - 1;
272 static const uint32_t MAX_INFO_VALUE
= (1 << PART_INFO_BITS
) - 1;
273 static const uint32_t TYPE_KIND_SHIFT
= 16 - PART_KIND_BITS
;
274 static const uint32_t PAYLOAD_KIND_SHIFT
= TYPE_KIND_SHIFT
- PART_KIND_BITS
;
275 static const uint32_t TYPE_INFO_SHIFT
= PAYLOAD_KIND_SHIFT
- PART_INFO_BITS
;
276 static const uint32_t PAYLOAD_INFO_SHIFT
= TYPE_INFO_SHIFT
- PART_INFO_BITS
;
278 static_assert(PAYLOAD_INFO_SHIFT
== 0);
281 static inline NunboxPartKind
AllocationToPartKind(const LAllocation
& a
) {
282 if (a
.isRegister()) {
285 if (a
.isStackSlot()) {
288 MOZ_ASSERT(a
.isArgument());
292 // gcc 4.5 doesn't actually inline CanEncodeInfoInHeader when only
293 // using the "inline" keyword, and miscompiles the function as well
294 // when doing block reordering with branch prediction information.
295 // See bug 799295 comment 71.
296 static MOZ_ALWAYS_INLINE
bool CanEncodeInfoInHeader(const LAllocation
& a
,
298 if (a
.isGeneralReg()) {
299 *out
= a
.toGeneralReg()->reg().code();
303 if (a
.isStackSlot()) {
304 *out
= a
.toStackSlot()->slot();
306 *out
= a
.toArgument()->index();
309 return *out
< MAX_INFO_VALUE
;
312 void SafepointWriter::writeNunboxParts(LSafepoint
* safepoint
) {
313 LSafepoint::NunboxList
& entries
= safepoint
->nunboxParts();
316 if (JitSpewEnabled(JitSpew_Safepoints
)) {
317 for (uint32_t i
= 0; i
< entries
.length(); i
++) {
318 SafepointNunboxEntry
& entry
= entries
[i
];
319 if (entry
.type
.isUse() || entry
.payload
.isUse()) {
322 JitSpewHeader(JitSpew_Safepoints
);
323 Fprinter
& out
= JitSpewPrinter();
324 out
.printf(" nunbox (type in ");
325 DumpNunboxPart(entry
.type
);
326 out
.printf(", payload in ");
327 DumpNunboxPart(entry
.payload
);
333 // Safepoints are permitted to have partially filled in entries for nunboxes,
334 // provided that only the type is live and not the payload. Omit these from
335 // the written safepoint.
337 size_t pos
= stream_
.length();
338 stream_
.writeUnsigned(entries
.length());
341 for (size_t i
= 0; i
< entries
.length(); i
++) {
342 SafepointNunboxEntry
& entry
= entries
[i
];
344 if (entry
.payload
.isUse()) {
345 // No allocation associated with the payload.
349 if (entry
.type
.isUse()) {
350 // No allocation associated with the type. Look for another
351 // safepoint entry with an allocation for the type.
352 entry
.type
= safepoint
->findTypeAllocation(entry
.typeVreg
);
353 if (entry
.type
.isUse()) {
362 header
|= (AllocationToPartKind(entry
.type
) << TYPE_KIND_SHIFT
);
363 header
|= (AllocationToPartKind(entry
.payload
) << PAYLOAD_KIND_SHIFT
);
366 bool typeExtra
= !CanEncodeInfoInHeader(entry
.type
, &typeVal
);
368 header
|= (typeVal
<< TYPE_INFO_SHIFT
);
370 header
|= (MAX_INFO_VALUE
<< TYPE_INFO_SHIFT
);
374 bool payloadExtra
= !CanEncodeInfoInHeader(entry
.payload
, &payloadVal
);
376 header
|= (payloadVal
<< PAYLOAD_INFO_SHIFT
);
378 header
|= (MAX_INFO_VALUE
<< PAYLOAD_INFO_SHIFT
);
381 stream_
.writeFixedUint16_t(header
);
383 stream_
.writeUnsigned(typeVal
);
386 stream_
.writeUnsigned(payloadVal
);
390 // Update the stream with the actual number of safepoint entries written.
391 stream_
.writeUnsignedAt(pos
, count
, entries
.length());
395 void SafepointWriter::encode(LSafepoint
* safepoint
) {
396 uint32_t safepointOffset
= startEntry();
398 MOZ_ASSERT(safepoint
->osiCallPointOffset());
400 writeOsiCallPointOffset(safepoint
->osiCallPointOffset());
401 writeGcRegs(safepoint
);
402 writeGcSlots(safepoint
);
405 writeValueSlots(safepoint
);
407 writeNunboxParts(safepoint
);
410 writeSlotsOrElementsSlots(safepoint
);
411 writeWasmAnyRefSlots(safepoint
);
414 safepoint
->setOffset(safepointOffset
);
417 void SafepointWriter::endEntry() {
418 JitSpew(JitSpew_Safepoints
, " -- entry ended at %u",
419 uint32_t(stream_
.length()));
422 SafepointReader::SafepointReader(IonScript
* script
, const SafepointIndex
* si
)
423 : stream_(script
->safepoints() + si
->safepointOffset(),
424 script
->safepoints() + script
->safepointsSize()),
425 localSlots_((script
->localSlotsSize() / sizeof(intptr_t)) +
426 1), // Stack slot counts are inclusive.
427 argumentSlots_(script
->argumentSlotsSize() / sizeof(intptr_t)),
428 nunboxSlotsRemaining_(0),
429 slotsOrElementsSlotsRemaining_(0),
430 wasmAnyRefSlotsRemaining_(0) {
431 osiCallPointOffset_
= stream_
.readUnsigned();
433 // gcSpills is a subset of allGprSpills.
434 allGprSpills_
= GeneralRegisterSet(ReadRegisterMask(stream_
));
435 if (allGprSpills_
.empty()) {
436 gcSpills_
= allGprSpills_
;
437 valueSpills_
= allGprSpills_
;
438 slotsOrElementsSpills_
= allGprSpills_
;
439 wasmAnyRefSpills_
= allGprSpills_
;
441 gcSpills_
= GeneralRegisterSet(ReadRegisterMask(stream_
));
442 slotsOrElementsSpills_
= GeneralRegisterSet(ReadRegisterMask(stream_
));
443 wasmAnyRefSpills_
= GeneralRegisterSet(ReadRegisterMask(stream_
));
445 valueSpills_
= GeneralRegisterSet(ReadRegisterMask(stream_
));
449 allFloatSpills_
= FloatRegisterSet(ReadFloatRegisterMask(stream_
));
454 uint32_t SafepointReader::osiReturnPointOffset() const {
455 return osiCallPointOffset_
+ Assembler::PatchWrite_NearCallSize();
458 CodeLocationLabel
SafepointReader::InvalidationPatchPoint(
459 IonScript
* script
, const SafepointIndex
* si
) {
460 SafepointReader
reader(script
, si
);
462 return CodeLocationLabel(script
->method(),
463 CodeOffset(reader
.osiCallPointOffset()));
466 void SafepointReader::advanceFromGcRegs() {
467 currentSlotChunk_
= 0;
468 nextSlotChunkNumber_
= 0;
469 currentSlotsAreStack_
= true;
472 bool SafepointReader::getSlotFromBitmap(SafepointSlotEntry
* entry
) {
473 while (currentSlotChunk_
== 0) {
474 // Are there any more chunks to read?
475 if (currentSlotsAreStack_
) {
476 if (nextSlotChunkNumber_
== BitSet::RawLengthForBits(localSlots_
)) {
477 nextSlotChunkNumber_
= 0;
478 currentSlotsAreStack_
= false;
481 } else if (nextSlotChunkNumber_
==
482 BitSet::RawLengthForBits(argumentSlots_
)) {
486 // Yes, read the next chunk.
487 currentSlotChunk_
= stream_
.readUnsigned();
488 nextSlotChunkNumber_
++;
491 // The current chunk still has bits in it, so get the next bit, then mask
492 // it out of the slot chunk.
493 uint32_t bit
= FloorLog2(currentSlotChunk_
);
494 currentSlotChunk_
&= ~(1 << bit
);
496 // Return the slot, and re-scale it by the pointer size, reversing the
497 // transformation in MapSlotsToBitset.
498 entry
->stack
= currentSlotsAreStack_
;
499 entry
->slot
= (((nextSlotChunkNumber_
- 1) * BitSet::BitsPerWord
) + bit
) *
504 bool SafepointReader::getGcSlot(SafepointSlotEntry
* entry
) {
505 if (getSlotFromBitmap(entry
)) {
508 advanceFromGcSlots();
512 void SafepointReader::advanceFromGcSlots() {
513 // No, reset the counter.
514 currentSlotChunk_
= 0;
515 nextSlotChunkNumber_
= 0;
516 currentSlotsAreStack_
= true;
518 // Nunbox slots are next.
519 nunboxSlotsRemaining_
= stream_
.readUnsigned();
521 // Value slots are next.
525 bool SafepointReader::getValueSlot(SafepointSlotEntry
* entry
) {
526 if (getSlotFromBitmap(entry
)) {
529 advanceFromNunboxOrValueSlots();
533 static inline LAllocation
PartFromStream(CompactBufferReader
& stream
,
534 NunboxPartKind kind
, uint32_t info
) {
535 if (kind
== Part_Reg
) {
536 return LGeneralReg(Register::FromCode(info
));
539 if (info
== MAX_INFO_VALUE
) {
540 info
= stream
.readUnsigned();
543 if (kind
== Part_Stack
) {
544 return LStackSlot(info
);
547 MOZ_ASSERT(kind
== Part_Arg
);
548 return LArgument(info
);
551 bool SafepointReader::getNunboxSlot(LAllocation
* type
, LAllocation
* payload
) {
552 if (!nunboxSlotsRemaining_
--) {
553 advanceFromNunboxOrValueSlots();
557 uint16_t header
= stream_
.readFixedUint16_t();
558 NunboxPartKind typeKind
=
559 (NunboxPartKind
)((header
>> TYPE_KIND_SHIFT
) & PART_KIND_MASK
);
560 NunboxPartKind payloadKind
=
561 (NunboxPartKind
)((header
>> PAYLOAD_KIND_SHIFT
) & PART_KIND_MASK
);
562 uint32_t typeInfo
= (header
>> TYPE_INFO_SHIFT
) & PART_INFO_MASK
;
563 uint32_t payloadInfo
= (header
>> PAYLOAD_INFO_SHIFT
) & PART_INFO_MASK
;
565 *type
= PartFromStream(stream_
, typeKind
, typeInfo
);
566 *payload
= PartFromStream(stream_
, payloadKind
, payloadInfo
);
570 void SafepointReader::advanceFromNunboxOrValueSlots() {
571 slotsOrElementsSlotsRemaining_
= stream_
.readUnsigned();
574 bool SafepointReader::getSlotsOrElementsSlot(SafepointSlotEntry
* entry
) {
575 if (!slotsOrElementsSlotsRemaining_
--) {
576 advanceFromSlotsOrElementsSlots();
580 entry
->slot
= stream_
.readUnsigned();
584 void SafepointReader::advanceFromSlotsOrElementsSlots() {
585 wasmAnyRefSlotsRemaining_
= stream_
.readUnsigned();
588 bool SafepointReader::getWasmAnyRefSlot(SafepointSlotEntry
* entry
) {
589 if (!wasmAnyRefSlotsRemaining_
--) {
593 entry
->slot
= stream_
.readUnsigned();