[INFER] Always maintain a synced interpreter stack for method JIT stub calls, bug 685358.

This commit is contained in:
Brian Hackett 2011-09-09 21:55:57 +02:00
parent ceb94a57c3
commit d3435e89d8
13 changed files with 24 additions and 371 deletions

View File

@ -506,7 +506,7 @@ JSCompartment::markTypes(JSTracer *trc)
}
void
JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
JSCompartment::sweep(JSContext *cx, bool releaseTypes)
{
/* Remove dead wrappers from the table. */
for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
@ -545,49 +545,8 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
traceMonitor()->sweep(cx);
#endif
# if defined JS_METHODJIT && defined JS_POLYIC
/*
* Purge all PICs in the compartment. These can reference type data and
* need to know which types are pending collection.
*/
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->hasJITCode())
mjit::ic::PurgePICs(cx, script);
}
# endif
bool discardScripts = !active && (releaseInterval != 0 || hasDebugModeCodeToDrop);
#if defined JS_METHODJIT && defined JS_MONOIC
/*
* The release interval is the frequency with which we should try to destroy
* executable pools by releasing all JIT code in them, zero to never destroy pools.
* Initialize counter so that the first pool will be destroyed, and eventually drive
* the amount of JIT code in never-used compartments to zero. Don't discard anything
* for compartments which currently have active stack frames.
*/
uint32 counter = 1;
if (discardScripts)
hasDebugModeCodeToDrop = false;
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->hasJITCode()) {
mjit::ic::SweepCallICs(cx, script, discardScripts);
if (discardScripts) {
ScriptTryDestroyCode(cx, script, true, releaseInterval, counter);
ScriptTryDestroyCode(cx, script, false, releaseInterval, counter);
}
}
}
#endif
#ifdef JS_METHODJIT
if (types.inferenceEnabled)
mjit::ClearAllFrames(this);
mjit::ClearAllFrames(this);
#endif
if (activeAnalysis) {
@ -597,11 +556,9 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
* GC then shape numbers baked into the code may change.
*/
#ifdef JS_METHODJIT
if (types.inferenceEnabled) {
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
mjit::ReleaseScriptCode(cx, script);
}
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
mjit::ReleaseScriptCode(cx, script);
}
#endif
} else {
@ -618,17 +575,19 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
* enabled in the compartment.
*/
if (types.inferenceEnabled) {
if (active)
releaseTypes = false;
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->types) {
types::TypeScript::Sweep(cx, script);
/*
* On each 1/8 lifetime, release observed types for all scripts.
* This is always safe to do when there are no frames for the
* compartment on the stack.
* Periodically release observed types for all scripts.
* This is always safe to do when there are no frames for
* the compartment on the stack.
*/
if (discardScripts) {
if (releaseTypes) {
script->types->destroy();
script->types = NULL;
script->typesPurged = true;
@ -684,20 +643,6 @@ JSCompartment::purge(JSContext *cx)
if (hasTraceMonitor())
traceMonitor()->needFlush = JS_TRUE;
#endif
#if defined JS_METHODJIT && defined JS_MONOIC
/*
* MICs do not refer to data which can be GC'ed and do not generate stubs
* which might need to be discarded, but are sensitive to shape regeneration.
*/
if (cx->runtime->gcRegenShapes) {
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->hasJITCode())
mjit::ic::PurgeMICs(cx, script);
}
}
#endif
}
MathCache *

View File

@ -532,7 +532,7 @@ struct JS_FRIEND_API(JSCompartment) {
bool wrap(JSContext *cx, js::AutoIdVector &props);
void markTypes(JSTracer *trc);
void sweep(JSContext *cx, uint32 releaseInterval);
void sweep(JSContext *cx, bool releaseTypes);
void purge(JSContext *cx);
void setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind);

View File

@ -646,12 +646,8 @@ js_GCThingIsMarked(void *thing, uintN color = BLACK)
return reinterpret_cast<Cell *>(thing)->isMarked(color);
}
/*
* 1/8 life for JIT code. After this number of microseconds have passed, 1/8 of all
* JIT code is discarded in inactive compartments, regardless of how often that
* code runs.
*/
static const int64 JIT_SCRIPT_EIGHTH_LIFETIME = 60 * 1000 * 1000;
/* Lifetime for type sets attached to scripts containing observed types. */
static const int64 JIT_SCRIPT_RELEASE_TYPES_INTERVAL = 60 * 1000 * 1000;
JSBool
js_InitGC(JSRuntime *rt, uint32 maxbytes)
@ -693,7 +689,7 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes)
*/
rt->setGCLastBytes(8192, GC_NORMAL);
rt->gcJitReleaseTime = PRMJ_Now() + JIT_SCRIPT_EIGHTH_LIFETIME;
rt->gcJitReleaseTime = PRMJ_Now() + JIT_SCRIPT_RELEASE_TYPES_INTERVAL;
return true;
}
@ -2127,20 +2123,12 @@ static void
SweepCrossCompartmentWrappers(JSContext *cx)
{
JSRuntime *rt = cx->runtime;
/*
* Figure out how much JIT code should be released from inactive compartments.
* If multiple eighth-lives have passed, compound the release interval linearly;
* if enough time has passed, all inactive JIT code will be released.
*/
uint32 releaseInterval = 0;
bool releaseTypes = false;
int64 now = PRMJ_Now();
if (now >= rt->gcJitReleaseTime) {
releaseInterval = 8;
while (now >= rt->gcJitReleaseTime) {
if (--releaseInterval == 1)
rt->gcJitReleaseTime = now;
rt->gcJitReleaseTime += JIT_SCRIPT_EIGHTH_LIFETIME;
}
releaseTypes = true;
rt->gcJitReleaseTime = now + JIT_SCRIPT_RELEASE_TYPES_INTERVAL;
}
/*
@ -2151,7 +2139,7 @@ SweepCrossCompartmentWrappers(JSContext *cx)
* (4) Sweep the method JIT ICs and release infrequently used JIT code.
*/
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
(*c)->sweep(cx, releaseInterval);
(*c)->sweep(cx, releaseTypes);
}
static void

