[INFER] Bail out from IC code on any recompilation/expansion change in the compartment, bug 646006.

This commit is contained in:
Brian Hackett 2011-03-29 18:51:15 -07:00
parent ed887865ed
commit 7b1f08d78a
8 changed files with 73 additions and 48 deletions

View File

@ -2009,8 +2009,6 @@ TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script)
cx->compartment->types.setPendingNukeTypes(cx);
return;
}
recompilations++;
}
bool

View File

@ -658,6 +658,14 @@ struct TypeCompartment
/* Pending recompilations to perform before execution of JIT code can resume. */
Vector<JSScript*> *pendingRecompiles;
/*
* Number of recompilation events and inline frame expansions that have
* occurred in this compartment. If these change, code should not count on
* compiled code or the current stack being intact.
*/
unsigned recompilations;
unsigned frameExpansions;
/* Tables for determining types of singleton/JSON objects. */
ArrayTypeTable *arrayTypeTable;
@ -700,9 +708,6 @@ struct TypeCompartment
unsigned typeCounts[TYPE_COUNT_LIMIT];
unsigned typeCountOver;
/* Number of recompilations triggered. */
unsigned recompilations;
void init(JSContext *cx);
~TypeCompartment();

View File

@ -40,6 +40,7 @@
#define jsjaeger_h__
#include "jscntxt.h"
#include "jscompartment.h"
#include "assembler/assembler/MacroAssemblerCodeRef.h"
#include "assembler/assembler/CodeLocation.h"
@ -145,6 +146,12 @@ struct VMFrame
JSRuntime *runtime() { return cx->runtime; }
/*
* Get the current frame and JIT. Note that these are NOT stable in case
* of recompilations; all code which expects these to be stable should
* check that cx->recompilations() has not changed across a call that could
* trigger recompilation (pretty much any time the VM is called into).
*/
JSStackFrame *&fp() { return regs.fp; }
mjit::JITScript *jit() { return fp()->jit(); }
@ -160,6 +167,30 @@ extern "C" void JaegerStubVeneer(void);
namespace mjit {
/* Helper to watch for recompilation and frame expansion activity on a compartment. */
struct RecompilationMonitor
{
JSContext *cx;
/*
* If either a recompilation or expansion occurs, then ICs and stubs should
* not depend on the frame or JITs being intact. The two are separated for logging.
*/
unsigned recompilations;
unsigned frameExpansions;
RecompilationMonitor(JSContext *cx)
: cx(cx),
recompilations(cx->compartment->types.recompilations),
frameExpansions(cx->compartment->types.frameExpansions)
{}
bool recompiled() {
return cx->compartment->types.recompilations != recompilations
|| cx->compartment->types.frameExpansions != frameExpansions;
}
};
/*
* Trampolines to force returns from jit code.
* See also TrampolineCompiler::generateForceReturn(Fast).
@ -348,13 +379,6 @@ struct JITScript {
uint32 nPICs;
#endif
/*
* Number of on-stack recompilations of this JIT script. Reset to zero if
* the JIT script is destroyed if marked for recompilation with no active
* frame on the stack.
*/
uint32 recompilations;
#ifdef JS_MONOIC
/* Inline cache at function entry for checking this/argument types. */
JSC::CodeLocationLabel argsCheckStub;

View File

@ -805,13 +805,13 @@ class CallCompiler : public BaseCompiler
if (callingNew)
vp[1].setMagicWithObjectOrNullPayload(NULL);
uint32 recompilations = jit->recompilations;
RecompilationMonitor monitor(cx);
if (!CallJSNative(cx, fun->u.n.native, ic.frameSize.getArgc(f), vp))
THROWV(true);
/* Don't touch the IC if the call triggered a recompilation. */
if (f.jit()->recompilations != recompilations)
if (monitor.recompiled())
return true;
/* Right now, take slow-path for IC misses or multiple stubs. */
@ -985,7 +985,7 @@ class CallCompiler : public BaseCompiler
{
JSStackFrame *fp = f.fp();
JITScript *jit = fp->jit();
uint32 recompilations = jit->recompilations;
RecompilationMonitor monitor(cx);
stubs::UncachedCallResult ucr;
if (callingNew)
@ -996,7 +996,7 @@ class CallCompiler : public BaseCompiler
// Watch out in case the IC was invalidated by a recompilation on the calling
// script. This can happen either if the callee is executed or if it compiles
// and the compilation has a static overflow.
if (fp->jit()->recompilations != recompilations)
if (monitor.recompiled())
return ucr.codeAddr;
// If the function cannot be jitted (generally unjittable or empty script),

View File

@ -505,10 +505,10 @@ class SetPropCompiler : public PICStubCompiler
JSProperty *prop = NULL;
/* lookupProperty can trigger recompilations. */
uint32 recompilations = f.jit()->recompilations;
RecompilationMonitor monitor(cx);
if (!obj->lookupProperty(cx, id, &holder, &prop))
return error();
if (f.jit()->recompilations != recompilations)
if (monitor.recompiled())
return Lookup_Uncacheable;
/* If the property exists but is on a prototype, treat as addprop. */
@ -609,10 +609,10 @@ class SetPropCompiler : public PICStubCompiler
return disable("insufficient slot capacity");
if (pic.typeMonitored) {
uint32 recompilations = f.jit()->recompilations;
RecompilationMonitor monitor(cx);
if (!cx->addTypePropertyId(obj->getType(), shape->id, pic.rhsTypes))
return error();
if (f.jit()->recompilations != recompilations)
if (monitor.recompiled())
return Lookup_Uncacheable;
}
@ -629,10 +629,10 @@ class SetPropCompiler : public PICStubCompiler
if (!shape->hasSlot())
return disable("invalid slot");
if (pic.typeMonitored) {
uint32 recompilations = f.jit()->recompilations;
RecompilationMonitor monitor(cx);
if (!cx->addTypePropertyId(obj->getType(), shape->id, pic.rhsTypes))
return error();
if (f.jit()->recompilations != recompilations)
if (monitor.recompiled())
return Lookup_Uncacheable;
}
} else {
@ -643,7 +643,7 @@ class SetPropCompiler : public PICStubCompiler
return disable("setter");
}
if (pic.typeMonitored) {
uint32 recompilations = f.jit()->recompilations;
RecompilationMonitor monitor(cx);
JSScript *script = obj->getCallObjCalleeFunction()->script();
uint16 slot = uint16(shape->shortid);
if (!script->ensureVarTypes(cx))
@ -655,7 +655,7 @@ class SetPropCompiler : public PICStubCompiler
if (!script->typeSetLocal(cx, slot, pic.rhsTypes))
return error();
}
if (f.jit()->recompilations != recompilations)
if (monitor.recompiled())
return Lookup_Uncacheable;
}
}
@ -726,10 +726,10 @@ struct GetPropertyHelper {
if (!aobj->isNative())
return ic.disable(cx, "non-native");
uint32 recompilations = f.jit()->recompilations;
RecompilationMonitor monitor(cx);
if (!aobj->lookupProperty(cx, ATOM_TO_JSID(atom), &holder, &prop))
return ic.error(cx);
if (f.jit()->recompilations != recompilations)
if (monitor.recompiled())
return Lookup_Uncacheable;
if (!prop)
@ -1869,7 +1869,7 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pic)
JSFrameRegs &regs = f.regs;
JSScript *script = f.fp()->script();
uint32 recompilations = f.jit()->recompilations;
RecompilationMonitor monitor(cx);
Value lval;
lval = regs.sp[-1];
@ -1960,7 +1960,7 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pic)
if (regs.sp[-2].isUndefined() && !f.script()->typeMonitorUndefined(cx, f.pc()))
THROW();
if (f.jit()->recompilations != recompilations)
if (monitor.recompiled())
return;
GetPropCompiler cc(f, script, &objv.toObject(), *pic, pic->atom, DisabledCallPropIC);

