Overhaul safepoints on X64 to correctly mark Values (bug 779390, r=pierron).

This commit is contained in:
David Anderson 2012-08-09 17:12:34 -07:00
parent 16caf5a06d
commit bc1eeba083
11 changed files with 171 additions and 120 deletions

View File

@ -290,9 +290,8 @@ MarkKind(JSTracer *trc, void **thingp, JSGCTraceKind kind)
}
void
MarkGCThingRoot(JSTracer *trc, void **thingp, const char *name)
MarkGCThingInternal(JSTracer *trc, void **thingp, const char *name)
{
JS_ROOT_MARKING_ASSERT(trc);
JS_SET_TRACING_NAME(trc, name);
JS_ASSERT(thingp);
if (!*thingp)
@ -300,6 +299,19 @@ MarkGCThingRoot(JSTracer *trc, void **thingp, const char *name)
MarkKind(trc, thingp, GetGCThingTraceKind(*thingp));
}
void
MarkGCThingRoot(JSTracer *trc, void **thingp, const char *name)
{
JS_ROOT_MARKING_ASSERT(trc);
MarkGCThingInternal(trc, thingp, name);
}
void
MarkGCThingUnbarriered(JSTracer *trc, void **thingp, const char *name)
{
MarkGCThingInternal(trc, thingp, name);
}
/*** ID Marking ***/
static inline void
@ -574,51 +586,6 @@ PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing)
static void
MarkChildren(JSTracer *trc, JSScript *script);
void
MarkThingOrValueUnbarriered(JSTracer *trc, uintptr_t *word, const char *name)
{
JS_SET_TRACING_NAME(trc, name);
#ifdef JS_PUNBOX64
// All pointers on x64 will have the top bits cleared. If those bits
// are not cleared, this must be a Value.
{
if (*word >> JSVAL_TAG_SHIFT) {
jsval_layout layout;
layout.asBits = *word;
Value v = IMPL_TO_JSVAL(layout);
gc::MarkValueInternal(trc, &v);
*word = JSVAL_TO_IMPL(v).asBits;
return;
}
}
#endif
void **thingp = reinterpret_cast<void **>(word);
MarkKind(trc, thingp, GetGCThingTraceKind(*thingp));
}
void
MarkThingOrValueRoot(JSTracer *trc, uintptr_t *word, const char *name)
{
#ifdef JS_PUNBOX64
// All pointers on x64 will have the top bits cleared. If those bits
// are not cleared, this must be a Value.
{
if (*word >> JSVAL_TAG_SHIFT) {
jsval_layout layout;
layout.asBits = *word;
Value v = IMPL_TO_JSVAL(layout);
gc::MarkValueRoot(trc, &v, name);
*word = JSVAL_TO_IMPL(v).asBits;
return;
}
}
#endif
gc::MarkGCThingRoot(trc, reinterpret_cast<void **>(word), name);
}
static void
PushMarkStack(GCMarker *gcmarker, JSScript *thing)
{

View File

@ -108,6 +108,9 @@ MarkKind(JSTracer *trc, void **thingp, JSGCTraceKind kind);
void
MarkGCThingRoot(JSTracer *trc, void **thingp, const char *name);
void
MarkGCThingUnbarriered(JSTracer *trc, void **thingp, const char *name);
/*** ID Marking ***/
void
@ -145,9 +148,6 @@ MarkValueRoot(JSTracer *trc, Value *v, const char *name);
void
MarkThingOrValueUnbarriered(JSTracer *trc, uintptr_t *word, const char *name);
void
MarkThingOrValueRoot(JSTracer *trc, uintptr_t *word, const char *name);
void
MarkValueRootRange(JSTracer *trc, size_t len, Value *vec, const char *name);

View File

@ -454,7 +454,7 @@ MarkIonJSFrame(JSTracer *trc, const IonFrameIterator &frame)
uint32 slot;
while (safepoint.getGcSlot(&slot)) {
uintptr_t *ref = layout->slotRef(slot);
gc::MarkThingOrValueRoot(trc, ref, "ion-gc-slot");
gc::MarkGCThingRoot(trc, reinterpret_cast<void **>(ref), "ion-gc-slot");
}
while (safepoint.getValueSlot(&slot)) {
@ -464,10 +464,13 @@ MarkIonJSFrame(JSTracer *trc, const IonFrameIterator &frame)
uintptr_t *spill = frame.spillBase();
GeneralRegisterSet gcRegs = safepoint.gcSpills();
GeneralRegisterSet valueRegs = safepoint.valueSpills();
for (GeneralRegisterIterator iter(safepoint.allSpills()); iter.more(); iter++) {
--spill;
if (gcRegs.has(*iter))
gc::MarkThingOrValueRoot(trc, spill, "ion-gc-spill");
gc::MarkGCThingRoot(trc, reinterpret_cast<void **>(spill), "ion-gc-spill");
else if (valueRegs.has(*iter))
gc::MarkValueRoot(trc, reinterpret_cast<Value *>(spill), "ion-value-spill");
}
#ifdef JS_NUNBOX32
@ -545,7 +548,7 @@ MarkIonExitFrame(JSTracer *trc, const IonFrameIterator &frame)
gc::MarkValueRoot(trc, reinterpret_cast<Value*>(argBase), "ion-vm-args");
break;
case VMFunction::RootCell:
gc::MarkThingOrValueRoot(trc, reinterpret_cast<uintptr_t *>(argBase), "ion-vm-args");
gc::MarkGCThingRoot(trc, reinterpret_cast<void **>(argBase), "ion-vm-args");
break;
}

View File

@ -925,9 +925,15 @@ class LSafepoint : public TempObject
// List of stack slots which have gc pointers.
SlotList gcSlots_;
#ifdef JS_NUNBOX32
// List of stack slots which have Values.
SlotList valueSlots_;
#ifdef JS_NUNBOX32
// List of registers which contain pieces of values.
NunboxList nunboxParts_;
#elif JS_PUNBOX64
// List of registers which contain values.
GeneralRegisterSet valueRegs_;
#endif
public:
@ -954,19 +960,27 @@ class LSafepoint : public TempObject
return gcSlots_;
}
#ifdef JS_NUNBOX32
bool addValueSlot(uint32 slot) {
return valueSlots_.append(slot);
}
SlotList &valueSlots() {
return valueSlots_;
}
#ifdef JS_NUNBOX32
bool addNunboxParts(LAllocation type, LAllocation payload) {
return nunboxParts_.append(NunboxEntry(type, payload));
}
NunboxList &nunboxParts() {
return nunboxParts_;
}
#elif JS_PUNBOX64
void addValueRegister(Register reg) {
valueRegs_.add(reg);
}
GeneralRegisterSet valueRegs() {
return valueRegs_;
}
#endif
bool encoded() const {
return safepointOffset_ != INVALID_SAFEPOINT_OFFSET;

View File

@ -1225,12 +1225,28 @@ LinearScanAllocator::populateSafepoints()
continue;
LAllocation *a = interval->getAllocation();
if (a->isGeneralReg() && !ins->isCall())
safepoint->addGcRegister(a->toGeneralReg()->reg());
if (a->isGeneralReg() && !ins->isCall()) {
#ifdef JS_PUNBOX64
if (reg->type() == LDefinition::BOX) {
safepoint->addValueRegister(a->toGeneralReg()->reg());
} else
#endif
{
safepoint->addGcRegister(a->toGeneralReg()->reg());
}
}
if (IsSpilledAt(interval, inputOf(ins))) {
if (!safepoint->addGcSlot(reg->canonicalSpillSlot()))
return false;
#ifdef JS_PUNBOX64
if (reg->type() == LDefinition::BOX) {
if (!safepoint->addValueSlot(reg->canonicalSpillSlot()))
return false;
} else
#endif
{
if (!safepoint->addGcSlot(reg->canonicalSpillSlot()))
return false;
}
}
#ifdef JS_NUNBOX32
} else {

View File

@ -45,21 +45,38 @@ WriteRegisterMask(CompactBufferWriter &stream, uint32 bits)
}
void
SafepointWriter::writeGcRegs(GeneralRegisterSet gc, GeneralRegisterSet spilled)
SafepointWriter::writeGcRegs(LSafepoint *safepoint)
{
GeneralRegisterSet gc = safepoint->gcRegs();
GeneralRegisterSet spilled = safepoint->liveRegs().gprs();
GeneralRegisterSet valueRegs;
WriteRegisterMask(stream_, spilled.bits());
if (!spilled.empty())
if (!spilled.empty()) {
WriteRegisterMask(stream_, gc.bits());
// gc is a subset of spilled.
#ifdef JS_PUNBOX64
valueRegs = safepoint->valueRegs();
WriteRegisterMask(stream_, valueRegs.bits());
#endif
}
// GC registers are a subset of the spilled registers.
JS_ASSERT((valueRegs.bits() & ~spilled.bits()) == 0);
JS_ASSERT((gc.bits() & ~spilled.bits()) == 0);
#ifdef DEBUG
if (IonSpewEnabled(IonSpew_Safepoints)) {
for (GeneralRegisterIterator iter(spilled); iter.more(); iter++) {
const char *type = gc.has(*iter) ? "gc" : "any";
const char *type = gc.has(*iter)
? "gc"
: valueRegs.has(*iter)
? "value"
: "any";
IonSpew(IonSpew_Safepoints, " %s reg: %s", type, (*iter).name());
}
}
#endif
}
static void
@ -81,28 +98,35 @@ MapSlotsToBitset(BitSet *set, CompactBufferWriter &stream, uint32 nslots, uint32
}
void
SafepointWriter::writeGcSlots(uint32 nslots, uint32 *slots)
SafepointWriter::writeGcSlots(LSafepoint *safepoint)
{
LSafepoint::SlotList &slots = safepoint->gcSlots();
#ifdef DEBUG
for (uint32 i = 0; i < nslots; i++)
for (uint32 i = 0; i < slots.length(); i++)
IonSpew(IonSpew_Safepoints, " gc slot: %d", slots[i]);
#endif
MapSlotsToBitset(frameSlots_, stream_, nslots, slots);
MapSlotsToBitset(frameSlots_,
stream_,
slots.length(),
slots.begin());
}
void
SafepointWriter::writeValueSlots(uint32 nslots, uint32 *slots)
SafepointWriter::writeValueSlots(LSafepoint *safepoint)
{
LSafepoint::SlotList &slots = safepoint->valueSlots();
#ifdef DEBUG
for (uint32 i = 0; i < nslots; i++)
for (uint32 i = 0; i < slots.length(); i++)
IonSpew(IonSpew_Safepoints, " gc value: %d", slots[i]);
#endif
MapSlotsToBitset(frameSlots_, stream_, nslots, slots);
MapSlotsToBitset(frameSlots_, stream_, slots.length(), slots.begin());
}
#ifdef DEBUG
#if defined(DEBUG) && defined(JS_NUNBOX32)
static void
DumpNunboxPart(const LAllocation &a)
{
@ -179,12 +203,15 @@ CanEncodeInfoInHeader(const LAllocation &a, uint32 *out)
return *out < MAX_INFO_VALUE;
}
#ifdef JS_NUNBOX32
void
SafepointWriter::writeNunboxParts(uint32 nentries, SafepointNunboxEntry *entries)
SafepointWriter::writeNunboxParts(LSafepoint *safepoint)
{
#ifdef DEBUG
LSafepoint::NunboxList &entries = safepoint->nunboxParts();
# ifdef DEBUG
if (IonSpewEnabled(IonSpew_Safepoints)) {
for (uint32 i = 0; i < nentries; i++) {
for (uint32 i = 0; i < entries.length(); i++) {
IonSpewHeader(IonSpew_Safepoints);
fprintf(IonSpewFile, " nunbox (type in ");
DumpNunboxPart(entries[i].type);
@ -193,11 +220,11 @@ SafepointWriter::writeNunboxParts(uint32 nentries, SafepointNunboxEntry *entries
fprintf(IonSpewFile, ")\n");
}
}
#endif
# endif
stream_.writeUnsigned(nentries);
stream_.writeUnsigned(entries.length());
for (size_t i = 0; i < nentries; i++) {
for (size_t i = 0; i < entries.length(); i++) {
SafepointNunboxEntry &entry = entries[i];
uint16 header = 0;
@ -226,6 +253,27 @@ SafepointWriter::writeNunboxParts(uint32 nentries, SafepointNunboxEntry *entries
stream_.writeUnsigned(payloadVal);
}
}
#endif
void
SafepointWriter::encode(LSafepoint *safepoint)
{
uint32 safepointOffset = startEntry();
JS_ASSERT(safepoint->osiCallPointOffset());
writeOsiCallPointOffset(safepoint->osiCallPointOffset());
writeGcRegs(safepoint);
writeGcSlots(safepoint);
writeValueSlots(safepoint);
#ifdef JS_NUNBOX32
writeNunboxParts(safepoint);
#endif
endEntry();
safepoint->setOffset(safepointOffset);
}
void
SafepointWriter::endEntry()
@ -242,10 +290,15 @@ SafepointReader::SafepointReader(IonScript *script, const SafepointIndex *si)
// gcSpills is a subset of allSpills.
allSpills_ = GeneralRegisterSet(stream_.readUnsigned());
if (allSpills_.empty())
if (allSpills_.empty()) {
gcSpills_ = allSpills_;
else
valueSpills_ = allSpills_;
} else {
gcSpills_ = GeneralRegisterSet(stream_.readUnsigned());
#ifdef JS_PUNBOX64
valueSpills_ = GeneralRegisterSet(stream_.readUnsigned());
#endif
}
advanceFromGcRegs();
}

View File

@ -19,6 +19,7 @@ namespace ion {
struct SafepointNunboxEntry;
class LAllocation;
class LSafepoint;
static const uint32 INVALID_SAFEPOINT_OFFSET = uint32(-1);
@ -30,15 +31,24 @@ class SafepointWriter
public:
bool init(uint32 slotCount);
private:
// A safepoint entry is written in the order these functions appear.
uint32 startEntry();
void writeOsiCallPointOffset(uint32 osiPointOffset);
void writeGcRegs(GeneralRegisterSet gc, GeneralRegisterSet spilled);
void writeGcSlots(uint32 nslots, uint32 *slots);
void writeValueSlots(uint32 nslots, uint32 *slots);
void writeNunboxParts(uint32 nentries, SafepointNunboxEntry *entries);
void writeGcRegs(LSafepoint *safepoint);
void writeGcSlots(LSafepoint *safepoint);
void writeValueSlots(LSafepoint *safepoint);
#ifdef JS_NUNBOX32
void writeNunboxParts(LSafepoint *safepoint);
#endif
void endEntry();
public:
void encode(LSafepoint *safepoint);
size_t size() const {
return stream_.length();
}
@ -55,6 +65,7 @@ class SafepointReader
uint32 currentSlotChunkNumber_;
uint32 osiCallPointOffset_;
GeneralRegisterSet gcSpills_;
GeneralRegisterSet valueSpills_;
GeneralRegisterSet allSpills_;
uint32 nunboxSlotsRemaining_;
@ -75,6 +86,9 @@ class SafepointReader
GeneralRegisterSet gcSpills() const {
return gcSpills_;
}
GeneralRegisterSet valueSpills() const {
return valueSpills_;
}
GeneralRegisterSet allSpills() const {
return allSpills_;
}

View File

@ -650,7 +650,7 @@ TraceDataRelocations(JSTracer *trc, uint8 *buffer, CompactBufferReader &reader)
InstructionIterator iter((Instruction*)(buffer+offset));
const void *ptr = js::ion::Assembler::getPtr32Target(&iter);
// No barrier needed since these are constants.
gc::MarkThingOrValueUnbarriered(trc, reinterpret_cast<uintptr_t *>(&ptr), "immgcptr");
gc::MarkGCThingUnbarriered(trc, reinterpret_cast<void **>(&ptr), "ion-masm-ptr");
}
}
@ -663,7 +663,7 @@ TraceDataRelocations(JSTracer *trc, ARMBuffer *buffer, js::Vector<BufferOffset,
const void *ptr = ion::Assembler::getPtr32Target(&iter);
// No barrier needed since these are constants.
gc::MarkThingOrValueUnbarriered(trc, reinterpret_cast<uintptr_t *>(&ptr), "immgcptr");
gc::MarkGCThingUnbarriered(trc, reinterpret_cast<void **>(&ptr), "ion-masm-ptr");
}
}

View File

@ -32,8 +32,22 @@ TraceDataRelocations(JSTracer *trc, uint8 *buffer, CompactBufferReader &reader)
size_t offset = reader.readUnsigned();
void **ptr = JSC::X86Assembler::getPointerRef(buffer + offset);
#ifdef JS_PUNBOX64
// All pointers on x64 will have the top bits cleared. If those bits
// are not cleared, this must be a Value.
uintptr_t *word = reinterpret_cast<uintptr_t *>(ptr);
if (*word >> JSVAL_TAG_SHIFT) {
jsval_layout layout;
layout.asBits = *word;
Value v = IMPL_TO_JSVAL(layout);
gc::MarkValueUnbarriered(trc, &v, "ion-masm-value");
JS_ASSERT(*word == JSVAL_TO_IMPL(v).asBits);
continue;
}
#endif
// No barrier needed since these are constants.
gc::MarkThingOrValueUnbarriered(trc, reinterpret_cast<uintptr_t *>(ptr), "imm-gc-word");
gc::MarkGCThingUnbarriered(trc, reinterpret_cast<void **>(ptr), "ion-masm-ptr");
}
}

