js/src/jit/Safepoints.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial