[INFER] LICM for typed arrays, bug 671084. r=bhackett
authorJan de Mooij <jandemooij@gmail.com>
Thu, 28 Jul 2011 11:53:29 +0200
changeset 77390 235a8bfe2665082640941e8247c119c8cde3fed6
parent 77389 c7a7d9ff99da33f98d56bff5b673df3d97116de3
child 77391 65c33bba9d01dc819ff0c68d8a0c057aaeb42598
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs671084
milestone8.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
[INFER] LICM for typed arrays, bug 671084. r=bhackett
js/src/methodjit/FastOps.cpp
js/src/methodjit/LoopState.cpp
js/src/methodjit/LoopState.h
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -1105,17 +1105,17 @@ mjit::Compiler::jsop_setelem_dense()
 
     // Register to hold the computed slots pointer for the object. If we can
     // hoist the initialized length check, we make the slots pointer loop
     // invariant and never access the object itself.
     RegisterID slotsReg;
     analyze::CrossSSAValue objv(a->inlineIndex, analysis->poppedValue(PC, 2));
     analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 1));
     bool hoisted = loop && id->isType(JSVAL_TYPE_INT32) &&
-        loop->hoistArrayLengthCheck(objv, indexv);
+        loop->hoistArrayLengthCheck(DENSE_ARRAY, objv, indexv);
 
     if (hoisted) {
         FrameEntry *slotsFe = loop->invariantArraySlots(objv);
         slotsReg = frame.tempRegForData(slotsFe);
 
         frame.unpinEntry(vr);
         if (pinKey)
             frame.unpinReg(key.reg());
@@ -1370,28 +1370,41 @@ mjit::Compiler::jsop_setelem_typed(int a
     // Allocate and pin object and key regs.
     Int32Key key = id->isConstant()
                  ? Int32Key::FromConstant(id->getValue().toInt32())
                  : Int32Key::FromRegister(frame.tempRegForData(id));
 
     bool pinKey = !key.isConstant() && !frame.haveSameBacking(id, value);
     if (pinKey)
         frame.pinReg(key.reg());
-    RegisterID objReg = frame.copyDataIntoReg(obj);
 
-    // Get the internal typed array.
-    masm.loadPtr(Address(objReg, offsetof(JSObject, privateData)), objReg);
+    analyze::CrossSSAValue objv(a->inlineIndex, analysis->poppedValue(PC, 1));
+    analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 0));
+    bool hoisted = loop && id->isType(JSVAL_TYPE_INT32) &&
+        loop->hoistArrayLengthCheck(TYPED_ARRAY, objv, indexv);
 
-    // Bounds check.
-    Jump lengthGuard = masm.guardArrayExtent(TypedArray::lengthOffset(),
-                                             objReg, key, Assembler::BelowOrEqual);
-    stubcc.linkExit(lengthGuard, Uses(3));
+    RegisterID objReg;
+    if (hoisted) {
+        FrameEntry *slotsFe = loop->invariantArraySlots(objv);
+        objReg = frame.tempRegForData(slotsFe);
+        frame.pinReg(objReg);
+    } else {
+        objReg = frame.copyDataIntoReg(obj);
 
-    // Load the array's packed data vector.
-    masm.loadPtr(Address(objReg, js::TypedArray::dataOffset()), objReg);
+        // Get the internal typed array.
+        masm.loadPtr(Address(objReg, offsetof(JSObject, privateData)), objReg);
+
+        // Bounds check.
+        Jump lengthGuard = masm.guardArrayExtent(TypedArray::lengthOffset(),
+                                                 objReg, key, Assembler::BelowOrEqual);
+        stubcc.linkExit(lengthGuard, Uses(3));
+
+        // Load the array's packed data vector.
+        masm.loadPtr(Address(objReg, js::TypedArray::dataOffset()), objReg);
+    }
 
     // Unpin value so that convertForTypedArray can assign a new data
     // register using tempRegInMaskForData.
     frame.unpinEntry(vr);
 
     // Make sure key is pinned.
     if (frame.haveSameBacking(id, value)) {
         frame.pinReg(key.reg());
@@ -1407,17 +1420,20 @@ mjit::Compiler::jsop_setelem_typed(int a
     if (allocated) {
         if (vr.isFPRegister())
             frame.freeReg(vr.fpReg());
         else
             frame.freeReg(vr.dataReg());
     }
     if (pinKey)
         frame.unpinReg(key.reg());
-    frame.freeReg(objReg);
+    if (hoisted)
+        frame.unpinReg(objReg);
+    else
+        frame.freeReg(objReg);
 
     stubcc.leave();
     OOL_STUBCALL(STRICT_VARIANT(stubs::SetElem), REJOIN_FALLTHROUGH);
 
     frame.shimmy(2);
     stubcc.rejoin(Changes(2));
 }
 #endif /* JS_METHODJIT_TYPED_ARRAY */
@@ -1666,17 +1682,17 @@ mjit::Compiler::jsop_getelem_dense(bool 
     // If we know the result of the GETELEM may be undefined, then misses on the
     // initialized length or hole checks can just produce an undefined value.
     // We checked in the caller that prototypes do not have indexed properties.
     bool allowUndefined = mayPushUndefined(0);
 
     analyze::CrossSSAValue objv(a->inlineIndex, analysis->poppedValue(PC, 1));
     analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 0));
     bool hoisted = loop && id->isType(JSVAL_TYPE_INT32) &&
