Bug 880816 - Mark IonCode embedded pointers during MinorGC if they may be Nursery things; r=bhackett

--HG--
extra : rebase_source : 2c2e6320671e38fde041fcb350730e6bd7f86f8e
This commit is contained in:
Terrence Cole 2013-06-10 11:16:57 -07:00
parent d4696b6e54
commit ec525ea7a7
17 changed files with 121 additions and 72 deletions

View File

@ -399,6 +399,7 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
parser.sct = &sct;
JS_ASSERT(fun);
JS_ASSERT(fun->isTenured());
fun->setArgCount(formals.length());

View File

@ -489,6 +489,8 @@ FunctionBox::FunctionBox(JSContext *cx, ObjectBox* traceListHead, JSFunction *fu
insideUseAsm(outerpc && outerpc->useAsmOrInsideUseAsm()),
funCxFlags()
{
JS_ASSERT(fun->isTenured());
if (!outerpc) {
inWith = false;

View File

@ -497,6 +497,9 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason)
{
JS_AbortIfWrongThread(rt);
if (rt->mainThread.suppressGC)
return;
if (!isEnabled())
return;

View File

@ -389,7 +389,8 @@ HandleDynamicLinkFailure(JSContext *cx, CallArgs args, AsmJSModule &module, Hand
const jschar *chars = src->chars().get();
RootedFunction fun(cx, NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED,
cx->global(), name));
cx->global(), name, JSFunction::FinalizeKind,
TenuredObject));
if (!fun)
return false;

View File

@ -5188,27 +5188,6 @@ CodeGenerator::generate()
return !masm.oom();
}
#ifdef JSGC_GENERATIONAL
/*
* IonScripts normally live as long as their owner JSScript; however, they can
* occasionally get destroyed outside the context of a GC by FinishInvalidationOf.
* Because of this case, we cannot use the normal store buffer to guard them.
* Instead we use the generic buffer to mark the owner script, which will mark the
* IonScript's fields, if it is still alive.
*/
class IonScriptRefs : public gc::BufferableRef
{
JSScript *script_;
public:
IonScriptRefs(JSScript *script) : script_(script) {}
virtual bool match(void *location) { return false; }
virtual void mark(JSTracer *trc) {
gc::MarkScriptUnbarriered(trc, &script_, "script for IonScript");
}
};
#endif // JSGC_GENERATIONAL
bool
CodeGenerator::link()
{
@ -5306,9 +5285,6 @@ CodeGenerator::link()
ionScript->copySnapshots(&snapshots_);
if (graph.numConstants())
ionScript->copyConstants(graph.constantPool());
#ifdef JSGC_GENERATIONAL
cx->runtime()->gcStoreBuffer.putGeneric(IonScriptRefs(script));
#endif
JS_ASSERT(graph.mir().numScripts() > 0);
ionScript->copyScriptEntries(graph.mir().scripts());
if (callTargets.length() > 0)

View File

@ -8,11 +8,34 @@
#define jsion_compileinfo_inl_h__
#include "CompileInfo.h"
#include "jsgcinlines.h"
#include "jsscriptinlines.h"
using namespace js;
using namespace ion;
CompileInfo::CompileInfo(JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing,
ExecutionMode executionMode)
: script_(script), fun_(fun), osrPc_(osrPc), constructing_(constructing),
executionMode_(executionMode)
{
JS_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY);
// The function here can flow in from anywhere so look up the canonical function to ensure that
// we do not try to embed a nursery pointer in jit-code.
if (fun_) {
fun_ = fun_->nonLazyScript()->function();
JS_ASSERT(fun_->isTenured());
}
nimplicit_ = StartArgSlot(script, fun) /* scope chain and argument obj */
+ (fun ? 1 : 0); /* this */
nargs_ = fun ? fun->nargs : 0;
nlocals_ = script->nfixed;
nstack_ = script->nslots - script->nfixed;
nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_;
}
const char *
CompileInfo::filename() const
{

View File

@ -40,18 +40,7 @@ class CompileInfo
{
public:
CompileInfo(JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing,
ExecutionMode executionMode)
: script_(script), fun_(fun), osrPc_(osrPc), constructing_(constructing),
executionMode_(executionMode)
{
JS_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY);
nimplicit_ = StartArgSlot(script, fun) /* scope chain and argument obj */
+ (fun ? 1 : 0); /* this */
nargs_ = fun ? fun->nargs : 0;
nlocals_ = script->nfixed;
nstack_ = script->nslots - script->nfixed;
nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_;
}
ExecutionMode executionMode);
CompileInfo(unsigned nlocals, ExecutionMode executionMode)
: script_(NULL), fun_(NULL), osrPc_(NULL), constructing_(false),

