Bug 739572 part 2 - Inline array.push. r=dvander
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 03 Apr 2012 09:45:34 +0200
changeset 112380 7b2c14a9fde2fed11d8834889753f224b5959e7a
parent 112379 cdc5f3570a4ccf0da8f403027d400c6234158368
child 112381 ec53c5d4c3dd2d794c2b7a64b2377c7bd4558dbf
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs739572
milestone14.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 739572 part 2 - Inline array.push. r=dvander
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/IonMacroAssembler.h
js/src/ion/LIR-Common.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/Lowering.h
js/src/ion/MCallOptimize.cpp
js/src/ion/MIR.h
js/src/ion/MOpcodes.h
js/src/ion/VMFunctions.cpp
js/src/ion/VMFunctions.h
js/src/ion/arm/MacroAssembler-arm.h
js/src/ion/x64/MacroAssembler-x64.h
js/src/ion/x86/MacroAssembler-x86.h
js/src/jit-test/tests/ion/inlining/array-push.js
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -1616,16 +1616,74 @@ CodeGenerator::visitArrayPopShiftT(LArra
     Register obj = ToRegister(lir->object());
     Register elements = ToRegister(lir->temp0());
     Register length = ToRegister(lir->temp1());
     TypedOrValueRegister out(lir->mir()->type(), ToAnyRegister(lir->output()));
     return emitArrayPopShift(lir, lir->mir(), obj, elements, length, out);
 }
 
 bool
+CodeGenerator::emitArrayPush(LInstruction *lir, const MArrayPush *mir, Register obj,
+                             ConstantOrRegister value, Register elementsTemp, Register length)
+{
+    typedef bool (*pf)(JSContext *, JSObject *, const Value &, uint32_t *);
+    static const VMFunction Info = FunctionInfo<pf>(ion::ArrayPushDense);
+    OutOfLineCode *ool = oolCallVM(Info, lir, (ArgList(), obj, value), StoreRegisterTo(length));
+    if (!ool)
+        return false;
+
+    // Load elements and length.
+    masm.loadPtr(Address(obj, JSObject::offsetOfElements()), elementsTemp);
+    masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
+
+    Int32Key key = Int32Key(length);
+    Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
+    Address capacity(elementsTemp, ObjectElements::offsetOfCapacity());
+
+    // Guard length == initializedLength.
+    masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
+
+    // Guard length < capacity.
+    masm.branchKey(Assembler::BelowOrEqual, capacity, key, ool->entry());
+
+    masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight));
+
+    masm.bumpKey(&key, 1);
+    masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
+    masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
+
+    masm.bind(ool->rejoin());
+    return true;
+}
+
+bool
+CodeGenerator::visitArrayPushV(LArrayPushV *lir)
+{
+    Register obj = ToRegister(lir->object());
+    Register elementsTemp = ToRegister(lir->temp());
+    Register length = ToRegister(lir->output());
+    ConstantOrRegister value = TypedOrValueRegister(ToValue(lir, LArrayPushV::Value));
+    return emitArrayPush(lir, lir->mir(), obj, value, elementsTemp, length);
+}
+
+bool
+CodeGenerator::visitArrayPushT(LArrayPushT *lir)
+{
+    Register obj = ToRegister(lir->object());
+    Register elementsTemp = ToRegister(lir->temp());
+    Register length = ToRegister(lir->output());
+    ConstantOrRegister value;
+    if (lir->value()->isConstant())
+        value = ConstantOrRegister(*lir->value()->toConstant());
+    else
+        value = TypedOrValueRegister(lir->mir()->value()->type(), ToAnyRegister(lir->value()));
+    return emitArrayPush(lir, lir->mir(), obj, value, elementsTemp, length);
+}
+
+bool
 CodeGenerator::visitCallIteratorStart(LCallIteratorStart *lir)
 {
     typedef JSObject *(*pf)(JSContext *, JSObject *, uint32_t);
     static const VMFunction Info = FunctionInfo<pf>(GetIteratorObject);
 
     pushArg(Imm32(lir->mir()->flags()));
     pushArg(ToRegister(lir->object()));
     return callVM(Info, lir);
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -146,16 +146,20 @@ class CodeGenerator : public CodeGenerat
     bool visitStoreElementT(LStoreElementT *lir);
     bool visitStoreElementV(LStoreElementV *lir);
     bool visitStoreElementHoleT(LStoreElementHoleT *lir);
     bool visitStoreElementHoleV(LStoreElementHoleV *lir);
     bool emitArrayPopShift(LInstruction *lir, const MArrayPopShift *mir, Register obj,
                            Register elementsTemp, Register lengthTemp, TypedOrValueRegister out);
     bool visitArrayPopShiftV(LArrayPopShiftV *lir);
     bool visitArrayPopShiftT(LArrayPopShiftT *lir);
+    bool emitArrayPush(LInstruction *lir, const MArrayPush *mir, Register obj,
+                       ConstantOrRegister value, Register elementsTemp, Register length);
+    bool visitArrayPushV(LArrayPushV *lir);
+    bool visitArrayPushT(LArrayPushT *lir);
     bool visitLoadTypedArrayElement(LLoadTypedArrayElement *lir);
     bool visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole *lir);
     bool visitStoreTypedArrayElement(LStoreTypedArrayElement *lir);
     bool visitClampIToUint8(LClampIToUint8 *lir);
     bool visitClampDToUint8(LClampDToUint8 *lir);
     bool visitClampVToUint8(LClampVToUint8 *lir);
     bool visitOutOfLineLoadTypedArray(OutOfLineLoadTypedArray *ool);
     bool visitCallIteratorStart(LCallIteratorStart *lir);
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -171,30 +171,32 @@ class MacroAssembler : public MacroAssem
                 branchTestMagic(Assembler::Equal, dest.valueReg(), hole);
         } else {
             if (holeCheck)
                 branchTestMagic(Assembler::Equal, src, hole);
             loadUnboxedValue(src, dest.typedReg());
         }
     }
 
