Bug 888872 - Keep track of slots/elements pointers stored in Ion frames for generational GC. r=dvander,terrence

This commit is contained in:
Jan de Mooij 2013-07-03 20:27:39 +02:00
parent 8e77fc67c6
commit bb019cc3a4
10 changed files with 219 additions and 25 deletions

View File

@ -325,6 +325,10 @@ void
js::Nursery::forwardBufferPointer(HeapSlot **pSlotsElems)
{
HeapSlot *old = *pSlotsElems;
if (!isInside(old))
return;
/*
* If the elements buffer is zero length, the "first" item could be inside
* of the next object or past the end of the allocable area. However,
@ -333,20 +337,10 @@ js::Nursery::forwardBufferPointer(HeapSlot **pSlotsElems)
* end of the allocable area. Thus, it is always safe to read the first
* word of |old| here.
*/
JS_ASSERT(isInside(old));
*pSlotsElems = *reinterpret_cast<HeapSlot **>(old);
JS_ASSERT(!isInside(*pSlotsElems));
}
void
js::Nursery::forwardMovedBuffers(JSRuntime *rt)
{
/*
for (current = rt->list; ; current = current->next)
forwardBufferPointer(current->item);
*/
}
void *
js::Nursery::moveToTenured(MinorCollectionTracer *trc, JSObject *src)
{
@ -604,9 +598,6 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason)
JS_TraceChildren(&trc, obj, MapAllocToTraceKind(obj->tenuredGetAllocKind()));
}
/* Forward any slot or elements pointers that were baked into jit code. */
forwardMovedBuffers(rt);
/* Resize the nursery. */
double promotionRate = trc.tenuredSize / double(allocationEnd() - start());
if (promotionRate > 0.5)

View File

@ -93,6 +93,9 @@ class Nursery
template <typename T>
JS_ALWAYS_INLINE bool getForwardedPointer(T **ref);
/* Forward a slots/elements pointer stored in an Ion frame. */
void forwardBufferPointer(HeapSlot **pSlotsElems);
private:
/*
* The start and end pointers are stored under the runtime so that we can
@ -208,12 +211,10 @@ class Nursery
size_t moveElementsToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind);
size_t moveSlotsToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind);
/* Handle relocation of slots/elements embedded in jit-code. */
/* Handle relocation of slots/elements pointers stored in Ion frames. */
void setSlotsForwardingPointer(HeapSlot *oldSlots, HeapSlot *newSlots, uint32_t nslots);
void setElementsForwardingPointer(ObjectElements *oldHeader, ObjectElements *newHeader,
uint32_t nelems);
void forwardBufferPointer(HeapSlot **pSlotsElems);
void forwardMovedBuffers(JSRuntime *rt);
/* Handle fallback marking. See the comment in MarkStoreBuffer. */
void markFallback(gc::Cell *cell);

View File

@ -753,6 +753,26 @@ MarkIonJSFrame(JSTracer *trc, const IonFrameIterator &frame)
}
}
#endif
#ifdef JSGC_GENERATIONAL
if (trc->runtime->isHeapMinorCollecting()) {
// Minor GCs may move slots/elements allocated in the nursery. Update
// any slots/elements pointers stored in this frame.
GeneralRegisterSet slotsRegs = safepoint.slotsOrElementsSpills();
spill = frame.spillBase();
for (GeneralRegisterBackwardIterator iter(safepoint.allSpills()); iter.more(); iter++) {
--spill;
if (slotsRegs.has(*iter))
trc->runtime->gcNursery.forwardBufferPointer(reinterpret_cast<HeapSlot **>(spill));
}
while (safepoint.getSlotsOrElementsSlot(&slot)) {
HeapSlot **slots = reinterpret_cast<HeapSlot **>(layout->slotRef(slot));
trc->runtime->gcNursery.forwardBufferPointer(slots);
}
}
#endif
}
static void

View File

