Bug 899139 - Part 2: SetElementIC typed array stubs. (r=jandem)
authorShu-yu Guo <shu@rfrn.org>
Mon, 09 Sep 2013 18:55:52 -0700
changeset 159213 12657c2feb9c73ab8a0a8e3e75014c301b9ca4e2
parent 159212 e9370865aae020dd07f00216f17787d7b6288d85
child 159214 8c7d2ec92d8b0a691e92f8caac72d74d6b8c28b8
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs899139
milestone26.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 899139 - Part 2: SetElementIC typed array stubs. (r=jandem)
js/src/jit/CodeGenerator.cpp
js/src/jit/IonCaches.cpp
js/src/jit/IonCaches.h
js/src/jit/LIR-Common.h
js/src/jit/Lowering.cpp
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -6106,39 +6106,41 @@ CodeGenerator::visitGetElementIC(OutOfLi
 }
 
 bool
 CodeGenerator::visitSetElementCacheV(LSetElementCacheV *ins)
 {
     Register obj = ToRegister(ins->object());
     Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex());
     Register temp = ToRegister(ins->temp());
+    FloatRegister tempFloat = ToFloatRegister(ins->tempFloat());
     ValueOperand index = ToValue(ins, LSetElementCacheV::Index);
     ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetElementCacheV::Value));
 
-    SetElementIC cache(obj, unboxIndex, temp, index, value, ins->mir()->strict());
+    SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, ins->mir()->strict());
 
     return addCache(ins, allocateCache(cache));
 }
 
 bool
 CodeGenerator::visitSetElementCacheT(LSetElementCacheT *ins)
 {
     Register obj = ToRegister(ins->object());
     Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex());
     Register temp = ToRegister(ins->temp());
+    FloatRegister tempFloat = ToFloatRegister(ins->tempFloat());
     ValueOperand index = ToValue(ins, LSetElementCacheT::Index);
     ConstantOrRegister value;
     const LAllocation *tmp = ins->value();
     if (tmp->isConstant())
         value = *tmp->toConstant();
     else
         value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(tmp));
 
-    SetElementIC cache(obj, unboxIndex, temp, index, value, ins->mir()->strict());
+    SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, ins->mir()->strict());
 
     return addCache(ins, allocateCache(cache));
 }
 
 typedef bool (*SetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, HandleValue);
 const VMFunction SetElementIC::UpdateInfo =
     FunctionInfo<SetElementICFn>(SetElementIC::update);
 
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -2938,19 +2938,19 @@ GetElementIC::canAttachTypedArrayElement
     // way to accept float typed arrays for now is to return a Value type.
     int arrayType = obj->as<TypedArrayObject>().type();
     bool floatOutput = arrayType == ScalarTypeRepresentation::TYPE_FLOAT32 ||
                        arrayType == ScalarTypeRepresentation::TYPE_FLOAT64;
     return !floatOutput || output.hasValue();
 }
 
 static void
-GenerateTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
-                          TypedArrayObject *tarr, const Value &idval, Register object,
-                          ConstantOrRegister index, TypedOrValueRegister output)
+GenerateGetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
+                             TypedArrayObject *tarr, const Value &idval, Register object,
+                             ConstantOrRegister index, TypedOrValueRegister output)
 {
     JS_ASSERT(GetElementIC::canAttachTypedArrayElement(tarr, idval, output));
 
     Label failures;
 
     // The array type is the object within the table of typed array classes.
     int arrayType = tarr->type();
 
@@ -3046,17 +3046,17 @@ GenerateTypedArrayElement(JSContext *cx,
 }
 
 bool
 GetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr,
                                       const Value &idval)
 {
     MacroAssembler masm(cx);
     RepatchStubAppender attacher(*this);
-    GenerateTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output());
+    GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output());
     return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
 }
 
 bool
 GetElementIC::attachArgumentsElement(JSContext *cx, IonScript *ion, JSObject *obj)
 {
     JS_ASSERT(obj->is<ArgumentsObject>());
 
@@ -3360,26 +3360,135 @@ SetElementIC::attachDenseElement(JSConte
     masm.bind(&outOfBounds);
     masm.bind(&failures);
     attacher.jumpNextStub(masm);
 
     setHasDenseStub();
     return linkAndAttachStub(cx, masm, attacher, ion, "dense array");
 }
 
+static bool
+GenerateSetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
+                             TypedArrayObject *tarr, const Value &idval, Register object,
+                             ValueOperand indexVal, ConstantOrRegister value,
+                             Register tempUnbox, Register temp, FloatRegister tempFloat)
+{
+    JS_ASSERT(SetElementIC::canAttachTypedArrayElement(tarr, idval));
+
+    Label failures, done, popObjectAndFail;
+
+    // Guard on the shape.
+    Shape *shape = tarr->lastProperty();
+    if (!shape)
+        return false;
+    masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
+
+    // Ensure the index is an int32.
+    masm.branchTestInt32(Assembler::NotEqual, indexVal, &failures);
+    Register index = masm.extractInt32(indexVal, tempUnbox);
+
+    // Guard on the length.
+    Address length(object, TypedArrayObject::lengthOffset());
+    masm.unboxInt32(length, temp);
+    masm.branch32(Assembler::BelowOrEqual, temp, index, &done);
+
+    // Load the elements vector.
+    Register elements = temp;
+    masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elements);
+
+    // Set the value.
+    int arrayType = tarr->type();
+    int width = TypedArrayObject::slotWidth(arrayType);
+    BaseIndex target(elements, index, ScaleFromElemWidth(width));
+
+    if (arrayType == ScalarTypeRepresentation::TYPE_FLOAT32 ||
+        arrayType == ScalarTypeRepresentation::TYPE_FLOAT64)
+    {
+        if (!masm.convertConstantOrRegisterToDouble(cx, value, tempFloat, &failures))
+            return false;
+        masm.storeToTypedFloatArray(arrayType, tempFloat, target);
+    } else {
+        // On x86 we only have 6 registers available to use, so reuse the object
+        // register to compute the intermediate value to store and restore it
+        // afterwards.
+        masm.push(object);
+
+        if (arrayType == ScalarTypeRepresentation::TYPE_UINT8_CLAMPED) {
+            if (!masm.clampConstantOrRegisterToUint8(cx, value, tempFloat, object,
+                                                     &popObjectAndFail))
+            {
+                return false;
+            }
+        } else {
+            if (!masm.truncateConstantOrRegisterToInt32(cx, value, tempFloat, object,
+                                                        &popObjectAndFail))
+            {
+                return false;
+            }
+        }
+        masm.storeToTypedIntArray(arrayType, object, target);
+
+        masm.pop(object);
+    }
+
+    // Out-of-bound writes jump here as they are no-ops.
+    masm.bind(&done);
+    attacher.jumpRejoin(masm);
+
+    if (popObjectAndFail.used()) {
+        masm.bind(&popObjectAndFail);
+        masm.pop(object);
+    }
+
+    masm.bind(&failures);
+    attacher.jumpNextStub(masm);
+    return true;
+}
+
+/* static */ bool
+SetElementIC::canAttachTypedArrayElement(JSObject *obj, const Value &idval)
+{
+    return obj->is<TypedArrayObject>() && idval.isInt32();
+}
+
+bool
+SetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion,
+                                      TypedArrayObject *tarr, const Value &idval)
+{
+    MacroAssembler masm(cx);
+    RepatchStubAppender attacher(*this);
+    if (!GenerateSetTypedArrayElement(cx, masm, attacher, tarr, idval,
+                                      object(), index(), value(),
+                                      tempToUnboxIndex(), temp(), tempFloat()))
+    {
+        return false;
+    }
+
+    return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
+}
+
 bool
 SetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
                      HandleValue idval, HandleValue value)
 {
     IonScript *ion = GetTopIonJSScript(cx)->ionScript();
     SetElementIC &cache = ion->getCache(cacheIndex).toSetElement();
 
-    if (cache.canAttachStub() && !cache.hasDenseStub() && IsElementSetInlineable(obj, idval)) {
-        if (!cache.attachDenseElement(cx, ion, obj, idval))
-            return false;
+    bool attachedStub = false;
+    if (cache.canAttachStub()) {
+        if (!cache.hasDenseStub() && IsElementSetInlineable(obj, idval)) {
+            if (!cache.attachDenseElement(cx, ion, obj, idval))
+                return false;
+            attachedStub = true;
+        }
+        if (!attachedStub && canAttachTypedArrayElement(obj, idval)) {
+            TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
+            if (!cache.attachTypedArrayElement(cx, ion, tarr, idval))
+                return false;
+        }
     }
 
     if (!SetObjectElement(cx, obj, idval, value, cache.strict()))
         return false;
     return true;
 }
 
 void
@@ -3421,17 +3530,17 @@ GetElementParIC::attachDenseElement(Lock
 }
 
 bool
 GetElementParIC::attachTypedArrayElement(LockedJSContext &cx, IonScript *ion,
                                          TypedArrayObject *tarr, const Value &idval)
 {
     MacroAssembler masm(cx);
     DispatchStubPrepender attacher(*this);
-    GenerateTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output());
+    GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output());
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array");
 }
 
 ParallelResult
 GetElementParIC::update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
                         HandleValue idval, MutableHandleValue vp)
 {
     AutoFlushCache afc("GetElementParCache", slice->runtime()->ionRuntime());
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -788,29 +788,31 @@ class GetElementIC : public RepatchIonCa
 };
 
 class SetElementIC : public RepatchIonCache
 {
   protected:
     Register object_;
     Register tempToUnboxIndex_;
     Register temp_;
+    FloatRegister tempFloat_;
     ValueOperand index_;
     ConstantOrRegister value_;
     bool strict_;
 
     bool hasDenseStub_ : 1;
 
   public:
     SetElementIC(Register object, Register tempToUnboxIndex, Register temp,
-                 ValueOperand index, ConstantOrRegister value,
+                 FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
                  bool strict)
       : object_(object),
         tempToUnboxIndex_(tempToUnboxIndex),
         temp_(temp),
+        tempFloat_(tempFloat),
         index_(index),
         value_(value),
         strict_(strict),
         hasDenseStub_(false)
     {
     }
 
     CACHE_HEADER(SetElement)
@@ -821,16 +823,19 @@ class SetElementIC : public RepatchIonCa
         return object_;
     }
     Register tempToUnboxIndex() const {
         return tempToUnboxIndex_;
     }
     Register temp() const {
         return temp_;
     }