View File

@ -526,7 +526,7 @@ IonCode::writeBarrierPre(IonCode *code)
void
IonCode::writeBarrierPost(IonCode *code, void *addr)
{
#ifdef JSGC_INCREMENTAL
#ifdef JSGC_GENERATIONAL
// Nothing to do.
#endif
}

View File

@ -439,8 +439,9 @@ IsCacheableDOMProxy(JSObject *obj)
}
static void
GeneratePrototypeGuards(JSContext *cx, MacroAssembler &masm, JSObject *obj, JSObject *holder,
Register objectReg, Register scratchReg, Label *failures)
GeneratePrototypeGuards(JSContext *cx, IonScript *ion, MacroAssembler &masm, JSObject *obj,
JSObject *holder, Register objectReg, Register scratchReg,
Label *failures)
{
JS_ASSERT(obj != holder);
@ -449,7 +450,8 @@ GeneratePrototypeGuards(JSContext *cx, MacroAssembler &masm, JSObject *obj, JSOb
// use objectReg in the rest of this function.
masm.loadPtr(Address(objectReg, JSObject::offsetOfType()), scratchReg);
Address proto(scratchReg, offsetof(types::TypeObject, proto));
masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->getProto()), failures);
masm.branchPtr(Assembler::NotEqual, proto,
ImmMaybeNurseryPtr(ion->method(), obj->getProto()), failures);
}
JSObject *pobj = IsCacheableDOMProxy(obj)
@ -460,7 +462,7 @@ GeneratePrototypeGuards(JSContext *cx, MacroAssembler &masm, JSObject *obj, JSOb
while (pobj != holder) {
if (pobj->hasUncacheableProto()) {
JS_ASSERT(!pobj->hasSingletonType());
masm.movePtr(ImmGCPtr(pobj), scratchReg);
masm.movePtr(ImmMaybeNurseryPtr(ion->method(), pobj), scratchReg);
Address objType(scratchReg, JSObject::offsetOfType());
masm.branchPtr(Assembler::NotEqual, objType, ImmGCPtr(pobj->type()), failures);
}
@ -699,9 +701,10 @@ GenerateDOMProxyChecks(JSContext *cx, MacroAssembler &masm, JSObject *obj,
}
static void
GenerateReadSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
JSObject *obj, PropertyName *name, JSObject *holder, Shape *shape,
Register object, TypedOrValueRegister output, Label *failures = NULL)
GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
IonCache::StubAttacher &attacher, JSObject *obj, PropertyName *name,
JSObject *holder, Shape *shape, Register object, TypedOrValueRegister output,
Label *failures = NULL)
{
// If there's a single jump to |failures|, we can patch the shape guard
// jump directly. Otherwise, jump to the end of the stub, so there's a
@ -759,13 +762,13 @@ GenerateReadSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &at
Register holderReg;
if (obj != holder) {
// Note: this may clobber the object register if it's used as scratch.
GeneratePrototypeGuards(cx, masm, obj, holder, object, scratchReg,
GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg,
failures);
if (holder) {
// Guard on the holder's shape.
holderReg = scratchReg;
masm.movePtr(ImmGCPtr(holder), holderReg);
masm.movePtr(ImmMaybeNurseryPtr(ion->method(), holder), holderReg);
masm.branchPtr(Assembler::NotEqual,
Address(holderReg, JSObject::offsetOfShape()),
ImmGCPtr(holder->lastProperty()),
@ -827,10 +830,10 @@ GenerateReadSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &at
}
static bool
GenerateCallGetter(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
JSObject *obj, PropertyName *name, JSObject *holder, HandleShape shape,
RegisterSet &liveRegs, Register object, TypedOrValueRegister output,
void *returnAddr, jsbytecode *pc)
GenerateCallGetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
IonCache::StubAttacher &attacher, JSObject *obj, PropertyName *name,
JSObject *holder, HandleShape shape, RegisterSet &liveRegs, Register object,
TypedOrValueRegister output, void *returnAddr, jsbytecode *pc)
{
// Initial shape check.
Label stubFailure;
@ -845,11 +848,11 @@ GenerateCallGetter(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &
// Note: this may clobber the object register if it's used as scratch.
if (obj != holder)
GeneratePrototypeGuards(cx, masm, obj, holder, object, scratchReg, &stubFailure);
GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, &stubFailure);
// Guard on the holder's shape.
Register holderReg = scratchReg;
masm.movePtr(ImmGCPtr(holder), holderReg);
masm.movePtr(ImmMaybeNurseryPtr(ion->method(), holder), holderReg);
masm.branchPtr(Assembler::NotEqual,
Address(holderReg, JSObject::offsetOfShape()),
ImmGCPtr(holder->lastProperty()),
@ -1013,7 +1016,7 @@ GetPropertyIC::attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSOb
{
RepatchStubAppender attacher(*this);
MacroAssembler masm(cx);
GenerateReadSlot(cx, masm, attacher, obj, name(), holder, shape, object(), output());
GenerateReadSlot(cx, ion, masm, attacher, obj, name(), holder, shape, object(), output());
const char *attachKind = "non idempotent reading";
if (idempotent())
attachKind = "idempotent reading";
@ -1147,7 +1150,7 @@ GetPropertyIC::attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj,
masm.setFramePushed(ion->frameSize());
RepatchStubAppender attacher(*this);
if (!GenerateCallGetter(cx, masm, attacher, obj, name(), holder, shape, liveRegs_,
if (!GenerateCallGetter(cx, ion, masm, attacher, obj, name(), holder, shape, liveRegs_,
object(), output(), returnAddr, pc))
{
return false;
@ -1621,7 +1624,7 @@ ParallelGetPropertyIC::attachReadSlot(LockedJSContext &cx, IonScript *ion,
// Ready to generate the read slot stub.
DispatchStubPrepender attacher(*this);
MacroAssembler masm(cx);
GenerateReadSlot(cx, masm, attacher, obj, name(), holder, shape, object(), output());
GenerateReadSlot(cx, ion, masm, attacher, obj, name(), holder, shape, object(), output());
const char *attachKind = "parallel non-idempotent reading";
if (idempotent())
@ -1794,9 +1797,9 @@ SetPropertyIC::attachSetterCall(JSContext *cx, IonScript *ion,
// Generate prototype/shape guards.
if (obj != holder)
GeneratePrototypeGuards(cx, masm, obj, holder, object(), scratchReg, &protoFailure);
GeneratePrototypeGuards(cx, ion, masm, obj, holder, object(), scratchReg, &protoFailure);
masm.movePtr(ImmGCPtr(holder), scratchReg);
masm.movePtr(ImmMaybeNurseryPtr(ion->method(), holder), scratchReg);
masm.branchPtr(Assembler::NotEqual,
Address(scratchReg, JSObject::offsetOfShape()),
ImmGCPtr(holder->lastProperty()),
@ -2185,7 +2188,7 @@ GetElementIC::attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj,
masm.branchTestValue(Assembler::NotEqual, val, idval, &failures);
RepatchStubAppender attacher(*this);
GenerateReadSlot(cx, masm, attacher, obj, name, holder, shape, object(), output(),
GenerateReadSlot(cx, ion, masm, attacher, obj, name, holder, shape, object(), output(),
&failures);
return linkAndAttachStub(cx, masm, attacher, ion, "property");
@ -2954,7 +2957,7 @@ NameIC::attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, JSObject
masm.setFramePushed(ion->frameSize());
RepatchStubAppender attacher(*this);
if (!GenerateCallGetter(cx, masm, attacher, obj, name(), holder, shape, liveRegs_,
if (!GenerateCallGetter(cx, ion, masm, attacher, obj, name(), holder, shape, liveRegs_,
scopeChainReg(), outputReg(), returnAddr, pc))
{
return false;

View File

@ -121,6 +121,24 @@ struct ImmGCPtr
explicit ImmGCPtr(const gc::Cell *ptr) : value(reinterpret_cast<uintptr_t>(ptr))
{
JS_ASSERT(!IsPoisonedPtr(ptr));
JS_ASSERT_IF(ptr, ptr->isTenured());
}
protected:
ImmGCPtr() : value(0) {}
};
// Used for immediates which require relocation and may be traced during minor GC.
struct ImmMaybeNurseryPtr : public ImmGCPtr
{
explicit ImmMaybeNurseryPtr(IonCode *code, gc::Cell *ptr)
{
this->value = reinterpret_cast<uintptr_t>(ptr);
JS_ASSERT(!IsPoisonedPtr(ptr));
#ifdef JSGC_GENERATIONAL
if (ptr && ptr->runtime()->gcNursery.isInside(ptr))
ptr->runtime()->gcStoreBuffer.putWholeCell(code);
#endif
}
};

View File

@ -35,8 +35,10 @@ class AssemblerX86Shared
bool enoughMemory_;
void writeDataRelocation(const Value &val) {
if (val.isMarkable())
if (val.isMarkable()) {
JS_ASSERT(static_cast<gc::Cell*>(val.toGCThing())->isTenured());
dataRelocations_.writeUnsigned(masm.currentOffset());
}
}
void writeDataRelocation(const ImmGCPtr &ptr) {
if (ptr.value)

View File

@ -0,0 +1,30 @@
var lfcode = new Array();
lfcode.push("const baz = 'bar';");
lfcode.push("2");
lfcode.push("{ function foo() {} }");
lfcode.push("evaluate('\
var INVALIDATE_MODES = INVALIDATE_MODE_STRINGS.map(s => ({mode: s}));\
function range(n, m) {}\
function seq_scan(array, f) {}\
function assertStructuralEq(e1, e2) {}\
for (var i = 0, l = a.length; i < l; i++) {}\
');");
lfcode.push("for (var x of Set(Object.getOwnPropertyNames(this))) {}");
var lfRunTypeId = -1;
while (true) {
var file = lfcode.shift(); if (file == undefined) { break; }
loadFile(file)
}
function loadFile(lfVarx) {
try {
if (lfVarx.substr(-3) == ".js") {}
if (!isNaN(lfVarx)) {
lfRunTypeId = parseInt(lfVarx);
} else {
switch (lfRunTypeId) {
case 2: new Function(lfVarx)(); break;
default: evaluate(lfVarx); break;
}
}
} catch (lfVare) {}
}

View File

@ -5499,7 +5499,8 @@ JS::CompileFunction(JSContext *cx, HandleObject obj, CompileOptions options,
return NULL;
}
RootedFunction fun(cx, NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED, obj, funAtom));
RootedFunction fun(cx, NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED, obj,
funAtom, JSFunction::FinalizeKind, TenuredObject));
if (!fun)
return NULL;

View File

@ -329,7 +329,7 @@ bool
js::XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
MutableHandleObject objp)
{
/* NB: Keep this in sync with CloneInterpretedFunction. */
/* NB: Keep this in sync with CloneFunctionAndScript. */
RootedAtom atom(xdr->cx());
uint32_t firstword; /* flag telling whether fun->atom is non-null,
plus for fun->u.i.skipmin, fun->u.i.wrapper,
@ -354,7 +354,8 @@ js::XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope, Han
atom = fun->atom();
script = fun->nonLazyScript();
} else {
fun = NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED, NullPtr(), NullPtr());
fun = NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED, NullPtr(), NullPtr(),
JSFunction::FinalizeKind, TenuredObject);
if (!fun)
return false;
atom = NULL;
@ -395,14 +396,13 @@ template bool
js::XDRInterpretedFunction(XDRState<XDR_DECODE> *, HandleObject, HandleScript, MutableHandleObject);
JSObject *
js::CloneInterpretedFunction(JSContext *cx, HandleObject enclosingScope, HandleFunction srcFun,
NewObjectKind newKind /* = GenericObject */)
js::CloneFunctionAndScript(JSContext *cx, HandleObject enclosingScope, HandleFunction srcFun)
{
/* NB: Keep this in sync with XDRInterpretedFunction. */
RootedFunction clone(cx, NewFunction(cx, NullPtr(), NULL, 0,
JSFunction::INTERPRETED, NullPtr(), NullPtr(),
JSFunction::FinalizeKind, newKind));
JSFunction::FinalizeKind, TenuredObject));
if (!clone)
return NULL;
@ -1462,7 +1462,8 @@ js::Function(JSContext *cx, unsigned argc, Value *vp)
*/
RootedAtom anonymousAtom(cx, cx->names().anonymous);
RootedFunction fun(cx, NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED_LAMBDA,
global, anonymousAtom));
global, anonymousAtom, JSFunction::FinalizeKind,
TenuredObject));
if (!fun)
return false;

View File

@ -443,8 +443,7 @@ XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope,
HandleScript enclosingScript, MutableHandleObject objp);
extern JSObject *
CloneInterpretedFunction(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
NewObjectKind newKind = GenericObject);
CloneFunctionAndScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun);
/*
* Report an error that call.thisv is not compatible with the specified class,

View File

@ -2334,8 +2334,7 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
else
enclosingScope = fun;
clone = CloneInterpretedFunction(cx, enclosingScope, innerFun,
src->selfHosted ? TenuredObject : newKind);
clone = CloneFunctionAndScript(cx, enclosingScope, innerFun);
}
} else {
/*

View File

@ -87,6 +87,7 @@ SetFrameArgumentsObject(JSContext *cx, AbstractFramePtr frame,
inline void
JSScript::setFunction(JSFunction *fun)
{
JS_ASSERT(fun->isTenured());
function_ = fun;
}