IonMonkey read/write barrier support. (Bug 701990, r=cdleary)

This commit is contained in:
Sean Stangl 2011-11-18 14:46:30 -08:00
parent 790edab73d
commit d045bd5143
23 changed files with 259 additions and 27 deletions

View File

@ -424,6 +424,7 @@ class ReadBarriered
T *value;
public:
ReadBarriered() : value(NULL) {}
ReadBarriered(T *value) : value(value) {}
T *get() const {

View File

@ -173,15 +173,15 @@ IonCompartment::mark(JSTracer *trc, JSCompartment *compartment)
// These must be available if we could be running JIT code.
if (enterJIT_)
MarkIonCode(trc, enterJIT_, "enterJIT");
MarkRoot(trc, enterJIT_, "enterJIT");
// These need to be here until we can figure out how to make the GC
// scan these references inside the code generator itself.
if (bailoutHandler_)
MarkIonCode(trc, bailoutHandler_, "bailoutHandler");
MarkRoot(trc, bailoutHandler_, "bailoutHandler");
for (size_t i = 0; i < bailoutTables_.length(); i++) {
if (bailoutTables_[i])
MarkIonCode(trc, bailoutTables_[i], "bailoutTable");
MarkRoot(trc, bailoutTables_[i], "bailoutTable");
}
// functionWrappers_ are not marked because this is a WeakCache of VM
@ -321,6 +321,40 @@ IonCode::finalize(JSContext *cx)
pool_->release();
}
void
IonCode::readBarrier(IonCode *code)
{
#ifdef JSGC_INCREMENTAL
if (!code)
return;
JSCompartment *comp = code->compartment();
if (comp->needsBarrier())
MarkIonCodeUnbarriered(comp->barrierTracer(), code, "ioncode read barrier");
#endif
}
void
IonCode::writeBarrierPre(IonCode *code)
{
#ifdef JSGC_INCREMENTAL
if (!code)
return;
JSCompartment *comp = code->compartment();
if (comp->needsBarrier())
MarkIonCodeUnbarriered(comp->barrierTracer(), code, "ioncode write barrier");
#endif
}
void
IonCode::writeBarrierPost(IonCode *code, void *addr)
{
#ifdef JSGC_INCREMENTAL
// Nothing to do.
#endif
}
IonScript::IonScript()
: method_(NULL),
deoptTable_(NULL),

View File

@ -2156,6 +2156,14 @@ IonBuilder::jsop_setgname(JSAtom *atom)
MStoreSlot *store = MStoreSlot::New(slots, shape->slot - globalObj->numFixedSlots(), value);
current->add(store);
#ifdef JSGC_INCREMENTAL
// Determine whether write barrier is required.
if (cx->compartment->needsBarrier() &&
(!propertyTypes || propertyTypes->needsBarrier(cx))) {
store->setNeedsBarrier(true);
}
#endif
if (!resumeAfter(store))
return false;

View File

