Bug 581893 - build a js::Invoke Gatling gun (r=waldo)

This commit is contained in:
Luke Wagner 2010-09-28 15:23:43 -07:00
parent 1f3d45495c
commit cbc9601aa0
10 changed files with 378 additions and 123 deletions

View File

@ -1766,12 +1766,11 @@ js_MergeSort(void *src, size_t nel, size_t elsize,
struct CompareArgs
{
JSContext *context;
Value fval;
InvokeArgsGuard args;
JSContext *context;
InvokeSessionGuard session;
CompareArgs(JSContext *cx, const Value &fval)
: context(cx), fval(fval)
CompareArgs(JSContext *cx)
: context(cx)
{}
};
@ -1792,17 +1791,15 @@ sort_compare(void *arg, const void *a, const void *b, int *result)
if (!JS_CHECK_OPERATION_LIMIT(cx))
return JS_FALSE;
CallArgs &args = ca->args;
args.callee() = ca->fval;
args.thisv().setNull();
args[0] = *av;
args[1] = *bv;
InvokeSessionGuard &session = ca->session;
session[0] = *av;
session[1] = *bv;
if (!Invoke(cx, ca->args, 0))
if (!session.invoke(cx))
return JS_FALSE;
jsdouble cmp;
if (!ValueToNumber(cx, args.rval(), &cmp))
if (!ValueToNumber(cx, session.rval(), &cmp))
return JS_FALSE;
/* Clamp cmp to -1, 0, 1. */
@ -2040,10 +2037,8 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp)
} while (++i != newlen);
}
} else {
LeaveTrace(cx);
CompareArgs ca(cx, fval);
if (!cx->stack().pushInvokeArgs(cx, 2, &ca.args))
CompareArgs ca(cx);
if (!ca.session.start(cx, fval, NullValue(), 2))
return false;
if (!js_MergeSort(vec, size_t(newlen), sizeof(Value),
@ -2819,34 +2814,31 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
if (length == 0)
return JS_TRUE;
JSObject *thisp;
Value thisv;
if (argc > 1 && !REDUCE_MODE(mode)) {
JSObject *thisp;
if (!js_ValueToObjectOrNull(cx, argv[1], &thisp))
return JS_FALSE;
argv[1].setObjectOrNull(thisp);
thisv.setObjectOrNull(thisp);
} else {
thisp = NULL;
thisv.setNull();
}
/*
* For all but REDUCE, we call with 3 args (value, index, array). REDUCE
* requires 4 args (accum, value, index, array).
*/
LeaveTrace(cx);
argc = 3 + REDUCE_MODE(mode);
InvokeArgsGuard args;
if (!cx->stack().pushInvokeArgs(cx, argc, &args))
InvokeSessionGuard session;
if (!session.start(cx, ObjectValue(*callable), thisv, argc))
return JS_FALSE;
MUST_FLOW_THROUGH("out");
JSBool ok = JS_TRUE;
JSBool cond;
Value calleev, thisv, objv;
calleev.setObject(*callable);
thisv.setObjectOrNull(thisp);
objv.setObject(*obj);
Value objv = ObjectValue(*obj);
AutoValueRooter tvr(cx);
for (jsint i = start; i != end; i += step) {
JSBool hole;
@ -2861,22 +2853,22 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
* Push callable and 'this', then args. We must do this for every
* iteration around the loop since Invoke clobbers its arguments.
*/
args.callee() = calleev;
args.thisv() = thisv;
Value *sp = args.argv();
uintN argi = 0;
if (REDUCE_MODE(mode))
*sp++ = *vp;
sp[0] = tvr.value();
sp[1].setInt32(i);
sp[2] = objv;
session[argi++] = *vp;
session[argi++] = tvr.value();
session[argi++] = Int32Value(i);
session[argi] = objv;
/* Do the call. */
ok = Invoke(cx, args, 0);
ok = session.invoke(cx);
if (!ok)
break;
const Value &rval = session.rval();
if (mode > MAP)
cond = js_ValueToBoolean(args.rval());
cond = js_ValueToBoolean(rval);
#ifdef __GNUC__ /* quell GCC overwarning */
else
cond = JS_FALSE;
@ -2887,10 +2879,10 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
break;
case REDUCE:
case REDUCE_RIGHT:
*vp = args.rval();
*vp = rval;
break;
case MAP:
ok = SetArrayElement(cx, newarr, i, args.rval());
ok = SetArrayElement(cx, newarr, i, rval);
if (!ok)
goto out;
break;

View File

@ -541,9 +541,8 @@ class InvokeArgsGuard : public CallArgs
JSStackFrame *prevInvokeFrame;
#endif
public:
inline InvokeArgsGuard() : cx(NULL), seg(NULL) {}
inline InvokeArgsGuard(JSContext *cx, Value *vp, uintN argc);
inline ~InvokeArgsGuard();
InvokeArgsGuard() : cx(NULL), seg(NULL) {}
~InvokeArgsGuard();
bool pushed() const { return cx != NULL; }
};
@ -565,8 +564,9 @@ class InvokeFrameGuard
JSFrameRegs *prevRegs_;
public:
InvokeFrameGuard() : cx_(NULL) {}
JS_REQUIRES_STACK ~InvokeFrameGuard();
~InvokeFrameGuard() { if (pushed()) pop(); }
bool pushed() const { return cx_ != NULL; }
void pop();
JSStackFrame *fp() const { return regs_.fp; }
};

View File

@ -352,12 +352,12 @@ StackSpace::popInvokeFrame(const InvokeFrameGuard &fg)
}
}
JS_REQUIRES_STACK JS_ALWAYS_INLINE
InvokeFrameGuard::~InvokeFrameGuard()
JS_ALWAYS_INLINE void
InvokeFrameGuard::pop()
{
if (JS_UNLIKELY(!pushed()))
return;
JS_ASSERT(pushed());
cx_->stack().popInvokeFrame(*this);
cx_ = NULL;
}
JS_REQUIRES_STACK JS_ALWAYS_INLINE JSStackFrame *

