diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 11b201bd04ee..14f5416edf80 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -2009,8 +2009,6 @@ TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script) cx->compartment->types.setPendingNukeTypes(cx); return; } - - recompilations++; } bool diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 06e2e47a9a3a..d50fd6b80ac1 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -658,6 +658,14 @@ struct TypeCompartment /* Pending recompilations to perform before execution of JIT code can resume. */ Vector *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(); diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index 5293ae0f7727..fc641386f0c1 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -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; diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index 60c416c9466d..62a25fae9139 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -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), diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index 5b33ea73bba0..2d28635148d1 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -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 ®s = 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); diff --git a/js/src/methodjit/Retcon.cpp b/js/src/methodjit/Retcon.cpp index 4a7718ec135f..9d87267ae4bd 100644 --- a/js/src/methodjit/Retcon.cpp +++ b/js/src/methodjit/Retcon.cpp @@ -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 normalSites(cx); Vector 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 *sites, uint32 *recompilations) +Recompiler::cleanup(JITScript *jit, Vector *sites) { while (!JS_CLIST_IS_EMPTY(&jit->callers)) { JaegerSpew(JSpew_Recompile, "Purging IC caller\n"); @@ -550,15 +554,12 @@ Recompiler::cleanup(JITScript *jit, Vector *sites, uint32 *recompilati return false; } - *recompilations = jit->recompilations; - return true; } bool Recompiler::recompile(Vector &frames, Vector &patches, Vector &sites, - Vector &natives, - uint32 recompilations) + Vector &natives) { JSStackFrame *fp = frames[0].fp; @@ -577,7 +578,6 @@ Recompiler::recompile(Vector &frames, Vector & 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++) diff --git a/js/src/methodjit/Retcon.h b/js/src/methodjit/Retcon.h index 7fb28b361730..9e4238308f65 100644 --- a/js/src/methodjit/Retcon.h +++ b/js/src/methodjit/Retcon.h @@ -118,14 +118,13 @@ private: void patchNative(JITScript *jit, PatchableNative &native); bool recompile(Vector &frames, Vector &patches, Vector &sites, - Vector &natives, - uint32 recompilations); + Vector &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 *sites, uint32 *recompilations); + bool cleanup(JITScript *jit, Vector *sites); }; } /* namespace mjit */ diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index f11a3e7c3783..134019606b3a 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -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