Bug 1095952 - Optimize accesses to known inline or outline typed objects, r=nmatsakis,jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 18 Nov 2014 08:37:32 -0700
changeset 216300 d696d5bfb8cf0b9f4db412d0761ce4944d9974e0
parent 216299 f61b87621f13953655eabd2e4c10081c0b4b2fcf
child 216301 8edd196e6d87fe4a0e68f6978e6dca6403946b02
push id27845
push userkwierso@gmail.com
push dateWed, 19 Nov 2014 02:08:01 +0000
treeherdermozilla-central@64e7a6391916 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnmatsakis, jandem
bugs1095952
milestone36.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 1095952 - Optimize accesses to known inline or outline typed objects, r=nmatsakis,jandem.
js/src/builtin/TypedObject.h
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonAnalysis.cpp
js/src/jit/IonAnalysis.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/Lowering.cpp
js/src/jit/MIR.h
js/src/jit/MIRGenerator.h
js/src/jit/MIRGraph.cpp
js/src/jit/TypePolicy.cpp
js/src/jit/shared/Assembler-shared.h
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -979,16 +979,30 @@ IsTypedObjectClass(const Class *class_)
 
 inline bool
 IsOpaqueTypedObjectClass(const Class *class_)
 {
     return class_ == &OutlineOpaqueTypedObject::class_ ||
            class_ == &InlineOpaqueTypedObject::class_;
 }
 
+inline bool
+IsOutlineTypedObjectClass(const Class *class_)
+{
+    return class_ == &OutlineOpaqueTypedObject::class_ ||
+           class_ == &OutlineTransparentTypedObject::class_;
+}
+
+inline bool
+IsInlineTypedObjectClass(const Class *class_)
+{
+    return class_ == &InlineOpaqueTypedObject::class_ ||
+           class_ == &InlineTransparentTypedObject::class_;
+}
+
 inline const Class *
 GetOutlineTypedObjectClass(bool opaque)
 {
     return opaque ? &OutlineOpaqueTypedObject::class_ : &OutlineTransparentTypedObject::class_;
 }
 
 inline bool
 IsSimpleTypeDescrClass(const Class* clasp)
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -5096,27 +5096,31 @@ CodeGenerator::visitTypedObjectProto(LTy
 }
 
 bool
 CodeGenerator::visitTypedObjectElements(LTypedObjectElements *lir)
 {
     Register obj = ToRegister(lir->object());
     Register out = ToRegister(lir->output());
 
-    Label inlineObject, done;
-    masm.loadObjClass(obj, out);
-    masm.branchPtr(Assembler::Equal, out, ImmPtr(&InlineOpaqueTypedObject::class_), &inlineObject);
-    masm.branchPtr(Assembler::Equal, out, ImmPtr(&InlineTransparentTypedObject::class_), &inlineObject);
-
-    masm.loadPtr(Address(obj, OutlineTypedObject::offsetOfData()), out);
-    masm.jump(&done);
-
-    masm.bind(&inlineObject);
-    masm.computeEffectiveAddress(Address(obj, InlineTypedObject::offsetOfDataStart()), out);
-    masm.bind(&done);
+    if (lir->mir()->definitelyOutline()) {
+        masm.loadPtr(Address(obj, OutlineTypedObject::offsetOfData()), out);
+    } else {
+        Label inlineObject, done;
+        masm.loadObjClass(obj, out);
+        masm.branchPtr(Assembler::Equal, out, ImmPtr(&InlineOpaqueTypedObject::class_), &inlineObject);
+        masm.branchPtr(Assembler::Equal, out, ImmPtr(&InlineTransparentTypedObject::class_), &inlineObject);
+
+        masm.loadPtr(Address(obj, OutlineTypedObject::offsetOfData()), out);
+        masm.jump(&done);
+
+        masm.bind(&inlineObject);
+        masm.computeEffectiveAddress(Address(obj, InlineTypedObject::offsetOfDataStart()), out);
+        masm.bind(&done);
+    }
 
     return true;
 }
 
 bool
 CodeGenerator::visitSetTypedObjectOffset(LSetTypedObjectOffset *lir)
 {
     Register object = ToRegister(lir->object());
@@ -6596,82 +6600,97 @@ class OutOfLineStoreElementHole : public
         return ins_;
     }
     Label *rejoinStore() {
         return &rejoinStore_;
     }
 };
 
 bool
-CodeGenerator::emitStoreHoleCheck(Register elements, const LAllocation *index, LSnapshot *snapshot)
+CodeGenerator::emitStoreHoleCheck(Register elements, const LAllocation *index,
+                                  int32_t offsetAdjustment, LSnapshot *snapshot)
 {
     Label bail;
     if (index->isConstant()) {
-        masm.branchTestMagic(Assembler::Equal,
-                             Address(elements, ToInt32(index) * sizeof(js::Value)), &bail);
+        Address dest(elements, ToInt32(index) * sizeof(js::Value) + offsetAdjustment);
+        masm.branchTestMagic(Assembler::Equal, dest, &bail);
     } else {
-        masm.branchTestMagic(Assembler::Equal,
-                             BaseIndex(elements, ToRegister(index), TimesEight), &bail);
+        BaseIndex dest(elements, ToRegister(index), TimesEight, offsetAdjustment);
+        masm.branchTestMagic(Assembler::Equal, dest, &bail);
     }
     return bailoutFrom(&bail, snapshot);
 }
 
 void
-CodeGenerator::emitStoreElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
-                                     Register elements, const LAllocation *index)
+CodeGenerator::emitStoreElementTyped(const LAllocation *value,
+                                     MIRType valueType, MIRType elementType,
+                                     Register elements, const LAllocation *index,
+                                     int32_t offsetAdjustment)
 {
     ConstantOrRegister v;
     if (value->isConstant())
         v = ConstantOrRegister(*value->toConstant());
     else
         v = TypedOrValueRegister(valueType, ToAnyRegister(value));
 
     if (index->isConstant()) {
-        Address dest(elements, ToInt32(index) * sizeof(js::Value));
+        Address dest(elements, ToInt32(index) * sizeof(js::Value) + offsetAdjustment);
         masm.storeUnboxedValue(v, valueType, dest, elementType);
     } else {
-        BaseIndex dest(elements, ToRegister(index), TimesEight);
+        BaseIndex dest(elements, ToRegister(index), TimesEight, offsetAdjustment);
         masm.storeUnboxedValue(v, valueType, dest, elementType);
     }
 }
 
 bool
 CodeGenerator::visitStoreElementT(LStoreElementT *store)
 {
     Register elements = ToRegister(store->elements());
     const LAllocation *index = store->index();
 
     if (store->mir()->needsBarrier())
         emitPreBarrier(elements, index);
 
-    if (store->mir()->needsHoleCheck() && !emitStoreHoleCheck(elements, index, store->snapshot()))
-        return false;
-
-    emitStoreElementTyped(store->value(), store->mir()->value()->type(), store->mir()->elementType(),
-                          elements, index);
+    if (store->mir()->needsHoleCheck() &&
+        !emitStoreHoleCheck(elements, index, store->mir()->offsetAdjustment(), store->snapshot()))
+    {
+        return false;
+    }
+
+    emitStoreElementTyped(store->value(),
+                          store->mir()->value()->type(), store->mir()->elementType(),
+                          elements, index, store->mir()->offsetAdjustment());
     return true;
 }
 
 bool
 CodeGenerator::visitStoreElementV(LStoreElementV *lir)
 {
     const ValueOperand value = ToValue(lir, LStoreElementV::Value);
     Register elements = ToRegister(lir->elements());
     const LAllocation *index = lir->index();
 
     if (lir->mir()->needsBarrier())
         emitPreBarrier(elements, index);
 
-    if (lir->mir()->needsHoleCheck() && !emitStoreHoleCheck(elements, index, lir->snapshot()))
-        return false;
-
-    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));
+    if (lir->mir()->needsHoleCheck() &&
+        !emitStoreHoleCheck(elements, index, lir->mir()->offsetAdjustment(), lir->snapshot()))
+    {
+        return false;
+    }
+
+    if (lir->index()->isConstant()) {
+        Address dest(elements,
+                     ToInt32(lir->index()) * sizeof(js::Value) + lir->mir()->offsetAdjustment());
+        masm.storeValue(value, dest);
+    } else {
+        BaseIndex dest(elements, ToRegister(lir->index()), TimesEight,
+                       lir->mir()->offsetAdjustment());
+        masm.storeValue(value, dest);
+    }
     return true;
 }
 
 bool
 CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT *lir)
 {
     OutOfLineStoreElementHole *ool = new(alloc()) OutOfLineStoreElementHole(lir);
     if (!addOutOfLineCode(ool, lir->mir()))
@@ -6684,17 +6703,17 @@ CodeGenerator::visitStoreElementHoleT(LS
     Address initLength(elements, ObjectElements::offsetOfInitializedLength());
     masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry());
 
     if (lir->mir()->needsBarrier())
         emitPreBarrier(elements, index);
 
     masm.bind(ool->rejoinStore());
     emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(),
-                          elements, index);
+                          elements, index, 0);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV *lir)
 {
@@ -6790,17 +6809,17 @@ CodeGenerator::visitOutOfLineStoreElemen
 
     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.
         emitStoreElementTyped(ins->toStoreElementHoleT()->value(), valueType, MIRType_None,
-                              elements, index);
+                              elements, index, 0);
         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);
@@ -6836,28 +6855,37 @@ StoreUnboxedPointer(MacroAssembler &masm
     } else {
         masm.storePtr(ToRegister(value), address);
     }
 }
 
 bool
 CodeGenerator::visitStoreUnboxedPointer(LStoreUnboxedPointer *lir)
 {
-    MOZ_ASSERT(lir->mir()->isStoreUnboxedObjectOrNull() || lir->mir()->isStoreUnboxedString());
-    MIRType type = lir->mir()->isStoreUnboxedObjectOrNull() ? MIRType_Object : MIRType_String;
+    MIRType type;
+    int32_t offsetAdjustment;
+    if (lir->mir()->isStoreUnboxedObjectOrNull()) {
+        type = MIRType_Object;
+        offsetAdjustment = lir->mir()->toStoreUnboxedObjectOrNull()->offsetAdjustment();
+    } else if (lir->mir()->isStoreUnboxedString()) {
+        type = MIRType_String;
+        offsetAdjustment = lir->mir()->toStoreUnboxedString()->offsetAdjustment();
+    } else {
+        MOZ_CRASH();
+    }
 
     Register elements = ToRegister(lir->elements());
     const LAllocation *index = lir->index();
     const LAllocation *value = lir->value();
 
     if (index->isConstant()) {
-        Address address(elements, ToInt32(index) * sizeof(uintptr_t));
+        Address address(elements, ToInt32(index) * sizeof(uintptr_t) + offsetAdjustment);
         StoreUnboxedPointer(masm, address, type, value);
     } else {
-        BaseIndex address(elements, ToRegister(index), ScalePointer);
+        BaseIndex address(elements, ToRegister(index), ScalePointer, offsetAdjustment);
         StoreUnboxedPointer(masm, address, type, value);
     }
 
     return true;
 }
 
 typedef bool (*ArrayPopShiftFn)(JSContext *, HandleObject, MutableHandleValue);
 static const VMFunction ArrayPopDenseInfo = FunctionInfo<ArrayPopShiftFn>(jit::ArrayPopDense);
