Bug 960523 - Add Baseline IC stub for fun.call(). r=djvj

This commit is contained in:
Jan de Mooij 2014-03-15 15:56:22 +01:00
Родитель c2f6e4f476
Коммит 6fb7125afd
3 изменённых файлов: 287 добавлений и 0 удалений

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

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