Support SETELEM with out-of-bounds index (bug 706328, r=dvander)
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 02 Feb 2012 13:41:58 +0100
changeset 105656 550a780f73aeb23ea958cab93de141376aa12f3a
parent 105655 41b54805815bfc9c01e42b1ed4680e9851f28dd1
child 105657 35cda526bd052dd220486c4fa25070b709085ba6
push id23447
push userdanderson@mozilla.com
push dateTue, 11 Sep 2012 17:34:27 +0000
treeherdermozilla-central@fdfaef738a00 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs706328
milestone13.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
Support SETELEM with out-of-bounds index (bug 706328, r=dvander)
js/src/ion/AliasAnalysis.cpp
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/IonBuilder.cpp
js/src/ion/IonMacroAssembler.h
js/src/ion/IonRegisters.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/MIR.h
js/src/ion/MOpcodes.h
js/src/ion/TypeOracle.cpp
js/src/ion/TypeOracle.h
js/src/ion/arm/CodeGenerator-arm.cpp
js/src/ion/arm/CodeGenerator-arm.h
js/src/ion/arm/MacroAssembler-arm.cpp
js/src/ion/arm/MacroAssembler-arm.h
js/src/ion/shared/Assembler-x86-shared.h
js/src/ion/shared/CodeGenerator-shared-inl.h
js/src/ion/shared/CodeGenerator-shared.h
js/src/ion/shared/Lowering-shared-inl.h
js/src/ion/shared/Lowering-shared.h
js/src/ion/shared/MacroAssembler-x86-shared.h
js/src/ion/x64/CodeGenerator-x64.cpp
js/src/ion/x64/CodeGenerator-x64.h
js/src/ion/x64/MacroAssembler-x64.h
js/src/ion/x86/CodeGenerator-x86.cpp
js/src/ion/x86/CodeGenerator-x86.h
js/src/ion/x86/MacroAssembler-x86.h
js/src/jit-test/tests/ion/setelem-hole.js
--- a/js/src/ion/AliasAnalysis.cpp
+++ b/js/src/ion/AliasAnalysis.cpp
@@ -188,17 +188,17 @@ AliasAnalysis::analyze()
                             return false;
                     }
                 } else {
                     // This instruction depends on stores inside the loop body. Mark it as having a
                     // dependency on the last instruction of the loop header. The last instruction is a
                     // control instruction and these are never hoisted.
                     MControlInstruction *controlIns = loop_->loopHeader()->lastIns();
                     IonSpew(IonSpew_Alias, "Load %d depends on %d (due to stores in loop body)",
-                            ins->id(), controlIns);
+                            ins->id(), controlIns->id());
                     ins->setDependency(controlIns);
                 }
             }
             loop_ = loop_->outer();
         }
     }
 
     JS_ASSERT(loop_ == NULL);
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -952,16 +952,193 @@ CodeGenerator::visitBoundsCheckRange(LBo
 bool
 CodeGenerator::visitBoundsCheckLower(LBoundsCheckLower *lir)
 {
     int32 min = lir->mir()->minimum();
     masm.cmp32(ToRegister(lir->index()), Imm32(min));
     return bailoutIf(Assembler::LessThan, lir->snapshot());
 }
 
+class OutOfLineStoreElementHole : public OutOfLineCodeBase<CodeGenerator>
+{
+    LInstruction *ins_;
+    Label rejoinStore_;
+
+  public:
+    OutOfLineStoreElementHole(LInstruction *ins)
+      : ins_(ins)
+    {
+        JS_ASSERT(ins->isStoreElementHoleV() || ins->isStoreElementHoleT());
+    }
+
+    bool accept(CodeGenerator *codegen) {
+        return codegen->visitOutOfLineStoreElementHole(this);
+    }
+    LInstruction *ins() const {
+        return ins_;
+    }
+    Label *rejoinStore() {
+        return &rejoinStore_;
+    }
+};
+
+bool
+CodeGenerator::visitStoreElementT(LStoreElementT *store)
+{
+    storeElementTyped(store->value(), store->mir()->value()->type(), store->mir()->elementType(),
+                      ToRegister(store->elements()), store->index());
+    return true;
+}
+
+bool
+CodeGenerator::visitStoreElementV(LStoreElementV *lir)
+{
+    const ValueOperand value = ToValue(lir, LStoreElementV::Value);
+    Register elements = ToRegister(lir->elements());
+
+    if (lir->index()->isConstant())
+        masm.storeValue(value, Address(elements, ToInt32(lir->index()) * sizeof(js::Value)));
+    else
+        masm.storeValue(value, BaseIndex(elements, ToRegister(lir->index()), TimesEight));
+    return true;
+}
+
+bool
+CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT *lir)
+{
+    OutOfLineStoreElementHole *ool = new OutOfLineStoreElementHole(lir);
+    if (!addOutOfLineCode(ool))
+        return false;
+
+    Register elements = ToRegister(lir->elements());
+    const LAllocation *index = lir->index();
+
+    // OOL path if index >= initializedLength.
+    Address initLength(elements, ObjectElements::offsetOfInitializedLength());
+    masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry());
+
+    masm.bind(ool->rejoinStore());
+    storeElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(),
+                      elements, index);
+
+    masm.bind(ool->rejoin());
+    return true;
+}
+
+bool
+CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV *lir)
+{
+    OutOfLineStoreElementHole *ool = new OutOfLineStoreElementHole(lir);
+    if (!addOutOfLineCode(ool))
+        return false;
+
+    Register elements = ToRegister(lir->elements());
+    const LAllocation *index = lir->index();
+    const ValueOperand value = ToValue(lir, LStoreElementHoleV::Value);
+
+    // OOL path if index >= initializedLength.
+    Address initLength(elements, ObjectElements::offsetOfInitializedLength());
+    masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry());
+
+    masm.bind(ool->rejoinStore());
+    if (lir->index()->isConstant())
+        masm.storeValue(value, Address(elements, ToInt32(lir->index()) * sizeof(js::Value)));
+    else
+        masm.storeValue(value, BaseIndex(elements, ToRegister(lir->index()), TimesEight));
+
+    masm.bind(ool->rejoin());
+    return true;
+}
+
+bool
+CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool)
+{
+    Register object, elements;
+    LInstruction *ins = ool->ins();
+    const LAllocation *index;
+    MIRType valueType;
+    ConstantOrRegister value;
+
+    if (ins->isStoreElementHoleV()) {
+        LStoreElementHoleV *store = ins->toStoreElementHoleV();
+        object = ToRegister(store->object());
+        elements = ToRegister(store->elements());
+        index = store->index();
+        valueType = store->mir()->value()->type();
+        value = TypedOrValueRegister(ToValue(store, LStoreElementHoleV::Value));
+    } else {
+        LStoreElementHoleT *store = ins->toStoreElementHoleT();
+        object = ToRegister(store->object());
+        elements = ToRegister(store->elements());
+        index = store->index();
+        valueType = store->mir()->value()->type();
+        if (store->value()->isConstant())
+            value = ConstantOrRegister(*store->value()->toConstant());
+        else
+            value = TypedOrValueRegister(valueType, ToAnyRegister(store->value()));
+    }
+
+    // If index == initializedLength, try to bump the initialized length inline.
+    // If index > initializedLength, call a stub. Note that this relies on the
+    // condition flags sticking from the incoming branch.
+    Label callStub;
+    masm.j(Assembler::NotEqual, &callStub);
+
+    Int32Key key = ToInt32Key(index);
+
+    // Check array capacity.
+    masm.branchKey(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()),
+                   key, &callStub);
+
+    // Update initialized length. The capacity guard above ensures this won't overflow,
+    // due to NELEMENTS_LIMIT.
+    masm.bumpKey(&key, 1);
+    masm.storeKey(key, Address(elements, ObjectElements::offsetOfInitializedLength()));
+
+    // Update length if length < initializedLength.
+    Label dontUpdate;
+    masm.branchKey(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()),
+                   key, &dontUpdate);
+    masm.storeKey(key, Address(elements, ObjectElements::offsetOfLength()));
+    masm.bind(&dontUpdate);
+
+    masm.bumpKey(&key, -1);
+
+    if (ins->isStoreElementHoleT() && valueType != MIRType_Double) {
+        // The inline path for StoreElementHoleT does not always store the type tag,
+        // so we do the store on the OOL path. We use MIRType_None for the element type
+        // so that storeElementTyped will always store the type tag.
+        storeElementTyped(ins->toStoreElementHoleT()->value(), valueType, MIRType_None, elements,
+                          index);
+        masm.jump(ool->rejoin());
+    } else {
+        // Jump to the inline path where we will store the value.
+        masm.jump(ool->rejoinStore());
+    }
+
+    masm.bind(&callStub);
+    saveLive(ins);
+
+    typedef bool (*pf)(JSContext *, JSObject *, const Value &, const Value &);
+    static const VMFunction Info = FunctionInfo<pf>(SetObjectElement);
+
+    pushArg(value);
+    if (index->isConstant())
+        pushArg(*index->toConstant());
+    else
+        pushArg(TypedOrValueRegister(MIRType_Int32, ToAnyRegister(index)));
+    pushArg(object);
+    if (!callVM(Info, ins))
+        return false;
+
+    restoreLive(ins);
+    masm.jump(ool->rejoin());
+    return true;
+}
+
 bool
 CodeGenerator::generate()
 {
     JSContext *cx = gen->cx;
 
     if (!safepoints_.init(graph.localSlotCount()))
         return false;
 
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -53,16 +53,17 @@
 #endif
 
 namespace js {
 namespace ion {
 
 class CheckOverRecursedFailure;
 class OutOfLineUnboxDouble;
 class OutOfLineCache;
+class OutOfLineStoreElementHole;
 
 class CodeGenerator : public CodeGeneratorSpecific
 {
     bool generateArgumentsChecks();
     bool generateInvalidateEpilogue();
     bool generateBody();
 
   public:
@@ -114,24 +115,29 @@ class CodeGenerator : public CodeGenerat
     bool visitCallGetProperty(LCallGetProperty *lir);
     bool visitCallGetName(LCallGetName *lir);
     bool visitCallGetNameTypeOf(LCallGetNameTypeOf *lir);
     bool visitCallGetElement(LCallGetElement *lir);
     bool visitCallSetElement(LCallSetElement *lir);
     bool visitThrow(LThrow *lir);
     bool visitLoadElementV(LLoadElementV *load);
     bool visitLoadElementHole(LLoadElementHole *lir);
+    bool visitStoreElementT(LStoreElementT *lir);
+    bool visitStoreElementV(LStoreElementV *lir);
+    bool visitStoreElementHoleT(LStoreElementHoleT *lir);
+    bool visitStoreElementHoleV(LStoreElementHoleV *lir);
 
     bool visitCheckOverRecursed(LCheckOverRecursed *lir);
     bool visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool);
 
     bool visitUnboxDouble(LUnboxDouble *lir);
     bool visitOutOfLineUnboxDouble(OutOfLineUnboxDouble *ool);
     bool visitOutOfLineCacheGetProperty(OutOfLineCache *ool);
     bool visitOutOfLineCacheSetProperty(OutOfLineCache *ool);
+    bool visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool);
 
     bool visitGetPropertyCacheV(LGetPropertyCacheV *ins) {
         return visitCache(ins);
     }
     bool visitGetPropertyCacheT(LGetPropertyCacheT *ins) {
         return visitCache(ins);
     }
     bool visitCacheSetPropertyV(LCacheSetPropertyV *ins) {
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -3194,42 +3194,56 @@ IonBuilder::jsop_setelem_dense()
     MInstruction *idInt32 = MToInt32::New(id);
     current->add(idInt32);
     id = idInt32;
 
     // Get the elements vector.
     MElements *elements = MElements::New(obj);
     current->add(elements);
 
-    // Read and check length.
-
-    MInitializedLength *initLength = MInitializedLength::New(elements);
-    current->add(initLength);
-
-    MBoundsCheck *check = MBoundsCheck::New(id, initLength);
-    current->add(check);
-
-    // Store the value.
-    MStoreElement *store = MStoreElement::New(elements, id, value);
-    current->add(store);
-    current->push(value);
+    // Use MStoreElementHole if this SETELEM has written to out-of-bounds
+    // indexes in the past. Otherwise, use MStoreElement so that we can hoist
+    // the initialized length and bounds check.
+    MStoreElementCommon *store;
+    if (oracle->setElementHasWrittenHoles(script, pc)) {
+        MStoreElementHole *ins = MStoreElementHole::New(obj, elements, id, value);
+        store = ins;
+
+        current->add(ins);
+        current->push(value);
+
+        if (!resumeAfter(ins))
+            return false;
+    } else {
+        MInitializedLength *initLength = MInitializedLength::New(elements);
+        current->add(initLength);
+
+        MBoundsCheck *check = MBoundsCheck::New(id, initLength);
+        current->add(check);
+
+        MStoreElement *ins = MStoreElement::New(elements, id, value);
+        store = ins;
+
+        current->add(ins);
+        current->push(value);
+
+        if (!resumeAfter(ins))
+            return false;
+    }
 
 #ifdef JSGC_INCREMENTAL
     // Determine whether a write barrier is required.
-    if (cx->compartment->needsBarrier() &&
-        oracle->propertyWriteNeedsBarrier(script, pc, JSID_VOID))
-    {
+    if (cx->compartment->needsBarrier() && oracle->propertyWriteNeedsBarrier(script, pc, JSID_VOID))
         store->setNeedsBarrier(true);
-    }
 #endif
 
     if (elementType != MIRType_None && packed)
         store->setElementType(elementType);
 
-    return resumeAfter(store);
+    return true;
 }
 
 bool
 IonBuilder::jsop_length()
 {
     if (jsop_length_fastPath())
         return true;
     return jsop_getprop(info().getAtom(pc));
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -230,15 +230,36 @@ class MacroAssembler : public MacroAssem
         pushValue(val);
         framePushed_ += sizeof(Value);
     }
 
     void Push(JSValueType type, Register reg) {
         pushValue(type, reg);
         framePushed_ += sizeof(Value);
     }
+
+    void bumpKey(Int32Key *key, int diff) {
+        if (key->isRegister())
+            add32(Imm32(diff), key->reg());
+        else
+            key->bumpConstant(diff);
+    }
+
+    void storeKey(const Int32Key &key, const Address &dest) {
+        if (key.isRegister())
+            store32(key.reg(), dest);
+        else
+            store32(Imm32(key.constant()), dest);
+    }
+
+    void branchKey(Condition cond, const Address &dest, const Int32Key &key, Label *label) {
+        if (key.isRegister())
+            branch32(cond, dest, key.reg(), label);
+        else
+            branch32(cond, dest, Imm32(key.constant()), label);
+    }
 };
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_macro_assembler_h__
 
--- a/js/src/ion/IonRegisters.h
+++ b/js/src/ion/IonRegisters.h
@@ -277,25 +277,25 @@ class TypedOrValueRegister
     }
 
     TypedOrValueRegister(ValueOperand value)
       : type_(MIRType_Value)
     {
         dataValue() = value;
     }
 
