1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/Safepoints.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,502 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jit/Safepoints.h" 1.11 + 1.12 +#include "mozilla/MathAlgorithms.h" 1.13 + 1.14 +#include "jit/BitSet.h" 1.15 +#include "jit/IonSpewer.h" 1.16 +#include "jit/LIR.h" 1.17 + 1.18 +using namespace js; 1.19 +using namespace jit; 1.20 + 1.21 +using mozilla::FloorLog2; 1.22 + 1.23 +bool 1.24 +SafepointWriter::init(TempAllocator &alloc, uint32_t slotCount) 1.25 +{ 1.26 + frameSlots_ = BitSet::New(alloc, slotCount / sizeof(intptr_t)); 1.27 + if (!frameSlots_) 1.28 + return false; 1.29 + 1.30 + return true; 1.31 +} 1.32 + 1.33 +uint32_t 1.34 +SafepointWriter::startEntry() 1.35 +{ 1.36 + IonSpew(IonSpew_Safepoints, "Encoding safepoint (position %d):", stream_.length()); 1.37 + return uint32_t(stream_.length()); 1.38 +} 1.39 + 1.40 +void 1.41 +SafepointWriter::writeOsiCallPointOffset(uint32_t osiCallPointOffset) 1.42 +{ 1.43 + stream_.writeUnsigned(osiCallPointOffset); 1.44 +} 1.45 + 1.46 +static void 1.47 +WriteRegisterMask(CompactBufferWriter &stream, uint32_t bits) 1.48 +{ 1.49 + if (sizeof(PackedRegisterMask) == 1) 1.50 + stream.writeByte(bits); 1.51 + else 1.52 + stream.writeUnsigned(bits); 1.53 +} 1.54 + 1.55 +static int32_t 1.56 +ReadRegisterMask(CompactBufferReader &stream) 1.57 +{ 1.58 + if (sizeof(PackedRegisterMask) == 1) 1.59 + return stream.readByte(); 1.60 + return stream.readUnsigned(); 1.61 +} 1.62 + 1.63 +void 1.64 +SafepointWriter::writeGcRegs(LSafepoint *safepoint) 1.65 +{ 1.66 + GeneralRegisterSet gc = safepoint->gcRegs(); 1.67 + GeneralRegisterSet spilledGpr = safepoint->liveRegs().gprs(); 1.68 + FloatRegisterSet spilledFloat = safepoint->liveRegs().fpus(); 1.69 + GeneralRegisterSet slots = safepoint->slotsOrElementsRegs(); 1.70 + GeneralRegisterSet valueRegs; 1.71 + 1.72 + WriteRegisterMask(stream_, spilledGpr.bits()); 1.73 + if (!spilledGpr.empty()) { 1.74 + WriteRegisterMask(stream_, gc.bits()); 1.75 + WriteRegisterMask(stream_, slots.bits()); 1.76 + 1.77 +#ifdef JS_PUNBOX64 1.78 + valueRegs = safepoint->valueRegs(); 1.79 + WriteRegisterMask(stream_, valueRegs.bits()); 1.80 +#endif 1.81 + } 1.82 + 1.83 + // GC registers are a subset of the spilled registers. 1.84 + JS_ASSERT((valueRegs.bits() & ~spilledGpr.bits()) == 0); 1.85 + JS_ASSERT((gc.bits() & ~spilledGpr.bits()) == 0); 1.86 + 1.87 + WriteRegisterMask(stream_, spilledFloat.bits()); 1.88 + 1.89 +#ifdef DEBUG 1.90 + if (IonSpewEnabled(IonSpew_Safepoints)) { 1.91 + for (GeneralRegisterForwardIterator iter(spilledGpr); iter.more(); iter++) { 1.92 + const char *type = gc.has(*iter) 1.93 + ? "gc" 1.94 + : slots.has(*iter) 1.95 + ? "slots" 1.96 + : valueRegs.has(*iter) 1.97 + ? "value" 1.98 + : "any"; 1.99 + IonSpew(IonSpew_Safepoints, " %s reg: %s", type, (*iter).name()); 1.100 + } 1.101 + for (FloatRegisterForwardIterator iter(spilledFloat); iter.more(); iter++) 1.102 + IonSpew(IonSpew_Safepoints, " float reg: %s", (*iter).name()); 1.103 + } 1.104 +#endif 1.105 +} 1.106 + 1.107 +static void 1.108 +MapSlotsToBitset(BitSet *set, CompactBufferWriter &stream, uint32_t nslots, uint32_t *slots) 1.109 +{ 1.110 + set->clear(); 1.111 + 1.112 + for (uint32_t i = 0; i < nslots; i++) { 1.113 + // Slots are represented at a distance from |fp|. We divide by the 1.114 + // pointer size, since we only care about pointer-sized/aligned slots 1.115 + // here. Since the stack grows down, this means slots start at index 1, 1.116 + // so we subtract 1 to pack the bitset. 1.117 + JS_ASSERT(slots[i] % sizeof(intptr_t) == 0); 1.118 + JS_ASSERT(slots[i] / sizeof(intptr_t) > 0); 1.119 + set->insert(slots[i] / sizeof(intptr_t) - 1); 1.120 + } 1.121 + 1.122 + size_t count = set->rawLength(); 1.123 + const uint32_t *words = set->raw(); 1.124 + for (size_t i = 0; i < count; i++) 1.125 + stream.writeUnsigned(words[i]); 1.126 +} 1.127 + 1.128 +void 1.129 +SafepointWriter::writeGcSlots(LSafepoint *safepoint) 1.130 +{ 1.131 + LSafepoint::SlotList &slots = safepoint->gcSlots(); 1.132 + 1.133 +#ifdef DEBUG 1.134 + for (uint32_t i = 0; i < slots.length(); i++) 1.135 + IonSpew(IonSpew_Safepoints, " gc slot: %d", slots[i]); 1.136 +#endif 1.137 + 1.138 + MapSlotsToBitset(frameSlots_, 1.139 + stream_, 1.140 + slots.length(), 1.141 + slots.begin()); 1.142 +} 1.143 + 1.144 +void 1.145 +SafepointWriter::writeSlotsOrElementsSlots(LSafepoint *safepoint) 1.146 +{ 1.147 + LSafepoint::SlotList &slots = safepoint->slotsOrElementsSlots(); 1.148 + 1.149 + stream_.writeUnsigned(slots.length()); 1.150 + 1.151 + for (uint32_t i = 0; i < slots.length(); i++) { 1.152 +#ifdef DEBUG 1.153 + IonSpew(IonSpew_Safepoints, " slots/elements slot: %d", slots[i]); 1.154 +#endif 1.155 + stream_.writeUnsigned(slots[i]); 1.156 + } 1.157 +} 1.158 + 1.159 +void 1.160 +SafepointWriter::writeValueSlots(LSafepoint *safepoint) 1.161 +{ 1.162 + LSafepoint::SlotList &slots = safepoint->valueSlots(); 1.163 + 1.164 +#ifdef DEBUG 1.165 + for (uint32_t i = 0; i < slots.length(); i++) 1.166 + IonSpew(IonSpew_Safepoints, " gc value: %d", slots[i]); 1.167 +#endif 1.168 + 1.169 + MapSlotsToBitset(frameSlots_, stream_, slots.length(), slots.begin()); 1.170 +} 1.171 + 1.172 +#if defined(DEBUG) && defined(JS_NUNBOX32) 1.173 +static void 1.174 +DumpNunboxPart(const LAllocation &a) 1.175 +{ 1.176 + if (a.isStackSlot()) { 1.177 + fprintf(IonSpewFile, "stack %d", a.toStackSlot()->slot()); 1.178 + } else if (a.isArgument()) { 1.179 + fprintf(IonSpewFile, "arg %d", a.toArgument()->index()); 1.180 + } else { 1.181 + fprintf(IonSpewFile, "reg %s", a.toGeneralReg()->reg().name()); 1.182 + } 1.183 +} 1.184 +#endif // DEBUG 1.185 + 1.186 +// Nunbox part encoding: 1.187 +// 1.188 +// Reg = 000 1.189 +// Stack = 001 1.190 +// Arg = 010 1.191 +// 1.192 +// [vwu] nentries: 1.193 +// uint16_t: tttp ppXX XXXY YYYY 1.194 +// 1.195 +// If ttt = Reg, type is reg XXXXX 1.196 +// If ppp = Reg, payload is reg YYYYY 1.197 +// 1.198 +// If ttt != Reg, type is: 1.199 +// XXXXX if not 11111, otherwise followed by [vwu] 1.200 +// If ppp != Reg, payload is: 1.201 +// YYYYY if not 11111, otherwise followed by [vwu] 1.202 +// 1.203 +enum NunboxPartKind { 1.204 + Part_Reg, 1.205 + Part_Stack, 1.206 + Part_Arg 1.207 +}; 1.208 + 1.209 +static const uint32_t PART_KIND_BITS = 3; 1.210 +static const uint32_t PART_KIND_MASK = (1 << PART_KIND_BITS) - 1; 1.211 +static const uint32_t PART_INFO_BITS = 5; 1.212 +static const uint32_t PART_INFO_MASK = (1 << PART_INFO_BITS) - 1; 1.213 + 1.214 +static const uint32_t MAX_INFO_VALUE = (1 << PART_INFO_BITS) - 1; 1.215 +static const uint32_t TYPE_KIND_SHIFT = 16 - PART_KIND_BITS; 1.216 +static const uint32_t PAYLOAD_KIND_SHIFT = TYPE_KIND_SHIFT - PART_KIND_BITS; 1.217 +static const uint32_t TYPE_INFO_SHIFT = PAYLOAD_KIND_SHIFT - PART_INFO_BITS; 1.218 +static const uint32_t PAYLOAD_INFO_SHIFT = TYPE_INFO_SHIFT - PART_INFO_BITS; 1.219 + 1.220 +JS_STATIC_ASSERT(PAYLOAD_INFO_SHIFT == 0); 1.221 + 1.222 +#ifdef JS_NUNBOX32 1.223 +static inline NunboxPartKind 1.224 +AllocationToPartKind(const LAllocation &a) 1.225 +{ 1.226 + if (a.isRegister()) 1.227 + return Part_Reg; 1.228 + if (a.isStackSlot()) 1.229 + return Part_Stack; 1.230 + JS_ASSERT(a.isArgument()); 1.231 + return Part_Arg; 1.232 +} 1.233 + 1.234 +// gcc 4.5 doesn't actually inline CanEncodeInfoInHeader when only 1.235 +// using the "inline" keyword, and miscompiles the function as well 1.236 +// when doing block reordering with branch prediction information. 1.237 +// See bug 799295 comment 71. 1.238 +static MOZ_ALWAYS_INLINE bool 1.239 +CanEncodeInfoInHeader(const LAllocation &a, uint32_t *out) 1.240 +{ 1.241 + if (a.isGeneralReg()) { 1.242 + *out = a.toGeneralReg()->reg().code(); 1.243 + return true; 1.244 + } 1.245 + 1.246 + if (a.isStackSlot()) 1.247 + *out = a.toStackSlot()->slot(); 1.248 + else 1.249 + *out = a.toArgument()->index(); 1.250 + 1.251 + return *out < MAX_INFO_VALUE; 1.252 +} 1.253 + 1.254 +void 1.255 +SafepointWriter::writeNunboxParts(LSafepoint *safepoint) 1.256 +{ 1.257 + LSafepoint::NunboxList &entries = safepoint->nunboxParts(); 1.258 + 1.259 +# ifdef DEBUG 1.260 + if (IonSpewEnabled(IonSpew_Safepoints)) { 1.261 + for (uint32_t i = 0; i < entries.length(); i++) { 1.262 + SafepointNunboxEntry &entry = entries[i]; 1.263 + if (entry.type.isUse() || entry.payload.isUse()) 1.264 + continue; 1.265 + IonSpewHeader(IonSpew_Safepoints); 1.266 + fprintf(IonSpewFile, " nunbox (type in "); 1.267 + DumpNunboxPart(entry.type); 1.268 + fprintf(IonSpewFile, ", payload in "); 1.269 + DumpNunboxPart(entry.payload); 1.270 + fprintf(IonSpewFile, ")\n"); 1.271 + } 1.272 + } 1.273 +# endif 1.274 + 1.275 + // Safepoints are permitted to have partially filled in entries for nunboxes, 1.276 + // provided that only the type is live and not the payload. Omit these from 1.277 + // the written safepoint. 1.278 + uint32_t partials = safepoint->partialNunboxes(); 1.279 + 1.280 + stream_.writeUnsigned(entries.length() - partials); 1.281 + 1.282 + for (size_t i = 0; i < entries.length(); i++) { 1.283 + SafepointNunboxEntry &entry = entries[i]; 1.284 + 1.285 + if (entry.type.isUse() || entry.payload.isUse()) { 1.286 + partials--; 1.287 + continue; 1.288 + } 1.289 + 1.290 + uint16_t header = 0; 1.291 + 1.292 + header |= (AllocationToPartKind(entry.type) << TYPE_KIND_SHIFT); 1.293 + header |= (AllocationToPartKind(entry.payload) << PAYLOAD_KIND_SHIFT); 1.294 + 1.295 + uint32_t typeVal; 1.296 + bool typeExtra = !CanEncodeInfoInHeader(entry.type, &typeVal); 1.297 + if (!typeExtra) 1.298 + header |= (typeVal << TYPE_INFO_SHIFT); 1.299 + else 1.300 + header |= (MAX_INFO_VALUE << TYPE_INFO_SHIFT); 1.301 + 1.302 + uint32_t payloadVal; 1.303 + bool payloadExtra = !CanEncodeInfoInHeader(entry.payload, &payloadVal); 1.304 + if (!payloadExtra) 1.305 + header |= (payloadVal << PAYLOAD_INFO_SHIFT); 1.306 + else 1.307 + header |= (MAX_INFO_VALUE << PAYLOAD_INFO_SHIFT); 1.308 + 1.309 + stream_.writeFixedUint16_t(header); 1.310 + if (typeExtra) 1.311 + stream_.writeUnsigned(typeVal); 1.312 + if (payloadExtra) 1.313 + stream_.writeUnsigned(payloadVal); 1.314 + } 1.315 + 1.316 + JS_ASSERT(partials == 0); 1.317 +} 1.318 +#endif 1.319 + 1.320 +void 1.321 +SafepointWriter::encode(LSafepoint *safepoint) 1.322 +{ 1.323 + uint32_t safepointOffset = startEntry(); 1.324 + 1.325 + JS_ASSERT(safepoint->osiCallPointOffset()); 1.326 + 1.327 + writeOsiCallPointOffset(safepoint->osiCallPointOffset()); 1.328 + writeGcRegs(safepoint); 1.329 + writeGcSlots(safepoint); 1.330 + writeValueSlots(safepoint); 1.331 + 1.332 +#ifdef JS_NUNBOX32 1.333 + writeNunboxParts(safepoint); 1.334 +#endif 1.335 + 1.336 + writeSlotsOrElementsSlots(safepoint); 1.337 + 1.338 + endEntry(); 1.339 + safepoint->setOffset(safepointOffset); 1.340 +} 1.341 + 1.342 +void 1.343 +SafepointWriter::endEntry() 1.344 +{ 1.345 + IonSpew(IonSpew_Safepoints, " -- entry ended at %d", uint32_t(stream_.length())); 1.346 +} 1.347 + 1.348 +SafepointReader::SafepointReader(IonScript *script, const SafepointIndex *si) 1.349 + : stream_(script->safepoints() + si->safepointOffset(), 1.350 + script->safepoints() + script->safepointsSize()), 1.351 + frameSlots_(script->frameSlots() / sizeof(intptr_t)) 1.352 +{ 1.353 + osiCallPointOffset_ = stream_.readUnsigned(); 1.354 + 1.355 + // gcSpills is a subset of allGprSpills. 1.356 + allGprSpills_ = GeneralRegisterSet(ReadRegisterMask(stream_)); 1.357 + if (allGprSpills_.empty()) { 1.358 + gcSpills_ = allGprSpills_; 1.359 + valueSpills_ = allGprSpills_; 1.360 + slotsOrElementsSpills_ = allGprSpills_; 1.361 + } else { 1.362 + gcSpills_ = GeneralRegisterSet(ReadRegisterMask(stream_)); 1.363 + slotsOrElementsSpills_ = GeneralRegisterSet(ReadRegisterMask(stream_)); 1.364 +#ifdef JS_PUNBOX64 1.365 + valueSpills_ = GeneralRegisterSet(ReadRegisterMask(stream_)); 1.366 +#endif 1.367 + } 1.368 + 1.369 + allFloatSpills_ = FloatRegisterSet(ReadRegisterMask(stream_)); 1.370 + 1.371 + advanceFromGcRegs(); 1.372 +} 1.373 + 1.374 +uint32_t 1.375 +SafepointReader::osiReturnPointOffset() const 1.376 +{ 1.377 + return osiCallPointOffset_ + Assembler::patchWrite_NearCallSize(); 1.378 +} 1.379 + 1.380 +CodeLocationLabel 1.381 +SafepointReader::InvalidationPatchPoint(IonScript *script, const SafepointIndex *si) 1.382 +{ 1.383 + SafepointReader reader(script, si); 1.384 + 1.385 + return CodeLocationLabel(script->method(), reader.osiCallPointOffset()); 1.386 +} 1.387 + 1.388 +void 1.389 +SafepointReader::advanceFromGcRegs() 1.390 +{ 1.391 + currentSlotChunk_ = 0; 1.392 + nextSlotChunkNumber_ = 0; 1.393 +} 1.394 + 1.395 +bool 1.396 +SafepointReader::getSlotFromBitmap(uint32_t *slot) 1.397 +{ 1.398 + while (currentSlotChunk_ == 0) { 1.399 + // Are there any more chunks to read? 1.400 + if (nextSlotChunkNumber_ == BitSet::RawLengthForBits(frameSlots_)) 1.401 + return false; 1.402 + 1.403 + // Yes, read the next chunk. 1.404 + currentSlotChunk_ = stream_.readUnsigned(); 1.405 + nextSlotChunkNumber_++; 1.406 + } 1.407 + 1.408 + // The current chunk still has bits in it, so get the next bit, then mask 1.409 + // it out of the slot chunk. 1.410 + uint32_t bit = FloorLog2(currentSlotChunk_); 1.411 + currentSlotChunk_ &= ~(1 << bit); 1.412 + 1.413 + // Return the slot, taking care to add 1 back in since it was subtracted 1.414 + // when added in the original bitset, and re-scale it by the pointer size, 1.415 + // reversing the transformation in MapSlotsToBitset. 1.416 + *slot = (((nextSlotChunkNumber_ - 1) * BitSet::BitsPerWord) + bit + 1) * sizeof(intptr_t); 1.417 + return true; 1.418 +} 1.419 + 1.420 +bool 1.421 +SafepointReader::getGcSlot(uint32_t *slot) 1.422 +{ 1.423 + if (getSlotFromBitmap(slot)) 1.424 + return true; 1.425 + advanceFromGcSlots(); 1.426 + return false; 1.427 +} 1.428 + 1.429 +void 1.430 +SafepointReader::advanceFromGcSlots() 1.431 +{ 1.432 + // No, reset the counter. 1.433 + currentSlotChunk_ = 0; 1.434 + nextSlotChunkNumber_ = 0; 1.435 +} 1.436 + 1.437 +bool 1.438 +SafepointReader::getValueSlot(uint32_t *slot) 1.439 +{ 1.440 + if (getSlotFromBitmap(slot)) 1.441 + return true; 1.442 + advanceFromValueSlots(); 1.443 + return false; 1.444 +} 1.445 + 1.446 +void 1.447 +SafepointReader::advanceFromValueSlots() 1.448 +{ 1.449 +#ifdef JS_NUNBOX32 1.450 + nunboxSlotsRemaining_ = stream_.readUnsigned(); 1.451 +#else 1.452 + nunboxSlotsRemaining_ = 0; 1.453 + advanceFromNunboxSlots(); 1.454 +#endif 1.455 +} 1.456 + 1.457 +static inline LAllocation 1.458 +PartFromStream(CompactBufferReader &stream, NunboxPartKind kind, uint32_t info) 1.459 +{ 1.460 + if (kind == Part_Reg) 1.461 + return LGeneralReg(Register::FromCode(info)); 1.462 + 1.463 + if (info == MAX_INFO_VALUE) 1.464 + info = stream.readUnsigned(); 1.465 + 1.466 + if (kind == Part_Stack) 1.467 + return LStackSlot(info); 1.468 + 1.469 + JS_ASSERT(kind == Part_Arg); 1.470 + return LArgument(info); 1.471 +} 1.472 + 1.473 +bool 1.474 +SafepointReader::getNunboxSlot(LAllocation *type, LAllocation *payload) 1.475 +{ 1.476 + if (!nunboxSlotsRemaining_--) { 1.477 + advanceFromNunboxSlots(); 1.478 + return false; 1.479 + } 1.480 + 1.481 + uint16_t header = stream_.readFixedUint16_t(); 1.482 + NunboxPartKind typeKind = (NunboxPartKind)((header >> TYPE_KIND_SHIFT) & PART_KIND_MASK); 1.483 + NunboxPartKind payloadKind = (NunboxPartKind)((header >> PAYLOAD_KIND_SHIFT) & PART_KIND_MASK); 1.484 + uint32_t typeInfo = (header >> TYPE_INFO_SHIFT) & PART_INFO_MASK; 1.485 + uint32_t payloadInfo = (header >> PAYLOAD_INFO_SHIFT) & PART_INFO_MASK; 1.486 + 1.487 + *type = PartFromStream(stream_, typeKind, typeInfo); 1.488 + *payload = PartFromStream(stream_, payloadKind, payloadInfo); 1.489 + return true; 1.490 +} 1.491 + 1.492 +void 1.493 +SafepointReader::advanceFromNunboxSlots() 1.494 +{ 1.495 + slotsOrElementsSlotsRemaining_ = stream_.readUnsigned(); 1.496 +} 1.497 + 1.498 +bool 1.499 +SafepointReader::getSlotsOrElementsSlot(uint32_t *slot) 1.500 +{ 1.501 + if (!slotsOrElementsSlotsRemaining_--) 1.502 + return false; 1.503 + *slot = stream_.readUnsigned(); 1.504 + return true; 1.505 +}