@@ -8914,32 +8942,37 @@ CodeGenerator::emitLoadElementT(LLoadEle
     return true;
 }
 
 bool
 CodeGenerator::visitLoadElementT(LLoadElementT *lir)
 {
     Register elements = ToRegister(lir->elements());
     const LAllocation *index = lir->index();
-    if (index->isConstant())
-        return emitLoadElementT(lir, Address(elements, ToInt32(index) * sizeof(js::Value)));
-    return emitLoadElementT(lir, BaseIndex(elements, ToRegister(index), TimesEight));
+    if (index->isConstant()) {
+        int32_t offset = ToInt32(index) * sizeof(js::Value) + lir->mir()->offsetAdjustment();
+        return emitLoadElementT(lir, Address(elements, offset));
+    }
+    return emitLoadElementT(lir, BaseIndex(elements, ToRegister(index), TimesEight,
+                                           lir->mir()->offsetAdjustment()));
 }
 
 bool
 CodeGenerator::visitLoadElementV(LLoadElementV *load)
 {
     Register elements = ToRegister(load->elements());
     const ValueOperand out = ToOutValue(load);
 
     if (load->index()->isConstant()) {
         NativeObject::elementsSizeMustNotOverflow();
-        masm.loadValue(Address(elements, ToInt32(load->index()) * sizeof(Value)), out);
+        int32_t offset = ToInt32(load->index()) * sizeof(Value) + load->mir()->offsetAdjustment();
+        masm.loadValue(Address(elements, offset), out);
     } else {
-        masm.loadValue(BaseObjectElementIndex(elements, ToRegister(load->index())), out);
+        masm.loadValue(BaseObjectElementIndex(elements, ToRegister(load->index()),
+                                              load->mir()->offsetAdjustment()), out);
     }
 
     if (load->mir()->needsHoleCheck()) {
         Label testMagic;
         masm.branchTestMagic(Assembler::Equal, out, &testMagic);
         if (!bailoutFrom(&testMagic, load->snapshot()))
             return false;
     }
@@ -8995,20 +9028,23 @@ CodeGenerator::visitLoadElementHole(LLoa
 }
 
 bool
 CodeGenerator::visitLoadUnboxedPointerV(LLoadUnboxedPointerV *lir)
 {
     Register elements = ToRegister(lir->elements());
     const ValueOperand out = ToOutValue(lir);
 
-    if (lir->index()->isConstant())
-        masm.loadPtr(Address(elements, ToInt32(lir->index()) * sizeof(uintptr_t)), out.scratchReg());
-    else
-        masm.loadPtr(BaseIndex(elements, ToRegister(lir->index()), ScalePointer), out.scratchReg());
+    if (lir->index()->isConstant()) {
+        int32_t offset = ToInt32(lir->index()) * sizeof(uintptr_t) + lir->mir()->offsetAdjustment();
+        masm.loadPtr(Address(elements, offset), out.scratchReg());
+    } else {
+        masm.loadPtr(BaseIndex(elements, ToRegister(lir->index()), ScalePointer,
+                               lir->mir()->offsetAdjustment()), out.scratchReg());
+    }
 
     Label notNull, done;
     masm.branchPtr(Assembler::NotEqual, out.scratchReg(), ImmWord(0), &notNull);
 
     masm.moveValue(NullValue(), out);
     masm.jump(&done);
 
     masm.bind(&notNull);
@@ -9020,39 +9056,43 @@ CodeGenerator::visitLoadUnboxedPointerV(
 
 bool
 CodeGenerator::visitLoadUnboxedPointerT(LLoadUnboxedPointerT *lir)
 {
     Register elements = ToRegister(lir->elements());
     const LAllocation *index = lir->index();
     Register out = ToRegister(lir->output());
 
-    if (index->isConstant())
-        masm.loadPtr(Address(elements, ToInt32(index) * sizeof(uintptr_t)), out);
-    else
-        masm.loadPtr(BaseIndex(elements, ToRegister(index), ScalePointer), out);
+    if (index->isConstant()) {
+        int32_t offset = ToInt32(index) * sizeof(uintptr_t) + lir->mir()->offsetAdjustment();
+        masm.loadPtr(Address(elements, offset), out);
+    } else {
+        masm.loadPtr(BaseIndex(elements, ToRegister(index), ScalePointer,
+                               lir->mir()->offsetAdjustment()), out);
+    }
     return true;
 }
 
 bool
 CodeGenerator::visitLoadTypedArrayElement(LLoadTypedArrayElement *lir)
 {
     Register elements = ToRegister(lir->elements());
     Register temp = lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp());
     AnyRegister out = ToAnyRegister(lir->output());
 
     Scalar::Type arrayType = lir->mir()->arrayType();
     int width = Scalar::byteSize(arrayType);
 
     Label fail;
     if (lir->index()->isConstant()) {
-        Address source(elements, ToInt32(lir->index()) * width);
+        Address source(elements, ToInt32(lir->index()) * width + lir->mir()->offsetAdjustment());
         masm.loadFromTypedArray(arrayType, source, out, temp, &fail);
     } else {
-        BaseIndex source(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
+        BaseIndex source(elements, ToRegister(lir->index()), ScaleFromElemWidth(width),
+                         lir->mir()->offsetAdjustment());
         masm.loadFromTypedArray(arrayType, source, out, temp, &fail);
     }
 
     if (fail.used() && !bailoutFrom(&fail, lir->snapshot()))
         return false;
 
     return true;
 }
@@ -9118,20 +9158,21 @@ CodeGenerator::visitStoreTypedArrayEleme
 {
     Register elements = ToRegister(lir->elements());
     const LAllocation *value = lir->value();
 
     Scalar::Type arrayType = lir->mir()->arrayType();
     int width = Scalar::byteSize(arrayType);
 
     if (lir->index()->isConstant()) {
-        Address dest(elements, ToInt32(lir->index()) * width);
+        Address dest(elements, ToInt32(lir->index()) * width + lir->mir()->offsetAdjustment());
         StoreToTypedArray(masm, arrayType, value, dest);
     } else {
-        BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
+        BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width),
+                       lir->mir()->offsetAdjustment());
         StoreToTypedArray(masm, arrayType, value, dest);
     }
 
     return true;
 }
 
 bool
 CodeGenerator::visitStoreTypedArrayElementHole(LStoreTypedArrayElementHole *lir)
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -464,20 +464,22 @@ class CodeGenerator : public CodeGenerat
                                      Label *ifDoesntEmulateUndefined,
                                      Register scratch, OutOfLineTestObject *ool);
 
     // Get a label for the start of block which can be used for jumping, in
     // place of jumpToBlock.
     Label *getJumpLabelForBranch(MBasicBlock *block);
 
     void emitStoreElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
-                               Register elements, const LAllocation *index);
+                               Register elements, const LAllocation *index,
+                               int32_t offsetAdjustment);
 
     // Bailout if an element about to be written to is a hole.
-    bool emitStoreHoleCheck(Register elements, const LAllocation *index, LSnapshot *snapshot);
+    bool emitStoreHoleCheck(Register elements, const LAllocation *index, int32_t offsetAdjustment,
+                            LSnapshot *snapshot);
 
     bool emitAssertRangeI(const Range *r, Register input);
     bool emitAssertRangeD(const Range *r, FloatRegister input, FloatRegister temp);
 
     Vector<CodeOffsetLabel, 0, IonAllocPolicy> ionScriptLabels_;
 #ifdef DEBUG
     bool branchIfInvalidated(Register temp, Label *invalidated);
 
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2503,32 +2503,64 @@ LinearSum::multiply(int32_t scale)
     for (size_t i = 0; i < terms_.length(); i++) {
         if (!SafeMul(scale, terms_[i].scale, &terms_[i].scale))
             return false;
     }
     return SafeMul(scale, constant_, &constant_);
 }
 
 bool
+LinearSum::divide(int32_t scale)
+{
+    MOZ_ASSERT(scale > 0);
+
+    for (size_t i = 0; i < terms_.length(); i++) {
+        if (terms_[i].scale % scale != 0)
+            return false;
+    }
+    if (constant_ % scale != 0)
+        return false;
+
+    for (size_t i = 0; i < terms_.length(); i++)
+        terms_[i].scale /= scale;
+    constant_ /= scale;
+
+    return true;
+}
+
+bool
 LinearSum::add(const LinearSum &other, int32_t scale /* = 1 */)
 {
     for (size_t i = 0; i < other.terms_.length(); i++) {
         int32_t newScale = scale;
         if (!SafeMul(scale, other.terms_[i].scale, &newScale))
             return false;
         if (!add(other.terms_[i].term, newScale))
             return false;
     }
     int32_t newConstant = scale;
     if (!SafeMul(scale, other.constant_, &newConstant))
         return false;
     return add(newConstant);
 }
 
 bool
+LinearSum::add(SimpleLinearSum other, int32_t scale)
+{
+    if (other.term && !add(other.term, scale))
+        return false;
+
+    int32_t constant;
+    if (!SafeMul(other.constant, scale, &constant))
+        return false;
+
+    return add(constant);
+}
+
+bool
 LinearSum::add(MDefinition *term, int32_t scale)
 {
     MOZ_ASSERT(term);
 
     if (scale == 0)
         return true;
 
     if (term->isConstant()) {
@@ -2545,17 +2577,19 @@ LinearSum::add(MDefinition *term, int32_
             if (terms_[i].scale == 0) {
                 terms_[i] = terms_.back();
                 terms_.popBack();
             }
             return true;
         }
     }
 
-    terms_.append(LinearTerm(term, scale));
+    if (!terms_.append(LinearTerm(term, scale)))
+        CrashAtUnhandlableOOM("LinearSum::add");
+
     return true;
 }
 
 bool
 LinearSum::add(int32_t constant)
 {
     return SafeAdd(constant, constant_, &constant_);
 }
@@ -2597,17 +2631,17 @@ LinearSum::dump(FILE *fp) const
 
 void
 LinearSum::dump() const
 {
     dump(stderr);
 }
 
 MDefinition *
