Bug 864214 - Generate MIR nodes for accessing statically known typed arrays on x86, r=luke,jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Sat, 27 Apr 2013 15:02:42 -0600
changeset 141103 fc9427895561219823fe8aef2b4f2a7a16d11dcb
parent 141102 791332cb80a723f8a9e01d43bc11b2390a8d1d7e
child 141104 5597242cf58068e06fc483429faa34a94357889a
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke, jandem
bugs864214
milestone23.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 864214 - Generate MIR nodes for accessing statically known typed arrays on x86, r=luke,jandem.
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/LIR-Common.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/Lowering.h
js/src/ion/MIR.cpp
js/src/ion/MIR.h
js/src/ion/MOpcodes.h
js/src/ion/ParallelArrayAnalysis.cpp
js/src/ion/RangeAnalysis.cpp
js/src/ion/TypePolicy.cpp
js/src/ion/TypePolicy.h
js/src/ion/arm/CodeGenerator-arm.cpp
js/src/ion/arm/CodeGenerator-arm.h
js/src/ion/arm/Lowering-arm.cpp
js/src/ion/arm/Lowering-arm.h
js/src/ion/shared/Lowering-shared.h
js/src/ion/x64/CodeGenerator-x64.cpp
js/src/ion/x64/CodeGenerator-x64.h
js/src/ion/x64/Lowering-x64.cpp
js/src/ion/x64/Lowering-x64.h
js/src/ion/x86/CodeGenerator-x86.cpp
js/src/ion/x86/CodeGenerator-x86.h
js/src/ion/x86/Lowering-x86.cpp
js/src/ion/x86/Lowering-x86.h
js/src/jsinfer.cpp
js/src/jstypedarray.h
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -6226,19 +6226,103 @@ IonBuilder::getTypedArrayElements(MDefin
         types::HeapTypeSet::WatchObjectStateChange(cx, array->getType(cx));
 
         obj->setFoldedUnchecked();
         return MConstantElements::New(data);
     }
     return MTypedArrayElements::New(obj);
 }
 
