Bug 933269: jit support for getElem in TypedObjects (r=nmatsakis).
authorFelix S. Klock II <pnkfelix>
Tue, 05 Nov 2013 09:53:00 +0100
changeset 154153 669ccf5fb31e368c13b77fbfb04fd5e753ac97da
parent 154152 780b232ec465a4a44a49077d6247f1b2ee39a9bb
child 154154 3ccaefa093326d7e90889c710d50437ec8abc7ad
push id35998
push userfklock@mozilla.com
push dateFri, 08 Nov 2013 17:02:05 +0000
treeherdermozilla-inbound@3ccaefa09332 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnmatsakis
bugs933269
milestone28.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 933269: jit support for getElem in TypedObjects (r=nmatsakis).
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/TypeRepresentationSet.cpp
js/src/jit/TypeRepresentationSet.h
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6428,23 +6428,26 @@ GetElemKnownType(bool needsHoleCheck, ty
 bool
 IonBuilder::jsop_getelem()
 {
     MDefinition *index = current->pop();
     MDefinition *obj = current->pop();
 
     bool emitted = false;
 
+    if (!getElemTryTypedObject(&emitted, obj, index) || emitted)
+        return emitted;
+
     if (!getElemTryDense(&emitted, obj, index) || emitted)
         return emitted;
 
     if (!getElemTryTypedStatic(&emitted, obj, index) || emitted)
         return emitted;
 
-    if (!getElemTryTyped(&emitted, obj, index) || emitted)
+    if (!getElemTryTypedArray(&emitted, obj, index) || emitted)
         return emitted;
 
     if (!getElemTryString(&emitted, obj, index) || emitted)
         return emitted;
 
     if (!getElemTryArguments(&emitted, obj, index) || emitted)
         return emitted;
 
@@ -6466,16 +6469,223 @@ IonBuilder::jsop_getelem()
     if (!resumeAfter(ins))
         return false;
 
     types::TemporaryTypeSet *types = bytecodeTypes(pc);
     return pushTypeBarrier(ins, types, true);
 }
 
 bool
+IonBuilder::getElemTryTypedObject(bool *emitted, MDefinition *obj, MDefinition *index)
+{
+    JS_ASSERT(*emitted == false);
+
+    TypeRepresentationSet objTypeReprs;
+    if (!lookupTypeRepresentationSet(obj, &objTypeReprs))
+        return false;
+
+    if (!objTypeReprs.allOfArrayKind())
+        return true;
+
+    TypeRepresentationSet elemTypeReprs;
+    if (!objTypeReprs.arrayElementType(*this, &elemTypeReprs))
+        return false;
+
+    size_t elemSize;
+    if (!elemTypeReprs.allHaveSameSize(&elemSize))
+        return true;
+
+    switch (elemTypeReprs.kind()) {
+    case TypeRepresentation::Struct:
+    case TypeRepresentation::Array:
+        return getElemTryComplexElemOfTypedObject(emitted,
+                                                  obj,
+                                                  index,
+                                                  objTypeReprs,
+                                                  elemTypeReprs,
+                                                  elemSize);
+    case TypeRepresentation::Scalar:
+        return getElemTryScalarElemOfTypedObject(emitted,
+                                                 obj,
+                                                 index,
+                                                 objTypeReprs,
+                                                 elemTypeReprs,
+                                                 elemSize);
+    }
+
+    MOZ_ASSUME_UNREACHABLE("Bad kind");
+}
+
+static MIRType
+MIRTypeForTypedArrayRead(ScalarTypeRepresentation::Type arrayType,
+                         bool observedDouble);
+
+bool
+IonBuilder::getElemTryScalarElemOfTypedObject(bool *emitted,
+                                              MDefinition *obj,
+                                              MDefinition *index,
+                                              TypeRepresentationSet objTypeReprs,
+                                              TypeRepresentationSet elemTypeReprs,
+                                              size_t elemSize)
+{
+    JS_ASSERT(objTypeReprs.allOfArrayKind());
+
+    // Must always be loading the same scalar type
+    if (elemTypeReprs.length() != 1)
+        return true;
+    ScalarTypeRepresentation *elemTypeRepr = elemTypeReprs.get(0)->asScalar();
+
+    // Get the length.
+    size_t lenOfAll = objTypeReprs.arrayLength();
+    if (lenOfAll >= size_t(INT_MAX)) // int32 max is bound
+        return true;
+    MInstruction *length = MConstant::New(Int32Value(int32_t(lenOfAll)));
+
+    *emitted = true;
+    current->add(length);
+
+    // Ensure index is an integer.
+    MInstruction *idInt32 = MToInt32::New(index);
+    current->add(idInt32);
+    index = idInt32;
+
+    // Typed-object accesses usually in bounds (bail out otherwise).
+    index = addBoundsCheck(index, length);
+
+    // Find location within the owner object.
+    MDefinition *owner;
+    MDefinition *indexFromOwner;
+    if (obj->isNewDerivedTypedObject()) {
+        MNewDerivedTypedObject *ins = obj->toNewDerivedTypedObject();
+        MDefinition *ownerOffset = ins->offset();
+
+        // Typed array offsets are expressed in units of the (array)
+        // element alignment.  The binary data uses byte units for
+        // offsets (such as the owner offset here).
+
+        MConstant *alignment = MConstant::New(Int32Value(elemTypeRepr->alignment()));
+        current->add(alignment);
+
+        MDiv *scaledOffset = MDiv::NewAsmJS(ownerOffset, alignment, MIRType_Int32);
+        current->add(scaledOffset);
+
+        MAdd *scaledOffsetPlusIndex = MAdd::NewAsmJS(scaledOffset, index,
+                                                     MIRType_Int32);
+        current->add(scaledOffsetPlusIndex);
+
+        owner = ins->owner();
+        indexFromOwner = scaledOffsetPlusIndex;
+    } else {
+        owner = obj;
+        indexFromOwner = index;
+    }
+
+    // Load the element data.
+    MTypedObjectElements *elements = MTypedObjectElements::New(owner);
+    current->add(elements);
+
+    // Load the element.
+    MLoadTypedArrayElement *load = MLoadTypedArrayElement::New(elements, indexFromOwner, elemTypeRepr->type());
+    current->add(load);
+    current->push(load);
+
+    // If we are reading in-bounds elements, we can use knowledge about
+    // the array type to determine the result type, even if the opcode has
+    // never executed. The known pushed type is only used to distinguish
+    // uint32 reads that may produce either doubles or integers.
+    types::TemporaryTypeSet *resultTypes = bytecodeTypes(pc);
+    bool allowDouble = resultTypes->hasType(types::Type::DoubleType());
+    MIRType knownType = MIRTypeForTypedArrayRead(elemTypeRepr->type(), allowDouble);
+    // Note: we can ignore the type barrier here, we know the type must
+    // be valid and unbarriered.
+    load->setResultType(knownType);
+    load->setResultTypeSet(resultTypes);
+
+    return true;
+}
+
+bool
+IonBuilder::getElemTryComplexElemOfTypedObject(bool *emitted,
+                                               MDefinition *obj,
+                                               MDefinition *index,
+                                               TypeRepresentationSet objTypeReprs,
+                                               TypeRepresentationSet elemTypeReprs,
+                                               size_t elemSize)
+{
+    JS_ASSERT(objTypeReprs.allOfArrayKind());
+
+    MDefinition *type = loadTypedObjectType(obj);
+    MInstruction *elemType = MLoadFixedSlot::New(type, JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE);
+    current->add(elemType);
+
+    // Get the length.
+    size_t lenOfAll = objTypeReprs.arrayLength();
+    if (lenOfAll >= size_t(INT_MAX)) // int32 max is bound
+        return true;
+    MInstruction *length = MConstant::New(Int32Value(int32_t(lenOfAll)));
+
+    *emitted = true;
+    current->add(length);
+
+    // Ensure index is an integer.
+    MInstruction *idInt32 = MToInt32::New(index);
+    current->add(idInt32);
+    index = idInt32;
+
+    // Typed-object accesses usually in bounds (bail out otherwise).
+    index = addBoundsCheck(index, length);
+
+    // Convert array index to element data offset.
+    MConstant *alignment = MConstant::New(Int32Value(elemSize));
+    current->add(alignment);
+
+    // Since we passed the bounds check, it is impossible for the
+    // result of multiplication to overflow; so enable imul path.
+    MMul *indexAsByteOffset = MMul::New(index, alignment, MIRType_Int32,
+                                        MMul::Integer);
+    current->add(indexAsByteOffset);
+
+    // Find location within the owner object.
+    MDefinition *owner;
+    MDefinition *indexAsByteOffsetFromOwner;
+    if (obj->isNewDerivedTypedObject()) {
+        MNewDerivedTypedObject *ins = obj->toNewDerivedTypedObject();
+        MDefinition *ownerOffset = ins->offset();
+
+        MAdd *offsetPlusScaledIndex = MAdd::NewAsmJS(ownerOffset,
+                                                     indexAsByteOffset,
+                                                     MIRType_Int32);
+        current->add(offsetPlusScaledIndex);
+
+        owner = ins->owner();
+        indexAsByteOffsetFromOwner = offsetPlusScaledIndex;
+    } else {
+        owner = obj;
+        indexAsByteOffsetFromOwner = indexAsByteOffset;
+    }
+
+    // Load the element data.
+    MTypedObjectElements *elements = MTypedObjectElements::New(owner);
+    current->add(elements);
+
+    // Create the derived type object.
+    MInstruction *derived = new MNewDerivedTypedObject(elemTypeReprs,
+                                                       elemType,
+                                                       owner,
+                                                       indexAsByteOffsetFromOwner);
+
+    types::TemporaryTypeSet *resultTypes = bytecodeTypes(pc);
+    derived->setResultTypeSet(resultTypes);
+    current->add(derived);
+    current->push(derived);
+
+    return true;
+}
+
+bool
 IonBuilder::getElemTryDense(bool *emitted, MDefinition *obj, MDefinition *index)
 {
     JS_ASSERT(*emitted == false);
 
     if (!ElementAccessIsDenseNative(obj, index))
         return true;
 
     // Don't generate a fast path if there have been bounds check failures
@@ -6553,17 +6763,17 @@ IonBuilder::getElemTryTypedStatic(bool *
             load->setInfallible();
     }
 
     *emitted = true;
     return true;
 }
 
 bool
-IonBuilder::getElemTryTyped(bool *emitted, MDefinition *obj, MDefinition *index)
+IonBuilder::getElemTryTypedArray(bool *emitted, MDefinition *obj, MDefinition *index)
 {
     JS_ASSERT(*emitted == false);
 
     ScalarTypeRepresentation::Type arrayType;
     if (!ElementAccessIsTypedArray(obj, index, &arrayType))
         return true;
 
     // Emit typed getelem variant.
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -430,21 +430,34 @@ class IonBuilder : public MIRGenerator
     bool setElemTryArguments(bool *emitted, MDefinition *object,
                              MDefinition *index, MDefinition *value);
     bool setElemTryCache(bool *emitted, MDefinition *object,
                          MDefinition *index, MDefinition *value);
 
     // jsop_getelem() helpers.
     bool getElemTryDense(bool *emitted, MDefinition *obj, MDefinition *index);
     bool getElemTryTypedStatic(bool *emitted, MDefinition *obj, MDefinition *index);
-    bool getElemTryTyped(bool *emitted, MDefinition *obj, MDefinition *index);
+    bool getElemTryTypedArray(bool *emitted, MDefinition *obj, MDefinition *index);
+    bool getElemTryTypedObject(bool *emitted, MDefinition *obj, MDefinition *index);
     bool getElemTryString(bool *emitted, MDefinition *obj, MDefinition *index);
     bool getElemTryArguments(bool *emitted, MDefinition *obj, MDefinition *index);
     bool getElemTryArgumentsInlined(bool *emitted, MDefinition *obj, MDefinition *index);
     bool getElemTryCache(bool *emitted, MDefinition *obj, MDefinition *index);
+    bool getElemTryScalarElemOfTypedObject(bool *emitted,
+                                           MDefinition *obj,
+                                           MDefinition *index,
+                                           TypeRepresentationSet objTypeReprs,
+                                           TypeRepresentationSet elemTypeReprs,
+                                           size_t elemSize);
+    bool getElemTryComplexElemOfTypedObject(bool *emitted,
+                                            MDefinition *obj,
+                                            MDefinition *index,
+                                            TypeRepresentationSet objTypeReprs,
+                                            TypeRepresentationSet elemTypeReprs,
+                                            size_t elemSize);
 
     // Typed array helpers.
     MInstruction *getTypedArrayLength(MDefinition *obj);
     MInstruction *getTypedArrayElements(MDefinition *obj);
 
     bool jsop_add(MDefinition *left, MDefinition *right);
     bool jsop_bitnot();
     bool jsop_bitop(JSOp op);
--- a/js/src/jit/TypeRepresentationSet.cpp
+++ b/js/src/jit/TypeRepresentationSet.cpp
@@ -174,24 +174,49 @@ TypeRepresentationSet::length()
 TypeRepresentation *
 TypeRepresentationSet::get(size_t i)
 {
     JS_ASSERT(i < length());
     return entries_[i];
 }
 
 bool
+TypeRepresentationSet::allOfArrayKind()
+{
+    if (empty())
+        return false;
+
+    return kind() == TypeRepresentation::Array;
+}
+
+bool
 TypeRepresentationSet::allOfKind(TypeRepresentation::Kind aKind)
 {
     if (empty())
         return false;
 
     return kind() == aKind;
 }
 
+bool
+TypeRepresentationSet::allHaveSameSize(size_t *out)
+{
+    if (empty())
+        return false;
+
+    size_t size = get(0)->size();
+    for (size_t i = 1; i < length(); i++) {
+        if (get(i)->size() != size)
+            return false;
+    }
+
+    *out = size;
+    return true;
+}
+
 TypeRepresentation::Kind
 TypeRepresentationSet::kind()
 {
     JS_ASSERT(!empty());
     return get(0)->kind();
 }
 
 size_t
--- a/js/src/jit/TypeRepresentationSet.h
+++ b/js/src/jit/TypeRepresentationSet.h
@@ -75,16 +75,30 @@ class TypeRepresentationSet {
     //////////////////////////////////////////////////////////////////////
     // Query the set
 
     bool empty();
     size_t length();
     TypeRepresentation *get(size_t i);
     bool allOfKind(TypeRepresentation::Kind kind);
 
+    // Returns true only when non-empty and `kind()` is
+    // `TypeRepresentation::Array`
+    bool allOfArrayKind();
+
+    // Returns true only if (1) non-empty, (2) for all types t in this
+    // set, t is sized, and (3) there is some size S such that for all
+    // types t in this set, `t.size() == S`.  When the above holds,
+    // then also sets `*out` to S; otherwise leaves `*out` unchanged
+    // and returns false.
+    //
+    // At the moment condition (2) trivially holds.  When Bug 922115
+    // lands, some array types will be unsized.
+    bool allHaveSameSize(size_t *out);
+
     //////////////////////////////////////////////////////////////////////
     // The following operations are only valid on a non-empty set:
 
     TypeRepresentation::Kind kind();
 
     //////////////////////////////////////////////////////////////////////
     // Array operations
     //