-jit::ConvertLinearSum(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum)
+jit::ConvertLinearSum(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum, bool convertConstant)
 {
     MDefinition *def = nullptr;
 
     for (size_t i = 0; i < sum.numTerms(); i++) {
         LinearTerm term = sum.term(i);
         MOZ_ASSERT(!term.term->isConstant());
         if (term.scale == 1) {
             if (def) {
@@ -2642,17 +2676,30 @@ jit::ConvertLinearSum(TempAllocator &all
                 block->insertAtEnd(def->toInstruction());
                 def->computeRange(alloc);
             } else {
                 def = mul;
             }
         }
     }
 
-    // Note: The constant component of the term is not converted.
+    if (convertConstant && sum.constant()) {
+        MConstant *constant = MConstant::New(alloc, Int32Value(sum.constant()));
+        block->insertAtEnd(constant);
+        constant->computeRange(alloc);
+        if (def) {
+            def = MAdd::New(alloc, def, constant);
+            def->toAdd()->setInt32();
+            block->insertAtEnd(def->toInstruction());
+            def->computeRange(alloc);
+        } else {
+            def = constant;
+        }
+    }
+
     if (!def) {
         def = MConstant::New(alloc, Int32Value(0));
         block->insertAtEnd(def->toInstruction());
         def->computeRange(alloc);
     }
 
     return def;
 }
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -126,39 +126,47 @@ class LinearSum
 
     LinearSum(const LinearSum &other)
       : terms_(other.terms_.allocPolicy()),
         constant_(other.constant_)
     {
         terms_.appendAll(other.terms_);
     }
 
+    // These return false on an integer overflow, and afterwards the sum must
+    // not be used.
     bool multiply(int32_t scale);
     bool add(const LinearSum &other, int32_t scale = 1);
+    bool add(SimpleLinearSum other, int32_t scale = 1);
     bool add(MDefinition *term, int32_t scale);
     bool add(int32_t constant);
 
+    // Unlike the above function, on failure this leaves the sum unchanged and
+    // it can still be used.
+    bool divide(int32_t scale);
+
     int32_t constant() const { return constant_; }
     size_t numTerms() const { return terms_.length(); }
     LinearTerm term(size_t i) const { return terms_[i]; }
     void replaceTerm(size_t i, MDefinition *def) { terms_[i].term = def; }
 
     void print(Sprinter &sp) const;
     void dump(FILE *) const;
     void dump() const;
 
   private:
     Vector<LinearTerm, 2, IonAllocPolicy> terms_;
     int32_t constant_;
 };
 
-// Convert all components of a linear sum *except* its constant to a definition,
-// adding any necessary instructions to the end of block.
+// Convert all components of a linear sum (except, optionally, the constant)
+// and add any new instructions to the end of block.
 MDefinition *
-ConvertLinearSum(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum);
+ConvertLinearSum(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum,
+                 bool convertConstant = false);
 
 // Convert the test 'sum >= 0' to a comparison, adding any necessary
 // instructions to the end of block.
 MCompare *
 ConvertLinearInequality(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum);
 
 bool
 AnalyzeNewScriptDefiniteProperties(JSContext *cx, JSFunction *fun,
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -763,16 +763,21 @@ IonBuilder::build()
         return false;
 
     if (!abortedNewScriptPropertiesTypes().empty()) {
         MOZ_ASSERT(!info().executionModeIsAnalysis());
         abortReason_ = AbortReason_NewScriptProperties;
         return false;
     }
 
+    if (shouldForceAbort()) {
+        abortReason_ = AbortReason_Disable;
+        return false;
+    }
+
     MOZ_ASSERT(loopDepth_ == 0);
     abortReason_ = AbortReason_NoAbort;
     return true;
 }
 
 bool
 IonBuilder::processIterators()
 {
@@ -907,16 +912,27 @@ IonBuilder::buildInline(IonBuilder *call
     insertRecompileCheck();
 
     if (!traverseBytecode())
         return false;
 
     // Discard unreferenced & pre-allocated resume points.
     replaceMaybeFallbackFunctionGetter(nullptr);
 
+    if (!abortedNewScriptPropertiesTypes().empty()) {
+        MOZ_ASSERT(!info().executionModeIsAnalysis());
+        abortReason_ = AbortReason_NewScriptProperties;
+        return false;
+    }
+
+    if (shouldForceAbort()) {
+        abortReason_ = AbortReason_Disable;
+        return false;
+    }
+
     return true;
 }
 
 void
 IonBuilder::rewriteParameter(uint32_t slotIdx, MDefinition *param, int32_t argIndex)
 {
     MOZ_ASSERT(param->isParameter() || param->isGetArgumentsObjectArg());
 
@@ -7277,17 +7293,17 @@ IonBuilder::getElemTryTypedObject(bool *
 static MIRType
 MIRTypeForTypedArrayRead(Scalar::Type arrayType, bool observedDouble);
 
 bool
 IonBuilder::checkTypedObjectIndexInBounds(int32_t elemSize,
                                           MDefinition *obj,
                                           MDefinition *index,
                                           TypedObjectPrediction objPrediction,
-                                          MDefinition **indexAsByteOffset)
+                                          LinearSum *indexAsByteOffset)
 {
     // Ensure index is an integer.
     MInstruction *idInt32 = MToInt32::New(alloc(), index);
     current->add(idInt32);
 
     // If we know the length statically from the type, just embed it.
     // Otherwise, load it from the appropriate reserved slot on the
     // typed object.  We know it's an int32, so we can convert from
@@ -7303,41 +7319,34 @@ IonBuilder::checkTypedObjectIndexInBound
         if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
             return false;
     } else {
         return false;
     }
 
     index = addBoundsCheck(idInt32, length);
 
-    // Since we passed the bounds check, it is impossible for the
-    // result of multiplication to overflow; so enable imul path.
-    MMul *mul = MMul::New(alloc(), index, constantInt(elemSize),
-                          MIRType_Int32, MMul::Integer);
-    current->add(mul);
-
-    *indexAsByteOffset = mul;
-    return true;
+    return indexAsByteOffset->add(index, elemSize);
 }
 
 bool
 IonBuilder::getElemTryScalarElemOfTypedObject(bool *emitted,
                                               MDefinition *obj,
                                               MDefinition *index,
                                               TypedObjectPrediction objPrediction,
                                               TypedObjectPrediction elemPrediction,
                                               int32_t elemSize)
 {
     MOZ_ASSERT(objPrediction.ofArrayKind());
 
     // Must always be loading the same scalar type
     ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
     MOZ_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
 
-    MDefinition *indexAsByteOffset;
+    LinearSum indexAsByteOffset(alloc());
     if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
         return true;
 
     *emitted = true;
 
     return pushScalarLoadFromTypedObject(obj, indexAsByteOffset, elemType);
 }
 
@@ -7348,39 +7357,43 @@ IonBuilder::getElemTryReferenceElemOfTyp
                                                  TypedObjectPrediction objPrediction,
                                                  TypedObjectPrediction elemPrediction)
 {
     MOZ_ASSERT(objPrediction.ofArrayKind());
 
     ReferenceTypeDescr::Type elemType = elemPrediction.referenceType();
     size_t elemSize = ReferenceTypeDescr::size(elemType);
 
-    MDefinition *indexAsByteOffset;
+    LinearSum indexAsByteOffset(alloc());
     if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
         return true;
 
     *emitted = true;
 
     return pushReferenceLoadFromTypedObject(obj, indexAsByteOffset, elemType);
 }
 
 bool
 IonBuilder::pushScalarLoadFromTypedObject(MDefinition *obj,
-                                          MDefinition *offset,
+                                          const LinearSum &byteOffset,
                                           ScalarTypeDescr::Type elemType)
 {
     int32_t size = ScalarTypeDescr::size(elemType);
     MOZ_ASSERT(size == ScalarTypeDescr::alignment(elemType));
 
     // Find location within the owner object.
     MDefinition *elements, *scaledOffset;
-    loadTypedObjectElements(obj, offset, size, &elements, &scaledOffset);
+    int32_t adjustment;
+    loadTypedObjectElements(obj, byteOffset, size, &elements, &scaledOffset, &adjustment);
 
     // Load the element.
-    MLoadTypedArrayElement *load = MLoadTypedArrayElement::New(alloc(), elements, scaledOffset, elemType);
+    MLoadTypedArrayElement *load = MLoadTypedArrayElement::New(alloc(), elements, scaledOffset,
+                                                               elemType,
+                                                               DoesNotRequireMemoryBarrier,
+                                                               adjustment);
     current->add(load);
     current->push(load);
 
     // If we are reading in-bounds elements, we can use knowledge about
     // the array type to determine the result type, even if the opcode has
     // never executed. The known pushed type is only used to distinguish
     // uint32 reads that may produce either doubles or integers.
     types::TemporaryTypeSet *resultTypes = bytecodeTypes(pc);
@@ -7396,41 +7409,42 @@ IonBuilder::pushScalarLoadFromTypedObjec
     // no useful additional info.
     load->setResultType(knownType);
 
     return true;
 }
 
 bool
 IonBuilder::pushReferenceLoadFromTypedObject(MDefinition *typedObj,
-                                             MDefinition *byteOffset,
+                                             const LinearSum &byteOffset,
                                              ReferenceTypeDescr::Type type)
 {
     // Find location within the owner object.
     MDefinition *elements, *scaledOffset;
+    int32_t adjustment;
     size_t alignment = ReferenceTypeDescr::alignment(type);
-    loadTypedObjectElements(typedObj, byteOffset, alignment, &elements, &scaledOffset);
+    loadTypedObjectElements(typedObj, byteOffset, alignment, &elements, &scaledOffset, &adjustment);
 
     types::TemporaryTypeSet *observedTypes = bytecodeTypes(pc);
 
     MInstruction *load;
     BarrierKind barrier = BarrierKind::NoBarrier;
     switch (type) {
       case ReferenceTypeDescr::TYPE_ANY:
-        load = MLoadElement::New(alloc(), elements, scaledOffset, false, false);
+        load = MLoadElement::New(alloc(), elements, scaledOffset, false, false, adjustment);
         if (!observedTypes->unknown())
             barrier = BarrierKind::TypeSet;
         break;
       case ReferenceTypeDescr::TYPE_OBJECT:
-        load = MLoadUnboxedObjectOrNull::New(alloc(), elements, scaledOffset);
+        load = MLoadUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, adjustment);
         if (!observedTypes->unknownObject() || !observedTypes->hasType(types::Type::NullType()))
             barrier = BarrierKind::TypeSet;
         break;
       case ReferenceTypeDescr::TYPE_STRING:
-        load = MLoadUnboxedString::New(alloc(), elements, scaledOffset);
+        load = MLoadUnboxedString::New(alloc(), elements, scaledOffset, adjustment);
         observedTypes->addType(types::Type::StringType(), alloc().lifoAlloc());
         break;
     }
 
     current->add(load);
     current->push(load);
 
     return pushTypeBarrier(load, observedTypes, barrier);
