Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
7 #include "jit/Safepoints.h"
9 #include "mozilla/MathAlgorithms.h"
11 #include "jit/BitSet.h"
12 #include "jit/IonSpewer.h"
13 #include "jit/LIR.h"
15 using namespace js;
16 using namespace jit;
18 using mozilla::FloorLog2;
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;
27 return true;
28 }
30 uint32_t
31 SafepointWriter::startEntry()
32 {
33 IonSpew(IonSpew_Safepoints, "Encoding safepoint (position %d):", stream_.length());
34 return uint32_t(stream_.length());
35 }
37 void
38 SafepointWriter::writeOsiCallPointOffset(uint32_t osiCallPointOffset)
39 {
40 stream_.writeUnsigned(osiCallPointOffset);
41 }
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 }
52 static int32_t
53 ReadRegisterMask(CompactBufferReader &stream)
54 {
55 if (sizeof(PackedRegisterMask) == 1)
56 return stream.readByte();
57 return stream.readUnsigned();
58 }
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;
69 WriteRegisterMask(stream_, spilledGpr.bits());
70 if (!spilledGpr.empty()) {
71 WriteRegisterMask(stream_, gc.bits());
72 WriteRegisterMask(stream_, slots.bits());
74 #ifdef JS_PUNBOX64
75 valueRegs = safepoint->valueRegs();
76 WriteRegisterMask(stream_, valueRegs.bits());
77 #endif
78 }
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);
84 WriteRegisterMask(stream_, spilledFloat.bits());
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 }
104 static void
105 MapSlotsToBitset(BitSet *set, CompactBufferWriter &stream, uint32_t nslots, uint32_t *slots)
106 {
107 set->clear();
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 }
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 }
125 void
126 SafepointWriter::writeGcSlots(LSafepoint *safepoint)
127 {
128 LSafepoint::SlotList &slots = safepoint->gcSlots();
130 #ifdef DEBUG
131 for (uint32_t i = 0; i < slots.length(); i++)
132 IonSpew(IonSpew_Safepoints, " gc slot: %d", slots[i]);
133 #endif
135 MapSlotsToBitset(frameSlots_,
136 stream_,
137 slots.length(),
138 slots.begin());
139 }
141 void
142 SafepointWriter::writeSlotsOrElementsSlots(LSafepoint *safepoint)
143 {
144 LSafepoint::SlotList &slots = safepoint->slotsOrElementsSlots();
146 stream_.writeUnsigned(slots.length());
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 }
156 void
157 SafepointWriter::writeValueSlots(LSafepoint *safepoint)
158 {
159 LSafepoint::SlotList &slots = safepoint->valueSlots();
161 #ifdef DEBUG
162 for (uint32_t i = 0; i < slots.length(); i++)
163 IonSpew(IonSpew_Safepoints, " gc value: %d", slots[i]);
164 #endif
166 MapSlotsToBitset(frameSlots_, stream_, slots.length(), slots.begin());
167 }
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
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 };
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;
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;
217 JS_STATIC_ASSERT(PAYLOAD_INFO_SHIFT == 0);
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 }
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 }
243 if (a.isStackSlot())
244 *out = a.toStackSlot()->slot();
245 else
246 *out = a.toArgument()->index();
248 return *out < MAX_INFO_VALUE;
249 }
251 void
252 SafepointWriter::writeNunboxParts(LSafepoint *safepoint)
253 {
254 LSafepoint::NunboxList &entries = safepoint->nunboxParts();
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
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();
277 stream_.writeUnsigned(entries.length() - partials);
279 for (size_t i = 0; i < entries.length(); i++) {
280 SafepointNunboxEntry &entry = entries[i];
282 if (entry.type.isUse() || entry.payload.isUse()) {
283 partials--;
284 continue;
285 }
287 uint16_t header = 0;
289 header |= (AllocationToPartKind(entry.type) << TYPE_KIND_SHIFT);
290 header |= (AllocationToPartKind(entry.payload) << PAYLOAD_KIND_SHIFT);
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);
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);
306 stream_.writeFixedUint16_t(header);
307 if (typeExtra)
308 stream_.writeUnsigned(typeVal);
309 if (payloadExtra)
310 stream_.writeUnsigned(payloadVal);
311 }
313 JS_ASSERT(partials == 0);
314 }
315 #endif
317 void
318 SafepointWriter::encode(LSafepoint *safepoint)
319 {
320 uint32_t safepointOffset = startEntry();
322 JS_ASSERT(safepoint->osiCallPointOffset());
324 writeOsiCallPointOffset(safepoint->osiCallPointOffset());
325 writeGcRegs(safepoint);
326 writeGcSlots(safepoint);
327 writeValueSlots(safepoint);
329 #ifdef JS_NUNBOX32
330 writeNunboxParts(safepoint);
331 #endif
333 writeSlotsOrElementsSlots(safepoint);
335 endEntry();
336 safepoint->setOffset(safepointOffset);
337 }
339 void
340 SafepointWriter::endEntry()
341 {
342 IonSpew(IonSpew_Safepoints, " -- entry ended at %d", uint32_t(stream_.length()));
343 }
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();
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 }
366 allFloatSpills_ = FloatRegisterSet(ReadRegisterMask(stream_));
368 advanceFromGcRegs();
369 }
371 uint32_t
372 SafepointReader::osiReturnPointOffset() const
373 {
374 return osiCallPointOffset_ + Assembler::patchWrite_NearCallSize();
375 }
377 CodeLocationLabel
378 SafepointReader::InvalidationPatchPoint(IonScript *script, const SafepointIndex *si)
379 {
380 SafepointReader reader(script, si);
382 return CodeLocationLabel(script->method(), reader.osiCallPointOffset());
383 }
385 void
386 SafepointReader::advanceFromGcRegs()
387 {
388 currentSlotChunk_ = 0;
389 nextSlotChunkNumber_ = 0;
390 }
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;
400 // Yes, read the next chunk.
401 currentSlotChunk_ = stream_.readUnsigned();
402 nextSlotChunkNumber_++;
403 }
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);
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 }
417 bool
418 SafepointReader::getGcSlot(uint32_t *slot)
419 {
420 if (getSlotFromBitmap(slot))
421 return true;
422 advanceFromGcSlots();
423 return false;
424 }
426 void
427 SafepointReader::advanceFromGcSlots()
428 {
429 // No, reset the counter.
430 currentSlotChunk_ = 0;
431 nextSlotChunkNumber_ = 0;
432 }
434 bool
435 SafepointReader::getValueSlot(uint32_t *slot)
436 {
437 if (getSlotFromBitmap(slot))
438 return true;
439 advanceFromValueSlots();
440 return false;
441 }
443 void
444 SafepointReader::advanceFromValueSlots()
445 {
446 #ifdef JS_NUNBOX32
447 nunboxSlotsRemaining_ = stream_.readUnsigned();
448 #else
449 nunboxSlotsRemaining_ = 0;
450 advanceFromNunboxSlots();
451 #endif
452 }
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));
460 if (info == MAX_INFO_VALUE)
461 info = stream.readUnsigned();
463 if (kind == Part_Stack)
464 return LStackSlot(info);
466 JS_ASSERT(kind == Part_Arg);
467 return LArgument(info);
468 }
470 bool
471 SafepointReader::getNunboxSlot(LAllocation *type, LAllocation *payload)
472 {
473 if (!nunboxSlotsRemaining_--) {
474 advanceFromNunboxSlots();
475 return false;
476 }
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;
484 *type = PartFromStream(stream_, typeKind, typeInfo);
485 *payload = PartFromStream(stream_, payloadKind, payloadInfo);
486 return true;
487 }
489 void
490 SafepointReader::advanceFromNunboxSlots()
491 {
492 slotsOrElementsSlotsRemaining_ = stream_.readUnsigned();
493 }
495 bool
496 SafepointReader::getSlotsOrElementsSlot(uint32_t *slot)
497 {
498 if (!slotsOrElementsSlotsRemaining_--)
499 return false;
500 *slot = stream_.readUnsigned();
501 return true;
502 }