-        loop->hoistArrayLengthCheck(objv, indexv);
+        loop->hoistArrayLengthCheck(DENSE_ARRAY, objv, indexv);
 
     // Get a register with either the object or its slots, depending on whether
     // we are hoisting the bounds check.
     RegisterID baseReg;
     if (hoisted) {
         FrameEntry *slotsFe = loop->invariantArraySlots(objv);
         baseReg = frame.tempRegForData(slotsFe);
     } else {
@@ -1856,17 +1872,41 @@ mjit::Compiler::jsop_getelem_typed(int a
     }
 
     // Load object and key.
     Int32Key key = id->isConstant()
                  ? Int32Key::FromConstant(id->getValue().toInt32())
                  : Int32Key::FromRegister(frame.tempRegForData(id));
     if (!key.isConstant())
         frame.pinReg(key.reg());
-    RegisterID objReg = frame.copyDataIntoReg(obj);
+
+    analyze::CrossSSAValue objv(a->inlineIndex, analysis->poppedValue(PC, 1));
+    analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 0));
+    bool hoisted = loop && id->isType(JSVAL_TYPE_INT32) &&
+        loop->hoistArrayLengthCheck(TYPED_ARRAY, objv, indexv);
+
+    RegisterID objReg;
+    if (hoisted) {
+        FrameEntry *slotsFe = loop->invariantArraySlots(objv);
+        objReg = frame.tempRegForData(slotsFe);
+        frame.pinReg(objReg);
+    } else {
+        objReg = frame.copyDataIntoReg(obj);
+
+        // Get the internal typed array.
+        masm.loadPtr(Address(objReg, offsetof(JSObject, privateData)), objReg);
+
+        // Bounds check.
+        Jump lengthGuard = masm.guardArrayExtent(TypedArray::lengthOffset(),
+                                                 objReg, key, Assembler::BelowOrEqual);
+        stubcc.linkExit(lengthGuard, Uses(2));
+
+        // Load the array's packed data vector.
+        masm.loadPtr(Address(objReg, js::TypedArray::dataOffset()), objReg);
+    }
 
     // We can load directly into an FP-register if the following conditions
     // are met:
     // 1) The array is an Uint32Array or a float array (loadFromTypedArray
     //    can't load into an FP-register for other arrays).
     // 2) The result is definitely a double (the result type set can include
     //    other types after reading out-of-bound values).
     AnyRegisterID dataReg;
@@ -1885,31 +1925,23 @@ mjit::Compiler::jsop_getelem_typed(int a
         // loadFromTypedArray expects a type register for Uint32Array or
         // float arrays. Also allocate a type register if the result may not
         // be int32 (due to reading out-of-bound values) or if there's a
         // type barrier.
         if (maybeReadFloat || type != JSVAL_TYPE_INT32)
             typeReg = frame.allocReg();
     }
 