+    FloatRegister tempFloat() const {
+        return tempFloat_;
+    }
     ValueOperand index() const {
         return index_;
     }
     ConstantOrRegister value() const {
         return value_;
     }
     bool strict() const {
         return strict_;
@@ -839,21 +844,25 @@ class SetElementIC : public RepatchIonCa
     bool hasDenseStub() const {
         return hasDenseStub_;
     }
     void setHasDenseStub() {
         JS_ASSERT(!hasDenseStub());
         hasDenseStub_ = true;
     }
 
+    static bool canAttachTypedArrayElement(JSObject *obj, const Value &idval);
+
     bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval);
+    bool attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr,
+                                 const Value &idval);
 
     static bool
     update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval,
-                HandleValue value);
+           HandleValue value);
 };
 
 class BindNameIC : public RepatchIonCache
 {
   protected:
     Register scopeChain_;
     PropertyName *name_;
     Register output_;
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -4254,59 +4254,65 @@ class LSetPropertyCacheT : public LInstr
     const MSetPropertyCache *mir() const {
         return mir_->toSetPropertyCache();
     }
     MIRType valueType() {
         return valueType_;
     }
 };
 
-class LSetElementCacheV : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 2>
+class LSetElementCacheV : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 3>
 {
   public:
     LIR_HEADER(SetElementCacheV);
 
     static const size_t Index = 1;
     static const size_t Value = 1 + BOX_PIECES;
 
     LSetElementCacheV(const LAllocation &object, const LDefinition &tempToUnboxIndex,
-                      const LDefinition &temp)
+                      const LDefinition &temp, const LDefinition &tempFloat)
     {
         setOperand(0, object);
         setTemp(0, tempToUnboxIndex);
         setTemp(1, temp);
+        setTemp(2, tempFloat);
     }
     const MSetElementCache *mir() const {
         return mir_->toSetElementCache();
     }
 
     const LAllocation *object() {
         return getOperand(0);
     }
     const LDefinition *tempToUnboxIndex() {
         return getTemp(0);
     }
     const LDefinition *temp() {
         return getTemp(1);
     }
