Bug 1364346 part 1 - Optimize Array.prototype.unshift fast path and use it more. r=anba
authorJan de Mooij <jdemooij@mozilla.com>
Sat, 27 May 2017 23:39:55 +0200
changeset 409132 a5bee800882e91f9609c934707bd48187819a987
parent 409131 3bfd7a30226705e59fad14d1708a973d72e2a668
child 409133 3f347989ea45c9ab7e1a3498b6bebb268f34362e
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersanba
bugs1364346
milestone55.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 1364346 part 1 - Optimize Array.prototype.unshift fast path and use it more. r=anba
js/src/jsarray.cpp
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2480,51 +2480,51 @@ js::array_unshift(JSContext* cx, unsigne
 
     // Step 2.
     uint64_t length;
     if (!GetLengthProperty(cx, obj, &length))
         return false;
 
     // Steps 3-4.
     if (args.length() > 0) {
-        /* Slide up the array to make room for all args at the bottom. */
-        if (length > 0) {
-            // Only include a fast path for boxed arrays. Unboxed arrays can't
-            // be optimized here because unshifting temporarily places holes at
-            // the start of the array.
-            // TODO: Implement unboxed array optimization similar to the one in
-            // array_splice_impl(), unshift() is a special version of splice():
-            // arr.unshift(...values) ~= arr.splice(0, 0, ...values).
-            bool optimized = false;
-            do {
-                if (length > UINT32_MAX)
-                    break;
-                if (!obj->is<ArrayObject>())
-                    break;
-                if (ObjectMayHaveExtraIndexedProperties(obj))
-                    break;
-                if (MaybeInIteration(obj, cx))
-                    break;
-                ArrayObject* aobj = &obj->as<ArrayObject>();
-                if (!aobj->lengthIsWritable())
-                    break;
-                DenseElementResult result = aobj->ensureDenseElements(cx, uint32_t(length), args.length());
-                if (result != DenseElementResult::Success) {
-                    if (result == DenseElementResult::Failure)
-                        return false;
-                    MOZ_ASSERT(result == DenseElementResult::Incomplete);
-                    break;
-                }
-                aobj->moveDenseElements(args.length(), 0, uint32_t(length));
-                for (uint32_t i = 0; i < args.length(); i++)
-                    aobj->setDenseElement(i, MagicValue(JS_ELEMENTS_HOLE));
-                optimized = true;
-            } while (false);
-
-            if (!optimized) {
+        // Only include a fast path for native objects. Unboxed arrays can't
+        // be optimized here because unshifting temporarily places holes at
+        // the start of the array.
+        // TODO: Implement unboxed array optimization similar to the one in
+        // array_splice_impl(), unshift() is a special version of splice():
+        // arr.unshift(...values) ~= arr.splice(0, 0, ...values).
+        bool optimized = false;
+        do {
+            if (length > UINT32_MAX)
+                break;
+            if (!obj->isNative())
+                break;
+            if (ObjectMayHaveExtraIndexedProperties(obj))
+                break;
+            if (MaybeInIteration(obj, cx))
+                break;
+            NativeObject* nobj = &obj->as<NativeObject>();
+            if (nobj->is<ArrayObject>() && !nobj->as<ArrayObject>().lengthIsWritable())
+                break;
+            DenseElementResult result = nobj->ensureDenseElements(cx, uint32_t(length), args.length());
+            if (result != DenseElementResult::Success) {
+                if (result == DenseElementResult::Failure)
+                    return false;
+                MOZ_ASSERT(result == DenseElementResult::Incomplete);
+                break;
+            }
+            if (length > 0)
+                nobj->moveDenseElements(args.length(), 0, uint32_t(length));
+            for (uint32_t i = 0; i < args.length(); i++)
+                nobj->setDenseElementWithType(cx, i, args[i]);
+            optimized = true;
+        } while (false);
+
+        if (!optimized) {
+            if (length > 0) {
                 uint64_t last = length;
                 uint64_t upperIndex = last + args.length();
 
                 // Step 4.a.
                 if (upperIndex >= uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT)) {
                     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TOO_LONG_ARRAY);
                     return false;
                 }
@@ -2542,22 +2542,22 @@ js::array_unshift(JSContext* cx, unsigne
                         if (!DeletePropertyOrThrow(cx, obj, upperIndex))
                             return false;
                     } else {
                         if (!SetArrayElement(cx, obj, upperIndex, value))
                             return false;
                     }
                 } while (last != 0);
             }
+
+            // Steps 4.d-f.
+            /* Copy from args to the bottom of the array. */
+            if (!SetArrayElements(cx, obj, 0, args.length(), args.array()))
+                return false;
         }
-
-        // Steps 4.d-f.
-        /* Copy from args to the bottom of the array. */
-        if (!SetArrayElements(cx, obj, 0, args.length(), args.array()))
-            return false;
     }
 
     // Step 5.
     uint64_t newlength = length + args.length();
     if (!SetLengthProperty(cx, obj, newlength))
         return false;
 
     // Step 6.