зеркало из https://github.com/mozilla/gecko-dev.git
Bug 960523 - Add Baseline IC stub for fun.call(). r=djvj
This commit is contained in:
Родитель
c2f6e4f476
Коммит
6fb7125afd
|
@ -0,0 +1,54 @@
|
|||
function test1() {
|
||||
var f = function() { return 1; };
|
||||
|
||||
for (var i=0; i<25; i++) {
|
||||
f.call();
|
||||
if (i > 20)
|
||||
f = Math.abs;
|
||||
}
|
||||
}
|
||||
test1();
|
||||
|
||||
var origCall = Function.prototype.call;
|
||||
|
||||
function test2() {
|
||||
var f = function() { return 1; };
|
||||
var c = 0;
|
||||
for (var i=0; i<25; i++) {
|
||||
f.call();
|
||||
if (i > 20)
|
||||
Function.prototype.call = function() { c++; };
|
||||
}
|
||||
assertEq(c, 3);
|
||||
}
|
||||
test2();
|
||||
Function.prototype.call = origCall;
|
||||
|
||||
function test3() {
|
||||
var f = function() { return 1; };
|
||||
for (var i=0; i<25; i++) {
|
||||
f.call();
|
||||
if (i > 20)
|
||||
Function.prototype.call = undefined;
|
||||
}
|
||||
}
|
||||
try {
|
||||
test3();
|
||||
assertEq(0, 1);
|
||||
} catch(e) {}
|
||||
|
||||
Function.prototype.call = origCall;
|
||||
|
||||
function test4() {
|
||||
var f = function(a, b, c) {
|
||||
assertEq(arguments.length, 1);
|
||||
assertEq(a, 1);
|
||||
assertEq(b, undefined);
|
||||
assertEq(c, undefined);
|
||||
return 1;
|
||||
};
|
||||
for (var i=0; i<25; i++) {
|
||||
f.call(null, 1);
|
||||
}
|
||||
}
|
||||
test4();
|
|
@ -671,6 +671,7 @@ ICStubCompiler::guardProfilingEnabled(MacroAssembler &masm, Register scratch, La
|
|||
kind == ICStub::Call_Native ||
|
||||
kind == ICStub::Call_ScriptedApplyArray ||
|
||||
kind == ICStub::Call_ScriptedApplyArguments ||
|
||||
kind == ICStub::Call_ScriptedFunCall ||
|
||||
kind == ICStub::GetProp_CallScripted ||
|
||||
kind == ICStub::GetProp_CallNative ||
|
||||
kind == ICStub::GetProp_CallDOMProxyNative ||
|
||||
|
@ -7807,6 +7808,39 @@ TryAttachFunApplyStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
TryAttachFunCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsbytecode *pc,
|
||||
HandleValue thisv, bool *attached)
|
||||
{
|
||||
// Try to attach a stub for Function.prototype.call with scripted |this|.
|
||||
|
||||
*attached = false;
|
||||
if (!thisv.isObject() || !thisv.toObject().is<JSFunction>())
|
||||
return true;
|
||||
RootedFunction target(cx, &thisv.toObject().as<JSFunction>());
|
||||
|
||||
// Attach a stub if the script can be Baseline-compiled. We do this also
|
||||
// if the script is not yet compiled to avoid attaching a CallNative stub
|
||||
// that handles everthing, even when the callee becomes hot.
|
||||
if (target->hasScript() && target->nonLazyScript()->canBaselineCompile() &&
|
||||
!stub->hasStub(ICStub::Call_ScriptedFunCall))
|
||||
{
|
||||
IonSpew(IonSpew_BaselineIC, " Generating Call_ScriptedFunCall stub");
|
||||
|
||||
ICCall_ScriptedFunCall::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
|
||||
script->pcToOffset(pc));
|
||||
ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!newStub)
|
||||
return false;
|
||||
|
||||
*attached = true;
|
||||
stub->addNewStub(newStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetTemplateObjectForNative(JSContext *cx, HandleScript script, jsbytecode *pc,
|
||||
Native native, const CallArgs &args, MutableHandleObject res)
|
||||
|
@ -7991,6 +8025,14 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsb
|
|||
return true;
|
||||
}
|
||||
|
||||
if (op == JSOP_FUNCALL && fun->native() == js_fun_call) {
|
||||
bool attached;
|
||||
if (!TryAttachFunCallStub(cx, stub, script, pc, thisv, &attached))
|
||||
return false;
|
||||
if (attached)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (stub->nativeStubCount() >= ICCall_Fallback::MAX_NATIVE_STUBS) {
|
||||
IonSpew(IonSpew_BaselineIC,
|
||||
" Too many Call_Native stubs. TODO: add Call_AnyNative!");
|
||||
|
@ -8972,6 +9014,144 @@ ICCall_ScriptedApplyArguments::Compiler::generateStubCode(MacroAssembler &masm)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ICCall_ScriptedFunCall::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
{
|
||||
Label failure;
|
||||
GeneralRegisterSet regs(availableGeneralRegs(0));
|
||||
bool canUseTailCallReg = regs.has(BaselineTailCallReg);
|
||||
|
||||
Register argcReg = R0.scratchReg();
|
||||
JS_ASSERT(argcReg != ArgumentsRectifierReg);
|
||||
|
||||
regs.take(argcReg);
|
||||
regs.take(ArgumentsRectifierReg);
|
||||
regs.takeUnchecked(BaselineTailCallReg);
|
||||
|
||||
// Load the callee in R1.
|
||||
// Stack Layout: [ ..., CalleeVal, ThisVal, Arg0Val, ..., ArgNVal, +ICStackValueOffset+ ]
|
||||
BaseIndex calleeSlot(BaselineStackReg, argcReg, TimesEight, ICStackValueOffset + sizeof(Value));
|
||||
masm.loadValue(calleeSlot, R1);
|
||||
regs.take(R1);
|
||||
|
||||
// Ensure callee is js_fun_call.
|
||||
masm.branchTestObject(Assembler::NotEqual, R1, &failure);
|
||||
|
||||
Register callee = masm.extractObject(R1, ExtractTemp0);
|
||||
masm.branchTestObjClass(Assembler::NotEqual, callee, regs.getAny(), &JSFunction::class_,
|
||||
&failure);
|
||||
masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
|
||||
masm.branchPtr(Assembler::NotEqual, callee, ImmPtr(js_fun_call), &failure);
|
||||
|
||||
// Ensure |this| is a scripted function with JIT code.
|
||||
BaseIndex thisSlot(BaselineStackReg, argcReg, TimesEight, ICStackValueOffset);
|
||||
masm.loadValue(thisSlot, R1);
|
||||
|
||||
masm.branchTestObject(Assembler::NotEqual, R1, &failure);
|
||||
callee = masm.extractObject(R1, ExtractTemp0);
|
||||
|
||||
masm.branchTestObjClass(Assembler::NotEqual, callee, regs.getAny(), &JSFunction::class_,
|
||||
&failure);
|
||||
masm.branchIfFunctionHasNoScript(callee, &failure);
|
||||
masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
|
||||
|
||||
// Load the start of the target JitCode.
|
||||
Register code = regs.takeAny();
|
||||
masm.loadBaselineOrIonRaw(callee, code, SequentialExecution, &failure);
|
||||
|
||||
// We no longer need R1.
|
||||
regs.add(R1);
|
||||
|
||||
// Push a stub frame so that we can perform a non-tail call.
|
||||
enterStubFrame(masm, regs.getAny());
|
||||
if (canUseTailCallReg)
|
||||
regs.add(BaselineTailCallReg);
|
||||
|
||||
// Values are on the stack left-to-right. Calling convention wants them
|
||||
// right-to-left so duplicate them on the stack in reverse order.
|
||||
pushCallArguments(masm, regs, argcReg);
|
||||
|
||||
// Discard callee (function.call).
|
||||
masm.addPtr(Imm32(sizeof(Value)), StackPointer);
|
||||
|
||||
// Pop scripted callee (the original |this|).
|
||||
ValueOperand val = regs.takeAnyValue();
|
||||
masm.popValue(val);
|
||||
|
||||
// Decrement argc if argc > 0. If argc == 0, push |undefined| as |this|.
|
||||
Label zeroArgs, done;
|
||||
masm.branchTest32(Assembler::Zero, argcReg, argcReg, &zeroArgs);
|
||||
masm.sub32(Imm32(1), argcReg);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(&zeroArgs);
|
||||
masm.pushValue(UndefinedValue());
|
||||
masm.bind(&done);
|
||||
|
||||
// Unbox scripted callee.
|
||||
callee = masm.extractObject(val, ExtractTemp0);
|
||||
|
||||
Register scratch = regs.takeAny();
|
||||
EmitCreateStubFrameDescriptor(masm, scratch);
|
||||
|
||||
// Note that we use Push, not push, so that callIon will align the stack
|
||||
// properly on ARM.
|
||||
masm.Push(argcReg);
|
||||
masm.Push(callee);
|
||||
masm.Push(scratch);
|
||||
|
||||
// Handle arguments underflow.
|
||||
Label noUnderflow;
|
||||
masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), callee);
|
||||
masm.branch32(Assembler::AboveOrEqual, argcReg, callee, &noUnderflow);
|
||||
{
|
||||
// Call the arguments rectifier.
|
||||
JS_ASSERT(ArgumentsRectifierReg != code);
|
||||
JS_ASSERT(ArgumentsRectifierReg != argcReg);
|
||||
|
||||
JitCode *argumentsRectifier =
|
||||
cx->runtime()->jitRuntime()->getArgumentsRectifier(SequentialExecution);
|
||||
|
||||
masm.movePtr(ImmGCPtr(argumentsRectifier), code);
|
||||
masm.loadPtr(Address(code, JitCode::offsetOfCode()), code);
|
||||
masm.mov(argcReg, ArgumentsRectifierReg);
|
||||
}
|
||||
|
||||
masm.bind(&noUnderflow);
|
||||
|
||||
// If needed, update SPS Profiler frame entry.
|
||||
{
|
||||
Label skipProfilerUpdate;
|
||||
|
||||
// Need to avoid using ArgumentsRectifierReg and code register.
|
||||
GeneralRegisterSet availRegs = availableGeneralRegs(0);
|
||||
availRegs.take(ArgumentsRectifierReg);
|
||||
availRegs.take(code);
|
||||
Register scratch = availRegs.takeAny();
|
||||
Register pcIdx = availRegs.takeAny();
|
||||
|
||||
// Check if profiling is enabled.
|
||||
guardProfilingEnabled(masm, scratch, &skipProfilerUpdate);
|
||||
|
||||
// Update profiling entry before leaving function.
|
||||
masm.load32(Address(BaselineStubReg, ICCall_ScriptedFunCall::offsetOfPCOffset()), pcIdx);
|
||||
masm.spsUpdatePCIdx(&cx->runtime()->spsProfiler, pcIdx, scratch);
|
||||
|
||||
masm.bind(&skipProfilerUpdate);
|
||||
}
|
||||
|
||||
masm.callIon(code);
|
||||
|
||||
leaveStubFrame(masm, true);
|
||||
|
||||
// Enter type monitor IC to type-check result.
|
||||
EmitEnterTypeMonitorIC(masm);
|
||||
|
||||
masm.bind(&failure);
|
||||
EmitStubGuardFailure(masm);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DoubleValueToInt32ForSwitch(Value *v)
|
||||
{
|
||||
|
|
|
@ -326,6 +326,7 @@ class ICEntry
|
|||
_(Call_Native) \
|
||||
_(Call_ScriptedApplyArray) \
|
||||
_(Call_ScriptedApplyArguments) \
|
||||
_(Call_ScriptedFunCall) \
|
||||
\
|
||||
_(GetElem_Fallback) \
|
||||
_(GetElem_NativeSlot) \
|
||||
|
@ -738,6 +739,7 @@ class ICStub
|
|||
case Call_Native:
|
||||
case Call_ScriptedApplyArray:
|
||||
case Call_ScriptedApplyArguments:
|
||||
case Call_ScriptedFunCall:
|
||||
case UseCount_Fallback:
|
||||
case GetElem_NativeSlot:
|
||||
case GetElem_NativePrototypeSlot:
|
||||
|
@ -5582,6 +5584,57 @@ class ICCall_ScriptedApplyArguments : public ICMonitoredStub
|
|||
};
|
||||
};
|
||||
|
||||
// Handles calls of the form |fun.call(...)| where fun is a scripted function.
|
||||
class ICCall_ScriptedFunCall : public ICMonitoredStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
protected:
|
||||
uint32_t pcOffset_;
|
||||
|
||||
ICCall_ScriptedFunCall(JitCode *stubCode, ICStub *firstMonitorStub, uint32_t pcOffset)
|
||||
: ICMonitoredStub(ICStub::Call_ScriptedFunCall, stubCode, firstMonitorStub),
|
||||
pcOffset_(pcOffset)
|
||||
{}
|
||||
|
||||
public:
|
||||
static inline ICCall_ScriptedFunCall *New(ICStubSpace *space, JitCode *code,
|
||||
ICStub *firstMonitorStub, uint32_t pcOffset)
|
||||
{
|
||||
if (!code)
|
||||
return nullptr;
|
||||
return space->allocate<ICCall_ScriptedFunCall>(code, firstMonitorStub, pcOffset);
|
||||
}
|
||||
|
||||
static size_t offsetOfPCOffset() {
|
||||
return offsetof(ICCall_ScriptedFunCall, pcOffset_);
|
||||
}
|
||||
|
||||
// Compiler for this stub kind.
|
||||
class Compiler : public ICCallStubCompiler {
|
||||
protected:
|
||||
ICStub *firstMonitorStub_;
|
||||
uint32_t pcOffset_;
|
||||
bool generateStubCode(MacroAssembler &masm);
|
||||
|
||||
virtual int32_t getKey() const {
|
||||
return static_cast<int32_t>(kind);
|
||||
}
|
||||
|
||||
public:
|
||||
Compiler(JSContext *cx, ICStub *firstMonitorStub, uint32_t pcOffset)
|
||||
: ICCallStubCompiler(cx, ICStub::Call_ScriptedFunCall),
|
||||
firstMonitorStub_(firstMonitorStub),
|
||||
pcOffset_(pcOffset)
|
||||
{ }
|
||||
|
||||
ICStub *getStub(ICStubSpace *space) {
|
||||
return ICCall_ScriptedFunCall::New(space, getStubCode(), firstMonitorStub_,
|
||||
pcOffset_);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Stub for performing a TableSwitch, updating the IC's return address to jump
|
||||
// to whatever point the switch is branching to.
|
||||
class ICTableSwitch : public ICStub
|
||||
|
|
Загрузка…
Ссылка в новой задаче