@@ -7444,41 +7458,48 @@ IonBuilder::getElemTryComplexElemOfTyped
                                                TypedObjectPrediction elemPrediction,
                                                int32_t elemSize)
 {
     MOZ_ASSERT(objPrediction.ofArrayKind());
 
     MDefinition *type = loadTypedObjectType(obj);
     MDefinition *elemTypeObj = typeObjectForElementFromArrayStructType(type);
 
-    MDefinition *indexAsByteOffset;
+    LinearSum indexAsByteOffset(alloc());
     if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
         return true;
 
     return pushDerivedTypedObject(emitted, obj, indexAsByteOffset,
                                   elemPrediction, elemTypeObj);
 }
 
 bool
 IonBuilder::pushDerivedTypedObject(bool *emitted,
                                    MDefinition *obj,
-                                   MDefinition *offset,
+                                   const LinearSum &baseByteOffset,
                                    TypedObjectPrediction derivedPrediction,
                                    MDefinition *derivedTypeObj)
 {
     // Find location within the owner object.
-    MDefinition *owner, *ownerOffset;
-    loadTypedObjectData(obj, offset, &owner, &ownerOffset);
+    MDefinition *owner;
+    LinearSum ownerByteOffset(alloc());
+    loadTypedObjectData(obj, &owner, &ownerByteOffset);
+
+    if (!ownerByteOffset.add(baseByteOffset, 1))
+        setForceAbort();
+
+    MDefinition *offset = ConvertLinearSum(alloc(), current, ownerByteOffset,
+                                           /* convertConstant = */ true);
 
     // Create the derived typed object.
     MInstruction *derivedTypedObj = MNewDerivedTypedObject::New(alloc(),
                                                                 derivedPrediction,
                                                                 derivedTypeObj,
                                                                 owner,
-                                                                ownerOffset);
+                                                                offset);
     current->add(derivedTypedObj);
     current->push(derivedTypedObj);
 
     // Determine (if possible) the class/proto that `derivedTypedObj` will
     // have. For derived typed objects, the opacity will be the same as the
     // incoming object from which the derived typed object is, well, derived.
     // The prototype will be determined based on the type descriptor (and is
     // immutable).
@@ -8231,17 +8252,17 @@ IonBuilder::setElemTryReferenceElemOfTyp
                                                  MDefinition *index,
                                                  TypedObjectPrediction objPrediction,
                                                  MDefinition *value,
                                                  TypedObjectPrediction elemPrediction)
 {
     ReferenceTypeDescr::Type elemType = elemPrediction.referenceType();
     size_t elemSize = ReferenceTypeDescr::size(elemType);
 
-    MDefinition *indexAsByteOffset;
+    LinearSum indexAsByteOffset(alloc());
     if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
         return true;
 
     if (!storeReferenceTypedObjectValue(obj, indexAsByteOffset, elemType, value))
         return false;
 
     current->push(value);
 
@@ -8257,17 +8278,17 @@ IonBuilder::setElemTryScalarElemOfTypedO
                                               MDefinition *value,
                                               TypedObjectPrediction elemPrediction,
                                               int32_t elemSize)
 {
     // Must always be loading the same scalar type
     ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
     MOZ_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
 
-    MDefinition *indexAsByteOffset;
+    LinearSum indexAsByteOffset(alloc());
     if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
         return true;
 
     // Store the element
     if (!storeScalarTypedObjectValue(obj, indexAsByteOffset, elemType, false, value))
         return false;
 
     current->push(value);
@@ -8629,28 +8650,26 @@ IonBuilder::jsop_setelem_typed(Scalar::T
 }
 
 bool
 IonBuilder::jsop_setelem_typed_object(Scalar::Type arrayType, SetElemSafety safety, bool racy,
                                       MDefinition *object, MDefinition *index, MDefinition *value)
 {
     MOZ_ASSERT(safety == SetElem_Unsafe); // Can be fixed, but there's been no reason to as of yet
 
-    MInstruction *int_index = MToInt32::New(alloc(), index);
-    current->add(int_index);
+    MInstruction *intIndex = MToInt32::New(alloc(), index);
+    current->add(intIndex);
 
     size_t elemSize = ScalarTypeDescr::alignment(arrayType);
-    MMul *byteOffset = MMul::New(alloc(), int_index, constantInt(elemSize),
-                                        MIRType_Int32, MMul::Integer);
-    current->add(byteOffset);
-
-    if (!storeScalarTypedObjectValue(object, byteOffset, arrayType, racy, value))
-        return false;
-
-    return true;
+
+    LinearSum byteOffset(alloc());
+    if (!byteOffset.add(intIndex, elemSize))
+        setForceAbort();
+
+    return storeScalarTypedObjectValue(object, byteOffset, arrayType, racy, value);
 }
 
 bool
 IonBuilder::jsop_length()
 {
     if (jsop_length_fastPath())
         return true;
 
@@ -9473,34 +9492,42 @@ IonBuilder::getPropTryScalarPropOfTypedO
 
     // Don't optimize if the typed object might be neutered.
     types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global());
     if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
         return true;
 
     *emitted = true;
 
-    return pushScalarLoadFromTypedObject(typedObj, constantInt(fieldOffset), fieldType);
+    LinearSum byteOffset(alloc());
+    if (!byteOffset.add(fieldOffset))
+        setForceAbort();
+
+    return pushScalarLoadFromTypedObject(typedObj, byteOffset, fieldType);
 }
 
 bool
 IonBuilder::getPropTryReferencePropOfTypedObject(bool *emitted, MDefinition *typedObj,
                                                  int32_t fieldOffset,
                                                  TypedObjectPrediction fieldPrediction,
                                                  types::TemporaryTypeSet *resultTypes)
 {
     ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType();
 
     types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global());
     if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
         return true;
 
     *emitted = true;
 
-    return pushReferenceLoadFromTypedObject(typedObj, constantInt(fieldOffset), fieldType);
+    LinearSum byteOffset(alloc());
+    if (!byteOffset.add(fieldOffset))
+        setForceAbort();
+
+    return pushReferenceLoadFromTypedObject(typedObj, byteOffset, fieldType);
 }
 
 bool
 IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted,
                                                MDefinition *typedObj,
                                                int32_t fieldOffset,
                                                TypedObjectPrediction fieldPrediction,
                                                size_t fieldIndex,
@@ -9512,17 +9539,21 @@ IonBuilder::getPropTryComplexPropOfTyped
         return true;
 
     // OK, perform the optimization
 
     // Identify the type object for the field.
     MDefinition *type = loadTypedObjectType(typedObj);
     MDefinition *fieldTypeObj = typeObjectForFieldFromStructType(type, fieldIndex);
 
-    return pushDerivedTypedObject(emitted, typedObj, constantInt(fieldOffset),
+    LinearSum byteOffset(alloc());
+    if (!byteOffset.add(fieldOffset))
+        setForceAbort();
+
+    return pushDerivedTypedObject(emitted, typedObj, byteOffset,
                                   fieldPrediction, fieldTypeObj);
 }
 
 bool
 IonBuilder::getPropTryDefiniteSlot(bool *emitted, MDefinition *obj, PropertyName *name,
                                    BarrierKind barrier, types::TemporaryTypeSet *types)
 {
     MOZ_ASSERT(*emitted == false);
@@ -10165,17 +10196,21 @@ IonBuilder::setPropTryReferencePropOfTyp
                                                  TypedObjectPrediction fieldPrediction)
 {
     ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType();
 
     types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global());
     if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
         return true;
 
-    if (!storeReferenceTypedObjectValue(obj, constantInt(fieldOffset), fieldType, value))
+    LinearSum byteOffset(alloc());
+    if (!byteOffset.add(fieldOffset))
+        setForceAbort();
+
+    if (!storeReferenceTypedObjectValue(obj, byteOffset, fieldType, value))
         return false;
 
     current->push(value);
 
     *emitted = true;
     return true;
 }
 
@@ -10189,19 +10224,21 @@ IonBuilder::setPropTryScalarPropOfTypedO
     // Must always be loading the same scalar type
     Scalar::Type fieldType = fieldPrediction.scalarType();
 
     // Don't optimize if the typed object might be neutered.
     types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global());
     if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
         return true;
 
-    // OK! Perform the optimization.
-
-    if (!storeScalarTypedObjectValue(obj, constantInt(fieldOffset), fieldType, false, value))
+    LinearSum byteOffset(alloc());
+    if (!byteOffset.add(fieldOffset))
+        setForceAbort();
+
+    if (!storeScalarTypedObjectValue(obj, byteOffset, fieldType, false, value))
         return false;
 
     current->push(value);
 
     *emitted = true;
     return true;
 }
 