+MDefinition *
+IonBuilder::convertShiftToMaskForStaticTypedArray(MDefinition *id,
+                                                  ArrayBufferView::ViewType viewType)
+{
+    if (!id->isRsh() || id->isEffectful())
+        return NULL;
+    if (!id->getOperand(1)->isConstant())
+        return NULL;
+    const Value &value = id->getOperand(1)->toConstant()->value();
+    if (!value.isInt32() || uint32_t(value.toInt32()) != TypedArrayShift(viewType))
+        return NULL;
+
+    // Instead of shifting, mask off the low bits of the index so that
+    // a non-scaled access on the typed array can be performed.
+    MConstant *mask = MConstant::New(Int32Value(~((1 << value.toInt32()) - 1)));
+    MBitAnd *ptr = MBitAnd::New(id->getOperand(0), mask);
+
+    ptr->infer();
+    JS_ASSERT(!ptr->isEffectful());
+
+    current->add(mask);
+    current->add(ptr);
+
+    return ptr;
+}
+
+bool
+IonBuilder::jsop_getelem_typed_static(bool *psucceeded)
+{
+    if (!LIRGenerator::allowStaticTypedArrayAccesses())
+        return true;
+
+    MDefinition *id = current->peek(-1);
+    MDefinition *obj = current->peek(-2);
+
+    if (ElementAccessHasExtraIndexedProperty(cx, obj))
+        return true;
+
+    if (!obj->resultTypeSet())
+        return true;
+    JSObject *typedArray = obj->resultTypeSet()->getSingleton();
+    if (!typedArray)
+        return true;
+    JS_ASSERT(typedArray->isTypedArray());
+
+    ArrayBufferView::ViewType viewType = JS_GetArrayBufferViewType(typedArray);
+
+    MDefinition *ptr = convertShiftToMaskForStaticTypedArray(id, viewType);
+    if (!ptr)
+        return true;
+
+    obj->setFolded();
+
+    MLoadTypedArrayElementStatic *load = MLoadTypedArrayElementStatic::New(typedArray, ptr);
+    current->add(load);
+
+    // The load is infallible if an undefined result will be coerced to the
+    // appropriate numeric type if the read is out of bounds. The truncation
+    // analysis picks up some of these cases, but is incomplete with respect
+    // to others. For now, sniff the bytecode for simple patterns following
+    // the load which guarantee a truncation or numeric conversion.
+    if (viewType == ArrayBufferView::TYPE_FLOAT32 || viewType == ArrayBufferView::TYPE_FLOAT64) {
+        jsbytecode *next = pc + JSOP_GETELEM_LENGTH;
+        if (*next == JSOP_POS)
+            load->setInfallible();
+    } else {
+        jsbytecode *next = pc + JSOP_GETELEM_LENGTH;
+        if (*next == JSOP_ZERO && *(next + JSOP_ZERO_LENGTH) == JSOP_BITOR)
+            load->setInfallible();
+    }
+
+    current->popn(2);
+    current->push(load);
+
+    *psucceeded = true;
+    return true;
+}
+
 bool
 IonBuilder::jsop_getelem_typed(int arrayType)
 {
+    bool staticAccess = false;
+    if (!jsop_getelem_typed_static(&staticAccess))
+        return false;
+    if (staticAccess)
+        return true;
+
     types::StackTypeSet *types = script()->analysis()->bytecodeTypes(pc);
 
     MDefinition *id = current->pop();
     MDefinition *obj = current->pop();
 
     bool maybeUndefined = types->hasType(types::Type::UndefinedType());
 
     // Reading from an Uint32Array will result in a double for values
@@ -6443,18 +6527,68 @@ IonBuilder::jsop_setelem_dense(types::St
 
     if (elementType != MIRType_None && packed)
         store->setElementType(elementType);
 
     return true;
 }
 
 bool
+IonBuilder::jsop_setelem_typed_static(bool *psucceeded)
+{
+    if (!LIRGenerator::allowStaticTypedArrayAccesses())
+        return true;
+
+    MDefinition *value = current->peek(-1);
+    MDefinition *id = current->peek(-2);
+    MDefinition *obj = current->peek(-3);
+
+    if (ElementAccessHasExtraIndexedProperty(cx, obj))
+        return true;
+
+    if (!obj->resultTypeSet())
+        return true;
+    JSObject *typedArray = obj->resultTypeSet()->getSingleton();
+    if (!typedArray)
+        return true;
+    JS_ASSERT(typedArray->isTypedArray());
+
+    ArrayBufferView::ViewType viewType = JS_GetArrayBufferViewType(typedArray);
+
+    MDefinition *ptr = convertShiftToMaskForStaticTypedArray(id, viewType);
+    if (!ptr)
+        return true;
+
+    obj->setFolded();
+
+    // Clamp value to [0, 255] for Uint8ClampedArray.
+    MDefinition *toWrite = value;
+    if (viewType == ArrayBufferView::TYPE_UINT8_CLAMPED) {
+        toWrite = MClampToUint8::New(value);
+        current->add(toWrite->toInstruction());
+    }
+
+    MInstruction *store = MStoreTypedArrayElementStatic::New(typedArray, ptr, toWrite);
+    current->add(store);
+    current->popn(3);
+    current->push(value);
+
+    *psucceeded = true;
+    return resumeAfter(store);
+}
+
+bool
 IonBuilder::jsop_setelem_typed(int arrayType)
 {
+    bool staticAccess = false;
+    if (!jsop_setelem_typed_static(&staticAccess))
+        return false;
+    if (staticAccess)
+        return true;
+
     MDefinition *value = current->pop();
     MDefinition *id = current->pop();
     MDefinition *obj = current->pop();
 
     SetElemICInspector icInspect(inspector->setElemICInspector(pc));
     bool expectOOB = icInspect.sawOOBTypedArrayWrite();
     if (expectOOB)
         spew("Emitting OOB TypedArray SetElem");
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -319,16 +319,18 @@ class IonBuilder : public MIRGenerator
 
     MDefinition *walkScopeChain(unsigned hops);
 
     MInstruction *addConvertElementsToDoubles(MDefinition *elements);
     MInstruction *addBoundsCheck(MDefinition *index, MDefinition *length);
     MInstruction *addShapeGuard(MDefinition *obj, const RawShape shape, BailoutKind bailoutKind);
 
     JSObject *getNewArrayTemplateObject(uint32_t count);
+    MDefinition *convertShiftToMaskForStaticTypedArray(MDefinition *id,
+                                                       ArrayBufferView::ViewType viewType);
 
     bool invalidatedIdempotentCache();
 
     bool loadSlot(MDefinition *obj, HandleShape shape, MIRType rvalType,
                   bool barrier, types::StackTypeSet *types);
     bool storeSlot(MDefinition *obj, RawShape shape, MDefinition *value, bool needsBarrier);
 
     // jsop_getprop() helpers.
@@ -372,20 +374,22 @@ class IonBuilder : public MIRGenerator
     bool jsop_getgname(HandlePropertyName name);
     bool jsop_setgname(HandlePropertyName name);
     bool jsop_getname(HandlePropertyName name);
     bool jsop_intrinsic(HandlePropertyName name);
     bool jsop_bindname(PropertyName *name);
     bool jsop_getelem();
     bool jsop_getelem_dense();
     bool jsop_getelem_typed(int arrayType);
+    bool jsop_getelem_typed_static(bool *psucceeded);
     bool jsop_getelem_string();
     bool jsop_setelem();
     bool jsop_setelem_dense(types::StackTypeSet::DoubleConversion conversion);
     bool jsop_setelem_typed(int arrayType);
+    bool jsop_setelem_typed_static(bool *psucceeded);
     bool jsop_length();
     bool jsop_length_fastPath();
     bool jsop_arguments();
     bool jsop_arguments_length();
     bool jsop_arguments_getelem();
     bool jsop_arguments_setelem();
     bool jsop_not();
     bool jsop_getprop(HandlePropertyName name);
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -3223,16 +3223,31 @@ class LLoadTypedArrayElementHole : publi
     const LAllocation *object() {
         return getOperand(0);
     }
     const LAllocation *index() {
         return getOperand(1);
     }
 };
 
+class LLoadTypedArrayElementStatic : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(LoadTypedArrayElementStatic);
+    LLoadTypedArrayElementStatic(const LAllocation &ptr) {
+        setOperand(0, ptr);
+    }
+    MLoadTypedArrayElementStatic *mir() const {
+        return mir_->toLoadTypedArrayElementStatic();
+    }
+    const LAllocation *ptr() {
+        return getOperand(0);
+    }
+};
+
 class LStoreTypedArrayElement : public LInstructionHelper<0, 3, 0>
 {
   public:
     LIR_HEADER(StoreTypedArrayElement)
 
     LStoreTypedArrayElement(const LAllocation &elements, const LAllocation &index,
                             const LAllocation &value) {
         setOperand(0, elements);
@@ -3280,16 +3295,35 @@ class LStoreTypedArrayElementHole : publ
     const LAllocation *index() {
         return getOperand(2);
     }
     const LAllocation *value() {
         return getOperand(3);
     }
 };
 
+class LStoreTypedArrayElementStatic : public LInstructionHelper<0, 2, 0>
+{
+  public:
+    LIR_HEADER(StoreTypedArrayElementStatic);
+    LStoreTypedArrayElementStatic(const LAllocation &ptr, const LAllocation &value) {
+        setOperand(0, ptr);
+        setOperand(1, value);
+    }
+    MStoreTypedArrayElementStatic *mir() const {
+        return mir_->toStoreTypedArrayElementStatic();
+    }
+    const LAllocation *ptr() {
+        return getOperand(0);
+    }
+    const LAllocation *value() {
+        return getOperand(1);
+    }
+};
+
 class LEffectiveAddress : public LInstructionHelper<1, 2, 0>
 {
   public:
     LIR_HEADER(EffectiveAddress);
 
     LEffectiveAddress(const LAllocation &base, const LAllocation &index) {
         setOperand(0, base);
         setOperand(1, index);
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -153,18 +153,20 @@
     _(ArrayPopShiftT)               \
     _(ArrayPushV)                   \
     _(ArrayPushT)                   \
     _(ArrayConcat)                  \
     _(StoreElementHoleV)            \
     _(StoreElementHoleT)            \
     _(LoadTypedArrayElement)        \
     _(LoadTypedArrayElementHole)    \
+    _(LoadTypedArrayElementStatic)  \
     _(StoreTypedArrayElement)       \
     _(StoreTypedArrayElementHole)   \
+    _(StoreTypedArrayElementStatic) \
     _(EffectiveAddress)             \
     _(ClampIToUint8)                \
     _(ClampDToUint8)                \
     _(ClampVToUint8)                \
     _(LoadFixedSlotV)               \
     _(LoadFixedSlotT)               \
     _(StoreFixedSlotV)              \
     _(StoreFixedSlotT)              \
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -2105,16 +2105,27 @@ LIRGenerator::visitLoadTypedArrayElement
 
     LLoadTypedArrayElementHole *lir = new LLoadTypedArrayElementHole(object, index);
     if (ins->fallible() && !assignSnapshot(lir))
         return false;
     return defineBox(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
+LIRGenerator::visitLoadTypedArrayElementStatic(MLoadTypedArrayElementStatic *ins)
+{
+    LLoadTypedArrayElementStatic *lir =
+        new LLoadTypedArrayElementStatic(useRegisterAtStart(ins->ptr()));
+
+    if (ins->fallible() && !assignSnapshot(lir))
+        return false;
+    return define(lir, ins);
+}
+
+bool
 LIRGenerator::visitLoadFixedSlot(MLoadFixedSlot *ins)
 {
     JS_ASSERT(ins->object()->type() == MIRType_Object);
 
     if (ins->type() == MIRType_Value) {
         LLoadFixedSlotV *lir = new LLoadFixedSlotV(useRegister(ins->object()));
         return defineBox(lir, ins);
     }
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -184,16 +184,17 @@ class LIRGenerator : public LIRGenerator
     bool visitStoreElement(MStoreElement *ins);
     bool visitStoreElementHole(MStoreElementHole *ins);
     bool visitEffectiveAddress(MEffectiveAddress *ins);
     bool visitArrayPopShift(MArrayPopShift *ins);
     bool visitArrayPush(MArrayPush *ins);
     bool visitArrayConcat(MArrayConcat *ins);
     bool visitLoadTypedArrayElement(MLoadTypedArrayElement *ins);
     bool visitLoadTypedArrayElementHole(MLoadTypedArrayElementHole *ins);
+    bool visitLoadTypedArrayElementStatic(MLoadTypedArrayElementStatic *ins);
     bool visitClampToUint8(MClampToUint8 *ins);
     bool visitLoadFixedSlot(MLoadFixedSlot *ins);
     bool visitStoreFixedSlot(MStoreFixedSlot *ins);
     bool visitGetPropertyCache(MGetPropertyCache *ins);
     bool visitGetElementCache(MGetElementCache *ins);
     bool visitBindNameCache(MBindNameCache *ins);
     bool visitGuardClass(MGuardClass *ins);
     bool visitGuardObject(MGuardObject *ins);
--- a/js/src/ion/MIR.cpp
+++ b/js/src/ion/MIR.cpp
@@ -10,17 +10,17 @@
 #include "MIR.h"
 #include "MIRGraph.h"
 #include "EdgeCaseAnalysis.h"
 #include "RangeAnalysis.h"
 #include "IonSpewer.h"
 #include "jsnum.h"
 #include "jsstr.h"
 #include "jsatominlines.h"
-#include "jstypedarrayinlines.h" // For ClampIntForUint8Array
+#include "jstypedarrayinlines.h"
 
 using namespace js;
 using namespace js::ion;
 
 void
 MDefinition::PrintOpcodeName(FILE *fp, MDefinition::Opcode op)
 {
     static const char *names[] =
@@ -2190,16 +2190,40 @@ InlinePropertyTable::buildTypeSetForFunc
 }
 
 bool
 MInArray::needsNegativeIntCheck() const
 {
     return !index()->range() || index()->range()->lower() < 0;
 }
 
+void *
+MLoadTypedArrayElementStatic::base() const
+{
+    return TypedArray::viewData(typedArray_);
+}
+
+size_t
+MLoadTypedArrayElementStatic::length() const
+{
+    return TypedArray::byteLength(typedArray_);
+}
+
+void *
+MStoreTypedArrayElementStatic::base() const
+{
+    return TypedArray::viewData(typedArray_);
+}
+
+size_t
+MStoreTypedArrayElementStatic::length() const
+{
+    return TypedArray::byteLength(typedArray_);
+}
+
 MDefinition *
 MAsmJSUnsignedToDouble::foldsTo(bool useValueNumbers)
 {
     if (input()->isConstant()) {
         const Value &v = input()->toConstant()->value();
         if (v.isInt32())
             return MConstant::New(DoubleValue(uint32_t(v.toInt32())));
     }
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -4822,16 +4822,60 @@ class MLoadTypedArrayElementHole
     }
     AliasSet getAliasSet() const {
         // Out-of-bounds accesses are handled using a VM call, this may
         // invoke getters on the prototype chain.
         return AliasSet::Store(AliasSet::Any);
     }
 };
 
+// Load a value fallibly or infallibly from a statically known typed array.
+class MLoadTypedArrayElementStatic : public MUnaryInstruction
+{
+    MLoadTypedArrayElementStatic(JSObject *typedArray, MDefinition *ptr)
+      : MUnaryInstruction(ptr), typedArray_(typedArray), fallible_(true)
+    {
+        int type = TypedArray::type(typedArray_);
+        if (type == TypedArray::TYPE_FLOAT32 || type == TypedArray::TYPE_FLOAT64)
+            setResultType(MIRType_Double);
+        else
+            setResultType(MIRType_Int32);
+    }
+
+    CompilerRootObject typedArray_;
+    bool fallible_;
+
+  public:
+    INSTRUCTION_HEADER(LoadTypedArrayElementStatic);
+
+    static MLoadTypedArrayElementStatic *New(JSObject *typedArray, MDefinition *ptr) {
+        return new MLoadTypedArrayElementStatic(typedArray, ptr);
+    }
+
+    ArrayBufferView::ViewType viewType() const { return JS_GetArrayBufferViewType(typedArray_); }
+    void *base() const;
+    size_t length() const;
+
+    MDefinition *ptr() const { return getOperand(0); }
+    AliasSet getAliasSet() const {
+        return AliasSet::Load(AliasSet::TypedArrayElement);
+    }
+
+    bool fallible() const {
+        return fallible_;
+    }
+
+    void setInfallible() {
+        fallible_ = false;
+    }
+
+    void computeRange();
+    bool truncate();
+};
+
 class MStoreTypedArrayElement
   : public MTernaryInstruction,
     public StoreTypedArrayPolicy
 {
     int arrayType_;
 
     // See note in MStoreElementCommon.
     bool racy_;
@@ -4946,16 +4990,49 @@ class MStoreTypedArrayElementHole
     MDefinition *value() const {
         return getOperand(3);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::TypedArrayElement);
     }
 };
 
+// Store a value infallibly to a statically known typed array.
+class MStoreTypedArrayElementStatic :
+    public MBinaryInstruction
+  , public StoreTypedArrayElementStaticPolicy
+{
+    MStoreTypedArrayElementStatic(JSObject *typedArray, MDefinition *ptr, MDefinition *v)
+      : MBinaryInstruction(ptr, v), typedArray_(typedArray)
+    {}
+
+    CompilerRootObject typedArray_;
+
+  public:
+    INSTRUCTION_HEADER(StoreTypedArrayElementStatic);
+
+    static MStoreTypedArrayElementStatic *New(JSObject *typedArray, MDefinition *ptr, MDefinition *v) {
+        return new MStoreTypedArrayElementStatic(typedArray, ptr, v);
+    }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+
+    ArrayBufferView::ViewType viewType() const { return JS_GetArrayBufferViewType(typedArray_); }
+    void *base() const;
+    size_t length() const;
+
+    MDefinition *ptr() const { return getOperand(0); }
+    MDefinition *value() const { return getOperand(1); }
+    AliasSet getAliasSet() const {
+        return AliasSet::Store(AliasSet::TypedArrayElement);
+    }
+};
+
 // Compute an "effective address", i.e., a compound computation of the form:
 //   base + index * scale + displacement
 class MEffectiveAddress : public MBinaryInstruction
 {
     MEffectiveAddress(MDefinition *base, MDefinition *index, Scale scale, int32_t displacement)
       : MBinaryInstruction(base, index), scale_(scale), displacement_(displacement)
     {
         JS_ASSERT(base->type() == MIRType_Int32);
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -121,18 +121,20 @@ namespace ion {
     _(LoadElementHole)                                                      \
     _(StoreElement)                                                         \
     _(StoreElementHole)                                                     \
     _(ArrayPopShift)                                                        \
     _(ArrayPush)                                                            \
     _(ArrayConcat)                                                          \
     _(LoadTypedArrayElement)                                                \
     _(LoadTypedArrayElementHole)                                            \
+    _(LoadTypedArrayElementStatic)                                          \
     _(StoreTypedArrayElement)                                               \
     _(StoreTypedArrayElementHole)                                           \
+    _(StoreTypedArrayElementStatic)                                         \
     _(EffectiveAddress)                                                     \
     _(ClampToUint8)                                                         \
     _(LoadFixedSlot)                                                        \
     _(StoreFixedSlot)                                                       \
     _(CallGetProperty)                                                      \
     _(GetNameCache)                                                         \
     _(CallGetIntrinsicValue)                                                \
     _(CallsiteCloneCache)                                                   \
--- a/js/src/ion/ParallelArrayAnalysis.cpp
+++ b/js/src/ion/ParallelArrayAnalysis.cpp
@@ -208,18 +208,20 @@ class ParallelArrayVisitor : public MIns
     SAFE_OP(LoadElement)
     SAFE_OP(LoadElementHole)
     MAYBE_WRITE_GUARDED_OP(StoreElement, elements)
     WRITE_GUARDED_OP(StoreElementHole, elements)
     UNSAFE_OP(ArrayPopShift)
     UNSAFE_OP(ArrayPush)
     SAFE_OP(LoadTypedArrayElement)
     SAFE_OP(LoadTypedArrayElementHole)
+    SAFE_OP(LoadTypedArrayElementStatic)
     MAYBE_WRITE_GUARDED_OP(StoreTypedArrayElement, elements)
     WRITE_GUARDED_OP(StoreTypedArrayElementHole, elements)
+    UNSAFE_OP(StoreTypedArrayElementStatic)
     UNSAFE_OP(ClampToUint8)
     SAFE_OP(LoadFixedSlot)
     WRITE_GUARDED_OP(StoreFixedSlot, object)
     UNSAFE_OP(CallGetProperty)
     UNSAFE_OP(GetNameCache)
     SAFE_OP(CallGetIntrinsicValue) // Bails in parallel mode
     UNSAFE_OP(CallsiteCloneCache)
     UNSAFE_OP(CallGetElement)
--- a/js/src/ion/RangeAnalysis.cpp
+++ b/js/src/ion/RangeAnalysis.cpp
@@ -745,16 +745,22 @@ MTruncateToInt32::computeRange()
 
 void
 MToInt32::computeRange()
 {
     Range input(getOperand(0));
     setRange(new Range(input.lower(), input.upper()));
 }
 
+void
+MLoadTypedArrayElementStatic::computeRange()
+{
+    setRange(new Range(this));
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Range Analysis
 ///////////////////////////////////////////////////////////////////////////////
 
 void
 RangeAnalysis::markBlocksInLoopBody(MBasicBlock *header, MBasicBlock *current)
 {
     // Visited.
@@ -1332,16 +1338,23 @@ MToDouble::truncate()
     setResultType(MIRType_Int32);
     if (range())
         range()->truncate();
 
     return true;
 }
 
 bool
+MLoadTypedArrayElementStatic::truncate()
+{
+    setInfallible();
+    return false;
+}
+
+bool
 MDefinition::isOperandTruncated(size_t index) const
 {
     return false;
 }
 
 bool
 MTruncateToInt32::isOperandTruncated(size_t index) const
 {
--- a/js/src/ion/TypePolicy.cpp
+++ b/js/src/ion/TypePolicy.cpp
@@ -544,16 +544,25 @@ StoreTypedArrayHolePolicy::adjustInputs(
     JS_ASSERT(store->elements()->type() == MIRType_Elements);
     JS_ASSERT(store->index()->type() == MIRType_Int32);
     JS_ASSERT(store->length()->type() == MIRType_Int32);
 
     return adjustValueInput(ins, store->arrayType(), store->value(), 3);
 }
 
 bool
+StoreTypedArrayElementStaticPolicy::adjustInputs(MInstruction *ins)
+{
+    MStoreTypedArrayElementStatic *store = ins->toStoreTypedArrayElementStatic();
+    JS_ASSERT(store->ptr()->type() == MIRType_Int32);
+
+    return adjustValueInput(ins, store->viewType(), store->value(), 1);
+}
+
+bool
 ClampPolicy::adjustInputs(MInstruction *ins)
 {
     MDefinition *in = ins->toClampToUint8()->input();
 
     switch (in->type()) {
       case MIRType_Int32:
       case MIRType_Double:
       case MIRType_Value:
--- a/js/src/ion/TypePolicy.h
+++ b/js/src/ion/TypePolicy.h
@@ -224,16 +224,22 @@ class StoreTypedArrayPolicy : public Box
 };
 
 class StoreTypedArrayHolePolicy : public StoreTypedArrayPolicy
 {
   public:
     bool adjustInputs(MInstruction *ins);
 };
 
+class StoreTypedArrayElementStaticPolicy : public StoreTypedArrayPolicy
+{
+  public:
+    bool adjustInputs(MInstruction *ins);
+};
+
 // Accepts integers and doubles. Everything else is boxed.
 class ClampPolicy : public BoxInputsPolicy
 {
   public:
     bool adjustInputs(MInstruction *ins);
 };
 
 static inline bool
--- a/js/src/ion/arm/CodeGenerator-arm.cpp
+++ b/js/src/ion/arm/CodeGenerator-arm.cpp
@@ -1657,16 +1657,30 @@ getBase(U *mir)
     switch (mir->base()) {
       case U::Heap: return HeapReg;
       case U::Global: return GlobalReg;
     }
     return InvalidReg;
 }
 
 bool
+CodeGeneratorARM::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins)
+{
+    JS_NOT_REACHED("NYI");
+    return true;
+}
+
+bool
+CodeGeneratorARM::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins)
+{
+    JS_NOT_REACHED("NYI");
+    return true;
+}
+
+bool
 CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
 {
     const MAsmJSLoadHeap *mir = ins->mir();
     bool isSigned;
     int size;
     bool isFloat = false;
     switch (mir->viewType()) {
       case ArrayBufferView::TYPE_INT8:    isSigned = true; size = 8; break;
--- a/js/src/ion/arm/CodeGenerator-arm.h
+++ b/js/src/ion/arm/CodeGenerator-arm.h
@@ -138,16 +138,18 @@ class CodeGeneratorARM : public CodeGene
     bool visitGuardObjectType(LGuardObjectType *guard);
     bool visitGuardClass(LGuardClass *guard);
     bool visitImplicitThis(LImplicitThis *lir);
 
     bool visitInterruptCheck(LInterruptCheck *lir);
 
     bool visitNegI(LNegI *lir);
     bool visitNegD(LNegD *lir);
+    bool visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins);
+    bool visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins);
     bool visitAsmJSLoadHeap(LAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(LAsmJSStoreHeap *ins);
     bool visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins);
     bool visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins);
     bool visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins);
     bool visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins);
 
     bool visitAsmJSPassStackArg(LAsmJSPassStackArg *ins);
--- a/js/src/ion/arm/Lowering-arm.cpp
+++ b/js/src/ion/arm/Lowering-arm.cpp
@@ -456,9 +456,16 @@ bool
 LIRGeneratorARM::lowerTruncateDToInt32(MTruncateToInt32 *ins)
 {
     MDefinition *opd = ins->input();
     JS_ASSERT(opd->type() == MIRType_Double);
 
     return define(new LTruncateDToInt32(useRegister(opd), LDefinition::BogusTemp()), ins);
 }
 
+bool
+LIRGeneratorARM::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins)
+{
+    JS_NOT_REACHED("NYI");
+    return true;
+}
+
 //__aeabi_uidiv
--- a/js/src/ion/arm/Lowering-arm.h
+++ b/js/src/ion/arm/Lowering-arm.h
@@ -67,16 +67,17 @@ class LIRGeneratorARM : public LIRGenera
     bool visitGuardShape(MGuardShape *ins);
     bool visitGuardObjectType(MGuardObjectType *ins);
     bool visitStoreTypedArrayElement(MStoreTypedArrayElement *ins);
     bool visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins);
     bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
     bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
     bool visitInterruptCheck(MInterruptCheck *ins);
+    bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
 };
 
 typedef LIRGeneratorARM LIRGeneratorSpecific;
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_ion_lowering_arm_h__
--- a/js/src/ion/shared/Lowering-shared.h
+++ b/js/src/ion/shared/Lowering-shared.h
@@ -170,16 +170,21 @@ class LIRGeneratorShared : public MInstr
 
   public:
     bool visitConstant(MConstant *ins);
 
     // Whether to generate typed reads for element accesses with hole checks.
     static bool allowTypedElementHoleCheck() {
         return false;
     }
