Bug 921120 - Enable Ion-compilation of JSOP_SETARG for functions which use magic arguments. r=nbp
authorKannan Vijayan <kvijayan@mozilla.com>
Mon, 30 Sep 2013 10:24:30 -0400
changeset 163087 2963a336e7ec57d6e9d4ed3d8f6ada5805306e61
parent 163086 4184c8516ee5826f712241a598acfdb22ae67487
child 163088 2c7230203ec219434acc19bc5c4d9a09369da4ce
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs921120
milestone27.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 921120 - Enable Ion-compilation of JSOP_SETARG for functions which use magic arguments. r=nbp
js/src/jit/BytecodeAnalysis.cpp
js/src/jit/BytecodeAnalysis.h
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/CompileInfo.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/LIR-Common.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/jsscript.h
--- a/js/src/jit/BytecodeAnalysis.cpp
+++ b/js/src/jit/BytecodeAnalysis.cpp
@@ -161,16 +161,20 @@ BytecodeAnalysis::init(JSContext *cx)
           case JSOP_SETCONST:
             usesScopeChain_ = true;
             break;
 
           case JSOP_FINALLY:
             hasTryFinally_ = true;
             break;
 
+          case JSOP_SETARG:
+            hasSetArg_ = true;
+            break;
+
           default:
             break;
         }
 
         bool jump = IsJumpOpcode(op);
         if (jump) {
             // Case instructions do not push the lvalue back when branching.
             unsigned newStackDepth = stackDepth;
--- a/js/src/jit/BytecodeAnalysis.h
+++ b/js/src/jit/BytecodeAnalysis.h
@@ -37,16 +37,17 @@ struct BytecodeInfo
 
 class BytecodeAnalysis
 {
     JSScript *script_;
     Vector<BytecodeInfo, 0, IonAllocPolicy> infos_;
 
     bool usesScopeChain_;
     bool hasTryFinally_;
+    bool hasSetArg_;
 
   public:
     explicit BytecodeAnalysis(JSScript *script);
 
     bool init(JSContext *cx);
 
     BytecodeInfo &info(jsbytecode *pc) {
         JS_ASSERT(infos_[pc - script_->code].initialized);
@@ -54,22 +55,26 @@ class BytecodeAnalysis
     }
 
     BytecodeInfo *maybeInfo(jsbytecode *pc) {
         if (infos_[pc - script_->code].initialized)
             return &infos_[pc - script_->code];
         return nullptr;
     }
 
-    bool usesScopeChain() {
+    bool usesScopeChain() const {
         return usesScopeChain_;
     }
 
-    bool hasTryFinally() {
+    bool hasTryFinally() const {
         return hasTryFinally_;
     }
+
+    bool hasSetArg() const {
+        return hasSetArg_;
+    }
 };
 
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_BytecodeAnalysis_h */
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -5450,17 +5450,17 @@ CodeGenerator::visitArgumentsLength(LArg
     Register argc = ToRegister(lir->output());
     Address ptr(StackPointer, frameSize() + IonJSFrameLayout::offsetOfNumActualArgs());
 
     masm.loadPtr(ptr, argc);
     return true;
 }
 
 bool
-CodeGenerator::visitGetArgument(LGetArgument *lir)
+CodeGenerator::visitGetFrameArgument(LGetFrameArgument *lir)
 {
     ValueOperand result = GetValueOutput(lir);
     const LAllocation *index = lir->index();
     size_t argvOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs();
 
     if (index->isConstant()) {
         int32_t i = index->toConstant()->toInt32();
         Address argPtr(StackPointer, sizeof(Value) * i + argvOffset);
@@ -5468,16 +5468,55 @@ CodeGenerator::visitGetArgument(LGetArgu
     } else {
         Register i = ToRegister(index);
         BaseIndex argPtr(StackPointer, i, ScaleFromElemWidth(sizeof(Value)), argvOffset);
         masm.loadValue(argPtr, result);
     }
     return true;
 }
 
+bool
+CodeGenerator::visitSetFrameArgumentT(LSetFrameArgumentT *lir)
+{
+    size_t argOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs() +
+                       (sizeof(Value) * lir->mir()->argno());
+
+    MIRType type = lir->mir()->value()->type();
+
+    if (type == MIRType_Double) {
+        // Store doubles directly.
+        FloatRegister input = ToFloatRegister(lir->input());
+        masm.storeDouble(input, Address(StackPointer, argOffset));
+
+    } else {
+        Register input = ToRegister(lir->input());
+        masm.storeValue(ValueTypeFromMIRType(type), input, Address(StackPointer, argOffset));
+    }
+    return true;
+}
+
+bool
+CodeGenerator:: visitSetFrameArgumentC(LSetFrameArgumentC *lir)
+{
+    size_t argOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs() +
+                       (sizeof(Value) * lir->mir()->argno());
+    masm.storeValue(lir->val(), Address(StackPointer, argOffset));
+    return true;
+}
+
+bool
+CodeGenerator:: visitSetFrameArgumentV(LSetFrameArgumentV *lir)
+{
+    const ValueOperand val = ToValue(lir, LSetFrameArgumentV::Input);
+    size_t argOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs() +
+                       (sizeof(Value) * lir->mir()->argno());
+    masm.storeValue(val, Address(StackPointer, argOffset));
+    return true;
+}
+
 typedef bool (*RunOnceScriptPrologueFn)(JSContext *, HandleScript);
 static const VMFunction RunOnceScriptPrologueInfo =
     FunctionInfo<RunOnceScriptPrologueFn>(js::RunOnceScriptPrologue);
 
 bool
 CodeGenerator::visitRunOncePrologue(LRunOncePrologue *lir)
 {
     pushArg(ImmGCPtr(lir->mir()->block()->info().script()));
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -232,17 +232,20 @@ class CodeGenerator : public CodeGenerat
     bool visitClampDToUint8(LClampDToUint8 *lir);
     bool visitClampVToUint8(LClampVToUint8 *lir);
     bool visitCallIteratorStart(LCallIteratorStart *lir);
     bool visitIteratorStart(LIteratorStart *lir);
     bool visitIteratorNext(LIteratorNext *lir);
     bool visitIteratorMore(LIteratorMore *lir);
     bool visitIteratorEnd(LIteratorEnd *lir);
     bool visitArgumentsLength(LArgumentsLength *lir);
-    bool visitGetArgument(LGetArgument *lir);
+    bool visitGetFrameArgument(LGetFrameArgument *lir);
+    bool visitSetFrameArgumentT(LSetFrameArgumentT *lir);
+    bool visitSetFrameArgumentC(LSetFrameArgumentC *lir);
+    bool visitSetFrameArgumentV(LSetFrameArgumentV *lir);
     bool visitRunOncePrologue(LRunOncePrologue *lir);
     bool emitRest(LInstruction *lir, Register array, Register numActuals,
                   Register temp0, Register temp1, unsigned numFormals,
                   JSObject *templateObject);
     bool visitRest(LRest *lir);
     bool visitRestPar(LRestPar *lir);
     bool visitCallSetProperty(LCallSetProperty *ins);
     bool visitCallDeleteProperty(LCallDeleteProperty *lir);
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -233,16 +233,19 @@ class CompileInfo
 
         JS_ASSERT(index >= firstStackSlot());
         return false;
     }
 
     bool hasArguments() const {
         return script()->argumentsHasVarBinding();
     }
+    bool argumentsAliasesFormals() const {
+        return script()->argumentsAliasesFormals();
+    }
     bool needsArgsObj() const {
         return script()->needsArgsObj();
     }
     bool argsObjAliasesFormals() const {
         return script()->argsObjAliasesFormals();
     }
 
     ExecutionMode executionMode() const {
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1383,59 +1383,17 @@ IonBuilder::inspectOpcode(JSOp op)
             current->add(getArg);
             current->push(getArg);
         } else {
             current->pushArg(GET_SLOTNO(pc));
         }
         return true;
 
       case JSOP_SETARG:
-        // To handle this case, we should spill the arguments to the space where
-        // actual arguments are stored. The tricky part is that if we add a MIR
-        // to wrap the spilling action, we don't want the spilling to be
-        // captured by the GETARG and by the resume point, only by
-        // MGetArgument.
-        if (info().argsObjAliasesFormals()) {
-            current->add(MSetArgumentsObjectArg::New(current->argumentsObject(), GET_SLOTNO(pc),
-                                                     current->peek(-1)));
-        } else {
-            // TODO: if hasArguments() is true, and the script has a JSOP_SETARG, then
-            // convert all arg accesses to go through the arguments object.
-            if (info().hasArguments())
-                return abort("NYI: arguments & setarg.");
-
-            int32_t arg = GET_SLOTNO(pc);
-
-            // If this assignment is at the start of the function and is coercing
-            // the original value for the argument which was passed in, loosen
-            // the type information for that original argument if it is currently
-            // empty due to originally executing in the interpreter.
-            MDefinition *value = current->peek(-1);
-            if (graph().numBlocks() == 1 &&
-                (value->isBitOr() || value->isBitAnd() || value->isMul() /* for JSOP_POS */))
-             {
-                 for (size_t i = 0; i < value->numOperands(); i++) {
-                    MDefinition *op = value->getOperand(i);
-                    if (op->isParameter() &&
-                        op->toParameter()->index() == arg &&
-                        op->resultTypeSet() &&
-                        op->resultTypeSet()->empty())
-                    {
-                        types::TypeSet *argTypes = types::TypeScript::ArgTypes(script(), arg);
-
-                        // Update both the original and cloned type set.
-                        argTypes->addType(cx, types::Type::UnknownType());
-                        op->resultTypeSet()->addType(cx, types::Type::UnknownType());
-                    }
-                }
-            }
-
-            current->setArg(arg);
-        }
-        return true;
+        return jsop_setarg(GET_SLOTNO(pc));
 
       case JSOP_GETLOCAL:
       case JSOP_CALLLOCAL:
         current->pushLocal(GET_SLOTNO(pc));
         return true;
 
       case JSOP_SETLOCAL:
         current->setLocal(GET_SLOTNO(pc));
@@ -3374,17 +3332,17 @@ IonBuilder::jsop_try()
     if (!js_IonOptions.compileTryCatch)
         return abort("Try-catch support disabled");
 
     // Try-finally is not yet supported.
     if (analysis().hasTryFinally())
         return abort("Has try-finally");
 
     // Try-catch within inline frames is not yet supported.
-    if (callerBuilder_)
+    if (isInlineBuilder())
         return abort("try-catch within inline frame");
 
     graph().setHasTryBlock();
 
     jssrcnote *sn = info().getNote(gsn, pc);
     JS_ASSERT(SN_TYPE(sn) == SRC_TRY);
 
     // Get the pc of the last instruction in the try block. It's a JSOP_GOTO to
@@ -3481,17 +3439,17 @@ IonBuilder::processReturn(JSOp op)
     setCurrent(nullptr);
     return processControlEnd();
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processThrow()
 {
     // JSOP_THROW can't be compiled within inlined frames.
-    if (callerBuilder_)
+    if (isInlineBuilder())
         return ControlStatus_Abort;
 
     MDefinition *def = current->pop();
 
     if (graph().hasTryBlock()) {
         // MThrow is not marked as effectful. This means when it throws and we
         // are inside a try block, we could use an earlier resume point and this
         // resume point may not be up-to-date, for example:
@@ -6736,17 +6694,17 @@ IonBuilder::getElemTryArguments(bool *em
     JS_ASSERT(*emitted == false);
 
     if (inliningDepth_ > 0)
         return true;
 
     if (obj->type() != MIRType_Magic)
         return true;
 
-    // Emit GetArgument.
+    // Emit GetFrameArgument.
 
     JS_ASSERT(!info().argsObjAliasesFormals());
 
     // Type Inference has guaranteed this is an optimized arguments object.
     obj->setFoldedUnchecked();
 
     // To ensure that we are not looking above the number of actual arguments.
     MArgumentsLength *length = MArgumentsLength::New();
@@ -6756,17 +6714,17 @@ IonBuilder::getElemTryArguments(bool *em
     MInstruction *idInt32 = MToInt32::New(index);
     current->add(idInt32);
     index = idInt32;
 
     // Bailouts if we read more than the number of actual arguments.
     index = addBoundsCheck(index, length);
 
     // Load the argument from the actual arguments.
-    MGetArgument *load = MGetArgument::New(index);
+    MGetFrameArgument *load = MGetFrameArgument::New(index, analysis_.hasSetArg());
     current->add(load);
     current->push(load);
 
     types::TemporaryTypeSet *types = bytecodeTypes(pc);
     if (!pushTypeBarrier(load, types, true))
         return false;
 
     *emitted = true;
@@ -9078,16 +9036,77 @@ IonBuilder::jsop_lambda(JSFunction *fun)
     MLambda *ins = MLambda::New(current->scopeChain(), fun);
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 bool
+IonBuilder::jsop_setarg(uint32_t arg)
+{
+    // To handle this case, we should spill the arguments to the space where
+    // actual arguments are stored. The tricky part is that if we add a MIR
+    // to wrap the spilling action, we don't want the spilling to be
+    // captured by the GETARG and by the resume point, only by
+    // MGetFrameArgument.
+    JS_ASSERT(analysis_.hasSetArg());
+    MDefinition *val = current->peek(-1);
+
+    // If an arguments object is in use, and it aliases formals, then all SETARGs
+    // must go through the arguments object.
+    if (info().argsObjAliasesFormals()) {
+        current->add(MSetArgumentsObjectArg::New(current->argumentsObject(), GET_SLOTNO(pc), val));
+        return true;
+    }
+
+    // Otherwise, if a magic arguments is in use, and it aliases formals, and there exist
+    // arguments[...] GETELEM expressions in the script, then SetFrameArgument must be used.
+    // If no arguments[...] GETELEM expressions are in the script, and an argsobj is not
+    // required, then it means that any aliased argument set can never be observed, and
+    // the frame does not actually need to be updated with the new arg value.
+    if (info().argumentsAliasesFormals()) {
+        // Try-catch within inline frames is not yet supported.
+        if (isInlineBuilder())
+            return abort("JSOP_SETARG with magic arguments in inlined function.");
+
+        MSetFrameArgument *store = MSetFrameArgument::New(arg, val);
+        current->add(store);
+        current->setArg(arg);
+        return true;
+    }
+
+    // If this assignment is at the start of the function and is coercing
+    // the original value for the argument which was passed in, loosen
+    // the type information for that original argument if it is currently
+    // empty due to originally executing in the interpreter.
+    if (graph().numBlocks() == 1 &&
+        (val->isBitOr() || val->isBitAnd() || val->isMul() /* for JSOP_POS */))
+     {
+         for (size_t i = 0; i < val->numOperands(); i++) {
+            MDefinition *op = val->getOperand(i);
+            if (op->isParameter() &&
+                op->toParameter()->index() == (int32_t)arg &&
+                op->resultTypeSet() &&
+                op->resultTypeSet()->empty())
+            {
+                types::TypeSet *argTypes = types::TypeScript::ArgTypes(script(), arg);
+
+                // Update both the original and cloned type set.
+                argTypes->addType(cx, types::Type::UnknownType());
+                op->resultTypeSet()->addType(cx, types::Type::UnknownType());
+            }
+        }
+    }
+
+    current->setArg(arg);
+    return true;
+}
+
+bool
 IonBuilder::jsop_defvar(uint32_t index)
 {
     JS_ASSERT(JSOp(*pc) == JSOP_DEFVAR || JSOp(*pc) == JSOP_DEFCONST);
 
     PropertyName *name = script()->getName(index);
 
     // Bake in attrs.
     unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -442,16 +442,17 @@ class IonBuilder : public MIRGenerator
 
     bool jsop_add(MDefinition *left, MDefinition *right);
     bool jsop_bitnot();
     bool jsop_bitop(JSOp op);
     bool jsop_binary(JSOp op);
     bool jsop_binary(JSOp op, MDefinition *left, MDefinition *right);
     bool jsop_pos();
     bool jsop_neg();
+    bool jsop_setarg(uint32_t arg);
     bool jsop_defvar(uint32_t index);
     bool jsop_deffun(uint32_t index);
     bool jsop_notearg();
     bool jsop_funcall(uint32_t argc);
     bool jsop_funapply(uint32_t argc);
     bool jsop_funapplyarguments(uint32_t argc);
     bool jsop_call(uint32_t argc, bool constructing);
     bool jsop_eval(uint32_t argc);
@@ -668,16 +669,20 @@ class IonBuilder : public MIRGenerator
 
     CodeGenerator *backgroundCodegen() const { return backgroundCodegen_; }
     void setBackgroundCodegen(CodeGenerator *codegen) { backgroundCodegen_ = codegen; }
 
     AbortReason abortReason() { return abortReason_; }
 
     TypeRepresentationSetHash *getOrCreateReprSetHash(); // fallible
 
+    bool isInlineBuilder() const {
+        return callerBuilder_ != NULL;
+    }
+
   private:
     bool init();
 
     JSContext *cx;
     BaselineFrame *baselineFrame_;
     AbortReason abortReason_;
     ScopedJSDeletePtr<TypeRepresentationSetHash> reprSetHash_;
 
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -4547,30 +4547,82 @@ class LIteratorEnd : public LInstruction
 // Read the number of actual arguments.
 class LArgumentsLength : public LInstructionHelper<1, 0, 0>
 {
   public:
     LIR_HEADER(ArgumentsLength)
 };
 
 // Load a value from the actual arguments.
-class LGetArgument : public LInstructionHelper<BOX_PIECES, 1, 0>
-{
-  public:
-    LIR_HEADER(GetArgument)
+class LGetFrameArgument : public LInstructionHelper<BOX_PIECES, 1, 0>
+{
+  public:
+    LIR_HEADER(GetFrameArgument)
     BOX_OUTPUT_ACCESSORS()
 
-    LGetArgument(const LAllocation &index) {
+    LGetFrameArgument(const LAllocation &index) {
         setOperand(0, index);
     }
     const LAllocation *index() {
         return getOperand(0);
     }
 };
 
+// Load a value from the actual arguments.
+class LSetFrameArgumentT : public LInstructionHelper<0, 1, 0>
+{
+  public:
+    LIR_HEADER(SetFrameArgumentT)
+
+    LSetFrameArgumentT(const LAllocation &input) {
+        setOperand(0, input);
+    }
+    MSetFrameArgument *mir() const {
+        return mir_->toSetFrameArgument();
+    }
+    const LAllocation *input() {
+        return getOperand(0);
+    }
+};
+
+// Load a value from the actual arguments.
+class LSetFrameArgumentC : public LInstructionHelper<0, 0, 0>
+{
+    Value val_;
+
+  public:
+    LIR_HEADER(SetFrameArgumentC)
+
+    LSetFrameArgumentC(const Value &val) {
+        val_ = val;
+    }
+    MSetFrameArgument *mir() const {
+        return mir_->toSetFrameArgument();
+    }
+    const Value &val() const {
+        return val_;
+    }
+};
+
+// Load a value from the actual arguments.
+class LSetFrameArgumentV : public LInstructionHelper<0, BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(SetFrameArgumentV)
+    BOX_OUTPUT_ACCESSORS()
+
+    LSetFrameArgumentV() {}
+
+    static const size_t Input = 0;
+
+    MSetFrameArgument *mir() const {
+        return mir_->toSetFrameArgument();
+    }
+};
+
 class LRunOncePrologue : public LCallInstructionHelper<0, 0, 0>
 {
   public:
     LIR_HEADER(RunOncePrologue)
 
     MRunOncePrologue *mir() const {
         return mir_->toRunOncePrologue();
     }
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -222,17 +222,20 @@
     _(IteratorMore)                 \
     _(IteratorEnd)                  \
     _(ArrayLength)                  \
     _(TypedArrayLength)             \
     _(TypedArrayElements)           \
     _(TypedObjectElements)          \
     _(StringLength)                 \
     _(ArgumentsLength)              \
-    _(GetArgument)                  \
+    _(GetFrameArgument)             \
+    _(SetFrameArgumentT)            \
+    _(SetFrameArgumentC)            \
+    _(SetFrameArgumentV)            \
     _(RunOncePrologue)              \
     _(Rest)                         \
     _(RestPar)                      \
     _(TypeOfV)                      \
     _(ToIdV)                        \
     _(Floor)                        \
     _(Round)                        \
     _(In)                           \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2909,20 +2909,42 @@ LIRGenerator::visitStringLength(MStringL
 
 bool
 LIRGenerator::visitArgumentsLength(MArgumentsLength *ins)
 {
     return define(new LArgumentsLength(), ins);
 }
 
 bool
-LIRGenerator::visitGetArgument(MGetArgument *ins)
+LIRGenerator::visitGetFrameArgument(MGetFrameArgument *ins)
+{
+    LGetFrameArgument *lir = new LGetFrameArgument(useRegisterOrConstant(ins->index()));
+    return defineBox(lir, ins);
+}
+
+bool
+LIRGenerator::visitSetFrameArgument(MSetFrameArgument *ins)
 {
-    LGetArgument *lir = new LGetArgument(useRegisterOrConstant(ins->index()));
-    return defineBox(lir, ins);
+    MDefinition *input = ins->input();
+
+    if (input->type() == MIRType_Value) {
+        LSetFrameArgumentV *lir = new LSetFrameArgumentV();
+        if (!useBox(lir, LSetFrameArgumentV::Input, input))
+            return false;
+        return add(lir, ins);
+    }
+
+    if (input->type() == MIRType_Undefined || input->type() == MIRType_Null) {
+        Value val = input->type() == MIRType_Undefined ? UndefinedValue() : NullValue();
+        LSetFrameArgumentC *lir = new LSetFrameArgumentC(val);
+        return add(lir, ins);
+    }
+
+    LSetFrameArgumentT *lir = new LSetFrameArgumentT(useRegister(input));
+    return add(lir, ins);
 }
 
 bool
 LIRGenerator::visitRunOncePrologue(MRunOncePrologue *ins)
 {
     LRunOncePrologue *lir = new LRunOncePrologue;
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -226,17 +226,18 @@ class LIRGenerator : public LIRGenerator
     bool visitSetElementCache(MSetElementCache *ins);
     bool visitCallSetProperty(MCallSetProperty *ins);
     bool visitIteratorStart(MIteratorStart *ins);
     bool visitIteratorNext(MIteratorNext *ins);
     bool visitIteratorMore(MIteratorMore *ins);
     bool visitIteratorEnd(MIteratorEnd *ins);
     bool visitStringLength(MStringLength *ins);
     bool visitArgumentsLength(MArgumentsLength *ins);
-    bool visitGetArgument(MGetArgument *ins);
+    bool visitGetFrameArgument(MGetFrameArgument *ins);
+    bool visitSetFrameArgument(MSetFrameArgument *ins);
     bool visitRunOncePrologue(MRunOncePrologue *ins);
     bool visitRest(MRest *ins);
     bool visitRestPar(MRestPar *ins);
     bool visitThrow(MThrow *ins);
     bool visitIn(MIn *ins);
     bool visitInArray(MInArray *ins);
     bool visitInstanceOf(MInstanceOf *ins);
     bool visitCallInstanceOf(MCallInstanceOf *ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -213,22 +213,23 @@ class AliasSet {
     enum Flag {
         None_             = 0,
         ObjectFields      = 1 << 0, // shape, class, slots, length etc.
         Element           = 1 << 1, // A member of obj->elements.
         DynamicSlot       = 1 << 2, // A member of obj->slots.
         FixedSlot         = 1 << 3, // A member of obj->fixedSlots().
         TypedArrayElement = 1 << 4, // A typed array element.
         DOMProperty       = 1 << 5, // A DOM property
-        AsmJSGlobalVar    = 1 << 6, // An asm.js global var
-        AsmJSHeap         = 1 << 7, // An asm.js heap load
+        FrameArgument     = 1 << 6, // An argument kept on the stack frame
+        AsmJSGlobalVar    = 1 << 7, // An asm.js global var
+        AsmJSHeap         = 1 << 8, // An asm.js heap load
         Last              = AsmJSHeap,
         Any               = Last | (Last - 1),
 
-        NumCategories     = 8,
+        NumCategories     = 9,
 
         // Indicates load or store.
         Store_            = 1 << 31
     };
     AliasSet(uint32_t flags)
       : flags_(flags)
     {
         JS_STATIC_ASSERT((1 << NumCategories) - 1 == Any);
@@ -7738,49 +7739,92 @@ class MArgumentsLength : public MNullary
         // Arguments |length| cannot be mutated by Ion Code.
         return AliasSet::None();
    }
 
     void computeRange();
 };
 
 // This MIR instruction is used to get an argument from the actual arguments.
-class MGetArgument
+class MGetFrameArgument
   : public MUnaryInstruction,
     public IntPolicy<0>
 {
-    MGetArgument(MDefinition *idx)
-      : MUnaryInstruction(idx)
+    bool scriptHasSetArg_;
+
+    MGetFrameArgument(MDefinition *idx, bool scriptHasSetArg)
+      : MUnaryInstruction(idx),
+        scriptHasSetArg_(scriptHasSetArg)
     {
         setResultType(MIRType_Value);
         setMovable();
     }
 
   public:
-    INSTRUCTION_HEADER(GetArgument)
-
-    static MGetArgument *New(MDefinition *idx) {
-        return new MGetArgument(idx);
+    INSTRUCTION_HEADER(GetFrameArgument)
+
+    static MGetFrameArgument *New(MDefinition *idx, bool scriptHasSetArg) {
+        return new MGetFrameArgument(idx, scriptHasSetArg);
     }
 
     MDefinition *index() const {
         return getOperand(0);
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
     bool congruentTo(MDefinition *ins) const {
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
+        // If the script doesn't have any JSOP_SETARG ops, then this instruction is never
+        // aliased.
+        if (scriptHasSetArg_)
+            return AliasSet::Load(AliasSet::FrameArgument);
         return AliasSet::None();
     }
 };
 
+// This MIR instruction is used to set an argument value in the frame.
+class MSetFrameArgument
+  : public MUnaryInstruction
+{
+    uint32_t argno_;
+
+    MSetFrameArgument(uint32_t argno, MDefinition *value)
+      : MUnaryInstruction(value),
+        argno_(argno)
+    {
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(SetFrameArgument)
+
+    static MSetFrameArgument *New(uint32_t argno, MDefinition *value) {
+        return new MSetFrameArgument(argno, value);
+    }
+
+    uint32_t argno() const {
+        return argno_;
+    }
+
+    MDefinition *value() const {
+        return getOperand(0);
+    }
+
+    bool congruentTo(MDefinition *ins) const {
+        return false;
+    }
+    AliasSet getAliasSet() const {
+        return AliasSet::Store(AliasSet::FrameArgument);
+    }
+};
+
 class MRestCommon
 {
     unsigned numFormals_;
     CompilerRootObject templateObject_;
 
   protected:
     MRestCommon(unsigned numFormals, JSObject *templateObject)
       : numFormals_(numFormals),
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -160,17 +160,18 @@ namespace jit {
     _(DeleteElement)                                                        \
     _(SetPropertyCache)                                                     \
     _(IteratorStart)                                                        \
     _(IteratorNext)                                                         \
     _(IteratorMore)                                                         \
     _(IteratorEnd)                                                          \
     _(StringLength)                                                         \
     _(ArgumentsLength)                                                      \
-    _(GetArgument)                                                          \
+    _(GetFrameArgument)                                                     \
+    _(SetFrameArgument)                                                     \
     _(RunOncePrologue)                                                      \
     _(Rest)                                                                 \
     _(Floor)                                                                \
     _(Round)                                                                \
     _(In)                                                                   \
     _(InstanceOf)                                                           \
     _(CallInstanceOf)                                                       \
     _(InterruptCheck)                                                       \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -243,17 +243,18 @@ class ParallelSafetyVisitor : public MIn
     UNSAFE_OP(DeleteElement)
     UNSAFE_OP(SetPropertyCache)
     UNSAFE_OP(IteratorStart)
     UNSAFE_OP(IteratorNext)
     UNSAFE_OP(IteratorMore)
     UNSAFE_OP(IteratorEnd)
     SAFE_OP(StringLength)
     SAFE_OP(ArgumentsLength)
-    SAFE_OP(GetArgument)
+    SAFE_OP(GetFrameArgument)
+    UNSAFE_OP(SetFrameArgument)
     UNSAFE_OP(RunOncePrologue)
     CUSTOM_OP(Rest)
     SAFE_OP(RestPar)
     SAFE_OP(Floor)
     SAFE_OP(Round)
     UNSAFE_OP(InstanceOf)
     CUSTOM_OP(InterruptCheck)
     SAFE_OP(ForkJoinSlice)
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -658,16 +658,19 @@ class JSScript : public js::gc::Barriere
     JSCompartment *compartment() const { return compartment_; }
 
     void setVersion(JSVersion v) { version = v; }
 
     /* See ContextFlags::funArgumentsHasLocalBinding comment. */
     bool argumentsHasVarBinding() const { return argsHasVarBinding_; }
     jsbytecode *argumentsBytecode() const { JS_ASSERT(code[0] == JSOP_ARGUMENTS); return code; }
     void setArgumentsHasVarBinding();
+    bool argumentsAliasesFormals() const {
+        return argumentsHasVarBinding() && !strict;
+    }
 
     js::GeneratorKind generatorKind() const {
         return js::GeneratorKindFromBits(generatorKindBits_);
     }
     bool isGenerator() const { return generatorKind() != js::NotGenerator; }
     bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
     bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
     void setGeneratorKind(js::GeneratorKind kind) {