-    void storeTypedOrValue(TypedOrValueRegister src, Address address) {
+    template <typename T>
+    void storeTypedOrValue(TypedOrValueRegister src, const T &dest) {
         if (src.hasValue())
-            storeValue(src.valueReg(), address);
+            storeValue(src.valueReg(), dest);
         else if (src.type() == MIRType_Double)
-            storeDouble(src.typedReg().fpu(), address);
+            storeDouble(src.typedReg().fpu(), dest);
         else
-            storeValue(ValueTypeFromMIRType(src.type()), src.typedReg().gpr(), address);
+            storeValue(ValueTypeFromMIRType(src.type()), src.typedReg().gpr(), dest);
     }
 
-    void storeConstantOrRegister(ConstantOrRegister src, Address address) {
+    template <typename T>
+    void storeConstantOrRegister(ConstantOrRegister src, const T &dest) {
         if (src.constant())
-            storeValue(src.value(), address);
+            storeValue(src.value(), dest);
         else
-            storeTypedOrValue(src.reg(), address);
+            storeTypedOrValue(src.reg(), dest);
     }
 
     void storeCallResult(Register reg) {
         if (reg != ReturnReg)
             mov(ReturnReg, reg);
     }
 
     void storeCallResultValue(AnyRegister dest) {
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -1783,16 +1783,70 @@ class LArrayPopShiftT : public LInstruct
     const LDefinition *temp1() {
         return getTemp(1);
     }
     const LDefinition *output() {
         return getDef(0);
     }
 };
 
+class LArrayPushV : public LInstructionHelper<1, 1 + BOX_PIECES, 1>
+{
+  public:
+    LIR_HEADER(ArrayPushV);
+
+    LArrayPushV(const LAllocation &object, const LDefinition &temp) {
+        setOperand(0, object);
+        setTemp(0, temp);
+    }
+
+    static const size_t Value = 1;
+
+    const MArrayPush *mir() const {
+        return mir_->toArrayPush();
+    }
+    const LAllocation *object() {
+        return getOperand(0);
+    }
+    const LDefinition *temp() {
+        return getTemp(0);
+    }
+    const LDefinition *output() {
+        return getDef(0);
+    }
+};
+
+class LArrayPushT : public LInstructionHelper<1, 2, 1>
+{
+  public:
+    LIR_HEADER(ArrayPushT);
+
+    LArrayPushT(const LAllocation &object, const LAllocation &value, const LDefinition &temp) {
+        setOperand(0, object);
+        setOperand(1, value);
+        setTemp(0, temp);
+    }
+
+    const MArrayPush *mir() const {
+        return mir_->toArrayPush();
+    }
+    const LAllocation *object() {
+        return getOperand(0);
+    }
+    const LAllocation *value() {
+        return getOperand(1);
+    }
+    const LDefinition *temp() {
+        return getTemp(0);
+    }
+    const LDefinition *output() {
+        return getDef(0);
+    }
+};
+
 // Load a typed value from a typed array's elements vector.
 class LLoadTypedArrayElement : public LInstructionHelper<1, 2, 1>
 {
   public:
     LIR_HEADER(LoadTypedArrayElement);
 
     LLoadTypedArrayElement(const LAllocation &elements, const LAllocation &index,
                            const LDefinition &temp) {
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -127,16 +127,18 @@
     _(BoundsCheckLower)             \
     _(LoadElementV)                 \
     _(LoadElementT)                 \
     _(LoadElementHole)              \
     _(StoreElementV)                \
     _(StoreElementT)                \
     _(ArrayPopShiftV)               \
     _(ArrayPopShiftT)               \
+    _(ArrayPushV)                   \
+    _(ArrayPushT)                   \
     _(StoreElementHoleV)            \
     _(StoreElementHoleT)            \
     _(LoadTypedArrayElement)        \
     _(LoadTypedArrayElementHole)    \
     _(StoreTypedArrayElement)       \
     _(ClampIToUint8)                \
     _(ClampDToUint8)                \
     _(ClampVToUint8)                \
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -1199,16 +1199,41 @@ LIRGenerator::visitArrayPopShift(MArrayP
       {
         LArrayPopShiftT *lir = new LArrayPopShiftT(object, temp(), temp());
         return define(lir, ins) && assignSafepoint(lir, ins);
       }
     }
 }
 
 bool
+LIRGenerator::visitArrayPush(MArrayPush *ins)
+{
+    JS_ASSERT(ins->type() == MIRType_Int32);
+
+    LUse object = useRegister(ins->object());
+
+    switch (ins->value()->type()) {
+      case MIRType_Value:
+      {
+        LArrayPushV *lir = new LArrayPushV(object, temp());
+        if (!useBox(lir, LArrayPushV::Value, ins->value()))
+            return false;
+        return define(lir, ins) && assignSafepoint(lir, ins);
+      }
+
+      default:
+      {
+        const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
+        LArrayPushT *lir = new LArrayPushT(object, value, temp());
+        return define(lir, ins) && assignSafepoint(lir, ins);
+      }
+    }
+}
+
+bool
 LIRGenerator::visitLoadTypedArrayElement(MLoadTypedArrayElement *ins)
 {
     JS_ASSERT(ins->elements()->type() == MIRType_Elements);
     JS_ASSERT(ins->index()->type() == MIRType_Int32);
 
     const LUse elements = useRegister(ins->elements());
     const LAllocation index = useRegisterOrConstant(ins->index());
 
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -167,16 +167,17 @@ class LIRGenerator : public LIRGenerator
     bool visitNot(MNot *ins);
     bool visitBoundsCheck(MBoundsCheck *ins);
     bool visitBoundsCheckLower(MBoundsCheckLower *ins);
     bool visitLoadElement(MLoadElement *ins);
     bool visitLoadElementHole(MLoadElementHole *ins);
     bool visitStoreElement(MStoreElement *ins);
     bool visitStoreElementHole(MStoreElementHole *ins);
     bool visitArrayPopShift(MArrayPopShift *ins);
+    bool visitArrayPush(MArrayPush *ins);
     bool visitLoadTypedArrayElement(MLoadTypedArrayElement *ins);
     bool visitLoadTypedArrayElementHole(MLoadTypedArrayElementHole *ins);
     bool visitClampToUint8(MClampToUint8 *ins);
     bool visitLoadFixedSlot(MLoadFixedSlot *ins);
     bool visitStoreFixedSlot(MStoreFixedSlot *ins);
     bool visitGetPropertyCache(MGetPropertyCache *ins);
     bool visitGetElementCache(MGetElementCache *ins);
     bool visitBindNameCache(MBindNameCache *ins);
--- a/js/src/ion/MCallOptimize.cpp
+++ b/js/src/ion/MCallOptimize.cpp
@@ -262,16 +262,31 @@ IonBuilder::inlineNativeCall(JSFunction 
                     current->add(ins);
                     current->push(ins);
                     if (!resumeAfter(ins))
                         return false;
                     return true;
                 }
             }
         }
+        if (native == js::array_push) {
+            // Constraints propagating properties into the 'this' object are
+            // generated by TypeConstraintCall during inference.
+            if (thisType == MIRType_Object && returnType == MIRType_Int32 &&
+                !thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) &&
+                !types::ArrayPrototypeHasIndexedProperty(cx, script))
+            {
+                if (!discardCall(argc, argv, current))
+                    return false;
+                MArrayPush *ins = MArrayPush::New(argv[0], argv[1]);
+                current->add(ins);
+                current->push(ins);
+                return resumeAfter(ins);
+            }
+        }
 
         return false;
     }
 
     return false;
 }
 
 } // namespace ion
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -3073,16 +3073,48 @@ class MArrayPopShift
     TypePolicy *typePolicy() {
         return this;
     }
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
     }
 };
 