+
+    // Whether to generate typed array accesses on statically known objects.
+    static bool allowStaticTypedArrayAccesses() {
+        return false;
+    }
 };
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_lowering_shared_h__
 
 
--- a/js/src/ion/x64/CodeGenerator-x64.cpp
+++ b/js/src/ion/x64/CodeGenerator-x64.cpp
@@ -380,16 +380,30 @@ CodeGeneratorX64::visitCompareVAndBranch
 bool
 CodeGeneratorX64::visitUInt32ToDouble(LUInt32ToDouble *lir)
 {
     masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
     return true;
 }
 
 bool
+CodeGeneratorX64::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins)
+{
+    JS_NOT_REACHED("NYI");
+    return true;
+}
+
+bool
+CodeGeneratorX64::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins)
+{
+    JS_NOT_REACHED("NYI");
+    return true;
+}
+
+bool
 CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
 {
     MAsmJSLoadHeap *mir = ins->mir();
     ArrayBufferView::ViewType vt = mir->viewType();
 
     Operand srcAddr(HeapReg, ToRegister(ins->ptr()), TimesOne);
 
     if (vt == ArrayBufferView::TYPE_FLOAT32) {
--- a/js/src/ion/x64/CodeGenerator-x64.h
+++ b/js/src/ion/x64/CodeGenerator-x64.h
@@ -47,16 +47,18 @@ class CodeGeneratorX64 : public CodeGene
     bool visitImplicitThis(LImplicitThis *lir);
     bool visitInterruptCheck(LInterruptCheck *lir);
     bool visitCompareB(LCompareB *lir);
     bool visitCompareBAndBranch(LCompareBAndBranch *lir);
     bool visitCompareV(LCompareV *lir);
     bool visitCompareVAndBranch(LCompareVAndBranch *lir);
     bool visitUInt32ToDouble(LUInt32ToDouble *lir);
     bool visitTruncateDToInt32(LTruncateDToInt32 *ins);
+    bool visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins);
+    bool visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins);
     bool visitAsmJSLoadHeap(LAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(LAsmJSStoreHeap *ins);
     bool visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins);
     bool visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins);
     bool visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins);
     bool visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins);
 
     void postAsmJSCall(LAsmJSCall *lir) {}