-    MIRType type() {
+    MIRType type() const {
         return type_;
     }
 
-    bool hasTyped() {
+    bool hasTyped() const {
         return type() != MIRType_None && type() != MIRType_Value;
     }
 
-    bool hasValue() {
+    bool hasValue() const {
         return type() == MIRType_Value;
     }
 
     AnyRegister typedReg() {
         return dataTyped();
     }
 
     ValueOperand valueReg() {
@@ -349,16 +349,48 @@ class ConstantOrRegister
         return dataValue();
     }
 
     TypedOrValueRegister reg() {
         return dataReg();
     }
 };
 
+struct Int32Key {
+    bool isRegister_;
+    union {
+        Register reg_;
+        int32_t constant_;
+    };
+
+    explicit Int32Key(Register reg)
+      : isRegister_(true), reg_(reg)
+    { }
+
+    explicit Int32Key(int32_t index)
+      : isRegister_(false), constant_(index)
+    { }
+
+    inline void bumpConstant(int diff) {
+        JS_ASSERT(!isRegister_);
+        constant_ += diff;
+    }
+    inline Register reg() const {
+        JS_ASSERT(isRegister_);
+        return reg_;
+    }
+    inline int32_t constant() const {
+        JS_ASSERT(!isRegister_);
+        return constant_;
+    }
+    inline bool isRegister() const {
+        return isRegister_;
+    }
+};
+
 template <typename T>
 class TypedRegisterSet
 {
     uint32 bits_;
 
   public:
     explicit TypedRegisterSet(uint32 bits)
       : bits_(bits)
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -1181,16 +1181,76 @@ class LStoreElementT : public LInstructi
     const LAllocation *index() {
         return getOperand(1);
     }
     const LAllocation *value() {
         return getOperand(2);
     }
 };
 
+// Like LStoreElementV, but supports indexes >= initialized length.
+class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(StoreElementHoleV);
+
+    LStoreElementHoleV(const LAllocation &object, const LAllocation &elements,
+                       const LAllocation &index) {
+        setOperand(0, object);
+        setOperand(1, elements);
+        setOperand(2, index);
+    }
+
+    static const size_t Value = 3;
+
+    const MStoreElementHole *mir() const {
+        return mir_->toStoreElementHole();
+    }
+    const LAllocation *object() {
+        return getOperand(0);
+    }
+    const LAllocation *elements() {
+        return getOperand(1);
+    }
+    const LAllocation *index() {
+        return getOperand(2);
+    }
+};
+
+// Like LStoreElementT, but supports indexes >= initialized length.
+class LStoreElementHoleT : public LInstructionHelper<0, 4, 0>
+{
+  public:
+    LIR_HEADER(StoreElementHoleT);
+
+    LStoreElementHoleT(const LAllocation &object, const LAllocation &elements,
+                       const LAllocation &index, const LAllocation &value) {
+        setOperand(0, object);
+        setOperand(1, elements);
+        setOperand(2, index);
+        setOperand(3, value);
+    }
+
+    const MStoreElementHole *mir() const {
+        return mir_->toStoreElementHole();
+    }
+    const LAllocation *object() {
+        return getOperand(0);
+    }
+    const LAllocation *elements() {
+        return getOperand(1);
+    }
+    const LAllocation *index() {
+        return getOperand(2);
+    }
+    const LAllocation *value() {
+        return getOperand(3);
+    }
+};
+
 // Load a boxed value from an object's fixed slot.
 class LLoadFixedSlotV : public LInstructionHelper<BOX_PIECES, 1, 0>
 {
   public:
     LIR_HEADER(LoadFixedSlotV);
     BOX_OUTPUT_ACCESSORS();
 
     LLoadFixedSlotV(const LAllocation &object) {
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -106,16 +106,18 @@
     _(BoundsCheck)                  \
     _(BoundsCheckRange)             \
     _(BoundsCheckLower)             \
     _(LoadElementV)                 \
     _(LoadElementT)                 \
     _(LoadElementHole)              \
     _(StoreElementV)                \
     _(StoreElementT)                \
+    _(StoreElementHoleV)            \
+    _(StoreElementHoleT)            \
     _(LoadFixedSlotV)               \
     _(LoadFixedSlotT)               \
     _(StoreFixedSlotV)              \
     _(StoreFixedSlotT)              \
     _(FunctionEnvironment)          \
     _(GetPropertyCacheV)            \
     _(GetPropertyCacheT)            \
     _(CallGetProperty)              \
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -910,38 +910,71 @@ LIRGenerator::visitStoreElement(MStoreEl
     JS_ASSERT(ins->elements()->type() == MIRType_Elements);
     JS_ASSERT(ins->index()->type() == MIRType_Int32);
 
 #ifdef JSGC_INCREMENTAL
     if (ins->needsBarrier() && !emitWriteBarrier(ins, ins->value()))
         return false;
 #endif
 
+    const LUse elements = useRegister(ins->elements());
+    const LAllocation index = useRegisterOrConstant(ins->index());
+
     switch (ins->value()->type()) {
       case MIRType_Value:
       {
-        LInstruction *lir = new LStoreElementV(useRegister(ins->elements()),
-                                               useRegisterOrConstant(ins->index()));
+        LInstruction *lir = new LStoreElementV(elements, index);
         if (!useBox(lir, LStoreElementV::Value, ins->value()))
             return false;
         return add(lir, ins);
       }
-      case MIRType_Double:
-        return add(new LStoreElementT(useRegister(ins->elements()),
-                                      useRegisterOrConstant(ins->index()),
-                                      useRegister(ins->value())), ins);
 
       default:
-        return add(new LStoreElementT(useRegister(ins->elements()),
-                                      useRegisterOrConstant(ins->index()),
-                                      useRegisterOrConstant(ins->value())), ins);
+      {
+        const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
+        return add(new LStoreElementT(elements, index, value), ins);
+      }
     }
 }
 
 bool
+LIRGenerator::visitStoreElementHole(MStoreElementHole *ins)
+{
+    JS_ASSERT(ins->elements()->type() == MIRType_Elements);
+    JS_ASSERT(ins->index()->type() == MIRType_Int32);
+
+#ifdef JSGC_INCREMENTAL
+    if (ins->needsBarrier() && !emitWriteBarrier(ins, ins->value()))
+        return false;
+#endif
+
+    const LUse object = useRegister(ins->object());
+    const LUse elements = useRegister(ins->elements());
+    const LAllocation index = useRegisterOrConstant(ins->index());
+
+    LInstruction *lir;
+    switch (ins->value()->type()) {
+      case MIRType_Value:
+        lir = new LStoreElementHoleV(object, elements, index);
+        if (!useBox(lir, LStoreElementHoleV::Value, ins->value()))
+            return false;
+        break;
+
+      default:
+      {
+        const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
+        lir = new LStoreElementHoleT(object, elements, index, value);
+        break;
+      }
+    }
+
+    return add(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
 LIRGenerator::visitLoadFixedSlot(MLoadFixedSlot *ins)
 {
     JS_ASSERT(ins->object()->type() == MIRType_Object);
 
     if (ins->type() == MIRType_Value) {
         LLoadFixedSlotV *lir = new LLoadFixedSlotV(useRegister(ins->object()));
         return defineBox(lir, ins);
     }
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -152,16 +152,17 @@ class LIRGenerator : public LIRGenerator
     bool visitTypeBarrier(MTypeBarrier *ins);
     bool visitArrayLength(MArrayLength *ins);
     bool visitInitializedLength(MInitializedLength *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 visitLoadFixedSlot(MLoadFixedSlot *ins);
     bool visitStoreFixedSlot(MStoreFixedSlot *ins);
     bool visitGetPropertyCache(MGetPropertyCache *ins);
     bool visitGuardClass(MGuardClass *ins);
     bool visitCallGetProperty(MCallGetProperty *ins);
     bool visitCallGetName(MCallGetName *ins);
     bool visitCallGetNameTypeOf(MCallGetNameTypeOf *ins);
     bool visitCallGetElement(MCallGetElement *ins);
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -2352,71 +2352,129 @@ class MLoadElementHole
     bool needsHoleCheck() const {
         return needsHoleCheck_;
     }
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::Element);
     }
 };
 
+class MStoreElementCommon
+{
+    bool needsBarrier_;
+    MIRType elementType_;
+
+  protected:
+    MStoreElementCommon()
+      : needsBarrier_(false),
+        elementType_(MIRType_None)
+    { }
+
+  public:
+    MIRType elementType() const {
+        return elementType_;
+    }
+    void setElementType(MIRType elementType) {
+        JS_ASSERT(elementType != MIRType_None);
+        elementType_ = elementType;
+    }
+    bool needsBarrier() const {
+        return needsBarrier_;
+    }
+    void setNeedsBarrier(bool needsBarrier) {
+        needsBarrier_ = needsBarrier;
+    }
+};
+
 // Store a value to a dense array slots vector.
 class MStoreElement
   : public MAryInstruction<3>,
+    public MStoreElementCommon,
     public ObjectPolicy
 {
-    MIRType elementType_;
-    bool needsBarrier_;
-
-    MStoreElement(MDefinition *elements, MDefinition *index, MDefinition *value)
-      : needsBarrier_(false)
-    {
+    MStoreElement(MDefinition *elements, MDefinition *index, MDefinition *value) {
         initOperand(0, elements);
         initOperand(1, index);
         initOperand(2, value);
         JS_ASSERT(elements->type() == MIRType_Elements);
         JS_ASSERT(index->type() == MIRType_Int32);
     }
 
   public:
     INSTRUCTION_HEADER(StoreElement);
 
     static MStoreElement *New(MDefinition *elements, MDefinition *index, MDefinition *value) {
         return new MStoreElement(elements, index, value);
     }
-
-    TypePolicy *typePolicy() {
-        return this;
-    }
     MDefinition *elements() const {
         return getOperand(0);
     }
     MDefinition *index() const {
         return getOperand(1);
     }
     MDefinition *value() const {
         return getOperand(2);
     }
-    MIRType elementType() const {
-        return elementType_;
-    }
-    void setElementType(MIRType elementType) {
-        JS_ASSERT(elementType != MIRType_None);
-        elementType_ = elementType;
-    }
-    bool needsBarrier() const {
-        return needsBarrier_;
-    }
-    void setNeedsBarrier(bool needsBarrier) {
-        needsBarrier_ = needsBarrier;
+    TypePolicy *typePolicy() {
+        return this;
     }
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::Element);
     }
 };
 
+// Like MStoreElement, but supports indexes >= initialized length. The downside
+// is that we cannot hoist the elements vector and bounds check, since this
+// instruction may update the (initialized) length and reallocate the elements
+// vector.
+class MStoreElementHole
+  : public MAryInstruction<4>,
+    public MStoreElementCommon,
+    public ObjectPolicy
+{
+    MStoreElementHole(MDefinition *object, MDefinition *elements,
+                      MDefinition *index, MDefinition *value) {
+        initOperand(0, object);
+        initOperand(1, elements);
+        initOperand(2, index);
+        initOperand(3, value);
+        JS_ASSERT(elements->type() == MIRType_Elements);
+        JS_ASSERT(index->type() == MIRType_Int32);
+    }
+
+  public:
+    INSTRUCTION_HEADER(StoreElementHole);
+
+    static MStoreElementHole *New(MDefinition *object, MDefinition *elements,
+                                  MDefinition *index, MDefinition *value) {
+        return new MStoreElementHole(object, elements, index, value);
+    }
+
+    MDefinition *object() const {
+        return getOperand(0);
+    }
+    MDefinition *elements() const {
+        return getOperand(1);
+    }
+    MDefinition *index() const {
+        return getOperand(2);
+    }
+    MDefinition *value() const {
+        return getOperand(3);
+    }
+    TypePolicy *typePolicy() {
+        return this;
+    }
+    AliasSet getAliasSet() const {
+        // StoreElementHole can update the initialized length, the array length
+        // or reallocate obj->elements.
+        return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
+    }
+};
+
 class MLoadFixedSlot : public MUnaryInstruction, public ObjectPolicy
 {
     size_t slot_;
 
     MLoadFixedSlot(MDefinition *obj, size_t slot)
       : MUnaryInstruction(obj), slot_(slot)
     {
         setResultType(MIRType_Value);
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -100,16 +100,17 @@ namespace ion {
     _(GuardClass)                                                           \
     _(ArrayLength)                                                          \
     _(InitializedLength)                                                    \
     _(BoundsCheck)                                                          \
     _(BoundsCheckLower)                                                     \
     _(LoadElement)                                                          \
     _(LoadElementHole)                                                      \
     _(StoreElement)                                                         \
+    _(StoreElementHole)                                                     \
     _(LoadFixedSlot)                                                        \
     _(StoreFixedSlot)                                                       \
     _(CallGetProperty)                                                      \
     _(CallGetName)                                                          \
     _(CallGetNameTypeOf)                                                    \
     _(CallGetElement)                                                       \
     _(CallSetElement)                                                       \
     _(GenericSetProperty)                                                   \
--- a/js/src/ion/TypeOracle.cpp
+++ b/js/src/ion/TypeOracle.cpp
@@ -241,16 +241,22 @@ TypeInferenceOracle::elementWriteIsDense
 
 bool
 TypeInferenceOracle::elementWriteIsPacked(JSScript *script, jsbytecode *pc)
 {
     types::TypeSet *types = script->analysis()->poppedTypes(pc, 2);
     return !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY);
 }
 
+bool
+TypeInferenceOracle::setElementHasWrittenHoles(JSScript *script, jsbytecode *pc)
+{
+    return script->analysis()->getCode(pc).arrayWriteHole;
+}
+
 MIRType
 TypeInferenceOracle::elementWrite(JSScript *script, jsbytecode *pc)
 {
     types::TypeSet *objTypes = script->analysis()->poppedTypes(pc, 2);
     MIRType elementType = MIRType_None;
     unsigned count = objTypes->getObjectCount();
 
     for (unsigned i = 0; i < count; i++) {
--- a/js/src/ion/TypeOracle.h
+++ b/js/src/ion/TypeOracle.h
@@ -119,16 +119,19 @@ class TypeOracle
         return NULL;
     }
     virtual bool elementReadIsDense(JSScript *script, jsbytecode *pc) {
         return false;
     }
     virtual bool elementReadIsPacked(JSScript *script, jsbytecode *pc) {
         return false;
     }
+    virtual bool setElementHasWrittenHoles(JSScript *script, jsbytecode *pc) {
+        return true;
+    }
     virtual bool elementWriteIsDense(JSScript *script, jsbytecode *pc) {
         return false;
     }
     virtual bool elementWriteIsPacked(JSScript *script, jsbytecode *pc) {
         return false;
     }
     virtual bool propertyWriteCanSpecialize(JSScript *script, jsbytecode *pc) {
         return true;
@@ -212,16 +215,17 @@ class TypeInferenceOracle : public TypeO
     types::TypeSet *propertyReadBarrier(JSScript *script, jsbytecode *pc);
     types::TypeSet *globalPropertyWrite(JSScript *script, jsbytecode *pc, jsid id, bool *canSpecialize);
     types::TypeSet *returnTypeSet(JSScript *script, jsbytecode *pc, types::TypeSet **barrier);
     types::TypeSet *getCallTarget(JSScript *caller, uint32 argc, jsbytecode *pc);
     bool elementReadIsDense(JSScript *script, jsbytecode *pc);
     bool elementReadIsPacked(JSScript *script, jsbytecode *pc);
     bool elementWriteIsDense(JSScript *script, jsbytecode *pc);
     bool elementWriteIsPacked(JSScript *script, jsbytecode *pc);
+    bool setElementHasWrittenHoles(JSScript *script, jsbytecode *pc);
     bool propertyWriteCanSpecialize(JSScript *script, jsbytecode *pc);
     bool propertyWriteNeedsBarrier(JSScript *script, jsbytecode *pc, jsid id);
     MIRType elementWrite(JSScript *script, jsbytecode *pc);
     bool arrayPrototypeHasIndexedProperty();
     bool canInlineCalls();
     bool canEnterInlinedScript(JSScript *inlineScript);
 };
 
--- a/js/src/ion/arm/CodeGenerator-arm.cpp
+++ b/js/src/ion/arm/CodeGenerator-arm.cpp
@@ -1123,69 +1123,53 @@ CodeGeneratorARM::visitLoadElementT(LLoa
             masm.ma_ldr(DTRAddr(base, DtrRegImmShift(ToRegister(load->index()), LSL, 3)),
                         ToRegister(load->output()));
         }
     }
     JS_ASSERT(!load->mir()->needsHoleCheck());
     return true;
 }
 
-bool
-CodeGeneratorARM::visitStoreElementV(LStoreElementV *store)
+void
+CodeGeneratorARM::storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
+                                    const Register &elements, const LAllocation *index)
 {
-    const ValueOperand out = ToOutValue(store);
-    Register base = ToRegister(store->elements());
-    if (store->index()->isConstant())
-        masm.storeValue(out, Address(base, ToInt32(store->index()) * sizeof(Value)));
-    else
-        masm.storeValue(out, base, ToRegister(store->index()));
-    return true;
-}
-
-bool
-CodeGeneratorARM::visitStoreElementT(LStoreElementT *store)
-{
-    const LAllocation *value = store->value();
-    MIRType valueType = store->mir()->value()->type();
-    Register base = ToRegister(store->elements());
-    if (store->index()->isConstant()) {
-        int32 index = ToInt32(store->index()) * sizeof(Value);
-        Address dest = Address(base, index);
+    if (index->isConstant()) {
+        Address dest = Address(elements, ToInt32(index) * sizeof(Value));
         if (valueType == MIRType_Double) {
             masm.ma_vstr(ToFloatRegister(value), Operand(dest));
-            return true;
+            return;
         }
 
         // Store the type tag if needed.
-        if (valueType != store->mir()->elementType())
+        if (valueType != elementType)
             masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest);
 
         // Store the payload.
         if (value->isConstant())
             masm.storePayload(*value->toConstant(), dest);
         else
             masm.storePayload(ToRegister(value), dest);
     } else {
-        Register index = ToRegister(store->index());
+        Register indexReg = ToRegister(index);
         if (valueType == MIRType_Double) {
-            masm.ma_vstr(ToFloatRegister(value), base, index);
-            return true;
+            masm.ma_vstr(ToFloatRegister(value), elements, indexReg);
+            return;
         }
 
         // Store the type tag if needed.
-        if (valueType != store->mir()->elementType())
-            masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), base, index);
+        if (valueType != elementType)
+            masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), elements, indexReg);
 
         // Store the payload.
         if (value->isConstant())
-            masm.storePayload(*value->toConstant(), base, index);
+            masm.storePayload(*value->toConstant(), elements, index);
         else
-            masm.storePayload(ToRegister(value), base, index);
+            masm.storePayload(ToRegister(value), elements, indexReg);
     }
-    return false;
 }
 
 bool
 CodeGeneratorARM::visitGuardShape(LGuardShape *guard)
 {
     Register obj = ToRegister(guard->input());
     Register tmp = ToRegister(guard->tempInt());
     masm.ma_ldr(DTRAddr(obj, DtrOffImm(JSObject::offsetOfShape())), tmp);
--- a/js/src/ion/arm/CodeGenerator-arm.h
+++ b/js/src/ion/arm/CodeGenerator-arm.h
@@ -127,16 +127,19 @@ class CodeGeneratorARM : public CodeGene
   protected:
     ValueOperand ToValue(LInstruction *ins, size_t pos);
     ValueOperand ToOutValue(LInstruction *ins);
 
     // Functions for LTestVAndBranch.
     Register splitTagForTest(const ValueOperand &value);
     Assembler::Condition testStringTruthy(bool truthy, const ValueOperand &value);
 
+    void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
+                           const Register &elements, const LAllocation *index);
+
   protected:
     void linkAbsoluteLabels();
 
   public:
     CodeGeneratorARM(MIRGenerator *gen, LIRGraph &graph);
 
   public:
     bool visitBox(LBox *box);