@ -441,6 +441,7 @@ class LDefinition
enum Type {
GENERAL, // Generic, integer or pointer-width data (GPR).
OBJECT, // Pointer that may be collected as garbage (GPR).
SLOTS, // Slots/elements pointer that may be moved by minor GCs (GPR).
DOUBLE, // 64-bit point value (FPU).
#ifdef JS_NUNBOX32
// A type virtual register must be followed by a payload virtual
@ -542,9 +543,7 @@ class LDefinition
#endif
case MIRType_Slots:
case MIRType_Elements:
// When we begin allocating slots vectors from the GC, this will
// need to change to ::OBJECT.
return LDefinition::GENERAL;
return LDefinition::SLOTS;
case MIRType_Pointer:
return LDefinition::GENERAL;
case MIRType_ForkJoinSlice:
@ -1006,6 +1005,12 @@ class LSafepoint : public TempObject
GeneralRegisterSet valueRegs_;
#endif
// The subset of liveRegs which contains pointers to slots/elements.
GeneralRegisterSet slotsOrElementsRegs_;
// List of stack slots which have slots/elements pointers.
SlotList slotsOrElementsSlots_;
public:
LSafepoint()
: safepointOffset_(INVALID_SAFEPOINT_OFFSET)
@ -1033,6 +1038,38 @@ class LSafepoint : public TempObject
return gcSlots_;
}
SlotList &slotsOrElementsSlots() {
return slotsOrElementsSlots_;
}
GeneralRegisterSet slotsOrElementsRegs() const {
return slotsOrElementsRegs_;
}
void addSlotsOrElementsRegister(Register reg) {
slotsOrElementsRegs_.addUnchecked(reg);
}
bool addSlotsOrElementsSlot(uint32_t slot) {
return slotsOrElementsSlots_.append(slot);
}
bool addSlotsOrElementsPointer(LAllocation alloc) {
if (alloc.isStackSlot())
return addSlotsOrElementsSlot(alloc.toStackSlot()->slot());
JS_ASSERT(alloc.isRegister());
addSlotsOrElementsRegister(alloc.toRegister().gpr());
return true;
}
bool hasSlotsOrElementsPointer(LAllocation alloc) {
if (alloc.isRegister())
return slotsOrElementsRegs().has(alloc.toRegister().gpr());
if (alloc.isStackSlot()) {
for (size_t i = 0; i < slotsOrElementsSlots_.length(); i++) {
if (slotsOrElementsSlots_[i] == alloc.toStackSlot()->slot())
return true;
}
return false;
}
return false;
}
bool addGcPointer(LAllocation alloc) {
if (alloc.isStackSlot())
return addGcSlot(alloc.toStackSlot()->slot());

View File

@ -453,7 +453,7 @@ LinearScanAllocator::populateSafepoints()
for (uint32_t i = 0; i < vregs.numVirtualRegisters(); i++) {
LinearScanVirtualRegister *reg = &vregs[i];
if (!reg->def() || (!IsTraceable(reg) && !IsNunbox(reg)))
if (!reg->def() || (!IsTraceable(reg) && !IsSlotsOrElements(reg) && !IsNunbox(reg)))
continue;
firstSafepoint = findFirstSafepoint(reg->getInterval(0), firstSafepoint);
@ -484,7 +484,20 @@ LinearScanAllocator::populateSafepoints()
LSafepoint *safepoint = ins->safepoint();
if (!IsNunbox(reg)) {
if (IsSlotsOrElements(reg)) {
LiveInterval *interval = reg->intervalFor(inputOf(ins));
if (!interval)
continue;
LAllocation *a = interval->getAllocation();
if (a->isGeneralReg() && !ins->isCall())
safepoint->addSlotsOrElementsRegister(a->toGeneralReg()->reg());
if (isSpilledAt(interval, inputOf(ins))) {
if (!safepoint->addSlotsOrElementsSlot(reg->canonicalSpillSlot()))
return false;
}
} else if (!IsNunbox(reg)) {
JS_ASSERT(IsTraceable(reg));
LiveInterval *interval = reg->intervalFor(inputOf(ins));

View File

@ -494,6 +494,12 @@ IsNunbox(VirtualRegister *vreg)
#endif
}
static inline bool
IsSlotsOrElements(VirtualRegister *vreg)
{
return vreg->type() == LDefinition::SLOTS;
}
static inline bool
IsTraceable(VirtualRegister *reg)
{

View File

@ -286,6 +286,15 @@ AllocationIntegrityState::checkSafepointAllocation(LInstruction *ins,
}
JS_ASSERT(safepoint->hasGcPointer(alloc));
break;
case LDefinition::SLOTS:
if (populateSafepoints) {
IonSpew(IonSpew_RegAlloc, "Safepoint slots v%u i%u %s",
vreg, ins->id(), alloc.toString());
if (!safepoint->addSlotsOrElementsPointer(alloc))
return false;
}
JS_ASSERT(safepoint->hasSlotsOrElementsPointer(alloc));
break;
#ifdef JS_NUNBOX32
// Do not assert that safepoint information for nunbox types is complete,
// as if a vreg for a value's components are copied in multiple places

View File

@ -48,11 +48,13 @@ SafepointWriter::writeGcRegs(LSafepoint *safepoint)
{
GeneralRegisterSet gc = safepoint->gcRegs();
GeneralRegisterSet spilled = safepoint->liveRegs().gprs();
GeneralRegisterSet slots = safepoint->slotsOrElementsRegs();
GeneralRegisterSet valueRegs;
WriteRegisterMask(stream_, spilled.bits());
if (!spilled.empty()) {
WriteRegisterMask(stream_, gc.bits());
WriteRegisterMask(stream_, slots.bits());
#ifdef JS_PUNBOX64
valueRegs = safepoint->valueRegs();
@ -69,9 +71,11 @@ SafepointWriter::writeGcRegs(LSafepoint *safepoint)
for (GeneralRegisterForwardIterator iter(spilled); iter.more(); iter++) {
const char *type = gc.has(*iter)
? "gc"
: valueRegs.has(*iter)
? "value"
: "any";
: slots.has(*iter)
? "slots"
: valueRegs.has(*iter)
? "value"
: "any";
IonSpew(IonSpew_Safepoints, " %s reg: %s", type, (*iter).name());
}
}
@ -112,6 +116,21 @@ SafepointWriter::writeGcSlots(LSafepoint *safepoint)
slots.begin());
}
void
SafepointWriter::writeSlotsOrElementsSlots(LSafepoint *safepoint)
{
LSafepoint::SlotList &slots = safepoint->slotsOrElementsSlots();
stream_.writeUnsigned(slots.length());
for (uint32_t i = 0; i < slots.length(); i++) {
#ifdef DEBUG
IonSpew(IonSpew_Safepoints, " slots/elements slot: %d", slots[i]);
#endif
stream_.writeUnsigned(slots[i]);
}
}
void
SafepointWriter::writeValueSlots(LSafepoint *safepoint)
{
@ -289,6 +308,8 @@ SafepointWriter::encode(LSafepoint *safepoint)
writeNunboxParts(safepoint);
#endif
writeSlotsOrElementsSlots(safepoint);
endEntry();
safepoint->setOffset(safepointOffset);
}
@ -311,8 +332,10 @@ SafepointReader::SafepointReader(IonScript *script, const SafepointIndex *si)
if (allSpills_.empty()) {
gcSpills_ = allSpills_;
valueSpills_ = allSpills_;
slotsOrElementsSpills_ = allSpills_;
} else {
gcSpills_ = GeneralRegisterSet(stream_.readUnsigned());
slotsOrElementsSpills_ = GeneralRegisterSet(stream_.readUnsigned());
#ifdef JS_PUNBOX64
valueSpills_ = GeneralRegisterSet(stream_.readUnsigned());
#endif
@ -401,6 +424,7 @@ SafepointReader::advanceFromValueSlots()
nunboxSlotsRemaining_ = stream_.readUnsigned();
#else
nunboxSlotsRemaining_ = 0;
advanceFromNunboxSlots();
#endif
}
@ -423,8 +447,10 @@ PartFromStream(CompactBufferReader &stream, NunboxPartKind kind, uint32_t info)
bool
SafepointReader::getNunboxSlot(LAllocation *type, LAllocation *payload)
{
if (!nunboxSlotsRemaining_--)
if (!nunboxSlotsRemaining_--) {
advanceFromNunboxSlots();
return false;
}
uint16_t header = stream_.readFixedUint16_t();
NunboxPartKind typeKind = (NunboxPartKind)((header >> TYPE_KIND_SHIFT) & PART_KIND_MASK);
@ -437,3 +463,17 @@ SafepointReader::getNunboxSlot(LAllocation *type, LAllocation *payload)
return true;
}
void
SafepointReader::advanceFromNunboxSlots()
{
slotsOrElementsSlotsRemaining_ = stream_.readUnsigned();
}
bool
SafepointReader::getSlotsOrElementsSlot(uint32_t *slot)
{
if (!slotsOrElementsSlotsRemaining_--)
return false;
*slot = stream_.readUnsigned();
return true;
}

View File

@ -39,6 +39,8 @@ class SafepointWriter
void writeGcSlots(LSafepoint *safepoint);
void writeValueSlots(LSafepoint *safepoint);
void writeSlotsOrElementsSlots(LSafepoint *safepoint);
#ifdef JS_NUNBOX32
void writeNunboxParts(LSafepoint *safepoint);
#endif
@ -65,13 +67,16 @@ class SafepointReader
uint32_t osiCallPointOffset_;
GeneralRegisterSet gcSpills_;
GeneralRegisterSet valueSpills_;
GeneralRegisterSet slotsOrElementsSpills_;
GeneralRegisterSet allSpills_;
uint32_t nunboxSlotsRemaining_;
uint32_t slotsOrElementsSlotsRemaining_;
private:
void advanceFromGcRegs();
void advanceFromGcSlots();
void advanceFromValueSlots();
void advanceFromNunboxSlots();
bool getSlotFromBitmap(uint32_t *slot);
public:
@ -85,6 +90,9 @@ class SafepointReader
GeneralRegisterSet gcSpills() const {
return gcSpills_;
}
GeneralRegisterSet slotsOrElementsSpills() const {
return slotsOrElementsSpills_;
}
GeneralRegisterSet valueSpills() const {
return valueSpills_;
}
@ -102,6 +110,9 @@ class SafepointReader
// Returns true if a nunbox slot was read, false if there are no more
// nunbox slots.
bool getNunboxSlot(LAllocation *type, LAllocation *payload);
// Returns true if a slot was read, false if there are no more slots.
bool getSlotsOrElementsSlot(uint32_t *slot);
};
} // namespace ion

View File

@ -0,0 +1,66 @@
var sjcl = {
hash: {},
};
sjcl.bitArray = {
concat: function (a, b) {
var c = a[a.length - 1],
d = sjcl.bitArray.getPartial(c);
return d === 32 ? a.concat(b) : sjcl.bitArray.P(b, d, c | 0, a.slice(0, a.length - 1))
},
getPartial: function (a) {
return Math.round(a / 0x10000000000) || 32
}
};
sjcl.hash.sha256 = function (a) {
this.a[0] || this.w();
this.reset()
};
sjcl.hash.sha256.prototype = {
reset: function () {
this.n = this.N.slice(0);
this.i = [];
},
update: function (a) {
var b, c = this.i = sjcl.bitArray.concat(this.i, a);
return this
},
finalize: function () {
var a, b = this.i,
c = this.n;
this.C(b.splice(0, 16));
return c
},
N: [],
a: [],
w: function () {
function a(e) {
return (e - Math.floor(e)) * 0x100000000 | 0
}
var b = 0,
c = 2,
d;
a: for (; b < 64; c++) {
if (b < 8)
this.N[b] = a(Math.pow(c, 0.5));
b++
}
},
C: function (a) {
var b, c, d = a.slice(0),
e = this.n,
h = e[1],
i = e[2],
k = e[3],
n = e[7];
for (a = 0; a < 64; a++) {
b = d[a + 1 & 15];
g = b + (h & i ^ k & (h ^ i)) + (h >>> 2 ^ h >>> 13 ^ h >>> 22 ^ h << 30 ^ h << 19 ^ h << 10) | 0
}
e[0] = e[0] + g | 0;
}
};
var ax1 = [-1862726214, -1544935945, -1650904951, -1523200565, 1783959997, -1422527763, -1915825893, 67249414];
var ax2 = ax1;
for (var aix = 0; aix < 200; aix++) ax1 = (new sjcl.hash.sha256(undefined)).update(ax1, undefined).finalize();
eval("for (var aix = 0; aix < 200; aix++) ax2 = (new sjcl.hash.sha256(undefined)).update(ax2, undefined).finalize();" +
"assertEq(ax2.toString(), ax1.toString());");