--- a/js/src/ion/x64/Lowering-x64.cpp
+++ b/js/src/ion/x64/Lowering-x64.cpp
@@ -209,8 +209,15 @@ LIRGeneratorX64::newLGetPropertyCacheT(M
 bool
 LIRGeneratorX64::lowerTruncateDToInt32(MTruncateToInt32 *ins)
 {
     MDefinition *opd = ins->input();
     JS_ASSERT(opd->type() == MIRType_Double);
 
     return define(new LTruncateDToInt32(useRegister(opd), LDefinition::BogusTemp()), ins);
 }
+
+bool
+LIRGeneratorX64::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins)
+{
+    JS_NOT_REACHED("NYI");
+    return true;
+}
--- a/js/src/ion/x64/Lowering-x64.h
+++ b/js/src/ion/x64/Lowering-x64.h
@@ -44,16 +44,17 @@ class LIRGeneratorX64 : public LIRGenera
     bool visitBox(MBox *box);
     bool visitUnbox(MUnbox *unbox);
     bool visitReturn(MReturn *ret);
     bool visitStoreTypedArrayElement(MStoreTypedArrayElement *ins);
     bool visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins);
     bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
     bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
+    bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
 };
 
 typedef LIRGeneratorX64 LIRGeneratorSpecific;
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_ion_lowering_x64_h__
--- a/js/src/ion/x86/CodeGenerator-x86.cpp
+++ b/js/src/ion/x86/CodeGenerator-x86.cpp
@@ -392,35 +392,94 @@ CodeGeneratorX86::visitUInt32ToDouble(LU
     if (input != temp)
         masm.mov(input, temp);
 
     // Beware: convertUInt32ToDouble clobbers input.
     masm.convertUInt32ToDouble(temp, ToFloatRegister(lir->output()));
     return true;
 }
 
