Bug 898349 - JIT support for getting and setting scalar properties and for optimizing away intermediate typed objects r=jandem
authorNicholas D. Matsakis <nmatsakis@mozilla.com>
Wed, 21 Aug 2013 13:35:30 -0400
changeset 148196 4c2b9302fae801caaa080995585e2af73d8e8bf5
parent 148195 a656666e63b636665d36ec4d299a6bd51bd14209
child 148197 4495cc6591c432af82e06dc60fe767dc41b4f4e1
push id34136
push usernmatsakis@mozilla.com
push dateSat, 21 Sep 2013 09:21:01 +0000
treeherdermozilla-inbound@4c2b9302fae8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs898349
milestone27.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 898349 - JIT support for getting and setting scalar properties and for optimizing away intermediate typed objects r=jandem
js/src/builtin/TypeRepresentation.cpp
js/src/builtin/TypeRepresentation.h
js/src/builtin/TypedObject.cpp
js/src/builtin/TypedObject.h
js/src/jit-test/tests/TypedObject/jit-complex.js
js/src/jit-test/tests/TypedObject/jit-prefix.js
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/LIR-Common.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/jit/TypePolicy.cpp
js/src/jit/TypeRepresentationSet.cpp
js/src/jit/TypeRepresentationSet.h
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/jscompartment.h
js/src/moz.build
--- a/js/src/builtin/TypeRepresentation.cpp
+++ b/js/src/builtin/TypeRepresentation.cpp
@@ -255,17 +255,17 @@ StructTypeRepresentation::init(JSContext
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Interning
 
 JSObject *
 TypeRepresentation::addToTableOrFree(JSContext *cx,
-                                     TypeRepresentationSet::AddPtr &p)
+                                     TypeRepresentationHash::AddPtr &p)
 {
     JS_ASSERT(!ownerObject_);
 
     JSCompartment *comp = cx->compartment();
 
     if (!comp->typeReprs.add(p, this)) {
         js_ReportOutOfMemory(cx);
         js_free(this); // do not finalize, not present in the table
@@ -293,17 +293,17 @@ TypeRepresentation::addToTableOrFree(JSC
 /*static*/
 JSObject *
 ScalarTypeRepresentation::Create(JSContext *cx,
                                  ScalarTypeRepresentation::Type type)
 {
     JSCompartment *comp = cx->compartment();
 
     ScalarTypeRepresentation sample(type);
-    TypeRepresentationSet::AddPtr p = comp->typeReprs.lookupForAdd(&sample);
+    TypeRepresentationHash::AddPtr p = comp->typeReprs.lookupForAdd(&sample);
     if (p)
         return (*p)->ownerObject();
 
     // Note: cannot use cx->new_ because constructor is private.
     ScalarTypeRepresentation *ptr =
         (ScalarTypeRepresentation *) cx->malloc_(
             sizeof(ScalarTypeRepresentation));
     if (!ptr)
@@ -327,17 +327,17 @@ ArrayTypeRepresentation::Create(JSContex
     int32_t temp;
     if (!SafeMul(element->size(), length, &temp)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_TYPEDOBJECT_TOO_BIG);
         return NULL;
     }
 
     ArrayTypeRepresentation sample(element, length);
-    TypeRepresentationSet::AddPtr p = comp->typeReprs.lookupForAdd(&sample);
+    TypeRepresentationHash::AddPtr p = comp->typeReprs.lookupForAdd(&sample);
     if (p)
         return (*p)->ownerObject();
 
     // Note: cannot use cx->new_ because constructor is private.
     ArrayTypeRepresentation *ptr =
         (ArrayTypeRepresentation *) cx->malloc_(
             sizeof(ArrayTypeRepresentation));
     if (!ptr)
@@ -359,17 +359,17 @@ StructTypeRepresentation::Create(JSConte
     // Note: cannot use cx->new_ because constructor is private.
     size_t size = sizeof(StructTypeRepresentation) + count * sizeof(StructField);
     StructTypeRepresentation *ptr =
         (StructTypeRepresentation *) cx->malloc_(size);
     new(ptr) StructTypeRepresentation();
     if (!ptr->init(cx, ids, typeReprOwners))
         return NULL;
 
-    TypeRepresentationSet::AddPtr p = comp->typeReprs.lookupForAdd(ptr);
+    TypeRepresentationHash::AddPtr p = comp->typeReprs.lookupForAdd(ptr);
     if (p) {
         js_free(ptr); // do not finalize, not present in the table
         return (*p)->ownerObject();
     }
 
     return ptr->addToTableOrFree(cx, p);
 }
 
@@ -535,20 +535,20 @@ StructTypeRepresentation::appendStringSt
 
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Misc
 
 const StructField *
-StructTypeRepresentation::fieldNamed(HandleId id) const
+StructTypeRepresentation::fieldNamed(jsid id) const
 {
     for (size_t i = 0; i < fieldCount(); i++) {
-        if (field(i).id.get() == id.get())
+        if (field(i).id.get() == id)
             return &field(i);
     }
     return NULL;
 }
 
 /*static*/ bool
 TypeRepresentation::isTypeRepresentationOwnerObject(JSObject *obj)
 {
--- a/js/src/builtin/TypeRepresentation.h
+++ b/js/src/builtin/TypeRepresentation.h
@@ -80,34 +80,34 @@ struct TypeRepresentationHasher
     static bool matchStructs(StructTypeRepresentation *key1,
                              StructTypeRepresentation *key2);
     static bool matchArrays(ArrayTypeRepresentation *key1,
                             ArrayTypeRepresentation *key2);
 };
 
 typedef js::HashSet<TypeRepresentation *,
                     TypeRepresentationHasher,
-                    RuntimeAllocPolicy> TypeRepresentationSet;
+                    RuntimeAllocPolicy> TypeRepresentationHash;
 
 class TypeRepresentation {
   public:
     enum Kind {
         Scalar,
         Struct,
         Array
     };
 
   protected:
     TypeRepresentation(Kind kind, size_t size, size_t align);
 
     size_t size_;
     size_t alignment_;
     Kind kind_;
 
-    JSObject *addToTableOrFree(JSContext *cx, TypeRepresentationSet::AddPtr &p);
+    JSObject *addToTableOrFree(JSContext *cx, TypeRepresentationHash::AddPtr &p);
 
   private:
     static const Class class_;
     static void obj_trace(JSTracer *trace, JSObject *object);
     static void obj_finalize(js::FreeOp *fop, JSObject *object);
 
     js::HeapPtrObject ownerObject_;
     void traceFields(JSTracer *tracer);
@@ -285,17 +285,17 @@ class StructTypeRepresentation : public 
         return fieldCount_;
     }
 
     const StructField &field(size_t i) const {
         JS_ASSERT(i < fieldCount());
         return fields()[i];
     }
 
-    const StructField *fieldNamed(HandleId id) const;
+    const StructField *fieldNamed(jsid id) const;
 
     static JSObject *Create(JSContext *cx,
                             AutoIdVector &ids,
                             AutoObjectVector &typeReprOwners);
 };
 
 } // namespace js
 
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -2361,8 +2361,15 @@ BinaryBlock::obj_enumerate(JSContext *cx
             statep.setNull();
             break;
         }
         break;
     }
 
     return true;
 }
+
+/* static */ size_t
+BinaryBlock::dataOffset()
+{
+    return JSObject::getPrivateDataOffset(BLOCK_RESERVED_SLOTS + 1);
+}
+
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -192,16 +192,20 @@ class BinaryBlock
                                     bool *succeeded);
 
     static bool obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
                                 MutableHandleValue statep, MutableHandleId idp);
 
   public:
     static const Class class_;
 