View File

@ -586,37 +586,6 @@ NoSuchMethod(JSContext *cx, uintN argc, Value *vp, uint32 flags)
namespace js {
class AutoPreserveEnumerators {
JSContext *cx;
JSObject *enumerators;
public:
AutoPreserveEnumerators(JSContext *cx) : cx(cx), enumerators(cx->enumerators)
{
}
~AutoPreserveEnumerators()
{
cx->enumerators = enumerators;
}
};
struct AutoInterpPreparer {
JSContext *cx;
JSScript *script;
AutoInterpPreparer(JSContext *cx, JSScript *script)
: cx(cx), script(script)
{
cx->interpLevel++;
}
~AutoInterpPreparer()
{
--cx->interpLevel;
}
};
JS_REQUIRES_STACK bool
RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp)
{
@ -652,6 +621,8 @@ RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp)
JS_REQUIRES_STACK bool
Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags)
{
/* N.B. Must be kept in sync with InvokeSessionGuard::start/invoke */
CallArgs args = argsRef;
JS_ASSERT(args.argc() <= JS_ARGS_LENGTH_MAX);
@ -685,10 +656,8 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags)
return CallJSNative(cx, fun->u.n.native, args.argc(), args.base());
}
JS_ASSERT(fun->isInterpreted());
JSScript *script = fun->u.i.script;
/* Handle the empty-script special case. */
JSScript *script = fun->script();
if (JS_UNLIKELY(script->isEmpty())) {
if (flags & JSINVOKE_CONSTRUCT) {
JSObject *obj = js_CreateThisForFunction(cx, &callee);
@ -719,6 +688,8 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags)
return false;
/*
* FIXME bug 592992: hoist to ExternalInvoke
*
* Compute |this|. Currently, this must happen after the frame is pushed
* and fp->scopeChain is correct because the thisObject hook may call
* JS_GetScopeChain.
@ -767,6 +738,98 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags)
return ok;
}
bool
InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &thisv, uintN argc)
{
#ifdef JS_TRACER
if (TRACE_RECORDER(cx))
AbortRecording(cx, "attempt to reenter VM while recording");
LeaveTrace(cx);
#endif
/* Always push arguments, regardless of optimized/normal invoke. */
StackSpace &stack = cx->stack();
if (!stack.pushInvokeArgs(cx, argc, &args_))
return false;
do {
/* Hoist dynamic checks from scripted Invoke. */
if (!calleev.isObject())
break;
JSObject &callee = calleev.toObject();
if (callee.getClass() != &js_FunctionClass)
break;
JSFunction *fun = callee.getFunctionPrivate();
if (fun->isNative())
break;
script_ = fun->script();
if (fun->isHeavyweight() || script_->isEmpty() || cx->compartment->debugMode)
break;
/* Set (callee, this) once for the session (before args are duped). */
args_.callee().setObject(callee);
args_.thisv() = thisv;
/* Push the stack frame once for the session. */
uint32 flags = 0;
if (!stack.getInvokeFrame(cx, args_, fun, script_, &flags, &frame_))
return false;
JSStackFrame *fp = frame_.fp();
fp->initCallFrame(cx, calleev.toObject(), fun, argc, flags);
stack.pushInvokeFrame(cx, args_, &frame_);
// FIXME bug 592992: hoist thisObject hook to ExternalInvoke
if (thisv.isObject()) {
JSObject *thisp = thisv.toObject().thisObject(cx);
if (!thisp)
return false;
JS_ASSERT(IsSaneThisObject(*thisp));
fp->functionThis().setObject(*thisp);
}
#ifdef JS_METHODJIT
/* Hoist dynamic checks from RunScript. */
mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, fp);
if (status == mjit::Compile_Error)
return false;
if (status != mjit::Compile_Okay)
break;
code_ = script_->getJIT(fp->isConstructing())->invokeEntry;
/* Hoist dynamic checks from CheckStackAndEnterMethodJIT. */
JS_CHECK_RECURSION(cx, return JS_FALSE);
stackLimit_ = stack.getStackLimit(cx);
if (!stackLimit_)
return false;
stop_ = script_->code + script_->length - 1;
JS_ASSERT(*stop_ == JSOP_STOP);
#endif
/* Cached to avoid canonicalActualArg in InvokeSessionGuard::operator[]. */
nformals_ = fp->numFormalArgs();
formals_ = fp->formalArgs();
actuals_ = args_.argv();
JS_ASSERT(actuals_ == fp->actualArgs());
return true;
} while (0);
/*
* Use the normal invoke path.
*
* The callee slot gets overwritten during an unoptimized Invoke, so we
* cache it here and restore it before every Invoke call. The 'this' value
* does not get overwritten, so we can fill it here once.
*/
if (frame_.pushed())
frame_.pop();
args_.thisv() = thisv;
savedCallee_ = calleev;
formals_ = actuals_ = args_.argv();
nformals_ = (unsigned)-1;
return true;
}
bool
ExternalInvoke(JSContext *cx, const Value &thisv, const Value &fval,
uintN argc, Value *argv, Value *rval)

