|
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 } |