-class ion::OutOfLineAsmJSLoadHeapOutOfBounds : public OutOfLineCodeBase<CodeGeneratorX86>
+// Load a NaN or zero into a register for an out of bounds AsmJS or static
+// typed array load.
+class ion::OutOfLineLoadTypedArrayOutOfBounds : public OutOfLineCodeBase<CodeGeneratorX86>
 {
     AnyRegister dest_;
   public:
-    OutOfLineAsmJSLoadHeapOutOfBounds(AnyRegister dest) : dest_(dest) {}
+    OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest) : dest_(dest) {}
     const AnyRegister &dest() const { return dest_; }
-    bool accept(CodeGeneratorX86 *codegen) { return codegen->visitOutOfLineAsmJSLoadHeapOutOfBounds(this); }
+    bool accept(CodeGeneratorX86 *codegen) { return codegen->visitOutOfLineLoadTypedArrayOutOfBounds(this); }
 };
 
+void
+CodeGeneratorX86::loadViewTypeElement(ArrayBufferView::ViewType vt, const Address &srcAddr,
+                                      const LDefinition *out)
+{
+    switch (vt) {
+      case ArrayBufferView::TYPE_INT8:    masm.movxblWithPatch(srcAddr, ToRegister(out)); break;
+      case ArrayBufferView::TYPE_UINT8_CLAMPED:
+      case ArrayBufferView::TYPE_UINT8:   masm.movzblWithPatch(srcAddr, ToRegister(out)); break;
+      case ArrayBufferView::TYPE_INT16:   masm.movxwlWithPatch(srcAddr, ToRegister(out)); break;
+      case ArrayBufferView::TYPE_UINT16:  masm.movzwlWithPatch(srcAddr, ToRegister(out)); break;
+      case ArrayBufferView::TYPE_INT32:   masm.movlWithPatch(srcAddr, ToRegister(out)); break;
+      case ArrayBufferView::TYPE_UINT32:  masm.movlWithPatch(srcAddr, ToRegister(out)); break;
+      case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(srcAddr, ToFloatRegister(out)); break;
+      default: JS_NOT_REACHED("unexpected array type");
+    }
+}
+
+bool
+CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins)
+{
+    const MLoadTypedArrayElementStatic *mir = ins->mir();
+    ArrayBufferView::ViewType vt = mir->viewType();
+
+    Register ptr = ToRegister(ins->ptr());
+    const LDefinition *out = ins->output();
+
+    OutOfLineLoadTypedArrayOutOfBounds *ool = NULL;
+    if (!mir->fallible()) {
+        ool = new OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out));
+        if (!addOutOfLineCode(ool))
+            return false;
+    }
+
+    masm.cmpl(ptr, Imm32(mir->length()));
+    if (ool)
+        masm.j(Assembler::AboveOrEqual, ool->entry());
+    else if (!bailoutIf(Assembler::AboveOrEqual, ins->snapshot()))
+        return false;
+
+    Address srcAddr(ptr, (int32_t) mir->base());
+    if (vt == ArrayBufferView::TYPE_FLOAT32) {
+        FloatRegister dest = ToFloatRegister(out);
+        masm.movssWithPatch(srcAddr, dest);
+        masm.cvtss2sd(dest, dest);
+        if (ool)
+            masm.bind(ool->rejoin());
+        return true;
+    }
+    loadViewTypeElement(vt, srcAddr, out);
+    if (ool)
+        masm.bind(ool->rejoin());
+    return true;
+}
+
 bool
 CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
 {
+    // This is identical to LoadTypedArrayElementStatic, except that the
+    // array's base and length are not known ahead of time and can be patched
+    // later on, and the instruction is always infallible.
     const MAsmJSLoadHeap *mir = ins->mir();
     ArrayBufferView::ViewType vt = mir->viewType();
 
     Register ptr = ToRegister(ins->ptr());
     const LDefinition *out = ins->output();
 
-    OutOfLineAsmJSLoadHeapOutOfBounds *ool = new OutOfLineAsmJSLoadHeapOutOfBounds(ToAnyRegister(out));
+    OutOfLineLoadTypedArrayOutOfBounds *ool = new OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out));
     if (!addOutOfLineCode(ool))
         return false;
 
     CodeOffsetLabel cmp = masm.cmplWithPatch(ptr, Imm32(0));
     masm.j(Assembler::AboveOrEqual, ool->entry());
 
     Address srcAddr(ptr, 0);
     if (vt == ArrayBufferView::TYPE_FLOAT32) {
@@ -428,45 +487,81 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAs
         uint32_t before = masm.size();
         masm.movssWithPatch(srcAddr, dest);
         uint32_t after = masm.size();
         masm.cvtss2sd(dest, dest);
         masm.bind(ool->rejoin());
         return gen->noteHeapAccess(AsmJSHeapAccess(cmp.offset(), before, after, vt, AnyRegister(dest)));
     }
     uint32_t before = masm.size();