View File

@ -261,30 +261,6 @@ CodeGeneratorShared::assignBailoutId(LSnapshot *snapshot)
return bailouts_.append(snapshot->snapshotOffset());
}
void
CodeGeneratorShared::encodeSafepoint(LSafepoint *safepoint)
{
if (safepoint->encoded())
return;
safepoint->fixupOffset(&masm);
uint32 safepointOffset = safepoints_.startEntry();
JS_ASSERT(safepoint->osiCallPointOffset());
safepoints_.writeOsiCallPointOffset(safepoint->osiCallPointOffset());
safepoints_.writeGcRegs(safepoint->gcRegs(), safepoint->liveRegs().gprs());
safepoints_.writeGcSlots(safepoint->gcSlots().length(), safepoint->gcSlots().begin());
#ifdef JS_NUNBOX32
safepoints_.writeValueSlots(safepoint->valueSlots().length(), safepoint->valueSlots().begin());
safepoints_.writeNunboxParts(safepoint->nunboxParts().length(), safepoint->nunboxParts().begin());
#endif
safepoints_.endEntry();
safepoint->setOffset(safepointOffset);
}
void
CodeGeneratorShared::encodeSafepoints()
{
@ -294,9 +270,11 @@ CodeGeneratorShared::encodeSafepoints()
{
LSafepoint *safepoint = it->safepoint();
// All safepoints must have a valid OSI displacement.
JS_ASSERT(safepoint->osiCallPointOffset());
encodeSafepoint(safepoint);
if (!safepoint->encoded()) {
safepoint->fixupOffset(&masm);
safepoints_.encode(safepoint);
}
it->resolve();
}
}
@ -475,13 +453,8 @@ bool
CodeGeneratorShared::markArgumentSlots(LSafepoint *safepoint)
{
for (size_t i = 0; i < pushedArgumentSlots_.length(); i++) {
#ifdef JS_NUNBOX32
if (!safepoint->addValueSlot(pushedArgumentSlots_[i]))
return false;
#else
if (!safepoint->addGcSlot(pushedArgumentSlots_[i]))
return false;
#endif
}
return true;
}

View File

@ -169,9 +169,6 @@ class CodeGeneratorShared : public LInstructionVisitor
// error (the code generator may use a slower bailout mechanism).
bool assignBailoutId(LSnapshot *snapshot);
// Encode a safepoint in the safepoint stream.
void encodeSafepoint(LSafepoint *safepoint);
// Encode all encountered safepoints in CG-order, and resolve |indices| for
// safepoint offsets.
void encodeSafepoints();