@@ -147,18 +150,16 @@ class CodeGeneratorARM : public CodeGene
     bool visitDouble(LDouble *ins);
 
     bool visitLoadSlotV(LLoadSlotV *load);
     bool visitLoadSlotT(LLoadSlotT *load);
     bool visitStoreSlotT(LStoreSlotT *load);
     bool visitWriteBarrierT(LWriteBarrierT *barrier);
 
     bool visitLoadElementT(LLoadElementT *load);
-    bool visitStoreElementV(LStoreElementV *store);
-    bool visitStoreElementT(LStoreElementT *store);
 
     bool visitGuardShape(LGuardShape *guard);
     bool visitGuardClass(LGuardClass *guard);
     bool visitImplicitThis(LImplicitThis *lir);
 
     bool visitRecompileCheck(LRecompileCheck *lir);
 };
 
--- a/js/src/ion/arm/MacroAssembler-arm.cpp
+++ b/js/src/ion/arm/MacroAssembler-arm.cpp
@@ -1144,16 +1144,29 @@ MacroAssemblerARMCompat::loadPtr(const I
 
 void
 MacroAssemblerARMCompat::store32(Register src, const ImmWord &imm)
 {
     storePtr(src, imm);
 }
 
 void
+MacroAssemblerARMCompat::store32(Register src, const Address &address)
+{
+    storePtr(src, address);
+}
+
+void
+MacroAssemblerARMCompat::store32(Imm32 src, const Address &address)
+{
+    move32(src, ScratchRegister);
+    storePtr(ScratchRegister, address);
+}
+
+void
 MacroAssemblerARMCompat::storePtr(Register src, const Address &address)
 {
     ma_str(src, Operand(address));
 }
 
 void
 MacroAssemblerARMCompat::storePtr(Register src, const ImmWord &imm)
 {
--- a/js/src/ion/arm/MacroAssembler-arm.h
+++ b/js/src/ion/arm/MacroAssembler-arm.h
@@ -491,16 +491,24 @@ class MacroAssemblerARMCompat : public M
     void branch32(Condition cond, Register lhs, Register rhs, Label *label) {
         ma_cmp(rhs, lhs);
         ma_b(label, cond);
     }
     void branch32(Condition cond, Register lhs, Imm32 imm, Label *label) {
         ma_cmp(lhs, imm);
         ma_b(label, InvertCondition(cond));
     }
+    void branch32(Condition cond, const Address &lhs, Register rhs, Label *label) {
+        move32(lhs, ScratchRegister);
+        branch32(cond, ScratchRegister, rhs, label);
+    }
+    void branch32(Condition cond, const Address &lhs, Imm32 rhs, Label *label) {
+        move32(lhs, ScratchRegister);
+        branch32(cond, ScratchRegister, rhs, label);
+    }
     template<typename T>
     void branchTestDouble(Condition cond, const T & t, Label *label) {
         Condition c = testDouble(cond, t);
         ma_b(label, c);
     }
     template<typename T>
     void branchTestNull(Condition cond, const T & t, Label *label) {
         Condition c = testNull(cond, t);
@@ -690,16 +698,18 @@ class MacroAssemblerARMCompat : public M
     void movePtr(const Address &src, const Register &dest);
 
     void load32(const Address &address, const Register &dest);
     void load32(const ImmWord &imm, const Register &dest);
     void loadPtr(const Address &address, const Register &dest);
     void loadPtr(const ImmWord &imm, const Register &dest);
 
     void store32(Register src, const ImmWord &imm);
+    void store32(Register src, const Address &address);
+    void store32(Imm32 src, const Address &address);
     void storePtr(Register src, const Address &address);
     void storePtr(Register src, const ImmWord &imm);
 
     void cmp32(const Register &lhs, const Imm32 &rhs);
     void cmp32(const Register &lhs, const Register &rhs);
     void cmpPtr(const Register &lhs, const ImmWord &rhs);
 
     void subPtr(Imm32 imm, const Register dest);
--- a/js/src/ion/shared/Assembler-x86-shared.h
+++ b/js/src/ion/shared/Assembler-x86-shared.h
@@ -447,16 +447,25 @@ class AssemblerX86Shared
           case Operand::ADDRESS:
             masm.cmpl_im(imm.value, op.address());
             break;
 #endif
           default:
             JS_NOT_REACHED("unexpected operand kind");
         }
     }
+    void cmpl(const Operand &lhs, const Register &rhs) {
+        switch (lhs.kind()) {
+          case Operand::REG_DISP:
+            masm.cmpl_rm(rhs.code(), lhs.disp(), lhs.base());
+            break;
+          default:
+            JS_NOT_REACHED("unexpected operand kind");
+        }
+    }
     void cmpl(const Operand &op, ImmWord imm) {
         switch (op.kind()) {
           case Operand::REG:
             masm.cmpl_ir(imm.value, op.reg());
             break;
           case Operand::REG_DISP:
             masm.cmpl_im(imm.value, op.disp(), op.base());
             break;
--- a/js/src/ion/shared/CodeGenerator-shared-inl.h
+++ b/js/src/ion/shared/CodeGenerator-shared-inl.h
@@ -40,16 +40,27 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef jsion_codegen_inl_h__
 #define jsion_codegen_inl_h__
 
 namespace js {
 namespace ion {
 
+static inline int32
+ToInt32(const LAllocation *a)
+{
+    if (a->isConstantValue())
+        return a->toConstant()->toInt32();
+    if (a->isConstantIndex())
+        return a->toConstantIndex()->index();
+    JS_NOT_REACHED("this is not a constant!");
+    return -1;
+}
+
 static inline Register
 ToRegister(const LAllocation &a)
 {
     JS_ASSERT(a.isGeneralReg());
     return a.toGeneralReg()->reg();
 }
 
 static inline Register
@@ -99,16 +110,24 @@ ToAnyRegister(const LAllocation *a)
 }
 
 static inline AnyRegister
 ToAnyRegister(const LDefinition *def)
 {
     return ToAnyRegister(def->output());
 }
 
+static inline Int32Key
+ToInt32Key(const LAllocation *a)
+{
+    if (a->isConstant())
+        return Int32Key(ToInt32(a));
+    return Int32Key(ToRegister(a));
+}
+
 static inline ValueOperand
 GetValueOutput(LInstruction *ins)
 {
 #if defined(JS_NUNBOX32)
     return ValueOperand(ToRegister(ins->getDef(TYPE_INDEX)),
                         ToRegister(ins->getDef(PAYLOAD_INDEX)));
 #elif defined(JS_PUNBOX64)
     return ValueOperand(ToRegister(ins->getDef(0)));
--- a/js/src/ion/shared/CodeGenerator-shared.h
+++ b/js/src/ion/shared/CodeGenerator-shared.h
@@ -81,27 +81,16 @@ class CodeGeneratorShared : public LInst
     js::Vector<OsiIndex, 0, SystemAllocPolicy> osiIndices_;
 
     // Mapping from bailout table ID to an offset in the snapshot buffer.
     js::Vector<SnapshotOffset, 0, SystemAllocPolicy> bailouts_;
 
     // Vector of information about generated polymorphic inline caches.
     js::Vector<IonCache, 0, SystemAllocPolicy> cacheList_;
 
-    static inline int32 ToInt32(const LAllocation *a) {
-        if (a->isConstantValue()) {
-            return a->toConstant()->toInt32();
-        }
-        if (a->isConstantIndex()) {
-            return a->toConstantIndex()->index();
-        }
-        JS_NOT_REACHED("this is not a constant!");
-        return -1;
-    }
-
   protected:
     // The offset of the first instruction of the OSR entry block from the
     // beginning of the code buffer.
     size_t osrEntryOffset_;
 
     inline void setOsrEntryOffset(size_t offset) {
         JS_ASSERT(osrEntryOffset_ == 0);
         osrEntryOffset_ = offset;
--- a/js/src/ion/shared/Lowering-shared-inl.h
+++ b/js/src/ion/shared/Lowering-shared-inl.h
@@ -284,16 +284,25 @@ LIRGeneratorShared::useOrConstant(MDefin
 
 LAllocation
 LIRGeneratorShared::useRegisterOrConstant(MDefinition *mir)
 {
     if (mir->isConstant())
         return LAllocation(mir->toConstant()->vp());
     return use(mir, LUse(LUse::REGISTER));
 }
+
+LAllocation
+LIRGeneratorShared::useRegisterOrNonDoubleConstant(MDefinition *mir)
+{
+    if (mir->isConstant() && mir->type() != MIRType_Double)
+        return LAllocation(mir->toConstant()->vp());
+    return use(mir, LUse(LUse::REGISTER));
+}
+
 #if defined(JS_CPU_ARM)
 LAllocation
 LIRGeneratorShared::useAnyOrConstant(MDefinition *mir)
 {
     return useRegisterOrConstant(mir);
 }
 #else
 LAllocation
--- a/js/src/ion/shared/Lowering-shared.h
+++ b/js/src/ion/shared/Lowering-shared.h
@@ -108,16 +108,17 @@ class LIRGeneratorShared : public MInstr
     inline LUse useFixed(MDefinition *mir, Register reg);
     inline LUse useFixed(MDefinition *mir, FloatRegister reg);
     inline LAllocation useOrConstant(MDefinition *mir);
     // "Any" is architecture dependent, and will include registers and stack slots on X86,
     // and only registers on ARM.
     inline LAllocation useAnyOrConstant(MDefinition *mir);
     inline LAllocation useKeepaliveOrConstant(MDefinition *mir);
     inline LAllocation useRegisterOrConstant(MDefinition *mir);
+    inline LAllocation useRegisterOrNonDoubleConstant(MDefinition *mir);
 
 #ifdef JS_NUNBOX32
     inline LUse useType(MDefinition *mir, LUse::Policy policy);
     inline LUse usePayload(MDefinition *mir, LUse::Policy policy);
     inline LUse usePayloadAtStart(MDefinition *mir, LUse::Policy policy);
     inline LUse usePayloadInRegisterAtStart(MDefinition *mir);
 
     // Adds a box input to an instruction, setting operand |n| to the type and
--- a/js/src/ion/shared/MacroAssembler-x86-shared.h
+++ b/js/src/ion/shared/MacroAssembler-x86-shared.h
@@ -122,16 +122,24 @@ class MacroAssemblerX86Shared : public A
     }
     void add32(Imm32 imm, Register dest) {
         addl(imm, dest);
     }
     void sub32(Imm32 imm, Register dest) {
         subl(imm, dest);
     }
 
+    void branch32(Condition cond, const Address &lhs, const Register &rhs, Label *label) {
+        cmpl(Operand(lhs), rhs);
+        j(cond, label);
+    }
+    void branch32(Condition cond, const Address &lhs, Imm32 imm, Label *label) {
+        cmpl(Operand(lhs), imm);
+        j(cond, label);
+    }
     void branch32(Condition cond, const Register &lhs, Imm32 imm, Label *label) {
         cmpl(lhs, imm);
         j(cond, label);
     }
     void branch32(Condition cond, const Register &lhs, const Register &rhs, Label *label) {
         cmpl(lhs, rhs);
         j(cond, label);
     }
@@ -179,16 +187,22 @@ class MacroAssemblerX86Shared : public A
     Condition testDoubleTruthy(bool truthy, const FloatRegister &reg) {
         xorpd(ScratchFloatReg, ScratchFloatReg);
         ucomisd(ScratchFloatReg, reg);
         return truthy ? NonZero : Zero;
     }
     void load32(const Address &address, Register dest) {
         movl(Operand(address), dest);
     }
+    void store32(Imm32 src, const Address &dest) {
+        movl(src, Operand(dest));
+    }
+    void store32(const Register &src, const Address &dest) {
+        movl(src, Operand(dest));
+    }
     void callWithExitFrame(IonCode *target) {
         uint32 descriptor = MakeFrameDescriptor(framePushed(), IonFrame_JS);
         Push(Imm32(descriptor));
         call(target);
     }
     void callIon(const Register &callee) {
         call(callee);
     }
--- a/js/src/ion/x64/CodeGenerator-x64.cpp
+++ b/js/src/ion/x64/CodeGenerator-x64.cpp
@@ -296,37 +296,23 @@ CodeGeneratorX64::visitLoadElementT(LLoa
 {
     Operand source = createArrayElementOperand(ToRegister(load->elements()), load->index());
     loadUnboxedValue(source, load->mir()->type(), load->output());
 
     JS_ASSERT(!load->mir()->needsHoleCheck());
     return true;
 }
 
-bool
-CodeGeneratorX64::visitStoreElementV(LStoreElementV *store)
+
+void
+CodeGeneratorX64::storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
+                                    const Register &elements, const LAllocation *index)
 {
-    Operand dest = createArrayElementOperand(ToRegister(store->elements()), store->index());
-    const ValueOperand value = ToValue(store, LStoreElementV::Value);
-
-    masm.storeValue(value, dest);
-    return true;
-}
-
-bool
-CodeGeneratorX64::visitStoreElementT(LStoreElementT *store)
-{
-    Operand dest = createArrayElementOperand(ToRegister(store->elements()), store->index());
-
-    const LAllocation *value = store->value();
-    MIRType valueType = store->mir()->value()->type();
-    MIRType elementType = store->mir()->elementType();
-
+    Operand dest = createArrayElementOperand(elements, index);
     storeUnboxedValue(value, valueType, dest, elementType);
-    return true;
 }
 
 bool
 CodeGeneratorX64::visitWriteBarrierV(LWriteBarrierV *barrier)
 {
     // TODO: Perform C++ call to some WriteBarrier stub.
     // For now, we just guard and breakpoint on failure.
 
--- a/js/src/ion/x64/CodeGenerator-x64.h
+++ b/js/src/ion/x64/CodeGenerator-x64.h
@@ -60,33 +60,34 @@ class CodeGeneratorX64 : public CodeGene
 
     // This returns the tag in ScratchReg.
     Assembler::Condition testStringTruthy(bool truthy, const ValueOperand &value);
 
     void loadUnboxedValue(Operand source, MIRType type, const LDefinition *dest);
     void storeUnboxedValue(const LAllocation *value, MIRType valueType,
                            Operand dest, MIRType slotType);
 
+    void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
+                           const Register &elements, const LAllocation *index);
+
   public:
     CodeGeneratorX64(MIRGenerator *gen, LIRGraph &graph);
 
   public:
     bool visitValue(LValue *value);
     bool visitOsrValue(LOsrValue *value);
     bool visitBox(LBox *box);
     bool visitUnbox(LUnbox *unbox);
     bool visitDouble(LDouble *ins);
     bool visitLoadSlotV(LLoadSlotV *ins);
     bool visitLoadSlotT(LLoadSlotT *load);
     bool visitStoreSlotT(LStoreSlotT *store);
     bool visitWriteBarrierV(LWriteBarrierV *barrier);
     bool visitWriteBarrierT(LWriteBarrierT *barrier);
     bool visitLoadElementT(LLoadElementT *load);
-    bool visitStoreElementV(LStoreElementV *store);
-    bool visitStoreElementT(LStoreElementT *store);
     bool visitImplicitThis(LImplicitThis *lir);
     bool visitRecompileCheck(LRecompileCheck *lir);
 };
 
 typedef CodeGeneratorX64 CodeGeneratorSpecific;
 
 } // namespace ion
 } // namespace js
--- a/js/src/ion/x64/MacroAssembler-x64.h
+++ b/js/src/ion/x64/MacroAssembler-x64.h
@@ -129,16 +129,19 @@ class MacroAssemblerX64 : public MacroAs
     }
     void storeValue(const Value &val, Address 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));
+    }
     void loadValue(Operand src, ValueOperand val) {
         movq(src, val.valueReg());
     }
     void loadValue(Address src, ValueOperand val) {
         loadValue(Operand(src), val);
     }
     void loadValue(const BaseIndex &src, ValueOperand val) {
         loadValue(Operand(src), val);
--- a/js/src/ion/x86/CodeGenerator-x86.cpp
+++ b/js/src/ion/x86/CodeGenerator-x86.cpp
@@ -269,50 +269,36 @@ CodeGeneratorX86::visitLoadElementT(LLoa
         masm.loadInt32OrDouble(source, ToFloatRegister(load->output()));
     else
         masm.movl(masm.ToPayload(source), ToRegister(load->output()));
 
     JS_ASSERT(!load->mir()->needsHoleCheck());
     return true;
 }
 
-bool
-CodeGeneratorX86::visitStoreElementV(LStoreElementV *store)
+void
+CodeGeneratorX86::storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
+                                    const Register &elements, const LAllocation *index)
 {
-    Operand dest = createArrayElementOperand(ToRegister(store->elements()), store->index());
-    const ValueOperand value = ToValue(store, LStoreElementV::Value);
-
-    masm.storeValue(value, dest);
-    return true;
-}
-
-bool
-CodeGeneratorX86::visitStoreElementT(LStoreElementT *store)
-{
-    Operand dest = createArrayElementOperand(ToRegister(store->elements()), store->index());
-
-    const LAllocation *value = store->value();
-    MIRType valueType = store->mir()->value()->type();
+    Operand dest = createArrayElementOperand(elements, index);
 
     if (valueType == MIRType_Double) {
         masm.movsd(ToFloatRegister(value), dest);
-        return true;
+        return;
     }
 
     // Store the type tag if needed.
-    if (valueType != store->mir()->elementType())
+    if (valueType != elementType)
         masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest);
 
     // Store the payload.
     if (value->isConstant())
         masm.storePayload(*value->toConstant(), dest);
     else
         masm.storePayload(ToRegister(value), dest);
