Bug 859609 - Inline functions that use the scope chain, and also inline call sites with monomorphic cloned lambdas. r=h4writer
authorKannan Vijayan <kvijayan@mozilla.com>
Tue, 11 Jun 2013 15:49:51 -0400
changeset 134684 1684c32be3286835c267d55eedce9a19788e019e
parent 134683 3a6cd8d533b758de31bb354b22dee94fc0d9dbb7
child 134685 bcf3b603a6622de219f754660308c5102eb56e86
push id29331
push userkvijayan@mozilla.com
push dateTue, 11 Jun 2013 19:50:37 +0000
treeherdermozilla-inbound@1684c32be328 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersh4writer
bugs859609
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 859609 - Inline functions that use the scope chain, and also inline call sites with monomorphic cloned lambdas. r=h4writer
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/MIR.h
js/src/jsanalyze.cpp
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -46,17 +46,18 @@ IonBuilder::IonBuilder(JSContext *cx, Te
     callerResumePoint_(NULL),
     callerBuilder_(NULL),
     inspector(inspector),
     inliningDepth_(inliningDepth),
     numLoopRestarts_(0),
     failedBoundsCheck_(info->script()->failedBoundsCheck),
     failedShapeGuard_(info->script()->failedShapeGuard),
     nonStringIteration_(false),
-    lazyArguments_(NULL)
+    lazyArguments_(NULL),
+    inlineCallInfo_(NULL)
 {
     script_.init(info->script());
     pc = info->startPC();
 }
 
 void
 IonBuilder::clearForBackEnd()
 {
@@ -155,44 +156,65 @@ IonBuilder::getSingleCallTarget(types::S
     if (!obj || !obj->isFunction())
         return NULL;
 
     return obj->toFunction();
 }
 
 bool
 IonBuilder::getPolyCallTargets(types::StackTypeSet *calleeTypes,
-                               AutoObjectVector &targets, uint32_t maxTargets)
+                               AutoObjectVector &targets,
+                               uint32_t maxTargets,
+                               bool *gotLambda)
 {
     JS_ASSERT(targets.length() == 0);
+    JS_ASSERT(gotLambda);
+    *gotLambda = false;
 
     if (!calleeTypes)
         return true;
 
     if (calleeTypes->baseFlags() != 0)
         return true;
 
     unsigned objCount = calleeTypes->getObjectCount();
 
     if (objCount == 0 || objCount > maxTargets)
         return true;
 
     if (!targets.reserve(objCount))
         return false;
     for(unsigned i = 0; i < objCount; i++) {
         JSObject *obj = calleeTypes->getSingleObject(i);
-        if (!obj || !obj->isFunction()) {
-            targets.clear();
-            return true;
+        if (obj) {
+            if (!obj->isFunction()) {
+                targets.clear();
+                return true;
+            }
+            if (obj->toFunction()->isInterpreted() && !obj->toFunction()->getOrCreateScript(cx))
+                return false;
+            DebugOnly<bool> appendOk = targets.append(obj);
+            JS_ASSERT(appendOk);
+        } else {
+            types::TypeObject *typeObj = calleeTypes->getTypeObject(i);
+            JS_ASSERT(typeObj);
+            if (!typeObj->isFunction() || !typeObj->interpretedFunction) {
+                targets.clear();
+                return true;
+            }
+            DebugOnly<bool> appendOk = targets.append(typeObj->interpretedFunction);
+            JS_ASSERT(appendOk);
+
+            *gotLambda = true;
         }
-        if (obj->toFunction()->isInterpreted() && !obj->toFunction()->getOrCreateScript(cx))
-            return false;
-        if (!targets.append(obj))
-            return false;
-    }
+    }
+
+    // For now, only inline "singleton" lambda calls
+    if (*gotLambda && targets.length() > 1)
+        targets.clear();
 
     return true;
 }
 
 bool
 IonBuilder::canEnterInlinedFunction(JSFunction *target)
 {
     RootedScript targetScript(cx, target->nonLazyScript());
@@ -204,28 +226,25 @@ IonBuilder::canEnterInlinedFunction(JSFu
         return false;
 
     if (targetScript->needsArgsObj())
         return false;
 
     if (!targetScript->compileAndGo)
         return false;
 
-    if (targetScript->analysis()->usesScopeChain())
-        return false;
-
     types::TypeObject *targetType = target->getType(cx);
     if (!targetType || targetType->unknownProperties())
         return false;
 
     return true;
 }
 
 bool
-IonBuilder::canInlineTarget(JSFunction *target, CallInfo &callInfo)
+IonBuilder::canInlineTarget(JSFunction *target)
 {
     if (!target->isInterpreted()) {
         IonSpew(IonSpew_Inlining, "Cannot inline due to non-interpreted");
         return false;
     }
 
     if (target->getParent() != &script()->global()) {
         IonSpew(IonSpew_Inlining, "Cannot inline due to scope mismatch");
@@ -567,16 +586,18 @@ IonBuilder::processIterators()
 
     return true;
 }
 
 bool
 IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint,
                         CallInfo &callInfo)
 {
+    inlineCallInfo_ = &callInfo;
+
     if (!script()->ensureHasBytecodeTypeMap(cx))
         return false;
 
     IonSpew(IonSpew_Scripts, "Inlining script %s:%d (%p)",
             script()->filename(), script()->lineno, (void *)script());
 
     if (!graph().addScript(script()))
         return false;
@@ -609,33 +630,31 @@ IonBuilder::buildInline(IonBuilder *call
         predecessor->add(MFunctionBoundary::New(script(),
                                                 MFunctionBoundary::Inline_Enter,
                                                 inliningDepth_));
 
     predecessor->end(MGoto::New(current));
     if (!current->addPredecessorWithoutPhis(predecessor))
         return false;
 
-    // Save the actual arguments the caller used to call this inlined call,
-    // to shortcut operations on "arguments" in the inlined call.
-    JS_ASSERT(inlinedArguments_.length() == 0);
-    if (!inlinedArguments_.append(callInfo.argv().begin(), callInfo.argv().end()))
-        return false;
-
-    // canEnterInlinedFunction vetoes scripts which use the scope chain or needs an
-    // arguments object.
-    JS_ASSERT(!script()->analysis()->usesScopeChain());
-    MInstruction *scope = MConstant::New(UndefinedValue());
-    current->add(scope);
-    current->initSlot(info().scopeChainSlot(), scope);
+    // Initialize scope chain slot to Undefined.  It's set later by |initScopeChain|.
+    {
+        MInstruction *scope = MConstant::New(UndefinedValue());
+        current->add(scope);
+        current->initSlot(info().scopeChainSlot(), scope);
+    }
+
+    // Initialize |arguments| slot.
     if (info().hasArguments()) {
         MInstruction *argsObj = MConstant::New(UndefinedValue());
         current->add(argsObj);
         current->initSlot(info().argsObjSlot(), argsObj);
     }
+
+    // Initialize |this| slot.
     current->initSlot(info().thisSlot(), callInfo.thisArg());
 
     IonSpew(IonSpew_Inlining, "Initializing %u arg slots", info().nargs());
 
     // NB: Ion does not inline functions which |needsArgsObj|.  So using argSlot()
     // instead of argSlotUnchecked() below is OK
     JS_ASSERT(!info().needsArgsObj());
 
@@ -648,16 +667,20 @@ IonBuilder::buildInline(IonBuilder *call
 
     // Pass Undefined for missing arguments
     for (size_t i = callInfo.argc(); i < info().nargs(); ++i) {
         MConstant *arg = MConstant::New(UndefinedValue());
         current->add(arg);
         current->initSlot(info().argSlot(i), arg);
     }
 
+    // Initialize the scope chain now that args are initialized.
+    if (!initScopeChain(callInfo.fun()))
+        return false;
+
     IonSpew(IonSpew_Inlining, "Initializing %u local slots", info().nlocals());
 
     // Initialize local variables.
     for (uint32_t i = 0; i < info().nlocals(); i++) {
         MConstant *undef = MConstant::New(UndefinedValue());
         current->add(undef);
         current->initSlot(info().localSlot(i), undef);
     }
@@ -760,17 +783,17 @@ IonBuilder::initParameters()
         current->add(param);
         current->initSlot(info().argSlotUnchecked(i), param);
     }
 
     return true;
 }
 
 bool
-IonBuilder::initScopeChain()
+IonBuilder::initScopeChain(MDefinition *callee)
 {
     MInstruction *scope = NULL;
 
     // If the script doesn't use the scopechain, then it's already initialized
     // from earlier.  However, always make a scope chain when |needsArgsObj| is true
     // for the script, since arguments object construction requires the scope chain
     // to be passed in.
     if (!info().needsArgsObj() && !script()->analysis()->usesScopeChain())
@@ -779,19 +802,21 @@ IonBuilder::initScopeChain()
     // The scope chain is only tracked in scripts that have NAME opcodes which
     // will try to access the scope. For other scripts, the scope instructions
     // will be held live by resume points and code will still be generated for
     // them, so just use a constant undefined value.
     if (!script()->compileAndGo)
         return abort("non-CNG global scripts are not supported");
 
     if (JSFunction *fun = info().fun()) {
-        MCallee *callee = MCallee::New();
-        current->add(callee);
-
+        if (!callee) {
+            MCallee *calleeIns = MCallee::New();
+            current->add(calleeIns);
+            callee = calleeIns;
+        }
         scope = MFunctionEnvironment::New(callee);
         current->add(scope);
 
         // This reproduce what is done in CallObject::createForFunction
         if (fun->isHeavyweight()) {
             if (fun->isNamedLambda()) {
                 scope = createDeclEnvObject(callee, scope);
                 if (!scope)
@@ -1387,22 +1412,24 @@ IonBuilder::inspectOpcode(JSOp op)
       case JSOP_NOT:
         return jsop_not();
 
       case JSOP_THIS:
         return jsop_this();
 
       case JSOP_CALLEE:
       {
-        MInstruction *callee;
-        if (inliningDepth_ == 0)
-            callee = MCallee::New();
-        else
-            callee = MConstant::New(ObjectValue(*info().fun()));
-        current->add(callee);
+        MDefinition *callee;
+        if (inliningDepth_ == 0) {
+            MInstruction *calleeIns = MCallee::New();
+            current->add(calleeIns);
+            callee = calleeIns;
+        } else {
+            callee = inlineCallInfo_->fun();
+        }
         current->push(callee);
         return true;
       }
 
       case JSOP_GETPROP:
       case JSOP_CALLPROP:
       {
         RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName());
@@ -3551,17 +3578,17 @@ IonBuilder::makeInliningDecision(JSFunct
     if (target == NULL)
         return false;
 
     // Native functions provide their own detection in inlineNativeCall().
     if (target->isNative())
         return true;
 
     // Determine whether inlining is possible at callee site
-    if (!canInlineTarget(target, callInfo))
+    if (!canInlineTarget(target))
         return false;
 
     // Heuristics!
     JSScript *targetScript = target->nonLazyScript();
 
     // Cap the inlining depth.
     if (IsSmallFunction(targetScript)) {
         if (inliningDepth_ >= js_IonOptions.smallFunctionMaxInlineDepth) {
@@ -3829,31 +3856,28 @@ IonBuilder::makePolyInlineDispatch(JSCon
     return MPolyInlineDispatch::New(targetObject, inlinePropTable,
                                     fallbackPrepBlock, fallbackBlock,
                                     fallbackEndBlock);
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineSingleCall(CallInfo &callInfo, JSFunction *target)
 {
-    // The inlined target must always be explicitly provided as a constant.
-    JS_ASSERT(callInfo.fun()->isConstant());
-
     // Expects formals to be popped and wrapped.
     if (target->isNative())
         return inlineNativeCall(callInfo, target->native());
 
     if (!inlineScriptedCall(callInfo, target))
         return InliningStatus_Error;
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineCallsite(AutoObjectVector &targets, AutoObjectVector &originals,
-                           CallInfo &callInfo)
+                           bool lambda, CallInfo &callInfo)
 {
     if (!inliningEnabled())
         return InliningStatus_NotInlined;
 
     if (targets.length() == 0)
         return InliningStatus_NotInlined;
 
     // Is the function provided by an MGetPropertyCache?
@@ -3863,21 +3887,30 @@ IonBuilder::inlineCallsite(AutoObjectVec
 
     // Inline single targets -- unless they derive from a cache, in which case
     // avoiding the cache and guarding is still faster.
     if (!propCache && targets.length() == 1) {
         JSFunction *target = targets[0]->toFunction();
         if (!makeInliningDecision(target, callInfo))
             return InliningStatus_NotInlined;
 
-        // Replace the function with an MConstant.
+        // Inlining will elminate uses of the original callee, but it needs to
+        // be preserved in phis if we bail out.  Mark the old callee definition as
+        // folded to ensure this happens.
         callInfo.fun()->setFoldedUnchecked();
-        MConstant *constFun = MConstant::New(ObjectValue(*target));
-        current->add(constFun);
-        callInfo.setFun(constFun);
+
+        // 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) {
+            // Replace the function with an MConstant.
+            MConstant *constFun = MConstant::New(ObjectValue(*target));
+            current->add(constFun);
+            callInfo.setFun(constFun);
+        }
 
         return inlineSingleCall(callInfo, target);
     }
 
     // Choose a subset of the targets for polymorphic inlining.
     Vector<bool> choiceSet(cx);
     uint32_t numInlined = selectInliningTargets(targets, callInfo, choiceSet);
     if (numInlined == 0)
@@ -4623,17 +4656,17 @@ IonBuilder::jsop_funapplyarguments(uint3
 
     // Vp
     MPassArg *passVp = current->pop()->toPassArg();
     passVp->replaceAllUsesWith(passVp->getArgument());
     passVp->block()->discard(passVp);
 
     // Arguments
     Vector<MDefinition *> args(cx);
-    if (!args.append(inlinedArguments_.begin(), inlinedArguments_.end()))
+    if (!args.append(inlineCallInfo_->argv().begin(), inlineCallInfo_->argv().end()))
         return false;
     callInfo.setArgs(&args);
 
     // This
     MPassArg *passThis = current->pop()->toPassArg();
     MDefinition *argThis = passThis->getArgument();
     passThis->replaceAllUsesWith(argThis);
     passThis->block()->discard(passThis);
@@ -4674,21 +4707,23 @@ IonBuilder::jsop_call(uint32_t argc, boo
             observed->addType(cx, types::Type::DoubleType());
         }
     }
 
     int calleeDepth = -((int)argc + 2);
 
     // Acquire known call target if existent.
     AutoObjectVector originals(cx);
+    bool gotLambda;
     types::StackTypeSet *calleeTypes = current->peek(calleeDepth)->resultTypeSet();
     if (calleeTypes) {
-        if (!getPolyCallTargets(calleeTypes, originals, 4))
+        if (!getPolyCallTargets(calleeTypes, originals, 4, &gotLambda))
             return false;
     }
+    JS_ASSERT_IF(gotLambda, originals.length() <= 1);
 
     // If any call targets need to be cloned, clone them. Keep track of the
     // originals as we need to case on them for poly inline.
     bool hasClones = false;
     AutoObjectVector targets(cx);
     RootedFunction fun(cx);
     RootedScript scriptRoot(cx, script());
     for (uint32_t i = 0; i < originals.length(); i++) {
@@ -4702,18 +4737,21 @@ IonBuilder::jsop_call(uint32_t argc, boo
         if (!targets.append(fun))
             return false;
     }
 
     CallInfo callInfo(cx, constructing);
     if (!callInfo.init(current, argc))
         return false;
 
+    if (gotLambda && targets.length() > 0)
+        callInfo.setLambda(true);
+
     // Try inlining
-    InliningStatus status = inlineCallsite(targets, originals, callInfo);
+    InliningStatus status = inlineCallsite(targets, originals, gotLambda, callInfo);
     if (status == InliningStatus_Inlined)
         return true;
     if (status == InliningStatus_Error)
         return false;
 
     // No inline, just make the call.
     RootedFunction target(cx, NULL);
     if (targets.length() == 1)
@@ -6942,17 +6980,17 @@ IonBuilder::jsop_arguments_length()
     if (inliningDepth_ == 0) {
         MInstruction *ins = MArgumentsLength::New();
         current->add(ins);
         current->push(ins);
         return true;
     }
 
     // We are inlining and know the number of arguments the callee pushed
-    return pushConstant(Int32Value(inlinedArguments_.length()));
+    return pushConstant(Int32Value(inlineCallInfo_->argv().length()));
 }
 
 bool
 IonBuilder::jsop_arguments_getelem()
 {
     if (inliningDepth_ != 0)
         return abort("NYI inlined get argument element");
 
@@ -7005,17 +7043,17 @@ IonBuilder::jsop_rest()
         // including the rest parameter slot itself), and the template object.
         MRest *rest = MRest::New(numActuals, info().nargs() - 1, templateObject);
         current->add(rest);
         current->push(rest);
         return true;
     }
 
     // We know the exact number of arguments the callee pushed.
-    unsigned numActuals = inlinedArguments_.length();
+    unsigned numActuals = inlineCallInfo_->argv().length();
     unsigned numFormals = info().nargs() - 1;
     unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
     JSObject *templateObject = getNewArrayTemplateObject(numRest);
 
     MNewArray *array = new MNewArray(numRest, templateObject, MNewArray::NewArray_Allocating);
     current->add(array);
 
     if (numActuals <= numFormals) {
@@ -7038,17 +7076,17 @@ IonBuilder::jsop_rest()
     // Unroll the argument copy loop. We don't need to do any bounds or hole
     // checking here.
     MConstant *index;
     for (unsigned i = numFormals; i < numActuals; i++) {
         index = MConstant::New(Int32Value(i - numFormals));
         current->add(index);
 
         MInstruction *store;
-        MDefinition *arg = inlinedArguments_[i];
+        MDefinition *arg = inlineCallInfo_->argv()[i];
         if (elemTypes && !TypeSetIncludes(elemTypes, arg->type(), arg->resultTypeSet())) {
             elemTypes->addFreeze(cx);
             store = MCallSetElement::New(array, index, arg);
             if (!setElemCalls.append(store))
                 return false;
         } else {
             store = MStoreElement::New(elements, index, arg, /* needsHoleCheck = */ false);
         }
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -214,18 +214,20 @@ class IonBuilder : public MIRGenerator
     void spew(const char *message);
 
     static bool inliningEnabled() {
         return js_IonOptions.inlining;
     }
 
     JSFunction *getSingleCallTarget(types::StackTypeSet *calleeTypes);
     bool getPolyCallTargets(types::StackTypeSet *calleeTypes,
-                            AutoObjectVector &targets, uint32_t maxTargets);
-    bool canInlineTarget(JSFunction *target, CallInfo &callInfo);
+                            AutoObjectVector &targets,
+                            uint32_t maxTargets,
+                            bool *gotLambda);
+    bool canInlineTarget(JSFunction *target);
 
     void popCfgStack();
     DeferredEdge *filterDeadDeferredEdges(DeferredEdge *edge);
     bool processDeferredContinues(CFGState &state);
     ControlStatus processControlEnd();
     ControlStatus processCfgStack();
     ControlStatus processCfgEntry(CFGState &state);
     ControlStatus processIfEnd(CFGState &state);
@@ -306,17 +308,17 @@ class IonBuilder : public MIRGenerator
     bool resume(MInstruction *ins, jsbytecode *pc, MResumePoint::Mode mode);
     bool resumeAt(MInstruction *ins, jsbytecode *pc);
     bool resumeAfter(MInstruction *ins);
     bool maybeInsertResume();
 
     bool initParameters();
     void rewriteParameter(uint32_t slotIdx, MDefinition *param, int32_t argIndex);
     void rewriteParameters();
-    bool initScopeChain();
+    bool initScopeChain(MDefinition *callee = NULL);
     bool initArgumentsObject();
     bool pushConstant(const Value &v);
 
     // Add a guard which ensure that the set of type which goes through this
     // generated code correspond to the observed types for the bytecode.
     bool pushTypeBarrier(MInstruction *ins, types::StackTypeSet *observed, bool needBarrier);
 
     JSObject *getSingletonPrototype(JSFunction *target);
@@ -503,17 +505,17 @@ class IonBuilder : public MIRGenerator
 
     // Main inlining functions
     InliningStatus inlineNativeCall(CallInfo &callInfo, JSNative native);
     bool inlineScriptedCall(CallInfo &callInfo, JSFunction *target);
     InliningStatus inlineSingleCall(CallInfo &callInfo, JSFunction *target);
 
     // Call functions
     InliningStatus inlineCallsite(AutoObjectVector &targets, AutoObjectVector &originals,
-                                  CallInfo &callInfo);
+                                  bool lambda, CallInfo &callInfo);
     bool inlineCalls(CallInfo &callInfo, AutoObjectVector &targets, AutoObjectVector &originals,
                      Vector<bool> &choiceSet, MGetPropertyCache *maybeCache);
 
     // Inlining helpers.
     bool inlineGenericFallback(JSFunction *target, CallInfo &callInfo, MBasicBlock *dispatchBlock,
                                bool clonedAtCallsite);
     bool inlineTypeObjectFallback(CallInfo &callInfo, MBasicBlock *dispatchBlock,
                                   MTypeObjectDispatch *dispatch, MGetPropertyCache *cache,
@@ -599,17 +601,16 @@ class IonBuilder : public MIRGenerator
     Vector<CFGState, 8, IonAllocPolicy> cfgStack_;
     Vector<ControlFlowInfo, 4, IonAllocPolicy> loops_;
     Vector<ControlFlowInfo, 0, IonAllocPolicy> switches_;
     Vector<ControlFlowInfo, 2, IonAllocPolicy> labels_;
     Vector<MInstruction *, 2, IonAllocPolicy> iterators_;
     BaselineInspector *inspector;
 
     size_t inliningDepth_;
-    Vector<MDefinition *, 0, IonAllocPolicy> inlinedArguments_;
 
     // Cutoff to disable compilation if excessive time is spent reanalyzing
     // loop bodies to compute a fixpoint of the types for loop variables.
     static const size_t MAX_LOOP_RESTARTS = 20;
     size_t numLoopRestarts_;
 
     // True if script->failedBoundsCheck is set for the current script or
     // an outer script.
@@ -620,25 +621,29 @@ class IonBuilder : public MIRGenerator
     bool failedShapeGuard_;
 
     // Has an iterator other than 'for in'.
     bool nonStringIteration_;
 
     // If this script can use a lazy arguments object, it will be pre-created
     // here.
     MInstruction *lazyArguments_;
+
+    // If this is an inline builder, the call info for the builder.
+    const CallInfo *inlineCallInfo_;
 };
 
 class CallInfo
 {
     MDefinition *fun_;
     MDefinition *thisArg_;
     Vector<MDefinition *> args_;
 
     bool constructing_;
+    bool lambda_;
 
   public:
     CallInfo(JSContext *cx, bool constructing)
       : fun_(NULL),
         thisArg_(NULL),
         args_(cx),
         constructing_(constructing)
     { }
@@ -695,16 +700,20 @@ class CallInfo
         JS_ASSERT(args_.length() == 0);
         args_.append(args->begin(), args->end());
     }
 
     Vector<MDefinition *> &argv() {
         return args_;
     }
 
+    const Vector<MDefinition *> &argv() const {
+        return args_;
+    }
+
     MDefinition *getArg(uint32_t i) {
         JS_ASSERT(i < argc());
         return args_[i];
     }
 
     void setArg(uint32_t i, MDefinition *def) {
         JS_ASSERT(i < argc());
         args_[i] = def;
@@ -718,16 +727,23 @@ class CallInfo
     void setThis(MDefinition *thisArg) {
         thisArg_ = thisArg;
     }
 
     bool constructing() {
         return constructing_;
     }
 
+    bool isLambda() const {
+        return lambda_;
+    }
+    void setLambda(bool lambda) {
+        lambda_ = lambda;
+    }
+
     void wrapArgs(MBasicBlock *current) {
         thisArg_ = wrap(current, thisArg_);
         for (uint32_t i = 0; i < argc(); i++)
             args_[i] = wrap(current, args_[i]);
     }
 
     void unwrapArgs() {
         thisArg_ = unwrap(thisArg_);
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -6175,16 +6175,20 @@ class MFunctionEnvironment
 
     static MFunctionEnvironment *New(MDefinition *function) {
         return new MFunctionEnvironment(function);
     }
 
     MDefinition *function() const {
         return getOperand(0);
     }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
 };
 
 // Loads the current js::ForkJoinSlice*.
 // Only applicable in ParallelExecution.
 class MParSlice
   : public MNullaryInstruction
 {
   public:
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -275,17 +275,16 @@ ScriptAnalysis::analyzeBytecode(JSContex
           case JSOP_BINDNAME:
           case JSOP_SETNAME:
           case JSOP_DELNAME:
           case JSOP_GETALIASEDVAR:
           case JSOP_CALLALIASEDVAR:
           case JSOP_SETALIASEDVAR:
           case JSOP_LAMBDA:
             usesScopeChain_ = true;
-            isIonInlineable = false;
             break;
 
           case JSOP_DEFFUN:
           case JSOP_DEFVAR:
           case JSOP_DEFCONST:
           case JSOP_SETCONST:
             usesScopeChain_ = true; // Requires access to VarObj via ScopeChain.
             canTrackVars = false;