-    switch (vt) {
-      case ArrayBufferView::TYPE_INT8:    masm.movxblWithPatch(srcAddr, ToRegister(out)); break;
-      case ArrayBufferView::TYPE_UINT8:   masm.movzblWithPatch(srcAddr, ToRegister(out)); break;
-      case ArrayBufferView::TYPE_INT16:   masm.movxwlWithPatch(srcAddr, ToRegister(out)); break;
-      case ArrayBufferView::TYPE_UINT16:  masm.movzwlWithPatch(srcAddr, ToRegister(out)); break;
-      case ArrayBufferView::TYPE_INT32:   masm.movlWithPatch(srcAddr, ToRegister(out)); break;
-      case ArrayBufferView::TYPE_UINT32:  masm.movlWithPatch(srcAddr, ToRegister(out)); break;
-      case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(srcAddr, ToFloatRegister(out)); break;
-      default: JS_NOT_REACHED("unexpected array type");
-    }
+    loadViewTypeElement(vt, srcAddr, out);
     uint32_t after = masm.size();
     masm.bind(ool->rejoin());
     return gen->noteHeapAccess(AsmJSHeapAccess(cmp.offset(), before, after, vt, ToAnyRegister(out)));
 }
 
 bool
-CodeGeneratorX86::visitOutOfLineAsmJSLoadHeapOutOfBounds(OutOfLineAsmJSLoadHeapOutOfBounds *ool)
+CodeGeneratorX86::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool)
 {
     if (ool->dest().isFloat())
         masm.movsd(&js_NaN, ool->dest().fpu());
     else
         masm.movl(Imm32(0), ool->dest().gpr());
     masm.jmp(ool->rejoin());
     return true;
 }
 