View File

@ -279,6 +279,12 @@ Recompiler::expandInlineFrames(JSContext *cx, JSStackFrame *fp, mjit::CallSite *
{
JS_ASSERT_IF(next, next->prev() == fp && next->prevInline() == inlined);
/*
* Treat any frame expansion as a recompilation event, so that f.jit() is
* stable if no recompilations have occurred.
*/
cx->compartment->types.frameExpansions++;
void **frameAddr = f->returnAddressLocation();
bool patchFrameReturn = (f->scratch != NATIVE_CALL_SCRATCH_VALUE && fp->jit()->isValidCode(*frameAddr));
@ -490,25 +496,21 @@ Recompiler::recompile()
Vector<CallSite> normalSites(cx);
Vector<CallSite> ctorSites(cx);
uint32 normalRecompilations;
uint32 ctorRecompilations;
if (script->jitNormal && !cleanup(script->jitNormal, &normalSites, &normalRecompilations))
if (script->jitNormal && !cleanup(script->jitNormal, &normalSites))
return false;
if (script->jitCtor && !cleanup(script->jitCtor, &ctorSites, &ctorRecompilations))
if (script->jitCtor && !cleanup(script->jitCtor, &ctorSites))
return false;
ReleaseScriptCode(cx, script);
if (normalFrames.length() &&
!recompile(normalFrames, normalPatches, normalSites, normalNatives,
normalRecompilations)) {
!recompile(normalFrames, normalPatches, normalSites, normalNatives)) {
return false;
}
if (ctorFrames.length() &&
!recompile(ctorFrames, ctorPatches, ctorSites, ctorNatives,
ctorRecompilations)) {
!recompile(ctorFrames, ctorPatches, ctorSites, ctorNatives)) {
return false;
}
@ -523,11 +525,13 @@ Recompiler::recompile()
return false;
}
cx->compartment->types.recompilations++;
return true;
}
bool
Recompiler::cleanup(JITScript *jit, Vector<CallSite> *sites, uint32 *recompilations)
Recompiler::cleanup(JITScript *jit, Vector<CallSite> *sites)
{
while (!JS_CLIST_IS_EMPTY(&jit->callers)) {
JaegerSpew(JSpew_Recompile, "Purging IC caller\n");
@ -550,15 +554,12 @@ Recompiler::cleanup(JITScript *jit, Vector<CallSite> *sites, uint32 *recompilati
return false;
}
*recompilations = jit->recompilations;
return true;
}
bool
Recompiler::recompile(Vector<PatchableFrame> &frames, Vector<PatchableAddress> &patches, Vector<CallSite> &sites,
Vector<PatchableNative> &natives,
uint32 recompilations)
Vector<PatchableNative> &natives)
{
JSStackFrame *fp = frames[0].fp;
@ -577,7 +578,6 @@ Recompiler::recompile(Vector<PatchableFrame> &frames, Vector<PatchableAddress> &
return false;
JITScript *jit = script->getJIT(fp->isConstructing());
jit->recompilations = recompilations + 1;
/* Perform the earlier scanned patches */
for (uint32 i = 0; i < patches.length(); i++)

View File

@ -118,14 +118,13 @@ private:
void patchNative(JITScript *jit, PatchableNative &native);
bool recompile(Vector<PatchableFrame> &frames,
Vector<PatchableAddress> &patches, Vector<CallSite> &sites,
Vector<PatchableNative> &natives,
uint32 recompilations);
Vector<PatchableNative> &natives);
static JSStackFrame *
expandInlineFrameChain(JSContext *cx, JSStackFrame *outer, InlineFrame *inner);
/* Detach jit from any IC callers and save any traps to sites. */
bool cleanup(JITScript *jit, Vector<CallSite> *sites, uint32 *recompilations);
bool cleanup(JITScript *jit, Vector<CallSite> *sites);
};
} /* namespace mjit */

View File

@ -2814,8 +2814,7 @@ stubs::CheckArgumentTypes(VMFrame &f)
JSStackFrame *fp = f.fp();
JSFunction *fun = fp->fun();
JSScript *script = fun->script();
uint32 recompilations = f.jit()->recompilations;
RecompilationMonitor monitor(f.cx);
/* Postpone recompilations until all args have been updated. */
types::AutoEnterTypeInference enter(f.cx);
@ -2833,7 +2832,7 @@ stubs::CheckArgumentTypes(VMFrame &f)
if (!f.cx->compartment->types.checkPendingRecompiles(f.cx))
THROW();
if (f.jit()->recompilations != recompilations)
if (monitor.recompiled())
return;
#ifdef JS_MONOIC