From 2457dac324ca39f36200ddcdcba59734de81c0d0 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Mon, 7 Jul 2014 13:11:11 -0700 Subject: [PATCH] Bug 1030985 - Optimize arguments.callee. (r=h4writer) --- js/src/jit/BaselineIC.cpp | 120 ++++++++++++++++++++++++++++--------- js/src/jit/BaselineIC.h | 36 +++++++++++ js/src/jit/IonAnalysis.cpp | 9 ++- js/src/jit/IonBuilder.cpp | 46 +++++++++++++- js/src/jit/IonBuilder.h | 2 + js/src/vm/Interpreter.cpp | 8 ++- 6 files changed, 188 insertions(+), 33 deletions(-) diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index dff103486117..4519daca8ad8 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -6071,6 +6071,36 @@ ICGetIntrinsic_Constant::Compiler::generateStubCode(MacroAssembler &masm) // GetProp_Fallback // +static bool +TryAttachMagicArgumentsGetPropStub(JSContext *cx, JSScript *script, ICGetProp_Fallback *stub, + HandlePropertyName name, HandleValue val, HandleValue res, + bool *attached) +{ + MOZ_ASSERT(!*attached); + + if (!val.isMagic(JS_OPTIMIZED_ARGUMENTS)) + return true; + + // Try handling arguments.callee on optimized arguments. + if (name == cx->names().callee) { + IonSpew(IonSpew_BaselineIC, " Generating GetProp(MagicArgs.callee) stub"); + + // Unlike ICGetProp_ArgumentsLength, only magic argument stubs are + // supported at the moment. + ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); + ICGetProp_ArgumentsCallee::Compiler compiler(cx, monitorStub); + ICStub *newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + + *attached = true; + stub->addNewStub(newStub); + return true; + } + + return true; +} + static bool TryAttachLengthStub(JSContext *cx, JSScript *script, ICGetProp_Fallback *stub, HandleValue val, HandleValue res, bool *attached) @@ -6426,39 +6456,23 @@ TryAttachNativeDoesNotExistStub(JSContext *cx, HandleScript script, jsbytecode * } static bool -DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_, - MutableHandleValue val, MutableHandleValue res) +ComputeGetPropResult(JSContext *cx, BaselineFrame *frame, JSOp op, HandlePropertyName name, + MutableHandleValue val, MutableHandleValue res) { - // This fallback stub may trigger debug mode toggling. - DebugModeOSRVolatileStub stub(frame, stub_); - - jsbytecode *pc = stub->icEntry()->pc(frame->script()); - JSOp op = JSOp(*pc); - FallbackICSpew(cx, stub, "GetProp(%s)", js_CodeName[op]); - - JS_ASSERT(op == JSOP_GETPROP || op == JSOP_CALLPROP || op == JSOP_LENGTH || op == JSOP_GETXPROP); - - RootedPropertyName name(cx, frame->script()->getName(pc)); - - if (op == JSOP_LENGTH && val.isMagic(JS_OPTIMIZED_ARGUMENTS)) { - // Handle arguments.length access. - if (IsOptimizedArguments(frame, val.address())) { + // Handle arguments.length and arguments.callee on optimized arguments, as + // it is not an object. + if (val.isMagic(JS_OPTIMIZED_ARGUMENTS) && IsOptimizedArguments(frame, val.address())) { + if (op == JSOP_LENGTH) { res.setInt32(frame->numActualArgs()); - - // Monitor result - types::TypeScript::Monitor(cx, frame->script(), pc, res); - if (!stub->addMonitorStubForValue(cx, frame->script(), res)) - return false; - - bool attached = false; - if (!TryAttachLengthStub(cx, frame->script(), stub, val, res, &attached)) - return false; - JS_ASSERT(attached); - - return true; + } else { + MOZ_ASSERT(name == cx->names().callee); + res.setObject(*frame->callee()); } + + return true; } + // Handle when val is an object. RootedObject obj(cx, ToObjectFromStack(cx, val)); if (!obj) return false; @@ -6475,6 +6489,26 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_ } #endif + return true; +} + +static bool +DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_, + MutableHandleValue val, MutableHandleValue res) +{ + // This fallback stub may trigger debug mode toggling. + DebugModeOSRVolatileStub stub(frame, stub_); + + jsbytecode *pc = stub->icEntry()->pc(frame->script()); + JSOp op = JSOp(*pc); + FallbackICSpew(cx, stub, "GetProp(%s)", js_CodeName[op]); + + JS_ASSERT(op == JSOP_GETPROP || op == JSOP_CALLPROP || op == JSOP_LENGTH || op == JSOP_GETXPROP); + + RootedPropertyName name(cx, frame->script()->getName(pc)); + if (!ComputeGetPropResult(cx, frame, op, name, val, res)) + return false; + types::TypeScript::Monitor(cx, frame->script(), pc, res); // Check if debug mode toggling made the stub invalid. @@ -6499,6 +6533,11 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_ return true; } + if (!TryAttachMagicArgumentsGetPropStub(cx, frame->script(), stub, name, val, res, &attached)) + return false; + if (attached) + return true; + RootedScript script(cx, frame->script()); if (!TryAttachNativeGetPropStub(cx, script, pc, stub, name, val, res, &attached)) @@ -7314,6 +7353,31 @@ ICGetProp_ArgumentsLength::Compiler::generateStubCode(MacroAssembler &masm) return true; } +bool +ICGetProp_ArgumentsCallee::Compiler::generateStubCode(MacroAssembler &masm) +{ + Label failure; + + // Ensure that this is lazy arguments. + masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure); + + // Ensure that frame has not loaded different arguments object since. + masm.branchTest32(Assembler::NonZero, + Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()), + Imm32(BaselineFrame::HAS_ARGS_OBJ), + &failure); + + Address callee(BaselineFrameReg, BaselineFrame::offsetOfCalleeToken()); + masm.loadPtr(callee, R0.scratchReg()); + masm.tagValue(JSVAL_TYPE_OBJECT, R0.scratchReg(), R0); + + EmitEnterTypeMonitorIC(masm); + + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + void BaselineScript::noteAccessedGetter(uint32_t pcOffset) { diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index 975a56987c33..5dc209e328fe 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -423,6 +423,7 @@ class ICEntry _(GetProp_CallDOMProxyWithGenerationNative)\ _(GetProp_DOMProxyShadowed) \ _(GetProp_ArgumentsLength) \ + _(GetProp_ArgumentsCallee) \ \ _(SetProp_Fallback) \ _(SetProp_Native) \ @@ -5077,6 +5078,41 @@ class ICGetProp_ArgumentsLength : public ICStub }; }; +class ICGetProp_ArgumentsCallee : public ICMonitoredStub +{ + friend class ICStubSpace; + protected: + explicit ICGetProp_ArgumentsCallee(JitCode *stubCode, ICStub *firstMonitorStub) + : ICMonitoredStub(ICStub::GetProp_ArgumentsCallee, stubCode, firstMonitorStub) + { } + + public: + static inline ICGetProp_ArgumentsCallee *New(ICStubSpace *space, JitCode *code, + ICStub *firstMonitorStub) + { + if (!code) + return nullptr; + return space->allocate(code, firstMonitorStub); + } + + class Compiler : public ICStubCompiler { + ICStub *firstMonitorStub_; + + protected: + bool generateStubCode(MacroAssembler &masm); + + public: + Compiler(JSContext *cx, ICStub *firstMonitorStub) + : ICStubCompiler(cx, ICStub::GetProp_ArgumentsCallee), + firstMonitorStub_(firstMonitorStub) + {} + + ICStub *getStub(ICStubSpace *space) { + return ICGetProp_ArgumentsCallee::New(space, getStubCode(), firstMonitorStub_); + } + }; +}; + // SetProp // JSOP_SETPROP // JSOP_SETNAME diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index e2e801a87072..5ccb64198d86 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -2419,9 +2419,14 @@ ArgumentsUseCanBeLazy(JSContext *cx, JSScript *script, MInstruction *ins, size_t if (ins->isGetArgumentsObjectArg() && index == 0) return true; - // arguments.length length can read fp->numActualArgs() directly. - if (ins->isCallGetProperty() && index == 0 && ins->toCallGetProperty()->name() == cx->names().length) + // arguments.length length can read fp->numActualArgs() directly and + // arguments.callee can read fp->callee() directly. + if (ins->isCallGetProperty() && index == 0 && + (ins->toCallGetProperty()->name() == cx->names().length || + ins->toCallGetProperty()->name() == cx->names().callee)) + { return true; + } return false; } diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index ab0514821f9e..a6ec23da5b5a 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -8598,6 +8598,10 @@ IonBuilder::jsop_getprop(PropertyName *name) if (!getPropTryArgumentsLength(&emitted, obj) || emitted) return emitted; + // Try to optimize arguments.callee. + if (!getPropTryArgumentsCallee(&emitted, obj, name) || emitted) + return emitted; + types::TemporaryTypeSet *types = bytecodeTypes(pc); BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj, name, types); @@ -8662,17 +8666,34 @@ IonBuilder::jsop_getprop(PropertyName *name) } bool -IonBuilder::getPropTryArgumentsLength(bool *emitted, MDefinition *obj) +IonBuilder::checkIsDefinitelyOptimizedArguments(MDefinition *obj, bool *isOptimizedArgs) { - JS_ASSERT(*emitted == false); if (obj->type() != MIRType_MagicOptimizedArguments) { if (script()->argumentsHasVarBinding() && obj->mightBeType(MIRType_MagicOptimizedArguments)) { return abort("Type is not definitely lazy arguments."); } + + *isOptimizedArgs = false; return true; } + + *isOptimizedArgs = true; + return true; +} + +bool +IonBuilder::getPropTryArgumentsLength(bool *emitted, MDefinition *obj) +{ + JS_ASSERT(*emitted == false); + + bool isOptimizedArgs = false; + if (!checkIsDefinitelyOptimizedArguments(obj, &isOptimizedArgs)) + return false; + if (!isOptimizedArgs) + return true; + if (JSOp(*pc) != JSOP_LENGTH) return true; @@ -8692,6 +8713,27 @@ IonBuilder::getPropTryArgumentsLength(bool *emitted, MDefinition *obj) return pushConstant(Int32Value(inlineCallInfo_->argv().length())); } +bool +IonBuilder::getPropTryArgumentsCallee(bool *emitted, MDefinition *obj, PropertyName *name) +{ + JS_ASSERT(*emitted == false); + + bool isOptimizedArgs = false; + if (!checkIsDefinitelyOptimizedArguments(obj, &isOptimizedArgs)) + return false; + if (!isOptimizedArgs) + return true; + + if (name != names().callee) + return true; + + obj->setImplicitlyUsedUnchecked(); + current->push(getCallee()); + + *emitted = true; + return true; +} + bool IonBuilder::getPropTryConstant(bool *emitted, MDefinition *obj, PropertyName *name, types::TemporaryTypeSet *types) diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 6dc191576cb6..27fe80c8a343 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -399,7 +399,9 @@ class IonBuilder : public MIRGenerator MDefinition *tryInnerizeWindow(MDefinition *obj); // jsop_getprop() helpers. + bool checkIsDefinitelyOptimizedArguments(MDefinition *obj, bool *isOptimizedArgs); bool getPropTryArgumentsLength(bool *emitted, MDefinition *obj); + bool getPropTryArgumentsCallee(bool *emitted, MDefinition *obj, PropertyName *name); bool getPropTryConstant(bool *emitted, MDefinition *obj, PropertyName *name, types::TemporaryTypeSet *types); bool getPropTryDefiniteSlot(bool *emitted, MDefinition *obj, PropertyName *name, diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index e067284943cb..ff1f4c443c90 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -221,8 +221,14 @@ GetPropertyOperation(JSContext *cx, InterpreterFrame *fp, HandleScript script, j return true; } - Rooted global(cx, &fp->global()); RootedId id(cx, NameToId(script->getName(pc))); + + if (id == NameToId(cx->names().callee) && IsOptimizedArguments(fp, lval.address())) { + vp.setObject(fp->callee()); + return true; + } + + Rooted global(cx, &fp->global()); RootedObject obj(cx); /* Optimize (.1).toString(). */