-};
-
-class LSetElementCacheT : public LInstructionHelper<0, 2 + BOX_PIECES, 2>
+    const LDefinition *tempFloat() {
+        return getTemp(2);
+    }
+};
+
+class LSetElementCacheT : public LInstructionHelper<0, 2 + BOX_PIECES, 3>
 {
   public:
     LIR_HEADER(SetElementCacheT);
 
     static const size_t Index = 2;
 
     LSetElementCacheT(const LAllocation &object, const LAllocation &value,
-                      const LDefinition &tempToUnboxIndex, const LDefinition &temp) {
+                      const LDefinition &tempToUnboxIndex,
+                      const LDefinition &temp, const LDefinition &tempFloat) {
         setOperand(0, object);
         setOperand(1, value);
         setTemp(0, tempToUnboxIndex);
         setTemp(1, temp);
+        setTemp(2, tempFloat);
     }
     const MSetElementCache *mir() const {
         return mir_->toSetElementCache();
     }
 
     const LAllocation *object() {
         return getOperand(0);
     }
@@ -4314,16 +4320,19 @@ class LSetElementCacheT : public LInstru
         return getOperand(1);
     }
     const LDefinition *tempToUnboxIndex() {
         return getTemp(0);
     }
     const LDefinition *temp() {
         return getTemp(1);
     }
+    const LDefinition *tempFloat() {
+        return getTemp(2);
+    }
 };
 
 class LCallIteratorStart : public LCallInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(CallIteratorStart)
 
     LCallIteratorStart(const LAllocation &object) {
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2685,27 +2685,28 @@ LIRGenerator::visitSetPropertyCache(MSet
 bool
 LIRGenerator::visitSetElementCache(MSetElementCache *ins)
 {
     JS_ASSERT(ins->object()->type() == MIRType_Object);
     JS_ASSERT(ins->index()->type() == MIRType_Value);
 
     LInstruction *lir;
     if (ins->value()->type() == MIRType_Value) {
-        lir = new LSetElementCacheV(useRegister(ins->object()), tempToUnbox(), temp());
+        lir = new LSetElementCacheV(useRegister(ins->object()), tempToUnbox(),
+                                    temp(), tempFloat());
 
         if (!useBox(lir, LSetElementCacheV::Index, ins->index()))
             return false;
         if (!useBox(lir, LSetElementCacheV::Value, ins->value()))
             return false;
     } else {
         lir = new LSetElementCacheT(
             useRegister(ins->object()),
             useRegisterOrConstant(ins->value()),
-            tempToUnbox(), temp());
+            tempToUnbox(), temp(), tempFloat());
 
         if (!useBox(lir, LSetElementCacheT::Index, ins->index()))
             return false;
     }
 
     return add(lir, ins) && assignSafepoint(lir, ins);
 }