+    // Returns the offset in bytes within the object where the `void*`
+    // pointer can be found.
+    static size_t dataOffset();
+
     static bool isBlock(HandleObject val);
     static uint8_t *mem(HandleObject val);
 
     // creates zeroed memory of size of type
     static JSObject *createZeroed(JSContext *cx, HandleObject type);
 
     // creates a block that aliases the memory owned by `owner` at the
     // given offset
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObject/jit-complex.js
@@ -0,0 +1,30 @@
+// Test that we can optimize stuff like line.target.x without
+// creating an intermediate object.
+
+if (!this.hasOwnProperty("Type"))
+  quit();
+
+var PointType = new StructType({x: float64,
+                                y: float64});
+var LineType = new StructType({source: PointType,
+                               target: PointType});
+
+function manhattenDistance(line) {
+  return (Math.abs(line.target.x - line.source.x) +
+          Math.abs(line.target.y - line.source.y));
+}
+
+function foo() {
+  var N = 30000;
+  var points = [];
+  var obj;
+  var s;
+
+  var fromAToB = new LineType({source: {x: 22, y: 44},
+                               target: {x: 66, y: 88}});
+
+  for (var i = 0; i < N; i++)
+    assertEq(manhattenDistance(fromAToB), 88);
+}
+
+foo();
--- a/js/src/jit-test/tests/TypedObject/jit-prefix.js
+++ b/js/src/jit-test/tests/TypedObject/jit-prefix.js
@@ -7,22 +7,31 @@ var PointType2 = new StructType({x: floa
 var PointType3 = new StructType({x: float64,
                                  y: float64,
                                  z: float64});
 
 function xPlusY(p) {
   return p.x + p.y;
 }
 
+function xPlusYTweak(p) {
+  p.x = 22;
+  return xPlusY(p);
+}
+
 function foo() {
   var N = 30000;
   var points = [];
+  var obj;
+  var s;
+
   for (var i = 0; i < N; i++) {
-    var s;
     if ((i % 2) == 0 || true)
-      s = xPlusY(new PointType2({x: i, y: i+1}));
+      obj = new PointType2({x: i, y: i+1});
     else
-      s = xPlusY(new PointType3({x: i, y: i+1, z: i+2}));
-    assertEq(s, i + i + 1);
+      obj = new PointType3({x: i, y: i+1, z: i+2});
+
+    assertEq(xPlusY(obj), i + i + 1);
+    assertEq(xPlusYTweak(obj), 22 + i + 1);
   }
 }
 
 foo();
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Util.h"
 
 #include "jslibmath.h"
 #include "jsmath.h"
 #include "jsnum.h"
 
 #include "builtin/Eval.h"
+#include "builtin/TypedObject.h"
 #include "gc/Nursery.h"
 #include "jit/ExecutionModeInlines.h"
 #include "jit/IonLinker.h"
 #include "jit/IonSpewer.h"
 #include "jit/Lowering.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MoveEmitter.h"
 #include "jit/ParallelFunctions.h"
@@ -2856,16 +2857,35 @@ CodeGenerator::visitNewArrayCallVM(LNewA
     if (ReturnReg != objReg)
         masm.movePtr(ReturnReg, objReg);
 
     restoreLive(lir);
 
     return true;
 }
 
+typedef JSObject *(*NewDerivedTypedObjectFn)(JSContext *,
+                                             HandleObject type,
+                                             HandleObject owner,
+                                             int32_t offset);
+static const VMFunction CreateDerivedTypedObjInfo =
+    FunctionInfo<NewDerivedTypedObjectFn>(CreateDerivedTypedObj);
+
+bool
+CodeGenerator::visitNewDerivedTypedObject(LNewDerivedTypedObject *lir)
+{
+    // Not yet made safe for par exec:
+    JS_ASSERT(gen->info().executionMode() == SequentialExecution);
+
+    pushArg(ToRegister(lir->offset()));
+    pushArg(ToRegister(lir->owner()));
+    pushArg(ToRegister(lir->type()));
+    return callVM(CreateDerivedTypedObjInfo, lir);
+}
+
 bool
 CodeGenerator::visitNewSlots(LNewSlots *lir)
 {
     Register temp1 = ToRegister(lir->temp1());
     Register temp2 = ToRegister(lir->temp2());
     Register temp3 = ToRegister(lir->temp3());
     Register output = ToRegister(lir->output());
 
@@ -3573,16 +3593,25 @@ CodeGenerator::visitTypedArrayElements(L
 {
     Register obj = ToRegister(lir->object());
     Register out = ToRegister(lir->output());
     masm.loadPtr(Address(obj, TypedArrayObject::dataOffset()), out);
     return true;
 }
 
 bool
+CodeGenerator::visitTypedObjectElements(LTypedObjectElements *lir)
+{
+    Register obj = ToRegister(lir->object());
+    Register out = ToRegister(lir->output());
+    masm.loadPtr(Address(obj, BinaryBlock::dataOffset()), out);
+    return true;
+}
+
+bool
 CodeGenerator::visitStringLength(LStringLength *lir)
 {
     Register input = ToRegister(lir->string());
     Register output = ToRegister(lir->output());
 
     masm.loadStringLength(input, output);
     return true;
 }
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -127,32 +127,34 @@ class CodeGenerator : public CodeGenerat
     bool visitNewObject(LNewObject *lir);
     bool visitOutOfLineNewObject(OutOfLineNewObject *ool);
     bool visitNewDeclEnvObject(LNewDeclEnvObject *lir);
     bool visitNewCallObject(LNewCallObject *lir);
     bool visitNewCallObjectPar(LNewCallObjectPar *lir);
     bool visitNewStringObject(LNewStringObject *lir);
     bool visitNewPar(LNewPar *lir);
     bool visitNewDenseArrayPar(LNewDenseArrayPar *lir);
+    bool visitNewDerivedTypedObject(LNewDerivedTypedObject *lir);
     bool visitAbortPar(LAbortPar *lir);
     bool visitInitElem(LInitElem *lir);
     bool visitInitElemGetterSetter(LInitElemGetterSetter *lir);
     bool visitInitProp(LInitProp *lir);
     bool visitInitPropGetterSetter(LInitPropGetterSetter *lir);
     bool visitCreateThis(LCreateThis *lir);
     bool visitCreateThisWithProto(LCreateThisWithProto *lir);
     bool visitCreateThisWithTemplate(LCreateThisWithTemplate *lir);
     bool visitCreateArgumentsObject(LCreateArgumentsObject *lir);
     bool visitGetArgumentsObjectArg(LGetArgumentsObjectArg *lir);
     bool visitSetArgumentsObjectArg(LSetArgumentsObjectArg *lir);
     bool visitReturnFromCtor(LReturnFromCtor *lir);
     bool visitComputeThis(LComputeThis *lir);
     bool visitArrayLength(LArrayLength *lir);
     bool visitTypedArrayLength(LTypedArrayLength *lir);
     bool visitTypedArrayElements(LTypedArrayElements *lir);
+    bool visitTypedObjectElements(LTypedObjectElements *lir);
     bool visitStringLength(LStringLength *lir);
     bool visitInitializedLength(LInitializedLength *lir);
     bool visitSetInitializedLength(LSetInitializedLength *lir);
     bool visitNotO(LNotO *ins);
     bool visitNotV(LNotV *ins);
     bool visitBoundsCheck(LBoundsCheck *lir);
     bool visitBoundsCheckRange(LBoundsCheckRange *lir);
     bool visitBoundsCheckLower(LBoundsCheckLower *lir);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6,16 +6,17 @@
 
 #include "jit/IonBuilder.h"
 
 #include "mozilla/DebugOnly.h"
 
 #include "jsautooplen.h"
 
 #include "builtin/Eval.h"
+#include "builtin/TypedObject.h"
 #include "builtin/TypeRepresentation.h"
 #include "frontend/SourceNotes.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineInspector.h"
 #include "jit/ExecutionModeInlines.h"
 #include "jit/Ion.h"
 #include "jit/IonAnalysis.h"
 #include "jit/IonSpewer.h"
@@ -1181,17 +1182,25 @@ IonBuilder::traverseBytecode()
                 // we may replace it with |undefined|, but the difference is
                 // not observable.
                 JS_ASSERT(i == 0);
                 if (current->peek(-1) == popped[0])
                     break;
                 // FALL THROUGH
 
               default:
-                JS_ASSERT(popped[i]->isFolded() || popped[i]->defUseCount() > poppedUses[i]);
+                JS_ASSERT(popped[i]->isFolded() ||
+
+                          // MNewDerivedTypedObject instances are
+                          // often dead unless they escape from the
+                          // fn. See IonBuilder::loadTypedObjectData()
+                          // for more details.
+                          popped[i]->isNewDerivedTypedObject() ||
+
+                          popped[i]->defUseCount() > poppedUses[i]);
                 break;
             }
         }
 #endif
 
         pc += js_CodeSpec[op].length;
         current->updateTrackedPc(pc);
     }
@@ -6996,16 +7005,38 @@ IonBuilder::convertShiftToMaskForStaticT
     JS_ASSERT(!ptr->isEffectful());
 
     current->add(mask);
     current->add(ptr);
 
     return ptr;
 }
 
