mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 22:55:23 +00:00
Bug 826148 - Part 3: Jaeger IC (r=bhackett)
This commit is contained in:
parent
8c3510746e
commit
1fdcab73ea
@ -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;
|
||||
|
@ -120,6 +120,7 @@ class Compiler : public BaseCompiler
|
||||
* more comments.
|
||||
*/
|
||||
uint32_t callIndex;
|
||||
Label funGuardLabel;
|
||||
DataLabelPtr funGuard;
|
||||
Jump funJump;
|
||||
Jump hotJump;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
@ -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.
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user