Improved JM call path, bug 587707. r=lw,dvander

This commit is contained in:
Brian Hackett 2010-09-29 06:27:34 -07:00
Родитель 5bf8ef0fc2
Коммит f6d77b2ef3
28 изменённых файлов: 561 добавлений и 424 удалений

Просмотреть файл

@ -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 *

Просмотреть файл

@ -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.

Просмотреть файл

@ -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);

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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
{

Просмотреть файл

@ -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");

Просмотреть файл

@ -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 */

Просмотреть файл

@ -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;

Просмотреть файл

@ -65,10 +65,14 @@ namespace js
OpcodeStatus *ops;
Vector<jsbytecode *, 16, ContextAllocPolicy> 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];

Просмотреть файл

@ -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);

Просмотреть файл

@ -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<PICGenInfo, 64> pics;
#endif
js::Vector<CallPatchInfo, 64> callPatches;
js::Vector<InternalCallSite, 64> callSites;
js::Vector<DoublePatch, 16> doubleList;
js::Vector<uint32, 16> 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);

Просмотреть файл

@ -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

Просмотреть файл

@ -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.
*/

Просмотреть файл

@ -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;
}
};

Просмотреть файл

@ -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;

Просмотреть файл

@ -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 <typename T>

Просмотреть файл

@ -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

Просмотреть файл

@ -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. */

Просмотреть файл

@ -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.
*/

Просмотреть файл

@ -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 <typename T>
void storeValueFromComponents(RegisterID type, RegisterID payload, T address) {
move(type, Registers::ValueReg);

Просмотреть файл

@ -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)

Просмотреть файл

@ -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);

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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

Просмотреть файл

@ -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 */

Просмотреть файл

@ -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