-
-    return true;
 }
 
 bool
 CodeGeneratorX86::visitWriteBarrierV(LWriteBarrierV *barrier)
 {
     // TODO: Perform C++ call to some WriteBarrier stub.
     // For now, we just guard and breakpoint on failure.
 
--- a/js/src/ion/x86/CodeGenerator-x86.h
+++ b/js/src/ion/x86/CodeGenerator-x86.h
@@ -76,16 +76,19 @@ class CodeGeneratorX86 : public CodeGene
 
   protected:
     ValueOperand ToValue(LInstruction *ins, size_t pos);
     ValueOperand ToOutValue(LInstruction *ins);
 
     // Functions for LTestVAndBranch.
     Assembler::Condition testStringTruthy(bool truthy, const ValueOperand &value);
 
+    void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
+                           const Register &elements, const LAllocation *index);
+
   protected:
     void linkAbsoluteLabels();
 
   public:
     CodeGeneratorX86(MIRGenerator *gen, LIRGraph &graph);
 
   public:
     bool visitBox(LBox *box);
@@ -95,18 +98,16 @@ class CodeGeneratorX86 : public CodeGene
     bool visitOsrValue(LOsrValue *value);
     bool visitDouble(LDouble *ins);
     bool visitLoadSlotV(LLoadSlotV *load);
     bool visitLoadSlotT(LLoadSlotT *load);
     bool visitStoreSlotT(LStoreSlotT *store);
     bool visitWriteBarrierV(LWriteBarrierV *barrier);
     bool visitWriteBarrierT(LWriteBarrierT *barrier);
     bool visitLoadElementT(LLoadElementT *load);