View File

@ -859,13 +859,6 @@ MarkChildren(JSTracer *trc, JSScript *script)
if (script->types)
script->types->trace(trc);
#ifdef JS_METHODJIT
if (script->jitNormal)
script->jitNormal->trace(trc);
if (script->jitCtor)
script->jitCtor->trace(trc);
#endif
}
void

View File

@ -122,7 +122,6 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript, bool isConstructi
jumpTables(CompilerAllocPolicy(cx, *thisFromCtor())),
jumpTableOffsets(CompilerAllocPolicy(cx, *thisFromCtor())),
loopEntries(CompilerAllocPolicy(cx, *thisFromCtor())),
rootedObjects(CompilerAllocPolicy(cx, *thisFromCtor())),
stubcc(cx, *thisFromCtor(), frame),
debugMode_(cx->compartment->debugMode()),
#if defined JS_TRACER
@ -942,7 +941,6 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
sizeof(NativeMapEntry) * nNmapLive +
sizeof(InlineFrame) * inlineFrames.length() +
sizeof(CallSite) * callSites.length() +
sizeof(JSObject *) * rootedObjects.length() +
#if defined JS_MONOIC
sizeof(ic::GetGlobalNameIC) * getGlobalNames.length() +
sizeof(ic::SetGlobalNameIC) * setGlobalNames.length() +
@ -1074,13 +1072,6 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
stubCode.patch(from.loopPatch.codePatch, result + codeOffset);
}
/* Build the list of objects rooted by the script. */
JSObject **jitRooted = (JSObject **)cursor;
jit->nRootedObjects = rootedObjects.length();
cursor += sizeof(JSObject *) * jit->nRootedObjects;
for (size_t i = 0; i < jit->nRootedObjects; i++)
jitRooted[i] = rootedObjects[i];
#if defined JS_MONOIC
JS_INIT_CLIST(&jit->callers);
@ -4661,12 +4652,6 @@ mjit::Compiler::jsop_callprop_str(JSAtom *atom)
if (!js_GetClassPrototype(cx, globalObj, JSProto_String, &obj))
return false;
/*
* Root the proto, since JS_ClearScope might overwrite the global object's
* copy.
*/
rootedObjects.append(obj);
/* Force into a register because getprop won't expect a constant. */
RegisterID reg = frame.allocReg();

View File

@ -455,7 +455,6 @@ class Compiler : public BaseCompiler
js::Vector<JumpTable, 16> jumpTables;
js::Vector<uint32, 16> jumpTableOffsets;
js::Vector<LoopEntry, 16> loopEntries;
js::Vector<JSObject *, 0, CompilerAllocPolicy> rootedObjects;
StubCompiler stubcc;
Label invokeLabel;
Label arityLabel;