@ -128,6 +128,11 @@ class IonCode : public gc::Cell
// object can be allocated, NULL is returned. On failure, |pool| is
// automatically released, so the code may be freed.
static IonCode *New(JSContext *cx, uint8 *code, uint32 bufferSize, JSC::ExecutablePool *pool);
public:
static void readBarrier(IonCode *code);
static void writeBarrierPre(IonCode *code);
static void writeBarrierPost(IonCode *code, void *addr);
};
#define ION_DISABLED_SCRIPT ((IonScript *)0x1)
@ -138,10 +143,10 @@ class SnapshotWriter;
struct IonScript
{
// Code pointer containing the actual method.
IonCode *method_;
HeapPtr<IonCode> method_;
// Deoptimization table used by this method.
IonCode *deoptTable_;
HeapPtr<IonCode> deoptTable_;
// Offset from the start of the code buffer to its snapshot buffer.
uint32 snapshots_;

View File

@ -61,7 +61,7 @@ struct VMFunction;
class IonCompartment
{
typedef WeakCache<const VMFunction *, IonCode *> VMWrapperMap;
typedef WeakCache<const VMFunction *, ReadBarriered<IonCode> > VMWrapperMap;
friend class IonActivation;
@ -71,17 +71,17 @@ class IonCompartment
IonActivation *active_;
// Trampoline for entering JIT code.
IonCode *enterJIT_;
ReadBarriered<IonCode> enterJIT_;
// Vector mapping frame class sizes to bailout tables.
js::Vector<IonCode *, 4, SystemAllocPolicy> bailoutTables_;
js::Vector<ReadBarriered<IonCode>, 4, SystemAllocPolicy> bailoutTables_;
// Generic bailout table; used if the bailout table overflows.
IonCode *bailoutHandler_;
ReadBarriered<IonCode> bailoutHandler_;
// Argument-rectifying thunk, in the case of insufficient arguments passed
// to a function call site. Pads with |undefined|.
IonCode *argumentsRectifier_;
ReadBarriered<IonCode> argumentsRectifier_;
// Map VMFunction addresses to the IonCode of the wrapper.
VMWrapperMap *functionWrappers_;
@ -137,7 +137,7 @@ class IonCompartment
if (!enterJIT_)
return NULL;
}
return enterJIT_->as<EnterIonCode>();
return enterJIT_.get()->as<EnterIonCode>();
}
IonActivation *activation() const {

View File

@ -753,6 +753,33 @@ class LStoreSlotT : public LInstructionHelper<0, 2, 0>
}
};
// Mark a Value if it is a GCThing.
class LWriteBarrierV : public LInstructionHelper<0, BOX_PIECES, 0>
{
public:
LIR_HEADER(WriteBarrierV);
LWriteBarrierV()
{ }
static const size_t Input = 0;
};
// Mark a GCThing.
class LWriteBarrierT : public LInstructionHelper<0, 1, 0>
{
public:
LIR_HEADER(WriteBarrierT);
LWriteBarrierT(const LAllocation &value) {
setOperand(0, value);
}
const LAllocation *value() {
return getOperand(0);
}
};
// Guard that a value is in a TypeSet.
class LTypeBarrier : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 1>
{

View File

@ -80,6 +80,8 @@
_(StoreSlotV) \
_(StoreSlotT) \
_(GuardShape) \
_(WriteBarrierV) \
_(WriteBarrierT) \
_(TypeBarrier)
#if defined(JS_CPU_X86)

View File

@ -557,11 +557,54 @@ LIRGenerator::visitLoadSlot(MLoadSlot *ins)
return true;
}
bool
LIRGenerator::emitWriteBarrier(MInstruction *ins, MDefinition *input)
{
#ifdef JSGC_INCREMENTAL
JS_ASSERT(GetIonContext()->cx->compartment->needsBarrier());
LInstruction *barrier;
switch (input->type()) {
// Possible GCThings.
case MIRType_Value:
barrier = new LWriteBarrierV;
if (!useBox(barrier, LWriteBarrierV::Input, input))
return false;
add(barrier, ins);
break;
// Known GCThings.
case MIRType_String:
case MIRType_Object:
add(new LWriteBarrierT(useRegisterOrConstant(input)), ins);
break;
// Known non-GCThings.
case MIRType_Undefined:
case MIRType_Null:
case MIRType_Boolean:
case MIRType_Int32:
case MIRType_Double:
break;
// Nonsensical input.
default:
JS_NOT_REACHED("Unexpected MIRType.");
}
#endif
return true;
}
bool
LIRGenerator::visitStoreSlot(MStoreSlot *ins)
{
LInstruction *lir;
#ifdef JSGC_INCREMENTAL
if (ins->needsBarrier() && !emitWriteBarrier(ins, ins->value()))
return false;
#endif
switch (ins->value()->type()) {
case MIRType_Value:
lir = new LStoreSlotV(useRegister(ins->slots()));

View File

@ -95,6 +95,9 @@ class LIRGenerator : public LIRGeneratorSpecific
// Free argument slots following a function call.
void freeArguments(uint32 argc);
// Emit an LWriteBarrierV or LWriteBarrierT if required, based on MIRType.
bool emitWriteBarrier(MInstruction *ins, MDefinition *input);
public:
bool visitInstruction(MInstruction *ins);
bool visitBlock(MBasicBlock *block);

View File

@ -1643,11 +1643,13 @@ class MStoreSlot
{
uint32 slot_;
MIRType slotType_;
bool needsBarrier_;
MStoreSlot(MDefinition *slots, uint32 slot, MDefinition *value)
: MBinaryInstruction(slots, value),
slot_(slot),
slotType_(MIRType_None)
slotType_(MIRType_None),
needsBarrier_(false)
{
JS_ASSERT(slots->type() == MIRType_Slots);
}
@ -1681,6 +1683,12 @@ class MStoreSlot
bool congruentTo(MDefinition * const &ins) const {
return false;
}
bool needsBarrier() const {
return needsBarrier_;
}
void setNeedsBarrier(bool needsBarrier) {
needsBarrier_ = needsBarrier;
}
};
// Given a value, guard that the value is in a particular TypeSet, then returns

View File

@ -211,7 +211,7 @@ Assembler::TraceJumpRelocations(JSTracer *trc, IonCode *code, CompactBufferReade
RelocationIterator iter(reader);
while (iter.read()) {
IonCode *child = CodeFromJump(code->raw() + iter.offset());
MarkIonCode(trc, child, "rel32");
MarkIonCodeUnbarriered(trc, child, "rel32");
};
#endif
}
@ -244,7 +244,7 @@ Assembler::trace(JSTracer *trc)
for (size_t i = 0; i < jumps_.length(); i++) {
RelativePatch &rp = jumps_[i];
if (rp.kind == Relocation::CODE)
MarkIonCode(trc, IonCode::FromExecutable((uint8 *)rp.target), "masmrel32");
MarkIonCodeUnbarriered(trc, IonCode::FromExecutable((uint8 *)rp.target), "masmrel32");
}
#endif
}

View File

@ -81,7 +81,7 @@ AssemblerX86Shared::trace(JSTracer *trc)
for (size_t i = 0; i < jumps_.length(); i++) {
RelativePatch &rp = jumps_[i];
if (rp.kind == Relocation::CODE)
MarkIonCode(trc, IonCode::FromExecutable((uint8 *)rp.target), "masmrel32");
MarkIonCodeUnbarriered(trc, IonCode::FromExecutable((uint8 *)rp.target), "masmrel32");
}
if (dataRelocations_.length()) {
CompactBufferReader reader(dataRelocations_);

View File

@ -173,7 +173,7 @@ Assembler::TraceJumpRelocations(JSTracer *trc, IonCode *code, CompactBufferReade
RelocationIterator iter(reader);
while (iter.read()) {
IonCode *child = CodeFromJump(code, code->raw() + iter.offset());
MarkIonCode(trc, child, "rel32");
MarkIonCodeUnbarriered(trc, child, "rel32");
}
}

View File

@ -296,6 +296,35 @@ CodeGeneratorX64::visitStackArg(LStackArg *arg)
return true;
}
bool
CodeGeneratorX64::visitWriteBarrierV(LWriteBarrierV *barrier)
{
// TODO: Perform C++ call to some WriteBarrier stub.
// For now, we just guard and breakpoint on failure.
const ValueOperand value = ToValue(barrier, LWriteBarrierV::Input);
Label skipBarrier;
masm.branchTestGCThing(Assembler::NotEqual, value, &skipBarrier);
{
masm.breakpoint();
masm.breakpoint();
}
masm.bind(&skipBarrier);
return true;
}
bool
CodeGeneratorX64::visitWriteBarrierT(LWriteBarrierT *barrier)
{
// TODO: Perform C++ call to some WriteBarrier stub.
// For now, we just breakpoint.
masm.breakpoint();
masm.breakpoint();
return true;
}
bool
CodeGeneratorX64::visitGuardShape(LGuardShape *guard)
{

View File

@ -74,6 +74,8 @@ class CodeGeneratorX64 : public CodeGeneratorX86Shared
bool visitLoadSlotT(LLoadSlotT *load);
bool visitStoreSlotV(LStoreSlotV *store);
bool visitStoreSlotT(LStoreSlotT *store);
bool visitWriteBarrierV(LWriteBarrierV *barrier);
bool visitWriteBarrierT(LWriteBarrierT *barrier);
bool visitGuardShape(LGuardShape *guard);
};

View File

@ -167,6 +167,12 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
cmpl(tag, Imm32(JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET));
return cond == Equal ? BelowOrEqual : Above;
}
Condition testGCThing(Condition cond, Register tag) {
JS_ASSERT(cond == Equal || cond == NotEqual);
cmpl(tag, Imm32(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET));
return cond == Equal ? AboveOrEqual : Below;
}
Condition testUndefined(Condition cond, const ValueOperand &src) {
splitTag(src, ScratchReg);
return testUndefined(cond, ScratchReg);
@ -197,6 +203,10 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
splitTag(src, ScratchReg);
return testObject(cond, ScratchReg);
}
Condition testGCThing(Condition cond, const ValueOperand &src) {
splitTag(src, ScratchReg);
return testGCThing(cond, ScratchReg);
}
void cmpPtr(const Register &lhs, const ImmWord rhs) {
JS_ASSERT(lhs != ScratchReg);
@ -338,6 +348,10 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
cond = testObject(cond, src);
j(cond, label);
}
void branchTestGCThing(Condition cond, const ValueOperand &src, Label *label) {
cond = testGCThing(cond, src);
j(cond, label);
}
Condition testError(Condition cond, const ValueOperand &src) {
splitTag(src, ScratchReg);
return testError(cond, ScratchReg);

View File

@ -91,7 +91,7 @@ Assembler::TraceJumpRelocations(JSTracer *trc, IonCode *code, CompactBufferReade
RelocationIterator iter(reader);
while (iter.read()) {
IonCode *child = CodeFromJump(code->raw() + iter.offset());
MarkIonCode(trc, child, "rel32");
MarkIonCodeUnbarriered(trc, child, "rel32");
}
}

View File

@ -322,6 +322,35 @@ CodeGeneratorX86::visitStoreSlotT(LStoreSlotT *store)
return true;
}
bool
CodeGeneratorX86::visitWriteBarrierV(LWriteBarrierV *barrier)
{
// TODO: Perform C++ call to some WriteBarrier stub.
// For now, we just guard and breakpoint on failure.
const ValueOperand value = ToValue(barrier, LWriteBarrierV::Input);
Label skipBarrier;
masm.branchTestGCThing(Assembler::NotEqual, value, &skipBarrier);
{
masm.breakpoint();
masm.breakpoint();
}
masm.bind(&skipBarrier);
return true;
}
bool
CodeGeneratorX86::visitWriteBarrierT(LWriteBarrierT *barrier)
{
// TODO: Perform C++ call to some WriteBarrier stub.
// For now, we just breakpoint.
masm.breakpoint();
masm.breakpoint();
return true;
}
bool
CodeGeneratorX86::visitGuardShape(LGuardShape *guard)
{

View File

@ -99,13 +99,15 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared
bool visitLoadSlotT(LLoadSlotT *load);
bool visitStoreSlotV(LStoreSlotV *store);
bool visitStoreSlotT(LStoreSlotT *store);
bool visitWriteBarrierV(LWriteBarrierV *barrier);
bool visitWriteBarrierT(LWriteBarrierT *barrier);
bool visitGuardShape(LGuardShape *guard);
};
typedef CodeGeneratorX86 CodeGeneratorSpecific;
} // ion
} // js
} // namespace ion
} // namespace js
#endif // jsion_codegen_x86_h__

