Bug 826148 - Part 3: Jaeger IC (r=bhackett)

This commit is contained in:
Shu-yu Guo 2013-01-10 13:04:04 -08:00
parent 8c3510746e
commit 1fdcab73ea
7 changed files with 148 additions and 12 deletions

View File

@ -1637,6 +1637,7 @@ mjit::Compiler::finishThisUp()
chunk->nCallICs = callICs.length();
cursor += sizeof(ic::CallICInfo) * chunk->nCallICs;
for (size_t i = 0; i < chunk->nCallICs; i++) {
jitCallICs[i].funGuardLabel = fullCode.locationOf(callICs[i].funGuardLabel);
jitCallICs[i].funGuard = fullCode.locationOf(callICs[i].funGuard);
jitCallICs[i].funJump = fullCode.locationOf(callICs[i].funJump);
jitCallICs[i].slowPathStart = stubCode.locationOf(callICs[i].slowPathStart);
@ -4430,6 +4431,7 @@ mjit::Compiler::inlineCallHelper(uint32_t argc, bool callingNew, FrameSize &call
* callee is scripted, compiled/compilable, and argc == nargs, then this
* guard is patched, and the compiled code address is baked in.
*/
callIC.funGuardLabel = masm.label();
Jump j = masm.branchPtrWithPatch(Assembler::NotEqual, icCalleeData, callIC.funGuard);
callIC.funJump = j;
@ -4629,6 +4631,11 @@ mjit::Compiler::inlineScriptedFunction(uint32_t argc, bool callingNew)
for (unsigned i = 0; i < ssa.numFrames(); i++) {
if (ssa.iterFrame(i).parent == a->inlineIndex && ssa.iterFrame(i).parentpc == PC) {
JSScript *script_ = ssa.iterFrame(i).script;
/* Don't inline if any of the callees should be cloned at callsite. */
if (script_->function()->isCloneAtCallsite())
return Compile_InlineAbort;
inlineCallees.append(script_);
if (script_->analysis()->numReturnSites() > 1)
calleeMultipleReturns = true;

View File

@ -120,6 +120,7 @@ class Compiler : public BaseCompiler
* more comments.
*/
uint32_t callIndex;
Label funGuardLabel;
DataLabelPtr funGuard;
Jump funJump;
Jump hotJump;

View File

@ -130,6 +130,22 @@ FindExceptionHandler(JSContext *cx)
* Clean up a frame and return.
*/
static inline bool
MaybeCloneAndPatchCallee(JSContext *cx, CallArgs args, HandleScript script, jsbytecode *pc)
{
if (cx->typeInferenceEnabled() &&
args.callee().isFunction() && args.callee().toFunction()->isCloneAtCallsite())
{
RootedFunction fun(cx, args.callee().toFunction());
fun = CloneFunctionAtCallsite(cx, fun, script, pc);
if (!fun)
return false;
args.setCallee(ObjectValue(*fun));
}
return true;
}
void JS_FASTCALL
stubs::SlowCall(VMFrame &f, uint32_t argc)
{
@ -137,10 +153,13 @@ stubs::SlowCall(VMFrame &f, uint32_t argc)
THROW();
CallArgs args = CallArgsFromSp(argc, f.regs.sp);
RootedScript fscript(f.cx, f.script());
if (!MaybeCloneAndPatchCallee(f.cx, args, fscript, f.pc()))
THROW();
if (!InvokeKernel(f.cx, args))
THROW();
RootedScript fscript(f.cx, f.script());
types::TypeScript::Monitor(f.cx, fscript, f.pc(), args.rval());
}
@ -148,10 +167,13 @@ void JS_FASTCALL
stubs::SlowNew(VMFrame &f, uint32_t argc)
{
CallArgs args = CallArgsFromSp(argc, f.regs.sp);
RootedScript fscript(f.cx, f.script());
if (!MaybeCloneAndPatchCallee(f.cx, args, fscript, f.pc()))
THROW();
if (!InvokeConstructorKernel(f.cx, args))
THROW();
RootedScript fscript(f.cx, f.script());
types::TypeScript::Monitor(f.cx, fscript, f.pc(), args.rval());
}
@ -210,6 +232,10 @@ stubs::FixupArity(VMFrame &f, uint32_t nactual)
/* Reserve enough space for a callee frame. */
CallArgs args = CallArgsFromSp(nactual, f.regs.sp);
if (fun->isCallsiteClone()) {
JS_ASSERT(args.callee().toFunction() == fun->getExtendedSlot(0).toObject().toFunction());
args.setCallee(ObjectValue(*fun));
}
StackFrame *fp = cx->stack.getFixupFrame(cx, DONT_REPORT_ERROR, args, fun,
script, ncode, initial, &f.stackLimit);
@ -287,7 +313,7 @@ UncachedInlineCall(VMFrame &f, InitialFrameFlags initial,
CallArgs args = CallArgsFromSp(argc, f.regs.sp);
RootedFunction newfun(cx, args.callee().toFunction());
RootedScript newscript(cx, JSFunction::getOrCreateScript(cx, newfun));
RootedScript newscript(cx, newfun->nonLazyScript());
if (!newscript)
return false;
@ -395,15 +421,18 @@ stubs::UncachedNewHelper(VMFrame &f, uint32_t argc, UncachedCallResult &ucr)
ucr.init();
JSContext *cx = f.cx;
CallArgs args = CallArgsFromSp(argc, f.regs.sp);
RootedScript fscript(cx, f.script());
if (!ucr.setFunction(cx, args, fscript, f.pc()))
THROW();
/* Try to do a fast inline call before the general Invoke path. */
if (IsFunctionObject(args.calleev(), ucr.fun.address()) && ucr.fun->isInterpretedConstructor()) {
if (ucr.fun && ucr.fun->isInterpretedConstructor()) {
if (!UncachedInlineCall(f, INITIAL_CONSTRUCT, &ucr.codeAddr, &ucr.unjittable, argc))
THROW();
} else {
if (!InvokeConstructorKernel(cx, args))
THROW();
RootedScript fscript(cx, f.script());
types::TypeScript::Monitor(f.cx, fscript, f.pc(), args.rval());
}
}
@ -453,8 +482,12 @@ stubs::UncachedCallHelper(VMFrame &f, uint32_t argc, bool lowered, UncachedCallR
JSContext *cx = f.cx;
CallArgs args = CallArgsFromSp(argc, f.regs.sp);
RootedScript fscript(cx, f.script());
if (IsFunctionObject(args.calleev(), ucr.fun.address())) {
if (!ucr.setFunction(cx, args, fscript, f.pc()))
THROW();
if (ucr.fun) {
if (ucr.fun->isInterpreted()) {
InitialFrameFlags initial = lowered ? INITIAL_LOWERED : INITIAL_NONE;
if (!UncachedInlineCall(f, initial, &ucr.codeAddr, &ucr.unjittable, argc))
@ -474,7 +507,6 @@ stubs::UncachedCallHelper(VMFrame &f, uint32_t argc, bool lowered, UncachedCallR
if (!InvokeKernel(f.cx, args))
THROW();
RootedScript fscript(cx, f.script());
types::TypeScript::Monitor(f.cx, fscript, f.pc(), args.rval());
return;
}

View File

@ -1029,13 +1029,13 @@ class CallCompiler : public BaseCompiler
linker.link(claspGuard, ic.slowPathStart);
linker.link(funGuard, ic.slowPathStart);
linker.link(done, ic.funGuard.labelAtOffset(ic.hotPathOffset));
JSC::CodeLocationLabel cs = linker.finalize(f);
ic.funJumpTarget = linker.finalize(f);
JaegerSpew(JSpew_PICs, "generated CALL closure stub %p (%lu bytes)\n",
cs.executableAddress(), (unsigned long) masm.size());
ic.funJumpTarget.executableAddress(), (unsigned long) masm.size());
Repatcher repatch(f.chunk());
repatch.relink(ic.funJump, cs);
repatch.relink(ic.funJump, ic.funJumpTarget);
return true;
}
@ -1209,10 +1209,67 @@ class CallCompiler : public BaseCompiler
ic.fastGuardedNative = fun;
linker.link(funGuard, ic.slowPathStart);
JSC::CodeLocationLabel start = linker.finalize(f);
ic.funJumpTarget = linker.finalize(f);
JaegerSpew(JSpew_PICs, "generated native CALL stub %p (%lu bytes)\n",
ic.funJumpTarget.executableAddress(), (unsigned long) masm.size());
Repatcher repatch(f.chunk());
repatch.relink(ic.funJump, ic.funJumpTarget);
return true;
}
bool generateCallsiteCloneStub(HandleFunction original, HandleFunction fun)
{
AutoAssertNoGC nogc;
Assembler masm;
// If we have a callsite clone, we do the folowing hack:
//
// 1) Patch funJump to a stub which guards on the identity of the
// original function. If this guard fails, we jump to the original
// funJump target.
// 2) Load the clone into the callee register.
// 3) Jump *back* to funGuard.
//
// For the inline path, hopefully we will succeed upon jumping back to
// funGuard after loading the clone.
//
// This hack is not ideal, as we can actually fail the first funGuard
// twice: we fail the first funGuard, pass the second funGuard, load
// the clone, and jump back to the first funGuard. We fail the first
// funGuard again, and this time we also fail the second funGuard,
// since a function's clone is never equal to itself. Finally, we jump
// to the original funJump target.
// Guard on the original function's identity.
Jump originalGuard = masm.branchPtr(Assembler::NotEqual, ic.funObjReg, ImmPtr(original));
// Load the clone.
masm.move(ImmPtr(fun), ic.funObjReg);
// Jump back to the first fun guard.
Jump done = masm.jump();
LinkerHelper linker(masm, JSC::JAEGER_CODE);
JSC::ExecutablePool *ep = linker.init(f.cx);
if (!ep)
return false;
if (!linker.verifyRange(f.chunk())) {
disable();
return true;
}
linker.link(originalGuard, !!ic.funJumpTarget ? ic.funJumpTarget : ic.slowPathStart);
linker.link(done, ic.funGuardLabel);
JSC::CodeLocationLabel start = linker.finalize(f);
JaegerSpew(JSpew_PICs, "generated CALL clone stub %p (%lu bytes)\n",
start.executableAddress(), (unsigned long) masm.size());
JaegerSpew(JSpew_PICs, "guarding %p with clone %p\n", original.get(), fun.get());
Repatcher repatch(f.chunk());
repatch.relink(ic.funJump, start);
@ -1303,6 +1360,9 @@ class CallCompiler : public BaseCompiler
}
}
if (ucr.original && !generateCallsiteCloneStub(ucr.original, ucr.fun))
THROWV(NULL);
return ucr.codeAddr;
}
};

View File

@ -165,6 +165,9 @@ struct CallICInfo {
FrameSize frameSize;
/* Label to the function object identity guard. */
JSC::CodeLocationLabel funGuardLabel;
/* Function object identity guard. */
JSC::CodeLocationDataLabelPtr funGuard;
@ -174,6 +177,12 @@ struct CallICInfo {
/* Inline to OOL jump, redirected by stubs. */
JSC::CodeLocationJump funJump;
/*
* Target of the above jump, remembered so that if we need to generate a
* callsite clone stub we can redirect to the original funJump target.
*/
JSC::CodeLocationLabel funJumpTarget;
/*
* If an Ion stub has been generated, its guard may be linked to another
* stub. The guard location is stored in this label.

View File

@ -29,6 +29,27 @@ ReportAtomNotDefined(JSContext *cx, JSAtom *atom)
js_ReportIsNotDefined(cx, printable.ptr());
}
inline bool
stubs::UncachedCallResult::setFunction(JSContext *cx, CallArgs &args,
HandleScript callScript, jsbytecode *callPc)
{
if (!IsFunctionObject(args.calleev(), fun.address()))
return true;
if (fun->isInterpretedLazy() && !JSFunction::getOrCreateScript(cx, fun))
return false;
if (cx->typeInferenceEnabled() && fun->isCloneAtCallsite()) {
original = fun;
fun = CloneFunctionAtCallsite(cx, original, callScript, callPc);
if (!fun)
return false;
args.setCallee(ObjectValue(*fun));
}
return true;
}
} /* namespace mjit */
} /* namespace js */

View File

@ -8,6 +8,7 @@
#if !defined jslogic_h__ && defined JS_METHODJIT
#define jslogic_h__
#include "jsfuninlines.h"
#include "MethodJIT.h"
namespace js {
@ -58,16 +59,21 @@ void JS_FASTCALL ScriptProbeOnlyEpilogue(VMFrame &f);
*/
struct UncachedCallResult {
RootedFunction fun; // callee function
RootedFunction original; // NULL if fun is not a callsite clone, else
// points to the original function.
void *codeAddr; // code address of compiled callee function
bool unjittable; // did we try to JIT and fail?
UncachedCallResult(JSContext *cx) : fun(cx) {}
UncachedCallResult(JSContext *cx) : fun(cx), original(cx) {}
void init() {
fun = NULL;
original = NULL;
codeAddr = NULL;
unjittable = false;
}
inline bool setFunction(JSContext *cx, CallArgs &args,
HandleScript callScript, jsbytecode *callPc);
};
/*