+void
+CodeGeneratorX86::storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
+                                       const Address &dstAddr)
+{
+    switch (vt) {
+      case ArrayBufferView::TYPE_INT8:    masm.movbWithPatch(ToRegister(value), dstAddr); break;
+      case ArrayBufferView::TYPE_UINT8_CLAMPED:
+      case ArrayBufferView::TYPE_UINT8:   masm.movbWithPatch(ToRegister(value), dstAddr); break;
+      case ArrayBufferView::TYPE_INT16:   masm.movwWithPatch(ToRegister(value), dstAddr); break;
+      case ArrayBufferView::TYPE_UINT16:  masm.movwWithPatch(ToRegister(value), dstAddr); break;
+      case ArrayBufferView::TYPE_INT32:   masm.movlWithPatch(ToRegister(value), dstAddr); break;
+      case ArrayBufferView::TYPE_UINT32:  masm.movlWithPatch(ToRegister(value), dstAddr); break;
+      case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(ToFloatRegister(value), dstAddr); break;
+      default: JS_NOT_REACHED("unexpected array type");
+    }
+}
+
+bool
+CodeGeneratorX86::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins)
+{
+    MStoreTypedArrayElementStatic *mir = ins->mir();
+    ArrayBufferView::ViewType vt = mir->viewType();
+
+    Register ptr = ToRegister(ins->ptr());
+    const LAllocation *value = ins->value();
+
+    masm.cmpl(ptr, Imm32(mir->length()));
+    Label rejoin;
+    masm.j(Assembler::AboveOrEqual, &rejoin);
+
+    Address dstAddr(ptr, (int32_t) mir->base());
+    if (vt == ArrayBufferView::TYPE_FLOAT32) {
+        masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg);
+        masm.movssWithPatch(ScratchFloatReg, dstAddr);
+        masm.bind(&rejoin);
+        return true;
+    }
+    storeViewTypeElement(vt, value, dstAddr);
+    masm.bind(&rejoin);
+    return true;
+}
+
 bool
 CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
 {
+    // This is identical to StoreTypedArrayElementStatic, except that the
+    // array's base and length are not known ahead of time and can be patched
+    // later on.
     MAsmJSStoreHeap *mir = ins->mir();
     ArrayBufferView::ViewType vt = mir->viewType();
 
     Register ptr = ToRegister(ins->ptr());
     const LAllocation *value = ins->value();
 
     CodeOffsetLabel cmp = masm.cmplWithPatch(ptr, Imm32(0));
     Label rejoin;
@@ -477,26 +572,17 @@ CodeGeneratorX86::visitAsmJSStoreHeap(LA
         masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg);
         uint32_t before = masm.size();
         masm.movssWithPatch(ScratchFloatReg, dstAddr);
         uint32_t after = masm.size();
         masm.bind(&rejoin);
         return gen->noteHeapAccess(AsmJSHeapAccess(cmp.offset(), before, after));
     }
     uint32_t before = masm.size();
-    switch (vt) {
-      case ArrayBufferView::TYPE_INT8:    masm.movbWithPatch(ToRegister(value), dstAddr); break;
-      case ArrayBufferView::TYPE_UINT8:   masm.movbWithPatch(ToRegister(value), dstAddr); break;
-      case ArrayBufferView::TYPE_INT16:   masm.movwWithPatch(ToRegister(value), dstAddr); break;
-      case ArrayBufferView::TYPE_UINT16:  masm.movwWithPatch(ToRegister(value), dstAddr); break;
-      case ArrayBufferView::TYPE_INT32:   masm.movlWithPatch(ToRegister(value), dstAddr); break;
-      case ArrayBufferView::TYPE_UINT32:  masm.movlWithPatch(ToRegister(value), dstAddr); break;
-      case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(ToFloatRegister(value), dstAddr); break;
-      default: JS_NOT_REACHED("unexpected array type");
-    }
+    storeViewTypeElement(vt, value, dstAddr);
     uint32_t after = masm.size();
     masm.bind(&rejoin);
     return gen->noteHeapAccess(AsmJSHeapAccess(cmp.offset(), before, after));
 }
 
 bool
 CodeGeneratorX86::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins)
 {
--- a/js/src/ion/x86/CodeGenerator-x86.h
+++ b/js/src/ion/x86/CodeGenerator-x86.h
@@ -8,31 +8,35 @@
 #define jsion_codegen_x86_h__
 
 #include "Assembler-x86.h"
 #include "ion/shared/CodeGenerator-x86-shared.h"
 
 namespace js {
 namespace ion {
 
-class OutOfLineAsmJSLoadHeapOutOfBounds;
+class OutOfLineLoadTypedArrayOutOfBounds;
 class OutOfLineTruncate;
 
 class CodeGeneratorX86 : public CodeGeneratorX86Shared
 {
   private:
     CodeGeneratorX86 *thisFromCtor() {
         return this;
     }
 
   protected:
     ValueOperand ToValue(LInstruction *ins, size_t pos);
     ValueOperand ToOutValue(LInstruction *ins);
     ValueOperand ToTempValue(LInstruction *ins, size_t pos);
 
+    void loadViewTypeElement(ArrayBufferView::ViewType vt, const Address &srcAddr,
+                             const LDefinition *out);
+    void storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
+                              const Address &dstAddr);
     void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
                            const Register &elements, const LAllocation *index);
 
   public:
     CodeGeneratorX86(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm);
 
   public:
     bool visitBox(LBox *box);
@@ -47,24 +51,26 @@ class CodeGeneratorX86 : public CodeGene
     bool visitImplicitThis(LImplicitThis *lir);
     bool visitInterruptCheck(LInterruptCheck *lir);
     bool visitCompareB(LCompareB *lir);
     bool visitCompareBAndBranch(LCompareBAndBranch *lir);
     bool visitCompareV(LCompareV *lir);
     bool visitCompareVAndBranch(LCompareVAndBranch *lir);
     bool visitUInt32ToDouble(LUInt32ToDouble *lir);
     bool visitTruncateDToInt32(LTruncateDToInt32 *ins);
+    bool visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins);
+    bool visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins);
     bool visitAsmJSLoadHeap(LAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(LAsmJSStoreHeap *ins);
     bool visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins);
     bool visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins);
     bool visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins);
     bool visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins);
 