View File

@ -1324,11 +1324,7 @@ FrameState::sync(Assembler &masm, Uses uses) const
Registers avail(freeRegs.freeMask & Registers::AvailRegs);
Registers temp(Registers::TempAnyRegs);
FrameEntry *bottom = (cx->typeInferenceEnabled() || cx->compartment->debugMode())
? entries
: a->sp - uses.nuses;
for (FrameEntry *fe = a->sp - 1; fe >= bottom; fe--) {
for (FrameEntry *fe = a->sp - 1; fe >= entries; fe--) {
if (!fe->isTracked())
continue;
@ -1378,7 +1374,7 @@ FrameState::sync(Assembler &masm, Uses uses) const
/* Fall back to a slower sync algorithm if load required. */
if ((!fe->type.synced() && backing->type.inMemory()) ||
(!fe->data.synced() && backing->data.inMemory())) {
syncFancy(masm, avail, fe, bottom);
syncFancy(masm, avail, fe, entries);
return;
}
#endif
@ -1459,11 +1455,7 @@ FrameState::syncAndKill(Registers kill, Uses uses, Uses ignore)
uint32 maxvisits = tracker.nentries;
FrameEntry *bottom = (cx->typeInferenceEnabled() || cx->compartment->debugMode())
? entries
: a->sp - uses.nuses;
for (FrameEntry *fe = a->sp - 1; fe >= bottom && maxvisits; fe--) {
for (FrameEntry *fe = a->sp - 1; fe >= entries && maxvisits; fe--) {
if (!fe->isTracked())
continue;

View File

@ -990,16 +990,10 @@ JITScript::callSites() const
return (js::mjit::CallSite *)&inlineFrames()[nInlineFrames];
}
JSObject **
JITScript::rootedObjects() const
{
return (JSObject **)&callSites()[nCallSites];
}
char *
JITScript::commonSectionLimit() const
{
return (char *)&rootedObjects()[nRootedObjects];
return (char *)&callSites()[nCallSites];
}
#ifdef JS_MONOIC
@ -1164,7 +1158,6 @@ mjit::JITScript::scriptDataSize(JSUsableSizeFun usf)
sizeof(NativeMapEntry) * nNmapPairs +
sizeof(InlineFrame) * nInlineFrames +
sizeof(CallSite) * nCallSites +
sizeof(JSObject *) * nRootedObjects +
#if defined JS_MONOIC
sizeof(ic::GetGlobalNameIC) * nGetGlobalNames +
sizeof(ic::SetGlobalNameIC) * nSetGlobalNames +
@ -1304,20 +1297,4 @@ mjit::NativeToPC(JITScript *jit, void *ncode, mjit::CallSite **pinline)
return jit->nativeToPC(ncode, pinline);
}
void
JITScript::trace(JSTracer *trc)
{
/*
* MICs and PICs attached to the JITScript are weak references, and either
* entirely purged or selectively purged on each GC. We do, however, need
* to maintain references to any scripts whose code was inlined into this.
*/
InlineFrame *inlineFrames_ = inlineFrames();
for (unsigned i = 0; i < nInlineFrames; i++)
MarkObject(trc, *inlineFrames_[i].fun, "jitscript_fun");
for (uint32 i = 0; i < nRootedObjects; ++i)
MarkObject(trc, *rootedObjects()[i], "mjit rooted object");
}
/* static */ const double mjit::Assembler::oneDouble = 1.0;

View File

@ -605,7 +605,6 @@ struct JITScript {
bool singleStepMode:1; /* compiled in "single step mode" */
uint32 nInlineFrames;
uint32 nCallSites;
uint32 nRootedObjects;
#ifdef JS_MONOIC
uint32 nGetGlobalNames;
uint32 nSetGlobalNames;
@ -643,7 +642,6 @@ struct JITScript {
NativeMapEntry *nmap() const;
js::mjit::InlineFrame *inlineFrames() const;
js::mjit::CallSite *callSites() const;
JSObject **rootedObjects() const;
#ifdef JS_MONOIC
ic::GetGlobalNameIC *getGlobalNames() const;
ic::SetGlobalNameIC *setGlobalNames() const;
@ -666,11 +664,6 @@ struct JITScript {
}
void nukeScriptDependentICs();
void sweepCallICs(JSContext *cx, bool purgeAll);
void purgeMICs();
void purgePICs();
void trace(JSTracer *trc);
/* |usf| can be NULL here, in which case the fallback size computation will be used. */
size_t scriptDataSize(JSUsableSizeFun usf);

View File

@ -1383,168 +1383,5 @@ JITScript::resetArgsCheck()
repatch.relink(argsCheckJump, argsCheckStub);
}
void
JITScript::purgeMICs()
{
if (!nGetGlobalNames || !nSetGlobalNames)
return;
Repatcher repatch(this);
ic::GetGlobalNameIC *getGlobalNames_ = getGlobalNames();
for (uint32 i = 0; i < nGetGlobalNames; i++) {
ic::GetGlobalNameIC &ic = getGlobalNames_[i];
JSC::CodeLocationDataLabel32 label = ic.fastPathStart.dataLabel32AtOffset(ic.shapeOffset);
repatch.repatch(label, int(INVALID_SHAPE));
}
ic::SetGlobalNameIC *setGlobalNames_ = setGlobalNames();
for (uint32 i = 0; i < nSetGlobalNames; i++) {
ic::SetGlobalNameIC &ic = setGlobalNames_[i];
ic.patchInlineShapeGuard(repatch, int32(INVALID_SHAPE));
if (ic.hasExtraStub) {
Repatcher repatcher(ic.extraStub);
ic.patchExtraShapeGuard(repatcher, int32(INVALID_SHAPE));
}
}
}
void
ic::PurgeMICs(JSContext *cx, JSScript *script)
{
/* MICs are purged during GC to handle changing shapes. */
JS_ASSERT(cx->runtime->gcRegenShapes);
if (script->jitNormal)
script->jitNormal->purgeMICs();
if (script->jitCtor)
script->jitCtor->purgeMICs();
}
void
JITScript::nukeScriptDependentICs()
{
if (!nCallICs)
return;
Repatcher repatcher(this);
ic::CallICInfo *callICs_ = callICs();
for (uint32 i = 0; i < nCallICs; i++) {
ic::CallICInfo &ic = callICs_[i];
if (!ic.fastGuardedObject)
continue;
repatcher.repatch(ic.funGuard, NULL);
repatcher.relink(ic.funJump, ic.slowPathStart);
ic.releasePool(CallICInfo::Pool_ClosureStub);
ic.fastGuardedObject = NULL;
ic.hasJsFunCheck = false;
}
}
void
JITScript::sweepCallICs(JSContext *cx, bool purgeAll)
{
Repatcher repatcher(this);
/*
* If purgeAll is set, purge stubs in the script except those covered by PurgePICs
* (which is always called during GC). We want to remove references which can keep
* alive pools that we are trying to destroy (see JSCompartment::sweep).
*/
ic::CallICInfo *callICs_ = callICs();
for (uint32 i = 0; i < nCallICs; i++) {
ic::CallICInfo &ic = callICs_[i];
/*
* If the object is unreachable, we're guaranteed not to be currently
* executing a stub generated by a guard on that object. This lets us
* precisely GC call ICs while keeping the identity guard safe.
*/
bool fastFunDead = ic.fastGuardedObject &&
(purgeAll || IsAboutToBeFinalized(cx, ic.fastGuardedObject));
bool nativeDead = ic.fastGuardedNative &&
(purgeAll || IsAboutToBeFinalized(cx, ic.fastGuardedNative));
/*
* There are three conditions where we need to relink:
* (1) purgeAll is true.
* (2) The native is dead, since it always has a stub.
* (3) The fastFun is dead *and* there is a closure stub.
*
* Note although both objects can be non-NULL, there can only be one
* of [closure, native] stub per call IC.
*/
if (purgeAll || nativeDead || (fastFunDead && ic.hasJsFunCheck)) {
repatcher.relink(ic.funJump, ic.slowPathStart);
ic.hit = false;
}
if (fastFunDead) {
repatcher.repatch(ic.funGuard, NULL);
ic.purgeGuardedObject();
}
if (nativeDead)
ic.fastGuardedNative = NULL;
if (purgeAll) {
ic.releasePool(CallICInfo::Pool_ScriptStub);
JSC::CodeLocationJump oolJump = ic.slowPathStart.jumpAtOffset(ic.oolJumpOffset);
JSC::CodeLocationLabel icCall = ic.slowPathStart.labelAtOffset(ic.icCallOffset);
repatcher.relink(oolJump, icCall);
}
}
/* The arguments type check IC can refer to type objects which might be swept. */
if (argsCheckPool)
resetArgsCheck();
if (purgeAll) {
/* Purge ICs generating stubs into execPools. */
uint32 released = 0;
ic::EqualityICInfo *equalityICs_ = equalityICs();
for (uint32 i = 0; i < nEqualityICs; i++) {
ic::EqualityICInfo &ic = equalityICs_[i];
if (!ic.generated)
continue;
JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, ic::Equality));
repatcher.relink(ic.stubCall, fptr);
repatcher.relink(ic.jumpToStub, ic.stubEntry);
ic.generated = false;
released++;
}
ic::SetGlobalNameIC *setGlobalNames_ = setGlobalNames();
for (uint32 i = 0; i < nSetGlobalNames; i ++) {
ic::SetGlobalNameIC &ic = setGlobalNames_[i];
if (!ic.hasExtraStub)
continue;
repatcher.relink(ic.fastPathStart.jumpAtOffset(ic.inlineShapeJump), ic.slowPathStart);
ic.hasExtraStub = false;
released++;
}
JS_ASSERT(released == execPools.length());
for (uint32 i = 0; i < released; i++)
execPools[i]->release();
execPools.clear();
}
}
void
ic::SweepCallICs(JSContext *cx, JSScript *script, bool purgeAll)
{
if (script->jitNormal)
script->jitNormal->sweepCallICs(cx, purgeAll);
if (script->jitCtor)
script->jitCtor->sweepCallICs(cx, purgeAll);
}
#endif /* JS_MONOIC */

View File

@ -301,9 +301,6 @@ JSBool JS_FASTCALL SplatApplyArgs(VMFrame &f);
void GenerateArgumentCheckStub(VMFrame &f);
void PurgeMICs(JSContext *cx, JSScript *script);
void SweepCallICs(JSContext *cx, JSScript *script, bool purgeAll);
} /* namespace ic */
} /* namespace mjit */
} /* namespace js */

