mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-24 10:45:42 +00:00
Bug 888872 - Keep track of slots/elements pointers stored in Ion frames for generational GC. r=dvander,terrence
This commit is contained in:
parent
8e77fc67c6
commit
bb019cc3a4
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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));
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
66
js/src/jit-test/tests/gc/bug888463.js
Normal file
66
js/src/jit-test/tests/gc/bug888463.js
Normal 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());");
|
Loading…
Reference in New Issue
Block a user