View File

@ -191,6 +191,9 @@ struct JSStackFrame
inline void initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun,
uint32 nactual, uint32 flags);
/* Used for SessionInvoke. */
inline void resetInvokeCallFrame();
/* Called by method-jit stubs and serve as a specification for jit-code. */
inline void initCallFrameCallerHalf(JSContext *cx, uint32 nactual, uint32 flags);
inline void initCallFrameEarlyPrologue(JSFunction *fun, void *ncode);
@ -368,7 +371,8 @@ struct JSStackFrame
template <class Op> inline void forEachCanonicalActualArg(Op op);
template <class Op> inline void forEachFormalArg(Op op);
/* True if we have created an arguments object for this frame; implies hasArgs(). */
inline void clearMissingArgs();
bool hasArgsObj() const {
return !!(flags_ & JSFRAME_HAS_ARGS_OBJ);
}
@ -558,7 +562,7 @@ struct JSStackFrame
/* Return value */
const js::Value& returnValue() {
const js::Value &returnValue() {
if (!(flags_ & JSFRAME_HAS_RVAL))
rval_.setUndefined();
return rval_;
@ -885,6 +889,32 @@ struct CallArgs
extern JS_REQUIRES_STACK bool
Invoke(JSContext *cx, const CallArgs &args, uint32 flags);
/*
* Natives like sort/forEach/replace call Invoke repeatedly with the same
* callee, this, and number of arguments. To optimize this, such natives can
* start an "invoke session" to factor out much of the dynamic setup logic
* required by a normal Invoke. Usage is:
*
* InvokeSessionGuard session(cx);
* if (!session.start(cx, callee, thisp, argc, &session))
* ...
*
* while (...) {
* // write actual args (not callee, this)
* session[0] = ...
* ...
* session[argc - 1] = ...
*
* if (!session.invoke(cx, session))
* ...
*
* ... = session.rval();
* }
*
* // session ended by ~InvokeSessionGuard
*/
class InvokeSessionGuard;
/*
* Consolidated js_Invoke flags simply rename certain JSFRAME_* flags, so that
* we can share bits stored in JSStackFrame.flags and passed to:

View File

@ -40,6 +40,9 @@
#ifndef jsinterpinlines_h__
#define jsinterpinlines_h__
#include "jsprobes.h"
#include "methodjit/MethodJIT.h"
inline void
JSStackFrame::initPrev(JSContext *cx)
{
@ -85,6 +88,42 @@ JSStackFrame::initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun,
JS_ASSERT(!hasCallObj());
}
inline void
JSStackFrame::resetInvokeCallFrame()
{
/* Undo changes to frame made during execution; see initCallFrame */
if (hasArgsObj())
args.nactual = argsObj().getArgsInitialLength();
JS_ASSERT(!(flags_ & ~(JSFRAME_FUNCTION |
JSFRAME_OVERFLOW_ARGS |
JSFRAME_UNDERFLOW_ARGS |
JSFRAME_HAS_CALL_OBJ |
JSFRAME_HAS_ARGS_OBJ |
JSFRAME_OVERRIDE_ARGS |
JSFRAME_HAS_PREVPC |
JSFRAME_HAS_RVAL |
JSFRAME_HAS_SCOPECHAIN |
JSFRAME_HAS_ANNOTATION |
JSFRAME_BAILED_AT_RETURN)));
flags_ &= JSFRAME_FUNCTION |
JSFRAME_OVERFLOW_ARGS |
JSFRAME_HAS_PREVPC |
JSFRAME_UNDERFLOW_ARGS;
JS_ASSERT_IF(!hasCallObj(), scopeChain_ == calleeValue().toObject().getParent());
JS_ASSERT_IF(hasCallObj(), scopeChain_ == callObj().getParent());
if (hasCallObj())
scopeChain_ = callObj().getParent();
JS_ASSERT(exec.fun == calleeValue().toObject().getFunctionPrivate());
JS_ASSERT(!hasImacropc());
JS_ASSERT(!hasHookData());
JS_ASSERT(annotation() == NULL);
JS_ASSERT(!hasCallObj());
}
inline void
JSStackFrame::initCallFrameCallerHalf(JSContext *cx, uint32 nactual, uint32 flagsArg)
{
@ -269,6 +308,13 @@ JSStackFrame::forEachFormalArg(Op op)
op(i, p);
}
JS_ALWAYS_INLINE void
JSStackFrame::clearMissingArgs()
{
if (flags_ & JSFRAME_UNDERFLOW_ARGS)
SetValueRangeToUndefined(formalArgs() + numActualArgs(), formalArgsEnd());
}
inline JSObject *
JSStackFrame::computeThisObject(JSContext *cx)
{
@ -394,6 +440,37 @@ JSStackFrame::maybeCallObj() const
namespace js {
class AutoPreserveEnumerators {
JSContext *cx;
JSObject *enumerators;
public:
AutoPreserveEnumerators(JSContext *cx) : cx(cx), enumerators(cx->enumerators)
{
}
~AutoPreserveEnumerators()
{
cx->enumerators = enumerators;
}
};
struct AutoInterpPreparer {
JSContext *cx;
JSScript *script;
AutoInterpPreparer(JSContext *cx, JSScript *script)
: cx(cx), script(script)
{
cx->interpLevel++;
}
~AutoInterpPreparer()
{
--cx->interpLevel;
}
};
inline void
PutActivationObjects(JSContext *cx, JSStackFrame *fp)
{
@ -407,6 +484,85 @@ PutActivationObjects(JSContext *cx, JSStackFrame *fp)
}
}
class InvokeSessionGuard
{
InvokeArgsGuard args_;
InvokeFrameGuard frame_;
Value savedCallee_;
Value *formals_, *actuals_;
unsigned nformals_;
JSScript *script_;
void *code_;
Value *stackLimit_;
jsbytecode *stop_;
bool optimized() const { return frame_.pushed(); }
public:
InvokeSessionGuard() : args_(), frame_() {}
~InvokeSessionGuard() {}
bool start(JSContext *cx, const Value &callee, const Value &thisv, uintN argc);
bool invoke(JSContext *cx) const;
bool started() const {
return args_.pushed();
}
Value &operator[](unsigned i) const {
JS_ASSERT(i < argc());
Value &arg = i < nformals_ ? formals_[i] : actuals_[i];
JS_ASSERT_IF(optimized(), &arg == &frame_.fp()->canonicalActualArg(i));
JS_ASSERT_IF(!optimized(), &arg == &args_[i]);
return arg;
}
uintN argc() const {
return args_.argc();
}
const Value &rval() const {
return optimized() ? frame_.fp()->returnValue() : args_.rval();
}
};
inline bool
InvokeSessionGuard::invoke(JSContext *cx) const
{
/* N.B. Must be kept in sync with Invoke */
if (!optimized()) {
args_.callee() = savedCallee_;
return Invoke(cx, args_, 0);
}
/* Clear any garbage left from the last Invoke. */
JSStackFrame *fp = frame_.fp();
fp->clearMissingArgs();
fp->resetInvokeCallFrame();
SetValueRangeToUndefined(fp->slots(), script_->nfixed);
JSBool ok;
{
AutoPreserveEnumerators preserve(cx);
Probes::enterJSFun(cx, fp->fun());
#ifdef JS_METHODJIT
AutoInterpPreparer prepareInterp(cx, script_);
ok = mjit::EnterMethodJIT(cx, fp, code_, stackLimit_);
cx->regs->pc = stop_;
#else
cx->regs->pc = script_->code;
ok = Interpret(cx, cx->fp());
#endif
Probes::exitJSFun(cx, fp->fun());
}
PutActivationObjects(cx, fp);
/* Don't clobber callee with rval; rval gets read from fp->rval. */
return ok;
}
}
#endif /* jsinterpinlines_h__ */

View File

@ -128,6 +128,7 @@ class AutoStringRooter;
class ExecuteArgsGuard;
class InvokeFrameGuard;
class InvokeArgsGuard;
class InvokeSessionGuard;
class TraceRecorder;
struct TraceMonitor;
class StackSpace;

View File

@ -77,6 +77,7 @@
#include "jsversion.h"
#include "jscntxtinlines.h"
#include "jsinterpinlines.h"
#include "jsobjinlines.h"
#include "jsstrinlines.h"
#include "jsregexpinlines.h"
@ -1977,18 +1978,19 @@ struct ReplaceData
: g(cx), cb(cx)
{}
JSString *str; /* 'this' parameter object as a string */
RegExpGuard g; /* regexp parameter object and private data */
JSObject *lambda; /* replacement function object or null */
JSString *repstr; /* replacement string */
jschar *dollar; /* null or pointer to first $ in repstr */
jschar *dollarEnd; /* limit pointer for js_strchr_limit */
jsint index; /* index in result of next replacement */
jsint leftIndex; /* left context index in str->chars */
JSSubString dollarStr; /* for "$$" InterpretDollar result */
bool calledBack; /* record whether callback has been called */
InvokeArgsGuard args; /* arguments for lambda's js_Invoke call */
JSCharBuffer cb; /* buffer built during DoMatch */
JSString *str; /* 'this' parameter object as a string */
RegExpGuard g; /* regexp parameter object and private data */
JSObject *lambda; /* replacement function object or null */
JSString *repstr; /* replacement string */
jschar *dollar; /* null or pointer to first $ in repstr */
jschar *dollarEnd; /* limit pointer for js_strchr_limit */
jsint index; /* index in result of next replacement */
jsint leftIndex; /* left context index in str->chars */
JSSubString dollarStr; /* for "$$" InterpretDollar result */
bool calledBack; /* record whether callback has been called */
InvokeSessionGuard session; /* arguments for repeated lambda Invoke call */
InvokeArgsGuard singleShot; /* arguments for single lambda Invoke call */
JSCharBuffer cb; /* buffer built during DoMatch */
};
static bool
@ -2076,8 +2078,6 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t
{
JSObject *lambda = rdata.lambda;
if (lambda) {
LeaveTrace(cx);
/*
* In the lambda case, not only do we find the replacement string's
* length, we compute repstr and return it via rdata for use within
@ -2089,32 +2089,33 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t
uintN p = res->getParenCount();
uintN argc = 1 + p + 2;
if (!rdata.args.pushed() && !cx->stack().pushInvokeArgs(cx, argc, &rdata.args))
return false;
if (!rdata.session.started()) {
Value lambdav = ObjectValue(*lambda);
if (!rdata.session.start(cx, lambdav, NullValue(), argc))
return false;
}
PreserveRegExpStatics save(res);
/* Push lambda and its 'this' parameter. */
CallArgs &args = rdata.args;
args.callee().setObject(*lambda);
args.thisv().setNull();
InvokeSessionGuard &session = rdata.session;
Value *sp = args.argv();
uintN argi = 0;
/* Push $&, $1, $2, ... */
if (!res->createLastMatch(cx, sp++))
if (!res->createLastMatch(cx, &session[argi++]))
return false;
for (size_t i = 0; i < res->getParenCount(); ++i) {
if (!res->createParen(cx, i, sp++))
if (!res->createParen(cx, i, &session[argi++]))
return false;
}
/* Push match index and input string. */
sp[0].setInt32(res->get(0, 0));
sp[1].setString(rdata.str);
session[argi++].setInt32(res->get(0, 0));
session[argi].setString(rdata.str);
if (!Invoke(cx, rdata.args, 0))
if (!session.invoke(cx))
return false;
/*
@ -2122,7 +2123,7 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t
* created by this js_ValueToString that would otherwise be GC-
* able, until we use rdata.repstr in DoReplace.
*/
JSString *repstr = js_ValueToString(cx, args.rval());
JSString *repstr = js_ValueToString(cx, session.rval());
if (!repstr)
return false;
@ -2402,10 +2403,10 @@ str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata
/* lambda(matchStr, matchStart, textstr) */
static const uint32 lambdaArgc = 3;
if (!cx->stack().pushInvokeArgs(cx, lambdaArgc, &rdata.args))
if (!cx->stack().pushInvokeArgs(cx, lambdaArgc, &rdata.singleShot))
return false;
CallArgs &args = rdata.args;
CallArgs &args = rdata.singleShot;
args.callee().setObject(*rdata.lambda);
args.thisv().setNull();
@ -2414,7 +2415,7 @@ str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata
sp[1].setInt32(fm.match());
sp[2].setString(rdata.str);
if (!Invoke(cx, rdata.args, 0))
if (!Invoke(cx, rdata.singleShot, 0))
return false;
JSString *repstr = js_ValueToString(cx, args.rval());

View File

@ -720,15 +720,12 @@ ThreadData::Finish()
#endif
}
extern "C" JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code,
Value *stackLimit);
extern "C" JSBool
JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code, Value *stackLimit);
static inline JSBool
EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code)
JSBool
mjit::EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code, Value *stackLimit)
{
JS_ASSERT(cx->regs);
JS_CHECK_RECURSION(cx, return JS_FALSE;);
#ifdef JS_METHODJIT_SPEW
Profiler prof;
JSScript *script = fp->script();
@ -738,10 +735,7 @@ EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code)
prof.start();
#endif
Value *stackLimit = cx->stack().getStackLimit(cx);
if (!stackLimit)
return false;
JS_ASSERT(cx->regs->fp == fp);
JSFrameRegs *oldRegs = cx->regs;
JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
@ -761,6 +755,18 @@ EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code)
return ok;
}
static inline JSBool
CheckStackAndEnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code)
{
JS_CHECK_RECURSION(cx, return JS_FALSE;);
Value *stackLimit = cx->stack().getStackLimit(cx);
if (!stackLimit)
return false;
return EnterMethodJIT(cx, fp, code, stackLimit);
}
JSBool
mjit::JaegerShot(JSContext *cx)
{
@ -775,7 +781,7 @@ mjit::JaegerShot(JSContext *cx)
JS_ASSERT(cx->regs->pc == script->code);
return EnterMethodJIT(cx, cx->fp(), jit->invokeEntry);
return CheckStackAndEnterMethodJIT(cx, cx->fp(), jit->invokeEntry);
}
JSBool
@ -785,7 +791,7 @@ js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint)
JS_ASSERT(!TRACE_RECORDER(cx));
#endif
return EnterMethodJIT(cx, cx->fp(), safePoint);
return CheckStackAndEnterMethodJIT(cx, cx->fp(), safePoint);
}
template <typename T>

View File

@ -216,6 +216,12 @@ struct JITScript {
void release();
};
/*
* Execute the given mjit code. This is a low-level call and callers must
* provide the same guarantees as JaegerShot/CheckStackAndEnterMethodJIT.
*/
JSBool EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code, Value *stackLimit);
/* Execute a method that has been JIT compiled. */
JSBool JaegerShot(JSContext *cx);