Bug 952992 - Remove MPrepareCall and MPassArg instructions. r=nbp
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 27 Dec 2013 20:49:33 +0100
changeset 161818 1ed3e04b050b17fac55c0a951917beae6cbd28ba
parent 161817 07f8896db19cbc95ac470406e0b810f8e68eed8b
child 161819 a3afdb32b40e60ebc144cc035ce8c9c94df2f6a5
push id37994
push userjandemooij@gmail.com
push dateFri, 27 Dec 2013 19:51:47 +0000
treeherdermozilla-inbound@1ed3e04b050b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs952992
milestone29.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 952992 - Remove MPrepareCall and MPassArg instructions. r=nbp
js/src/jit/CodeGenerator.cpp
js/src/jit/IonAnalysis.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/LIR-Common.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/jit/RangeAnalysis.cpp
js/src/jit/TypePolicy.cpp
js/src/jit/UnreachableCodeElimination.cpp
js/src/jit/shared/CodeGenerator-shared.cpp
js/src/jit/shared/Lowering-shared.cpp
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1136,17 +1136,17 @@ CodeGenerator::visitOsrReturnValue(LOsrR
 
     return true;
 }
 
 bool
 CodeGenerator::visitStackArgT(LStackArgT *lir)
 {
     const LAllocation *arg = lir->getArgument();
-    MIRType argType = lir->mir()->getArgument()->type();
+    MIRType argType = lir->type();
     uint32_t argslot = lir->argslot();
     JS_ASSERT(argslot - 1u < graph.argumentSlotCount());
 
     int32_t stack_offset = StackOffsetOfPassedArg(argslot);
     Address dest(StackPointer, stack_offset);
 
     if (arg->isFloatReg())
         masm.storeDouble(ToFloatRegister(arg), dest);
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -104,17 +104,16 @@ jit::EliminateDeadResumePointOperands(MI
             // current block, and keep track of its last use in a definition
             // (not resume point). This requires the instructions in the block
             // to be numbered, ensured by running this immediately after alias
             // analysis.
             uint32_t maxDefinition = 0;
             for (MUseDefIterator uses(*ins); uses; uses++) {
                 if (uses.def()->block() != *block ||
                     uses.def()->isBox() ||
-                    uses.def()->isPassArg() ||
                     uses.def()->isPhi())
                 {
                     maxDefinition = UINT32_MAX;
                     break;
                 }
                 maxDefinition = Max(maxDefinition, uses.def()->id());
             }
             if (maxDefinition == UINT32_MAX)
@@ -968,18 +967,16 @@ TypeAnalyzer::checkFloatCoherency()
     // operations
     for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); ++block) {
         if (mir->shouldCancel("Check Float32 coherency"))
             return false;
 
         for (MDefinitionIterator def(*block); def; def++) {
             if (def->type() != MIRType_Float32)
                 continue;
-            if (def->isPassArg()) // no check for PassArg as it is broken, see bug 915479
-                continue;
 
             for (MUseDefIterator use(*def); use; use++) {
                 MDefinition *consumer = use.def();
                 JS_ASSERT(consumer->isConsistentFloat32Use());
             }
         }
     }
 #endif
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1295,20 +1295,16 @@ IonBuilder::traverseBytecode()
         if (++lockOpcodeCount == LOCK_OPCODE_GRANULARITY) {
             unlock();
             lock();
             lockOpcodeCount = 0;
         }
 
 #ifdef DEBUG
         for (size_t i = 0; i < popped.length(); i++) {
-            // Call instructions can discard PassArg instructions. Ignore them.
-            if (popped[i]->isPassArg() && !popped[i]->hasUses())
-                continue;
-
             switch (op) {
               case JSOP_POP:
               case JSOP_POPN:
               case JSOP_POPNV:
               case JSOP_DUP:
               case JSOP_DUP2:
               case JSOP_PICK:
               case JSOP_SWAP:
@@ -3815,23 +3811,17 @@ IonBuilder::jsop_neg()
     if (!jsop_binary(JSOP_MUL, negator, right))
         return false;
     return true;
 }
 
 bool
 IonBuilder::jsop_notearg()
 {
-    // JSOP_NOTEARG notes that the value in current->pop() has just
-    // been pushed onto the stack for use in calling a function.
-    MDefinition *def = current->pop();
-    MPassArg *arg = MPassArg::New(alloc(), def);
-
-    current->add(arg);
-    current->push(arg);
+    // :TODO: Remove JSOP_NOTEARG (bug 953284).
     return true;
 }
 
 class AutoAccumulateReturns
 {
     MIRGraph &graph_;
     MIRGraphReturns *prev_;
 
@@ -3848,19 +3838,17 @@ class AutoAccumulateReturns
 };
 
 bool
 IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target)
 {
     JS_ASSERT(target->isInterpreted());
     JS_ASSERT(IsIonInlinablePC(pc));
 
-    // Remove any MPassArgs.
-    if (callInfo.isWrapped())
-        callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     // Ensure sufficient space in the slots: needed for inlining from FUNAPPLY.
     uint32_t depth = current->stackDepth() + callInfo.numFormals();
     if (depth > current->nslots()) {
         if (!current->increaseSlots(depth - current->nslots()))
             return false;
     }
 
@@ -4159,20 +4147,16 @@ IonBuilder::getInlineableGetPropertyCach
 {
     if (callInfo.constructing())
         return nullptr;
 
     MDefinition *thisDef = callInfo.thisArg();
     if (thisDef->type() != MIRType_Object)
         return nullptr;
 
-    // Unwrap thisDef for pointer comparison purposes.
-    if (thisDef->isPassArg())
-        thisDef = thisDef->toPassArg()->getArgument();
-
     MDefinition *funcDef = callInfo.fun();
     if (funcDef->type() != MIRType_Object)
         return nullptr;
 
     // MGetPropertyCache with no uses may be optimized away.
     if (funcDef->isGetPropertyCache()) {
         MGetPropertyCache *cache = funcDef->toGetPropertyCache();
         if (cache->hasUses())
@@ -4283,17 +4267,16 @@ IonBuilder::inlineGenericFallback(JSFunc
     if (!fallbackBlock)
         return false;
 
     // Create a new CallInfo to track modified state within this block.
     CallInfo fallbackInfo(alloc(), callInfo.constructing());
     if (!fallbackInfo.init(callInfo))
         return false;
     fallbackInfo.popFormals(fallbackBlock);
-    fallbackInfo.wrapArgs(alloc(), fallbackBlock);
 
     // Generate an MCall, which uses stateful |current|.
     setCurrentAndSpecializePhis(fallbackBlock);
     if (!makeCall(target, fallbackInfo, clonedAtCallsite))
         return false;
 
     // Pass return block to caller as |current|.
     return true;
@@ -4402,20 +4385,17 @@ IonBuilder::inlineCalls(CallInfo &callIn
 {
     // Only handle polymorphic inlining.
     JS_ASSERT(IsIonInlinablePC(pc));
     JS_ASSERT(choiceSet.length() == targets.length());
     JS_ASSERT_IF(!maybeCache, targets.length() >= 2);
     JS_ASSERT_IF(maybeCache, targets.length() >= 1);
 
     MBasicBlock *dispatchBlock = current;
-
-    // Unwrap the arguments.
-    JS_ASSERT(callInfo.isWrapped());
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
     callInfo.pushFormals(dispatchBlock);
 
     // Patch any InlinePropertyTable to only contain functions that are inlineable.
     //
     // Note that we trim using originals, as callsite clones are not user
     // visible. We don't patch the entries inside the table with the cloned
     // targets, as the entries should only be used for comparison.
     //
@@ -4504,17 +4484,16 @@ IonBuilder::inlineCalls(CallInfo &callIn
         inlineBlock->rewriteSlot(funIndex, funcDef);
 
         // Create a new CallInfo to track modified state within the inline block.
         CallInfo inlineInfo(alloc(), callInfo.constructing());
         if (!inlineInfo.init(callInfo))
             return false;
         inlineInfo.popFormals(inlineBlock);
         inlineInfo.setFun(funcDef);
-        inlineInfo.wrapArgs(alloc(), inlineBlock);
 
         if (maybeCache) {
             JS_ASSERT(callInfo.thisArg() == maybeCache->object());
             types::TemporaryTypeSet *targetThisTypes =
                 maybeCache->propTable()->buildTypeSetForFunction(original);
             if (!targetThisTypes)
                 return false;
             maybeCache->object()->setResultTypeSet(targetThisTypes);
@@ -4819,20 +4798,20 @@ IonBuilder::createThis(JSFunction *targe
 
     return createThisScripted(callee);
 }
 
 bool
 IonBuilder::jsop_funcall(uint32_t argc)
 {
     // Stack for JSOP_FUNCALL:
-    // 1:      MPassArg(arg0)
+    // 1:      arg0
     // ...
-    // argc:   MPassArg(argN)
-    // argc+1: MPassArg(JSFunction *), the 'f' in |f.call()|, in |this| position.
+    // argc:   argN
+    // argc+1: JSFunction*, the 'f' in |f.call()|, in |this| position.
     // argc+2: The native 'call' function.
 
     int calleeDepth = -((int)argc + 2);
     int funcDepth = -((int)argc + 1);
 
     // If |Function.prototype.call| may be overridden, don't optimize callsite.
     types::TemporaryTypeSet *calleeTypes = current->peek(calleeDepth)->resultTypeSet();
     JSFunction *native = getSingleCallTarget(calleeTypes);
@@ -4843,37 +4822,25 @@ IonBuilder::jsop_funcall(uint32_t argc)
         return makeCall(native, callInfo, false);
     }
     current->peek(calleeDepth)->setFoldedUnchecked();
 
     // Extract call target.
     types::TemporaryTypeSet *funTypes = current->peek(funcDepth)->resultTypeSet();
     JSFunction *target = getSingleCallTarget(funTypes);
 
-    // Unwrap the (JSFunction *) parameter.
-    MPassArg *passFunc = current->peek(funcDepth)->toPassArg();
-    current->rewriteAtDepth(funcDepth, passFunc->getArgument());
-
-    // Remove the MPassArg(JSFunction *).
-    passFunc->replaceAllUsesWith(passFunc->getArgument());
-    passFunc->block()->discard(passFunc);
-
     // Shimmy the slots down to remove the native 'call' function.
     current->shimmySlots(funcDepth - 1);
 
     bool zeroArguments = (argc == 0);
 
     // If no |this| argument was provided, explicitly pass Undefined.
     // Pushing is safe here, since one stack slot has been removed.
     if (zeroArguments) {
-        MConstant *undef = MConstant::New(alloc(), UndefinedValue());
-        current->add(undef);
-        MPassArg *pass = MPassArg::New(alloc(), undef);
-        current->add(pass);
-        current->push(pass);
+        pushConstant(UndefinedValue());
     } else {
         // |this| becomes implicit in the call.
         argc -= 1;
     }
 
     CallInfo callInfo(alloc(), false);
     if (!callInfo.init(current, argc))
         return false;
@@ -4941,48 +4908,41 @@ IonBuilder::jsop_funapply(uint32_t argc)
     // Use funapply that definitely uses |arguments|
     return jsop_funapplyarguments(argc);
 }
 
 bool
 IonBuilder::jsop_funapplyarguments(uint32_t argc)
 {
     // Stack for JSOP_FUNAPPLY:
-    // 1:      MPassArg(Vp)
-    // 2:      MPassArg(This)
-    // argc+1: MPassArg(JSFunction *), the 'f' in |f.call()|, in |this| position.
+    // 1:      Vp
+    // 2:      This
+    // argc+1: JSFunction*, the 'f' in |f.call()|, in |this| position.
     // argc+2: The native 'apply' function.
 
     int funcDepth = -((int)argc + 1);
 
     // Extract call target.
     types::TemporaryTypeSet *funTypes = current->peek(funcDepth)->resultTypeSet();
     JSFunction *target = getSingleCallTarget(funTypes);
 
     // When this script isn't inlined, use MApplyArgs,
     // to copy the arguments from the stack and call the function
     if (inliningDepth_ == 0 && info().executionMode() != DefinitePropertiesAnalysis) {
-
-        // Vp
-        MPassArg *passVp = current->pop()->toPassArg();
-        passVp->getArgument()->setFoldedUnchecked();
-        passVp->replaceAllUsesWith(passVp->getArgument());
-        passVp->block()->discard(passVp);
-
-        // This
-        MPassArg *passThis = current->pop()->toPassArg();
-        MDefinition *argThis = passThis->getArgument();
-        passThis->replaceAllUsesWith(argThis);
-        passThis->block()->discard(passThis);
+        // The array argument corresponds to the arguments object. As the JIT
+        // is implicitly reading the arguments object in the next instruction,
+        // we need to prevent the deletion of the arguments object from resume
+        // points, so that Baseline will behave correctly after a bailout.
+        MDefinition *vp = current->pop();
+        vp->setFoldedUnchecked();
+
+        MDefinition *argThis = current->pop();
 
         // Unwrap the (JSFunction *) parameter.
-        MPassArg *passFunc = current->pop()->toPassArg();
-        MDefinition *argFunc = passFunc->getArgument();
-        passFunc->replaceAllUsesWith(argFunc);
-        passFunc->block()->discard(passFunc);
+        MDefinition *argFunc = current->pop();
 
         // Pop apply function.
         current->pop();
 
         MArgumentsLength *numArgs = MArgumentsLength::New(alloc());
         current->add(numArgs);
 
         MApplyArgs *apply = MApplyArgs::New(alloc(), target, argFunc, numArgs, argThis);
@@ -4999,42 +4959,33 @@ IonBuilder::jsop_funapplyarguments(uint3
     // and can optimize even more, by just calling the functions with the args.
     // We also try this path when doing the definite properties analysis, as we
     // can inline the apply() target and don't care about the actual arguments
     // that were passed in.
 
     CallInfo callInfo(alloc(), false);
 
     // Vp
-    MPassArg *passVp = current->pop()->toPassArg();
-    passVp->getArgument()->setFoldedUnchecked();
-    passVp->replaceAllUsesWith(passVp->getArgument());
-    passVp->block()->discard(passVp);
+    MDefinition *vp = current->pop();
+    vp->setFoldedUnchecked();
 
     // Arguments
     MDefinitionVector args(alloc());
     if (inliningDepth_) {
         if (!args.appendAll(inlineCallInfo_->argv()))
             return false;
     }
     callInfo.setArgs(&args);
 
     // This
-    MPassArg *passThis = current->pop()->toPassArg();
-    MDefinition *argThis = passThis->getArgument();
-    passThis->replaceAllUsesWith(argThis);
-    passThis->block()->discard(passThis);
+    MDefinition *argThis = current->pop();
     callInfo.setThis(argThis);
 
-    // Unwrap the (JSFunction *) parameter.
-    MPassArg *passFunc = current->pop()->toPassArg();
-    MDefinition *argFunc = passFunc->getArgument();
-    passFunc->replaceAllUsesWith(argFunc);
-    passFunc->block()->discard(passFunc);
-
+    // Pop function parameter.
+    MDefinition *argFunc = current->pop();
     callInfo.setFun(argFunc);
 
     // Pop apply function.
     current->pop();
 
     // Try to inline the call.
     InliningDecision decision = makeInliningDecision(target, callInfo);
     switch (decision) {
@@ -5042,17 +4993,16 @@ IonBuilder::jsop_funapplyarguments(uint3
         return false;
       case InliningDecision_DontInline:
         break;
       case InliningDecision_Inline:
         if (target->isInterpreted())
             return inlineScriptedCall(callInfo, target);
     }
 
-    callInfo.wrapArgs(alloc(), current);
     return makeCall(target, callInfo, false);
 }
 
 bool
 IonBuilder::jsop_call(uint32_t argc, bool constructing)
 {
     // If this call has never executed, try to seed the observed type set
     // based on how the call result is used.
@@ -5215,18 +5165,16 @@ IonBuilder::testNeedsArgumentCheck(JSFun
     }
 
     return false;
 }
 
 MCall *
 IonBuilder::makeCallHelper(JSFunction *target, CallInfo &callInfo, bool cloneAtCallsite)
 {
-    JS_ASSERT(callInfo.isWrapped());
-
     // This function may be called with mutated stack.
     // Querying TI for popped types is invalid.
 
     uint32_t targetArgs = callInfo.argc();
 
     // Collect number of missing arguments provided that the target is
     // scripted. Native functions are passed an explicit 'argc' parameter.
     if (target && !target->isNative())
@@ -5236,58 +5184,39 @@ IonBuilder::makeCallHelper(JSFunction *t
         MCall::New(alloc(), target, targetArgs + 1, callInfo.argc(), callInfo.constructing());
     if (!call)
         return nullptr;
 
     // Explicitly pad any missing arguments with |undefined|.
     // This permits skipping the argumentsRectifier.
     for (int i = targetArgs; i > (int)callInfo.argc(); i--) {
         JS_ASSERT_IF(target, !target->isNative());
-        MConstant *undef = MConstant::New(alloc(), UndefinedValue());
-        current->add(undef);
-        MPassArg *pass = MPassArg::New(alloc(), undef);
-        current->add(pass);
-        call->addArg(i, pass);
+        MConstant *undef = constant(UndefinedValue());
+        call->addArg(i, undef);
     }
 
     // Add explicit arguments.
     // Skip addArg(0) because it is reserved for this
-    for (int32_t i = callInfo.argc() - 1; i >= 0; i--) {
-        JS_ASSERT(callInfo.getArg(i)->isPassArg());
-        call->addArg(i + 1, callInfo.getArg(i)->toPassArg());
-    }
-
-    // Place an MPrepareCall before the first passed argument, before we
-    // potentially perform rearrangement.
-    JS_ASSERT(callInfo.thisArg()->isPassArg());
-    MPassArg *thisArg = callInfo.thisArg()->toPassArg();
-    MPrepareCall *start = MPrepareCall::New(alloc());
-    thisArg->block()->insertBefore(thisArg, start);
-    call->initPrepareCall(start);
+    for (int32_t i = callInfo.argc() - 1; i >= 0; i--)
+        call->addArg(i + 1, callInfo.getArg(i));
 
     // Inline the constructor on the caller-side.
     if (callInfo.constructing()) {
         MDefinition *create = createThis(target, callInfo.fun());
         if (!create) {
             abort("Failure inlining constructor for call.");
             return nullptr;
         }
 
-        // Unwrap the MPassArg before discarding: it may have been captured by an MResumePoint.
-        thisArg->replaceAllUsesWith(thisArg->getArgument());
-        thisArg->block()->discard(thisArg);
-
-        MPassArg *newThis = MPassArg::New(alloc(), create);
-        current->add(newThis);
-
-        thisArg = newThis;
-        callInfo.setThis(newThis);
+        callInfo.thisArg()->setFoldedUnchecked();
+        callInfo.setThis(create);
     }
 
     // Pass |this| and function.
+    MDefinition *thisArg = callInfo.thisArg();
     call->addArg(0, thisArg);
 
     // Add a callsite clone IC for multiple targets which all should be
     // callsite cloned, or bake in the clone for a single target.
     if (cloneAtCallsite) {
         MDefinition *fun = makeCallsiteClone(target, callInfo.fun());
         callInfo.setFun(fun);
     }
@@ -5383,17 +5312,17 @@ IonBuilder::jsop_eval(uint32_t argc)
         // boxed into different objects if accessed via 'this'.
         JSValueType type = thisTypes->getKnownTypeTag();
         if (type != JSVAL_TYPE_OBJECT && type != JSVAL_TYPE_NULL && type != JSVAL_TYPE_UNDEFINED)
             return abort("Direct eval from script with maybe-primitive 'this'");
 
         CallInfo callInfo(alloc(), /* constructing = */ false);
         if (!callInfo.init(current, argc))
             return false;
-        callInfo.unwrapArgs();
+        callInfo.setFoldedUnchecked();
 
         callInfo.fun()->setFoldedUnchecked();
 
         MDefinition *scopeChain = current->scopeChain();
         MDefinition *string = callInfo.getArg(0);
 
         // Direct eval acts as identity on non-string types according to
         // ES5 15.1.2.1 step 1.
@@ -5416,21 +5345,18 @@ IonBuilder::jsop_eval(uint32_t argc)
             JSAtom *atom = &string->getOperand(1)->toConstant()->value().toString()->asAtom();
             AutoThreadSafeAccess ts(atom);
 
             if (StringEqualsAscii(atom, "()")) {
                 MDefinition *name = string->getOperand(0);
                 MInstruction *dynamicName = MGetDynamicName::New(alloc(), scopeChain, name);
                 current->add(dynamicName);
 
-                MInstruction *thisv = MPassArg::New(alloc(), thisValue);
-                current->add(thisv);
-
                 current->push(dynamicName);
-                current->push(thisv);
+                current->push(thisValue);
 
                 CallInfo evalCallInfo(alloc(), /* constructing = */ false);
                 if (!evalCallInfo.init(current, /* argc = */ 0))
                     return false;
 
                 return makeCall(nullptr, evalCallInfo, false);
             }
         }
@@ -8558,19 +8484,17 @@ IonBuilder::getPropTryCommonGetter(bool 
         MGuardObject *guardObj = MGuardObject::New(alloc(), obj);
         current->add(guardObj);
         obj = guardObj;
     }
 
     // Spoof stack to expected state for call.
     pushConstant(ObjectValue(*commonGetter));
 
-    MPassArg *wrapper = MPassArg::New(alloc(), obj);
-    current->add(wrapper);
-    current->push(wrapper);
+    current->push(obj);
 
     CallInfo callInfo(alloc(), false);
     if (!callInfo.init(current, 0))
         return false;
 
     // Inline if we can, otherwise, forget it and just generate a call.
     bool inlineable = false;
     if (commonGetter->isInterpreted()) {
@@ -8853,23 +8777,18 @@ IonBuilder::setPropTryCommonSetter(bool 
     uint32_t depth = current->stackDepth() + 3;
     if (depth > current->nslots()) {
         if (!current->increaseSlots(depth - current->nslots()))
             return false;
     }
 
     pushConstant(ObjectValue(*commonSetter));
 
-    MPassArg *wrapper = MPassArg::New(alloc(), obj);
-    current->push(wrapper);
-    current->add(wrapper);
-
-    MPassArg *arg = MPassArg::New(alloc(), value);
-    current->push(arg);
-    current->add(arg);
+    current->push(obj);
+    current->push(value);
 
     // Call the setter. Note that we have to push the original value, not
     // the setter's return value.
     CallInfo callInfo(alloc(), false);
     if (!callInfo.init(current, 1))
         return false;
 
     // Ensure that we know we are calling a setter in case we inline it.
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -927,85 +927,48 @@ class CallInfo
         return args_[i];
     }
 
     void setArg(uint32_t i, MDefinition *def) {
         JS_ASSERT(i < argc());
         args_[i] = def;
     }
 
-    MDefinition *thisArg() {
+    MDefinition *thisArg() const {
         JS_ASSERT(thisArg_);
         return thisArg_;
     }
 
     void setThis(MDefinition *thisArg) {
         thisArg_ = thisArg;
     }
 
-    bool constructing() {
+    bool constructing() const {
         return constructing_;
     }
 
     bool isSetter() const {
         return setter_;
     }
     void markAsSetter() {
         setter_ = true;
     }
 
-    void wrapArgs(TempAllocator &alloc, MBasicBlock *current) {
-        thisArg_ = wrap(alloc, current, thisArg_);
-        for (uint32_t i = 0; i < argc(); i++)
-            args_[i] = wrap(alloc, current, args_[i]);
-    }
-
-    void unwrapArgs() {
-        thisArg_ = unwrap(thisArg_);
-        for (uint32_t i = 0; i < argc(); i++)
-            args_[i] = unwrap(args_[i]);
-    }
-
     MDefinition *fun() const {
         JS_ASSERT(fun_);
         return fun_;
     }
 
     void setFun(MDefinition *fun) {
-        JS_ASSERT(!fun->isPassArg());
         fun_ = fun;
     }
-
-    bool isWrapped() {
-        bool wrapped = thisArg()->isPassArg();
-
-#if DEBUG
+    void setFoldedUnchecked() {
+        thisArg_->setFoldedUnchecked();
         for (uint32_t i = 0; i < argc(); i++)
-            JS_ASSERT(args_[i]->isPassArg() == wrapped);
-#endif
-
-        return wrapped;
-    }
-
-  private:
-    static MDefinition *unwrap(MDefinition *arg) {
-        JS_ASSERT(arg->isPassArg());
-        MPassArg *passArg = arg->toPassArg();
-        MBasicBlock *block = passArg->block();
-        MDefinition *wrapped = passArg->getArgument();
-        wrapped->setFoldedUnchecked();
-        passArg->replaceAllUsesWith(wrapped);
-        block->discard(passArg);
-        return wrapped;
-    }
-    static MDefinition *wrap(TempAllocator &alloc, MBasicBlock *current, MDefinition *arg) {
-        JS_ASSERT(!arg->isPassArg());
-        MPassArg *passArg = MPassArg::New(alloc, arg);
-        current->add(passArg);
-        return passArg;
+            getArg(i)->setFoldedUnchecked();
     }
 };
 
 bool TypeSetIncludes(types::TypeSet *types, MIRType input, types::TypeSet *inputTypes);
 
 bool NeedsPostBarrier(CompileInfo &info, MDefinition *value);
 
 } // namespace jit
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -956,32 +956,33 @@ class LComputeThis : public LInstruction
         return mir_->toComputeThis();
     }
 };
 
 // Writes a typed argument for a function call to the frame's argument vector.
 class LStackArgT : public LInstructionHelper<0, 1, 0>
 {
     uint32_t argslot_; // Index into frame-scope argument vector.
+    MIRType type_;
 
   public:
     LIR_HEADER(StackArgT)
 
-    LStackArgT(uint32_t argslot, const LAllocation &arg)
-      : argslot_(argslot)
+    LStackArgT(uint32_t argslot, MIRType type, const LAllocation &arg)
+      : argslot_(argslot),
+        type_(type)
     {
         setOperand(0, arg);
     }
-
-    MPassArg *mir() const {
-        return this->mir_->toPassArg();
-    }
     uint32_t argslot() const {
         return argslot_;
     }
+    MIRType type() const {
+        return type_;
+    }
     const LAllocation *getArgument() {
         return getOperand(0);
     }
 };
 
 // Writes an untyped argument for a function call to the frame's argument vector.
 class LStackArgV : public LInstructionHelper<0, BOX_PIECES, 0>
 {
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -283,54 +283,16 @@ LIRGenerator::visitInitPropGetterSetter(
 {
     LInitPropGetterSetter *lir =
         new(alloc()) LInitPropGetterSetter(useRegisterAtStart(ins->object()),
                                            useRegisterAtStart(ins->value()));
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
-LIRGenerator::visitPrepareCall(MPrepareCall *ins)
-{
-    allocateArguments(ins->argc());
-
-#ifdef DEBUG
-    if (!prepareCallStack_.append(ins))
-        return false;
-#endif
-
-    return true;
-}
-
-bool
-LIRGenerator::visitPassArg(MPassArg *arg)
-{
-    MDefinition *opd = arg->getArgument();
-    uint32_t argslot = getArgumentSlot(arg->getArgnum());
-    JS_ASSERT(arg->getArgnum() < prepareCallStack_.back()->argc());
-
-    // Pass through the virtual register of the operand.
-    // This causes snapshots to correctly copy the operand on the stack.
-    //
-    // This keeps the backing store around longer than strictly required.
-    // We could do better by informing snapshots about the argument vector.
-    arg->setVirtualRegister(opd->virtualRegister());
-
-    // Values take a slow path.
-    if (opd->type() == MIRType_Value) {
-        LStackArgV *stack = new(alloc()) LStackArgV(argslot);
-        return useBox(stack, 0, opd) && add(stack);
-    }
-
-    // Known types can move constant types and/or payloads.
-    LStackArgT *stack = new(alloc()) LStackArgT(argslot, useRegisterOrConstant(opd));
-    return add(stack, arg);
-}
-
-bool
 LIRGenerator::visitCreateThisWithTemplate(MCreateThisWithTemplate *ins)
 {
     LCreateThisWithTemplate *lir = new(alloc()) LCreateThisWithTemplate();
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitCreateThisWithProto(MCreateThisWithProto *ins)
@@ -398,32 +360,56 @@ LIRGenerator::visitComputeThis(MComputeT
     // have its inputs in different registers than its return value so that
     // they aren't clobbered.
     if (!useBox(lir, LComputeThis::ValueIndex, ins->input()))
         return false;
 
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
+bool
+LIRGenerator::lowerCallArguments(MCall *call)
+{
+    uint32_t argc = call->numStackArgs();
+    if (argc > maxargslots_)
+        maxargslots_ = argc;
+
+    for (size_t i = 0; i < argc; i++) {
+        MDefinition *arg = call->getArg(i);
+        uint32_t argslot = argc - i;
+
+        // Values take a slow path.
+        if (arg->type() == MIRType_Value) {
+            LStackArgV *stack = new(alloc()) LStackArgV(argslot);
+            if (!useBox(stack, 0, arg) || !add(stack))
+                return false;
+        } else {
+            // Known types can move constant types and/or payloads.
+            LStackArgT *stack = new(alloc()) LStackArgT(argslot, arg->type(), useRegisterOrConstant(arg));
+            if (!add(stack))
+                return false;
+        }
+    }
+
+    return true;
+}
 
 bool
 LIRGenerator::visitCall(MCall *call)
 {
     JS_ASSERT(CallTempReg0 != CallTempReg1);
     JS_ASSERT(CallTempReg0 != ArgumentsRectifierReg);
     JS_ASSERT(CallTempReg1 != ArgumentsRectifierReg);
     JS_ASSERT(call->getFunction()->type() == MIRType_Object);
 
+    if (!lowerCallArguments(call))
+        return false;
+
     // Height of the current argument vector.
-    uint32_t argslot = getArgumentSlotForCall();
-    freeArguments(call->numStackArgs());
-
-    // Check MPrepareCall/MCall nesting.
-    JS_ASSERT(prepareCallStack_.popCopy() == call->getPrepareCall());
-
+    uint32_t argslot = call->numStackArgs();
     JSFunction *target = call->getSingleTarget();
 
     // Call DOM functions.
     if (call->isDOMFunction()) {
         JS_ASSERT(target && target->isNative());
         Register cxReg, objReg, privReg, argsReg;
         GetTempRegForIntArg(0, 0, &cxReg);
         GetTempRegForIntArg(1, 0, &objReg);
@@ -1856,56 +1842,41 @@ LIRGenerator::visitToString(MToString *i
 
       default:
         // Objects might be effectful. (see ToPrimitive)
         MOZ_ASSUME_UNREACHABLE("unexpected type");
     }
 }
 
 static bool
-MustCloneRegExpForCall(MPassArg *arg)
+MustCloneRegExpForCall(MCall *call, uint32_t useIndex)
 {
-    // |arg| is a regex literal flowing into a call. Return |false| iff
+    // We have a regex literal flowing into a call. Return |false| iff
     // this is a native call that does not let the regex escape.
 
-    JS_ASSERT(arg->getArgument()->isRegExp());
-
-    for (MUseIterator iter(arg->usesBegin()); iter != arg->usesEnd(); iter++) {
-        MNode *node = iter->consumer();
-        if (!node->isDefinition())
-            return true;
-
-        MDefinition *def = node->toDefinition();
-        if (!def->isCall())
-            return true;
-
-        MCall *call = def->toCall();
-        JSFunction *target = call->getSingleTarget();
-        if (!target || !target->isNative())
-            return true;
-
-        if (iter->index() == MCall::IndexOfThis() &&
-            (target->native() == regexp_exec || target->native() == regexp_test))
-        {
-            continue;
-        }
-
-        if (iter->index() == MCall::IndexOfArgument(0) &&
-            (target->native() == str_split ||
-             target->native() == str_replace ||
-             target->native() == str_match ||
-             target->native() == str_search))
-        {
-            continue;
-        }
-
+    JSFunction *target = call->getSingleTarget();
+    if (!target || !target->isNative())
         return true;
+
+    if (useIndex == MCall::IndexOfThis() &&
+        (target->native() == regexp_exec || target->native() == regexp_test))
+    {
+        return false;
     }
 
-    return false;
+    if (useIndex == MCall::IndexOfArgument(0) &&
+        (target->native() == str_split ||
+         target->native() == str_replace ||
+         target->native() == str_match ||
+         target->native() == str_search))
+    {
+        return false;
+    }
+
+    return true;
 }
 
 
 static bool
 MustCloneRegExp(MRegExp *regexp)
 {
     if (regexp->mustClone())
         return true;
@@ -1920,17 +1891,17 @@ MustCloneRegExp(MRegExp *regexp)
 
         MDefinition *def = node->toDefinition();
         if (def->isRegExpTest() && iter->index() == 1) {
             // Optimized RegExp.prototype.test.
             JS_ASSERT(def->toRegExpTest()->regexp() == regexp);
             continue;
         }
 
-        if (def->isPassArg() && !MustCloneRegExpForCall(def->toPassArg()))
+        if (def->isCall() && !MustCloneRegExpForCall(def->toCall(), iter->index()))
             continue;
 
         return true;
     }
     return false;
 }
 
 bool
@@ -3504,39 +3475,16 @@ LIRGenerator::updateResumeState(MInstruc
 void
 LIRGenerator::updateResumeState(MBasicBlock *block)
 {
     lastResumePoint_ = block->entryResumePoint();
     if (IonSpewEnabled(IonSpew_Snapshots) && lastResumePoint_)
         SpewResumePoint(block, nullptr, lastResumePoint_);
 }
 
-void
-LIRGenerator::allocateArguments(uint32_t argc)
-{
-    argslots_ += argc;
-    if (argslots_ > maxargslots_)
-        maxargslots_ = argslots_;
-}
-
-uint32_t
-LIRGenerator::getArgumentSlot(uint32_t argnum)
-{
-    // First slot has index 1.
-    JS_ASSERT(argnum < argslots_);
-    return argslots_ - argnum ;
-}
-
-void
-LIRGenerator::freeArguments(uint32_t argc)
-{
-    JS_ASSERT(argc <= argslots_);
-    argslots_ -= argc;
-}
-
 bool
 LIRGenerator::visitBlock(MBasicBlock *block)
 {
     current = block->lir();
     updateResumeState(block);
 
     if (!definePhis())
         return false;
@@ -3626,13 +3574,10 @@ LIRGenerator::generate()
         if (!visitBlock(*block))
             return false;
     }
 
     if (graph.osrBlock())
         lirGraph_.setOsrBlock(graph.osrBlock()->lir());
 
     lirGraph_.setArgumentSlotCount(maxargslots_);
-
-    JS_ASSERT(argslots_ == 0);
-    JS_ASSERT(prepareCallStack_.empty());
     return true;
 }
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -24,31 +24,23 @@
 namespace js {
 namespace jit {
 
 class LIRGenerator : public LIRGeneratorSpecific
 {
     void updateResumeState(MInstruction *ins);
     void updateResumeState(MBasicBlock *block);
 
-    // The active depth of the (perhaps nested) call argument vectors.
-    uint32_t argslots_;
     // The maximum depth, for framesizeclass determination.
     uint32_t maxargslots_;
 
-#ifdef DEBUG
-    // In debug builds, check MPrepareCall and MCall are properly
-    // nested. The argslots_ mechanism relies on this.
-    Vector<MPrepareCall *, 4, SystemAllocPolicy> prepareCallStack_;
-#endif
-
   public:
     LIRGenerator(MIRGenerator *gen, MIRGraph &graph, LIRGraph &lirGraph)
       : LIRGeneratorSpecific(gen, graph, lirGraph),
-        argslots_(0), maxargslots_(0)
+        maxargslots_(0)
     { }
 
     bool generate();
 
   private:
 
     bool useBoxAtStart(LInstruction *lir, size_t n, MDefinition *mir,
                        LUse::Policy policy = LUse::REGISTER) {
@@ -56,24 +48,17 @@ class LIRGenerator : public LIRGenerator
     }
 
     bool lowerBitOp(JSOp op, MInstruction *ins);
     bool lowerShiftOp(JSOp op, MShiftInstruction *ins);
     bool lowerBinaryV(JSOp op, MBinaryInstruction *ins);
     bool precreatePhi(LBlock *block, MPhi *phi);
     bool definePhis();
 
-    // Allocate argument slots for a future function call.
-    void allocateArguments(uint32_t argc);
-    // Map an MPassArg's argument number to a slot in the frame arg vector.
-    // Slots are indexed from 1. argnum is indexed from 0.
-    uint32_t getArgumentSlot(uint32_t argnum);
-    uint32_t getArgumentSlotForCall() { return argslots_; }
-    // Free argument slots following a function call.
-    void freeArguments(uint32_t argc);
+    bool lowerCallArguments(MCall *call);
 
   public:
     bool visitInstruction(MInstruction *ins);
     bool visitBlock(MBasicBlock *block);
 
     // Visitor hooks are explicit, to give CPU-specific versions a chance to
     // intercept without a bunch of explicit gunk in the .cpp.
     bool visitParameter(MParameter *param);
@@ -94,18 +79,16 @@ class LIRGenerator : public LIRGenerator
     bool visitInitElem(MInitElem *ins);
     bool visitInitElemGetterSetter(MInitElemGetterSetter *ins);
     bool visitInitProp(MInitProp *ins);
     bool visitInitPropGetterSetter(MInitPropGetterSetter *ins);
     bool visitCheckOverRecursed(MCheckOverRecursed *ins);
     bool visitCheckOverRecursedPar(MCheckOverRecursedPar *ins);
     bool visitDefVar(MDefVar *ins);
     bool visitDefFun(MDefFun *ins);
-    bool visitPrepareCall(MPrepareCall *ins);
-    bool visitPassArg(MPassArg *arg);
     bool visitCreateThisWithTemplate(MCreateThisWithTemplate *ins);
     bool visitCreateThisWithProto(MCreateThisWithProto *ins);
     bool visitCreateThis(MCreateThis *ins);
     bool visitCreateArgumentsObject(MCreateArgumentsObject *ins);
     bool visitGetArgumentsObjectArg(MGetArgumentsObjectArg *ins);
     bool visitSetArgumentsObjectArg(MSetArgumentsObjectArg *ins);
     bool visitReturnFromCtor(MReturnFromCtor *ins);
     bool visitComputeThis(MComputeThis *ins);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -187,17 +187,17 @@ IonBuilder::inlineMathFunction(CallInfo 
         return InliningStatus_NotInlined;
     if (!IsNumberType(callInfo.getArg(0)->type()))
         return InliningStatus_NotInlined;
 
     const MathCache *cache = compartment->runtime()->maybeGetMathCache();
     if (!cache)
         return InliningStatus_NotInlined;
 
-    callInfo.unwrapArgs();
+    callInfo.thisArg()->setFoldedUnchecked();
 
     MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), function, cache);
     current->add(ins);
     current->push(ins);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
@@ -229,27 +229,27 @@ IonBuilder::inlineArray(CallInfo &callIn
             }
         }
     }
 
     // A single integer argument denotes initial length.
     if (callInfo.argc() == 1) {
         if (callInfo.getArg(0)->type() != MIRType_Int32)
             return InliningStatus_NotInlined;
-        MDefinition *arg = callInfo.getArg(0)->toPassArg()->getArgument();
+        MDefinition *arg = callInfo.getArg(0);
         if (!arg->isConstant())
             return InliningStatus_NotInlined;
 
         // Negative lengths generate a RangeError, unhandled by the inline path.
         initLength = arg->toConstant()->value().toInt32();
         if (initLength >= JSObject::NELEMENTS_LIMIT)
             return InliningStatus_NotInlined;
     }
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     types::TemporaryTypeSet::DoubleConversion conversion =
         getInlineReturnTypeSet()->convertDoubleElements(constraints());
     if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles) {
         AutoThreadSafeAccess ts(templateObject);
         templateObject->setShouldConvertDoubleElements();
     }
 
@@ -321,17 +321,17 @@ IonBuilder::inlineArrayPopShift(CallInfo
     if (!thisTypes || thisTypes->getKnownClass() != &ArrayObject::class_)
         return InliningStatus_NotInlined;
     if (thisTypes->hasObjectFlags(constraints(), unhandledFlags))
         return InliningStatus_NotInlined;
 
     if (types::ArrayPrototypeHasIndexedProperty(constraints(), script()))
         return InliningStatus_NotInlined;
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet();
     bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED);
     bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType());
 
     bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
                                                 callInfo.thisArg(), nullptr, returnTypes);
     if (barrier)
@@ -384,17 +384,17 @@ IonBuilder::inlineArrayPush(CallInfo &ca
     if (types::ArrayPrototypeHasIndexedProperty(constraints(), script()))
         return InliningStatus_NotInlined;
 
     types::TemporaryTypeSet::DoubleConversion conversion =
         thisTypes->convertDoubleElements(constraints());
     if (conversion == types::TemporaryTypeSet::AmbiguousDoubleConversion)
         return InliningStatus_NotInlined;
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
     value = callInfo.getArg(0);
 
     if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles ||
         conversion == types::TemporaryTypeSet::MaybeConvertToDoubles)
     {
         MInstruction *valueDouble = MToDouble::New(alloc(), value);
         current->add(valueDouble);
         value = valueDouble;
@@ -495,17 +495,17 @@ IonBuilder::inlineArrayConcat(CallInfo &
     }
 
     // Inline the call.
     JSObject *templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat);
     if (!templateObj || templateObj->type() != baseThisType)
         return InliningStatus_NotInlined;
     JS_ASSERT(templateObj->is<ArrayObject>());
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MArrayConcat *ins = MArrayConcat::New(alloc(), constraints(), callInfo.thisArg(), callInfo.getArg(0),
                                           templateObj, templateObj->type()->initialHeap(constraints()));
     current->add(ins);
     current->push(ins);
 
     if (!resumeAfter(ins))
         return InliningStatus_Error;
@@ -530,17 +530,17 @@ IonBuilder::inlineMathAbs(CallInfo &call
     //        argType == Double or Float32, returnType == Int, or
     //        argType == Float32, returnType == Double
     if (argType != returnType && !(IsFloatingPointType(argType) && returnType == MIRType_Int32)
         && !(argType == MIRType_Float32 && returnType == MIRType_Double))
     {
         return InliningStatus_NotInlined;
     }
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     // If the arg is a Float32, we specialize the op as double, it will be specialized
     // as float32 if necessary later.
     MIRType absType = (argType == MIRType_Float32) ? MIRType_Double : argType;
     MInstruction *ins = MAbs::New(alloc(), callInfo.getArg(0), absType);
     current->add(ins);
 
     if (IsFloatingPointType(argType) && returnType == MIRType_Int32) {
@@ -563,31 +563,31 @@ IonBuilder::inlineMathFloor(CallInfo &ca
     if (callInfo.argc() != 1)
         return InliningStatus_NotInlined;
 
     MIRType argType = callInfo.getArg(0)->type();
     MIRType returnType = getInlineReturnType();
 
     // Math.floor(int(x)) == int(x)
     if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
-        callInfo.unwrapArgs();
+        callInfo.setFoldedUnchecked();
         current->push(callInfo.getArg(0));
         return InliningStatus_Inlined;
     }
 
     if (IsFloatingPointType(argType) && returnType == MIRType_Int32) {
-        callInfo.unwrapArgs();
+        callInfo.setFoldedUnchecked();
         MFloor *ins = MFloor::New(alloc(), callInfo.getArg(0));
         current->add(ins);
         current->push(ins);
         return InliningStatus_Inlined;
     }
 
     if (IsFloatingPointType(argType) && returnType == MIRType_Double) {
-        callInfo.unwrapArgs();
+        callInfo.setFoldedUnchecked();
         MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Floor, nullptr);
         current->add(ins);
         current->push(ins);
         return InliningStatus_Inlined;
     }
 
     return InliningStatus_NotInlined;
 }
@@ -601,23 +601,23 @@ IonBuilder::inlineMathCeil(CallInfo &cal
     if (callInfo.argc() != 1)
         return InliningStatus_NotInlined;
 
     MIRType argType = callInfo.getArg(0)->type();
     MIRType returnType = getInlineReturnType();
 
     // Math.ceil(int(x)) == int(x)
     if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
-        callInfo.unwrapArgs();
+        callInfo.setFoldedUnchecked();
         current->push(callInfo.getArg(0));
         return InliningStatus_Inlined;
     }
 
     if (IsFloatingPointType(argType) && returnType == MIRType_Double) {
-        callInfo.unwrapArgs();
+        callInfo.setFoldedUnchecked();
         MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Ceil, nullptr);
         current->add(ins);
         current->push(ins);
         return InliningStatus_Inlined;
     }
 
     return InliningStatus_NotInlined;
 }
@@ -631,31 +631,31 @@ IonBuilder::inlineMathRound(CallInfo &ca
     if (callInfo.argc() != 1)
         return InliningStatus_NotInlined;
 
     MIRType returnType = getInlineReturnType();
     MIRType argType = callInfo.getArg(0)->type();
 
     // Math.round(int(x)) == int(x)
     if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
-        callInfo.unwrapArgs();
+        callInfo.setFoldedUnchecked();
         current->push(callInfo.getArg(0));
         return InliningStatus_Inlined;
     }
 
     if (argType == MIRType_Double && returnType == MIRType_Int32) {
-        callInfo.unwrapArgs();
+        callInfo.setFoldedUnchecked();
         MRound *ins = MRound::New(alloc(), callInfo.getArg(0));
         current->add(ins);
         current->push(ins);
         return InliningStatus_Inlined;
     }
 
     if (argType == MIRType_Double && returnType == MIRType_Double) {
-        callInfo.unwrapArgs();
+        callInfo.setFoldedUnchecked();
         MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Round, nullptr);
         current->add(ins);
         current->push(ins);
         return InliningStatus_Inlined;
     }
 
     return InliningStatus_NotInlined;
 }
@@ -670,17 +670,17 @@ IonBuilder::inlineMathSqrt(CallInfo &cal
         return InliningStatus_NotInlined;
 
     MIRType argType = callInfo.getArg(0)->type();
     if (getInlineReturnType() != MIRType_Double)
         return InliningStatus_NotInlined;
     if (!IsNumberType(argType))
         return InliningStatus_NotInlined;
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MSqrt *sqrt = MSqrt::New(alloc(), callInfo.getArg(0));
     current->add(sqrt);
     current->push(sqrt);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
@@ -696,17 +696,17 @@ IonBuilder::inlineMathAtan2(CallInfo &ca
         return InliningStatus_NotInlined;
 
     MIRType argType0 = callInfo.getArg(0)->type();
     MIRType argType1 = callInfo.getArg(1)->type();
 
     if (!IsNumberType(argType0) || !IsNumberType(argType1))
         return InliningStatus_NotInlined;
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MAtan2 *atan2 = MAtan2::New(alloc(), callInfo.getArg(0), callInfo.getArg(1));
     current->add(atan2);
     current->push(atan2);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
@@ -722,17 +722,17 @@ IonBuilder::inlineMathHypot(CallInfo &ca
         return InliningStatus_NotInlined;
 
     MIRType argType0 = callInfo.getArg(0)->type();
     MIRType argType1 = callInfo.getArg(1)->type();
 
     if (!IsNumberType(argType0) || !IsNumberType(argType1))
         return InliningStatus_NotInlined;
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MHypot *hypot = MHypot::New(alloc(), callInfo.getArg(0), callInfo.getArg(1));
     current->add(hypot);
     current->push(hypot);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
@@ -751,17 +751,17 @@ IonBuilder::inlineMathPow(CallInfo &call
 
     if (outputType != MIRType_Int32 && outputType != MIRType_Double)
         return InliningStatus_NotInlined;
     if (baseType != MIRType_Int32 && baseType != MIRType_Double)
         return InliningStatus_NotInlined;
     if (powerType != MIRType_Int32 && powerType != MIRType_Double)
         return InliningStatus_NotInlined;
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MDefinition *base = callInfo.getArg(0);
     MDefinition *power = callInfo.getArg(1);
     MDefinition *output = nullptr;
 
     // Optimize some constant powers.
     if (callInfo.getArg(1)->isConstant() &&
         callInfo.getArg(1)->toConstant()->value().isNumber())
@@ -843,17 +843,17 @@ IonBuilder::InliningStatus
 IonBuilder::inlineMathRandom(CallInfo &callInfo)
 {
     if (callInfo.constructing())
         return InliningStatus_NotInlined;
 
     if (getInlineReturnType() != MIRType_Double)
         return InliningStatus_NotInlined;
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MRandom *rand = MRandom::New(alloc());
     current->add(rand);
     current->push(rand);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
@@ -866,17 +866,17 @@ IonBuilder::inlineMathImul(CallInfo &cal
     if (returnType != MIRType_Int32)
         return InliningStatus_NotInlined;
 
     if (!IsNumberType(callInfo.getArg(0)->type()))
         return InliningStatus_NotInlined;
     if (!IsNumberType(callInfo.getArg(1)->type()))
         return InliningStatus_NotInlined;
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MInstruction *first = MTruncateToInt32::New(alloc(), callInfo.getArg(0));
     current->add(first);
 
     MInstruction *second = MTruncateToInt32::New(alloc(), callInfo.getArg(1));
     current->add(second);
 
     MMul *ins = MMul::New(alloc(), first, second, MIRType_Int32, MMul::Integer);
@@ -907,17 +907,17 @@ IonBuilder::inlineMathFRound(CallInfo &c
         if (!IsNumberType(returnType))
             return InliningStatus_NotInlined;
     }
 
     MIRType arg = callInfo.getArg(0)->type();
     if (!IsNumberType(arg))
         return InliningStatus_NotInlined;
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MToFloat32 *ins = MToFloat32::New(alloc(), callInfo.getArg(0));
     current->add(ins);
     current->push(ins);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
@@ -935,17 +935,17 @@ IonBuilder::inlineMathMinMax(CallInfo &c
         if (!IsNumberType(argType))
             return InliningStatus_NotInlined;
 
         // We would need to inform TI if we happen to return a double.
         if (returnType == MIRType_Int32 && IsFloatingPointType(argType))
             return InliningStatus_NotInlined;
     }
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     // Chain N-1 MMinMax instructions to compute the MinMax.
     MMinMax *last = MMinMax::New(alloc(), callInfo.getArg(0), callInfo.getArg(1), returnType, max);
     current->add(last);
 
     for (unsigned i = 2; i < callInfo.argc(); i++) {
         MMinMax *ins = MMinMax::New(alloc(), last, callInfo.getArg(i), returnType, max);
         current->add(ins);
@@ -967,17 +967,17 @@ IonBuilder::inlineStringObject(CallInfo 
     if (type != MIRType_Int32 && type != MIRType_String)
         return InliningStatus_NotInlined;
 
     JSObject *templateObj = inspector->getTemplateObjectForNative(pc, js_String);
     if (!templateObj)
         return InliningStatus_NotInlined;
     JS_ASSERT(templateObj->is<StringObject>());
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MNewStringObject *ins = MNewStringObject::New(alloc(), callInfo.getArg(0), templateObj);
     current->add(ins);
     current->push(ins);
 
     if (!resumeAfter(ins))
         return InliningStatus_Error;
 
@@ -1007,17 +1007,17 @@ IonBuilder::inlineStringSplit(CallInfo &
     if (!key.maybeTypes())
         return InliningStatus_NotInlined;
 
     if (!key.maybeTypes()->hasType(types::Type::StringType())) {
         key.freeze(constraints());
         return InliningStatus_NotInlined;
     }
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MStringSplit *ins = MStringSplit::New(alloc(), constraints(), callInfo.thisArg(),
                                           callInfo.getArg(0), templateObject);
     current->add(ins);
     current->push(ins);
 
     return InliningStatus_Inlined;
 }
@@ -1031,17 +1031,17 @@ IonBuilder::inlineStrCharCodeAt(CallInfo
     if (getInlineReturnType() != MIRType_Int32)
         return InliningStatus_NotInlined;
     if (callInfo.thisArg()->type() != MIRType_String && callInfo.thisArg()->type() != MIRType_Value)
         return InliningStatus_NotInlined;
     MIRType argType = callInfo.getArg(0)->type();
     if (argType != MIRType_Int32 && argType != MIRType_Double)
         return InliningStatus_NotInlined;
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MInstruction *index = MToInt32::New(alloc(), callInfo.getArg(0));
     current->add(index);
 
     MStringLength *length = MStringLength::New(alloc(), callInfo.thisArg());
     current->add(length);
 
     index = addBoundsCheck(index, length);
@@ -1058,17 +1058,17 @@ IonBuilder::inlineStrFromCharCode(CallIn
     if (callInfo.argc() != 1 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     if (getInlineReturnType() != MIRType_String)
         return InliningStatus_NotInlined;
     if (callInfo.getArg(0)->type() != MIRType_Int32)
         return InliningStatus_NotInlined;
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MToInt32 *charCode = MToInt32::New(alloc(), callInfo.getArg(0));
     current->add(charCode);
 
     MFromCharCode *string = MFromCharCode::New(alloc(), charCode);
     current->add(string);
     current->push(string);
     return InliningStatus_Inlined;
@@ -1083,17 +1083,17 @@ IonBuilder::inlineStrCharAt(CallInfo &ca
     if (getInlineReturnType() != MIRType_String)
         return InliningStatus_NotInlined;
     if (callInfo.thisArg()->type() != MIRType_String)
         return InliningStatus_NotInlined;
     MIRType argType = callInfo.getArg(0)->type();
     if (argType != MIRType_Int32 && argType != MIRType_Double)
         return InliningStatus_NotInlined;
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MInstruction *index = MToInt32::New(alloc(), callInfo.getArg(0));
     current->add(index);
 
     MStringLength *length = MStringLength::New(alloc(), callInfo.thisArg());
     current->add(length);
 
     index = addBoundsCheck(index, length);
@@ -1122,17 +1122,17 @@ IonBuilder::inlineRegExpTest(CallInfo &c
         return InliningStatus_NotInlined;
     types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
     const Class *clasp = thisTypes ? thisTypes->getKnownClass() : nullptr;
     if (clasp != &RegExpObject::class_)
         return InliningStatus_NotInlined;
     if (callInfo.getArg(0)->type() != MIRType_String)
         return InliningStatus_NotInlined;
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MInstruction *match = MRegExpTest::New(alloc(), callInfo.thisArg(), callInfo.getArg(0));
     current->add(match);
     current->push(match);
     if (!resumeAfter(match))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
@@ -1176,17 +1176,17 @@ IonBuilder::inlineUnsafePutElements(Call
         ScalarTypeRepresentation::Type arrayType;
         if ((!isDenseNative || writeNeedsBarrier) &&
             !ElementAccessIsTypedArray(obj, id, &arrayType))
         {
             return InliningStatus_NotInlined;
         }
     }
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     // Push the result first so that the stack depth matches up for
     // the potential bailouts that will occur in the stores below.
     MConstant *udef = MConstant::New(alloc(), UndefinedValue());
     current->add(udef);
     current->push(udef);
 
     for (uint32_t base = 0; base < argc; base += 3) {
@@ -1269,17 +1269,17 @@ IonBuilder::inlineForceSequentialOrInPar
         // In sequential mode, leave as is, because we'd have to
         // access the "in warmup" flag of the runtime.
         return InliningStatus_NotInlined;
 
       case ParallelExecution: {
         // During Parallel Exec, we always force sequential, so
         // replace with true.  This permits UCE to eliminate the
         // entire path as dead, which is important.
-        callInfo.unwrapArgs();
+        callInfo.setFoldedUnchecked();
         MConstant *ins = MConstant::New(alloc(), BooleanValue(true));
         current->add(ins);
         current->push(ins);
         return InliningStatus_Inlined;
       }
     }
 
     MOZ_ASSUME_UNREACHABLE("Invalid execution mode");
@@ -1326,17 +1326,17 @@ IonBuilder::inlineNewDenseArrayForParall
     if (callInfo.getArg(0)->type() != MIRType_Int32)
         return InliningStatus_NotInlined;
     types::TypeObject *typeObject = returnTypes->getTypeObject(0);
 
     JSObject *templateObject = inspector->getTemplateObjectForNative(pc, intrinsic_NewDenseArray);
     if (!templateObject || templateObject->type() != typeObject)
         return InliningStatus_NotInlined;
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MNewDenseArrayPar *newObject = MNewDenseArrayPar::New(alloc(),
                                                           graph().forkJoinSlice(),
                                                           callInfo.getArg(0),
                                                           templateObject);
     current->add(newObject);
     current->push(newObject);
 
@@ -1351,22 +1351,22 @@ IonBuilder::inlineUnsafeSetReservedSlot(
     if (getInlineReturnType() != MIRType_Undefined)
         return InliningStatus_NotInlined;
     if (callInfo.getArg(0)->type() != MIRType_Object)
         return InliningStatus_NotInlined;
     if (callInfo.getArg(1)->type() != MIRType_Int32)
         return InliningStatus_NotInlined;
 
     // Don't inline if we don't have a constant slot.
-    MDefinition *arg = callInfo.getArg(1)->toPassArg()->getArgument();
+    MDefinition *arg = callInfo.getArg(1);
     if (!arg->isConstant())
         return InliningStatus_NotInlined;
     uint32_t slot = arg->toConstant()->value().toPrivateUint32();
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MStoreFixedSlot *store = MStoreFixedSlot::New(alloc(), callInfo.getArg(0), slot, callInfo.getArg(2));
     current->add(store);
     current->push(store);
 
     if (NeedsPostBarrier(info(), callInfo.getArg(2)))
         current->add(MPostWriteBarrier::New(alloc(), callInfo.thisArg(), callInfo.getArg(2)));
 
@@ -1379,22 +1379,22 @@ IonBuilder::inlineUnsafeGetReservedSlot(
     if (callInfo.argc() != 2 || callInfo.constructing())
         return InliningStatus_NotInlined;
     if (callInfo.getArg(0)->type() != MIRType_Object)
         return InliningStatus_NotInlined;
     if (callInfo.getArg(1)->type() != MIRType_Int32)
         return InliningStatus_NotInlined;
 
     // Don't inline if we don't have a constant slot.
-    MDefinition *arg = callInfo.getArg(1)->toPassArg()->getArgument();
+    MDefinition *arg = callInfo.getArg(1);
     if (!arg->isConstant())
         return InliningStatus_NotInlined;
     uint32_t slot = arg->toConstant()->value().toPrivateUint32();
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MLoadFixedSlot *load = MLoadFixedSlot::New(alloc(), callInfo.getArg(0), slot);
     current->add(load);
     current->push(load);
 
     // We don't track reserved slot types, so always emit a barrier.
     if (!pushTypeBarrier(load, getInlineReturnTypeSet(), true))
         return InliningStatus_Error;
@@ -1418,17 +1418,17 @@ IonBuilder::inlineHaveSameClass(CallInfo
     const Class *arg2Clasp = arg2Types ? arg2Types->getKnownClass() : nullptr;
     if (arg1Clasp && arg2Clasp) {
         MConstant *constant = MConstant::New(alloc(), BooleanValue(arg1Clasp == arg2Clasp));
         current->add(constant);
         current->push(constant);
         return InliningStatus_Inlined;
     }
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MHaveSameClass *sameClass = MHaveSameClass::New(alloc(), callInfo.getArg(0), callInfo.getArg(1));
     current->add(sameClass);
     current->push(sameClass);
 
     return InliningStatus_Inlined;
 }
 
@@ -1454,25 +1454,25 @@ IonBuilder::inlineIsCallable(CallInfo &c
         types::TemporaryTypeSet *types = callInfo.getArg(0)->resultTypeSet();
         const Class *clasp = types ? types->getKnownClass() : nullptr;
         if (clasp) {
             isCallableKnown = true;
             isCallableConstant = clasp->isCallable();
         }
     }
 
+    callInfo.setFoldedUnchecked();
+
     if (isCallableKnown) {
         MConstant *constant = MConstant::New(alloc(), BooleanValue(isCallableConstant));
         current->add(constant);
         current->push(constant);
         return InliningStatus_Inlined;
     }
 
-    callInfo.unwrapArgs();
-
     MIsCallable *isCallable = MIsCallable::New(alloc(), callInfo.getArg(0));
     current->add(isCallable);
     current->push(isCallable);
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
@@ -1482,40 +1482,40 @@ IonBuilder::inlineToObject(CallInfo &cal
         return InliningStatus_NotInlined;
 
     // If we know the input type is an object, nop ToObject.
     if (getInlineReturnType() != MIRType_Object)
         return InliningStatus_NotInlined;
     if (callInfo.getArg(0)->type() != MIRType_Object)
         return InliningStatus_NotInlined;
 
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
     MDefinition *object = callInfo.getArg(0);
 
     current->push(object);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineBailout(CallInfo &callInfo)
 {
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     current->add(MBail::New(alloc()));
 
     MConstant *undefined = MConstant::New(alloc(), UndefinedValue());
     current->add(undefined);
     current->push(undefined);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineAssertFloat32(CallInfo &callInfo)
 {
-    callInfo.unwrapArgs();
+    callInfo.setFoldedUnchecked();
 
     MDefinition *secondArg = callInfo.getArg(1);
 
     JS_ASSERT(secondArg->type() == MIRType_Boolean);
     JS_ASSERT(secondArg->isConstant());
 
     bool mustBeFloat32 = JSVAL_TO_BOOLEAN(secondArg->toConstant()->value());
     current->add(MAssertFloat32::New(alloc(), callInfo.getArg(0), mustBeFloat32));
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1011,39 +1011,22 @@ MPhi::addInputSlow(MDefinition *ins, boo
             MUse *use = &inputs_[i];
             use->producer()->addUse(use);
         }
     }
 
     return true;
 }
 
-uint32_t
-MPrepareCall::argc() const
-{
-    JS_ASSERT(hasOneUse());
-    MCall *call = usesBegin()->consumer()->toDefinition()->toCall();
-    return call->numStackArgs();
-}
-
 void
-MPassArg::printOpcode(FILE *fp) const
-{
-    PrintOpcodeName(fp, op());
-    fprintf(fp, " %d ", argnum_);
-    getOperand(0)->printName(fp);
-}
-
-void
-MCall::addArg(size_t argnum, MPassArg *arg)
+MCall::addArg(size_t argnum, MDefinition *arg)
 {
     // The operand vector is initialized in reverse order by the IonBuilder.
     // It cannot be checked for consistency until all arguments are added.
-    arg->setArgnum(argnum);
-    setOperand(argnum + NumNonArgumentOperands, arg->toDefinition());
+    setOperand(argnum + NumNonArgumentOperands, arg);
 }
 
 void
 MBitNot::infer()
 {
     if (getOperand(0)->mightBeType(MIRType_Object))
         specialization_ = MIRType_None;
     else
@@ -2105,20 +2088,16 @@ MResumePoint::MResumePoint(MBasicBlock *
     block->addResumePoint(this);
 }
 
 void
 MResumePoint::inherit(MBasicBlock *block)
 {
     for (size_t i = 0; i < stackDepth(); i++) {
         MDefinition *def = block->getSlot(i);
-        // We have to unwrap MPassArg: it's removed when inlining calls
-        // and LStackArg does not define a value.
-        if (def->isPassArg())
-            def = def->toPassArg()->getArgument();
         setOperand(i, def);
     }
 }
 
 MDefinition *
 MToInt32::foldsTo(TempAllocator &alloc, bool useValueNumbers)
 {
     MDefinition *input = getOperand(0);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1742,36 +1742,16 @@ class MInitElemGetterSetter
     MDefinition *value() const {
         return getOperand(2);
     }
     TypePolicy *typePolicy() {
         return this;
     }
 };
 
-// Designates the start of call frame construction.
-// Generates code to adjust the stack pointer for the argument vector.
-// Argc is inferred by checking the use chain during lowering.
-class MPrepareCall : public MNullaryInstruction
-{
-  public:
-    INSTRUCTION_HEADER(PrepareCall)
-
-    static MPrepareCall *New(TempAllocator &alloc) {
-        return new(alloc) MPrepareCall();
-    }
-
-    // Get the vector size for the upcoming call by looking at the call.
-    uint32_t argc() const;
-
-    AliasSet getAliasSet() const {
-        return AliasSet::None();
-    }
-};
-
 class MVariadicInstruction : public MInstruction
 {
     FixedList<MUse> operands_;
 
   protected:
     bool init(TempAllocator &alloc, size_t length) {
         return operands_.init(alloc, length);
     }
@@ -1796,19 +1776,18 @@ class MVariadicInstruction : public MIns
 
 class MCall
   : public MVariadicInstruction,
     public CallPolicy
 {
   private:
     // An MCall uses the MPrepareCall, MDefinition for the function, and
     // MPassArg instructions. They are stored in the same list.
-    static const size_t PrepareCallOperandIndex  = 0;
-    static const size_t FunctionOperandIndex   = 1;
-    static const size_t NumNonArgumentOperands = 2;
+    static const size_t FunctionOperandIndex   = 0;
+    static const size_t NumNonArgumentOperands = 1;
 
   protected:
     // True if the call is for JSOP_NEW.
     bool construct_;
     // Monomorphic cache of single target from TI, or nullptr.
     CompilerRootFunction target_;
     // Original value of argc from the bytecode.
     uint32_t numActualArgs_;
@@ -1824,59 +1803,53 @@ class MCall
         setResultType(MIRType_Value);
     }
 
   public:
     INSTRUCTION_HEADER(Call)
     static MCall *New(TempAllocator &alloc, JSFunction *target, size_t maxArgc, size_t numActualArgs,
                       bool construct);
 
-    void initPrepareCall(MDefinition *start) {
-        JS_ASSERT(start->isPrepareCall());
-        return setOperand(PrepareCallOperandIndex, start);
-    }
     void initFunction(MDefinition *func) {
-        JS_ASSERT(!func->isPassArg());
         return setOperand(FunctionOperandIndex, func);
     }
 
     bool needsArgCheck() const {
         return needsArgCheck_;
     }
 
     void disableArgCheck() {
         needsArgCheck_ = false;
     }
-
-    MPrepareCall *getPrepareCall() {
-        return getOperand(PrepareCallOperandIndex)->toPrepareCall();
-    }
     MDefinition *getFunction() const {
         return getOperand(FunctionOperandIndex);
     }
     void replaceFunction(MInstruction *newfunc) {
         replaceOperand(FunctionOperandIndex, newfunc);
     }
 
-    void addArg(size_t argnum, MPassArg *arg);
+    void addArg(size_t argnum, MDefinition *arg);
 
     MDefinition *getArg(uint32_t index) const {
         return getOperand(NumNonArgumentOperands + index);
     }
 
     void replaceArg(uint32_t index, MDefinition *def) {
         replaceOperand(NumNonArgumentOperands + index, def);
     }
 
     static size_t IndexOfThis() {
         return NumNonArgumentOperands;
     }
     static size_t IndexOfArgument(size_t index) {
         return NumNonArgumentOperands + index + 1; // +1 to skip |this|.
     }
+    static size_t IndexOfStackArg(size_t index) {
+        return NumNonArgumentOperands + index;
+    }
 
     // For TI-informed monomorphic callsites.
     JSFunction *getSingleTarget() const {
         return target_;
     }
 
     bool isConstructing() const {
         return construct_;
@@ -2798,65 +2771,16 @@ class MReturnFromCtor
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
     TypePolicy *typePolicy() {
         return this;
     }
 };
 
-// Passes an MDefinition to an MCall. Must occur between an MPrepareCall and
-// MCall. Boxes the input and stores it to the correct location on stack.
-//
-// Arguments are *not* simply pushed onto a call stack: they are evaluated
-// left-to-right, but stored in the arg vector in C-style, right-to-left.
-class MPassArg
-  : public MUnaryInstruction,
-    public NoFloatPolicy<0>
-{
-    int32_t argnum_;
-
-  private:
-    MPassArg(MDefinition *def)
-      : MUnaryInstruction(def), argnum_(-1)
-    {
-        setResultType(def->type());
-        setResultTypeSet(def->resultTypeSet());
-    }
-
-  public:
-    INSTRUCTION_HEADER(PassArg)
-    static MPassArg *New(TempAllocator &alloc, MDefinition *def)
-    {
-        return new(alloc) MPassArg(def);
-    }
-
-    MDefinition *getArgument() const {
-        return getOperand(0);
-    }
-
-    // Set by the MCall.
-    void setArgnum(uint32_t argnum) {
-        argnum_ = argnum;
-        JS_ASSERT(argnum_ >= 0);
-    }
-    uint32_t getArgnum() const {
-        JS_ASSERT(argnum_ >= 0);
-        return (uint32_t)argnum_;
-    }
-    AliasSet getAliasSet() const {
-        return AliasSet::None();
-    }
-    void printOpcode(FILE *fp) const;
-
-    TypePolicy *typePolicy() {
-        return this;
-    }
-};
-
 // Converts a primitive (either typed or untyped) to a double. If the input is
 // not primitive at runtime, a bailout occurs.
 class MToDouble
   : public MUnaryInstruction,
     public ToDoublePolicy
 {
   public:
     // Types of values which can be converted.
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -32,18 +32,16 @@ namespace jit {
     _(DefFun)                                                               \
     _(CreateThis)                                                           \
     _(CreateThisWithProto)                                                  \
     _(CreateThisWithTemplate)                                               \
     _(CreateArgumentsObject)                                                \
     _(GetArgumentsObjectArg)                                                \
     _(SetArgumentsObjectArg)                                                \
     _(ComputeThis)                                                          \
-    _(PrepareCall)                                                          \
-    _(PassArg)                                                              \
     _(Call)                                                                 \
     _(ApplyArgs)                                                            \
     _(Bail)                                                                 \
     _(AssertFloat32)                                                        \
     _(GetDynamicName)                                                       \
     _(FilterArgumentsOrEval)                                                \
     _(CallDirectEval)                                                       \
     _(BitNot)                                                               \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -130,18 +130,16 @@ class ParallelSafetyVisitor : public MIn
     UNSAFE_OP(DefFun)
     UNSAFE_OP(CreateThis)
     CUSTOM_OP(CreateThisWithTemplate)
     UNSAFE_OP(CreateThisWithProto)
     UNSAFE_OP(CreateArgumentsObject)
     UNSAFE_OP(GetArgumentsObjectArg)
     UNSAFE_OP(SetArgumentsObjectArg)
     UNSAFE_OP(ComputeThis)
-    SAFE_OP(PrepareCall)
-    SAFE_OP(PassArg)
     CUSTOM_OP(Call)
     UNSAFE_OP(ApplyArgs)
     UNSAFE_OP(Bail)
     UNSAFE_OP(AssertFloat32)
     UNSAFE_OP(GetDynamicName)
     UNSAFE_OP(FilterArgumentsOrEval)
     UNSAFE_OP(CallDirectEval)
     SAFE_OP(BitNot)
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -2064,20 +2064,16 @@ RangeAnalysis::addRangeAssertions()
             }
 
             Range r(ins);
 
             // Don't insert assertions if there's nothing interesting to assert.
             if (r.isUnknown() || (ins->type() == MIRType_Int32 && r.isUnknownInt32()))
                 continue;
 
-            // Range-checking PassArgs breaks stuff.
-            if (ins->isPassArg())
-                continue;
-
             MAssertRange *guard = MAssertRange::New(alloc(), ins, new(alloc()) Range(r));
 
             // The code that removes beta nodes assumes that it can find them
             // in a contiguous run at the top of each block. Don't insert
             // range assertions in between beta nodes.
             MInstructionIterator insertIter = iter;
             while (insertIter->isBeta())
                 insertIter++;
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -520,26 +520,32 @@ Float32Policy<Op>::staticAdjustInputs(Te
     def->replaceOperand(Op, replace);
     return true;
 }
 
 template bool Float32Policy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 template bool Float32Policy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 template bool Float32Policy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 
+static void
+EnsureOperandNotFloat32(TempAllocator &alloc, MInstruction *def, unsigned op)
+{
+    MDefinition *in = def->getOperand(op);
+    if (in->type() == MIRType_Float32) {
+        MToDouble *replace = MToDouble::New(alloc, in);
+        def->block()->insertBefore(def, replace);
+        def->replaceOperand(op, replace);
+    }
+}
+
 template <unsigned Op>
 bool
 NoFloatPolicy<Op>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def)
 {
-    MDefinition *in = def->getOperand(Op);
-    if (in->type() == MIRType_Float32) {
-        MToDouble *replace = MToDouble::New(alloc, in);
-        def->block()->insertBefore(def, replace);
-        def->replaceOperand(Op, replace);
-    }
+    EnsureOperandNotFloat32(alloc, def, Op);
     return true;
 }
 
 template bool NoFloatPolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 template bool NoFloatPolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 template bool NoFloatPolicy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 template bool NoFloatPolicy<3>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 
@@ -635,27 +641,29 @@ template bool ObjectPolicy<2>::staticAdj
 template bool ObjectPolicy<3>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins);
 
 bool
 CallPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
     MCall *call = ins->toCall();
 
     MDefinition *func = call->getFunction();
-    if (func->type() == MIRType_Object)
-        return true;
+    if (func->type() != MIRType_Object) {
+        // If the function is impossible to call,
+        // bail out by causing a subsequent unbox to fail.
+        if (func->type() != MIRType_Value)
+            func = boxAt(alloc, call, func);
 
-    // If the function is impossible to call,
-    // bail out by causing a subsequent unbox to fail.
-    if (func->type() != MIRType_Value)
-        func = boxAt(alloc, call, func);
+        MInstruction *unbox = MUnbox::New(alloc, func, MIRType_Object, MUnbox::Fallible);
+        call->block()->insertBefore(call, unbox);
+        call->replaceFunction(unbox);
+    }
 
-    MInstruction *unbox = MUnbox::New(alloc, func, MIRType_Object, MUnbox::Fallible);
-    call->block()->insertBefore(call, unbox);
-    call->replaceFunction(unbox);
+    for (uint32_t i = 0; i < call->numStackArgs(); i++)
+        EnsureOperandNotFloat32(alloc, call, MCall::IndexOfStackArg(i));
 
     return true;
 }
 
 bool
 CallSetElementPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
     // The first operand should be an object.
--- a/js/src/jit/UnreachableCodeElimination.cpp
+++ b/js/src/jit/UnreachableCodeElimination.cpp
@@ -283,32 +283,16 @@ UnreachableCodeElimination::removeUnmark
                                 redundantPhis_ = true;
                                 break;
                             }
                         }
                     }
                 }
             }
 
-            // When we remove a call, we can't leave the corresponding MPassArg
-            // in the graph. Since lowering will fail. Replace it with the
-            // argument for the exceptional case when it is kept alive in a
-            // ResumePoint. DCE will remove the unused MPassArg instruction.
-            for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) {
-                if (iter->isCall()) {
-                    MCall *call = iter->toCall();
-                    for (size_t i = 0; i < call->numStackArgs(); i++) {
-                        JS_ASSERT(call->getArg(i)->isPassArg());
-                        JS_ASSERT(call->getArg(i)->hasOneDefUse());
-                        MPassArg *arg = call->getArg(i)->toPassArg();
-                        arg->replaceAllUsesWith(arg->getArgument());
-                    }
-                }
-            }
-
             graph_.removeBlock(block);
         }
     }
 
     JS_ASSERT(id == 0);
 
     return true;
 }
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -135,20 +135,16 @@ CodeGeneratorShared::encodeSlots(LSnapsh
                                  uint32_t *startIndex)
 {
     IonSpew(IonSpew_Codegen, "Encoding %u of resume point %p's operands starting from %u",
             resumePoint->numOperands(), (void *) resumePoint, *startIndex);
     for (uint32_t slotno = 0, e = resumePoint->numOperands(); slotno < e; slotno++) {
         uint32_t i = slotno + *startIndex;
         MDefinition *mir = resumePoint->getOperand(slotno);
 
-        if (mir->isPassArg())
-            mir = mir->toPassArg()->getArgument();
-        JS_ASSERT(!mir->isPassArg());
-
         if (mir->isBox())
             mir = mir->toBox()->getOperand(0);
 
         MIRType type = mir->isUnused()
                        ? MIRType_Undefined
                        : mir->type();
 
         switch (type) {
--- a/js/src/jit/shared/Lowering-shared.cpp
+++ b/js/src/jit/shared/Lowering-shared.cpp
@@ -72,20 +72,16 @@ LIRGeneratorShared::buildSnapshot(LInstr
     for (MResumePoint **it = iter.begin(), **end = iter.end(); it != end; ++it) {
         MResumePoint *mir = *it;
         for (size_t j = 0, e = mir->numOperands(); j < e; ++i, ++j) {
             MDefinition *ins = mir->getOperand(j);
 
             LAllocation *type = snapshot->typeOfSlot(i);
             LAllocation *payload = snapshot->payloadOfSlot(i);
 
-            if (ins->isPassArg())
-                ins = ins->toPassArg()->getArgument();
-            JS_ASSERT(!ins->isPassArg());
-
             if (ins->isBox())
                 ins = ins->toBox()->getOperand(0);
 
             // Guards should never be eliminated.
             JS_ASSERT_IF(ins->isUnused(), !ins->isGuard());
 
             // Snapshot operands other than constants should never be
             // emitted-at-uses. Try-catch support depends on there being no
@@ -127,20 +123,16 @@ LIRGeneratorShared::buildSnapshot(LInstr
         return nullptr;
 
     size_t i = 0;
     for (MResumePoint **it = iter.begin(), **end = iter.end(); it != end; ++it) {
         MResumePoint *mir = *it;
         for (size_t j = 0, e = mir->numOperands(); j < e; ++i, ++j) {
             MDefinition *def = mir->getOperand(j);
 
-            if (def->isPassArg())
-                def = def->toPassArg()->getArgument();
-            JS_ASSERT(!def->isPassArg());
-
             if (def->isBox())
                 def = def->toBox()->getOperand(0);
 
             // Guards should never be eliminated.
             JS_ASSERT_IF(def->isUnused(), !def->isGuard());
 
             // Snapshot operands other than constants should never be
             // emitted-at-uses. Try-catch support depends on there being no