Bug 854400 - Rebase Shu's patch, and combine inlined code with jsop_setelem_dense r=jandem
authorNicholas D. Matsakis <nmatsakis@mozilla.com>
Tue, 07 May 2013 17:37:42 -0400
changeset 138159 89426b7c3b3bb26453f57be0f052ed0dd5b428d6
parent 138158 fee19aa62f5db0621b605fd83041926bbec13b6a
child 138160 c460bded7a668a2b6ff4af1dea51203e61b8c5a5
push id3752
push userlsblakk@mozilla.com
push dateMon, 13 May 2013 17:21:10 +0000
treeherdermozilla-aurora@1580544aef0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs854400
milestone23.0a1
Bug 854400 - Rebase Shu's patch, and combine inlined code with jsop_setelem_dense r=jandem
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/MCallOptimize.cpp
js/src/jit-test/tests/ion/doubleArrays.js
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -6513,17 +6513,18 @@ IonBuilder::jsop_setelem()
         return jsop_setelem_typed(arrayType, SetElem_Normal,
                                   object, index, value);
 
     if (!PropertyWriteNeedsTypeBarrier(cx, current, &object, NULL, &value)) {
         if (ElementAccessIsDenseNative(object, index)) {
             types::StackTypeSet::DoubleConversion conversion =
                 object->resultTypeSet()->convertDoubleElements(cx);
             if (conversion != types::StackTypeSet::AmbiguousDoubleConversion)
-                return jsop_setelem_dense(conversion, object, index, value);
+                return jsop_setelem_dense(conversion, SetElem_Normal,
+                                          object, index, value);
         }
     }
 
     if (object->type() == MIRType_Magic)
         return jsop_arguments_setelem(object, index, value);
 
     if (script()->argumentsHasVarBinding() && object->mightBeType(MIRType_Magic))
         return abort("Type is not definitely lazy arguments.");
@@ -6532,16 +6533,17 @@ IonBuilder::jsop_setelem()
     current->add(ins);
     current->push(value);
 
     return resumeAfter(ins);
 }
 
 bool
 IonBuilder::jsop_setelem_dense(types::StackTypeSet::DoubleConversion conversion,
+                               SetElemSafety safety,
                                MDefinition *obj, MDefinition *id, MDefinition *value)
 {
     MIRType elementType = DenseNativeElementType(cx, obj);
     bool packed = ElementAccessIsPacked(cx, obj);
 
     // Writes which are on holes in the object do not have to bail out if they
     // cannot hit another indexed property on the object or its prototypes.
     bool writeOutOfBounds = !ElementAccessHasExtraIndexedProperty(cx, obj);
@@ -6560,46 +6562,62 @@ IonBuilder::jsop_setelem_dense(types::St
         current->add(valueDouble);
         newValue = valueDouble;
     }
 
     // Get the elements vector.
     MElements *elements = MElements::New(obj);
     current->add(elements);
 
-    bool writeHole = script()->analysis()->getCode(pc).arrayWriteHole;
-    SetElemICInspector icInspect(inspector->setElemICInspector(pc));
-    writeHole |= icInspect.sawOOBDenseWrite();
+    bool writeHole;
+    if (safety == SetElem_Normal) {
+        writeHole = script()->analysis()->getCode(pc).arrayWriteHole;
+        SetElemICInspector icInspect(inspector->setElemICInspector(pc));
+        writeHole |= icInspect.sawOOBDenseWrite();
+    } else {
+        writeHole = false;
+    }
 
     // Use MStoreElementHole if this SETELEM has written to out-of-bounds
     // indexes in the past. Otherwise, use MStoreElement so that we can hoist
     // the initialized length and bounds check.
     MStoreElementCommon *store;
     if (writeHole && writeOutOfBounds) {
+        JS_ASSERT(safety == SetElem_Normal);
+
         MStoreElementHole *ins = MStoreElementHole::New(obj, elements, id, newValue);
         store = ins;
 
         current->add(ins);
         current->push(value);
 
         if (!resumeAfter(ins))
             return false;
     } else {
         MInitializedLength *initLength = MInitializedLength::New(elements);
         current->add(initLength);
 
-        id = addBoundsCheck(id, initLength);
-
-        bool needsHoleCheck = !packed && !writeOutOfBounds;
+        bool needsHoleCheck;
+        if (safety == SetElem_Normal) {
+            id = addBoundsCheck(id, initLength);
+            needsHoleCheck = !packed && !writeOutOfBounds;
+        } else {
+            needsHoleCheck = false;
+        }
 
         MStoreElement *ins = MStoreElement::New(elements, id, newValue, needsHoleCheck);
         store = ins;
 
+        if (safety == SetElem_Unsafe)
+            ins->setRacy();
+
         current->add(ins);
-        current->push(value);
+
+        if (safety == SetElem_Normal)
+            current->push(value);
 
         if (!resumeAfter(ins))
             return false;
     }
 
     // Determine whether a write barrier is required.
     if (obj->resultTypeSet()->propertyNeedsBarrier(cx, JSID_VOID))
         store->setNeedsBarrier();
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -26,16 +26,26 @@ class IonBuilder : public MIRGenerator
         ControlStatus_Error,
         ControlStatus_Abort,
         ControlStatus_Ended,        // There is no continuation/join point.
         ControlStatus_Joined,       // Created a join node.
         ControlStatus_Jumped,       // Parsing another branch at the same level.
         ControlStatus_None          // No control flow.
     };
 