@@ -11154,77 +11191,95 @@ IonBuilder::loadTypedObjectType(MDefinit
 // object's data, returns another typed object and adusted offset
 // where the data can be found. Often, these returned values are the
 // same as the inputs, but in cases where intermediate derived type
 // objects have been created, the return values will remove
 // intermediate layers (often rendering those derived type objects
 // into dead code).
 void
 IonBuilder::loadTypedObjectData(MDefinition *typedObj,
-                                MDefinition *offset,
                                 MDefinition **owner,
-                                MDefinition **ownerOffset)
+                                LinearSum *ownerOffset)
 {
     MOZ_ASSERT(typedObj->type() == MIRType_Object);
-    MOZ_ASSERT(offset->type() == MIRType_Int32);
 
     // Shortcircuit derived type objects, meaning the intermediate
     // objects created to represent `a.b` in an expression like
     // `a.b.c`. In that case, the owned and a base offset can be
     // pulled from the operands of the instruction and combined with
     // `offset`.
     if (typedObj->isNewDerivedTypedObject()) {
         MNewDerivedTypedObject *ins = typedObj->toNewDerivedTypedObject();
 
-        // Note: we never need to check for neutering on this path,
-        // because when we create the derived typed object, we check
-        // for neutering there, if needed.
-
-        MAdd *offsetAdd = MAdd::NewAsmJS(alloc(), ins->offset(), offset, MIRType_Int32);
-        current->add(offsetAdd);
+        SimpleLinearSum base = ExtractLinearSum(ins->offset());
+        if (!ownerOffset->add(base))
+            setForceAbort();
 
         *owner = ins->owner();
-        *ownerOffset = offsetAdd;
         return;
     }
 
     *owner = typedObj;
-    *ownerOffset = offset;
 }
 
 // Takes as input a typed object, an offset into that typed object's
 // memory, and the type repr of the data found at that offset. Returns
 // the elements pointer and a scaled offset. The scaled offset is
 // expressed in units of `unit`; when working with typed array MIR,
 // this is typically the alignment.
 void
 IonBuilder::loadTypedObjectElements(MDefinition *typedObj,
-                                    MDefinition *offset,
-                                    int32_t unit,
+                                    const LinearSum &baseByteOffset,
+                                    int32_t scale,
                                     MDefinition **ownerElements,
-                                    MDefinition **ownerScaledOffset)
-{
-    MDefinition *owner, *ownerOffset;
-    loadTypedObjectData(typedObj, offset, &owner, &ownerOffset);
-
-    // Load the element data.
-    MTypedObjectElements *elements = MTypedObjectElements::New(alloc(), owner);
-    current->add(elements);
-
-    // Scale to a different unit for compat with typed array MIRs.
-    if (unit != 1) {
-        MDiv *scaledOffset = MDiv::NewAsmJS(alloc(), ownerOffset, constantInt(unit), MIRType_Int32,
-                                            /* unsignd = */ false);
-        current->add(scaledOffset);
-        *ownerScaledOffset = scaledOffset;
+                                    MDefinition **ownerScaledOffset,
+                                    int32_t *ownerByteAdjustment)
+{
+    MDefinition *owner;
+    LinearSum ownerByteOffset(alloc());
+    loadTypedObjectData(typedObj, &owner, &ownerByteOffset);
+
+    if (!ownerByteOffset.add(baseByteOffset))
+        setForceAbort();
+
+    types::TemporaryTypeSet *ownerTypes = owner->resultTypeSet();
+    const Class *clasp = ownerTypes ? ownerTypes->getKnownClass() : nullptr;
+    if (clasp && IsInlineTypedObjectClass(clasp)) {
+        // Perform the load directly from the owner pointer.
+        if (!ownerByteOffset.add(InlineTypedObject::offsetOfDataStart()))
+            setForceAbort();
+        *ownerElements = owner;
     } else {
-        *ownerScaledOffset = ownerOffset;
-    }
-
-    *ownerElements = elements;
+        bool definitelyOutline = clasp && IsOutlineTypedObjectClass(clasp);
+        *ownerElements = MTypedObjectElements::New(alloc(), owner, definitelyOutline);
+        current->add((*ownerElements)->toInstruction());
+    }
+
+    // Extract the constant adjustment from the byte offset.
+    *ownerByteAdjustment = ownerByteOffset.constant();
+    int32_t negativeAdjustment;
+    if (!SafeSub(0, *ownerByteAdjustment, &negativeAdjustment))
+        setForceAbort();
+    if (!ownerByteOffset.add(negativeAdjustment))
+        setForceAbort();
+
+    // Scale the byte offset if required by the MIR node which will access the
+    // typed object. In principle we should always be able to cleanly divide
+    // the terms in this lienar sum due to alignment restrictions, but due to
+    // limitations of ExtractLinearSum when applied to the terms in derived
+    // typed objects this isn't always be possible. In these cases, fall back
+    // on an explicit division operation.
+    if (ownerByteOffset.divide(scale)) {
+        *ownerScaledOffset = ConvertLinearSum(alloc(), current, ownerByteOffset);
+    } else {
+        MDefinition *unscaledOffset = ConvertLinearSum(alloc(), current, ownerByteOffset);
+        *ownerScaledOffset = MDiv::NewAsmJS(alloc(), unscaledOffset, constantInt(scale),
+                                            MIRType_Int32, /* unsigned = */ false);
+        current->add((*ownerScaledOffset)->toInstruction());
+    }
 }
 
 // Looks up the offset/type-repr-set of the field `id`, given the type
 // set `objTypes` of the field owner. If a field is found, returns true
 // and sets *fieldOffset, *fieldPrediction, and *fieldIndex. Returns false
 // otherwise. Infallible.
 bool
 IonBuilder::typedObjectHasField(MDefinition *typedObj,
@@ -11283,72 +11338,74 @@ IonBuilder::typeObjectForFieldFromStruct
     MInstruction *unboxFieldType = MUnbox::New(alloc(), fieldType, MIRType_Object, MUnbox::Infallible);
     current->add(unboxFieldType);
 
     return unboxFieldType;
 }
 
 bool
 IonBuilder::storeScalarTypedObjectValue(MDefinition *typedObj,
-                                        MDefinition *byteOffset,
+                                        const LinearSum &byteOffset,
                                         ScalarTypeDescr::Type type,
                                         bool racy,
                                         MDefinition *value)
 {
     // Find location within the owner object.
     MDefinition *elements, *scaledOffset;
+    int32_t adjustment;
     size_t alignment = ScalarTypeDescr::alignment(type);
-    loadTypedObjectElements(typedObj, byteOffset, alignment, &elements, &scaledOffset);
+    loadTypedObjectElements(typedObj, byteOffset, alignment, &elements, &scaledOffset, &adjustment);
 
     // Clamp value to [0, 255] when type is Uint8Clamped
     MDefinition *toWrite = value;
     if (type == Scalar::Uint8Clamped) {
         toWrite = MClampToUint8::New(alloc(), value);
         current->add(toWrite->toInstruction());
     }
 
     MStoreTypedArrayElement *store =
         MStoreTypedArrayElement::New(alloc(), elements, scaledOffset, toWrite,
-                                     type);
+                                     type, DoesNotRequireMemoryBarrier, adjustment);
     if (racy)
         store->setRacy();
     current->add(store);
 
     return true;
 }
 
 bool
 IonBuilder::storeReferenceTypedObjectValue(MDefinition *typedObj,
-                                           MDefinition *byteOffset,
+                                           const LinearSum &byteOffset,
                                            ReferenceTypeDescr::Type type,
                                            MDefinition *value)
 {
     // Find location within the owner object.
     MDefinition *elements, *scaledOffset;
+    int32_t adjustment;
     size_t alignment = ReferenceTypeDescr::alignment(type);
-    loadTypedObjectElements(typedObj, byteOffset, alignment, &elements, &scaledOffset);
+    loadTypedObjectElements(typedObj, byteOffset, alignment, &elements, &scaledOffset, &adjustment);
 
     MInstruction *store;
     switch (type) {
       case ReferenceTypeDescr::TYPE_ANY:
         if (NeedsPostBarrier(info(), value))
             current->add(MPostWriteBarrier::New(alloc(), typedObj, value));
-        store = MStoreElement::New(alloc(), elements, scaledOffset, value, false);
+        store = MStoreElement::New(alloc(), elements, scaledOffset, value, false, adjustment);
         break;
       case ReferenceTypeDescr::TYPE_OBJECT:
         // Note: We cannot necessarily tell at this point whether a post
         // barrier is needed, because the type policy may insert ToObjectOrNull
         // instructions later, and those may require a post barrier. Therefore,
         // defer the insertion of post barriers to the type policy.
-        store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, typedObj);
+        store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, typedObj, adjustment);
         break;
       case ReferenceTypeDescr::TYPE_STRING:
         // Strings are not nursery allocated, so these writes do not need post
         // barriers.
-        store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value);
+        store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, adjustment);
         break;
     }
 
     current->add(store);
     return true;
 }
 
 MConstant *
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -8,16 +8,17 @@
 #define jit_IonBuilder_h
 
 // This file declares the data structures for building a MIRGraph from a
 // JSScript.
 
 #include "mozilla/LinkedList.h"
 
 #include "jit/BytecodeAnalysis.h"
+#include "jit/IonAnalysis.h"
 #include "jit/IonOptimizationLevels.h"
 #include "jit/MIR.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MIRGraph.h"
 
 namespace js {
 namespace jit {
 
@@ -474,51 +475,51 @@ class IonBuilder
     TypedObjectPrediction typedObjectPrediction(types::TemporaryTypeSet *types);
     bool typedObjectHasField(MDefinition *typedObj,
                              PropertyName *name,
                              size_t *fieldOffset,
                              TypedObjectPrediction *fieldTypeReprs,
                              size_t *fieldIndex);
     MDefinition *loadTypedObjectType(MDefinition *value);
     void loadTypedObjectData(MDefinition *typedObj,
-                             MDefinition *offset,
                              MDefinition **owner,
-                             MDefinition **ownerOffset);
+                             LinearSum *ownerOffset);
     void loadTypedObjectElements(MDefinition *typedObj,
-                                 MDefinition *offset,
-                                 int32_t unit,
+                                 const LinearSum &byteOffset,
+                                 int32_t scale,
                                  MDefinition **ownerElements,
-                                 MDefinition **ownerScaledOffset);
+                                 MDefinition **ownerScaledOffset,
+                                 int32_t *ownerByteAdjustment);
     MDefinition *typeObjectForElementFromArrayStructType(MDefinition *typedObj);
     MDefinition *typeObjectForFieldFromStructType(MDefinition *type,
                                                   size_t fieldIndex);
     bool storeReferenceTypedObjectValue(MDefinition *typedObj,
-                                        MDefinition *byteOffset,
+                                        const LinearSum &byteOffset,
                                         ReferenceTypeDescr::Type type,
                                         MDefinition *value);
     bool storeScalarTypedObjectValue(MDefinition *typedObj,
-                                     MDefinition *offset,
+                                     const LinearSum &byteOffset,
                                      ScalarTypeDescr::Type type,
                                      bool racy,
                                      MDefinition *value);
     bool checkTypedObjectIndexInBounds(int32_t elemSize,
                                        MDefinition *obj,
                                        MDefinition *index,
                                        TypedObjectPrediction objTypeDescrs,
-                                       MDefinition **indexAsByteOffset);
+                                       LinearSum *indexAsByteOffset);
     bool pushDerivedTypedObject(bool *emitted,
                                 MDefinition *obj,
-                                MDefinition *offset,
+                                const LinearSum &byteOffset,
                                 TypedObjectPrediction derivedTypeDescrs,
                                 MDefinition *derivedTypeObj);
     bool pushScalarLoadFromTypedObject(MDefinition *obj,
-                                       MDefinition *offset,
+                                       const LinearSum &byteoffset,
                                        ScalarTypeDescr::Type type);
     bool pushReferenceLoadFromTypedObject(MDefinition *typedObj,
-                                          MDefinition *byteOffset,
+                                          const LinearSum &byteOffset,
                                           ReferenceTypeDescr::Type type);
     MDefinition *neuterCheck(MDefinition *obj);
 
     // jsop_setelem() helpers.
     bool setElemTryTypedArray(bool *emitted, MDefinition *object,
                          MDefinition *index, MDefinition *value);
     bool setElemTryTypedObject(bool *emitted, MDefinition *obj,
                                MDefinition *index, MDefinition *value);
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2671,17 +2671,17 @@ LIRGenerator::visitInArray(MInArray *ins
                                           useRegister(ins->initLength()),
                                           object);
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitLoadElement(MLoadElement *ins)
 {
-    MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
+    MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment()));
     MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
 
     switch (ins->type()) {
       case MIRType_Value:
       {
         LLoadElementV *lir = new(alloc()) LLoadElementV(useRegister(ins->elements()),
                                                         useRegisterOrConstant(ins->index()));
         if (ins->fallible() && !assignSnapshot(lir, Bailout_Hole))
@@ -2717,41 +2717,41 @@ LIRGenerator::visitLoadElementHole(MLoad
     if (ins->needsNegativeIntCheck() && !assignSnapshot(lir, Bailout_NegativeIndex))
         return false;
     return defineBox(lir, ins);
 }
 
 bool
 LIRGenerator::visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull *ins)
 {
-    MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
+    MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment()));
     MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
     MOZ_ASSERT(ins->type() == MIRType_Value);
 
     LLoadUnboxedPointerV *lir = new(alloc()) LLoadUnboxedPointerV(useRegister(ins->elements()),
                                                                   useRegisterOrConstant(ins->index()));
     return defineBox(lir, ins);
 }
 
 bool
 LIRGenerator::visitLoadUnboxedString(MLoadUnboxedString *ins)
 {
-    MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
+    MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment()));
     MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
     MOZ_ASSERT(ins->type() == MIRType_String);
 
     LLoadUnboxedPointerT *lir = new(alloc()) LLoadUnboxedPointerT(useRegister(ins->elements()),
                                                                   useRegisterOrConstant(ins->index()));
     return define(lir, ins);
 }
 
 bool
 LIRGenerator::visitStoreElement(MStoreElement *ins)
 {
-    MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
+    MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment()));
     MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
 
     const LUse elements = useRegister(ins->elements());
     const LAllocation index = useRegisterOrConstant(ins->index());
 
     switch (ins->value()->type()) {
       case MIRType_Value:
       {
@@ -2801,34 +2801,34 @@ LIRGenerator::visitStoreElementHole(MSto
     }
 
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull *ins)
 {
-    MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
+    MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment()));
     MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
     MOZ_ASSERT(ins->value()->type() == MIRType_Object ||
                ins->value()->type() == MIRType_Null ||
                ins->value()->type() == MIRType_ObjectOrNull);
 
     const LUse elements = useRegister(ins->elements());
     const LAllocation index = useRegisterOrNonDoubleConstant(ins->index());
     const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
 
     LInstruction *lir = new(alloc()) LStoreUnboxedPointer(elements, index, value);
     return add(lir, ins);
 }
 
 bool
 LIRGenerator::visitStoreUnboxedString(MStoreUnboxedString *ins)
 {
-    MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
+    MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment()));
     MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
     MOZ_ASSERT(ins->value()->type() == MIRType_String);
 
     const LUse elements = useRegister(ins->elements());
     const LAllocation index = useRegisterOrConstant(ins->index());
     const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
 
     LInstruction *lir = new(alloc()) LStoreUnboxedPointer(elements, index, value);