-    bool visitOutOfLineAsmJSLoadHeapOutOfBounds(OutOfLineAsmJSLoadHeapOutOfBounds *ool);
+    bool visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool);
     bool visitOutOfLineTruncate(OutOfLineTruncate *ool);
 
     void postAsmJSCall(LAsmJSCall *lir);
 };
 
 typedef CodeGeneratorX86 CodeGeneratorSpecific;
 
 } // namespace ion
--- a/js/src/ion/x86/Lowering-x86.cpp
+++ b/js/src/ion/x86/Lowering-x86.cpp
@@ -276,16 +276,40 @@ LIRGeneratorX86::visitAsmJSStoreHeap(MAs
         break;
       default: JS_NOT_REACHED("unexpected array type");
     }
 
     return add(lir, ins);
 }
 
 bool
+LIRGeneratorX86::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins)
+{
+    // The code generated for StoreTypedArrayElementStatic is identical to that
+    // for AsmJSStoreHeap, and the same concerns apply.
+    LStoreTypedArrayElementStatic *lir;
+    switch (ins->viewType()) {
+      case ArrayBufferView::TYPE_INT8: case ArrayBufferView::TYPE_UINT8:
+      case ArrayBufferView::TYPE_UINT8_CLAMPED:
+        lir = new LStoreTypedArrayElementStatic(useRegister(ins->ptr()),
+                                                useFixed(ins->value(), eax));
+        break;
+      case ArrayBufferView::TYPE_INT16: case ArrayBufferView::TYPE_UINT16:
+      case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32:
+      case ArrayBufferView::TYPE_FLOAT32: case ArrayBufferView::TYPE_FLOAT64:
+        lir = new LStoreTypedArrayElementStatic(useRegisterAtStart(ins->ptr()),
+                                                useRegisterAtStart(ins->value()));
+        break;
+      default: JS_NOT_REACHED("unexpected array type");
+    }
+
+    return add(lir, ins);
+}
+
+bool
 LIRGeneratorX86::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins)
 {
     return define(new LAsmJSLoadFuncPtr(useRegisterAtStart(ins->index())), ins);
 }
 
 LGetPropertyCacheT *
 LIRGeneratorX86::newLGetPropertyCacheT(MGetPropertyCache *ins)
 {
--- a/js/src/ion/x86/Lowering-x86.h
+++ b/js/src/ion/x86/Lowering-x86.h
@@ -46,21 +46,26 @@ class LIRGeneratorX86 : public LIRGenera
     bool visitBox(MBox *box);
     bool visitUnbox(MUnbox *unbox);
     bool visitReturn(MReturn *ret);
     bool visitStoreTypedArrayElement(MStoreTypedArrayElement *ins);
     bool visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins);
     bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
     bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
+    bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
     bool lowerPhi(MPhi *phi);
 
     static bool allowTypedElementHoleCheck() {
         return true;
     }
+
+    static bool allowStaticTypedArrayAccesses() {
+        return true;
+    }
 };
 
 typedef LIRGeneratorX86 LIRGeneratorSpecific;
 
 } // namespace js
 } // namespace ion
 
 #endif // jsion_ion_lowering_x86_h__
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2749,17 +2749,20 @@ types::ArrayPrototypeHasIndexedProperty(
     return PrototypeHasIndexedProperty(cx, proto);
 }
 
 bool
 types::TypeCanHaveExtraIndexedProperties(JSContext *cx, StackTypeSet *types)
 {
     Class *clasp = types->getKnownClass();
 
-    if (!clasp || ClassCanHaveExtraProperties(clasp))
+    // Note: typed arrays have indexed properties not accounted for by type
+    // information, though these are all in bounds and will be accounted for
+    // by JIT paths.
+    if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsTypedArrayClass(clasp)))
         return true;
 
     if (types->hasObjectFlags(cx, types::OBJECT_FLAG_SPARSE_INDEXES))
         return true;
 
     JSObject *proto = types->getCommonPrototype();
     if (!proto)
         return true;
--- a/js/src/jstypedarray.h
+++ b/js/src/jstypedarray.h
@@ -316,16 +316,17 @@ bool
 IsTypedArrayBuffer(const Value &v);
 
 static inline unsigned
 TypedArrayShift(ArrayBufferView::ViewType viewType)
 {
     switch (viewType) {
       case ArrayBufferView::TYPE_INT8:
       case ArrayBufferView::TYPE_UINT8:
+      case ArrayBufferView::TYPE_UINT8_CLAMPED:
         return 0;
       case ArrayBufferView::TYPE_INT16:
       case ArrayBufferView::TYPE_UINT16:
         return 1;
       case ArrayBufferView::TYPE_INT32:
       case ArrayBufferView::TYPE_UINT32:
       case ArrayBufferView::TYPE_FLOAT32:
         return 2;