diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 02a419e7e0f..01a68827a39 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -434,7 +434,7 @@ void FrameRegsIter::incSlow(JSStackFrame *fp, JSStackFrame *prev) { JS_ASSERT(prev); - JS_ASSERT(curpc == prev->savedpc_); + JS_ASSERT(curpc == curfp->pc(cx, fp)); JS_ASSERT(fp == curseg->getInitialFrame()); /* @@ -2035,16 +2035,10 @@ void JSContext::pushSegmentAndFrame(js::StackSegment *newseg, JSFrameRegs &newregs) { JS_ASSERT(regs != &newregs); - if (hasActiveSegment()) { - JS_ASSERT(regs->fp->savedpc_ == JSStackFrame::sInvalidpc); - regs->fp->savedpc_ = regs->pc; + if (hasActiveSegment()) currentSegment->suspend(regs); - } newseg->setPreviousInContext(currentSegment); currentSegment = newseg; -#ifdef DEBUG - newregs.fp->savedpc_ = JSStackFrame::sInvalidpc; -#endif setCurrentRegs(&newregs); newseg->joinContext(this, newregs.fp); } @@ -2054,7 +2048,6 @@ JSContext::popSegmentAndFrame() { JS_ASSERT(currentSegment->maybeContext() == this); JS_ASSERT(currentSegment->getInitialFrame() == regs->fp); - JS_ASSERT(regs->fp->savedpc_ == JSStackFrame::sInvalidpc); currentSegment->leaveContext(); currentSegment = currentSegment->getPreviousInContext(); if (currentSegment) { @@ -2063,9 +2056,6 @@ JSContext::popSegmentAndFrame() } else { setCurrentRegs(currentSegment->getSuspendedRegs()); currentSegment->resume(); -#ifdef DEBUG - regs->fp->savedpc_ = JSStackFrame::sInvalidpc; -#endif } } else { JS_ASSERT(regs->fp->prev() == NULL); @@ -2078,8 +2068,6 @@ JSContext::saveActiveSegment() { JS_ASSERT(hasActiveSegment()); currentSegment->save(regs); - JS_ASSERT(regs->fp->savedpc_ == JSStackFrame::sInvalidpc); - regs->fp->savedpc_ = regs->pc; setCurrentRegs(NULL); } @@ -2089,9 +2077,6 @@ JSContext::restoreSegment() js::StackSegment *ccs = currentSegment; setCurrentRegs(ccs->getSuspendedRegs()); ccs->restore(); -#ifdef DEBUG - regs->fp->savedpc_ = JSStackFrame::sInvalidpc; -#endif } JSGenerator * diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index fa745a54787..4f869917b77 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -883,6 +883,7 @@ JS_STATIC_ASSERT(StackSpace::CAPACITY_VALS % StackSpace::COMMIT_VALS == 0); */ class FrameRegsIter { + JSContext *cx; StackSegment *curseg; JSStackFrame *curfp; Value *cursp; @@ -2090,6 +2091,9 @@ struct JSContext /* Undoes calls to suspendActiveSegment. */ void restoreSegment(); + /* Get the frame whose prev() is fp, which may be in any segment. */ + inline JSStackFrame *computeNextFrame(JSStackFrame *fp); + /* * Perform a linear search of all frames in all segments in the given context * for the given frame, returning the segment, if found, and null otherwise. diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index 8cff08e975f..c5134c05387 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -71,6 +71,21 @@ JSContext::ensureGeneratorStackSpace() return ok; } +JSStackFrame * +JSContext::computeNextFrame(JSStackFrame *fp) +{ + JSStackFrame *next = NULL; + for (js::StackSegment *ss = currentSegment; ; ss = ss->getPreviousInContext()) { + JSStackFrame *end = ss->getInitialFrame()->prev(); + for (JSStackFrame *f = ss->getCurrentFrame(); f != end; next = f, f = f->prev()) { + if (f == fp) + return next; + } + if (end != ss->getPreviousInContext()->getCurrentFrame()) + next = NULL; + } +} + namespace js { JS_REQUIRES_STACK JS_ALWAYS_INLINE JSFrameRegs * @@ -309,16 +324,10 @@ StackSpace::pushInvokeFrame(JSContext *cx, const CallArgs &args, JS_ASSERT(firstUnused() == args.argv() + args.argc()); JSStackFrame *fp = fg->regs_.fp; - JSStackFrame *prev = cx->maybefp(); - fp->prev_ = prev; + fp->setPrev(cx->regs); if (JS_UNLIKELY(!currentSegment->inContext())) { cx->pushSegmentAndFrame(currentSegment, fg->regs_); } else { -#ifdef DEBUG - fp->savedpc_ = JSStackFrame::sInvalidpc; - JS_ASSERT(prev->savedpc_ == JSStackFrame::sInvalidpc); -#endif - prev->savedpc_ = cx->regs->pc; fg->prevRegs_ = cx->regs; cx->setCurrentRegs(&fg->regs_); } @@ -339,10 +348,8 @@ StackSpace::popInvokeFrame(const InvokeFrameGuard &fg) } else { JS_ASSERT(&fg.regs_ == cx->regs); JS_ASSERT(fp->prev_ == fg.prevRegs_->fp); + JS_ASSERT(fp->prevpc() == fg.prevRegs_->pc); cx->setCurrentRegs(fg.prevRegs_); -#ifdef DEBUG - cx->fp()->savedpc_ = JSStackFrame::sInvalidpc; -#endif } } @@ -384,11 +391,8 @@ StackSpace::pushInlineFrame(JSContext *cx, JSScript *script, JSStackFrame *fp, JS_ASSERT(isCurrentAndActive(cx)); JS_ASSERT(cx->regs == regs && script == fp->script()); - regs->fp->savedpc_ = regs->pc; - fp->prev_ = regs->fp; -#ifdef DEBUG - fp->savedpc_ = JSStackFrame::sInvalidpc; -#endif + fp->setPrev(regs); + regs->fp = fp; regs->pc = script->code; regs->sp = fp->slots() + script->nfixed; @@ -400,17 +404,13 @@ StackSpace::popInlineFrame(JSContext *cx, JSStackFrame *prev, Value *newsp) JS_ASSERT(isCurrentAndActive(cx)); JS_ASSERT(cx->hasActiveSegment()); JS_ASSERT(cx->regs->fp->prev_ == prev); - JS_ASSERT(cx->regs->fp->savedpc_ == JSStackFrame::sInvalidpc); JS_ASSERT(!cx->regs->fp->hasImacropc()); JS_ASSERT(prev->base() <= newsp && newsp <= cx->regs->fp->formalArgsEnd()); JSFrameRegs *regs = cx->regs; + regs->pc = prev->pc(cx, regs->fp); regs->fp = prev; - regs->pc = prev->savedpc_; regs->sp = newsp; -#ifdef DEBUG - prev->savedpc_ = JSStackFrame::sInvalidpc; -#endif } JS_ALWAYS_INLINE Value * @@ -442,6 +442,7 @@ StackSpace::getStackLimit(JSContext *cx) JS_REQUIRES_STACK inline FrameRegsIter::FrameRegsIter(JSContext *cx) + : cx(cx) { curseg = cx->getCurrentSegment(); if (JS_UNLIKELY(!curseg || !curseg->isActive())) { @@ -463,7 +464,7 @@ FrameRegsIter::operator++() if (!prev) return *this; - curpc = prev->savedpc_; + curpc = curfp->pc(cx, fp); if (JS_UNLIKELY(fp == curseg->getInitialFrame())) { incSlow(fp, prev); diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 548eaf4473f..c31d5d4e0cc 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -274,6 +274,7 @@ js_PutArgsObject(JSContext *cx, JSStackFrame *fp) } else { JS_ASSERT(!argsobj.getPrivate()); } + fp->clearArgsObj(); } #ifdef JS_TRACER @@ -1157,6 +1158,7 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp) } callobj.setPrivate(NULL); + fp->clearCallObj(); } JSBool JS_FASTCALL diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 171242f8c2f..5b78f376f24 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -93,6 +93,10 @@ #include "jsautooplen.h" +#if defined(JS_METHODJIT) && defined(JS_MONOIC) +#include "methodjit/MonoIC.h" +#endif + using namespace js; using namespace js::gc; @@ -100,10 +104,61 @@ using namespace js::gc; #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ #ifdef DEBUG -jsbytecode *const JSStackFrame::sInvalidpc = (jsbytecode *)0xbeef; JSObject *const JSStackFrame::sInvalidScopeChain = (JSObject *)0xbeef; #endif +jsbytecode * +JSStackFrame::pc(JSContext *cx, JSStackFrame *next) +{ + JS_ASSERT_IF(next, next->prev_ == this); + JS_ASSERT(cx->containingSegment(this) != NULL); + + JSFrameRegs *regs; + if (cx->regs) { + regs = cx->regs; + } else { + StackSegment *segment = cx->getCurrentSegment(); + regs = segment->getSuspendedRegs(); + } + + if (this == regs->fp) + return regs->pc; + + if (!next) + next = cx->computeNextFrame(this); + + if (next->flags_ & JSFRAME_HAS_PREVPC) + return next->prevpc_; + +#if defined(JS_METHODJIT) && defined(JS_MONOIC) + JSScript *script = this->script(); + size_t low = 0; + size_t high = script->jit->nCallICs; + while (high > low + 1) { + /* Could overflow here on a script with 2 billion calls. Oh well. */ + size_t mid = (high + low) / 2; + void *entry = script->callICs[mid].funGuard.executableAddress(); + + /* + * Use >= here as the return address of the call is likely to be + * the start address of the next (possibly IC'ed) operation. + */ + if (entry >= next->ncode_) + high = mid; + else + low = mid; + } + + js::mjit::ic::CallICInfo &callIC = script->callICs[low]; + + JS_ASSERT((uint8*)callIC.funGuard.executableAddress() + callIC.joinPointOffset == next->ncode_); + return callIC.pc; +#else + JS_NOT_REACHED("Unknown PC for frame"); + return NULL; +#endif +} + /* * We can't determine in advance which local variables can live on the stack and * be freed when their dynamic scope ends, and which will be closed over and @@ -670,7 +725,7 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script, JSObject *initialVarObj; if (prev) { JS_ASSERT(chain == &prev->scopeChain()); - frame.fp()->initEvalFrame(script, prev, flags); + frame.fp()->initEvalFrame(script, prev, prev->pc(cx), flags); /* * We want to call |prev->varobj()|, but this requires knowing the diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index d1c0cb16440..33e645b8b53 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -91,10 +91,22 @@ enum JSFrameFlags /* Lazy frame initialization */ JSFRAME_HAS_IMACRO_PC = 0x8000, /* frame has imacpc value available */ - JSFRAME_HAS_CALL_OBJ = 0x10000, /* frame has a callobj in JSStackFrame::exec */ + JSFRAME_HAS_CALL_OBJ = 0x10000, /* frame has a callobj reachable from scopeChain_ */ JSFRAME_HAS_ARGS_OBJ = 0x20000, /* frame has an argsobj in JSStackFrame::args */ JSFRAME_HAS_HOOK_DATA = 0x40000, /* frame has hookData_ set */ - JSFRAME_HAS_ANNOTATION = 0x80000 /* frame has annotation_ set */ + JSFRAME_HAS_ANNOTATION = 0x80000, /* frame has annotation_ set */ + + /* + * Whether the prevpc_ value is valid. If not set, the ncode_ value is + * valid and prevpc_ can be recovered using it. + */ + JSFRAME_HAS_PREVPC = 0x100000, + + /* + * For use by compiled functions, at function exit indicates whether rval_ + * has been assigned to. Otherwise the return value is carried in registers. + */ + JSFRAME_RVAL_ASSIGNED = 0x200000 }; /* @@ -116,16 +128,16 @@ struct JSStackFrame } args; JSObject *scopeChain_; /* current scope chain */ JSStackFrame *prev_; /* previous cx->regs->fp */ - jsbytecode *savedpc_; /* only valid if cx->fp != this */ + void *ncode_; /* return address for method JIT */ /* Lazily initialized */ - js::Value rval_; /* (TODO bug 595073) return value of the frame */ + js::Value rval_; /* return value of the frame */ + jsbytecode *prevpc_; /* pc of previous frame*/ jsbytecode *imacropc_; /* pc of macro caller */ void *hookData_; /* closure returned by call hook */ void *annotation_; /* perhaps remove with bug 546848 */ /* TODO: remove */ - void *ncode_; /* bug 535912 */ JSObject *blockChain_; /* bug 540675 */ #if JS_BITS_PER_WORD == 32 @@ -195,7 +207,8 @@ struct JSStackFrame inline void initCallFrameLatePrologue(); /* Used for eval. */ - inline void initEvalFrame(JSScript *script, JSStackFrame *prev, uint32 flags); + inline void initEvalFrame(JSScript *script, JSStackFrame *prev, + jsbytecode *prevpc, uint32 flags); inline void initGlobalFrame(JSScript *script, JSObject &chain, uint32 flags); /* Used when activating generators. */ @@ -222,8 +235,26 @@ struct JSStackFrame return prev_; } - void repointGeneratorFrameDown(JSStackFrame *prev) { + void setPrev(JSStackFrame *prev, jsbytecode *prevpc) { + JS_ASSERT(flags_ & JSFRAME_HAS_PREVPC); prev_ = prev; + if (prev) { + prevpc_ = prevpc; + JS_ASSERT_IF(!prev->isDummyFrame() && !prev->hasImacropc(), + uint32(prevpc - prev->script()->code) < prev->script()->length); + } + } + + void setPrev(JSFrameRegs *regs) { + JS_ASSERT(flags_ & JSFRAME_HAS_PREVPC); + if (regs) { + prev_ = regs->fp; + prevpc_ = regs->pc; + JS_ASSERT_IF(!prev_->isDummyFrame() && !prev_->hasImacropc(), + uint32(prevpc_ - prev_->script()->code) < prev_->script()->length); + } else { + prev_ = NULL; + } } /* @@ -249,7 +280,16 @@ struct JSStackFrame * the bytecode being executed for the frame. */ - jsbytecode *pc(JSContext *cx) const; + /* + * Get the frame's current bytecode, assuming |this| is in |cx|. + * next is frame whose prev == this, NULL if not known or if this == cx->fp(). + */ + jsbytecode *pc(JSContext *cx, JSStackFrame *next = NULL); + + jsbytecode *prevpc() { + JS_ASSERT((prev_ != NULL) && (flags_ & JSFRAME_HAS_PREVPC)); + return prevpc_; + } JSScript *script() const { JS_ASSERT(isScriptFrame()); @@ -367,7 +407,8 @@ struct JSStackFrame return hasArgsObj() ? &argsObj() : NULL; } - void setArgsObj(JSObject &obj); + inline void setArgsObj(JSObject &obj); + inline void clearArgsObj(); /* * This value @@ -469,6 +510,7 @@ struct JSStackFrame inline JSObject *maybeCallObj() const; inline void setScopeChainNoCallObj(JSObject &obj); inline void setScopeChainAndCallObj(JSObject &obj); + inline void clearCallObj(); /* Block chain */ @@ -570,12 +612,21 @@ struct JSStackFrame return &rval_; } + void setAssignedReturnValue(const js::Value &v) { + flags_ |= JSFRAME_RVAL_ASSIGNED; + setReturnValue(v); + } + /* Native-code return address */ void *nativeReturnAddress() const { return ncode_; } + void setNativeReturnAddress(void *addr) { + ncode_ = addr; + } + void **addressOfNativeReturnAddress() { return &ncode_; } @@ -717,10 +768,6 @@ struct JSStackFrame return offsetof(JSStackFrame, prev_); } - static size_t offsetOfSavedpc() { - return offsetof(JSStackFrame, savedpc_); - } - static size_t offsetOfReturnValue() { return offsetof(JSStackFrame, rval_); } @@ -763,9 +810,6 @@ struct JSStackFrame void methodjitStaticAsserts(); #ifdef DEBUG - /* Magic value to represent invalid JSStackFrame::savedpc entry. */ - static jsbytecode *const sInvalidpc; - /* Poison scopeChain value set before a frame is flushed. */ static JSObject *const sInvalidScopeChain; #endif diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index 00bdc0a7b9d..9b81f592c34 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -50,11 +50,11 @@ JSStackFrame::initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun, JS_ASSERT(fun == callee.getFunctionPrivate()); /* Initialize stack frame members. */ - flags_ = JSFRAME_FUNCTION | flagsArg; + flags_ = JSFRAME_FUNCTION | JSFRAME_HAS_PREVPC | flagsArg; exec.fun = fun; args.nactual = nactual; /* only need to write if over/under-flow */ scopeChain_ = callee.getParent(); - /* savedpc_, prev_ initialized by push*Frame */ + /* prevpc_, prev_ initialized by push*Frame */ JS_ASSERT(!hasImacropc()); JS_ASSERT(!hasHookData()); rval_.setUndefined(); @@ -66,7 +66,7 @@ JSStackFrame::initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun, inline void JSStackFrame::initCallFrameCallerHalf(JSContext *cx, JSObject &scopeChain, - uint32 nactual, uint32 flagsArg) + uint32 nactual, uint32 flagsArg) { JS_ASSERT((flagsArg & ~(JSFRAME_CONSTRUCTING | JSFRAME_FUNCTION | @@ -74,9 +74,6 @@ JSStackFrame::initCallFrameCallerHalf(JSContext *cx, JSObject &scopeChain, JSFRAME_UNDERFLOW_ARGS)) == 0); JSFrameRegs *regs = cx->regs; - /* Save caller pc. */ - regs->fp->savedpc_ = regs->pc; - /* Initialize the caller half of the stack frame members. */ flags_ = JSFRAME_FUNCTION | flagsArg; args.nactual = nactual; /* only need to write if over/under-flow */ @@ -99,9 +96,6 @@ JSStackFrame::initCallFrameEarlyPrologue(JSFunction *fun, void *ncode) /* Initialize state that gets set early in a jitted function's prologue. */ exec.fun = fun; ncode_ = ncode; -#ifdef DEBUG - savedpc_ = JSStackFrame::sInvalidpc; -#endif } /* @@ -118,41 +112,43 @@ JSStackFrame::initCallFrameLatePrologue() } inline void -JSStackFrame::initEvalFrame(JSScript *script, JSStackFrame *downFrame, uint32 flagsArg) +JSStackFrame::initEvalFrame(JSScript *script, JSStackFrame *prev, + jsbytecode *prevpc, uint32 flagsArg) { JS_ASSERT(flagsArg & JSFRAME_EVAL); JS_ASSERT((flagsArg & ~(JSFRAME_EVAL | JSFRAME_DEBUGGER)) == 0); - JS_ASSERT(downFrame->flags_ & (JSFRAME_FUNCTION | JSFRAME_GLOBAL)); + JS_ASSERT(prev->flags_ & (JSFRAME_FUNCTION | JSFRAME_GLOBAL)); /* Copy (callee, thisv). */ js::Value *dstvp = (js::Value *)this - 2; - js::Value *srcvp = downFrame->flags_ & (JSFRAME_GLOBAL | JSFRAME_EVAL) - ? (js::Value *)downFrame - 2 - : downFrame->formalArgs() - 2; + js::Value *srcvp = prev->flags_ & (JSFRAME_GLOBAL | JSFRAME_EVAL) + ? (js::Value *)prev - 2 + : prev->formalArgs() - 2; dstvp[0] = srcvp[0]; dstvp[1] = srcvp[1]; - JS_ASSERT_IF(downFrame->flags_ & JSFRAME_FUNCTION, + JS_ASSERT_IF(prev->flags_ & JSFRAME_FUNCTION, dstvp[0].toObject().isFunction()); /* Initialize stack frame members. */ - flags_ = flagsArg | (downFrame->flags_ & (JSFRAME_FUNCTION | - JSFRAME_GLOBAL | - JSFRAME_HAS_CALL_OBJ)); + flags_ = flagsArg | JSFRAME_HAS_PREVPC | + (prev->flags_ & (JSFRAME_FUNCTION | + JSFRAME_GLOBAL | + JSFRAME_HAS_CALL_OBJ)); if (isFunctionFrame()) { - exec = downFrame->exec; + exec = prev->exec; args.script = script; } else { exec.script = script; } - scopeChain_ = &downFrame->scopeChain(); - JS_ASSERT_IF(isFunctionFrame(), &callObj() == &downFrame->callObj()); - /* savedpc initialized by pushExecuteFrame */ - prev_ = downFrame; + scopeChain_ = &prev->scopeChain(); + JS_ASSERT_IF(isFunctionFrame(), &callObj() == &prev->callObj()); + + setPrev(prev, prevpc); JS_ASSERT(!hasImacropc()); JS_ASSERT(!hasHookData()); rval_.setUndefined(); blockChain_ = NULL; - setAnnotation(downFrame->annotation()); + setAnnotation(prev->annotation()); } inline void @@ -166,11 +162,11 @@ JSStackFrame::initGlobalFrame(JSScript *script, JSObject &chain, uint32 flagsArg vp[1].setUndefined(); /* Set after frame pushed using thisObject */ /* Initialize stack frame members. */ - flags_ = flagsArg | JSFRAME_GLOBAL; + flags_ = flagsArg | JSFRAME_GLOBAL | JSFRAME_HAS_PREVPC; exec.script = script; args.script = (JSScript *)0xbad; scopeChain_ = &chain; - /* savedpc initialized by pushExecuteFrame */ + prev_ = NULL; JS_ASSERT(!hasImacropc()); JS_ASSERT(!hasHookData()); @@ -183,8 +179,8 @@ inline void JSStackFrame::initDummyFrame(JSContext *cx, JSObject &chain) { js::PodZero(this); - flags_ = JSFRAME_DUMMY; - prev_ = cx->maybefp(); + flags_ = JSFRAME_DUMMY | JSFRAME_HAS_PREVPC; + setPrev(cx->regs); chain.isGlobal(); setScopeChainNoCallObj(chain); } @@ -288,13 +284,6 @@ JSStackFrame::varobj(JSContext *cx) const return isFunctionFrame() ? callObj() : cx->activeSegment()->getInitialVarObj(); } -inline jsbytecode * -JSStackFrame::pc(JSContext *cx) const -{ - JS_ASSERT(cx->regs && cx->containingSegment(this) != NULL); - return (cx->regs->fp == this) ? cx->regs->pc : savedpc_; -} - inline uintN JSStackFrame::numActualArgs() const { @@ -336,6 +325,14 @@ JSStackFrame::setArgsObj(JSObject &obj) flags_ |= JSFRAME_HAS_ARGS_OBJ; } +inline void +JSStackFrame::clearArgsObj() +{ + JS_ASSERT(hasArgsObj()); + args.nactual = args.obj->getArgsInitialLength(); + flags_ ^= JSFRAME_HAS_ARGS_OBJ; +} + inline void JSStackFrame::setScopeChainNoCallObj(JSObject &obj) { @@ -360,6 +357,13 @@ JSStackFrame::setScopeChainAndCallObj(JSObject &obj) flags_ |= JSFRAME_HAS_CALL_OBJ; } +inline void +JSStackFrame::clearCallObj() +{ + JS_ASSERT(hasCallObj()); + flags_ ^= JSFRAME_HAS_CALL_OBJ; +} + inline JSObject & JSStackFrame::callObj() const { diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 033fbddf9b5..33e569b508b 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -1271,7 +1271,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, /* Copy frame onto the stack. */ stackfp->stealFrameAndSlots(stackvp, genfp, genvp, gen->regs.sp); - stackfp->repointGeneratorFrameDown(cx->maybefp()); + stackfp->setPrev(cx->regs); stackfp->unsetFloatingGenerator(); RebaseRegsFromTo(&gen->regs, genfp, stackfp); MUST_FLOW_THROUGH("restore"); diff --git a/js/src/methodjit/BaseAssembler.h b/js/src/methodjit/BaseAssembler.h index b7f3d12baea..c6d5f98f876 100644 --- a/js/src/methodjit/BaseAssembler.h +++ b/js/src/methodjit/BaseAssembler.h @@ -310,7 +310,10 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::ARMRegiste /* regs->sp = sp */ storePtr(ClobberInCall, - FrameAddress(offsetof(VMFrame, regs) + offsetof(JSFrameRegs, sp))); + FrameAddress(offsetof(VMFrame, regs.sp))); + + /* regs->fp = fp */ + storePtr(JSFrameReg, FrameAddress(offsetof(VMFrame, regs.fp))); } void setupVMFrame() { @@ -332,23 +335,6 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::ARMRegiste return MacroAssembler::call(reg); } - void restoreReturnAddress() - { -#ifndef JS_CPU_ARM - /* X86 and X64's "ret" instruction expects a return address on the stack. */ - push(Address(JSFrameReg, JSStackFrame::offsetOfncode())); -#else - /* ARM returns either using its link register (LR) or directly from the stack, but masm.ret() - * always emits a return to LR. */ - load32(Address(JSFrameReg, JSStackFrame::offsetOfncode()), JSC::ARMRegisters::lr); -#endif - } - - void saveReturnAddress(RegisterID reg) - { - storePtr(reg, Address(JSFrameReg, JSStackFrame::offsetOfncode())); - } - void finalize(uint8 *ncode) { JSC::JITCode jc(ncode, size()); JSC::CodeBlock cb(jc); @@ -367,6 +353,13 @@ static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = BaseAssembler::J static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = BaseAssembler::JSReturnReg_Data; static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = BaseAssembler::JSParamReg_Argc; +struct FrameFlagsAddress : JSC::MacroAssembler::Address +{ + FrameFlagsAddress() + : Address(JSFrameReg, JSStackFrame::offsetOfFlags()) + {} +}; + } /* namespace mjit */ } /* namespace js */ diff --git a/js/src/methodjit/BytecodeAnalyzer.cpp b/js/src/methodjit/BytecodeAnalyzer.cpp index 84e0262d7d2..d5586ae243e 100644 --- a/js/src/methodjit/BytecodeAnalyzer.cpp +++ b/js/src/methodjit/BytecodeAnalyzer.cpp @@ -111,6 +111,11 @@ BytecodeAnalyzer::analyze(uint32 index) case JSOP_TRAP: return false; + case JSOP_SETRVAL: + case JSOP_POPV: + usesRval = true; + break; + case JSOP_DEFAULT: case JSOP_GOTO: offs = (pc + JSOP_GOTO_LENGTH) - script->code; diff --git a/js/src/methodjit/BytecodeAnalyzer.h b/js/src/methodjit/BytecodeAnalyzer.h index 3bc219680d3..fa672a24090 100644 --- a/js/src/methodjit/BytecodeAnalyzer.h +++ b/js/src/methodjit/BytecodeAnalyzer.h @@ -65,10 +65,14 @@ namespace js OpcodeStatus *ops; Vector doList; + /* Whether there are POPV/SETRVAL bytecodes which can write to the frame's rval. */ + bool usesRval; + public: BytecodeAnalyzer(JSContext *cx, JSScript *script) : cx(cx), script(script), ops(NULL), - doList(ContextAllocPolicy(cx)) + doList(ContextAllocPolicy(cx)), + usesRval(false) { } ~BytecodeAnalyzer(); @@ -78,6 +82,8 @@ namespace js public: + bool usesReturnValue() const { return usesRval; } + inline const OpcodeStatus & operator [](uint32 offs) const { JS_ASSERT(offs < script->length); return ops[offs]; diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 3f071cc559b..7b57e797384 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -83,6 +83,7 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObj #if defined JS_POLYIC pics(ContextAllocPolicy(cx)), #endif + callPatches(ContextAllocPolicy(cx)), callSites(ContextAllocPolicy(cx)), doubleList(ContextAllocPolicy(cx)), escapingList(ContextAllocPolicy(cx)), @@ -178,27 +179,11 @@ mjit::TryCompile(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *sco return status; } -JSC::MacroAssembler::RegisterID -mjit::Compiler::takeHWReturnAddress(Assembler &masm) -{ -#ifndef JS_CPU_ARM - JS_STATIC_ASSERT(JSParamReg_Argc != Registers::ReturnReg); - masm.pop(Registers::ReturnReg); - return Registers::ReturnReg; -#else - return JSC::ARMRegisters::lr; -#endif -} - CompileStatus mjit::Compiler::generatePrologue() { invokeLabel = masm.label(); - RegisterID retAddr = takeHWReturnAddress(masm); - restoreFrameRegs(masm); - masm.saveReturnAddress(retAddr); - /* * If there is no function, then this can only be called via JaegerShot(), * which expects an existing frame to be initialized like the interpreter. @@ -211,17 +196,11 @@ mjit::Compiler::generatePrologue() * either argc >= nargs or the arity check has corrected the frame. */ invokeLabel = masm.label(); - RegisterID retAddr = takeHWReturnAddress(masm); - masm.saveReturnAddress(retAddr); -#ifdef DEBUG - masm.storePtr(ImmPtr(JSStackFrame::sInvalidpc), Address(JSFrameReg, JSStackFrame::offsetOfSavedpc())); -#endif Label fastPath = masm.label(); - /* Store these early on so slow paths can access them. */ + /* Store this early on so slow paths can access it. */ masm.storePtr(ImmPtr(fun), Address(JSFrameReg, JSStackFrame::offsetOfExec())); - masm.storePtr(JSFrameReg, FrameAddress(offsetof(VMFrame, regs.fp))); { /* @@ -231,11 +210,6 @@ mjit::Compiler::generatePrologue() * This loops back to entry point #2. */ arityLabel = stubcc.masm.label(); - RegisterID retAddr = takeHWReturnAddress(stubcc.masm); - stubcc.masm.saveReturnAddress(retAddr); -#ifdef DEBUG - stubcc.masm.storePtr(ImmPtr(JSStackFrame::sInvalidpc), Address(JSFrameReg, JSStackFrame::offsetOfSavedpc())); -#endif Jump argMatch = stubcc.masm.branch32(Assembler::Equal, JSParamReg_Argc, Imm32(fun->nargs)); stubcc.crossJump(argMatch, fastPath); @@ -440,10 +414,10 @@ mjit::Compiler::finishThisUp() script->callICs[i].slowPathStart = stubCode.locationOf(callICs[i].slowPathStart); /* Compute the hot call offset. */ - uint32 offset = fullCode.locationOf(callICs[i].hotCall) - + uint32 offset = fullCode.locationOf(callICs[i].hotJump) - fullCode.locationOf(callICs[i].funGuard); - script->callICs[i].hotCallOffset = offset; - JS_ASSERT(script->callICs[i].hotCallOffset == offset); + script->callICs[i].hotJumpOffset = offset; + JS_ASSERT(script->callICs[i].hotJumpOffset == offset); /* Compute the join point offset. */ offset = fullCode.locationOf(callICs[i].joinPoint) - @@ -483,6 +457,24 @@ mjit::Compiler::finishThisUp() } #endif /* JS_MONOIC */ + for (size_t i = 0; i < callPatches.length(); i++) { + void *joinPoint = fullCode.locationOf(callPatches[i].joinPoint).executableAddress(); + + /* Patch the write of ncode in the hot path. */ + JSC::CodeLocationDataLabelPtr fastNcode = + fullCode.locationOf(callPatches[i].fastNcodePatch); + JSC::RepatchBuffer fastRepatch((uint8*)fastNcode.executableAddress() - 32, 64, false); + fastRepatch.repatch(fastNcode, joinPoint); + + /* Patch the write of ncode in the slow path. */ + if (callPatches[i].hasSlowNcode) { + JSC::CodeLocationDataLabelPtr slowNcode = + stubCode.locationOf(callPatches[i].slowNcodePatch); + JSC::RepatchBuffer slowRepatch((uint8*)slowNcode.executableAddress() - 32, 64, false); + slowRepatch.repatch(slowNcode, joinPoint); + } + } + #if defined JS_POLYIC script->jit->nPICs = pics.length(); if (pics.length()) { @@ -666,6 +658,12 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_POPV) BEGIN_CASE(JSOP_SETRVAL) { + RegisterID reg = frame.allocReg(); + masm.load32(FrameFlagsAddress(), reg); + masm.or32(Imm32(JSFRAME_RVAL_ASSIGNED), reg); + masm.store32(reg, FrameFlagsAddress()); + frame.freeReg(reg); + FrameEntry *fe = frame.peek(-1); frame.storeTo(fe, Address(JSFrameReg, JSStackFrame::offsetOfReturnValue()), true); frame.pop(); @@ -673,12 +671,7 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_POPV) BEGIN_CASE(JSOP_RETURN) - { - FrameEntry *fe = frame.peek(-1); - frame.storeTo(fe, Address(JSFrameReg, JSStackFrame::offsetOfReturnValue()), true); - frame.pop(); - emitReturn(); - } + emitReturn(frame.peek(-1)); END_CASE(JSOP_RETURN) BEGIN_CASE(JSOP_GOTO) @@ -1490,7 +1483,7 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_DEFLOCALFUN) BEGIN_CASE(JSOP_RETRVAL) - emitReturn(); + emitReturn(NULL); END_CASE(JSOP_RETRVAL) BEGIN_CASE(JSOP_GETGNAME) @@ -1550,7 +1543,7 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_STOP) /* Safe point! */ - emitReturn(); + emitReturn(NULL); goto done; END_CASE(JSOP_STOP) @@ -1805,21 +1798,39 @@ mjit::Compiler::jsop_getglobal(uint32 index) } void -mjit::Compiler::emitReturn() +mjit::Compiler::emitFinalReturn(Assembler &masm) +{ + masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfncode()), Registers::ReturnReg); + masm.jump(Registers::ReturnReg); +} + +void +mjit::Compiler::loadReturnValue(Assembler &masm) { /* - * if (!f.inlineCallCount) - * return; + * Load a return value from POPV or SETRVAL into the return registers, + * otherwise return undefined. */ - Jump noInlineCalls = masm.branchPtr(Assembler::Equal, - FrameAddress(offsetof(VMFrame, entryFp)), - JSFrameReg); - stubcc.linkExit(noInlineCalls, Uses(frame.frameDepth())); - stubcc.masm.restoreReturnAddress(); - stubcc.masm.ret(); + masm.loadValueAsComponents(UndefinedValue(), JSReturnReg_Type, JSReturnReg_Data); + if (analysis.usesReturnValue()) { + Jump rvalClear = masm.branchTest32(Assembler::Zero, + FrameFlagsAddress(), + Imm32(JSFRAME_RVAL_ASSIGNED)); + Address rvalAddress(JSFrameReg, JSStackFrame::offsetOfReturnValue()); + masm.loadValueAsComponents(rvalAddress, + JSReturnReg_Type, JSReturnReg_Data); + rvalClear.linkTo(masm.label(), &masm); + } +} +void +mjit::Compiler::emitReturn(FrameEntry *fe) +{ JS_ASSERT_IF(!fun, JSOp(*PC) == JSOP_STOP); + /* Only the top of the stack can be returned. */ + JS_ASSERT_IF(fe, fe == frame.peek(-1)); + /* * 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 @@ -1832,42 +1843,44 @@ mjit::Compiler::emitReturn() if (fun) { if (fun->isHeavyweight()) { /* There will always be a call object. */ - prepareStubCall(Uses(0)); - stubCall(stubs::PutCallObject); - frame.discardFrame(); + prepareStubCall(Uses(fe ? 1 : 0)); + stubCall(stubs::PutActivationObjects); + + if (fe) { + masm.loadValueAsComponents(frame.addressOf(fe), + JSReturnReg_Type, JSReturnReg_Data); + emitFinalReturn(masm); + frame.discardFrame(); + return; + } } else { /* if (hasCallObj() || hasArgsObj()) stubs::PutActivationObjects() */ Jump putObjs = masm.branchTest32(Assembler::NonZero, Address(JSFrameReg, JSStackFrame::offsetOfFlags()), Imm32(JSFRAME_HAS_CALL_OBJ | JSFRAME_HAS_ARGS_OBJ)); stubcc.linkExit(putObjs, Uses(frame.frameDepth())); - frame.discardFrame(); stubcc.leave(); stubcc.call(stubs::PutActivationObjects); - stubcc.rejoin(Changes(0)); + + if (fe) { + stubcc.masm.loadValueAsComponents(frame.addressOf(fe), + JSReturnReg_Type, JSReturnReg_Data); + } else { + loadReturnValue(stubcc.masm); + } + + emitFinalReturn(stubcc.masm); } } - /* - * r = fp->prev - * f.fp = r - */ - masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfPrev()), Registers::ReturnReg); - masm.storePtr(Registers::ReturnReg, FrameAddress(offsetof(VMFrame, regs.fp))); + if (fe) + frame.storeTo(fe, JSReturnReg_Data, JSReturnReg_Type, Registers::ReturnReg); + else + loadReturnValue(masm); - JS_STATIC_ASSERT(Registers::ReturnReg != JSReturnReg_Data); - JS_STATIC_ASSERT(Registers::ReturnReg != JSReturnReg_Type); - - Address rval(JSFrameReg, JSStackFrame::offsetOfReturnValue()); - masm.loadValueAsComponents(rval, JSReturnReg_Type, JSReturnReg_Data); - masm.restoreReturnAddress(); - masm.move(Registers::ReturnReg, JSFrameReg); -#ifdef DEBUG - masm.storePtr(ImmPtr(JSStackFrame::sInvalidpc), - Address(JSFrameReg, JSStackFrame::offsetOfSavedpc())); -#endif - masm.ret(); + emitFinalReturn(masm); + frame.discardFrame(); } void @@ -1955,6 +1968,9 @@ mjit::Compiler::emitPrimitiveTestForNew(uint32 argc) void mjit::Compiler::emitUncachedCall(uint32 argc, bool callingNew) { + CallPatchInfo callPatch; + callPatch.hasSlowNcode = false; + RegisterID r0 = Registers::ReturnReg; VoidPtrStubUInt32 stub = callingNew ? stubs::UncachedNew : stubs::UncachedCall; @@ -1966,12 +1982,21 @@ mjit::Compiler::emitUncachedCall(uint32 argc, bool callingNew) Jump notCompiled = masm.branchTestPtr(Assembler::Zero, r0, r0); - masm.call(r0); + masm.loadPtr(FrameAddress(offsetof(VMFrame, regs.fp)), JSFrameReg); + callPatch.fastNcodePatch = + masm.storePtrWithPatch(ImmPtr(NULL), + Address(JSFrameReg, JSStackFrame::offsetOfncode())); + + masm.jump(r0); + #if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64) masm.callLabel = masm.label(); #endif ADD_CALLSITE(false); + callPatch.joinPoint = masm.label(); + masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfPrev()), JSFrameReg); + if (callingNew) emitPrimitiveTestForNew(argc); @@ -1982,6 +2007,7 @@ mjit::Compiler::emitUncachedCall(uint32 argc, bool callingNew) stubcc.linkExitDirect(notCompiled, stubcc.masm.label()); stubcc.rejoin(Changes(0)); + callPatches.append(callPatch); } /* See MonoIC.cpp, CallCompiler for more information on call ICs. */ @@ -2006,6 +2032,8 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) CallGenInfo callIC(argc); uint32 callICIndex = callICs.length(); + CallPatchInfo callPatch; + /* * Save constant |this| to optimize thisv stores for common call cases * like CALL[LOCAL, GLOBAL, ARG] which push NULL. @@ -2062,7 +2090,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) Jump j = masm.branchPtrWithPatch(Assembler::NotEqual, dataReg, callIC.funGuard); callIC.funJump = j; - Jump oolCallDone; Jump rejoin1, rejoin2; { stubcc.linkExitDirect(j, stubcc.masm.label()); @@ -2130,8 +2157,11 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) Registers::ReturnReg); stubcc.masm.move(Imm32(argc), JSParamReg_Argc); stubcc.masm.loadPtr(FrameAddress(offsetof(VMFrame, regs.fp)), JSFrameReg); - stubcc.masm.call(Registers::ReturnReg); - oolCallDone = stubcc.masm.jump(); + callPatch.hasSlowNcode = true; + callPatch.slowNcodePatch = + stubcc.masm.storePtrWithPatch(ImmPtr(NULL), + Address(JSFrameReg, JSStackFrame::offsetOfncode())); + stubcc.masm.jump(Registers::ReturnReg); /* Catch-all case, for natives this will turn into a MIC. */ if (notObjectJump.isSet()) @@ -2163,12 +2193,11 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) flags |= JSFRAME_CONSTRUCTING; InlineFrameAssembler inlFrame(masm, callIC, flags); - inlFrame.assemble(); + callPatch.fastNcodePatch = inlFrame.assemble(NULL); - callIC.hotCall = masm.call(); - stubcc.crossJump(oolCallDone, masm.label()); - - callIC.joinPoint = masm.label(); + callIC.hotJump = masm.jump(); + callIC.joinPoint = callPatch.joinPoint = masm.label(); + masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfPrev()), JSFrameReg); /* * Functions invoked with |new| can return primitive values. @@ -2188,6 +2217,7 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) stubcc.rejoin(Changes(0)); callICs.append(callIC); + callPatches.append(callPatch); #else emitUncachedCall(argc, callingNew); #endif @@ -2739,6 +2769,7 @@ mjit::Compiler::jsop_callprop_str(JSAtom *atom) /* Force into a register because getprop won't expect a constant. */ RegisterID reg = frame.allocReg(); + masm.move(ImmPtr(obj), reg); frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg); diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index f543abb83f6..03bcb3695bb 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -120,7 +120,7 @@ class Compiler uint32 argc; DataLabelPtr funGuard; Jump funJump; - Call hotCall; + Jump hotJump; Call oolCall; Label joinPoint; Label slowJoinPoint; @@ -135,6 +135,17 @@ class Compiler private: #endif + /* + * Writes of call return addresses which needs to be delayed until the final + * absolute address of the join point is known. + */ + struct CallPatchInfo { + Label joinPoint; + DataLabelPtr fastNcodePatch; + DataLabelPtr slowNcodePatch; + bool hasSlowNcode; + }; + #if defined JS_POLYIC struct PICGenInfo { PICGenInfo(ic::PICInfo::Kind kind) : kind(kind) @@ -215,6 +226,7 @@ class Compiler #if defined JS_POLYIC js::Vector pics; #endif + js::Vector callPatches; js::Vector callSites; js::Vector doubleList; js::Vector escapingList; @@ -253,8 +265,6 @@ class Compiler void addCallSite(uint32 id, bool stub); /* Emitting helpers. */ - RegisterID takeHWReturnAddress(Assembler &masm); - void restoreReturnAddress(Assembler &masm); void restoreFrameRegs(Assembler &masm); void emitStubCmpOp(BoolStub stub, jsbytecode *target, JSOp fused); void iter(uintN flags); @@ -271,7 +281,9 @@ class Compiler void jsop_getprop_slow(); void jsop_getarg(uint32 index); void jsop_this(); - void emitReturn(); + void emitReturn(FrameEntry *fe); + void emitFinalReturn(Assembler &masm); + void loadReturnValue(Assembler &masm); void dispatchCall(VoidPtrStubUInt32 stub, uint32 argc); void interruptCheckHelper(); void emitUncachedCall(uint32 argc, bool callingNew); diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index 5c965648f9d..ff34ecb31d8 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -327,6 +327,49 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped) #endif } +void FrameState::storeTo(FrameEntry *fe, RegisterID dataReg, RegisterID typeReg, RegisterID tempReg) +{ + JS_ASSERT(dataReg != typeReg && dataReg != tempReg && typeReg != tempReg); + + if (fe->isConstant()) { + masm.loadValueAsComponents(fe->getValue(), typeReg, dataReg); + return; + } + + if (fe->isCopy()) + fe = fe->copyOf(); + + if (fe->isTypeKnown()) { + RegisterID data = tempRegForData(fe); + if (data != dataReg) + masm.move(data, dataReg); + masm.move(ImmType(fe->getKnownType()), typeReg); + return; + } + + RegisterID data = tempRegForData(fe); + RegisterID type = tempRegForType(fe); + if (data == typeReg && type == dataReg) { + masm.move(type, tempReg); + masm.move(data, dataReg); + masm.move(tempReg, typeReg); + } else if (data != dataReg) { + if (type == typeReg) { + masm.move(data, dataReg); + } else if (type != dataReg) { + masm.move(data, dataReg); + if (type != typeReg) + masm.move(type, typeReg); + } else { + JS_ASSERT(data != typeReg); + masm.move(type, typeReg); + masm.move(data, dataReg); + } + } else if (type != typeReg) { + masm.move(type, typeReg); + } +} + #ifdef DEBUG void FrameState::assertValidRegisterState() const diff --git a/js/src/methodjit/FrameState.h b/js/src/methodjit/FrameState.h index c316cc4d055..1fbd60fd065 100644 --- a/js/src/methodjit/FrameState.h +++ b/js/src/methodjit/FrameState.h @@ -586,6 +586,12 @@ class FrameState */ void storeTo(FrameEntry *fe, Address address, bool popHint = false); + /* + * Fully stores a FrameEntry into two arbitrary registers. tempReg may be + * used as a temporary. + */ + void storeTo(FrameEntry *fe, RegisterID dataReg, RegisterID typeReg, RegisterID tempReg); + /* * Stores the top stack slot back to a slot. */ diff --git a/js/src/methodjit/InlineFrameAssembler.h b/js/src/methodjit/InlineFrameAssembler.h index 4fcf6c7d248..833d3a40d68 100644 --- a/js/src/methodjit/InlineFrameAssembler.h +++ b/js/src/methodjit/InlineFrameAssembler.h @@ -71,6 +71,7 @@ class InlineFrameAssembler { typedef JSC::MacroAssembler::Address Address; typedef JSC::MacroAssembler::Imm32 Imm32; typedef JSC::MacroAssembler::ImmPtr ImmPtr; + typedef JSC::MacroAssembler::DataLabelPtr DataLabelPtr; Assembler &masm; uint32 frameDepth; // script->nfixed + stack depth at caller call site @@ -105,24 +106,27 @@ class InlineFrameAssembler { tempRegs.takeReg(funObjReg); } - inline void assemble() + DataLabelPtr assemble(void *ncode) { JS_ASSERT((flags & ~JSFRAME_CONSTRUCTING) == 0); RegisterID t0 = tempRegs.takeAnyReg(); - masm.storePtr(ImmPtr(pc), Address(JSFrameReg, JSStackFrame::offsetOfSavedpc())); - AdjustedFrame adj(sizeof(JSStackFrame) + frameDepth * sizeof(Value)); masm.store32(Imm32(JSFRAME_FUNCTION | flags), adj.addrOf(JSStackFrame::offsetOfFlags())); masm.loadPtr(Address(funObjReg, offsetof(JSObject, parent)), t0); masm.storePtr(t0, adj.addrOf(JSStackFrame::offsetOfScopeChain())); masm.storePtr(JSFrameReg, adj.addrOf(JSStackFrame::offsetOfPrev())); + DataLabelPtr ncodePatch = + masm.storePtrWithPatch(ImmPtr(ncode), adj.addrOf(JSStackFrame::offsetOfncode())); + /* Adjust JSFrameReg. Callee fills in the rest. */ masm.addPtr(Imm32(sizeof(JSStackFrame) + sizeof(Value) * frameDepth), JSFrameReg); tempRegs.putReg(t0); + + return ncodePatch; } }; diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index 947e77ff61e..4a08ac26521 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -65,6 +65,7 @@ #include "jsobjinlines.h" #include "jscntxtinlines.h" #include "jsatominlines.h" +#include "StubCalls-inl.h" #include "jsautooplen.h" @@ -72,19 +73,8 @@ using namespace js; using namespace js::mjit; using namespace JSC; -#define THROW() \ - do { \ - void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \ - *f.returnAddressLocation() = ptr; \ - return; \ - } while (0) - -#define THROWV(v) \ - do { \ - void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \ - *f.returnAddressLocation() = ptr; \ - return v; \ - } while (0) +static bool +InlineReturn(VMFrame &f, JSBool ok, JSBool popFrame = JS_TRUE); static jsbytecode * FindExceptionHandler(JSContext *cx) @@ -179,8 +169,16 @@ top: return NULL; } +/* + * Clean up a frame and return. popFrame indicates whether to additionally pop + * the frame and store the return value on the caller's stack. The frame will + * normally be popped by the caller on return from a call into JIT code, + * so must be popped here when that caller code will not execute. This can be + * either because of a call into an un-JITable script, or because the call is + * throwing an exception. + */ static bool -InlineReturn(VMFrame &f, JSBool ok) +InlineReturn(VMFrame &f, JSBool ok, JSBool popFrame) { JSContext *cx = f.cx; JSStackFrame *fp = f.regs.fp; @@ -213,9 +211,11 @@ InlineReturn(VMFrame &f, JSBool ok) if (fp->isConstructing() && fp->returnValue().isPrimitive()) fp->setReturnValue(fp->thisValue()); - Value *newsp = fp->actualArgs() - 1; - newsp[-1] = fp->returnValue(); - cx->stack().popInlineFrame(cx, fp->prev(), newsp); + if (popFrame) { + Value *newsp = fp->actualArgs() - 1; + newsp[-1] = fp->returnValue(); + cx->stack().popInlineFrame(cx, fp->prev(), newsp); + } return ok; } @@ -317,10 +317,11 @@ stubs::FixupArity(VMFrame &f, uint32 nactual) void *ncode = oldfp->nativeReturnAddress(); /* Pop the inline frame. */ - RemovePartialFrame(cx, oldfp); + f.fp() = oldfp->prev(); + f.regs.sp = (Value*) oldfp; /* Reserve enough space for a callee frame. */ - JSStackFrame *newfp = cx->stack().getInlineFrameWithinLimit(cx, cx->regs->sp, nactual, + JSStackFrame *newfp = cx->stack().getInlineFrameWithinLimit(cx, (Value*) oldfp, nactual, fun, fun->script(), &flags, f.entryFp, &f.stackLimit); if (!newfp) @@ -356,11 +357,10 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual) /* * FixupArity/RemovePartialFrame expect to be called after the early - * prologue. Pass null for ncode: either we will jump into jit code, which - * will set ncode, or we will jump into js::Interpret, which does not care - * about ncode. + * prologue. Pass the existing value for ncode, it has already been set + * by the jit code calling into this stub. */ - fp->initCallFrameEarlyPrologue(fun, NULL); + fp->initCallFrameEarlyPrologue(fun, fp->nativeReturnAddress()); /* Empty script does nothing. */ if (script->isEmpty()) { @@ -897,11 +897,12 @@ RunTracer(VMFrame &f) if (op == JSOP_RETURN && !entryFrame->isBailedAtReturn()) entryFrame->setReturnValue(f.regs.sp[-1]); - /* Don't pop the frame if it's maybe owned by an Invoke. */ + /* Cleanup activation objects on the frame unless it's owned by an Invoke. */ if (f.fp() != f.entryFp) { - if (!InlineReturn(f, JS_TRUE)) + if (!InlineReturn(f, JS_TRUE, JS_FALSE)) THROWV(NULL); } + void *retPtr = JS_FUNC_TO_DATA_PTR(void *, InjectJaegerReturn); *f.returnAddressLocation() = retPtr; return NULL; diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index 52d62dfa817..bca47771b46 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -61,14 +61,14 @@ JSStackFrame::methodjitStaticAsserts() #if defined(JS_CPU_X86) JS_STATIC_ASSERT(offsetof(JSStackFrame, rval_) == 0x18); JS_STATIC_ASSERT(offsetof(JSStackFrame, rval_) + 4 == 0x1C); - JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode_) == 0x2C); + JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode_) == 0x14); /* ARM uses decimal literals. */ JS_STATIC_ASSERT(offsetof(JSStackFrame, rval_) == 24); JS_STATIC_ASSERT(offsetof(JSStackFrame, rval_) + 4 == 28); - JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode_) == 44); + JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode_) == 20); #elif defined(JS_CPU_X64) JS_STATIC_ASSERT(offsetof(JSStackFrame, rval_) == 0x30); - JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode_) == 0x50); + JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode_) == 0x28); #endif } @@ -76,13 +76,11 @@ JSStackFrame::methodjitStaticAsserts() * Explanation of VMFrame activation and various helper thunks below. * * JaegerTrampoline - Executes a method JIT-compiled JSFunction. This function - * creates a VMFrame on the machine stack and calls into JIT'd code. The JIT'd - * code will eventually return to the VMFrame. + * creates a VMFrame on the machine stack and jumps into JIT'd code. The JIT'd + * code will eventually jump back to the VMFrame. * * - Called from C++ function EnterMethodJIT. - * - Parameters: cx, fp, code, stackLimit, safePoint - * - Notes: safePoint is used in combination with SafePointTrampoline, - * explained further down. + * - Parameters: cx, fp, code, stackLimit * * JaegerThrowpoline - Calls into an exception handler from JIT'd code, and if a * scripted exception handler is not found, unwinds the VMFrame and returns @@ -100,19 +98,6 @@ JSStackFrame::methodjitStaticAsserts() * at. Because the jit-code ABI conditions are satisfied, we can just jump to * that point. * - * - * SafePointTrampoline - Inline script calls link their return addresses through - * JSStackFrame::ncode. This includes the return address that unwinds back - * to JaegerTrampoline. However, the tracer integration code often wants to - * enter a method JIT'd function at an arbitrary safe point. Safe points - * do not have the return address linking code that the method prologue has. - * SafePointTrampoline is a thunk which correctly links the initial return - * address. It is used in JaegerShotAtSafePoint, and passed as the "script - * code" parameter. Using the "safePoint" parameter to JaegerTrampoline, it - * correctly jumps to the intended point in the method. - * - * - Used by JaegerTrampoline() - * * InjectJaegerReturn - Implements the tail of InlineReturn. This is needed for * tracer integration, where a "return" opcode might not be a safe-point, * and thus the return path must be injected by hijacking the stub return @@ -126,11 +111,15 @@ static const size_t STUB_CALLS_FOR_OP_COUNT = 255; static uint32 StubCallsForOp[STUB_CALLS_FOR_OP_COUNT]; #endif +extern "C" void JaegerTrampolineReturn(); + extern "C" void JS_FASTCALL PushActiveVMFrame(VMFrame &f) { f.previous = JS_METHODJIT_DATA(f.cx).activeFrame; JS_METHODJIT_DATA(f.cx).activeFrame = &f; + + f.regs.fp->setNativeReturnAddress(JS_FUNC_TO_DATA_PTR(void*, JaegerTrampolineReturn)); } extern "C" void JS_FASTCALL @@ -229,10 +218,7 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" /* Space for the rest of the VMFrame. */ "subq $0x28, %rsp" "\n" - /* - * This is actually part of the VMFrame, but we need to save |r8| for - * SafePointTrampoline. - */ + /* This is actually part of the VMFrame. */ "pushq %r8" "\n" /* Set cx->regs and set the active frame. Save rdx and align frame in one. */ @@ -242,10 +228,16 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" "movq %rsp, %rdi" "\n" "call " SYMBOL_STRING_VMFRAME(PushActiveVMFrame) "\n" - /* - * Jump into into the JIT'd code. - */ - "call *0(%rsp)" "\n" + /* Jump into the JIT'd code. */ + "jmp *0(%rsp)" "\n" +); + +asm volatile ( +".text\n" +".globl " SYMBOL_STRING(JaegerTrampolineReturn) "\n" +SYMBOL_STRING(JaegerTrampolineReturn) ":" "\n" + "or %rdx, %rcx" "\n" + "movq %rcx, 0x30(%rbx)" "\n" "movq %rsp, %rdi" "\n" "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n" @@ -285,21 +277,12 @@ SYMBOL_STRING(JaegerThrowpoline) ":" "\n" JS_STATIC_ASSERT(offsetof(VMFrame, regs.fp) == 0x38); -asm volatile ( -".text\n" -".globl " SYMBOL_STRING(SafePointTrampoline) "\n" -SYMBOL_STRING(SafePointTrampoline) ":" "\n" - "popq %rax" "\n" - "movq %rax, 0x50(%rbx)" "\n" - "jmp *8(%rsp)" "\n" -); - asm volatile ( ".text\n" ".globl " SYMBOL_STRING(InjectJaegerReturn) "\n" SYMBOL_STRING(InjectJaegerReturn) ":" "\n" "movq 0x30(%rbx), %rcx" "\n" /* load fp->rval_ into typeReg */ - "movq 0x50(%rbx), %rax" "\n" /* fp->ncode_ */ + "movq 0x28(%rbx), %rax" "\n" /* fp->ncode_ */ /* Reimplementation of PunboxAssembler::loadValueAsComponents() */ "movq %r14, %rdx" "\n" /* payloadReg = payloadMaskReg */ @@ -349,7 +332,15 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" "movl %esp, %ecx" "\n" "call " SYMBOL_STRING_VMFRAME(PushActiveVMFrame) "\n" - "call *16(%ebp)" "\n" + "jmp *16(%ebp)" "\n" +); + +asm volatile ( +".text\n" +".globl " SYMBOL_STRING(JaegerTrampolineReturn) "\n" +SYMBOL_STRING(JaegerTrampolineReturn) ":" "\n" + "movl %edx, 0x18(%ebx)" "\n" + "movl %ecx, 0x1C(%ebx)" "\n" "movl %esp, %ecx" "\n" "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n" @@ -399,23 +390,9 @@ asm volatile ( SYMBOL_STRING(InjectJaegerReturn) ":" "\n" "movl 0x18(%ebx), %edx" "\n" /* fp->rval_ data */ "movl 0x1C(%ebx), %ecx" "\n" /* fp->rval_ type */ - "movl 0x2C(%ebx), %eax" "\n" /* fp->ncode_ */ + "movl 0x14(%ebx), %eax" "\n" /* fp->ncode_ */ "movl 0x1C(%esp), %ebx" "\n" /* f.fp */ - "pushl %eax" "\n" - "ret" "\n" -); - -/* - * Take the fifth parameter from JaegerShot() and jump to it. This makes it so - * we can jump into arbitrary JIT code, which won't have the frame-fixup prologue. - */ -asm volatile ( -".text\n" -".globl " SYMBOL_STRING(SafePointTrampoline) "\n" -SYMBOL_STRING(SafePointTrampoline) ":" "\n" - "popl %eax" "\n" - "movl %eax, 0x2C(%ebx)" "\n" - "jmp *24(%ebp)" "\n" + "jmp *%eax" "\n" ); # elif defined(JS_CPU_ARM) @@ -448,31 +425,13 @@ FUNCTION_HEADER_EXTRA ".globl " SYMBOL_STRING(InjectJaegerReturn) "\n" SYMBOL_STRING(InjectJaegerReturn) ":" "\n" /* Restore frame regs. */ - "ldr lr, [r11, #44]" "\n" /* fp->ncode */ + "ldr lr, [r11, #20]" "\n" /* fp->ncode */ "ldr r1, [r11, #24]" "\n" /* fp->rval data */ "ldr r2, [r11, #28]" "\n" /* fp->rval type */ "ldr r11, [sp, #28]" "\n" /* load f.fp */ "bx lr" "\n" ); -asm volatile ( -".text\n" -FUNCTION_HEADER_EXTRA -".globl " SYMBOL_STRING(SafePointTrampoline) "\n" -SYMBOL_STRING(SafePointTrampoline) ":" - /* - * On entry to SafePointTrampoline: - * r11 = fp - * sp[80] = safePoint - */ - "ldr ip, [sp, #80]" "\n" - /* Save the return address (in JaegerTrampoline) to fp->ncode. */ - "str lr, [r11, #44]" "\n" - /* Jump to 'safePoint' via 'ip' because a load into the PC from an address on - * the stack looks like a return, and may upset return stack prediction. */ - "bx ip" "\n" -); - asm volatile ( ".text\n" FUNCTION_HEADER_EXTRA @@ -484,7 +443,6 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" * r1 = fp * r2 = code * r3 = stackLimit - * sp[0] = safePoint * * The VMFrame for ARM looks like this: * [ lr ] \ @@ -521,7 +479,7 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" /* Preserve 'code' (r2) in an arbitrary callee-saved register. */ " mov r4, r2" "\n" - /* Preserve 'fp' (r1) in r11 (JSFrameReg) for SafePointTrampoline. */ + /* Preserve 'fp' (r1) in r11 (JSFrameReg). */ " mov r11, r1" "\n" " mov r0, sp" "\n" @@ -530,7 +488,16 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" " blx " SYMBOL_STRING_VMFRAME(PushActiveVMFrame)"\n" /* Call the compiled JavaScript function. */ -" blx r4" "\n" +" bx r4" "\n" +); + +asm volatile ( +".text\n" +FUNCTION_HEADER_EXTRA +".globl " SYMBOL_STRING(JaegerTrampolineReturn) "\n" +SYMBOL_STRING(JaegerTrampolineReturn) ":" "\n" +" str r1, [r11, #24]" "\n" /* fp->rval data */ +" str r2, [r11, #28]" "\n" /* fp->rval type */ /* Tidy up. */ " mov r0, sp" "\n" @@ -608,24 +575,14 @@ extern "C" { __asm { mov edx, [ebx + 0x18]; mov ecx, [ebx + 0x1C]; - mov eax, [ebx + 0x2C]; + mov eax, [ebx + 0x14]; mov ebx, [esp + 0x1C]; - push eax; - ret; - } - } - - __declspec(naked) void SafePointTrampoline() - { - __asm { - pop eax; - mov [ebx + 0x2C], eax; - jmp [ebp + 24]; + jmp eax; } } __declspec(naked) JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code, - Value *stackLimit, void *safePoint) + Value *stackLimit) { __asm { /* Prologue. */ @@ -651,7 +608,15 @@ extern "C" { mov ecx, esp; call PushActiveVMFrame; - call [ebp + 16]; + jmp dword ptr [ebp + 16]; + } + } + + __declspec(naked) void JaegerTrampolineReturn() + { + __asm { + mov [ebx + 0x18], edx; + mov [ebx + 0x1C], ecx; mov ecx, esp; call PopActiveVMFrame; @@ -756,11 +721,10 @@ ThreadData::Finish() } extern "C" JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code, - Value *stackLimit, void *safePoint); -extern "C" void SafePointTrampoline(); + Value *stackLimit); static inline JSBool -EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code, void *safePoint) +EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code) { JS_ASSERT(cx->regs); JS_CHECK_RECURSION(cx, return JS_FALSE;); @@ -769,8 +733,7 @@ EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code, void *safePoint) Profiler prof; JSScript *script = fp->script(); - JaegerSpew(JSpew_Prof, "%s jaeger script: %s, line %d\n", - safePoint ? "dropping" : "entering", + JaegerSpew(JSpew_Prof, "%s jaeger script, line %d\n", script->filename, script->lineno); prof.start(); #endif @@ -786,7 +749,7 @@ EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code, void *safePoint) JSFrameRegs *oldRegs = cx->regs; JSAutoResolveFlags rf(cx, JSRESOLVE_INFER); - JSBool ok = JaegerTrampoline(cx, fp, code, stackLimit, safePoint); + JSBool ok = JaegerTrampoline(cx, fp, code, stackLimit); cx->setCurrentRegs(oldRegs); @@ -814,7 +777,7 @@ mjit::JaegerShot(JSContext *cx) JS_ASSERT(cx->regs->pc == script->code); - return EnterMethodJIT(cx, cx->fp(), script->jit->invoke, NULL); + return EnterMethodJIT(cx, cx->fp(), script->jit->invoke); } JSBool @@ -824,9 +787,7 @@ js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint) JS_ASSERT(!TRACE_RECORDER(cx)); #endif - void *code = JS_FUNC_TO_DATA_PTR(void *, SafePointTrampoline); - - return EnterMethodJIT(cx, cx->fp(), code, safePoint); + return EnterMethodJIT(cx, cx->fp(), safePoint); } template diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index 9085385a93f..1a23505dba8 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -317,7 +317,7 @@ class CallCompiler RegisterID t0 = inlFrame.tempRegs.takeAnyReg(); /* Generate the inline frame creation. */ - inlFrame.assemble(); + inlFrame.assemble(ic.funGuard.labelAtOffset(ic.joinPointOffset).executableAddress()); /* funPtrReg is still valid. Check if a compilation is needed. */ Address scriptAddr(ic.funPtrReg, offsetof(JSFunction, u) + @@ -338,35 +338,25 @@ class CallCompiler JSC::MacroAssembler::Call tryCompile = masm.stubCall(JS_FUNC_TO_DATA_PTR(void *, stubs::CompileFunction), script->code, ic.frameDepth); + masm.loadPtr(FrameAddress(offsetof(VMFrame, regs.fp)), JSFrameReg); Jump notCompiled = masm.branchTestPtr(Assembler::Zero, Registers::ReturnReg, Registers::ReturnReg); - masm.call(Registers::ReturnReg); - Jump done = masm.jump(); + masm.jump(Registers::ReturnReg); hasCode.linkTo(masm.label(), &masm); /* Get nmap[ARITY], set argc, call. */ masm.move(Imm32(ic.argc), JSParamReg_Argc); masm.loadPtr(Address(t0, offsetof(JITScript, arityCheck)), t0); - masm.call(t0); - - /* Rejoin with the fast path. */ - Jump rejoin = masm.jump(); - - /* Worst case - function didn't compile. */ - notCompiled.linkTo(masm.label(), &masm); - masm.loadPtr(FrameAddress(offsetof(VMFrame, regs.fp)), JSFrameReg); - notCompiled = masm.jump(); + masm.jump(t0); JSC::ExecutablePool *ep = poolForSize(masm.size(), CallICInfo::Pool_ScriptStub); if (!ep) return false; JSC::LinkBuffer buffer(&masm, ep); - buffer.link(rejoin, ic.funGuard.labelAtOffset(ic.joinPointOffset)); - buffer.link(done, ic.funGuard.labelAtOffset(ic.joinPointOffset)); buffer.link(notCompiled, ic.slowPathStart.labelAtOffset(ic.slowJoinOffset)); buffer.link(tryCompile, JSC::FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, stubs::CompileFunction))); @@ -392,8 +382,8 @@ class CallCompiler ic.fastGuardedObject = obj; repatch.repatch(ic.funGuard, obj); - repatch.relink(ic.funGuard.callAtOffset(ic.hotCallOffset), - JSC::FunctionPtr(script->ncode)); + repatch.relink(ic.funGuard.jumpAtOffset(ic.hotJumpOffset), + JSC::CodeLocationLabel(script->ncode)); JaegerSpew(JSpew_PICs, "patched CALL path %p (obj: %p)\n", start, ic.fastGuardedObject); } @@ -486,12 +476,15 @@ class CallCompiler /* Store pc. */ masm.storePtr(ImmPtr(cx->regs->pc), - FrameAddress(offsetof(VMFrame, regs) + offsetof(JSFrameRegs, pc))); + FrameAddress(offsetof(VMFrame, regs.pc))); /* Store sp. */ uint32 spOffset = sizeof(JSStackFrame) + ic.frameDepth * sizeof(Value); masm.addPtr(Imm32(spOffset), JSFrameReg, t0); - masm.storePtr(t0, FrameAddress(offsetof(VMFrame, regs) + offsetof(JSFrameRegs, sp))); + masm.storePtr(t0, FrameAddress(offsetof(VMFrame, regs.sp))); + + /* Store fp. */ + masm.storePtr(JSFrameReg, FrameAddress(offsetof(VMFrame, regs.fp))); /* Grab cx early on to avoid stack mucking on x86. */ #ifdef JS_CPU_X86 diff --git a/js/src/methodjit/MonoIC.h b/js/src/methodjit/MonoIC.h index 085bf9aed65..485e21aab9b 100644 --- a/js/src/methodjit/MonoIC.h +++ b/js/src/methodjit/MonoIC.h @@ -145,7 +145,7 @@ struct CallICInfo { JSC::CodeLocationJump funJump; /* Offset to inline scripted call, from funGuard. */ - uint32 hotCallOffset : 16; + uint32 hotJumpOffset : 16; uint32 joinPointOffset : 16; /* Out of line slow call. */ diff --git a/js/src/methodjit/NunboxAssembler.h b/js/src/methodjit/NunboxAssembler.h index 847e55f4856..f3f781fbcf9 100644 --- a/js/src/methodjit/NunboxAssembler.h +++ b/js/src/methodjit/NunboxAssembler.h @@ -145,6 +145,14 @@ class Assembler : public BaseAssembler return l; } + void loadValueAsComponents(const Value &val, RegisterID type, RegisterID payload) { + jsval_layout jv; + jv.asBits = JSVAL_BITS(Jsvalify(val)); + + move(ImmTag(jv.s.tag), type); + move(Imm32(jv.s.payload.u32), payload); + } + /* * Stores type first, then payload. */ diff --git a/js/src/methodjit/PunboxAssembler.h b/js/src/methodjit/PunboxAssembler.h index 2e5ba4418c7..a23e47ec4d0 100644 --- a/js/src/methodjit/PunboxAssembler.h +++ b/js/src/methodjit/PunboxAssembler.h @@ -125,6 +125,11 @@ class Assembler : public BaseAssembler return l; } + void loadValueAsComponents(const Value &val, RegisterID type, RegisterID payload) { + move(Imm64(val.asRawBits() & 0xFFFF800000000000), type); + move(Imm64(val.asRawBits() & 0x00007FFFFFFFFFFF), payload); + } + template void storeValueFromComponents(RegisterID type, RegisterID payload, T address) { move(type, Registers::ValueReg); diff --git a/js/src/methodjit/StubCalls-inl.h b/js/src/methodjit/StubCalls-inl.h index 0bff7f1876c..15c6ee4577f 100644 --- a/js/src/methodjit/StubCalls-inl.h +++ b/js/src/methodjit/StubCalls-inl.h @@ -44,19 +44,15 @@ namespace js { namespace mjit { -#define THROW() \ - do { \ - void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \ - *f.returnAddressLocation() = ptr; \ - return; \ - } while (0) +static inline void +ThrowException(VMFrame &f) +{ + void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); + *f.returnAddressLocation() = ptr; +} -#define THROWV(v) \ - do { \ - void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \ - *f.returnAddressLocation() = ptr; \ - return v; \ - } while (0) +#define THROW() do { ThrowException(f); return; } while (0) +#define THROWV(v) do { ThrowException(f); return v; } while (0) static inline JSObject * ValueToObject(JSContext *cx, Value *vp) diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index ce9d465fd39..9b37c5f5ea2 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -1346,7 +1346,7 @@ stubs::Debugger(VMFrame &f, jsbytecode *pc) case JSTRAP_RETURN: f.cx->throwing = JS_FALSE; - f.cx->fp()->setReturnValue(rval); + f.cx->fp()->setAssignedReturnValue(rval); #if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64) *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *, JS_METHODJIT_DATA(f.cx).trampolines.forceReturnFast); @@ -1386,7 +1386,7 @@ stubs::Trap(VMFrame &f, jsbytecode *pc) case JSTRAP_RETURN: f.cx->throwing = JS_FALSE; - f.cx->fp()->setReturnValue(rval); + f.cx->fp()->setAssignedReturnValue(rval); #if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64) *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *, JS_METHODJIT_DATA(f.cx).trampolines.forceReturnFast); diff --git a/js/src/methodjit/TrampolineCompiler.cpp b/js/src/methodjit/TrampolineCompiler.cpp index af1e8cb5495..e7dd63ee236 100644 --- a/js/src/methodjit/TrampolineCompiler.cpp +++ b/js/src/methodjit/TrampolineCompiler.cpp @@ -118,31 +118,22 @@ bool TrampolineCompiler::generateForceReturn(Assembler &masm) { /* if (hasArgsObj() || hasCallObj()) stubs::PutActivationObjects() */ - Jump noActObjs = masm.branchTest32(Assembler::Zero, - Address(JSFrameReg, JSStackFrame::offsetOfFlags()), + Jump noActObjs = masm.branchTest32(Assembler::Zero, FrameFlagsAddress(), Imm32(JSFRAME_HAS_CALL_OBJ | JSFRAME_HAS_ARGS_OBJ)); masm.stubCall(stubs::PutActivationObjects, NULL, 0); noActObjs.linkTo(masm.label(), &masm); - /* - * r = fp->prev - * f.fp = r - */ - masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfPrev()), Registers::ReturnReg); - masm.storePtr(Registers::ReturnReg, FrameAddress(offsetof(VMFrame, regs.fp))); + /* Store any known return value */ + masm.loadValueAsComponents(UndefinedValue(), JSReturnReg_Type, JSReturnReg_Data); + Jump rvalClear = masm.branchTest32(Assembler::Zero, + FrameFlagsAddress(), Imm32(JSFRAME_RVAL_ASSIGNED)); + Address rvalAddress(JSFrameReg, JSStackFrame::offsetOfReturnValue()); + masm.loadValueAsComponents(rvalAddress, JSReturnReg_Type, JSReturnReg_Data); + rvalClear.linkTo(masm.label(), &masm); - Address rval(JSFrameReg, JSStackFrame::offsetOfReturnValue()); - masm.loadValueAsComponents(rval, JSReturnReg_Type, JSReturnReg_Data); - - masm.restoreReturnAddress(); - - masm.move(Registers::ReturnReg, JSFrameReg); -#ifdef DEBUG - masm.storePtr(ImmPtr(JSStackFrame::sInvalidpc), - Address(JSFrameReg, JSStackFrame::offsetOfSavedpc())); -#endif - - masm.ret(); + /* Return to the caller */ + masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfncode()), Registers::ReturnReg); + masm.jump(Registers::ReturnReg); return true; } diff --git a/js/src/methodjit/TrampolineMasmX64.asm b/js/src/methodjit/TrampolineMasmX64.asm index 09392a9a9d4..464d1bfa13d 100644 --- a/js/src/methodjit/TrampolineMasmX64.asm +++ b/js/src/methodjit/TrampolineMasmX64.asm @@ -84,8 +84,7 @@ JaegerTrampoline PROC FRAME ; Space for the rest of the VMFrame. sub rsp, 28h - ; This is actually part of VMFrame, but we need to save 5th param for - ; SafePointTrampoline + ; This is actually part of the VMFrame. mov r10, [rbp+8*5+8] push r10 @@ -99,7 +98,13 @@ JaegerTrampoline PROC FRAME add rsp, 20h ; Jump into the JIT code. - call qword ptr [rsp] + jmp qword ptr [rsp] +JaegerTrampoline ENDP + +; void JaegerTrampolineReturn(); +JaegerTrampolineReturn PROC FRAME + or rcx, rdx + mov [rbx + 0x30], rcx sub rsp, 20h lea rcx, [rsp+20h] call PopActiveVMFrame @@ -147,14 +152,6 @@ throwpoline_exit: JaegerThrowpoline ENDP -; void SafePointTrampoline(); -SafePointTrampoline PROC FRAME - .ENDPROLOG - pop rax - mov qword ptr [rbx+50h], rax ; fp->ncode_ - jmp qword ptr [rsp+8] -SafePointTrampoline ENDP - ; void InjectJaegerReturn(); InjectJaegerReturn PROC FRAME diff --git a/js/src/methodjit/TrampolineSUNWX64.s b/js/src/methodjit/TrampolineSUNWX64.s index 1e4c8880680..b34f6a673b1 100644 --- a/js/src/methodjit/TrampolineSUNWX64.s +++ b/js/src/methodjit/TrampolineSUNWX64.s @@ -71,10 +71,7 @@ JaegerTrampoline: /* Space for the rest of the VMFrame. */ subq $0x28, %rsp - /* - * This is actually part of the VMFrame, but we need to save |r8| for - * SafePointTrampoline. - */ + /* This is actually part of the VMFrame. */ pushq %r8 /* Set cx->regs and set the active frame. Save rdx and align frame in one. */ @@ -84,10 +81,16 @@ JaegerTrampoline: movq %rsp, %rdi call PushActiveVMFrame - /* - * Jump into into the JIT'd code. - */ - call *0(%rsp) + /* Jump into into the JIT'd code. */ + jmp *0(%rsp) +.size JaegerTrampoline, . - JaegerTrampoline + +/ void JaegerTrampolineReturn() +.global JaegerTrampolineReturn +.type JaegerTrampolineReturn, @function +JaegerTrampolineReturn: + or %rdx, %rcx + movq %rcx, 0x30(%rbx) movq %rsp, %rdi call PopActiveVMFrame @@ -100,7 +103,7 @@ JaegerTrampoline: popq %rbp movq $1, %rax ret -.size JaegerTrampoline, . - JaegerTrampoline +.size JaegerTrampolineReturn, . - JaegerTrampolineReturn / void *JaegerThrowpoline(js::VMFrame *vmFrame) @@ -126,19 +129,11 @@ JaegerThrowpoline: ret .size JaegerThrowpoline, . - JaegerThrowpoline -.global SafePointTrampoline -.type SafePointTrampoline, @function -SafePointTrampoline: - popq %rax - movq %rax, 0x50(%rbx) - jmp *8(%rsp) -.size SafePointTrampoline, . - SafePointTrampoline - .global InjectJaegerReturn .type InjectJaegerReturn, @function InjectJaegerReturn: movq 0x30(%rbx), %rcx /* load fp->rval_ into typeReg */ - movq 0x50(%rbx), %rax /* fp->ncode_ */ + movq 0x28(%rbx), %rax /* fp->ncode_ */ /* Reimplementation of PunboxAssembler::loadValueAsComponents() */ movq %r14, %rdx /* payloadReg = payloadMaskReg */ diff --git a/js/src/methodjit/TrampolineSUNWX86.s b/js/src/methodjit/TrampolineSUNWX86.s index 1f18a869e17..16ee60fcb6b 100644 --- a/js/src/methodjit/TrampolineSUNWX86.s +++ b/js/src/methodjit/TrampolineSUNWX86.s @@ -65,7 +65,15 @@ JaegerTrampoline: call SetVMFrameRegs call PushActiveVMFrame popl %edx - call *16(%ebp) + jmp *16(%ebp) +.size JaegerTrampoline, . - JaegerTrampoline + +/ void JaegerTrampolineReturn() +.global JaegerTrampolineReturn +.type JaegerTrampolineReturn, @function +JaegerTrampolineReturn: + movl %edx, 0x18(%ebx) + movl %ecx, 0x1C(%ebx) pushl %esp call PopActiveVMFrame @@ -76,7 +84,7 @@ JaegerTrampoline: popl %ebp movl $1, %eax ret -.size JaegerTrampoline, . - JaegerTrampoline +.size JaegerTrampolineReturn, . - JaegerTrampolineReturn / void *JaegerThrowpoline(js::VMFrame *vmFrame) @@ -117,24 +125,11 @@ throwpoline_exit: InjectJaegerReturn: movl 0x18(%ebx), %edx /* fp->rval_ data */ movl 0x1C(%ebx), %ecx /* fp->rval_ type */ - movl 0x2C(%ebx), %eax /* fp->ncode_ */ + movl 0x14(%ebx), %eax /* fp->ncode_ */ /* For Sun Studio there is no fast call. */ /* We add the stack by 8 before. */ addl $0x8, %esp /* Restore frame regs. */ movl 0x1C(%esp), %ebx /* f.fp */ - pushl %eax - ret + jmp *%eax .size InjectJaegerReturn, . - InjectJaegerReturn - -/* - * Take the fifth parameter from JaegerShot() and jump to it. This makes it so - * we can jump into arbitrary JIT code, which won't have the frame-fixup prologue. - */ -.global SafePointTrampoline -.type SafePointTrampoline, @function -SafePointTrampoline: - popl %eax - movl %eax, 0x2C(%ebx) - jmp *24(%ebp) -.size SafePointTrampoline, . - SafePointTrampoline