@@ -2925,17 +2925,17 @@ LIRGenerator::visitStringSplit(MStringSp
     LStringSplit *lir = new(alloc()) LStringSplit(useRegisterAtStart(ins->string()),
                                                   useRegisterAtStart(ins->separator()));
     return defineReturn(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitLoadTypedArrayElement(MLoadTypedArrayElement *ins)
 {
-    MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
+    MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment()));
     MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
 
     const LUse elements = useRegister(ins->elements());
     const LAllocation index = useRegisterOrConstant(ins->index());
 
     MOZ_ASSERT(IsNumberType(ins->type()));
 
     // We need a temp register for Uint32Array with known double result.
@@ -3018,17 +3018,17 @@ LIRGenerator::visitLoadTypedArrayElement
     if (ins->fallible() && !assignSnapshot(lir, Bailout_BoundsCheck))
         return false;
     return define(lir, ins);
 }
 
 bool
 LIRGenerator::visitStoreTypedArrayElement(MStoreTypedArrayElement *ins)
 {
-    MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
+    MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment()));
     MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
 
     if (ins->isFloatArray()) {
         MOZ_ASSERT_IF(ins->arrayType() == Scalar::Float32, ins->value()->type() == MIRType_Float32);
         MOZ_ASSERT_IF(ins->arrayType() == Scalar::Float64, ins->value()->type() == MIRType_Double);
     } else {
         MOZ_ASSERT(ins->value()->type() == MIRType_Int32);
     }
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -7479,36 +7479,48 @@ class MTypedArrayElements
 
 // Load a binary data object's "elements", which is just its opaque
 // binary data space. Eventually this should probably be
 // unified with `MTypedArrayElements`.
 class MTypedObjectElements
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
+    bool definitelyOutline_;
+
   private:
-    explicit MTypedObjectElements(MDefinition *object)
-      : MUnaryInstruction(object)
+    explicit MTypedObjectElements(MDefinition *object, bool definitelyOutline)
+      : MUnaryInstruction(object),
+        definitelyOutline_(definitelyOutline)
     {
         setResultType(MIRType_Elements);
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(TypedObjectElements)
 
-    static MTypedObjectElements *New(TempAllocator &alloc, MDefinition *object) {
-        return new(alloc) MTypedObjectElements(object);
+    static MTypedObjectElements *New(TempAllocator &alloc, MDefinition *object,
+                                     bool definitelyOutline) {
+        return new(alloc) MTypedObjectElements(object, definitelyOutline);
     }
 
     MDefinition *object() const {
         return getOperand(0);
     }
-    bool congruentTo(const MDefinition *ins) const {
-        return congruentIfOperandsEqual(ins);
+    bool definitelyOutline() const {
+        return definitelyOutline_;
+    }
+    bool congruentTo(const MDefinition *ins) const {
+        if (!ins->isTypedObjectElements())
+            return false;
+        const MTypedObjectElements *other = ins->toTypedObjectElements();
+        if (other->definitelyOutline() != definitelyOutline())
+            return false;
+        return congruentIfOperandsEqual(other);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::ObjectFields);
     }
 };
 
 // Inlined version of the js::SetTypedObjectOffset() intrinsic.
 class MSetTypedObjectOffset
@@ -7709,73 +7721,92 @@ class MBoundsCheckLower
         return AliasSet::None();
     }
     bool fallible() const {
         return fallible_;
     }
     void collectRangeInfoPreTrunc();
 };
 
+// Instructions which access an object's elements can either do so on a
+// definition accessing that elements pointer, or on the object itself, if its
+// elements are inline. In the latter case there must be an offset associated
+// with the access.
+static inline bool
+IsValidElementsType(MDefinition *elements, int32_t offsetAdjustment)
+{
+    return elements->type() == MIRType_Elements ||
+           (elements->type() == MIRType_Object && offsetAdjustment != 0);
+}
+
 // Load a value from a dense array's element vector and does a hole check if the
 // array is not known to be packed.
 class MLoadElement
   : public MBinaryInstruction,
     public SingleObjectPolicy::Data
 {
     bool needsHoleCheck_;
     bool loadDoubles_;
-
-    MLoadElement(MDefinition *elements, MDefinition *index, bool needsHoleCheck, bool loadDoubles)
+    int32_t offsetAdjustment_;
+
+    MLoadElement(MDefinition *elements, MDefinition *index,
+                 bool needsHoleCheck, bool loadDoubles, int32_t offsetAdjustment)
       : MBinaryInstruction(elements, index),
         needsHoleCheck_(needsHoleCheck),
-        loadDoubles_(loadDoubles)
+        loadDoubles_(loadDoubles),
+        offsetAdjustment_(offsetAdjustment)
     {
         if (needsHoleCheck) {
             // Uses may be optimized away based on this instruction's result
             // type. This means it's invalid to DCE this instruction, as we
             // have to invalidate when we read a hole.
             setGuard();
         }
         setResultType(MIRType_Value);
         setMovable();
-        MOZ_ASSERT(elements->type() == MIRType_Elements);
+        MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
         MOZ_ASSERT(index->type() == MIRType_Int32);
     }
 
   public:
     INSTRUCTION_HEADER(LoadElement)
 
     static MLoadElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index,
-                             bool needsHoleCheck, bool loadDoubles) {
-        return new(alloc) MLoadElement(elements, index, needsHoleCheck, loadDoubles);
+                             bool needsHoleCheck, bool loadDoubles, int32_t offsetAdjustment = 0) {
+        return new(alloc) MLoadElement(elements, index, needsHoleCheck, loadDoubles, offsetAdjustment);
     }
 
     MDefinition *elements() const {
         return getOperand(0);
     }
     MDefinition *index() const {
         return getOperand(1);
     }
     bool needsHoleCheck() const {
         return needsHoleCheck_;
     }
     bool loadDoubles() const {
         return loadDoubles_;
     }
+    int32_t offsetAdjustment() const {
+        return offsetAdjustment_;
+    }
     bool fallible() const {
         return needsHoleCheck();
     }
     bool congruentTo(const MDefinition *ins) const {
         if (!ins->isLoadElement())
             return false;
         const MLoadElement *other = ins->toLoadElement();
         if (needsHoleCheck() != other->needsHoleCheck())
             return false;
         if (loadDoubles() != other->loadDoubles())
             return false;
+        if (offsetAdjustment() != other->offsetAdjustment())
+            return false;
         return congruentIfOperandsEqual(other);
     }
     MDefinition *foldsTo(TempAllocator &alloc);
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::Element);
     }
 
     ALLOW_CLONE(MLoadElement)
@@ -7839,77 +7870,104 @@ class MLoadElementHole
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::Element);
     }
     void collectRangeInfoPreTrunc();
 
     ALLOW_CLONE(MLoadElementHole)
 };
 