-    // Get the internal typed array.
-    masm.loadPtr(Address(objReg, offsetof(JSObject, privateData)), objReg);
-
-    // Bounds check.
-    Jump lengthGuard = masm.guardArrayExtent(TypedArray::lengthOffset(),
-                                             objReg, key, Assembler::BelowOrEqual);
-    stubcc.linkExit(lengthGuard, Uses(2));
-
-    // Load the array's packed data vector.
-    masm.loadPtr(Address(objReg, js::TypedArray::dataOffset()), objReg);
-
     // Load value from the array.
     masm.loadFromTypedArray(atype, objReg, key, typeReg, dataReg, tempReg);
 
-    frame.freeReg(objReg);
+    if (hoisted)
+        frame.unpinReg(objReg);
+    else
+        frame.freeReg(objReg);
     if (!key.isConstant())
         frame.unpinReg(key.reg());
     if (tempReg.isSet())
         frame.freeReg(tempReg.reg());
 
     stubcc.leave();
     OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH);
 
--- a/js/src/methodjit/LoopState.cpp
+++ b/js/src/methodjit/LoopState.cpp
@@ -317,29 +317,29 @@ LoopState::entryRedundant(const Invarian
      * initialized lengths are always <= JSObject::NSLOTS_LIMIT, check for
      * integer overflow checks redundant given initialized length checks.
      * If Y <= c0 and Y + c1 < initlen(array):
      *
      * Y <= c0
      * initlen(array) - c1 <= c0
      * NSLOTS_LIMIT <= c0 + c1
      */
-    if (e0.kind == InvariantEntry::RANGE_CHECK && e1.kind == InvariantEntry::BOUNDS_CHECK &&
+    if (e0.kind == InvariantEntry::RANGE_CHECK && e1.isBoundsCheck() &&
         value01 == value11 && value02 == value12) {
         int32 constant;
         if (c1 >= 0)
             constant = c0;
         else if (!SafeAdd(c0, c1, &constant))
             return false;
         return constant >= JSObject::NSLOTS_LIMIT;
     }
 
     /* Look for matching tests that differ only in their constants. */
     if (e0.kind == e1.kind && array0 == array1 && value01 == value11 && value02 == value12) {
-        if (e0.kind == InvariantEntry::BOUNDS_CHECK) {
+        if (e0.isBoundsCheck()) {
             /* If e0 is X >= Y + c0 and e1 is X >= Y + c1, e0 is redundant if c0 <= c1 */
             return (c0 <= c1);
         } else {
             /* If e0 is c0 >= Y and e1 is c1 >= Y, e0 is redundant if c0 >= c1 */
             return (c0 >= c1);
         }
     }
 
@@ -378,66 +378,71 @@ LoopState::checkRedundantEntry(const Inv
             length--;
         }
     }
 
     return false;
 }
 
 bool
-LoopState::addHoistedCheck(uint32 arraySlot, uint32 valueSlot1, uint32 valueSlot2, int32 constant)
+LoopState::addHoistedCheck(InvariantArrayKind arrayKind, uint32 arraySlot,
+                           uint32 valueSlot1, uint32 valueSlot2, int32 constant)
 {
 #ifdef DEBUG
     JS_ASSERT_IF(valueSlot1 == UNASSIGNED, valueSlot2 == UNASSIGNED);
+    const char *field = (arrayKind == DENSE_ARRAY) ? "initlen" : "length";
     if (valueSlot1 == UNASSIGNED) {
-        JaegerSpew(JSpew_Analysis, "Hoist initlen > %d\n", constant);
+        JaegerSpew(JSpew_Analysis, "Hoist %s > %d\n", field, constant);
     } else if (valueSlot2 == UNASSIGNED) {
-        JaegerSpew(JSpew_Analysis, "Hoisted as initlen > %s + %d\n",
+        JaegerSpew(JSpew_Analysis, "Hoisted as %s > %s + %d\n", field,
                    frame.entryName(valueSlot1), constant);
     } else {
-        JaegerSpew(JSpew_Analysis, "Hoisted as initlen > %s + %s + %d\n",
+        JaegerSpew(JSpew_Analysis, "Hoisted as %s > %s + %s + %d\n", field,
                    frame.entryName(valueSlot1), frame.entryName(valueSlot2), constant);
     }
 #endif
 
     InvariantEntry entry;
-    entry.kind = InvariantEntry::BOUNDS_CHECK;
+    entry.kind = (arrayKind == DENSE_ARRAY)
+                 ? InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK
+                 : InvariantEntry::TYPED_ARRAY_BOUNDS_CHECK;
     entry.u.check.arraySlot = arraySlot;
     entry.u.check.valueSlot1 = valueSlot1;
     entry.u.check.valueSlot2 = valueSlot2;
     entry.u.check.constant = constant;
 
     if (checkRedundantEntry(entry))
         return true;
 
     /*
      * Maintain an invariant that for any array with a hoisted bounds check,
      * we also have a loop invariant slot to hold the array's slots pointer.
      * The compiler gets invariant array slots only for accesses with a hoisted
      * bounds check, so this makes invariantSlots infallible.
      */
     bool hasInvariantSlots = false;
+    InvariantEntry::EntryKind slotsKind = (arrayKind == DENSE_ARRAY)
+                                          ? InvariantEntry::DENSE_ARRAY_SLOTS
+                                          : InvariantEntry::TYPED_ARRAY_SLOTS;
     for (unsigned i = 0; !hasInvariantSlots && i < invariantEntries.length(); i++) {
         InvariantEntry &entry = invariantEntries[i];
-        if (entry.kind == InvariantEntry::INVARIANT_SLOTS &&
-            entry.u.array.arraySlot == arraySlot) {
+        if (entry.kind == slotsKind && entry.u.array.arraySlot == arraySlot)
             hasInvariantSlots = true;
-        }
     }
     if (!hasInvariantSlots) {
         uint32 which = frame.allocTemporary();
         if (which == uint32(-1))
             return false;
         FrameEntry *fe = frame.getTemporary(which);
 
         JaegerSpew(JSpew_Analysis, "Using %s for loop invariant slots of %s\n",
                    frame.entryName(fe), frame.entryName(arraySlot));
 
         InvariantEntry slotsEntry;
-        slotsEntry.kind = InvariantEntry::INVARIANT_SLOTS;
+        slotsEntry.kind = slotsKind;
         slotsEntry.u.array.arraySlot = arraySlot;
         slotsEntry.u.array.temporary = which;
         invariantEntries.append(slotsEntry);
     }
 
     invariantEntries.append(entry);
     return true;
 }
@@ -506,21 +511,22 @@ LoopState::setLoopReg(AnyRegisterID reg,
          */
         RegisterAllocation *alloc = outerAnalysis->getAllocation(lifetime->entry);
         JS_ASSERT(alloc && !alloc->assigned(reg));
         alloc->set(reg, slot, true);
     }
 }
 
 bool
-LoopState::hoistArrayLengthCheck(const CrossSSAValue &obj, const CrossSSAValue &index)
+LoopState::hoistArrayLengthCheck(InvariantArrayKind arrayKind, const CrossSSAValue &obj,
+                                 const CrossSSAValue &index)
 {
     /*
      * Note: this method requires that the index is definitely an integer, and
-     * that obj is either a dense array or not an object.
+     * that obj is either a dense array, a typed array or not an object.
      */
     if (skipAnalysis)
         return false;
 
     uint32 objSlot;
     int32 objConstant;
     if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0)
         return false;
@@ -535,17 +541,17 @@ LoopState::hoistArrayLengthCheck(const C
 
     /*
      * Check for an overlap with the arrays we think might grow in this loop.
      * This information is only a guess; if we don't think the array can grow
      * but it actually can, we will probably recompile after the hoisted
      * bounds check fails.
      */
     TypeSet *objTypes = ssa->getValueTypes(obj);
-    if (!growArrays.empty()) {
+    if (arrayKind == DENSE_ARRAY && !growArrays.empty()) {
         unsigned count = objTypes->getObjectCount();
         for (unsigned i = 0; i < count; i++) {
             if (objTypes->getSingleObject(i) != NULL) {
                 JaegerSpew(JSpew_Analysis, "Object might be a singleton");
                 return false;
             }
             TypeObject *object = objTypes->getTypeObject(i);
             if (object && hasGrowArray(object)) {
@@ -563,22 +569,22 @@ LoopState::hoistArrayLengthCheck(const C
     int32 indexConstant;
     if (!getEntryValue(index, &indexSlot, &indexConstant)) {
         JaegerSpew(JSpew_Analysis, "Could not compute index in terms of loop entry state\n");
         return false;
     }
 
     if (indexSlot == UNASSIGNED) {
         /* Hoist checks on x[n] accesses for constant n. */
-        return addHoistedCheck(objSlot, UNASSIGNED, UNASSIGNED, indexConstant);
+        return addHoistedCheck(arrayKind, objSlot, UNASSIGNED, UNASSIGNED, indexConstant);
     }
 
     if (loopInvariantEntry(indexSlot)) {
         /* Hoist checks on x[y] accesses when y is loop invariant. */
-        return addHoistedCheck(objSlot, indexSlot, UNASSIGNED, indexConstant);
+        return addHoistedCheck(arrayKind, objSlot, indexSlot, UNASSIGNED, indexConstant);
     }
 
     /*
      * If the LHS can decrease in the loop, it could become negative and
      * underflow the array. We currently only hoist bounds checks for loops
      * which walk arrays going forward.
      */
     if (!outerAnalysis->liveness(indexSlot).nonDecreasing(outerScript, lifetime)) {
@@ -604,17 +610,17 @@ LoopState::hoistArrayLengthCheck(const C
          * Check that the LHS is nonnegative every time we rejoin the loop.
          * This is only really necessary on initial loop entry. Note that this
          * test is not sensitive to changes to the LHS between when we make
          * the test and the start of the next iteration, as we've ensured the
          * LHS is nondecreasing within the body of the loop.
          */
         addNegativeCheck(indexSlot, indexConstant);
 
-        return addHoistedCheck(objSlot, testRHS, UNASSIGNED, constant);
+        return addHoistedCheck(arrayKind, objSlot, testRHS, UNASSIGNED, constant);
     }
 
     /*
      * If the access is of the form x[y + a] where we know that z >= b at the
      * head of the loop and y has a linear relationship with z such that
      * (y + z) always has the same value at the head of the loop, hoist as
      * follows:
      *
@@ -624,17 +630,17 @@ LoopState::hoistArrayLengthCheck(const C
      * y + z + a - b < initlen(x)
      */
     if (hasTestLinearRelationship(indexSlot)) {
         int32 constant;
         if (!SafeSub(indexConstant, testConstant, &constant))
             return false;
 
         addNegativeCheck(indexSlot, indexConstant);
-        return addHoistedCheck(objSlot, indexSlot, testLHS, constant);
+        return addHoistedCheck(arrayKind, objSlot, indexSlot, testLHS, constant);
     }
 
     JaegerSpew(JSpew_Analysis, "No match found\n");
     return false;
 }
 
 bool
 LoopState::hoistArgsLengthCheck(const CrossSSAValue &index)
@@ -733,19 +739,25 @@ LoopState::invariantArraySlots(const Cro
 
     uint32 objSlot;
     int32 objConstant;
     if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0) {
         JS_NOT_REACHED("Bad value");
         return NULL;
     }
 
+    /*
+     * Note: we don't have to check arrayKind (dense array or typed array) here,
+     * because an array cannot have entries for both dense array slots and typed
+     * array slots.
+     */
     for (unsigned i = 0; i < invariantEntries.length(); i++) {
         InvariantEntry &entry = invariantEntries[i];
-        if (entry.kind == InvariantEntry::INVARIANT_SLOTS &&
+        if ((entry.kind == InvariantEntry::DENSE_ARRAY_SLOTS ||
+             entry.kind == InvariantEntry::TYPED_ARRAY_SLOTS) &&
             entry.u.array.arraySlot == objSlot) {
             return frame.getTemporary(entry.u.array.temporary);
         }
     }
 
     /* addHoistedCheck should have ensured there is an entry for the slots. */
     JS_NOT_REACHED("Missing invariant slots");
     return NULL;
@@ -810,27 +822,55 @@ LoopState::invariantLength(const CrossSS
         entry.u.array.temporary = which;
         invariantEntries.append(entry);
 
         JaegerSpew(JSpew_Analysis, "Using %s for loop invariant args length\n",
                    frame.entryName(fe));
         return fe;
     }
 
+    /*
+     * Note: we don't have to check arrayKind (dense array or typed array) here,
+     * because an array cannot have entries for both dense array length and typed
+     * array length.
+     */
     for (unsigned i = 0; i < invariantEntries.length(); i++) {
         InvariantEntry &entry = invariantEntries[i];
-        if (entry.kind == InvariantEntry::INVARIANT_LENGTH &&
+        if ((entry.kind == InvariantEntry::DENSE_ARRAY_LENGTH ||
+             entry.kind == InvariantEntry::TYPED_ARRAY_LENGTH) &&
             entry.u.array.arraySlot == objSlot) {
             return frame.getTemporary(entry.u.array.temporary);
         }
     }
 
     if (!loopInvariantEntry(objSlot))
         return NULL;
 
+    /* Hoist 'length' access on typed arrays. */
+    if (!objTypes->hasObjectFlags(cx, OBJECT_FLAG_NON_TYPED_ARRAY)) {
+        /* Recompile if object type changes. */
+        objTypes->addFreeze(cx);
+
+        uint32 which = frame.allocTemporary();
+        if (which == uint32(-1))
+            return NULL;
+        FrameEntry *fe = frame.getTemporary(which);
+
+        JaegerSpew(JSpew_Analysis, "Using %s for loop invariant typed array length of %s\n",
+                   frame.entryName(fe), frame.entryName(objSlot));
+
+        InvariantEntry entry;
+        entry.kind = InvariantEntry::TYPED_ARRAY_LENGTH;
+        entry.u.array.arraySlot = objSlot;
+        entry.u.array.temporary = which;
+        invariantEntries.append(entry);
+
+        return fe;
+    }
+
     if (objTypes->hasObjectFlags(cx, OBJECT_FLAG_NON_DENSE_ARRAY))
         return NULL;
 
     /*
      * Don't make 'length' loop invariant if the loop might directly write
      * to the elements of any of the accessed arrays. This could invoke an
      * inline path which updates the length. There is no need to check the
      * modset for direct 'length' writes, as we don't generate inline paths
@@ -845,21 +885,21 @@ LoopState::invariantLength(const CrossSS
     }
     objTypes->addFreeze(cx);
 
     uint32 which = frame.allocTemporary();
     if (which == uint32(-1))
         return NULL;
     FrameEntry *fe = frame.getTemporary(which);
 
-    JaegerSpew(JSpew_Analysis, "Using %s for loop invariant length of %s\n",
+    JaegerSpew(JSpew_Analysis, "Using %s for loop invariant dense array length of %s\n",
                frame.entryName(fe), frame.entryName(objSlot));
 
     InvariantEntry entry;
-    entry.kind = InvariantEntry::INVARIANT_LENGTH;
+    entry.kind = InvariantEntry::DENSE_ARRAY_LENGTH;
     entry.u.array.arraySlot = objSlot;
     entry.u.array.temporary = which;
     invariantEntries.append(entry);
 
     return fe;
 }
 
 FrameEntry *
@@ -1262,23 +1302,29 @@ LoopState::restoreInvariants(jsbytecode 
 
     RegisterID T0 = regs.takeAnyReg().reg();
     RegisterID T1 = regs.takeAnyReg().reg();
 
     for (unsigned i = 0; i < invariantEntries.length(); i++) {
         const InvariantEntry &entry = invariantEntries[i];
         switch (entry.kind) {
 
-          case InvariantEntry::BOUNDS_CHECK: {
+          case InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK:
+          case InvariantEntry::TYPED_ARRAY_BOUNDS_CHECK: {
             /*
              * Hoisted bounds checks always have preceding invariant slots
              * in the invariant list, so don't recheck this is an object.
              */
             masm.loadPayload(frame.addressOf(entry.u.check.arraySlot), T0);
-            masm.load32(Address(T0, offsetof(JSObject, initializedLength)), T0);
+            if (entry.kind == InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK) {
+                masm.load32(Address(T0, offsetof(JSObject, initializedLength)), T0);
+            } else {
+                masm.loadPtr(Address(T0, offsetof(JSObject, privateData)), T0);
+                masm.load32(Address(T0, TypedArray::lengthOffset()), T0);
+            }
 
             int32 constant = entry.u.check.constant;
 
             if (entry.u.check.valueSlot1 != uint32(-1)) {
                 constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot1);
                 masm.loadPayload(frame.addressOf(entry.u.check.valueSlot1), T1);
                 if (entry.u.check.valueSlot2 != uint32(-1)) {
                     constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot2);
@@ -1328,37 +1374,59 @@ LoopState::restoreInvariants(jsbytecode 
                                                  Imm32(entry.u.check.constant), T0);
                 jumps->append(overflow);
             }
             Jump j = masm.branch32(Assembler::LessThan, T0, Imm32(0));
             jumps->append(j);
             break;
           }
 
-          case InvariantEntry::INVARIANT_SLOTS:
-          case InvariantEntry::INVARIANT_LENGTH: {
+          case InvariantEntry::DENSE_ARRAY_SLOTS:
+          case InvariantEntry::DENSE_ARRAY_LENGTH: {
             uint32 array = entry.u.array.arraySlot;
             Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(array));
             jumps->append(notObject);
             masm.loadPayload(frame.addressOf(array), T0);
 
-            uint32 offset = (entry.kind == InvariantEntry::INVARIANT_SLOTS)
+            uint32 offset = (entry.kind == InvariantEntry::DENSE_ARRAY_SLOTS)
                 ? JSObject::offsetOfSlots()
                 : offsetof(JSObject, privateData);
 
             Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
 
             masm.loadPtr(Address(T0, offset), T0);
-            if (entry.kind == InvariantEntry::INVARIANT_LENGTH)
+            if (entry.kind == InvariantEntry::DENSE_ARRAY_LENGTH)
                 masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address);
             else
                 masm.storePtr(T0, address);
             break;
           }
 
+          case InvariantEntry::TYPED_ARRAY_SLOTS:
+          case InvariantEntry::TYPED_ARRAY_LENGTH: {
+            uint32 array = entry.u.array.arraySlot;
+            Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(array));
+            jumps->append(notObject);
+            masm.loadPayload(frame.addressOf(array), T0);
+
+            Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
+
+            /* Load the internal typed array. */
+            masm.loadPtr(Address(T0, offsetof(JSObject, privateData)), T0);
+
+            if (entry.kind == InvariantEntry::TYPED_ARRAY_LENGTH) {
+                masm.load32(Address(T0, TypedArray::lengthOffset()), T0);
+                masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address);
+            } else {
+                masm.loadPtr(Address(T0, js::TypedArray::dataOffset()), T0);
+                masm.storePtr(T0, address);
+            }
+            break;
+          }
+
           case InvariantEntry::INVARIANT_ARGS_BASE: {
             Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
             masm.loadFrameActuals(outerScript->fun, T0);
             masm.storePtr(T0, address);
             break;
           }
 
           case InvariantEntry::INVARIANT_ARGS_LENGTH: {
--- a/js/src/methodjit/LoopState.h
+++ b/js/src/methodjit/LoopState.h
@@ -82,16 +82,18 @@ namespace mjit {
  * need to be loop invariant wrt the side effects that happen directly in the
  * loop; if C++ calls a getter which scribbles on the object properties
  * involved in an 'invariant' then we will reload the invariant's new value
  * after the call finishes.
  */
 
 struct TemporaryCopy;
 
+enum InvariantArrayKind { DENSE_ARRAY, TYPED_ARRAY };
+
 class LoopState : public MacroAssemblerTypedefs
 {
     JSContext *cx;
     analyze::CrossScriptSSA *ssa;
     JSScript *outerScript;
     analyze::ScriptAnalysis *outerAnalysis;
 
     Compiler &cc;
@@ -154,32 +156,37 @@ class LoopState : public MacroAssemblerT
     Vector<RestoreInvariantCall> restoreInvariantCalls;
 
     /*
      * Aggregate structure for all loop invariant code and hoisted checks we
      * can perform. These are all stored in the same vector as they may depend
      * on each other and we need to emit code restoring them in order.
      */
     struct InvariantEntry {
-        enum {
+        enum EntryKind {
             /*
              * initializedLength(array) > value1 + value2 + constant.
              * Unsigned comparison, so will fail if value + constant < 0
              */
-            BOUNDS_CHECK,
+            DENSE_ARRAY_BOUNDS_CHECK,
+            TYPED_ARRAY_BOUNDS_CHECK,
 
             /* value1 + constant >= 0 */
             NEGATIVE_CHECK,
 
             /* constant >= value1 + value2 */
             RANGE_CHECK,
 
             /* For dense arrays */
-            INVARIANT_SLOTS,
-            INVARIANT_LENGTH,
+            DENSE_ARRAY_SLOTS,
+            DENSE_ARRAY_LENGTH,
+
+            /* For typed arrays */
+            TYPED_ARRAY_SLOTS,
+            TYPED_ARRAY_LENGTH,
 
             /* For lazy arguments */
             INVARIANT_ARGS_BASE,
             INVARIANT_ARGS_LENGTH,
 
             /* For definite properties */
             INVARIANT_PROPERTY
         } kind;
@@ -197,27 +204,30 @@ class LoopState : public MacroAssemblerT
             struct {
                 uint32 objectSlot;
                 uint32 propertySlot;
                 uint32 temporary;
                 jsid id;
             } property;
         } u;
         InvariantEntry() { PodZero(this); }
+        bool isBoundsCheck() const {
+            return kind == DENSE_ARRAY_BOUNDS_CHECK || kind == TYPED_ARRAY_BOUNDS_CHECK;
+        }
         bool isCheck() const {
-            return kind == BOUNDS_CHECK || kind == NEGATIVE_CHECK || kind == RANGE_CHECK;
+            return isBoundsCheck() || kind == NEGATIVE_CHECK || kind == RANGE_CHECK;
         }
     };
     Vector<InvariantEntry, 4, CompilerAllocPolicy> invariantEntries;
 
     static inline bool entryRedundant(const InvariantEntry &e0, const InvariantEntry &e1);
     bool checkRedundantEntry(const InvariantEntry &entry);
 
     bool loopInvariantEntry(uint32 slot);
-    bool addHoistedCheck(uint32 arraySlot,
+    bool addHoistedCheck(InvariantArrayKind arrayKind, uint32 arraySlot,
                          uint32 valueSlot1, uint32 valueSlot2, int32 constant);
     void addNegativeCheck(uint32 valueSlot, int32 constant);
     void addRangeCheck(uint32 valueSlot1, uint32 valueSlot2, int32 constant);
     bool hasTestLinearRelationship(uint32 slot);
 
     bool hasInvariants() { return !invariantEntries.empty(); }
     void restoreInvariants(jsbytecode *pc, Assembler &masm,
                            Vector<TemporaryCopy> *temporaryCopies, Vector<Jump> *jumps);
@@ -275,17 +285,18 @@ class LoopState : public MacroAssemblerT
     void clearLoopRegisters();
 
     void flushLoop(StubCompiler &stubcc);
 
     /*
      * These should only be used for entries which are known to be dense arrays
      * (if they are objects at all).
      */
-    bool hoistArrayLengthCheck(const analyze::CrossSSAValue &obj,
+    bool hoistArrayLengthCheck(InvariantArrayKind arrayKind,
+                               const analyze::CrossSSAValue &obj,
                                const analyze::CrossSSAValue &index);
     FrameEntry *invariantArraySlots(const analyze::CrossSSAValue &obj);
 
     /* Methods for accesses on lazy arguments. */
     bool hoistArgsLengthCheck(const analyze::CrossSSAValue &index);
     FrameEntry *invariantArguments();
 
     FrameEntry *invariantLength(const analyze::CrossSSAValue &obj);