+static MIRType
+MIRTypeForTypedArrayRead(ScalarTypeRepresentation::Type arrayType,
+                         bool observedDouble)
+{
+    switch (arrayType) {
+      case ScalarTypeRepresentation::TYPE_INT8:
+      case ScalarTypeRepresentation::TYPE_UINT8:
+      case ScalarTypeRepresentation::TYPE_UINT8_CLAMPED:
+      case ScalarTypeRepresentation::TYPE_INT16:
+      case ScalarTypeRepresentation::TYPE_UINT16:
+      case ScalarTypeRepresentation::TYPE_INT32:
+        return MIRType_Int32;
+      case ScalarTypeRepresentation::TYPE_UINT32:
+        return observedDouble ? MIRType_Double : MIRType_Int32;
+      case ScalarTypeRepresentation::TYPE_FLOAT32:
+        return (LIRGenerator::allowFloat32Optimizations()) ? MIRType_Float32 : MIRType_Double;
+      case ScalarTypeRepresentation::TYPE_FLOAT64:
+        return MIRType_Double;
+    }
+    MOZ_ASSUME_UNREACHABLE("Unknown typed array type");
+}
+
 bool
 IonBuilder::jsop_getelem_typed(MDefinition *obj, MDefinition *index,
                                ScalarTypeRepresentation::Type arrayType)
 {
     types::TemporaryTypeSet *types = bytecodeTypes(pc);
 
     bool maybeUndefined = types->hasType(types::Type::UndefinedType());
 
@@ -7022,38 +7053,17 @@ IonBuilder::jsop_getelem_typed(MDefiniti
     if (!maybeUndefined) {
         // Assume the index is in range, so that we can hoist the length,
         // elements vector and bounds check.
 
         // 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.
-        MIRType knownType;
-        switch (arrayType) {
-          case ScalarTypeRepresentation::TYPE_INT8:
-          case ScalarTypeRepresentation::TYPE_UINT8:
-          case ScalarTypeRepresentation::TYPE_UINT8_CLAMPED:
-          case ScalarTypeRepresentation::TYPE_INT16:
-          case ScalarTypeRepresentation::TYPE_UINT16:
-          case ScalarTypeRepresentation::TYPE_INT32:
-            knownType = MIRType_Int32;
-            break;
-          case ScalarTypeRepresentation::TYPE_UINT32:
-            knownType = allowDouble ? MIRType_Double : MIRType_Int32;
-            break;
-          case ScalarTypeRepresentation::TYPE_FLOAT32:
-            knownType = (LIRGenerator::allowFloat32Optimizations()) ? MIRType_Float32 : MIRType_Double;
-            break;
-          case ScalarTypeRepresentation::TYPE_FLOAT64:
-            knownType = MIRType_Double;
-            break;
-          default:
-            MOZ_ASSUME_UNREACHABLE("Unknown typed array type");
-        }
+        MIRType knownType = MIRTypeForTypedArrayRead(arrayType, allowDouble);
 
         // Get the length.
         MInstruction *length = getTypedArrayLength(obj);
         current->add(length);
 
         // Bounds check.
         index = addBoundsCheck(index, length);
 
@@ -8145,16 +8155,20 @@ IonBuilder::jsop_getprop(PropertyName *n
     if (info().executionMode() == DefinitePropertiesAnalysis) {
         MDefinition *obj = current->pop();
         MCallGetProperty *call = MCallGetProperty::New(obj, name);
         current->add(call);
         current->push(call);
         return resumeAfter(call);
     }
 
+    // Try to emit loads from known binary data blocks
+    if (!getPropTryTypedObject(&emitted, id, types) || emitted)
+        return emitted;
+
     // Try to emit loads from definite slots.
     if (!getPropTryDefiniteSlot(&emitted, name, barrier, types) || emitted)
         return emitted;
 
     // Try to inline a common property getter, or make a call.
     if (!getPropTryCommonGetter(&emitted, id, barrier, types) || emitted)
         return emitted;
 
@@ -8227,16 +8241,135 @@ IonBuilder::getPropTryConstant(bool *emi
     current->add(known);
     current->push(known);
 
     *emitted = true;
     return true;
 }
 
 bool
+IonBuilder::getPropTryTypedObject(bool *emitted,
+                                  jsid id,
+                                  types::TemporaryTypeSet *resultTypes)
+{
+    TypeRepresentationSet fieldTypeReprs;
+    int32_t fieldOffset;
+    size_t fieldIndex;
+    if (!lookupTypedObjectField(current->peek(-1), id, &fieldOffset,
+                                &fieldTypeReprs, &fieldIndex))
+        return false;
+    if (fieldTypeReprs.empty())
+        return true;
+
+    switch (fieldTypeReprs.kind()) {
+      case TypeRepresentation::Struct:
+      case TypeRepresentation::Array:
+        return getPropTryComplexPropOfTypedObject(emitted,
+                                                  fieldOffset,
+                                                  fieldTypeReprs,
+                                                  fieldIndex,
+                                                  resultTypes);
+
+      case TypeRepresentation::Scalar:
+        return getPropTryScalarPropOfTypedObject(emitted,
+                                                 fieldOffset,
+                                                 fieldTypeReprs,
+                                                 resultTypes);
+    }
+
+    MOZ_ASSUME_UNREACHABLE("Bad kind");
+}
+
+bool
+IonBuilder::getPropTryScalarPropOfTypedObject(bool *emitted,
+                                              int32_t fieldOffset,
+                                              TypeRepresentationSet fieldTypeReprs,
+                                              types::TemporaryTypeSet *resultTypes)
+{
+    // Must always be loading the same scalar type
+    if (fieldTypeReprs.length() != 1)
+        return true;
+    ScalarTypeRepresentation *fieldTypeRepr = fieldTypeReprs.get(0)->asScalar();
+
+    // OK!
+    *emitted = true;
+
+    MDefinition *typedObj = current->pop();
+
+    // Find location within the owner object.
+    MDefinition *owner, *ownerOffset;
+    loadTypedObjectData(typedObj, fieldOffset, &owner, &ownerOffset);
+
+    // Load the element data.
+    MTypedObjectElements *elements = MTypedObjectElements::New(owner);
+    current->add(elements);
+
+    // Reading from an Uint32Array will result in a double for values
+    // that don't fit in an int32. We have to bailout if this happens
+    // and the instruction is not known to return a double.
+    bool allowDouble =
+        resultTypes->hasType(types::Type::DoubleType());
+    MIRType knownType =
+        MIRTypeForTypedArrayRead(fieldTypeRepr->type(), allowDouble);
+
+    // Typed array offsets are expressed in units of the alignment,
+    // and the binary data API guarantees all offsets are properly
+    // aligned. So just do the divide.
+    MConstant *alignment =
+        MConstant::New(Int32Value(fieldTypeRepr->alignment()));
+    current->add(alignment);
+    MDiv *scaledOffset =
+        MDiv::NewAsmJS(ownerOffset, alignment, MIRType_Int32);
+    current->add(scaledOffset);
+
+    MLoadTypedArrayElement *load =
+        MLoadTypedArrayElement::New(elements, scaledOffset,
+                                    fieldTypeRepr->type());
+    load->setResultType(knownType);
+    load->setResultTypeSet(resultTypes);
+    current->add(load);
+    current->push(load);
+    return true;
+}
+
+bool
+IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted,
+                                               int32_t fieldOffset,
+                                               TypeRepresentationSet fieldTypeReprs,
+                                               size_t fieldIndex,
+                                               types::TemporaryTypeSet *resultTypes)
+{
+    // Must know the field index so that we can load the new type
+    // object for the derived value
+    if (fieldIndex == SIZE_MAX)
+        return true;
+
+    *emitted = true;
+    MDefinition *typedObj = current->pop();
+
+    // Identify the type object for the field.
+    MDefinition *type = loadTypedObjectType(typedObj);
+    MDefinition *fieldType = typeObjectForFieldFromStructType(type, fieldIndex);
+
+    // Find location within the owner object.
+    MDefinition *owner, *ownerOffset;
+    loadTypedObjectData(typedObj, fieldOffset, &owner, &ownerOffset);
+
+    // Create the derived type object.
+    MInstruction *derived = new MNewDerivedTypedObject(fieldTypeReprs,
+                                                       fieldType,
+                                                       owner,
+                                                       ownerOffset);
+    derived->setResultTypeSet(resultTypes);
+    current->add(derived);
+    current->push(derived);
+    return true;
+}
+
+bool
 IonBuilder::getPropTryDefiniteSlot(bool *emitted, PropertyName *name,
                                    bool barrier, types::TemporaryTypeSet *types)
 {
     JS_ASSERT(*emitted == false);
     types::TypeSet *propTypes = GetDefiniteSlot(cx, current->peek(-1)->resultTypeSet(), name);
     if (!propTypes)
         return true;
 
@@ -8516,16 +8649,20 @@ IonBuilder::jsop_setprop(PropertyName *n
     types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
     bool barrier;
     if (!PropertyWriteNeedsTypeBarrier(cx, current, &obj, name, &value,
                                        /* canModify = */ true, &barrier))
     {
         return false;
     }
 
+    // Try to emit stores to known binary data blocks
+    if (!setPropTryTypedObject(&emitted, obj, id, value) || emitted)
+        return emitted;
+
     // Try to emit store from definite slots.
     if (!setPropTryDefiniteSlot(&emitted, obj, name, value, barrier, objTypes) || emitted)
         return emitted;
 
     // Try to emit a monomorphic/polymorphic store based on baseline caches.
     if (!setPropTryInlineAccess(&emitted, obj, name, id, value, barrier, objTypes) || emitted)
         return emitted;
 
@@ -8649,16 +8786,76 @@ IonBuilder::setPropTryCommonDOMSetter(bo
     if (!resumeAfter(set))
         return false;
 
     *emitted = true;
     return true;
 }
 
 bool
+IonBuilder::setPropTryTypedObject(bool *emitted, MDefinition *obj,
+                                  jsid id, MDefinition *value)
+{
+    TypeRepresentationSet fieldTypeReprs;
+    int32_t fieldOffset;
+    size_t fieldIndex;
+    if (!lookupTypedObjectField(obj, id, &fieldOffset, &fieldTypeReprs,
+                                &fieldIndex))
+        return false;
+    if (fieldTypeReprs.empty())
+        return true;
+
+    switch (fieldTypeReprs.kind()) {
+      case TypeRepresentation::Struct:
+      case TypeRepresentation::Array:
+        // For now, only optimize storing scalars.
+        return true;
+
+      case TypeRepresentation::Scalar:
+        break;
+    }
+
+    // Must always be storing the same scalar type
+    if (fieldTypeReprs.length() != 1)
+        return true;
+    ScalarTypeRepresentation *fieldTypeRepr = fieldTypeReprs.get(0)->asScalar();
+
+    // OK!
+    *emitted = true;
+
+    MTypedObjectElements *elements = MTypedObjectElements::New(obj);
+    current->add(elements);
+
+    // Typed array offsets are expressed in units of the alignment,
+    // and the binary data API guarantees all offsets are properly
+    // aligned.
+    JS_ASSERT(fieldOffset % fieldTypeRepr->alignment() == 0);
+    int32_t scaledFieldOffset = fieldOffset / fieldTypeRepr->alignment();
+
+    MConstant *offset = MConstant::New(Int32Value(scaledFieldOffset));
+    current->add(offset);
+
+    // Clamp value to [0, 255] for Uint8ClampedArray.
+    MDefinition *toWrite = value;
+    if (fieldTypeRepr->type() == ScalarTypeRepresentation::TYPE_UINT8_CLAMPED) {
+        toWrite = MClampToUint8::New(value);
+        current->add(toWrite->toInstruction());
+    }
+
+    MStoreTypedArrayElement *store =
+        MStoreTypedArrayElement::New(elements, offset, toWrite,
+                                     fieldTypeRepr->type());
+    current->add(store);
+
+    current->push(value);
+
+    return true;
+}
+
+bool
 IonBuilder::setPropTryDefiniteSlot(bool *emitted, MDefinition *obj,
                                    PropertyName *name, MDefinition *value,
                                    bool barrier, types::TemporaryTypeSet *objTypes)
 {
     JS_ASSERT(*emitted == false);
 
     if (barrier)
         return true;
@@ -9320,8 +9517,177 @@ types::TemporaryTypeSet *
 IonBuilder::cloneTypeSet(types::StackTypeSet *types)
 {
     // Clone a type set so that it can be stored into the MIR and accessed
     // during off thread compilation. This is necessary because main thread
     // updates to type sets can race with reads in the compiler backend, and
     // after bug 804676 this code can be removed.
     return types->clone(GetIonContext()->temp->lifoAlloc());
 }
+
+TypeRepresentationSetHash *
+IonBuilder::getOrCreateReprSetHash()
+{
+    if (!reprSetHash_) {
+        TypeRepresentationSetHash* hash =
+            cx->new_<TypeRepresentationSetHash>();
+        if (!hash || !hash->init()) {
+            js_delete(hash);
+            return NULL;
+        }
+
+        reprSetHash_ = hash;
+    }
+    return reprSetHash_.get();
+}
+
+bool
+IonBuilder::lookupTypeRepresentationSet(MDefinition *typedObj,
+                                        TypeRepresentationSet *out)
+{
+    *out = TypeRepresentationSet(); // default to unknown
+
+    // Extract TypeRepresentationSet directly if we can
+    if (typedObj->isNewDerivedTypedObject()) {
+        *out = typedObj->toNewDerivedTypedObject()->set();
+        return true;
+    }
+
+    // Extract TypeRepresentationSet directly if we can
+    types::TemporaryTypeSet *types = typedObj->resultTypeSet();
+    if (!types || types->getKnownTypeTag() != JSVAL_TYPE_OBJECT)
+        return true;
+
+    // And only known objects.
+    if (types->unknownObject())
+        return true;
+
+    TypeRepresentationSetBuilder set;
+    for (uint32_t i = 0; i < types->getObjectCount(); i++) {
+        types::TypeObject *type = types->getTypeObject(0);
+        if (!type || type->unknownProperties())
+            return true;
+
+        if (!type->hasTypedObject())
+            return true;
+
+        TypeRepresentation *typeRepr = type->typedObject()->typeRepr;
+        if (!set.insert(typeRepr))
+            return false;
+    }
+
+    return set.build(*this, out);
+}
+
+MDefinition *
+IonBuilder::loadTypedObjectType(MDefinition *typedObj)
+{
+    // Shortcircuit derived type objects, meaning the intermediate
+    // objects created to represent `a.b` in an expression like
+    // `a.b.c`. In that case, the type object can be simply pulled
+    // from the operands of that instruction.
+    if (typedObj->isNewDerivedTypedObject())
+        return typedObj->toNewDerivedTypedObject()->type();
+
+    MInstruction *load = MLoadFixedSlot::New(typedObj, js::SLOT_DATATYPE);
+    current->add(load);
+    return load;
+}
+
+// Given a typed object `typedObj` and an offset `offset` into that
+// 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,
+                                int32_t offset,
+                                MDefinition **owner,
+                                MDefinition **ownerOffset)
+{
+    MConstant *offsetDef = MConstant::New(Int32Value(offset));
+    current->add(offsetDef);
+
+    // 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()) {
+        // If we see that the
+        MNewDerivedTypedObject *ins = typedObj->toNewDerivedTypedObject();
+
+        MAdd *offsetAdd = MAdd::NewAsmJS(ins->offset(), offsetDef,
+                                         MIRType_Int32);
+        current->add(offsetAdd);
+
+        *owner = ins->owner();
+        *ownerOffset = offsetAdd;
+        return;
+    }
+
+    *owner = typedObj;
+    *ownerOffset = offsetDef;
+}
+
+// Looks up the offset/type-repr-set of the field `id`, given the type
+// set `objTypes` of the field owner. Note that even when true is
+// returned, `*fieldTypeReprs` might be empty if no useful type/offset
+// pair could be determined.
+bool
+IonBuilder::lookupTypedObjectField(MDefinition *typedObj,
+                                   jsid id,
+                                   int32_t *fieldOffset,
+                                   TypeRepresentationSet *fieldTypeReprs,
+                                   size_t *fieldIndex)
+{
+    TypeRepresentationSet objTypeReprs;
+    if (!lookupTypeRepresentationSet(typedObj, &objTypeReprs))
+        return false;
+
+    // Must be accessing a struct.
+    if (!objTypeReprs.allOfKind(TypeRepresentation::Struct))
+        return true;
+
+    // Determine the type/offset of the field `id`, if any.
+    size_t offset;
+    if (!objTypeReprs.fieldNamed(*this, id, &offset,
+                                 fieldTypeReprs, fieldIndex))
+        return false;
+    if (fieldTypeReprs->empty())
+        return false;
+
+    // Field offset must be representable as signed integer.
+    if (offset >= size_t(INT_MAX)) {
+        *fieldTypeReprs = TypeRepresentationSet();
+        return true;
+    }
+
+    *fieldOffset = int32_t(offset);
+    JS_ASSERT(*fieldOffset >= 0);
+
+    return true;
+}
+
+MDefinition *
+IonBuilder::typeObjectForFieldFromStructType(MDefinition *typeObj,
+                                             size_t fieldIndex)
+{
+    // Load list of field type objects.
+
+    MInstruction *fieldTypes = MLoadFixedSlot::New(typeObj, SLOT_STRUCT_FIELD_TYPES);
+    current->add(fieldTypes);
+
+    // Index into list with index of field.
+
+    MInstruction *fieldTypesElements = MElements::New(fieldTypes);
+    current->add(fieldTypesElements);
+
+    MConstant *fieldIndexDef = MConstant::New(Int32Value(fieldIndex));
+    current->add(fieldIndexDef);
+
+    MInstruction *fieldType = MLoadElement::New(fieldTypesElements, fieldIndexDef, false, false);
+    current->add(fieldType);
+
+    return fieldType;
+}
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -10,16 +10,17 @@
 #ifdef JS_ION
 
 // This file declares the data structures for building a MIRGraph from a
 // JSScript.
 
 #include "jit/BytecodeAnalysis.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
+#include "jit/TypeRepresentationSet.h"
 
 namespace js {
 namespace jit {
 
 class CodeGenerator;
 class CallInfo;
 class BaselineInspector;
 
@@ -358,16 +359,27 @@ class IonBuilder : public MIRGenerator
     bool getPropTryArgumentsLength(bool *emitted);
     bool getPropTryConstant(bool *emitted, jsid id, types::TemporaryTypeSet *types);
     bool getPropTryDefiniteSlot(bool *emitted, PropertyName *name,
                                 bool barrier, types::TemporaryTypeSet *types);
     bool getPropTryCommonGetter(bool *emitted, jsid id,
                                 bool barrier, types::TemporaryTypeSet *types);
     bool getPropTryInlineAccess(bool *emitted, PropertyName *name, jsid id,
                                 bool barrier, types::TemporaryTypeSet *types);
+    bool getPropTryTypedObject(bool *emitted, jsid id,
+                               types::TemporaryTypeSet *resultTypes);
+    bool getPropTryScalarPropOfTypedObject(bool *emitted,
+                                           int32_t fieldOffset,
+                                           TypeRepresentationSet fieldTypeReprs,
+                                           types::TemporaryTypeSet *resultTypes);
+    bool getPropTryComplexPropOfTypedObject(bool *emitted,
+                                            int32_t fieldOffset,
+                                            TypeRepresentationSet fieldTypeReprs,
+                                            size_t fieldIndex,
+                                            types::TemporaryTypeSet *resultTypes);
     bool getPropTryCache(bool *emitted, PropertyName *name, jsid id,
                          bool barrier, types::TemporaryTypeSet *types);
     bool needsToMonitorMissingProperties(types::TemporaryTypeSet *types);
 
     // jsop_setprop() helpers.
     bool setPropTryCommonSetter(bool *emitted, MDefinition *obj,
                                 PropertyName *name, jsid id,
                                 MDefinition *value);
@@ -376,20 +388,38 @@ class IonBuilder : public MIRGenerator
                                    bool isDOM);
     bool setPropTryDefiniteSlot(bool *emitted, MDefinition *obj,
                                 PropertyName *name, MDefinition *value,
                                 bool barrier, types::TemporaryTypeSet *objTypes);
     bool setPropTryInlineAccess(bool *emitted, MDefinition *obj,
                                 PropertyName *name, jsid id,
                                 MDefinition *value, bool barrier,
                                 types::TemporaryTypeSet *objTypes);
+    bool setPropTryTypedObject(bool *emitted, MDefinition *obj,
+                               jsid id, MDefinition *value);
     bool setPropTryCache(bool *emitted, MDefinition *obj,
                          PropertyName *name, MDefinition *value,
                          bool barrier, types::TemporaryTypeSet *objTypes);
 
+    // binary data lookup helpers.
+    bool lookupTypeRepresentationSet(MDefinition *typedObj,
+                                     TypeRepresentationSet *out);
+    bool lookupTypedObjectField(MDefinition *typedObj,
+                                jsid id,
+                                int32_t *fieldOffset,
+                                TypeRepresentationSet *fieldTypeReprs,
+                                size_t *fieldIndex);
+    MDefinition *loadTypedObjectType(MDefinition *value);
+    void loadTypedObjectData(MDefinition *inOwner,
+                             int32_t inOffset,
+                             MDefinition **outOwner,
+                             MDefinition **outOffset);
+    MDefinition *typeObjectForFieldFromStructType(MDefinition *type,
+                                                  size_t fieldIndex);
+
     // jsop_setelem() helpers.
     bool setElemTryTyped(bool *emitted, MDefinition *object,
                          MDefinition *index, MDefinition *value);
     bool setElemTryTypedStatic(bool *emitted, MDefinition *object,
                                MDefinition *index, MDefinition *value);
     bool setElemTryDense(bool *emitted, MDefinition *object,
                          MDefinition *index, MDefinition *value);
     bool setElemTryArguments(bool *emitted, MDefinition *object,
@@ -636,22 +666,25 @@ class IonBuilder : public MIRGenerator
 
     JSScript *script() const { return script_.get(); }
 
     CodeGenerator *backgroundCodegen() const { return backgroundCodegen_; }
     void setBackgroundCodegen(CodeGenerator *codegen) { backgroundCodegen_ = codegen; }
 
     AbortReason abortReason() { return abortReason_; }
 
+    TypeRepresentationSetHash *getOrCreateReprSetHash(); // fallible
+
   private:
     bool init();
 
     JSContext *cx;
     BaselineFrame *baselineFrame_;
     AbortReason abortReason_;
+    ScopedJSDeletePtr<TypeRepresentationSetHash> reprSetHash_;
 
     // Basic analysis information about the script.
     BytecodeAnalysis analysis_;
     BytecodeAnalysis &analysis() {
         return analysis_;
     }
 
     GSNCache gsn;
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -496,16 +496,42 @@ public:
         return getTemp(0)->output();
     }
 
     const LAllocation *getTemp1() {
         return getTemp(1)->output();
     }
 };
 
+class LNewDerivedTypedObject : public LCallInstructionHelper<1, 3, 0>
+{
+  public:
+    LIR_HEADER(NewDerivedTypedObject);
+
+    LNewDerivedTypedObject(const LAllocation &type,
+                           const LAllocation &owner,
+                           const LAllocation &offset) {
+        setOperand(0, type);
+        setOperand(1, owner);
+        setOperand(2, offset);
+    }
+
+    const LAllocation *type() {
+        return getOperand(0);
+    }
+
+    const LAllocation *owner() {
+        return getOperand(1);
+    }
+
+    const LAllocation *offset() {
+        return getOperand(2);
+    }
+};
+
 class LNewStringObject : public LInstructionHelper<1, 1, 1>
 {
   public:
     LIR_HEADER(NewStringObject)
 
     LNewStringObject(const LAllocation &input, const LDefinition &temp) {
         setOperand(0, input);
         setTemp(0, temp);
@@ -3037,16 +3063,30 @@ class LTypedArrayElements : public LInst
     LTypedArrayElements(const LAllocation &object) {
         setOperand(0, object);
     }
     const LAllocation *object() {
         return getOperand(0);
     }
 };
 
+// Load a typed array's elements vector.
+class LTypedObjectElements : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(TypedObjectElements)
+
+    LTypedObjectElements(const LAllocation &object) {
+        setOperand(0, object);
+    }
+    const LAllocation *object() {
+        return getOperand(0);
+    }
+};
+
 // Bailout if index >= length.
 class LBoundsCheck : public LInstructionHelper<0, 2, 0>
 {
   public:
     LIR_HEADER(BoundsCheck)
 
     LBoundsCheck(const LAllocation &index, const LAllocation &length) {
         setOperand(0, index);
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -27,16 +27,17 @@
     _(NewObject)                    \
     _(NewSlots)                     \
     _(NewDeclEnvObject)             \
     _(NewCallObject)                \
     _(NewStringObject)              \
     _(NewPar)                       \
     _(NewDenseArrayPar)             \
     _(NewCallObjectPar)             \
+    _(NewDerivedTypedObject)        \
     _(AbortPar)                     \
     _(InitElem)                     \
     _(InitElemGetterSetter)         \
     _(InitProp)                     \
     _(InitPropGetterSetter)         \
     _(CheckOverRecursed)            \
     _(CheckOverRecursedPar)         \
     _(DefVar)                       \
@@ -216,16 +217,17 @@
     _(CallIteratorStart)            \
     _(IteratorStart)                \
     _(IteratorNext)                 \
     _(IteratorMore)                 \
     _(IteratorEnd)                  \
     _(ArrayLength)                  \
     _(TypedArrayLength)             \
     _(TypedArrayElements)           \
+    _(TypedObjectElements)          \
     _(StringLength)                 \
     _(ArgumentsLength)              \
     _(GetArgument)                  \
     _(RunOncePrologue)              \
     _(Rest)                         \
     _(RestPar)                      \
     _(TypeOfV)                      \
     _(ToIdV)                        \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -203,16 +203,26 @@ LIRGenerator::visitNewCallObject(MNewCal
 
     if (!assignSafepoint(lir, ins))
         return false;
 
     return true;
 }
 
 bool
+LIRGenerator::visitNewDerivedTypedObject(MNewDerivedTypedObject *ins)
+{
+    LNewDerivedTypedObject *lir =
+        new LNewDerivedTypedObject(useRegisterAtStart(ins->type()),
+                                   useRegisterAtStart(ins->owner()),
+                                   useRegisterAtStart(ins->offset()));
+    return defineReturn(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
 LIRGenerator::visitNewCallObjectPar(MNewCallObjectPar *ins)
 {
     const LAllocation &parThreadContext = useRegister(ins->forkJoinSlice());
     const LDefinition &temp1 = temp();
     const LDefinition &temp2 = temp();
 
     LNewCallObjectPar *lir;
     if (ins->slots()->type() == MIRType_Slots) {
@@ -2064,16 +2074,23 @@ LIRGenerator::visitTypedArrayLength(MTyp
 bool
 LIRGenerator::visitTypedArrayElements(MTypedArrayElements *ins)
 {
     JS_ASSERT(ins->type() == MIRType_Elements);
     return define(new LTypedArrayElements(useRegisterAtStart(ins->object())), ins);
 }
 
 bool
+LIRGenerator::visitTypedObjectElements(MTypedObjectElements *ins)
+{
+    JS_ASSERT(ins->type() == MIRType_Elements);
+    return define(new LTypedObjectElements(useRegisterAtStart(ins->object())), ins);
+}
+
+bool
 LIRGenerator::visitInitializedLength(MInitializedLength *ins)
 {
     JS_ASSERT(ins->elements()->type() == MIRType_Elements);
     return define(new LInitializedLength(useRegisterAtStart(ins->elements())), ins);
 }
 
 bool
 LIRGenerator::visitSetInitializedLength(MSetInitializedLength *ins)
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -84,16 +84,17 @@ class LIRGenerator : public LIRGenerator
     bool visitTableSwitch(MTableSwitch *tableswitch);
     bool visitNewSlots(MNewSlots *ins);
     bool visitNewParallelArray(MNewParallelArray *ins);
     bool visitNewArray(MNewArray *ins);
     bool visitNewObject(MNewObject *ins);
     bool visitNewDeclEnvObject(MNewDeclEnvObject *ins);
     bool visitNewCallObject(MNewCallObject *ins);
     bool visitNewStringObject(MNewStringObject *ins);
+    bool visitNewDerivedTypedObject(MNewDerivedTypedObject *ins);
     bool visitNewPar(MNewPar *ins);
     bool visitNewCallObjectPar(MNewCallObjectPar *ins);
     bool visitNewDenseArrayPar(MNewDenseArrayPar *ins);
     bool visitAbortPar(MAbortPar *ins);
     bool visitInitElem(MInitElem *ins);
     bool visitInitElemGetterSetter(MInitElemGetterSetter *ins);
     bool visitInitProp(MInitProp *ins);
     bool visitInitPropGetterSetter(MInitPropGetterSetter *ins);
@@ -177,16 +178,17 @@ class LIRGenerator : public LIRGenerator
     bool visitCheckInterruptPar(MCheckInterruptPar *ins);
     bool visitStoreSlot(MStoreSlot *ins);
     bool visitTypeBarrier(MTypeBarrier *ins);
     bool visitMonitorTypes(MMonitorTypes *ins);
     bool visitPostWriteBarrier(MPostWriteBarrier *ins);
     bool visitArrayLength(MArrayLength *ins);
     bool visitTypedArrayLength(MTypedArrayLength *ins);
     bool visitTypedArrayElements(MTypedArrayElements *ins);
+    bool visitTypedObjectElements(MTypedObjectElements *ins);
     bool visitInitializedLength(MInitializedLength *ins);
     bool visitSetInitializedLength(MSetInitializedLength *ins);
     bool visitNot(MNot *ins);
     bool visitBoundsCheck(MBoundsCheck *ins);
     bool visitBoundsCheckLower(MBoundsCheckLower *ins);
     bool visitLoadElement(MLoadElement *ins);
     bool visitLoadElementHole(MLoadElementHole *ins);
     bool visitStoreElement(MStoreElement *ins);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -534,16 +534,23 @@ MCompare::printOpcode(FILE *fp) const
 
 void
 MConstantElements::printOpcode(FILE *fp) const
 {
     PrintOpcodeName(fp, op());
     fprintf(fp, " %p", value());
 }
 
+void
+MLoadTypedArrayElement::printOpcode(FILE *fp) const
+{
+    MDefinition::printOpcode(fp);
+    fprintf(fp, " %s", ScalarTypeRepresentation::typeName(arrayType()));
+}
+
 MParameter *
 MParameter::New(int32_t index, types::TemporaryTypeSet *types)
 {
     return new MParameter(index, types);
 }
 
 void
 MParameter::printOpcode(FILE *fp) const
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -19,16 +19,17 @@
 #include "jit/Bailouts.h"
 #include "jit/CompilerRoot.h"
 #include "jit/FixedList.h"
 #include "jit/InlineList.h"
 #include "jit/IonAllocPolicy.h"
 #include "jit/IonMacroAssembler.h"
 #include "jit/MOpcodes.h"
 #include "jit/TypePolicy.h"
+#include "jit/TypeRepresentationSet.h"
 #include "vm/ScopeObject.h"
 
 namespace js {
 
 class StringObject;
 
 namespace jit {
 
@@ -1482,16 +1483,76 @@ class MNewPar : public MUnaryInstruction
         return getOperand(0);
     }
 
     JSObject *templateObject() const {
         return templateObject_;
     }
 };
 
+// Creates a new derived type object. At runtime, this is just a call
+// to `BinaryBlock::createDerived()`. That is, the MIR itself does not
+// compile to particularly optimized code. However, using a distinct
+// MIR for creating derived type objects allows the compiler to
+// optimize ephemeral typed objects as would be created for a
+// reference like `a.b.c` -- here, the `a.b` will create an ephemeral
+// derived type object that aliases the memory of `a` itself. The
+// specific nature of `a.b` is revealed by using
+// `MNewDerivedTypedObject` rather than `MGetProperty` or what have
+// you. Moreover, the compiler knows that there are no side-effects,
+// so `MNewDerivedTypedObject` instructions can be reordered or pruned
+// as dead code.
+class MNewDerivedTypedObject
+  : public MTernaryInstruction,
+    public Mix3Policy<ObjectPolicy<0>,
+                      ObjectPolicy<1>,
+                      IntPolicy<2> >
+{
+  private:
+    TypeRepresentationSet set_;
+
+  public:
+    INSTRUCTION_HEADER(NewDerivedTypedObject);
+
+    MNewDerivedTypedObject(TypeRepresentationSet set,
+                           MDefinition *type,
+                           MDefinition *owner,
+                           MDefinition *offset)
+      : MTernaryInstruction(type, owner, offset),
+        set_(set)
+    {
+        setMovable();
+        setResultType(MIRType_Object);
+    }
+
+    TypeRepresentationSet set() const {
+        return set_;
+    }
+
+    MDefinition *type() const {
+        return getOperand(0);
+    }
+
+    MDefinition *owner() const {
+        return getOperand(1);
+    }
+
+    MDefinition *offset() const {
+        return getOperand(2);
+    }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+
+    virtual AliasSet getAliasSet() const {
+        return AliasSet::None();
+    }
+};
+
 // Abort parallel execution.
 class MAbortPar : public MAryControlInstruction<0, 0>
 {
   public:
     INSTRUCTION_HEADER(AbortPar);
 
     MAbortPar()
       : MAryControlInstruction<0, 0>()
@@ -4877,16 +4938,52 @@ class MTypedArrayElements
     bool congruentTo(MDefinition *ins) const {
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::ObjectFields);
     }
 };
 
+// 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
+{
+  private:
+    MTypedObjectElements(MDefinition *object)
+      : MUnaryInstruction(object)
+    {
+        setResultType(MIRType_Elements);
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(TypedObjectElements)
+
+    static MTypedObjectElements *New(MDefinition *object) {
+        return new MTypedObjectElements(object);
+    }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+    MDefinition *object() const {
+        return getOperand(0);
+    }
+    bool congruentTo(MDefinition *ins) const {
+        return congruentIfOperandsEqual(ins);
+    }
+    AliasSet getAliasSet() const {
+        return AliasSet::Load(AliasSet::ObjectFields);
+    }
+};
+
 // Perform !-operation
 class MNot
   : public MUnaryInstruction,
     public TestPolicy
 {
     bool operandMightEmulateUndefined_;
 
   public:
@@ -5418,16 +5515,18 @@ class MLoadTypedArrayElement
     }
     MDefinition *index() const {
         return getOperand(1);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::TypedArrayElement);
     }
 
+    void printOpcode(FILE *fp) const;
+
     void computeRange();
 
     bool canProduceFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
 };
 
 // Load a value from a typed array. Out-of-bounds accesses are handled using
 // a VM call.
 class MLoadTypedArrayElementHole
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -118,16 +118,17 @@ namespace jit {
     _(SetElementCache)                                                      \
     _(BindNameCache)                                                        \
     _(GuardShape)                                                           \
     _(GuardObjectType)                                                      \
     _(GuardClass)                                                           \
     _(ArrayLength)                                                          \
     _(TypedArrayLength)                                                     \
     _(TypedArrayElements)                                                   \
+    _(TypedObjectElements)                                                  \
     _(InitializedLength)                                                    \
     _(SetInitializedLength)                                                 \
     _(Not)                                                                  \
     _(BoundsCheck)                                                          \
     _(BoundsCheckLower)                                                     \
     _(InArray)                                                              \
     _(LoadElement)                                                          \
     _(LoadElementHole)                                                      \
@@ -192,16 +193,17 @@ namespace jit {
     _(AsmJSVoidReturn)                                                      \
     _(AsmJSPassStackArg)                                                    \
     _(AsmJSCall)                                                            \
     _(AsmJSCheckOverRecursed)                                               \
     _(CheckOverRecursedPar)                                                 \
     _(NewCallObjectPar)                                                     \
     _(NewPar)                                                               \
     _(NewDenseArrayPar)                                                     \
+    _(NewDerivedTypedObject)                                                \
     _(AbortPar)                                                             \
     _(LambdaPar)                                                            \
     _(RestPar)                                                              \
     _(ForkJoinSlice)                                                        \
     _(GuardThreadLocalObject)                                               \
     _(CheckInterruptPar)
 
 // Forward declarations of MIR types.
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -170,16 +170,17 @@ class ParallelSafetyVisitor : public MIn
     SAFE_OP(TruncateToInt32)
     SAFE_OP(MaybeToDoubleElement)
     CUSTOM_OP(ToString)
     SAFE_OP(NewSlots)
     CUSTOM_OP(NewArray)
     CUSTOM_OP(NewObject)
     CUSTOM_OP(NewCallObject)
     CUSTOM_OP(NewParallelArray)
+    UNSAFE_OP(NewDerivedTypedObject)
     UNSAFE_OP(InitElem)
     UNSAFE_OP(InitElemGetterSetter)
     UNSAFE_OP(InitProp)
     UNSAFE_OP(InitPropGetterSetter)
     SAFE_OP(Start)
     UNSAFE_OP(OsrEntry)
     SAFE_OP(Nop)
     UNSAFE_OP(RegExp)
@@ -202,16 +203,17 @@ class ParallelSafetyVisitor : public MIn
     UNSAFE_OP(BindNameCache)
     SAFE_OP(GuardShape)
     SAFE_OP(GuardObjectType)
     SAFE_OP(GuardClass)
     SAFE_OP(AssertRange)
     SAFE_OP(ArrayLength)
     SAFE_OP(TypedArrayLength)
     SAFE_OP(TypedArrayElements)
+    SAFE_OP(TypedObjectElements)
     SAFE_OP(InitializedLength)
     WRITE_GUARDED_OP(SetInitializedLength, elements)
     SAFE_OP(Not)
     SAFE_OP(BoundsCheck)
     SAFE_OP(BoundsCheckLower)
     SAFE_OP(LoadElement)
     SAFE_OP(LoadElementHole)
     MAYBE_WRITE_GUARDED_OP(StoreElement, elements)
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -411,16 +411,17 @@ IntPolicy<Op>::staticAdjustInputs(MInstr
     MUnbox *replace = MUnbox::New(in, MIRType_Int32, MUnbox::Fallible);
     def->block()->insertBefore(def, replace);
     def->replaceOperand(Op, replace);
     return true;
 }
 
 template bool IntPolicy<0>::staticAdjustInputs(MInstruction *def);
 template bool IntPolicy<1>::staticAdjustInputs(MInstruction *def);
+template bool IntPolicy<2>::staticAdjustInputs(MInstruction *def);
 
 template <unsigned Op>
 bool
 DoublePolicy<Op>::staticAdjustInputs(MInstruction *def)
 {
     MDefinition *in = def->getOperand(Op);
     if (in->type() == MIRType_Double)
         return true;
new file mode 100644
--- /dev/null
+++ b/js/src/jit/TypeRepresentationSet.cpp
@@ -0,0 +1,278 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jit/TypeRepresentationSet.h"
+
+#include "mozilla/HashFunctions.h"
+
+#include "jit/IonBuilder.h"
+
+#include "jsinferinlines.h"
+
+using namespace js;
+using namespace jit;
+
+///////////////////////////////////////////////////////////////////////////
+// TypeRepresentationSet hasher
+
+HashNumber
+TypeRepresentationSetHasher::hash(TypeRepresentationSet key)
+{
+    HashNumber hn = mozilla::HashGeneric(key.length());
+    for (size_t i = 0; i < key.length(); i++)
+        hn = mozilla::AddToHash(hn, uintptr_t(key.get(i)));
+    return hn;
+}
+
+bool
+TypeRepresentationSetHasher::match(TypeRepresentationSet key1,
+                                   TypeRepresentationSet key2)
+{
+    if (key1.length() != key2.length())
+        return false;
+
+    // Note: entries are always sorted
+    for (size_t i = 0; i < key1.length(); i++) {
+        if (key1.get(i) != key2.get(i))
+            return false;
+    }
+
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// TypeRepresentationSetBuilder
+
+TypeRepresentationSetBuilder::TypeRepresentationSetBuilder()
+  : invalid_(false)
+{}
+
+bool
+TypeRepresentationSetBuilder::insert(TypeRepresentation *typeRepr)
+{
+    if (invalid_)
+        return true;
+
+    if (entries_.length() == 0)
+        return entries_.append(typeRepr);
+
+    // Check that this new type repr is of the same basic kind as the
+    // ones we have seen thus far. If not, for example if we have an
+    // `int` and a `struct`, then convert this set to the invalid set.
+    TypeRepresentation *entry0 = entries_[0];
+    if (typeRepr->kind() != entry0->kind()) {
+        invalid_ = true;
+        entries_.clear();
+        return true;
+    }
+
+    // Otherwise, use binary search to find the right place to insert
+    // the type descriptor. We keep list sorted by the *address* of
+    // the type representations within.
+    uintptr_t typeReprAddr = (uintptr_t) typeRepr;
+    size_t min = 0;
+    size_t max = entries_.length();
+    while (min != max) {
+        size_t i = min + ((max - min) >> 1); // average w/o fear of overflow
+
+        uintptr_t entryiaddr = (uintptr_t) entries_[i];
+        if (entryiaddr == typeReprAddr)
+            return true; // typeRepr already present in the set
+
+        if (entryiaddr < typeReprAddr) {
+            // typeRepr lies to the right of entry i
+            min = i;
+        } else {
+            // typeRepr lies to the left of entry i
+            max = i;
+        }
+    }
+
+    // As a sanity check, give up if the TypeRepresentationSet grows too large.
+    if (entries_.length() >= 512) {
+        invalid_ = true;
+        entries_.clear();
+        return true;
+    }
+
+    // Not present. Insert at position `min`.
+    if (min == entries_.length())
+        return entries_.append(typeRepr);
+    TypeRepresentation **insertLoc = &entries_[min];
+    return entries_.insert(insertLoc, typeRepr) != NULL;
+}
+
+bool
+TypeRepresentationSetBuilder::build(IonBuilder &builder,
+                                    TypeRepresentationSet *out)
+{
+    if (invalid_) {
+        *out = TypeRepresentationSet();
+        return true;
+    }
+
+    TypeRepresentationSetHash *table = builder.getOrCreateReprSetHash();
+    if (!table)
+        return false;
+
+    // Check if there is already a copy in the hashtable.
+    size_t length = entries_.length();
+    TypeRepresentationSet tempSet(length, entries_.begin());
+    TypeRepresentationSetHash::AddPtr p = table->lookupForAdd(tempSet);
+    if (p) {
+        *out = *p;
+        return true;
+    }
+
+    // If not, allocate a permanent copy in Ion temp memory and add it.
+    size_t space = sizeof(TypeRepresentation*) * length;
+    TypeRepresentation **array = (TypeRepresentation**)
+        GetIonContext()->temp->allocate(space);
+    if (!array)
+        return false;
+    memcpy(array, entries_.begin(), space);
+    TypeRepresentationSet permSet(length, array);
+    if (!table->add(p, permSet))
+        return false;
+
+    *out = permSet;
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// TypeRepresentationSet
+
+TypeRepresentationSet::TypeRepresentationSet(const TypeRepresentationSet &c)
+  : length_(c.length_),
+    entries_(c.entries_)
+{}
+
+TypeRepresentationSet::TypeRepresentationSet(size_t length,
+                                             TypeRepresentation **entries)
+  : length_(length),
+    entries_(entries)
+{}
+
+TypeRepresentationSet::TypeRepresentationSet()
+  : length_(0),
+    entries_(NULL)
+{}
+
+bool
+TypeRepresentationSet::empty()
+{
+    return length() == 0;
+}
+
+size_t
+TypeRepresentationSet::length()
+{
+    return length_;
+}
+
+TypeRepresentation *
+TypeRepresentationSet::get(size_t i)
+{
+    JS_ASSERT(i < length());
+    return entries_[i];
+}
+
+bool
+TypeRepresentationSet::allOfKind(TypeRepresentation::Kind aKind)
+{
+    if (empty())
+        return false;
+
+    return kind() == aKind;
+}
+
+TypeRepresentation::Kind
+TypeRepresentationSet::kind()
+{
+    JS_ASSERT(!empty());
+    return get(0)->kind();
+}
+
+size_t
+TypeRepresentationSet::arrayLength()
+{
+    JS_ASSERT(kind() == TypeRepresentation::Array);
+    const size_t result = get(0)->asArray()->length();
+    for (size_t i = 1; i < length(); i++) {
+        if (get(i)->asArray()->length() != result)
+            return SIZE_MAX;
+    }
+    return result;
+}
+
+bool
+TypeRepresentationSet::arrayElementType(IonBuilder &builder,
+                                        TypeRepresentationSet *out)
+{
+    JS_ASSERT(kind() == TypeRepresentation::Array);
+
+    TypeRepresentationSetBuilder elementTypes;
+    for (size_t i = 0; i < length(); i++) {
+        if (!elementTypes.insert(get(i)->asArray()->element()))
+            return false;
+    }
+    return elementTypes.build(builder, out);
+}
+
+bool
+TypeRepresentationSet::fieldNamed(IonBuilder &builder,
+                                  jsid id,
+                                  size_t *offset,
+                                  TypeRepresentationSet *out,
+                                  size_t *index)
+{
+    JS_ASSERT(kind() == TypeRepresentation::Struct);
+
+    // Initialize `*offset` and `*out` for the case where incompatible
+    // or absent fields are found.
+    *offset = SIZE_MAX;
+    *index = SIZE_MAX;
+    *out = TypeRepresentationSet();
+
+    // Remember offset of the first field.
+    size_t offset0;
+    size_t index0;
+    TypeRepresentationSetBuilder fieldTypes;
+    {
+        const StructField *field = get(0)->asStruct()->fieldNamed(id);
+        if (!field)
+            return true;
+
+        offset0 = field->offset;
+        index0 = field->index;
+        if (!fieldTypes.insert(field->typeRepr))
+            return false;
+    }
+
+    // Check that all subsequent fields are at the same offset
+    // and compute the union of their types.
+    for (size_t i = 1; i < length(); i++) {
+        const StructField *field = get(i)->asStruct()->fieldNamed(id);
+        if (!field)
+            return true;
+
+        if (field->offset != offset0)
+            return true;
+
+        if (field->index != index0)
+            index0 = SIZE_MAX;
+
+        if (!fieldTypes.insert(field->typeRepr))
+            return false;
+    }
+
+    // All struct types had a field named `id` at the same offset
+    // (though it's still possible that the types are incompatible and
+    // that the indices disagree).
+    *offset = offset0;
+    *index = index0;
+    return fieldTypes.build(builder, out);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit/TypeRepresentationSet.h
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jit_TypeRepresentationSet_h
+#define jit_TypeRepresentationSet_h
+
+#include "builtin/TypeRepresentation.h"
+#include "jit/IonAllocPolicy.h"
+#include "js/HashTable.h"
+
+// TypeRepresentationSet stores a set of TypeRepresentation* objects,
+// representing the possible types of the binary data associated with
+// a typed object value.  Often TypeRepresentationSets will be
+// singleton sets, but it is also possible to have cases where many
+// type representations flow into a single point. In such cases, the
+// various type representations may differ in their details but often
+// have a common prefix. We try to optimize this case as well.
+//
+// So, for example, consider some code like:
+//
+//     var Point2Type = new StructType({x: uint8, y: uint8});
+//     var Point3Type = new StructType({x: uint8, y: uint8, z: uint8});
+//
+//     function distance2d(pnt) {
+//         return Math.sqrt(pnt.x * pnt.x + pnt.y * pnt.y);
+//     }
+//
+// Even if the function `distance2d()` were used with instances of
+// both Point2Type and Point3Type, we can still generate optimal code,
+// because both of those types contain fields named `x` and `y` with
+// the same types at the same offset.
+
+namespace js {
+namespace jit {
+
+class IonBuilder;
+class TypeRepresentationSet;
+
+class TypeRepresentationSetBuilder {
+  private:
+    Vector<TypeRepresentation *, 4, SystemAllocPolicy> entries_;
+    bool invalid_;
+
+    bool overlaps(TypeRepresentation *a, TypeRepresentation *b);
+
+  public:
+    TypeRepresentationSetBuilder();
+
+    bool insert(TypeRepresentation *typeRepr);
+    bool build(IonBuilder &builder, TypeRepresentationSet *out);
+};
+
+class TypeRepresentationSet {
+  private:
+    friend class TypeRepresentationSetBuilder;
+
+    size_t length_;
+    TypeRepresentation **entries_; // Allocated using temp policy
+
+    TypeRepresentationSet(size_t length, TypeRepresentation **entries);
+
+  public:
+    //////////////////////////////////////////////////////////////////////
+    // Constructors
+    //
+    // For more flexible constructors, see
+    // TypeRepresentationSetBuilder above.
+
+    TypeRepresentationSet(const TypeRepresentationSet &c);
+    TypeRepresentationSet(); // empty set
+
+    //////////////////////////////////////////////////////////////////////
+    // Query the set
+
+    bool empty();
+    size_t length();
+    TypeRepresentation *get(size_t i);
+    bool allOfKind(TypeRepresentation::Kind kind);
+
+    //////////////////////////////////////////////////////////////////////
+    // The following operations are only valid on a non-empty set:
+
+    TypeRepresentation::Kind kind();
+
+    //////////////////////////////////////////////////////////////////////
+    // Array operations
+    //
+    // Only valid when `kind() == TypeRepresentation::Array`
+
+    // Returns the length of the arrays in this set, or SIZE_MAX
+    // if they are not all the same.
+    size_t arrayLength();
+
+    // Returns a `TypeRepresentationSet` representing the element
+    // types of the various array types in this set. The returned set
+    // may be the empty set.
+    bool arrayElementType(IonBuilder &builder, TypeRepresentationSet *out);
+
+    //////////////////////////////////////////////////////////////////////
+    // Struct operations
+    //
+    // Only valid when `kind() == TypeRepresentation::Struct`
+
+    // Searches the type in the set for a field named `id`. All
+    // possible types must agree on the offset of the field within the
+    // structure and the possible types of the field must be
+    // compatible. If any pair of types disagree on the offset or have
+    // incompatible types for the field, then `*out` will be set to
+    // the empty set.
+    //
+    // Upon success, `out` will be set to the set of possible types of
+    // the field and `offset` will be set to the field's offset within
+    // the struct (measured in bytes).
+    //
+    // The parameter `*index` is special. If all types agree on the
+    // index of the field, then `*index` is set to the field index.
+    // Otherwise, it is set to SIZE_MAX. Note that two types may agree
+    // on the type and offset of a field but disagree about its index,
+    // e.g. the field `c` in `new StructType({a: uint8, b: uint8, c:
+    // uint16})` and `new StructType({a: uint16, c: uint16})`.
+    bool fieldNamed(IonBuilder &builder,
+                    jsid id,
+                    size_t *offset,
+                    TypeRepresentationSet *out,
+                    size_t *index);
+};
+
+struct TypeRepresentationSetHasher
+{
+    typedef TypeRepresentationSet Lookup;
+    static HashNumber hash(TypeRepresentationSet key);
+    static bool match(TypeRepresentationSet key1,
+                      TypeRepresentationSet key2);
+};
+
+typedef js::HashSet<TypeRepresentationSet,
+                    TypeRepresentationSetHasher,
+                    IonAllocPolicy> TypeRepresentationSetHash;
+
+} // namespace jit
+} // namespace js
+
+#endif
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/VMFunctions.h"
 
 #include "builtin/ParallelArray.h"
+#include "builtin/TypedObject.h"
 #include "frontend/BytecodeCompiler.h"
 #include "jit/BaselineIC.h"
 #include "jit/Ion.h"
 #include "jit/IonCompartment.h"
 #include "jit/IonFrames.h"
 #include "vm/ArrayObject.h"
 #include "vm/Debugger.h"
 #include "vm/Interpreter.h"
@@ -867,10 +868,17 @@ LeaveBlock(JSContext *cx, BaselineFrame 
 }
 
 bool
 InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame, uint32_t numStackValues)
 {
     return frame->initForOsr(interpFrame, numStackValues);
 }
 
+JSObject *CreateDerivedTypedObj(JSContext *cx, HandleObject type,
+                                HandleObject owner, int32_t offset)
+{
+    return BinaryBlock::createDerived(cx, type, owner, offset);
+}
+
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -660,12 +660,15 @@ bool HandleDebugTrap(JSContext *cx, Base
 bool OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustReturn);
 
 bool EnterBlock(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block);
 bool LeaveBlock(JSContext *cx, BaselineFrame *frame);
 
 bool InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame,
                              uint32_t numStackValues);
 
+JSObject *CreateDerivedTypedObj(JSContext *cx, HandleObject type,
+                                HandleObject owner, int32_t offset);
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_VMFunctions_h */
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -199,17 +199,17 @@ struct JSCompartment
 
   public:
     /* Last time at which an animation was played for a global in this compartment. */
     int64_t                      lastAnimationTime;
 
     js::RegExpCompartment        regExps;
 
     /* Set of all currently living type representations. */
-    js::TypeRepresentationSet    typeReprs;
+    js::TypeRepresentationHash   typeReprs;
 
   private:
     void sizeOfTypeInferenceData(JS::TypeInferenceSizes *stats, mozilla::MallocSizeOf mallocSizeOf);
 
   public:
     void sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *compartmentObject,
                              JS::TypeInferenceSizes *tiSizes,
                              size_t *shapesCompartmentTables, size_t *crossCompartmentWrappers,
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -243,16 +243,17 @@ if CONFIG['ENABLE_ION']:
         'ParallelSafetyAnalysis.cpp',
         'ParallelFunctions.cpp',
         'RangeAnalysis.cpp',
         'RegisterAllocator.cpp',
         'Safepoints.cpp',
         'Snapshots.cpp',
         'StupidAllocator.cpp',
         'TypePolicy.cpp',
+        'TypeRepresentationSet.cpp',
         'UnreachableCodeElimination.cpp',
         'VMFunctions.cpp',
         'ValueNumbering.cpp',
     ]
     if CONFIG['TARGET_CPU'].find('86') != -1:
         CPP_SOURCES += [
             'Assembler-x86-shared.cpp',
             'BaselineCompiler-x86-shared.cpp',