-class MLoadUnboxedObjectOrNull : public MBinaryInstruction
-{
-    MLoadUnboxedObjectOrNull(MDefinition *elements, MDefinition *index)
-      : MBinaryInstruction(elements, index)
+class MLoadUnboxedObjectOrNull
+  : public MBinaryInstruction,
+    public SingleObjectPolicy::Data
+{
+    int32_t offsetAdjustment_;
+
+    MLoadUnboxedObjectOrNull(MDefinition *elements, MDefinition *index, int32_t offsetAdjustment)
+      : MBinaryInstruction(elements, index),
+        offsetAdjustment_(offsetAdjustment)
     {
         setResultType(MIRType_Value);
         setMovable();
-        MOZ_ASSERT(elements->type() == MIRType_Elements);
+        MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
         MOZ_ASSERT(index->type() == MIRType_Int32);
     }
 
   public:
     INSTRUCTION_HEADER(LoadUnboxedObjectOrNull)
 
     static MLoadUnboxedObjectOrNull *New(TempAllocator &alloc,
-                                         MDefinition *elements, MDefinition *index) {
-        return new(alloc) MLoadUnboxedObjectOrNull(elements, index);
+                                         MDefinition *elements, MDefinition *index,
+                                         int32_t offsetAdjustment) {
+        return new(alloc) MLoadUnboxedObjectOrNull(elements, index, offsetAdjustment);
     }
 
     MDefinition *elements() const {
         return getOperand(0);
     }
     MDefinition *index() const {
         return getOperand(1);
     }
-    bool congruentTo(const MDefinition *ins) const {
-        return congruentIfOperandsEqual(ins);
+    int32_t offsetAdjustment() const {
+        return offsetAdjustment_;
+    }
+    bool congruentTo(const MDefinition *ins) const {
+        if (!ins->isLoadUnboxedObjectOrNull())
+            return false;
+        const MLoadUnboxedObjectOrNull *other = ins->toLoadUnboxedObjectOrNull();
+        if (offsetAdjustment() != other->offsetAdjustment())
+            return false;
+        return congruentIfOperandsEqual(other);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::Element);
     }
 
     ALLOW_CLONE(MLoadUnboxedObjectOrNull)
 };
 
-class MLoadUnboxedString : public MBinaryInstruction
-{
-    MLoadUnboxedString(MDefinition *elements, MDefinition *index)
-      : MBinaryInstruction(elements, index)
+class MLoadUnboxedString
+  : public MBinaryInstruction,
+    public SingleObjectPolicy::Data
+{
+    int32_t offsetAdjustment_;
+
+    MLoadUnboxedString(MDefinition *elements, MDefinition *index, int32_t offsetAdjustment)
+      : MBinaryInstruction(elements, index),
+        offsetAdjustment_(offsetAdjustment)
     {
         setResultType(MIRType_String);
         setMovable();
-        MOZ_ASSERT(elements->type() == MIRType_Elements);
+        MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
         MOZ_ASSERT(index->type() == MIRType_Int32);
     }
 
   public:
     INSTRUCTION_HEADER(LoadUnboxedString)
 
     static MLoadUnboxedString *New(TempAllocator &alloc,
-                                   MDefinition *elements, MDefinition *index) {
-        return new(alloc) MLoadUnboxedString(elements, index);
+                                   MDefinition *elements, MDefinition *index, int32_t offsetAdjustment) {
+        return new(alloc) MLoadUnboxedString(elements, index, offsetAdjustment);
     }
 
     MDefinition *elements() const {
         return getOperand(0);
     }
     MDefinition *index() const {
         return getOperand(1);
     }
-    bool congruentTo(const MDefinition *ins) const {
+    int32_t offsetAdjustment() const {
+        return offsetAdjustment_;
+    }
+    bool congruentTo(const MDefinition *ins) const {
+        if (!ins->isLoadUnboxedString())
+            return false;
+        const MLoadUnboxedString *other = ins->toLoadUnboxedString();
+        if (offsetAdjustment() != other->offsetAdjustment())
+            return false;
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::Element);
     }
 
     ALLOW_CLONE(MLoadUnboxedString)
 };
@@ -7951,48 +8009,55 @@ class MStoreElementCommon
 
 // Store a value to a dense array slots vector.
 class MStoreElement
   : public MAryInstruction<3>,
     public MStoreElementCommon,
     public MixPolicy<SingleObjectPolicy, NoFloatPolicy<2> >::Data
 {
     bool needsHoleCheck_;
-
-    MStoreElement(MDefinition *elements, MDefinition *index, MDefinition *value, bool needsHoleCheck) {
+    int32_t offsetAdjustment_;
+
+    MStoreElement(MDefinition *elements, MDefinition *index, MDefinition *value,
+                  bool needsHoleCheck, int32_t offsetAdjustment) {
         initOperand(0, elements);
         initOperand(1, index);
         initOperand(2, value);
         needsHoleCheck_ = needsHoleCheck;
-        MOZ_ASSERT(elements->type() == MIRType_Elements);
+        offsetAdjustment_ = offsetAdjustment;
+        MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
         MOZ_ASSERT(index->type() == MIRType_Int32);
     }
 
   public:
     INSTRUCTION_HEADER(StoreElement)
 
     static MStoreElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index,
-                              MDefinition *value, bool needsHoleCheck) {
-        return new(alloc) MStoreElement(elements, index, value, needsHoleCheck);
+                              MDefinition *value,
+                              bool needsHoleCheck, int32_t offsetAdjustment = 0) {
+        return new(alloc) MStoreElement(elements, index, value, needsHoleCheck, offsetAdjustment);
     }
     MDefinition *elements() const {
         return getOperand(0);
     }
     MDefinition *index() const {
         return getOperand(1);
     }
     MDefinition *value() const {
         return getOperand(2);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::Element);
     }
     bool needsHoleCheck() const {
         return needsHoleCheck_;
     }
+    int32_t offsetAdjustment() const {
+        return offsetAdjustment_;
+    }
     bool fallible() const {
         return needsHoleCheck();
     }
 
     ALLOW_CLONE(MStoreElement)
 };
 
 // Like MStoreElement, but supports indexes >= initialized length. The downside
@@ -8038,95 +8103,113 @@ class MStoreElementHole
         // StoreElementHole can update the initialized length, the array length
         // or reallocate obj->elements.
         return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
     }
 
     ALLOW_CLONE(MStoreElementHole)
 };
 
-// Store an unboxed object or null pointer to a vector.
+// Store an unboxed object or null pointer to a v\ector.
 class MStoreUnboxedObjectOrNull
   : public MAryInstruction<4>,
     public StoreUnboxedObjectOrNullPolicy::Data
 {
+    int32_t offsetAdjustment_;
+
     MStoreUnboxedObjectOrNull(MDefinition *elements, MDefinition *index,
-                              MDefinition *value, MDefinition *typedObj) {
+                              MDefinition *value, MDefinition *typedObj,
+                              int32_t offsetAdjustment)
+      : offsetAdjustment_(offsetAdjustment)
+    {
         initOperand(0, elements);
         initOperand(1, index);
         initOperand(2, value);
         initOperand(3, typedObj);
-        MOZ_ASSERT(elements->type() == MIRType_Elements);
+        MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
         MOZ_ASSERT(index->type() == MIRType_Int32);
         MOZ_ASSERT(typedObj->type() == MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(StoreUnboxedObjectOrNull)
 
     static MStoreUnboxedObjectOrNull *New(TempAllocator &alloc,
                                           MDefinition *elements, MDefinition *index,
-                                          MDefinition *value, MDefinition *typedObj) {
-        return new(alloc) MStoreUnboxedObjectOrNull(elements, index, value, typedObj);
+                                          MDefinition *value, MDefinition *typedObj,
+                                          int32_t offsetAdjustment) {
+        return new(alloc) MStoreUnboxedObjectOrNull(elements, index, value, typedObj,
+                                                    offsetAdjustment);
     }
     MDefinition *elements() const {
         return getOperand(0);
     }
     MDefinition *index() const {
         return getOperand(1);
     }
     MDefinition *value() const {
         return getOperand(2);
     }
     MDefinition *typedObj() const {
         return getOperand(3);
     }
+    int32_t offsetAdjustment() const {
+        return offsetAdjustment_;
+    }
     AliasSet getAliasSet() const {
         // Use AliasSet::Element for reference typed object fields.
         return AliasSet::Store(AliasSet::Element);
     }
 
     // For StoreUnboxedObjectOrNullPolicy.
     void setValue(MDefinition *def) {
         replaceOperand(2, def);
     }
 
     ALLOW_CLONE(MStoreUnboxedObjectOrNull)
 };
 
 // Store an unboxed object or null pointer to a vector.
 class MStoreUnboxedString
   : public MAryInstruction<3>,
-    public ConvertToStringPolicy<2>::Data
-{
-    MStoreUnboxedString(MDefinition *elements, MDefinition *index, MDefinition *value) {
+    public MixPolicy<SingleObjectPolicy, ConvertToStringPolicy<2> >::Data
+{
+    int32_t offsetAdjustment_;
+
+    MStoreUnboxedString(MDefinition *elements, MDefinition *index, MDefinition *value,
+                        int32_t offsetAdjustment)
+      : offsetAdjustment_(offsetAdjustment)
+    {
         initOperand(0, elements);
         initOperand(1, index);
         initOperand(2, value);
-        MOZ_ASSERT(elements->type() == MIRType_Elements);
+        MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
         MOZ_ASSERT(index->type() == MIRType_Int32);
     }
 
   public:
     INSTRUCTION_HEADER(StoreUnboxedString)
 
     static MStoreUnboxedString *New(TempAllocator &alloc,
                                     MDefinition *elements, MDefinition *index,
-                                    MDefinition *value) {
-        return new(alloc) MStoreUnboxedString(elements, index, value);
+                                    MDefinition *value, int32_t offsetAdjustment) {
+        return new(alloc) MStoreUnboxedString(elements, index, value, offsetAdjustment);
     }
     MDefinition *elements() const {
         return getOperand(0);
     }
     MDefinition *index() const {
         return getOperand(1);
     }
     MDefinition *value() const {
         return getOperand(2);
     }
+    int32_t offsetAdjustment() const {
+        return offsetAdjustment_;
+    }
     AliasSet getAliasSet() const {
         // Use AliasSet::Element for reference typed object fields.
         return AliasSet::Store(AliasSet::Element);
     }
 
     ALLOW_CLONE(MStoreUnboxedString)
 };
 
@@ -8291,45 +8374,51 @@ enum MemoryBarrierRequirement
 {
     DoesNotRequireMemoryBarrier,
     DoesRequireMemoryBarrier
 };
 
 // Also see comments above MMemoryBarrier, below.
 
 class MLoadTypedArrayElement
-  : public MBinaryInstruction
+  : public MBinaryInstruction,
+    public SingleObjectPolicy::Data
 {
     Scalar::Type arrayType_;
     bool requiresBarrier_;
+    int32_t offsetAdjustment_;
 
     MLoadTypedArrayElement(MDefinition *elements, MDefinition *index,
-                           Scalar::Type arrayType, MemoryBarrierRequirement requiresBarrier)
+                           Scalar::Type arrayType, MemoryBarrierRequirement requiresBarrier,
+                           int32_t offsetAdjustment)
       : MBinaryInstruction(elements, index),
         arrayType_(arrayType),
-        requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier)
+        requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier),
+        offsetAdjustment_(offsetAdjustment)
     {
         setResultType(MIRType_Value);
         if (requiresBarrier_)
             setGuard();         // Not removable or movable
         else
             setMovable();
-        MOZ_ASSERT(elements->type() == MIRType_Elements);
+        MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
         MOZ_ASSERT(index->type() == MIRType_Int32);
         MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::TypeMax);
     }
 
   public:
     INSTRUCTION_HEADER(LoadTypedArrayElement)
 
     static MLoadTypedArrayElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index,
                                        Scalar::Type arrayType,
-                                       MemoryBarrierRequirement requiresBarrier=DoesNotRequireMemoryBarrier)
-    {
-        return new(alloc) MLoadTypedArrayElement(elements, index, arrayType, requiresBarrier);
+                                       MemoryBarrierRequirement requiresBarrier=DoesNotRequireMemoryBarrier,
+                                       int32_t offsetAdjustment = 0)
+    {
+        return new(alloc) MLoadTypedArrayElement(elements, index, arrayType,
+                                                 requiresBarrier, offsetAdjustment);
     }
 
     Scalar::Type arrayType() const {
         return arrayType_;
     }
     bool fallible() const {
         // Bailout if the result does not fit in an int32.
         return arrayType_ == Scalar::Uint32 && type() == MIRType_Int32;
@@ -8338,32 +8427,37 @@ class MLoadTypedArrayElement
         return requiresBarrier_;
     }
     MDefinition *elements() const {
         return getOperand(0);
     }
     MDefinition *index() const {
         return getOperand(1);
     }