View File

@ -3261,57 +3261,5 @@ ic::SetElement(VMFrame &f, ic::SetElementIC *ic)
template void JS_FASTCALL ic::SetElement<true>(VMFrame &f, SetElementIC *ic);
template void JS_FASTCALL ic::SetElement<false>(VMFrame &f, SetElementIC *ic);
void
JITScript::purgePICs()
{
if (!nPICs && !nGetElems && !nSetElems)
return;
Repatcher repatcher(this);
ic::PICInfo *pics_ = pics();
for (uint32 i = 0; i < nPICs; i++) {
ic::PICInfo &pic = pics_[i];
switch (pic.kind) {
case ic::PICInfo::SET:
case ic::PICInfo::SETMETHOD:
SetPropCompiler::reset(repatcher, pic);
break;
case ic::PICInfo::NAME:
case ic::PICInfo::XNAME:
case ic::PICInfo::CALLNAME:
ScopeNameCompiler::reset(repatcher, pic);
break;
case ic::PICInfo::BIND:
BindNameCompiler::reset(repatcher, pic);
break;
case ic::PICInfo::CALL: /* fall-through */
case ic::PICInfo::GET:
GetPropCompiler::reset(repatcher, pic);
break;
default:
JS_NOT_REACHED("Unhandled PIC kind");
break;
}
pic.reset();
}
ic::GetElementIC *getElems_ = getElems();
ic::SetElementIC *setElems_ = setElems();
for (uint32 i = 0; i < nGetElems; i++)
getElems_[i].purge(repatcher);
for (uint32 i = 0; i < nSetElems; i++)
setElems_[i].purge(repatcher);
}
void
ic::PurgePICs(JSContext *cx, JSScript *script)
{
if (script->jitNormal)
script->jitNormal->purgePICs();
if (script->jitCtor)
script->jitCtor->purgePICs();
}
#endif /* JS_POLYIC */

View File

@ -554,7 +554,6 @@ struct PICInfo : public BasePolyIC {
};
#ifdef JS_POLYIC
void PurgePICs(JSContext *cx, JSScript *script);
void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL GetPropNoCache(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL SetProp(VMFrame &f, ic::PICInfo *);