+    enum SetElemSafety {
+        // Normal write like a[b] = c.
+        SetElem_Normal,
+
+        // Write due to UnsafeSetElement:
+        // - assumed to be in bounds,
+        // - not checked for data races
+        SetElem_Unsafe,
+    };
+
     struct DeferredEdge : public TempObject
     {
         MBasicBlock *block;
         DeferredEdge *next;
 
         DeferredEdge(MBasicBlock *block, DeferredEdge *next)
           : block(block), next(next)
         { }
@@ -378,16 +388,17 @@ class IonBuilder : public MIRGenerator
     bool jsop_bindname(PropertyName *name);
     bool jsop_getelem();
     bool jsop_getelem_dense();
     bool jsop_getelem_typed(int arrayType);
     bool jsop_getelem_typed_static(bool *psucceeded);
     bool jsop_getelem_string();
     bool jsop_setelem();
     bool jsop_setelem_dense(types::StackTypeSet::DoubleConversion conversion,
+                            SetElemSafety safety,
                             MDefinition *object, MDefinition *index, MDefinition *value);
     bool jsop_setelem_typed(int arrayType,
                             SetElemSafety safety,
                             MDefinition *object, MDefinition *index, MDefinition *value);
     bool jsop_setelem_typed_static(MDefinition *object, MDefinition *index, MDefinition *value,
                                    bool *psucceeded);
     bool jsop_length();
     bool jsop_length_fastPath();
--- a/js/src/ion/MCallOptimize.cpp
+++ b/js/src/ion/MCallOptimize.cpp
@@ -937,22 +937,16 @@ IonBuilder::inlineUnsafeSetElement(CallI
             return InliningStatus_NotInlined;
 
         int arrayType;
         if (!ElementAccessIsDenseNative(obj, id) &&
             !ElementAccessIsTypedArray(obj, id, &arrayType))
         {
             return InliningStatus_NotInlined;
         }
-
-        if (obj->resultTypeSet()->convertDoubleElements(cx) !=
-            types::StackTypeSet::DontConvertToDoubles)
-        {
-            return InliningStatus_NotInlined;
-        }
     }
 
     callInfo.unwrapArgs();
 
     // Push the result first so that the stack depth matches up for
     // the potential bailouts that will occur in the stores below.
     MConstant *udef = MConstant::New(UndefinedValue());
     current->add(udef);
@@ -980,52 +974,34 @@ IonBuilder::inlineUnsafeSetElement(CallI
 
         JS_NOT_REACHED("Element access not dense array nor typed array");
     }
 
     return InliningStatus_Inlined;
 }
 
 bool
-IonBuilder::inlineUnsafeSetDenseArrayElement(CallInfo &callInfo, uint32_t base)
+IonBuilder::inlineUnsafeSetDenseArrayElement(
+    CallInfo &callInfo, uint32_t base)
 {
     // Note: we do not check the conditions that are asserted as true
     // in intrinsic_UnsafeSetElement():
     // - arr is a dense array
     // - idx < initialized length
     // Furthermore, note that inlineUnsafeSetElement ensures the type of the
     // value is reflected in the JSID_VOID property of the array.
 
-    uint32_t arri = base + 0;
-    uint32_t idxi = base + 1;
-    uint32_t elemi = base + 2;
-
-    MElements *elements = MElements::New(callInfo.getArg(arri));
-    current->add(elements);
-
-    MToInt32 *id = MToInt32::New(callInfo.getArg(idxi));
-    current->add(id);
-
-    // We disable the hole check for this store.  This implies that if
-    // there were setters on the prototype, they would not be invoked.
-    // But this is actually the desired behavior.
+    MDefinition *obj = callInfo.getArg(base + 0);
+    MDefinition *id = callInfo.getArg(base + 1);
+    MDefinition *elem = callInfo.getArg(base + 2);
 
-    MStoreElement *store = MStoreElement::New(elements, id,
-                                              callInfo.getArg(elemi),
-                                              /* needsHoleCheck = */ false);
-    store->setRacy();
-
-    if (callInfo.getArg(arri)->resultTypeSet()->propertyNeedsBarrier(cx, JSID_VOID))
-        store->setNeedsBarrier();
-
-    current->add(store);
-
-    if (!resumeAfter(store))
+    types::StackTypeSet::DoubleConversion conversion =
+        obj->resultTypeSet()->convertDoubleElements(cx);
+    if (!jsop_setelem_dense(conversion, SetElem_Unsafe, obj, id, elem))
         return false;
-
     return true;
 }
 
 bool
 IonBuilder::inlineUnsafeSetTypedArrayElement(CallInfo &callInfo,
                                              uint32_t base,
                                              int arrayType)
 {
--- a/js/src/jit-test/tests/ion/doubleArrays.js
+++ b/js/src/jit-test/tests/ion/doubleArrays.js
@@ -7,16 +7,29 @@ function testPushConvert() {
     x.push(i);
   var res = 0;
   for (var i = 0; i < x.length; i++)
     res += x[i];
   assertEq(res, 60);
 }
 testPushConvert();
 
+var UnsafeSetElement = getSelfHostedValue("UnsafeSetElement");
+function testUnsafeSetConvert() {
+  var x = [0.5, 1.5, 2.1];
+  for (var i = 0; i < 2000; i++)
+    // try to ensure we JIT and hence inline
+    UnsafeSetElement(x, 1, i);
+  var res = 0;
+  for (var i = 0; i < x.length; i++)
+    res += x[i];
+  assertEq(res, 2001.6);
+}
+testUnsafeSetConvert();
+
 function testArrayInitializer() {
   var x = [.5,1.5,2.5,3];
   var res = 0;
   for (var i = 0; i < x.length; i++)
     res += x[i];
   assertEq(res, 7.5);
 }
 for (var i = 0; i < 5; i++)