diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 61ddde8f3d08..65f5a6ed1e44 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -722,9 +722,14 @@ CodeGenerator::visitFunctionDispatch(LFunctionDispatch *lir) // Compare function pointers, except for the last case. for (size_t i = 0; i < casesWithFallback - 1; i++) { MOZ_ASSERT(i < mir->numCases()); - JSFunction *func = mir->getCase(i); LBlock *target = skipTrivialBlocks(mir->getCaseBlock(i))->lir(); - masm.branchPtr(Assembler::Equal, input, ImmGCPtr(func), target->label()); + if (types::TypeObject *funcType = mir->getCaseTypeObject(i)) { + masm.branchPtr(Assembler::Equal, Address(input, JSObject::offsetOfType()), + ImmGCPtr(funcType), target->label()); + } else { + JSFunction *func = mir->getCase(i); + masm.branchPtr(Assembler::Equal, input, ImmGCPtr(func), target->label()); + } } // Jump to the last case. diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index bddb2aa449c9..e6e6adbd3eb8 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -271,11 +271,9 @@ IonBuilder::getSingleCallTarget(types::TemporaryTypeSet *calleeTypes) bool IonBuilder::getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constructing, - ObjectVector &targets, uint32_t maxTargets, bool *gotLambda) + ObjectVector &targets, uint32_t maxTargets) { MOZ_ASSERT(targets.empty()); - MOZ_ASSERT(gotLambda); - *gotLambda = false; if (!calleeTypes) return true; @@ -290,9 +288,11 @@ IonBuilder::getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constr if (!targets.reserve(objCount)) return false; - for(unsigned i = 0; i < objCount; i++) { + for (unsigned i = 0; i < objCount; i++) { JSObject *obj = calleeTypes->getSingleObject(i); - if (!obj) { + if (obj) { + MOZ_ASSERT(obj->hasSingletonType()); + } else { types::TypeObject *typeObj = calleeTypes->getTypeObject(i); if (!typeObj) continue; @@ -303,7 +303,7 @@ IonBuilder::getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constr return true; } - *gotLambda = true; + MOZ_ASSERT(!obj->hasSingletonType()); } // Don't optimize if the callee is not callable or constructable per @@ -317,10 +317,6 @@ IonBuilder::getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constr targets.infallibleAppend(obj); } - // For now, only inline "singleton" lambda calls - if (*gotLambda && targets.length() > 1) - targets.clear(); - return true; } @@ -4833,7 +4829,7 @@ IonBuilder::inlineSingleCall(CallInfo &callInfo, JSObject *targetArg) IonBuilder::InliningStatus IonBuilder::inlineCallsite(const ObjectVector &targets, ObjectVector &originals, - bool lambda, CallInfo &callInfo) + CallInfo &callInfo) { if (targets.empty()) return InliningStatus_NotInlined; @@ -4868,7 +4864,7 @@ IonBuilder::inlineCallsite(const ObjectVector &targets, ObjectVector &originals, // If the callee is not going to be a lambda (which may vary across // different invocations), then the callee definition can be replaced by a // constant. - if (!lambda) { + if (target->hasSingletonType()) { // Replace the function with an MConstant. MConstant *constFun = constant(ObjectValue(*target)); callInfo.setFun(constFun); @@ -5110,12 +5106,19 @@ IonBuilder::inlineCalls(CallInfo &callInfo, const ObjectVector &targets, if (!inlineBlock) return false; - // Create a function MConstant to use in the entry ResumePoint. - MConstant *funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints()); + // Create a function MConstant to use in the entry ResumePoint. If we + // can't use a constant, add a no-op MPolyInlineGuard, to prevent + // hoisting scope chain gets above the dispatch instruction. + MInstruction *funcDef; + if (target->hasSingletonType()) + funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints()); + else + funcDef = MPolyInlineGuard::New(alloc(), callInfo.fun()); + funcDef->setImplicitlyUsedUnchecked(); dispatchBlock->add(funcDef); - // Use the MConstant in the inline resume point and on stack. + // Use the inlined callee in the inline resume point and on stack. int funIndex = inlineBlock->entryResumePoint()->stackDepth() - callInfo.numFormals(); inlineBlock->entryResumePoint()->replaceOperand(funIndex, funcDef); inlineBlock->rewriteSlot(funIndex, funcDef); @@ -5160,7 +5163,8 @@ IonBuilder::inlineCalls(CallInfo &callInfo, const ObjectVector &targets, // // Note that guarding is on the original function pointer even // if there is a clone, since cloning occurs at the callsite. - dispatch->addCase(original, inlineBlock); + types::TypeObject *funType = original->hasSingletonType() ? nullptr : original->type(); + dispatch->addCase(original, funType, inlineBlock); MDefinition *retVal = inlineReturnBlock->peek(-1); retPhi->addInput(retVal); @@ -5211,8 +5215,11 @@ IonBuilder::inlineCalls(CallInfo &callInfo, const ObjectVector &targets, MOZ_ASSERT(!remaining); if (targets[i]->is()) { - remaining = &targets[i]->as(); - clonedAtCallsite = targets[i] != originals[i]; + JSFunction *target = &targets[i]->as(); + if (target->hasSingletonType()) { + remaining = target; + clonedAtCallsite = target != originals[i]; + } } break; } @@ -5641,13 +5648,9 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing) // Acquire known call target if existent. ObjectVector originals(alloc()); - bool gotLambda = false; types::TemporaryTypeSet *calleeTypes = current->peek(calleeDepth)->resultTypeSet(); - if (calleeTypes) { - if (!getPolyCallTargets(calleeTypes, constructing, originals, 4, &gotLambda)) - return false; - } - MOZ_ASSERT_IF(gotLambda, originals.length() <= 1); + if (calleeTypes && !getPolyCallTargets(calleeTypes, constructing, originals, 4)) + return false; // If any call targets need to be cloned, look for existing clones to use. // Keep track of the originals as we need to case on them for poly inline. @@ -5676,7 +5679,7 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing) return false; // Try inlining - InliningStatus status = inlineCallsite(targets, originals, gotLambda, callInfo); + InliningStatus status = inlineCallsite(targets, originals, callInfo); if (status == InliningStatus_Inlined) return true; if (status == InliningStatus_Error) diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index ef4cfec8c123..175aed9485e1 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -238,7 +238,7 @@ class IonBuilder JSFunction *getSingleCallTarget(types::TemporaryTypeSet *calleeTypes); bool getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constructing, - ObjectVector &targets, uint32_t maxTargets, bool *gotLambda); + ObjectVector &targets, uint32_t maxTargets); void popCfgStack(); DeferredEdge *filterDeadDeferredEdges(DeferredEdge *edge); @@ -817,7 +817,7 @@ class IonBuilder // Call functions InliningStatus inlineCallsite(const ObjectVector &targets, ObjectVector &originals, - bool lambda, CallInfo &callInfo); + CallInfo &callInfo); bool inlineCalls(CallInfo &callInfo, const ObjectVector &targets, ObjectVector &originals, BoolVector &choiceSet, MGetPropertyCache *maybeCache); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 3cb751eb247e..d647f4d75cbb 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3159,6 +3159,13 @@ LIRGenerator::visitGuardString(MGuardString *ins) redefine(ins, ins->input()); } +void +LIRGenerator::visitPolyInlineGuard(MPolyInlineGuard *ins) +{ + MOZ_ASSERT(ins->input()->type() == MIRType_Object); + redefine(ins, ins->input()); +} + void LIRGenerator::visitGuardShapePolymorphic(MGuardShapePolymorphic *ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index d53fd2a2dabc..69f8ed575328 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -220,6 +220,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitGuardObject(MGuardObject *ins); void visitGuardString(MGuardString *ins); void visitGuardShapePolymorphic(MGuardShapePolymorphic *ins); + void visitPolyInlineGuard(MPolyInlineGuard *ins); void visitAssertRange(MAssertRange *ins); void visitCallGetProperty(MCallGetProperty *ins); void visitDeleteProperty(MDeleteProperty *ins); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index f981d1306cfe..1927df043ba7 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -4120,6 +4120,28 @@ class MGuardString } }; +class MPolyInlineGuard + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + explicit MPolyInlineGuard(MDefinition *ins) + : MUnaryInstruction(ins) + { + setGuard(); + setResultType(MIRType_Object); + } + + public: + INSTRUCTION_HEADER(PolyInlineGuard) + + static MPolyInlineGuard *New(TempAllocator &alloc, MDefinition *ins) { + return new(alloc) MPolyInlineGuard(ins); + } + AliasSet getAliasSet() const MOZ_OVERRIDE { + return AliasSet::None(); + } +}; + class MAssertRange : public MUnaryInstruction, public NoTypePolicy::Data @@ -9384,10 +9406,14 @@ class MDispatchInstruction // Map from JSFunction* -> MBasicBlock. struct Entry { JSFunction *func; + // If |func| has a singleton type, |funcType| is null. Otherwise, + // |funcType| holds the TypeObject for |func|, and dispatch guards + // on the type instead of directly on the function. + types::TypeObject *funcType; MBasicBlock *block; - Entry(JSFunction *func, MBasicBlock *block) - : func(func), block(block) + Entry(JSFunction *func, types::TypeObject *funcType, MBasicBlock *block) + : func(func), funcType(funcType), block(block) { } }; Vector map_; @@ -9455,8 +9481,8 @@ class MDispatchInstruction } public: - void addCase(JSFunction *func, MBasicBlock *block) { - map_.append(Entry(func, block)); + void addCase(JSFunction *func, types::TypeObject *funcType, MBasicBlock *block) { + map_.append(Entry(func, funcType, block)); } uint32_t numCases() const { return map_.length(); @@ -9464,6 +9490,9 @@ class MDispatchInstruction JSFunction *getCase(uint32_t i) const { return map_[i].func; } + types::TypeObject *getCaseTypeObject(uint32_t i) const { + return map_[i].funcType; + } MBasicBlock *getCaseBlock(uint32_t i) const { return map_[i].block; } diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 3cb83462d86d..3b8c18d278c6 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -102,6 +102,7 @@ namespace jit { _(Unbox) \ _(GuardObject) \ _(GuardString) \ + _(PolyInlineGuard) \ _(AssertRange) \ _(ToDouble) \ _(ToFloat32) \