diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 37692bf3d03..b662df50172 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -2008,7 +2008,7 @@ struct JSContext public: friend class js::StackSpace; - friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, JSInterpMode); + friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, uintN); void resetCompartment(); diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index eadfef06edb..ff7bd598d85 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -3716,10 +3716,15 @@ bad: JSBool js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body) { + CG_SWITCH_TO_PROLOG(cg); + JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); + if (js_Emit1(cx, cg, JSOP_BEGIN) < 0) + return false; + CG_SWITCH_TO_MAIN(cg); + if (cg->flags & TCF_FUN_IS_GENERATOR) { - /* JSOP_GENERATOR must be the first instruction. */ + /* JSOP_GENERATOR must be the first real instruction. */ CG_SWITCH_TO_PROLOG(cg); - JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0) return false; CG_SWITCH_TO_MAIN(cg); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 007e086e75c..457b69ec0bc 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -82,6 +82,7 @@ #include "jscntxtinlines.h" #include "jsinterpinlines.h" #include "jsobjinlines.h" +#include "jsprobes.h" #include "jspropertycacheinlines.h" #include "jsscopeinlines.h" #include "jsscriptinlines.h" @@ -733,13 +734,28 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) } } + JSInterpreterHook hook = cx->debugHooks->callHook; + void *hookData = NULL; + if (JS_UNLIKELY(hook != NULL)) + hookData = hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData); + /* Run function until JSOP_STOP, JSOP_RETURN or error. */ JSBool ok; { AutoPreserveEnumerators preserve(cx); + Probes::enterJSFun(cx, fun); ok = RunScript(cx, script, fp); + Probes::exitJSFun(cx, fun); } + if (JS_UNLIKELY(hookData != NULL)) { + hook = cx->debugHooks->callHook; + if (hook) + hook(cx, fp, JS_FALSE, &ok, hookData); + } + + PutActivationObjects(cx, fp); + args.rval() = fp->returnValue(); JS_ASSERT_IF(ok && (flags & JSINVOKE_CONSTRUCT), !args.rval().isPrimitive()); @@ -2145,28 +2161,11 @@ IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval) return js_IteratorNext(cx, iterobj, rval); } -static inline bool -ScriptPrologue(JSContext *cx, JSStackFrame *fp) -{ - if (fp->isConstructing()) { - JSObject *obj = js_CreateThisForFunction(cx, &fp->callee()); - if (!obj) - return false; - fp->functionThis().setObject(*obj); - } - JSInterpreterHook hook = cx->debugHooks->callHook; - if (JS_UNLIKELY(hook != NULL)) - fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData)); - - Probes::enterJSFun(cx, fp->maybeFun()); - - return true; -} namespace js { JS_REQUIRES_STACK JS_NEVER_INLINE bool -Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInterpMode interpMode) +Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN interpFlags) { #ifdef MOZ_TRACEVIS TraceVisStateObj tvso(cx, S_INTERP); @@ -2397,7 +2396,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte script->maybeNativeCodeForPC(regs.fp->isConstructing(), regs.pc)) { \ JS_ASSERT(!TRACE_RECORDER(cx)); \ interpReturnOK = true; \ - goto leave_on_safe_point; \ + goto stop_recording; \ } \ } while (0) #else @@ -2433,25 +2432,13 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte /* Check for too deep of a native thread stack. */ JS_CHECK_RECURSION(cx, return JS_FALSE); - JSFrameRegs regs = *cx->regs; + MUST_FLOW_THROUGH("exit"); + ++cx->interpLevel; /* Repoint cx->regs to a local variable for faster access. */ - struct InterpExitGuard { - JSContext *cx; - const JSFrameRegs ®s; - JSFrameRegs *prevContextRegs; - InterpExitGuard(JSContext *cx, JSFrameRegs ®s) - : cx(cx), regs(regs), prevContextRegs(cx->regs) { - cx->setCurrentRegs(®s); - ++cx->interpLevel; - } - ~InterpExitGuard() { - --cx->interpLevel; - JS_ASSERT(cx->regs == ®s); - *prevContextRegs = regs; - cx->setCurrentRegs(prevContextRegs); - } - } interpGuard(cx, regs); + JSFrameRegs *const prevContextRegs = cx->regs; + JSFrameRegs regs = *cx->regs; + cx->setCurrentRegs(®s); /* Copy in hot values that change infrequently. */ JSRuntime *const rt = cx->runtime; @@ -2463,7 +2450,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte JS_ASSERT(script->length > 1); #if defined(JS_TRACER) && defined(JS_METHODJIT) - bool leaveOnSafePoint = (interpMode == JSINTERP_SAFEPOINT); + bool leaveOnSafePoint = !!(interpFlags & JSINTERP_SAFEPOINT); # define CLEAR_LEAVE_ON_TRACE_POINT() ((void) (leaveOnSafePoint = false)) #else # define CLEAR_LEAVE_ON_TRACE_POINT() ((void) 0) @@ -2483,7 +2470,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte #if JS_HAS_GENERATORS if (JS_UNLIKELY(regs.fp->isGeneratorFrame())) { - JS_ASSERT(interpGuard.prevContextRegs == &cx->generatorFor(regs.fp)->regs); + JS_ASSERT(prevContextRegs == &cx->generatorFor(regs.fp)->regs); JS_ASSERT((size_t) (regs.pc - script->code) <= script->length); JS_ASSERT((size_t) (regs.sp - regs.fp->base()) <= StackDepth(script)); @@ -2502,7 +2489,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte * there should already be a valid recorder. Otherwise... * we cannot reenter the interpreter while recording. */ - if (interpMode == JSINTERP_RECORD) { + if (interpFlags & JSINTERP_RECORD) { JS_ASSERT(TRACE_RECORDER(cx)); ENABLE_INTERRUPTS(); } else if (TRACE_RECORDER(cx)) { @@ -2513,15 +2500,6 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte atoms = COMMON_ATOMS_START(&rt->atomState); #endif - /* Don't call the script prologue if executing between Method and Trace JIT. */ - if (interpMode == JSINTERP_NORMAL) { - JS_ASSERT_IF(!regs.fp->isGeneratorFrame(), regs.pc == script->code); - if (!ScriptPrologue(cx, regs.fp)) - goto error; - } - - CHECK_INTERRUPT_HANDLER(); - /* State communicated between non-local jumps: */ JSBool interpReturnOK; JSAtom *atomNotDefined; @@ -2607,8 +2585,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte AbortableRecordingStatus status = tr->monitorRecording(op); JS_ASSERT_IF(cx->throwing, status == ARECORD_ERROR); - if (interpMode != JSINTERP_NORMAL) { - JS_ASSERT(interpMode == JSINTERP_RECORD || JSINTERP_SAFEPOINT); + if (interpFlags & (JSINTERP_RECORD | JSINTERP_SAFEPOINT)) { switch (status) { case ARECORD_IMACRO_ABORTED: case ARECORD_ABORTED: @@ -2783,11 +2760,27 @@ BEGIN_CASE(JSOP_STOP) inline_return: { JS_ASSERT(!js_IsActiveWithOrBlock(cx, ®s.fp->scopeChain(), 0)); - interpReturnOK = ScriptEpilogue(cx, regs.fp, interpReturnOK); - CHECK_INTERRUPT_HANDLER(); + if (JS_UNLIKELY(regs.fp->hasHookData())) { + if (JSInterpreterHook hook = cx->debugHooks->callHook) { + hook(cx, regs.fp, JS_FALSE, &interpReturnOK, regs.fp->hookData()); + CHECK_INTERRUPT_HANDLER(); + } + } + + PutActivationObjects(cx, regs.fp); + + Probes::exitJSFun(cx, regs.fp->maybeFun()); + + /* + * If inline-constructing, replace primitive rval with the new object + * passed in via |this|, and instrument this constructor invocation. + */ + if (regs.fp->isConstructing()) { + if (regs.fp->returnValue().isPrimitive()) + regs.fp->setReturnValue(ObjectValue(regs.fp->constructorThis())); + JS_RUNTIME_METER(cx->runtime, constructs); + } - /* The JIT inlines ScriptEpilogue. */ - jit_return: Value *newsp = regs.fp->actualArgs() - 1; newsp[-1] = regs.fp->returnValue(); cx->stack().popInlineFrame(cx, regs.fp->prev(), newsp); @@ -2810,6 +2803,18 @@ BEGIN_CASE(JSOP_STOP) goto error; } else { JS_ASSERT(regs.sp == regs.fp->base()); + if (regs.fp->isConstructing() && regs.fp->returnValue().isPrimitive()) + regs.fp->setReturnValue(ObjectValue(regs.fp->constructorThis())); + +#if defined(JS_TRACER) && defined(JS_METHODJIT) + /* Hack: re-push rval so either JIT will read it properly. */ + regs.fp->setBailedAtReturn(); + if (TRACE_RECORDER(cx)) { + AbortRecording(cx, "recording out of Interpret"); + interpReturnOK = true; + goto stop_recording; + } +#endif } interpReturnOK = true; goto exit; @@ -4564,6 +4569,41 @@ BEGIN_CASE(JSOP_ENUMELEM) } END_CASE(JSOP_ENUMELEM) +BEGIN_CASE(JSOP_BEGIN) +{ + if (regs.fp->isConstructing()) { + JSObject *obj2 = js_CreateThisForFunction(cx, ®s.fp->callee()); + if (!obj2) + goto error; + regs.fp->functionThis().setObject(*obj2); + } + + /* Call the debugger hook if present. */ + if (JSInterpreterHook hook = cx->debugHooks->callHook) { + regs.fp->setHookData(hook(cx, regs.fp, JS_TRUE, 0, + cx->debugHooks->callHookData)); + CHECK_INTERRUPT_HANDLER(); + } + + JS_RUNTIME_METER(rt, inlineCalls); + + Probes::enterJSFun(cx, regs.fp->fun()); + +#ifdef JS_METHODJIT + /* Try to ensure methods are method JIT'd. */ + mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp); + if (status == mjit::Compile_Error) + goto error; + if (!TRACE_RECORDER(cx) && status == mjit::Compile_Okay) { + if (!mjit::JaegerShot(cx)) + goto error; + interpReturnOK = true; + goto inline_return; + } +#endif +} +END_CASE(JSOP_BEGIN) + { JSFunction *newfun; JSObject *callee; @@ -4663,31 +4703,12 @@ BEGIN_CASE(JSOP_APPLY) goto error; inlineCallCount++; - JS_RUNTIME_METER(rt, inlineCalls); TRACE_0(EnterFrame); - CHECK_INTERRUPT_HANDLER(); - -#ifdef JS_METHODJIT - /* Try to ensure methods are method JIT'd. */ - mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp); - if (status == mjit::Compile_Error) - goto error; - if (!TRACE_RECORDER(cx) && status == mjit::Compile_Okay) { - interpReturnOK = mjit::JaegerShot(cx); - CHECK_INTERRUPT_HANDLER(); - goto jit_return; - } -#endif - - if (!ScriptPrologue(cx, regs.fp)) - goto error; - - CHECK_INTERRUPT_HANDLER(); - /* Load first op and dispatch it (safe since JSOP_STOP). */ op = (JSOp) *regs.pc; + JS_ASSERT(op == JSOP_BEGIN); DO_OP(); } @@ -6912,9 +6933,6 @@ END_CASE(JSOP_ARRAYPUSH) goto inline_return; exit: - interpReturnOK = ScriptEpilogue(cx, regs.fp, interpReturnOK); - regs.fp->setFinishedInInterpreter(); - /* * At this point we are inevitably leaving an interpreted function or a * top-level script, and returning to one of: @@ -6927,15 +6945,20 @@ END_CASE(JSOP_ARRAYPUSH) * frame pc. */ JS_ASSERT(entryFrame == regs.fp); + JS_ASSERT(cx->regs == ®s); + *prevContextRegs = regs; + cx->setCurrentRegs(prevContextRegs); #ifdef JS_TRACER - JS_ASSERT_IF(interpReturnOK && interpMode == JSINTERP_RECORD, !TRACE_RECORDER(cx)); + JS_ASSERT_IF(interpReturnOK && (interpFlags & JSINTERP_RECORD), !TRACE_RECORDER(cx)); if (TRACE_RECORDER(cx)) AbortRecording(cx, "recording out of Interpret"); #endif JS_ASSERT_IF(!regs.fp->isGeneratorFrame(), !js_IsActiveWithOrBlock(cx, ®s.fp->scopeChain(), 0)); + --cx->interpLevel; + return interpReturnOK; atom_not_defined: @@ -6948,13 +6971,12 @@ END_CASE(JSOP_ARRAYPUSH) goto error; } - /* - * This path is used when it's guaranteed the method can be finished - * inside the JIT. - */ #if defined(JS_TRACER) && defined(JS_METHODJIT) - leave_on_safe_point: + stop_recording: #endif + JS_ASSERT(cx->regs == ®s); + *prevContextRegs = regs; + cx->setCurrentRegs(prevContextRegs); return interpReturnOK; } diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index f26bb75c442..8625d96076e 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -59,11 +59,10 @@ struct JSFrameRegs }; /* Flags to toggle js::Interpret() execution. */ -enum JSInterpMode +enum JSInterpFlags { - JSINTERP_NORMAL = 0, /* Interpreter is running normally. */ - JSINTERP_RECORD = 1, /* interpreter has been started to record/run traces */ - JSINTERP_SAFEPOINT = 2 /* interpreter should leave on a method JIT safe point */ + JSINTERP_RECORD = 0x1, /* interpreter has been started to record/run traces */ + JSINTERP_SAFEPOINT = 0x2 /* interpreter should leave on a method JIT safe point */ }; /* Flags used in JSStackFrame::flags_ */ @@ -84,7 +83,7 @@ enum JSFrameFlags /* Temporary frame states */ JSFRAME_ASSIGNING = 0x100, /* not-JOF_ASSIGNING op is assigning */ JSFRAME_YIELDING = 0x200, /* js::Interpret dispatched JSOP_YIELD */ - JSFRAME_FINISHED_IN_INTERPRETER = 0x400, /* set if frame finished in Interpret() */ + JSFRAME_BAILED_AT_RETURN = 0x400, /* bailed at JSOP_RETURN */ /* Concerning function arguments */ JSFRAME_OVERRIDE_ARGS = 0x1000, /* overridden arguments local variable */ @@ -681,12 +680,12 @@ struct JSStackFrame flags_ &= ~JSFRAME_YIELDING; } - void setFinishedInInterpreter() { - flags_ |= JSFRAME_FINISHED_IN_INTERPRETER; + bool isBailedAtReturn() const { + return flags_ & JSFRAME_BAILED_AT_RETURN; } - bool finishedInInterpreter() const { - return !!(flags_ & JSFRAME_FINISHED_IN_INTERPRETER); + void setBailedAtReturn() { + flags_ |= JSFRAME_BAILED_AT_RETURN; } /* @@ -983,7 +982,7 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script, * pointed to by cx->fp until completion or error. */ extern JS_REQUIRES_STACK JS_NEVER_INLINE bool -Interpret(JSContext *cx, JSStackFrame *stopFp, uintN inlineCallCount = 0, JSInterpMode mode = JSINTERP_NORMAL); +Interpret(JSContext *cx, JSStackFrame *stopFp, uintN inlineCallCount = 0, uintN interpFlags = 0); extern JS_REQUIRES_STACK bool RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp); diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index 9bf87e01cb7..6040ea63565 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -1,5 +1,4 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=4 sw=4 et tw=99: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -112,7 +111,7 @@ JSStackFrame::resetInvokeCallFrame() JSFRAME_HAS_RVAL | JSFRAME_HAS_SCOPECHAIN | JSFRAME_HAS_ANNOTATION | - JSFRAME_FINISHED_IN_INTERPRETER))); + JSFRAME_BAILED_AT_RETURN))); flags_ &= JSFRAME_FUNCTION | JSFRAME_OVERFLOW_ARGS | JSFRAME_HAS_PREVPC | @@ -263,11 +262,6 @@ JSStackFrame::stealFrameAndSlots(js::Value *vp, JSStackFrame *otherfp, if (hasCallObj()) { callObj().setPrivate(this); otherfp->flags_ &= ~JSFRAME_HAS_CALL_OBJ; - if (js_IsNamedLambda(fun())) { - JSObject *env = callObj().getParent(); - JS_ASSERT(env->getClass() == &js_DeclEnvClass); - env->setPrivate(this); - } } if (hasArgsObj()) { argsObj().setPrivate(this); @@ -679,35 +673,6 @@ ValuePropertyBearer(JSContext *cx, const Value &v, int spindex) return pobj; } -static inline bool -ScriptEpilogue(JSContext *cx, JSStackFrame *fp, JSBool ok) -{ - Probes::exitJSFun(cx, fp->maybeFun()); - JSInterpreterHook hook = cx->debugHooks->callHook; - if (hook && fp->hasHookData()) - hook(cx, fp, JS_FALSE, &ok, fp->hookData()); - - /* - * An eval frame's parent owns its activation objects. A yielding frame's - * activation objects are transferred to the floating frame, stored in the - * generator. - */ - if (fp->isFunctionFrame() && !fp->isEvalFrame() && !fp->isYielding()) - PutActivationObjects(cx, fp); - - /* - * If inline-constructing, replace primitive rval with the new object - * passed in via |this|, and instrument this constructor invocation. - */ - if (fp->isConstructing()) { - if (fp->returnValue().isPrimitive()) - fp->setReturnValue(ObjectValue(fp->constructorThis())); - JS_RUNTIME_METER(cx->runtime, constructs); - } - - return ok; -} - } #endif /* jsinterpinlines_h__ */ diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 39c90471ecd..e24ee6b58f2 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -1272,7 +1272,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, if (!cx->ensureGeneratorStackSpace()) return JS_FALSE; - JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN); + JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN); switch (op) { case JSGENOP_NEXT: case JSGENOP_SEND: diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 3e42ab75e81..784db7c1ddd 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -623,4 +623,6 @@ OPDEF(JSOP_FORGLOBAL, 246,"forglobal", NULL, 3, 1, 1, 19, JOF_GLOBAL OPDEF(JSOP_BLOCKCHAIN, 247,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT) OPDEF(JSOP_NULLBLOCKCHAIN,248,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE) -/* When changing bytecodes, don't forget to update JSXDR_BYTECODE_VERSION. */ +OPDEF(JSOP_BEGIN, 249,"begin", NULL, 1, 0, 0, 0, JOF_BYTE|JOF_TMPSLOT) + +/* When adding bytecodes, don't forget to update JSXDR_BYTECODE_VERSION. */ diff --git a/js/src/jspropertycache.cpp b/js/src/jspropertycache.cpp index d7fcc5dc841..aa359990489 100644 --- a/js/src/jspropertycache.cpp +++ b/js/src/jspropertycache.cpp @@ -308,7 +308,7 @@ GetAtomFromBytecode(JSContext *cx, jsbytecode *pc, JSOp op, const JSCodeSpec &cs // The method JIT's implementation of instanceof contains an internal lookup // of the prototype property. - if (op == JSOP_INSTANCEOF) + if (op == JSOP_INSTANCEOF || op == JSOP_BEGIN) return cx->runtime->atomState.classPrototypeAtom; ptrdiff_t pcoff = (JOF_TYPE(cs.format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0; diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index b0ab994d614..3f15d89238c 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -10453,13 +10453,6 @@ TraceRecorder::record_EnterFrame() RETURN_STOP_A("recursion started inlining"); } - if (fp->isConstructing()) { - LIns* args[] = { callee_ins, INS_CONSTPTR(&js_ObjectClass), cx_ins }; - LIns* tv_ins = lir->insCall(&js_CreateThisFromTrace_ci, args); - guard(false, lir->insEqP_0(tv_ins), OOM_EXIT); - set(&fp->thisValue(), tv_ins); - } - return ARECORD_CONTINUE; } @@ -11163,6 +11156,20 @@ TraceRecorder::emitNativePropertyOp(const Shape* shape, LIns* obj_ins, guard(true, lir->insEqI_0(status_ins), STATUS_EXIT); } +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_BEGIN() +{ + JSStackFrame* fp = cx->fp(); + if (fp->isConstructing()) { + LIns* callee_ins = get(&cx->fp()->calleeValue()); + LIns* args[] = { callee_ins, INS_CONSTPTR(&js_ObjectClass), cx_ins }; + LIns* tv_ins = lir->insCall(&js_CreateThisFromTrace_ci, args); + guard(false, lir->insEqP_0(tv_ins), OOM_EXIT); + set(&fp->thisValue(), tv_ins); + } + return ARECORD_CONTINUE; +} + JS_REQUIRES_STACK RecordingStatus TraceRecorder::emitNativeCall(JSSpecializedNative* sn, uintN argc, LIns* args[], bool rooted) { diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index 9a6caa082ca..34294aedb63 100644 --- a/js/src/jsxdrapi.h +++ b/js/src/jsxdrapi.h @@ -205,7 +205,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id); * before deserialization of bytecode. If the saved version does not match * the current version, abort deserialization and invalidate the file. */ -#define JSXDR_BYTECODE_VERSION (0xb973c0de - 74) +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 73) /* * Library-private functions. diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 23fba7995c3..35fe192c1d1 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -307,12 +307,6 @@ mjit::Compiler::generatePrologue() } } - if (isConstructing) - constructThis(); - - if (debugMode) - stubCall(stubs::EnterScript); - return Compile_Okay; } @@ -1349,16 +1343,16 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_LOCALDEC) BEGIN_CASE(JSOP_BINDNAME) - jsop_bindname(fullAtomIndex(PC), true); + jsop_bindname(fullAtomIndex(PC)); END_CASE(JSOP_BINDNAME) BEGIN_CASE(JSOP_SETPROP) - jsop_setprop(script->getAtom(fullAtomIndex(PC)), true); + jsop_setprop(script->getAtom(fullAtomIndex(PC))); END_CASE(JSOP_SETPROP) BEGIN_CASE(JSOP_SETNAME) BEGIN_CASE(JSOP_SETMETHOD) - jsop_setprop(script->getAtom(fullAtomIndex(PC)), true); + jsop_setprop(script->getAtom(fullAtomIndex(PC))); END_CASE(JSOP_SETNAME) BEGIN_CASE(JSOP_THROW) @@ -1708,6 +1702,11 @@ mjit::Compiler::generateMethod() break; END_CASE(JSOP_GLOBALINC) + BEGIN_CASE(JSOP_BEGIN) + if (isConstructing) + constructThis(); + END_CASE(JSOP_BEGIN) + default: /* Sorry, this opcode isn't implemented yet. */ #ifdef JS_METHODJIT_SPEW @@ -1931,11 +1930,6 @@ mjit::Compiler::emitReturn(FrameEntry *fe) /* Only the top of the stack can be returned. */ JS_ASSERT_IF(fe, fe == frame.peek(-1)); - if (debugMode) { - prepareStubCall(Uses(0)); - stubCall(stubs::LeaveScript); - } - /* * If there's a function object, deal with the fact that it can escape. * Note that after we've placed the call object, all tracked state can @@ -2368,28 +2362,20 @@ mjit::Compiler::emitStubCmpOp(BoolStub stub, jsbytecode *target, JSOp fused) } void -mjit::Compiler::jsop_setprop_slow(JSAtom *atom, bool usePropCache) +mjit::Compiler::jsop_setprop_slow(JSAtom *atom) { prepareStubCall(Uses(2)); masm.move(ImmPtr(atom), Registers::ArgReg1); - if (usePropCache) - stubCall(STRICT_VARIANT(stubs::SetName)); - else - stubCall(STRICT_VARIANT(stubs::SetPropNoCache)); + stubCall(STRICT_VARIANT(stubs::SetName)); JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH); frame.shimmy(1); } void -mjit::Compiler::jsop_getprop_slow(JSAtom *atom, bool usePropCache) +mjit::Compiler::jsop_getprop_slow() { prepareStubCall(Uses(1)); - if (usePropCache) { - stubCall(stubs::GetProp); - } else { - masm.move(ImmPtr(atom), Registers::ArgReg1); - stubCall(stubs::GetPropNoCache); - } + stubCall(stubs::GetProp); frame.pop(); frame.pushSynced(); } @@ -2454,7 +2440,7 @@ mjit::Compiler::passPICAddress(PICGenInfo &pic) } void -mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache) +mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck) { FrameEntry *top = frame.peek(-1); @@ -2462,7 +2448,7 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache) if (top->isTypeKnown() && top->getKnownType() != JSVAL_TYPE_OBJECT) { JS_ASSERT_IF(atom == cx->runtime->atomState.lengthAtom, top->getKnownType() != JSVAL_TYPE_STRING); - jsop_getprop_slow(atom, usePropCache); + jsop_getprop_slow(); return; } @@ -2478,7 +2464,7 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache) shapeReg = frame.allocReg(); } - PICGenInfo pic(ic::PICInfo::GET, usePropCache); + PICGenInfo pic(ic::PICInfo::GET); /* Guard that the type is an object. */ Jump typeCheck; @@ -2585,7 +2571,7 @@ void mjit::Compiler::jsop_getelem_pic(FrameEntry *obj, FrameEntry *id, RegisterID objReg, RegisterID idReg, RegisterID shapeReg) { - PICGenInfo pic(ic::PICInfo::GETELEM, true); + PICGenInfo pic(ic::PICInfo::GETELEM); pic.objRemat = frame.dataRematInfo(obj); pic.idRemat = frame.dataRematInfo(id); @@ -2698,7 +2684,7 @@ mjit::Compiler::jsop_callprop_generic(JSAtom *atom) RegisterID objReg = frame.copyDataIntoReg(top); RegisterID shapeReg = frame.allocReg(); - PICGenInfo pic(ic::PICInfo::CALL, true); + PICGenInfo pic(ic::PICInfo::CALL); /* Guard that the type is an object. */ pic.typeReg = frame.copyTypeIntoReg(top); @@ -2866,7 +2852,7 @@ mjit::Compiler::jsop_callprop_obj(JSAtom *atom) { FrameEntry *top = frame.peek(-1); - PICGenInfo pic(ic::PICInfo::CALL, true); + PICGenInfo pic(ic::PICInfo::CALL); JS_ASSERT(top->isTypeKnown()); JS_ASSERT(top->getKnownType() == JSVAL_TYPE_OBJECT); @@ -2983,20 +2969,20 @@ mjit::Compiler::jsop_callprop(JSAtom *atom) } void -mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache) +mjit::Compiler::jsop_setprop(JSAtom *atom) { FrameEntry *lhs = frame.peek(-2); FrameEntry *rhs = frame.peek(-1); /* If the incoming type will never PIC, take slow path. */ if (lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_OBJECT) { - jsop_setprop_slow(atom, usePropCache); + jsop_setprop_slow(atom); return; } JSOp op = JSOp(*PC); - PICGenInfo pic(op == JSOP_SETMETHOD ? ic::PICInfo::SETMETHOD : ic::PICInfo::SET, usePropCache); + PICGenInfo pic(op == JSOP_SETMETHOD ? ic::PICInfo::SETMETHOD : ic::PICInfo::SET); pic.atom = atom; /* Guard that the type is an object. */ @@ -3012,11 +2998,19 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache) pic.typeCheck = stubcc.linkExit(j, Uses(2)); stubcc.leave(); + /* + * This gets called from PROPINC/PROPDEC which aren't compatible with + * the normal SETNAME property cache logic. + */ + JSOp op = JSOp(*PC); stubcc.masm.move(ImmPtr(atom), Registers::ArgReg1); - if (usePropCache) + if (op == JSOP_SETNAME || op == JSOP_SETPROP || op == JSOP_SETGNAME || op == + JSOP_SETMETHOD) { stubcc.call(STRICT_VARIANT(stubs::SetName)); - else + } else { stubcc.call(STRICT_VARIANT(stubs::SetPropNoCache)); + } + typeCheck = stubcc.masm.jump(); pic.hasTypeCheck = true; } else { @@ -3119,7 +3113,7 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache) void mjit::Compiler::jsop_name(JSAtom *atom) { - PICGenInfo pic(ic::PICInfo::NAME, true); + PICGenInfo pic(ic::PICInfo::NAME); pic.shapeReg = frame.allocReg(); pic.objReg = frame.allocReg(); @@ -3151,7 +3145,7 @@ mjit::Compiler::jsop_name(JSAtom *atom) void mjit::Compiler::jsop_xname(JSAtom *atom) { - PICGenInfo pic(ic::PICInfo::XNAME, true); + PICGenInfo pic(ic::PICInfo::XNAME); FrameEntry *fe = frame.peek(-1); if (fe->isNotType(JSVAL_TYPE_OBJECT)) { @@ -3193,9 +3187,9 @@ mjit::Compiler::jsop_xname(JSAtom *atom) } void -mjit::Compiler::jsop_bindname(uint32 index, bool usePropCache) +mjit::Compiler::jsop_bindname(uint32 index) { - PICGenInfo pic(ic::PICInfo::BIND, usePropCache); + PICGenInfo pic(ic::PICInfo::BIND); pic.shapeReg = frame.allocReg(); pic.objReg = frame.allocReg(); @@ -3256,9 +3250,9 @@ mjit::Compiler::jsop_xname(JSAtom *atom) } void -mjit::Compiler::jsop_getprop(JSAtom *atom, bool typecheck, bool usePropCache) +mjit::Compiler::jsop_getprop(JSAtom *atom, bool typecheck) { - jsop_getprop_slow(atom, usePropCache); + jsop_getprop_slow(); } bool @@ -3268,13 +3262,13 @@ mjit::Compiler::jsop_callprop(JSAtom *atom) } void -mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache) +mjit::Compiler::jsop_setprop(JSAtom *atom) { - jsop_setprop_slow(atom, usePropCache); + jsop_setprop_slow(atom); } void -mjit::Compiler::jsop_bindname(uint32 index, bool usePropCache) +mjit::Compiler::jsop_bindname(uint32 index) { RegisterID reg = frame.allocReg(); Address scopeChain(JSFrameReg, JSStackFrame::offsetOfScopeChain()); @@ -3286,12 +3280,7 @@ mjit::Compiler::jsop_bindname(uint32 index, bool usePropCache) stubcc.linkExit(j, Uses(0)); stubcc.leave(); - if (usePropCache) { - stubcc.call(stubs::BindName); - } else { - masm.move(ImmPtr(script->getAtom(index)), Registers::ArgReg1); - stubcc.call(stubs::BindNameNoCache); - } + stubcc.call(stubs::BindName); frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg); @@ -3434,7 +3423,7 @@ mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index) jsop_binary(JSOP_SUB, stubs::Sub); // N+1 - jsop_bindname(index, false); + jsop_bindname(index); // V+1 OBJ frame.dup2(); @@ -3446,7 +3435,7 @@ mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index) frame.shift(-1); // OBJ V+1 - jsop_setprop(atom, false); + jsop_setprop(atom); // V+1 if (pop) @@ -3469,7 +3458,7 @@ mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index) jsop_binary(JSOP_ADD, stubs::Add); // N N+1 - jsop_bindname(index, false); + jsop_bindname(index); // N N+1 OBJ frame.dup2(); @@ -3481,7 +3470,7 @@ mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index) frame.shift(-1); // N OBJ N+1 - jsop_setprop(atom, false); + jsop_setprop(atom); // N N+1 frame.pop(); @@ -3527,7 +3516,7 @@ mjit::Compiler::jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index) jsop_binary(JSOP_SUB, stubs::Sub); // OBJ V+1 - jsop_setprop(atom, false); + jsop_setprop(atom); // V+1 if (pop) @@ -3559,7 +3548,7 @@ mjit::Compiler::jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index) frame.dupAt(-2); // OBJ N N+1 OBJ N+1 - jsop_setprop(atom, false); + jsop_setprop(atom); // OBJ N N+1 N+1 frame.popn(2); @@ -4364,7 +4353,7 @@ mjit::Compiler::constructThis() frame.pushTypedPayload(JSVAL_TYPE_OBJECT, calleeReg); // Get callee.prototype. - jsop_getprop(cx->runtime->atomState.classPrototypeAtom, false, false); + jsop_getprop(cx->runtime->atomState.classPrototypeAtom); // Reach into the proto Value and grab a register for its data. FrameEntry *protoFe = frame.peek(-1); diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index 1c5036c1587..7c702145e3d 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -139,8 +139,7 @@ class Compiler : public BaseCompiler #if defined JS_POLYIC struct PICGenInfo { - PICGenInfo(ic::PICInfo::Kind kind, bool usePropCache) - : kind(kind), usePropCache(usePropCache) + PICGenInfo(ic::PICInfo::Kind kind) : kind(kind) { } ic::PICInfo::Kind kind; Label fastPathStart; @@ -158,25 +157,23 @@ class Compiler : public BaseCompiler StateRemat idRemat; Call callReturn; bool hasTypeCheck; - bool usePropCache; ValueRemat vr; # if defined JS_CPU_X64 ic::PICLabels labels; # endif - void copySimpleMembersTo(ic::PICInfo &ic) const { - ic.kind = kind; - ic.shapeReg = shapeReg; - ic.objReg = objReg; - ic.atom = atom; - ic.usePropCache = usePropCache; + void copySimpleMembersTo(ic::PICInfo &pi) const { + pi.kind = kind; + pi.shapeReg = shapeReg; + pi.objReg = objReg; + pi.atom = atom; if (kind == ic::PICInfo::SET) { - ic.u.vr = vr; + pi.u.vr = vr; } else if (kind != ic::PICInfo::NAME) { - ic.u.get.idReg = idReg; - ic.u.get.typeReg = typeReg; - ic.u.get.hasTypeCheck = hasTypeCheck; - ic.u.get.objRemat = objRemat.offset; + pi.u.get.idReg = idReg; + pi.u.get.typeReg = typeReg; + pi.u.get.hasTypeCheck = hasTypeCheck; + pi.u.get.objRemat = objRemat.offset; } } @@ -278,10 +275,10 @@ class Compiler : public BaseCompiler /* Opcode handlers. */ void jumpAndTrace(Jump j, jsbytecode *target, Jump *slowOne = NULL, Jump *slowTwo = NULL); - void jsop_bindname(uint32 index, bool usePropCache); + void jsop_bindname(uint32 index); void jsop_setglobal(uint32 index); void jsop_getglobal(uint32 index); - void jsop_getprop_slow(JSAtom *atom, bool usePropCache = true); + void jsop_getprop_slow(); void jsop_getarg(uint32 index); void jsop_this(); void emitReturn(FrameEntry *fe); @@ -305,10 +302,10 @@ class Compiler : public BaseCompiler void jsop_setelem_slow(); void jsop_getelem_slow(); void jsop_unbrand(); - void jsop_getprop(JSAtom *atom, bool typeCheck = true, bool usePropCache = true); + void jsop_getprop(JSAtom *atom, bool typeCheck = true); void jsop_length(); - void jsop_setprop(JSAtom *atom, bool usePropCache = true); - void jsop_setprop_slow(JSAtom *atom, bool usePropCache = true); + void jsop_setprop(JSAtom *atom); + void jsop_setprop_slow(JSAtom *atom); bool jsop_callprop_slow(JSAtom *atom); bool jsop_callprop(JSAtom *atom); bool jsop_callprop_obj(JSAtom *atom); diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index 9b02cc5c3f1..bcbe3a3308d 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -73,6 +73,9 @@ using namespace js; using namespace js::mjit; using namespace JSC; +static bool +InlineReturn(VMFrame &f, JSBool ok, JSBool popFrame = JS_TRUE); + static jsbytecode * FindExceptionHandler(JSContext *cx) { @@ -174,8 +177,8 @@ top: * either because of a call into an un-JITable script, or because the call is * throwing an exception. */ -static void -InlineReturn(VMFrame &f) +static bool +InlineReturn(VMFrame &f, JSBool ok, JSBool popFrame) { JSContext *cx = f.cx; JSStackFrame *fp = f.regs.fp; @@ -184,9 +187,36 @@ InlineReturn(VMFrame &f) JS_ASSERT(!js_IsActiveWithOrBlock(cx, &fp->scopeChain(), 0)); - Value *newsp = fp->actualArgs() - 1; - newsp[-1] = fp->returnValue(); - cx->stack().popInlineFrame(cx, fp->prev(), newsp); + // Marker for debug support. + if (JS_UNLIKELY(fp->hasHookData())) { + JSInterpreterHook hook; + JSBool status; + + hook = cx->debugHooks->callHook; + if (hook) { + /* + * Do not pass &ok directly as exposing the address inhibits + * optimizations and uninitialised warnings. + */ + status = ok; + hook(cx, fp, JS_FALSE, &status, fp->hookData()); + ok = (status == JS_TRUE); + // CHECK_INTERRUPT_HANDLER(); + } + } + + PutActivationObjects(cx, fp); + + if (fp->isConstructing() && fp->returnValue().isPrimitive()) + fp->setReturnValue(fp->thisValue()); + + if (popFrame) { + Value *newsp = fp->actualArgs() - 1; + newsp[-1] = fp->returnValue(); + cx->stack().popInlineFrame(cx, fp->prev(), newsp); + } + + return ok; } void JS_FASTCALL @@ -342,7 +372,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual) /* Function did not compile... interpret it. */ JSBool ok = Interpret(cx, fp); - InlineReturn(f); + InlineReturn(f, ok); if (!ok) THROWV(NULL); @@ -354,6 +384,7 @@ static inline bool UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc) { JSContext *cx = f.cx; + JSStackFrame *fp = f.fp(); Value *vp = f.regs.sp - (argc + 2); JSObject &callee = vp->toObject(); JSFunction *newfun = callee.getFunctionPrivate(); @@ -381,11 +412,17 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc) if (newfun->isHeavyweight() && !js_GetCallObject(cx, newfp)) return false; + /* Marker for debug support. */ + if (JSInterpreterHook hook = cx->debugHooks->callHook) { + newfp->setHookData(hook(cx, fp, JS_TRUE, 0, + cx->debugHooks->callHookData)); + } + /* Try to compile if not already compiled. */ if (newscript->getJITStatus(newfp->isConstructing()) == JITScript_None) { if (mjit::TryCompile(cx, newfp) == Compile_Error) { /* A runtime exception was thrown, get out. */ - InlineReturn(f); + InlineReturn(f, JS_FALSE); return false; } } @@ -398,7 +435,7 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc) /* Otherwise, run newscript in the interpreter. */ bool ok = !!Interpret(cx, cx->fp()); - InlineReturn(f); + InlineReturn(f, JS_TRUE); *pret = NULL; return ok; @@ -537,18 +574,11 @@ js_InternalThrow(VMFrame &f) // JS function. bool lastFrame = (f.entryFp == f.fp()); js_UnwindScope(cx, 0, cx->throwing); - - // For consistency with Interpret(), always run the script epilogue. - // This simplifies interactions with RunTracer(), since it can assume - // no matter how a function exited (error or not), that the epilogue - // does not need to be run. - ScriptEpilogue(f.cx, f.fp(), false); - if (lastFrame) break; JS_ASSERT(f.regs.sp == cx->regs->sp); - InlineReturn(f); + InlineReturn(f, JS_FALSE); } JS_ASSERT(f.regs.sp == cx->regs->sp); @@ -581,43 +611,21 @@ stubs::CreateThis(VMFrame &f, JSObject *proto) fp->formalArgs()[-1].setObject(*obj); } -void JS_FASTCALL -stubs::EnterScript(VMFrame &f) +static inline void +AdvanceReturnPC(JSContext *cx) { - JSStackFrame *fp = f.fp(); - JSContext *cx = f.cx; - JSInterpreterHook hook = cx->debugHooks->callHook; - if (JS_UNLIKELY(hook != NULL)) - fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData)); - - Probes::enterJSFun(cx, fp->maybeFun()); -} - -void JS_FASTCALL -stubs::LeaveScript(VMFrame &f) -{ - JSStackFrame *fp = f.fp(); - JSContext *cx = f.cx; - Probes::exitJSFun(cx, fp->maybeFun()); - JSInterpreterHook hook = cx->debugHooks->callHook; - - if (hook && fp->hasHookData()) { - JSBool ok = JS_TRUE; - hook(cx, fp, JS_FALSE, &ok, fp->hookData()); - if (!ok) - THROW(); - } + /* Simulate an inline_return by advancing the pc. */ + JS_ASSERT(*cx->regs->pc == JSOP_CALL || + *cx->regs->pc == JSOP_NEW || + *cx->regs->pc == JSOP_EVAL || + *cx->regs->pc == JSOP_APPLY); + cx->regs->pc += JSOP_CALL_LENGTH; } #ifdef JS_TRACER -/* - * Called when an error is in progress and the topmost frame could not handle - * it. This will unwind to a given frame, or find and align to an exception - * handler in the process. - */ static inline bool -HandleErrorInExcessFrame(VMFrame &f, JSStackFrame *stopFp, bool searchedTopmostFrame = true) +HandleErrorInExcessFrames(VMFrame &f, JSStackFrame *stopFp) { JSContext *cx = f.cx; @@ -625,19 +633,14 @@ HandleErrorInExcessFrame(VMFrame &f, JSStackFrame *stopFp, bool searchedTopmostF * Callers of this called either Interpret() or JaegerShot(), which would * have searched for exception handlers already. If we see stopFp, just * return false. Otherwise, pop the frame, since it's guaranteed useless. - * - * Note that this also guarantees ScriptEpilogue() has been called. */ JSStackFrame *fp = cx->fp(); - if (searchedTopmostFrame) { - if (fp == stopFp) - return false; + if (fp == stopFp) + return false; - InlineReturn(f); - } + bool returnOK = InlineReturn(f, false); /* Remove the bottom frame. */ - bool returnOK = false; for (;;) { fp = cx->fp(); @@ -664,8 +667,7 @@ HandleErrorInExcessFrame(VMFrame &f, JSStackFrame *stopFp, bool searchedTopmostF /* Unwind and return. */ returnOK &= bool(js_UnwindScope(cx, 0, returnOK || cx->throwing)); - returnOK = ScriptEpilogue(cx, fp, returnOK); - InlineReturn(f); + returnOK = InlineReturn(f, returnOK); } JS_ASSERT(&f.regs == cx->regs); @@ -674,7 +676,6 @@ HandleErrorInExcessFrame(VMFrame &f, JSStackFrame *stopFp, bool searchedTopmostF return returnOK; } -/* Returns whether the current PC has method JIT'd code. */ static inline void * AtSafePoint(JSContext *cx) { @@ -686,10 +687,6 @@ AtSafePoint(JSContext *cx) return script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs->pc); } -/* - * Interprets until either a safe point is reached that has method JIT'd - * code, or the current frame tries to return. - */ static inline JSBool PartialInterpret(VMFrame &f) { @@ -698,7 +695,6 @@ PartialInterpret(VMFrame &f) #ifdef DEBUG JSScript *script = fp->script(); - JS_ASSERT(!fp->finishedInInterpreter()); JS_ASSERT(fp->hasImacropc() || !script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs->pc)); #endif @@ -711,7 +707,6 @@ PartialInterpret(VMFrame &f) JS_STATIC_ASSERT(JSOP_NOP == 0); -/* Returns whether the current PC would return, popping the frame. */ static inline JSOp FrameIsFinished(JSContext *cx) { @@ -723,134 +718,39 @@ FrameIsFinished(JSContext *cx) : JSOP_NOP; } - -/* Simulate an inline_return by advancing the pc. */ -static inline void -AdvanceReturnPC(JSContext *cx) -{ - JS_ASSERT(*cx->regs->pc == JSOP_CALL || - *cx->regs->pc == JSOP_NEW || - *cx->regs->pc == JSOP_EVAL || - *cx->regs->pc == JSOP_APPLY); - cx->regs->pc += JSOP_CALL_LENGTH; -} - - -/* - * Given a frame that is about to return, make sure its return value and - * activation objects are fixed up. Then, pop the frame and advance the - * current PC. Note that while we could enter the JIT at this point, the - * logic would still be necessary for the interpreter, so it's easier - * (and faster) to finish frames in C++ even if at a safe point here. - */ -static bool -HandleFinishedFrame(VMFrame &f, JSStackFrame *entryFrame) -{ - JSContext *cx = f.cx; - - JS_ASSERT(FrameIsFinished(cx)); - - /* - * This is the most difficult and complicated piece of the tracer - * integration, and historically has been very buggy. The problem is that - * although this frame has to be popped (see RemoveExcessFrames), it may - * be at a JSOP_RETURN opcode, and it might not have ever been executed. - * That is, fp->rval may not be set to the top of the stack, and if it - * has, the stack has already been decremented. Note that fp->rval is not - * the only problem: the epilogue may never have been executed. - * - * Here are the edge cases and whether the frame has been exited cleanly: - * 1. No: A trace exited directly before a RETURN op, and the - * interpreter never ran. - * 2. Yes: The interpreter exited cleanly. - * 3. No: The interpreter exited on a safe point. LEAVE_ON_SAFE_POINT - * is not used in between JSOP_RETURN and advancing the PC, - * therefore, it cannot have been run if at a safe point. - * 4. No: Somewhere in the RunTracer call tree, we removed a frame, - * and we returned to a JSOP_RETURN opcode. Note carefully - * that in this situation, FrameIsFinished() returns true! - * 5. Yes: The function exited in the method JIT. However, in this - * case, we'll never enter HandleFinishedFrame(): we always - * immediately pop JIT'd frames. - * - * Since the only scenario where this fixup is NOT needed is a normal exit - * from the interpreter, we can cleanly check for this scenario by checking - * a bit it sets in the frame. - */ - bool returnOK = true; - if (!cx->fp()->finishedInInterpreter()) { - if (JSOp(*cx->regs->pc) == JSOP_RETURN) - cx->fp()->setReturnValue(f.regs.sp[-1]); - - returnOK = ScriptEpilogue(cx, cx->fp(), true); - } - - JS_ASSERT_IF(cx->fp()->isFunctionFrame() && - !cx->fp()->isEvalFrame(), - !cx->fp()->hasCallObj()); - - if (cx->fp() != entryFrame) { - InlineReturn(f); - AdvanceReturnPC(cx); - } - - return returnOK; -} - -/* - * Given a frame newer than the entry frame, try to finish it. If it's at a - * return position, pop the frame. If it's at a safe point, execute it in - * Jaeger code. Otherwise, try to interpret until a safe point. - * - * While this function is guaranteed to make progress, it may not actually - * finish or pop the current frame. It can either: - * 1) Finalize a finished frame, or - * 2) Finish and finalize the frame in the Method JIT, or - * 3) Interpret, which can: - * a) Propagate an error, or - * b) Finish the frame, but not finalize it, or - * c) Abruptly leave at any point in the frame, or in a newer frame - * pushed by a call, that has method JIT'd code. - */ -static bool -EvaluateExcessFrame(VMFrame &f, JSStackFrame *entryFrame) -{ - JSContext *cx = f.cx; - JSStackFrame *fp = cx->fp(); - - /* - * A "finished" frame is when the interpreter rested on a STOP, - * RETURN, RETRVAL, etc. We check for finished frames BEFORE looking - * for a safe point. If the frame was finished, we could have already - * called ScriptEpilogue(), and entering the JIT could call it twice. - */ - if (!fp->hasImacropc() && FrameIsFinished(cx)) - return HandleFinishedFrame(f, entryFrame); - - if (void *ncode = AtSafePoint(cx)) { - if (!JaegerShotAtSafePoint(cx, ncode)) - return false; - InlineReturn(f); - AdvanceReturnPC(cx); - return true; - } - - return PartialInterpret(f); -} - -/* - * Evaluate frames newer than the entry frame until all are gone. This will - * always leave f.regs.fp == entryFrame. - */ static bool FinishExcessFrames(VMFrame &f, JSStackFrame *entryFrame) { JSContext *cx = f.cx; - while (cx->fp() != entryFrame || entryFrame->hasImacropc()) { - if (!EvaluateExcessFrame(f, entryFrame)) { - if (!HandleErrorInExcessFrame(f, entryFrame)) - return false; + if (void *ncode = AtSafePoint(cx)) { + if (!JaegerShotAtSafePoint(cx, ncode)) { + if (!HandleErrorInExcessFrames(f, entryFrame)) + return false; + + /* Could be anywhere - restart outer loop. */ + continue; + } + InlineReturn(f, JS_TRUE); + AdvanceReturnPC(cx); + } else { + if (!PartialInterpret(f)) { + if (!HandleErrorInExcessFrames(f, entryFrame)) + return false; + } else if (cx->fp() != entryFrame) { + /* + * Partial interpret could have dropped us anywhere. Deduce the + * edge case: at a RETURN, needing to pop a frame. + */ + JS_ASSERT(!cx->fp()->hasImacropc()); + if (FrameIsFinished(cx)) { + JSOp op = JSOp(*cx->regs->pc); + if (op == JSOP_RETURN && !cx->fp()->isBailedAtReturn()) + cx->fp()->setReturnValue(f.regs.sp[-1]); + InlineReturn(f, JS_TRUE); + AdvanceReturnPC(cx); + } + } } } @@ -925,18 +825,18 @@ RunTracer(VMFrame &f) DisableTraceHint(f, mic); #endif - // Even though ExecuteTree() bypasses the interpreter, it should propagate - // error failures correctly. - JS_ASSERT_IF(cx->throwing, tpa == TPA_Error); + if ((tpa == TPA_RanStuff || tpa == TPA_Recorded) && cx->throwing) + tpa = TPA_Error; + /* Sync up the VMFrame's view of cx->fp(). */ f.fp() = cx->fp(); - JS_ASSERT(f.fp() == cx->fp()); + switch (tpa) { case TPA_Nothing: return NULL; case TPA_Error: - if (!HandleErrorInExcessFrame(f, entryFrame, f.fp()->finishedInInterpreter())) + if (!HandleErrorInExcessFrames(f, entryFrame)) THROWV(NULL); JS_ASSERT(!cx->fp()->hasImacropc()); break; @@ -974,26 +874,32 @@ RunTracer(VMFrame &f) THROWV(NULL); /* IMacros are guaranteed to have been removed by now. */ - JS_ASSERT(f.fp() == entryFrame); JS_ASSERT(!entryFrame->hasImacropc()); - /* Step 2. If entryFrame is done, use a special path to return to EnterMethodJIT(). */ - if (FrameIsFinished(cx)) { - if (!HandleFinishedFrame(f, entryFrame)) - THROWV(NULL); + /* Step 2. If entryFrame is at a safe point, just leave. */ + if (void *ncode = AtSafePoint(cx)) + return ncode; + + /* Step 3. If entryFrame is at a RETURN, then leave slightly differently. */ + if (JSOp op = FrameIsFinished(cx)) { + /* We're not guaranteed that the RETURN was run. */ + if (op == JSOP_RETURN && !entryFrame->isBailedAtReturn()) + entryFrame->setReturnValue(f.regs.sp[-1]); + + /* Cleanup activation objects on the frame unless it's owned by an Invoke. */ + if (f.fp() != f.entryFp) { + if (!InlineReturn(f, JS_TRUE, JS_FALSE)) + THROWV(NULL); + } void *retPtr = JS_FUNC_TO_DATA_PTR(void *, InjectJaegerReturn); *f.returnAddressLocation() = retPtr; return NULL; } - /* Step 3. If entryFrame is at a safe point, just leave. */ - if (void *ncode = AtSafePoint(cx)) - return ncode; - /* Step 4. Do a partial interp, then restart the whole process. */ if (!PartialInterpret(f)) { - if (!HandleErrorInExcessFrame(f, entryFrame)) + if (!HandleErrorInExcessFrames(f, entryFrame)) THROWV(NULL); } diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index d2c8ed7b1e7..a672f6473e2 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -690,10 +690,10 @@ class SetPropCompiler : public PICStubCompiler class GetPropCompiler : public PICStubCompiler { - JSObject *obj; - JSAtom *atom; - VoidStubPIC stub; - int lastStubSecondShapeGuard; + JSObject *obj; + JSAtom *atom; + void *stub; + int lastStubSecondShapeGuard; static int32 inlineShapeOffset(ic::PICInfo &pic) { #if defined JS_NUNBOX32 @@ -732,12 +732,17 @@ class GetPropCompiler : public PICStubCompiler } public: + GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom, + VoidStub stub) + : PICStubCompiler("getprop", f, script, pic), obj(obj), atom(atom), + stub(JS_FUNC_TO_DATA_PTR(void *, stub)), + lastStubSecondShapeGuard(pic.secondShapeGuard) + { } + GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom, VoidStubPIC stub) - : PICStubCompiler(pic.kind == ic::PICInfo::CALL ? "callprop" : "getprop", f, script, pic), - obj(obj), - atom(atom), - stub(stub), + : PICStubCompiler("callprop", f, script, pic), obj(obj), atom(atom), + stub(JS_FUNC_TO_DATA_PTR(void *, stub)), lastStubSecondShapeGuard(pic.secondShapeGuard) { } @@ -2007,24 +2012,6 @@ class BindNameCompiler : public PICStubCompiler } }; -static void JS_FASTCALL -DisabledLengthIC(VMFrame &f, ic::PICInfo *pic) -{ - stubs::Length(f); -} - -static void JS_FASTCALL -DisabledGetPropIC(VMFrame &f, ic::PICInfo *pic) -{ - stubs::GetProp(f); -} - -static void JS_FASTCALL -DisabledGetPropICNoCache(VMFrame &f, ic::PICInfo *pic) -{ - stubs::GetPropNoCache(f, pic->atom); -} - void JS_FASTCALL ic::GetProp(VMFrame &f, ic::PICInfo *pic) { @@ -2033,7 +2020,7 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic) JSAtom *atom = pic->atom; if (atom == f.cx->runtime->atomState.lengthAtom) { if (f.regs.sp[-1].isString()) { - GetPropCompiler cc(f, script, NULL, *pic, NULL, DisabledLengthIC); + GetPropCompiler cc(f, script, NULL, *pic, NULL, stubs::Length); if (!cc.generateStringLengthStub()) { cc.disable("error"); THROW(); @@ -2044,7 +2031,7 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic) } else if (!f.regs.sp[-1].isPrimitive()) { JSObject *obj = &f.regs.sp[-1].toObject(); if (obj->isArray() || (obj->isArguments() && !obj->isArgsLengthOverridden())) { - GetPropCompiler cc(f, script, obj, *pic, NULL, DisabledLengthIC); + GetPropCompiler cc(f, script, obj, *pic, NULL, stubs::Length); if (obj->isArray()) { if (!cc.generateArrayLengthStub()) { cc.disable("error"); @@ -2069,10 +2056,7 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic) THROW(); if (pic->shouldGenerate()) { - VoidStubPIC stub = pic->usePropCache - ? DisabledGetPropIC - : DisabledGetPropICNoCache; - GetPropCompiler cc(f, script, obj, *pic, atom, stub); + GetPropCompiler cc(f, script, obj, *pic, atom, stubs::GetProp); if (!cc.update()) { cc.disable("error"); THROW(); @@ -2116,16 +2100,16 @@ ic::GetElem(VMFrame &f, ic::PICInfo *pic) template static void JS_FASTCALL -DisabledSetPropIC(VMFrame &f, ic::PICInfo *pic) +SetPropDumb(VMFrame &f, ic::PICInfo *pic) { - stubs::SetName(f, pic->atom); + stubs::SetPropNoCache(f, pic->atom); } template static void JS_FASTCALL -DisabledSetPropICNoCache(VMFrame &f, ic::PICInfo *pic) +SetPropSlow(VMFrame &f, ic::PICInfo *pic) { - stubs::SetPropNoCache(f, pic->atom); + stubs::SetName(f, pic->atom); } void JS_FASTCALL @@ -2147,9 +2131,22 @@ ic::SetProp(VMFrame &f, ic::PICInfo *pic) // cache can't handle a GET and SET from the same scripted PC. // - VoidStubPIC stub = pic->usePropCache - ? STRICT_VARIANT(DisabledSetPropIC) - : STRICT_VARIANT(DisabledSetPropICNoCache); + VoidStubPIC stub; + switch (JSOp(*f.regs.pc)) { + case JSOP_PROPINC: + case JSOP_PROPDEC: + case JSOP_INCPROP: + case JSOP_DECPROP: + case JSOP_NAMEINC: + case JSOP_NAMEDEC: + case JSOP_INCNAME: + case JSOP_DECNAME: + stub = STRICT_VARIANT(SetPropDumb); + break; + default: + stub = STRICT_VARIANT(SetPropSlow); + break; + } SetPropCompiler cc(f, script, obj, *pic, pic->atom, stub); if (!cc.update()) { @@ -2162,7 +2159,7 @@ ic::SetProp(VMFrame &f, ic::PICInfo *pic) } static void JS_FASTCALL -DisabledCallPropIC(VMFrame &f, ic::PICInfo *pic) +CallPropSlow(VMFrame &f, ic::PICInfo *pic) { stubs::CallProp(f, pic->atom); } @@ -2257,7 +2254,7 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pic) } } - GetPropCompiler cc(f, script, &objv.toObject(), *pic, pic->atom, DisabledCallPropIC); + GetPropCompiler cc(f, script, &objv.toObject(), *pic, pic->atom, CallPropSlow); if (usePIC) { if (lval.isObject()) { if (!cc.update()) { @@ -2286,13 +2283,13 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pic) } static void JS_FASTCALL -DisabledNameIC(VMFrame &f, ic::PICInfo *pic) +SlowName(VMFrame &f, ic::PICInfo *pic) { stubs::Name(f); } static void JS_FASTCALL -DisabledXNameIC(VMFrame &f, ic::PICInfo *pic) +SlowXName(VMFrame &f, ic::PICInfo *pic) { stubs::GetProp(f); } @@ -2305,7 +2302,7 @@ ic::XName(VMFrame &f, ic::PICInfo *pic) /* GETXPROP is guaranteed to have an object. */ JSObject *obj = &f.regs.sp[-1].toObject(); - ScopeNameCompiler cc(f, script, obj, *pic, pic->atom, DisabledXNameIC); + ScopeNameCompiler cc(f, script, obj, *pic, pic->atom, SlowXName); if (!cc.updateForXName()) { cc.disable("error"); @@ -2323,7 +2320,7 @@ ic::Name(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, DisabledNameIC); + ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, SlowName); if (!cc.updateForName()) { cc.disable("error"); @@ -2337,26 +2334,17 @@ ic::Name(VMFrame &f, ic::PICInfo *pic) } static void JS_FASTCALL -DisabledBindNameIC(VMFrame &f, ic::PICInfo *pic) +SlowBindName(VMFrame &f, ic::PICInfo *pic) { stubs::BindName(f); } -static void JS_FASTCALL -DisabledBindNameICNoCache(VMFrame &f, ic::PICInfo *pic) -{ - stubs::BindNameNoCache(f, pic->atom); -} - void JS_FASTCALL ic::BindName(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - VoidStubPIC stub = pic->usePropCache - ? DisabledBindNameIC - : DisabledBindNameICNoCache; - BindNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, stub); + BindNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, SlowBindName); JSObject *obj = cc.update(); if (!obj) { diff --git a/js/src/methodjit/PolyIC.h b/js/src/methodjit/PolyIC.h index 77fdd5af99c..9e3d63e03be 100644 --- a/js/src/methodjit/PolyIC.h +++ b/js/src/methodjit/PolyIC.h @@ -236,9 +236,6 @@ struct PICInfo { // last stub. bool shapeRegHasBaseShape : 1; - // True if can use the property cache. - bool usePropCache : 1; - // State flags. bool hit : 1; // this PIC has been executed bool inlinePathPatched : 1; // inline path has been patched diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 36e9dffc169..12ba0a0e66c 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -99,15 +99,6 @@ stubs::BindName(VMFrame &f) f.regs.sp[-1].setObject(*obj); } -void JS_FASTCALL -stubs::BindNameNoCache(VMFrame &f, JSAtom *atom) -{ - JSObject *obj = js_FindIdentifierBase(f.cx, &f.fp()->scopeChain(), ATOM_TO_JSID(atom)); - if (!obj) - THROW(); - f.regs.sp[0].setObject(*obj); -} - JSObject * JS_FASTCALL stubs::BindGlobalName(VMFrame &f) { @@ -2077,20 +2068,6 @@ stubs::GetProp(VMFrame &f) THROW(); } -void JS_FASTCALL -stubs::GetPropNoCache(VMFrame &f, JSAtom *atom) -{ - JSContext *cx = f.cx; - - Value *vp = &f.regs.sp[-1]; - JSObject *obj = ValueToObject(cx, vp); - if (!obj) - THROW(); - - if (!obj->getProperty(cx, ATOM_TO_JSID(atom), vp)) - THROW(); -} - void JS_FASTCALL stubs::CallProp(VMFrame &f, JSAtom *origAtom) { diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index 6d148c298a0..7ee6b00670a 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -65,8 +65,6 @@ void JS_FASTCALL SlowNew(VMFrame &f, uint32 argc); void JS_FASTCALL SlowCall(VMFrame &f, uint32 argc); void * JS_FASTCALL UncachedNew(VMFrame &f, uint32 argc); void * JS_FASTCALL UncachedCall(VMFrame &f, uint32 argc); -void JS_FASTCALL EnterScript(VMFrame &f); -void JS_FASTCALL LeaveScript(VMFrame &f); /* * Result struct for UncachedXHelper. @@ -114,7 +112,6 @@ void * JS_FASTCALL LookupSwitch(VMFrame &f, jsbytecode *pc); void * JS_FASTCALL TableSwitch(VMFrame &f, jsbytecode *origPc); void JS_FASTCALL BindName(VMFrame &f); -void JS_FASTCALL BindNameNoCache(VMFrame &f, JSAtom *atom); JSObject * JS_FASTCALL BindGlobalName(VMFrame &f); template void JS_FASTCALL SetName(VMFrame &f, JSAtom *atom); template void JS_FASTCALL SetPropNoCache(VMFrame &f, JSAtom *atom); @@ -122,7 +119,6 @@ template void JS_FASTCALL SetGlobalName(VMFrame &f, JSAtom *atom) template void JS_FASTCALL SetGlobalNameDumb(VMFrame &f, JSAtom *atom); void JS_FASTCALL Name(VMFrame &f); void JS_FASTCALL GetProp(VMFrame &f); -void JS_FASTCALL GetPropNoCache(VMFrame &f, JSAtom *atom); void JS_FASTCALL GetElem(VMFrame &f); void JS_FASTCALL CallElem(VMFrame &f); template void JS_FASTCALL SetElem(VMFrame &f); diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js b/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js index 1d19aeebeb1..fcb2eab3ef1 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js @@ -3,5 +3,5 @@ function main() { return "failure"; } /* JSOP_RETURN in main. */ -trap(main, 3, "'success'"); +trap(main, 4, "'success'"); assertEq(main(), "success"); diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-parent-from-trap.js b/js/src/trace-test/tests/jaeger/bug563000/trap-parent-from-trap.js index d6ded288128..f1b626b8112 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-parent-from-trap.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-parent-from-trap.js @@ -10,8 +10,8 @@ function child() { function parent() { x = "failure2"; } -/* First op in parent. */ -trap(parent, 0, "child()"); +/* First op in parent: because of JSOP_BEGIN, it is op 1. */ +trap(parent, 1, "child()"); function success() { x = "success"; diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js b/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js index 8a9caaaafda..46de9b96e4c 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js @@ -6,14 +6,14 @@ function doNothing() { } function myparent(nested) { if (nested) { /* JSOP_CALL to doNothing in myparent with nested = true. */ - trap(myparent, 24, "success()"); + trap(myparent, 25, "success()"); doNothing(); } else { doNothing(); } } /* JSOP_CALL to doNothing in myparent with nested = false. */ -trap(myparent, 35, "myparent(true)"); +trap(myparent, 36, "myparent(true)"); function success() { x = "success"; diff --git a/js/src/trace-test/tests/jaeger/deepBailAfterRunTracer.js b/js/src/trace-test/tests/jaeger/deepBailAfterRunTracer.js deleted file mode 100644 index 63399a47432..00000000000 --- a/js/src/trace-test/tests/jaeger/deepBailAfterRunTracer.js +++ /dev/null @@ -1,25 +0,0 @@ -var o = { }; -for (var i = 0; i <= 50; i++) - o[i] = i; - -Object.defineProperty(o, "51", { get: assertEq }); - -var threw = 0; -function g(o, i) { - try { - assertEq(o[i], i); - } catch (e) { - threw++; - } -} - -function f() { - for (var i = 0; i <= 51; i++) - g(o, i); -} - -f(); -f(); -f(); -assertEq(threw, 3); -