+    int32_t offsetAdjustment() const {
+        return offsetAdjustment_;
+    }
     AliasSet getAliasSet() const {
         // When a barrier is needed make the instruction effectful by
         // giving it a "store" effect.
         if (requiresBarrier_)
             return AliasSet::Store(AliasSet::TypedArrayElement);
         return AliasSet::Load(AliasSet::TypedArrayElement);
     }
 
     bool congruentTo(const MDefinition *ins) const {
         if (requiresBarrier_)
             return false;
         if (!ins->isLoadTypedArrayElement())
             return false;
         const MLoadTypedArrayElement *other = ins->toLoadTypedArrayElement();
         if (arrayType_ != other->arrayType_)
             return false;
+        if (offsetAdjustment() != other->offsetAdjustment())
+            return false;
         return congruentIfOperandsEqual(other);
     }
 
     void printOpcode(FILE *fp) const;
 
     void computeRange(TempAllocator &alloc);
 
     bool canProduceFloat32() const { return arrayType_ == Scalar::Float32; }
@@ -8484,45 +8578,49 @@ class MLoadTypedArrayElementStatic
 };
 
 class MStoreTypedArrayElement
   : public MTernaryInstruction,
     public StoreTypedArrayPolicy::Data
 {
     Scalar::Type arrayType_;
     bool requiresBarrier_;
+    int32_t offsetAdjustment_;
 
     // See note in MStoreElementCommon.
     bool racy_;
 
     MStoreTypedArrayElement(MDefinition *elements, MDefinition *index, MDefinition *value,
-                            Scalar::Type arrayType, MemoryBarrierRequirement requiresBarrier)
+                            Scalar::Type arrayType, MemoryBarrierRequirement requiresBarrier,
+                            int32_t offsetAdjustment)
       : MTernaryInstruction(elements, index, value),
         arrayType_(arrayType),
         requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier),
+        offsetAdjustment_(offsetAdjustment),
         racy_(false)
     {
         if (requiresBarrier_)
             setGuard();         // Not removable or movable
         else
             setMovable();
-        MOZ_ASSERT(elements->type() == MIRType_Elements);
+        MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
         MOZ_ASSERT(index->type() == MIRType_Int32);
         MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::TypeMax);
     }
 
   public:
     INSTRUCTION_HEADER(StoreTypedArrayElement)
 
     static MStoreTypedArrayElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index,
                                         MDefinition *value, Scalar::Type arrayType,
-                                        MemoryBarrierRequirement requiresBarrier = DoesNotRequireMemoryBarrier)
+                                        MemoryBarrierRequirement requiresBarrier = DoesNotRequireMemoryBarrier,
+                                        int32_t offsetAdjustment = 0)
     {
         return new(alloc) MStoreTypedArrayElement(elements, index, value, arrayType,
-                                                  requiresBarrier);
+                                                  requiresBarrier, offsetAdjustment);
     }
 
     Scalar::Type arrayType() const {
         return arrayType_;
     }
     bool isByteArray() const {
         return arrayType_ == Scalar::Int8 ||
                arrayType_ == Scalar::Uint8 ||
@@ -8542,16 +8640,19 @@ class MStoreTypedArrayElement
         return getOperand(2);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::TypedArrayElement);
     }
     bool requiresMemoryBarrier() const {
         return requiresBarrier_;
     }
+    int32_t offsetAdjustment() const {
+        return offsetAdjustment_;
+    }
     bool racy() const {
         return racy_;
     }
     void setRacy() {
         racy_ = true;
     }
     TruncateKind operandTruncateKind(size_t index) const;
 
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -170,16 +170,17 @@ class MIRGenerator
   protected:
     CompileInfo *info_;
     const OptimizationInfo *optimizationInfo_;
     TempAllocator *alloc_;
     JSFunction *fun_;
     uint32_t nslots_;
     MIRGraph *graph_;
     AbortReason abortReason_;
+    bool shouldForceAbort_; // Force AbortReason_Disable
     TypeObjectVector abortedNewScriptPropertiesTypes_;
     bool error_;
     mozilla::Atomic<bool, mozilla::Relaxed> *pauseBuild_;
     mozilla::Atomic<bool, mozilla::Relaxed> cancelBuild_;
 
     uint32_t maxAsmJSStackArgBytes_;
     bool performsCall_;
     bool usesSimd_;
@@ -190,16 +191,22 @@ class MIRGenerator
     // RegAlloc needs to know this as spilling values back to their register
     // slots is not compatible with that.
     bool modifiesFrameArguments_;
 
     bool instrumentedProfiling_;
     bool instrumentedProfilingIsCached_;
 
     void addAbortedNewScriptPropertiesType(types::TypeObject *type);
+    void setForceAbort() {
+        shouldForceAbort_ = true;
+    }
+    bool shouldForceAbort() {
+        return shouldForceAbort_;
+    }
 
 #if defined(JS_ION_PERF)
     AsmJSPerfSpewer asmJSPerfSpewer_;
 
   public:
     AsmJSPerfSpewer &perfSpewer() { return asmJSPerfSpewer_; }
 #endif
 
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -21,16 +21,17 @@ MIRGenerator::MIRGenerator(CompileCompar
                            TempAllocator *alloc, MIRGraph *graph, CompileInfo *info,
                            const OptimizationInfo *optimizationInfo)
   : compartment(compartment),
     info_(info),
     optimizationInfo_(optimizationInfo),
     alloc_(alloc),
     graph_(graph),
     abortReason_(AbortReason_NoAbort),
+    shouldForceAbort_(false),
     abortedNewScriptPropertiesTypes_(*alloc_),
     error_(false),
     pauseBuild_(nullptr),
     cancelBuild_(false),
     maxAsmJSStackArgBytes_(0),
     performsCall_(false),
     usesSimd_(false),
     usesSimdCached_(false),
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -731,16 +731,18 @@ InstanceOfPolicy::adjustInputs(TempAlloc
 
     return true;
 }
 
 bool
 StoreTypedArrayPolicy::adjustValueInput(TempAllocator &alloc, MInstruction *ins, int arrayType,
                                         MDefinition *value, int valueOperand)
 {
+    SingleObjectPolicy::staticAdjustInputs(alloc, ins);
+
     MDefinition *curValue = value;
     // First, ensure the value is int32, boolean, double or Value.
     // The conversion is based on TypedArrayObjectTemplate::setElementTail.
     switch (value->type()) {
       case MIRType_Int32:
       case MIRType_Double:
       case MIRType_Float32:
       case MIRType_Boolean:
@@ -813,17 +815,17 @@ StoreTypedArrayPolicy::adjustValueInput(
 
     return true;
 }
 
 bool
 StoreTypedArrayPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
     MStoreTypedArrayElement *store = ins->toStoreTypedArrayElement();
-    MOZ_ASSERT(store->elements()->type() == MIRType_Elements);
+    MOZ_ASSERT(IsValidElementsType(store->elements(), store->offsetAdjustment()));
     MOZ_ASSERT(store->index()->type() == MIRType_Int32);
 
     return adjustValueInput(alloc, ins, store->arrayType(), store->value(), 2);
 }
 
 bool
 StoreTypedArrayHolePolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
@@ -842,16 +844,18 @@ StoreTypedArrayElementStaticPolicy::adju
 
     return ConvertToInt32Policy<0>::staticAdjustInputs(alloc, ins) &&
         adjustValueInput(alloc, ins, store->viewType(), store->value(), 1);
 }
 
 bool
 StoreUnboxedObjectOrNullPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
+    SingleObjectPolicy::staticAdjustInputs(alloc, ins);
+
     // Change the value input to a ToObjectOrNull instruction if it might be
     // a non-null primitive. Insert a post barrier for the instruction's object
     // and whatever its new value is, unless the value is definitely null.
     MStoreUnboxedObjectOrNull *store = ins->toStoreUnboxedObjectOrNull();
 
     MDefinition *value = store->value();
     if (value->type() == MIRType_Object ||
         value->type() == MIRType_Null ||
@@ -996,16 +1000,17 @@ FilterTypeSetPolicy::adjustInputs(TempAl
     _(MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >)                        \
     _(MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<1> >)            \
     _(MixPolicy<ObjectPolicy<0>, IntPolicy<1> >)                        \
     _(MixPolicy<ObjectPolicy<0>, NoFloatPolicy<1> >)                    \
     _(MixPolicy<ObjectPolicy<0>, NoFloatPolicy<2> >)                    \
     _(MixPolicy<ObjectPolicy<0>, NoFloatPolicy<3> >)                    \
     _(MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >)                     \
     _(MixPolicy<ObjectPolicy<0>, StringPolicy<1> >)                     \
+    _(MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<2> >)            \
     _(MixPolicy<ObjectPolicy<1>, ConvertToStringPolicy<0> >)            \
     _(MixPolicy<StringPolicy<0>, IntPolicy<1> >)                        \
     _(MixPolicy<StringPolicy<0>, StringPolicy<1> >)                     \
     _(NoFloatPolicy<0>)                                                 \
     _(NoFloatPolicyAfter<1>)                                            \
     _(NoFloatPolicyAfter<2>)                                            \
     _(ObjectPolicy<0>)                                                  \
     _(ObjectPolicy<1>)                                                  \
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -326,18 +326,18 @@ struct BaseValueIndex : BaseIndex
       : BaseIndex(base, index, ValueScale, offset)
     { }
 };
 
 // Specifies the address of an indexed Value within object elements from a
 // base.  The index must not already be scaled by sizeof(Value)!
 struct BaseObjectElementIndex : BaseValueIndex
 {
-    BaseObjectElementIndex(Register base, Register index)
-      : BaseValueIndex(base, index)
+    BaseObjectElementIndex(Register base, Register index, int32_t offset = 0)
+      : BaseValueIndex(base, index, offset)
     {
         NativeObject::elementsSizeMustNotOverflow();
     }
 };
 
 // Like BaseObjectElementIndex, except for object slots.
 struct BaseObjectSlotIndex : BaseValueIndex
 {