-    bool visitStoreElementV(LStoreElementV *store);
-    bool visitStoreElementT(LStoreElementT *store);
     bool visitImplicitThis(LImplicitThis *lir);
     bool visitRecompileCheck(LRecompileCheck *lir);
 };
 
 typedef CodeGeneratorX86 CodeGeneratorSpecific;
 
 } // namespace ion
 } // namespace js
--- a/js/src/ion/x86/MacroAssembler-x86.h
+++ b/js/src/ion/x86/MacroAssembler-x86.h
@@ -140,16 +140,19 @@ class MacroAssemblerX86 : public MacroAs
         storeTypeTag(ImmTag(JSVAL_TYPE_TO_TAG(type)), Operand(dest));
         storePayload(reg, Operand(dest));
     }
     void storeValue(const Value &val, Address 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) {
         Operand payload = ToPayload(src);
         Operand type = ToType(src);
 
         // Ensure that loading the payload does not erase the pointer to the
         // Value in memory.
         if (Register::FromCode(type.base()) != val.payloadReg()) {
             movl(payload, val.payloadReg());
@@ -171,17 +174,20 @@ class MacroAssemblerX86 : public MacroAs
     }
     void popValue(ValueOperand val) {
         pop(val.payloadReg());
         pop(val.typeReg());
     }
     void pushValue(const Value &val) {
         jsval_layout jv = JSVAL_TO_IMPL(val);
         push(Imm32(jv.s.tag));
-        push(Imm32(jv.s.payload.i32));
+        if (val.isGCThing())
+            push(ImmGCPtr(reinterpret_cast<gc::Cell *>(val.toGCThing())));
+        else
+            push(Imm32(jv.s.payload.i32));
     }
     void pushValue(JSValueType type, Register reg) {
         push(ImmTag(JSVAL_TYPE_TO_TAG(type)));
         push(reg);
     }
     void storePayload(const Value &val, Operand dest) {
         jsval_layout jv = JSVAL_TO_IMPL(val);
         if (val.isMarkable())
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/setelem-hole.js
@@ -0,0 +1,68 @@
+// Storing a typed value.
+function test1() {
+    var a = [];
+    for (var i=0; i<130; i++) {
+        a[i] = i + 1;
+    }
+    return a;
+}
+
+var arr = test1();
+assertEq(arr.length, 130);
+
+for (var i=0; i<130; i++)
+    assertEq(arr[i], i + 1);
+
+// Storing a Value.
+function getValue(x) {
+    var y = x & 0x3;
+    if (y == 0) return null;
+    if (y == 1) return true;
+    if (y == 2) return 1.23;
+    if (y == 3) return Math;
+    assertEq(0, 1);
+}
+getValue(0);
+getValue(1);
+
+function test2() {
+    var a = [];
+    for (var i=0; i<130; i++) {
+        a[i] = getValue(i);
+    }
+    return a;
+}
+
+var arr = test2();
+assertEq(arr.length, 130);
+
+for (var i=0; i<130; i++)
+    assertEq(arr[i], getValue(i));
+
+// Make sure the length-property is not updated if it's greater than
+// the (new) initialized length.
+function test3(arr, start, end) {
+    for (var i=start; i<end; i++) {
+        arr[i] = 10;
+    }
+}
+var a = new Array(200);
+test3(a, 10, 130);
+assertEq(a.length, 200);
+
+for (var i=10; i<130; i++)
+    assertEq(a[i], 10);
+
+test3(a, 130, 220);
+assertEq(a.length, 220);
+
+// Test constant index.
+function test4() {
+    var a = [0, 1, 2, 3, 4, 5];
+    for (var i=0; i<150; i++) {
+        a[6] = i;
+    }
+    return a;
+}
+var arr = test4();
+assertEq(arr[6], 149);