View File

@ -203,6 +203,11 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
cmpl(tag, ImmTag(JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET));
return cond == Equal ? BelowOrEqual : Above;
}
Condition testGCThing(Condition cond, const Register &tag) {
JS_ASSERT(cond == Equal || cond == NotEqual);
cmpl(tag, ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET));
return cond == Equal ? AboveOrEqual : Below;
}
Condition testError(Condition cond, const Register &tag) {
JS_ASSERT(cond == Equal || cond == NotEqual);
cmpl(tag, ImmType(JSVAL_TYPE_MAGIC));
@ -235,6 +240,9 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
Condition testNumber(Condition cond, const ValueOperand &value) {
return testNumber(cond, value.typeReg());
}
Condition testGCThing(Condition cond, const ValueOperand &value) {
return testGCThing(cond, value.typeReg());
}
void cmpPtr(const Register &lhs, const ImmWord rhs) {
cmpl(lhs, Imm32(rhs.value));
@ -325,6 +333,11 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
cond = testNumber(cond, t);
j(cond, label);
}
template <typename T>
void branchTestGCThing(Condition cond, const T &t, Label *label) {
cond = testGCThing(cond, t);
j(cond, label);
}
void unboxInt32(const ValueOperand &operand, const Register &dest) {
movl(operand.payloadReg(), dest);

View File

@ -232,7 +232,7 @@ MarkShape(JSTracer *trc, const MarkablePtr<const Shape> &shape, const char *name
}
void
MarkIonCode(JSTracer *trc, ion::IonCode *code, const char *name)
MarkIonCodeUnbarriered(JSTracer *trc, ion::IonCode *code, const char *name)
{
JS_ASSERT(trc);
JS_ASSERT(code);
@ -240,6 +240,12 @@ MarkIonCode(JSTracer *trc, ion::IonCode *code, const char *name)
Mark(trc, code);
}
void
MarkIonCode(JSTracer *trc, const MarkablePtr<ion::IonCode> &code, const char *name)
{
MarkIonCodeUnbarriered(trc, code.value, name);
}
void
MarkTypeObjectUnbarriered(JSTracer *trc, types::TypeObject *type, const char *name)
{
@ -610,7 +616,7 @@ MarkRoot(JSTracer *trc, JSXML *thing, const char *name)
void
MarkRoot(JSTracer *trc, ion::IonCode *code, const char *name)
{
MarkIonCode(trc, code, name);
MarkIonCodeUnbarriered(trc, code, name);
}
void

View File

@ -64,9 +64,6 @@ MarkAtom(JSTracer *trc, JSAtom *str, const char *name);
void
MarkObjectUnbarriered(JSTracer *trc, JSObject *obj, const char *name);
void
MarkIonCode(JSTracer *trc, ion::IonCode *code, const char *name);
void
MarkObject(JSTracer *trc, const MarkablePtr<JSObject> &obj, const char *name);
@ -88,6 +85,12 @@ MarkShapeUnbarriered(JSTracer *trc, const Shape *shape, const char *name);
void
MarkShape(JSTracer *trc, const MarkablePtr<const Shape> &shape, const char *name);
void
MarkIonCodeUnbarriered(JSTracer *trc, ion::IonCode *code, const char *name);
void
MarkIonCode(JSTracer *trc, const MarkablePtr<ion::IonCode> &code, const char *name);
void
MarkTypeObjectUnbarriered(JSTracer *trc, types::TypeObject *type, const char *name);
@ -224,7 +227,7 @@ Mark(JSTracer *trc, const MarkablePtr<JSObject> &o, const char *name)
}
inline void
Mark(JSTracer *trc, ion::IonCode *code, const char *name)
Mark(JSTracer *trc, const MarkablePtr<ion::IonCode> &code, const char *name)
{
MarkIonCode(trc, code, name);
}

View File

@ -281,14 +281,17 @@ typedef uint64 JSValueShiftedTag;
#define JSVAL_TYPE_TO_TAG(type) ((JSValueTag)(JSVAL_TAG_MAX_DOUBLE | (type)))
#define JSVAL_TYPE_TO_SHIFTED_TAG(type) (((uint64)JSVAL_TYPE_TO_TAG(type)) << JSVAL_TAG_SHIFT)
#define JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET JSVAL_TAG_NULL
#define JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET JSVAL_TAG_OBJECT
#define JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET JSVAL_TAG_INT32
#define JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET JSVAL_TAG_STRING
#define JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET JSVAL_SHIFTED_TAG_NULL
#define JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET JSVAL_SHIFTED_TAG_OBJECT
#define JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET JSVAL_SHIFTED_TAG_UNDEFINED
#define JSVAL_LOWER_INCL_SHIFTED_TAG_OF_PTR_PAYLOAD_SET JSVAL_SHIFTED_TAG_MAGIC
#define JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET JSVAL_SHIFTED_TAG_STRING
#define JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET JSVAL_TAG_INT32
#endif /* JS_BITS_PER_WORD */
typedef enum JSWhyMagic