+// Array.prototype.push on a dense array. Returns the new array length.
+class MArrayPush
+  : public MBinaryInstruction,
+    public SingleObjectPolicy
+{
+    MArrayPush(MDefinition *object, MDefinition *value)
+      : MBinaryInstruction(object, value)
+    {
+        setResultType(MIRType_Int32);
+    }
+
+  public:
+    INSTRUCTION_HEADER(ArrayPush);
+
+    static MArrayPush *New(MDefinition *object, MDefinition *value) {
+        return new MArrayPush(object, value);
+    }
+
+    MDefinition *object() const {
+        return getOperand(0);
+    }
+    MDefinition *value() const {
+        return getOperand(1);
+    }
+    TypePolicy *typePolicy() {
+        return this;
+    }
+    AliasSet getAliasSet() const {
+        return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
+    }
+};
+
 class MLoadTypedArrayElement
   : public MBinaryInstruction
 {
     int arrayType_;
 
     MLoadTypedArrayElement(MDefinition *elements, MDefinition *index, int arrayType)
       : MBinaryInstruction(elements, index), arrayType_(arrayType)
     {
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -119,16 +119,17 @@ namespace ion {
     _(Not)                                                                  \
     _(BoundsCheck)                                                          \
     _(BoundsCheckLower)                                                     \
     _(LoadElement)                                                          \
     _(LoadElementHole)                                                      \
     _(StoreElement)                                                         \
     _(StoreElementHole)                                                     \
     _(ArrayPopShift)                                                        \
+    _(ArrayPush)                                                            \
     _(LoadTypedArrayElement)                                                \
     _(LoadTypedArrayElementHole)                                            \
     _(StoreTypedArrayElement)                                               \
     _(ClampToUint8)                                                         \
     _(LoadFixedSlot)                                                        \
     _(StoreFixedSlot)                                                       \
     _(CallGetProperty)                                                      \
     _(CallGetName)                                                          \
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -255,16 +255,29 @@ ArrayPopDense(JSContext *cx, JSObject *o
     // have to monitor the return value.
     *rval = argv[0];
     if (rval->isUndefined())
         types::TypeScript::Monitor(cx, *rval);
     return true;
 }
 
 bool
+ArrayPushDense(JSContext *cx, JSObject *obj, const Value &v, uint32_t *length)
+{
+    JS_ASSERT(obj->isDenseArray());
+
+    Value argv[3] = { UndefinedValue(), ObjectValue(*obj), v };
+    if (!js::array_push(cx, 1, argv))
+        return false;
+
+    *length = argv[0].toInt32();
+    return true;
+}
+
+bool
 ArrayShiftDense(JSContext *cx, JSObject *obj, Value *rval)
 {
     AutoDetectInvalidation adi(cx, rval);
 
     Value argv[3] = { UndefinedValue(), ObjectValue(*obj) };
     if (!js::array_shift(cx, 0, argv))
         return false;
 
--- a/js/src/ion/VMFunctions.h
+++ b/js/src/ion/VMFunctions.h
@@ -194,16 +194,17 @@ template <class T> struct TypeToArgPrope
 };
 template <> struct TypeToArgProperties<const Value &> {
     static const uint32 result = TypeToArgProperties<Value>::result | VMFunction::ByRef;
 };
 
 template <class> struct OutParamToDataType { static const DataType result = Type_Void; };
 template <> struct OutParamToDataType<Value *> { static const DataType result = Type_Value; };
 template <> struct OutParamToDataType<int *> { static const DataType result = Type_Int32; };
+template <> struct OutParamToDataType<uint32_t *> { static const DataType result = Type_Int32; };
 
 #define FOR_EACH_ARGS_1(Macro, Sep, Last) Macro(1) Last(1)
 #define FOR_EACH_ARGS_2(Macro, Sep, Last) FOR_EACH_ARGS_1(Macro, Sep, Sep) Macro(2) Last(2)
 #define FOR_EACH_ARGS_3(Macro, Sep, Last) FOR_EACH_ARGS_2(Macro, Sep, Sep) Macro(3) Last(3)
 #define FOR_EACH_ARGS_4(Macro, Sep, Last) FOR_EACH_ARGS_3(Macro, Sep, Sep) Macro(4) Last(4)
 
 #define COMPUTE_INDEX(NbArg) NbArg
 #define COMPUTE_OUTPARAM_RESULT(NbArg) OutParamToDataType<A ## NbArg>::result
@@ -342,15 +343,16 @@ bool IteratorMore(JSContext *cx, JSObjec
 
 bool CloseIteratorFromIon(JSContext *cx, JSObject *obj);
 
 // Allocation functions for JSOP_NEWARRAY and JSOP_NEWOBJECT
 JSObject *NewInitArray(JSContext *cx, uint32_t count, types::TypeObject *type);
 JSObject *NewInitObject(JSContext *cx, JSObject *baseObj, types::TypeObject *type);
 
 bool ArrayPopDense(JSContext *cx, JSObject *obj, Value *rval);
+bool ArrayPushDense(JSContext *cx, JSObject *obj, const Value &v, uint32_t *length);
 bool ArrayShiftDense(JSContext *cx, JSObject *obj, Value *rval);
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_vm_functions_h_
 
--- a/js/src/ion/arm/MacroAssembler-arm.h
+++ b/js/src/ion/arm/MacroAssembler-arm.h
@@ -735,31 +735,43 @@ class MacroAssemblerARMCompat : public M
     void storeValue(ValueOperand val, const Address &dest) {
         storeValue(val, Operand(dest));
     }
     void storeValue(JSValueType type, Register reg, Address dest) {
         ma_mov(ImmTag(JSVAL_TYPE_TO_TAG(type)), lr);
         ma_str(lr, Address(dest.base, dest.offset + 4));
         ma_str(reg, dest);
     }
+    void storeValue(JSValueType type, Register reg, BaseIndex dest) {
+        // Harder cases not handled yet.
+        JS_ASSERT(dest.offset == 0);
+        ma_alu(dest.base, lsl(dest.index, dest.scale), ScratchRegister, op_add);
+        storeValue(type, reg, Address(ScratchRegister, 0));
+    }
     void storeValue(ValueOperand val, const BaseIndex &dest) {
         // Harder cases not handled yet.
         JS_ASSERT(dest.offset == 0);
         storeValue(val, dest.base, dest.index);
     }
     void storeValue(const Value &val, Address dest) {
         jsval_layout jv = JSVAL_TO_IMPL(val);
         ma_mov(Imm32(jv.s.tag), lr);
         ma_str(lr, Address(dest.base, dest.offset + 4));
         if (val.isMarkable())
             ma_mov(ImmGCPtr(reinterpret_cast<gc::Cell *>(val.toGCThing())), lr);
         else
             ma_mov(Imm32(jv.s.payload.i32), lr);
         ma_str(lr, dest);
     }
+    void storeValue(const Value &val, BaseIndex dest) {
+        // Harder cases not handled yet.
+        JS_ASSERT(dest.offset == 0);
+        ma_alu(dest.base, lsl(dest.index, dest.scale), ScratchRegister, op_add);
+        storeValue(val, Address(ScratchRegister, 0));
+    }
 
     void loadValue(Address src, ValueOperand val);
     void loadValue(Operand dest, ValueOperand val) {
         loadValue(dest.toAddress(), val);
     }
     void loadValue(Register base, Register index, ValueOperand val);
     void loadValue(const BaseIndex &addr, ValueOperand val) {
         // Harder cases not handled yet.
@@ -795,16 +807,21 @@ class MacroAssemblerARMCompat : public M
     }
 
     void loadDouble(Address addr, FloatRegister dest) {
         ma_vldr(Operand(addr), dest);
     }
     void storeDouble(FloatRegister src, Address addr) {
         ma_vstr(src, Operand(addr));
     }
+    void storeDouble(FloatRegister src, BaseIndex addr) {
+        // Harder cases not handled yet.
+        JS_ASSERT(addr.offset == 0);
+        ma_vstr(src, addr.base, addr.index);
+    }
 
     void linkExitFrame();
     void handleException();
 
     /////////////////////////////////////////////////////////////////
     // Common interface.
     /////////////////////////////////////////////////////////////////
   public:
--- a/js/src/ion/x64/MacroAssembler-x64.h
+++ b/js/src/ion/x64/MacroAssembler-x64.h
@@ -119,22 +119,24 @@ class MacroAssemblerX64 : public MacroAs
     // X86/X64-common interface.
     /////////////////////////////////////////////////////////////////
     void storeValue(ValueOperand val, Operand dest) {
         movq(val.valueReg(), dest);
     }
     void storeValue(ValueOperand val, const Address &dest) {
         storeValue(val, Operand(dest));
     }
-    void storeValue(JSValueType type, Register reg, Address dest) {
+    template <typename T>
+    void storeValue(JSValueType type, Register reg, const T &dest) {
         boxValue((JSValueShiftedTag)JSVAL_TYPE_TO_SHIFTED_TAG(type),
                  Operand(reg), ScratchReg);
         movq(ScratchReg, Operand(dest));
     }
-    void storeValue(const Value &val, Address dest) {
+    template <typename T>
+    void storeValue(const Value &val, const T &dest) {
         jsval_layout jv = JSVAL_TO_IMPL(val);
         movq(ImmWord(jv.asBits), ScratchReg);
         if (val.isMarkable())
             writeDataRelocation(masm.currentOffset());
         movq(ScratchReg, Operand(dest));
     }
     void storeValue(ValueOperand val, BaseIndex dest) {
         storeValue(val, Operand(dest));
--- a/js/src/ion/x86/MacroAssembler-x86.h
+++ b/js/src/ion/x86/MacroAssembler-x86.h
@@ -135,21 +135,23 @@ class MacroAssemblerX86 : public MacroAs
     /////////////////////////////////////////////////////////////////
     void storeValue(ValueOperand val, Operand dest) {
         movl(val.payloadReg(), ToPayload(dest));
         movl(val.typeReg(), ToType(dest));
     }
     void storeValue(ValueOperand val, const Address &dest) {
         storeValue(val, Operand(dest));
     }
-    void storeValue(JSValueType type, Register reg, Address dest) {
+    template <typename T>
+    void storeValue(JSValueType type, Register reg, const T &dest) {
         storeTypeTag(ImmTag(JSVAL_TYPE_TO_TAG(type)), Operand(dest));
         storePayload(reg, Operand(dest));
     }
-    void storeValue(const Value &val, Address dest) {
+    template <typename T>
+    void storeValue(const Value &val, const T &dest) {
         jsval_layout jv = JSVAL_TO_IMPL(val);
         storeTypeTag(ImmTag(jv.s.tag), Operand(dest));
         storePayload(val, Operand(dest));
     }
     void storeValue(ValueOperand val, BaseIndex dest) {
         storeValue(val, Operand(dest));
     }
     void loadValue(Operand src, ValueOperand val) {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/inlining/array-push.js
@@ -0,0 +1,37 @@
+function test1() {
+    function push(arr, x) {
+        return arr.push(x);
+    }
+    var arr = [];
+    for (var i=0; i<100; i++) {
+        assertEq(push(arr, i), i + 1);
+    }
+}
+test1();
+
+function test2() {
+    var arr;
+    for (var i=0; i<60; i++) {
+        arr = [];
+        assertEq(arr.push(3.3), 1);
+        assertEq(arr.push(undefined), 2);
+        assertEq(arr.push(true), 3);
+        assertEq(arr.push(Math), 4);
+        assertEq(arr.toString(), "3.3,,true,[object Math]");
+    }
+}
+test2();
+
+function test3() {
+    function push(arr, v) {
+        arr.push(v);
+    }
+    for (var i=0; i<60; i++) {
+        var arr = [];
+        push(arr, null);
+        push(arr, 3.14);
+        push(arr, {});
+        assertEq(arr.toString(), ",3.14,[object Object]");
+    }
+}
+test3();