Bug 1422726 - Optimize addEnumerableDataProperty by adding a fast path for the most common case. r=bhackett
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 04 Dec 2017 14:56:06 +0100
changeset 394652 10aa5c0593fa4f639cf78ece78faf7577d086a80
parent 394651 032b89b764b9f4269cf8504253719f0835775c3a
child 394653 71beb0f9975266d01fb2c261af4cb3b4842c5124
push id97954
push userjandemooij@gmail.com
push dateMon, 04 Dec 2017 13:57:42 +0000
treeherdermozilla-inbound@10aa5c0593fa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs1422726
milestone59.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 1422726 - Optimize addEnumerableDataProperty by adding a fast path for the most common case. r=bhackett
js/src/vm/Shape.cpp
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -613,25 +613,88 @@ NativeObject::addDataPropertyInternal(JS
     MOZ_ASSERT(shape == obj->lastProperty());
 
     if (table)
         shape->updateDictionaryTable(table, entry, keep);
 
     return shape;
 }
 
+static MOZ_ALWAYS_INLINE Shape*
+PropertyTreeReadBarrier(Shape* parent, Shape* shape)
+{
+    JS::Zone* zone = shape->zone();
+    if (zone->needsIncrementalBarrier()) {
+        // We need a read barrier for the shape tree, since these are weak
+        // pointers.
+        Shape* tmp = shape;
+        TraceManuallyBarrieredEdge(zone->barrierTracer(), &tmp, "read barrier");
+        MOZ_ASSERT(tmp == shape);
+        return shape;
+    }
+
+    if (MOZ_LIKELY(!zone->isGCSweepingOrCompacting() ||
+                   !IsAboutToBeFinalizedUnbarriered(&shape)))
+    {
+        if (shape->isMarkedGray())
+            UnmarkGrayShapeRecursively(shape);
+        return shape;
+    }
+
+    // The shape we've found is unreachable and due to be finalized, so
+    // remove our weak reference to it and don't use it.
+    MOZ_ASSERT(parent->isMarkedAny());
+    parent->removeChild(shape);
+
+    return nullptr;
+}
+
 /* static */ Shape*
 NativeObject::addEnumerableDataProperty(JSContext* cx, HandleNativeObject obj, HandleId id)
 {
     // Like addProperty(Internal), but optimized for the common case of adding a
     // new enumerable data property.
 
-    AutoKeepShapeTables keep(cx);
     AutoCheckShapeConsistency check(obj);
 
+    // Fast path for non-dictionary shapes with a single kid.
+    do {
+        AutoCheckCannotGC nogc;
+
+        Shape* lastProperty = obj->lastProperty();
+        if (lastProperty->inDictionary())
+            break;
+
+        KidsPointer* kidp = &lastProperty->kids;
+        if (!kidp->isShape())
+            break;
+
+        Shape* kid = kidp->toShape();
+        MOZ_ASSERT(!kid->inDictionary());
+
+        if (kid->propidRaw() != id ||
+            kid->isAccessorShape() ||
+            kid->attributes() != JSPROP_ENUMERATE ||
+            kid->base()->unowned() != lastProperty->base()->unowned())
+        {
+            break;
+        }
+
+        MOZ_ASSERT(kid->isDataProperty());
+
+        kid = PropertyTreeReadBarrier(lastProperty, kid);
+        if (!kid)
+            break;
+
+        if (!obj->setLastProperty(cx, kid))
+            return nullptr;
+        return kid;
+    } while (0);
+
+    AutoKeepShapeTables keep(cx);
     ShapeTable* table = nullptr;
     ShapeTable::Entry* entry = nullptr;
 
     if (!obj->inDictionaryMode()) {
         if (MOZ_UNLIKELY(ShouldConvertToDictionary(obj))) {
             if (!toDictionaryMode(cx, obj))
                 return nullptr;
             table = obj->lastProperty()->maybeTable(keep);
@@ -1671,40 +1734,19 @@ PropertyTree::inlinedGetChild(JSContext*
     } else if (kidp->isHash()) {
         if (KidsHash::Ptr p = kidp->toHash()->lookup(child))
             existingShape = *p;
     } else {
         /* If kidp->isNull(), we always insert. */
     }
 
     if (existingShape) {
-        JS::Zone* zone = existingShape->zone();
-        if (zone->needsIncrementalBarrier()) {
-            /*
-             * We need a read barrier for the shape tree, since these are weak
-             * pointers.
-             */
-            Shape* tmp = existingShape;
-            TraceManuallyBarrieredEdge(zone->barrierTracer(), &tmp, "read barrier");
-            MOZ_ASSERT(tmp == existingShape);
+        existingShape = PropertyTreeReadBarrier(parent, existingShape);
+        if (existingShape)
             return existingShape;
-        }
-        if (!zone->isGCSweepingOrCompacting() ||
-            !IsAboutToBeFinalizedUnbarriered(&existingShape))
-        {
-            if (existingShape->isMarkedGray())
-                UnmarkGrayShapeRecursively(existingShape);
-            return existingShape;
-        }
-        /*
-         * The shape we've found is unreachable and due to be finalized, so
-         * remove our weak reference to it and don't use it.
-         */
-        MOZ_ASSERT(parent->isMarkedAny());
-        parent->removeChild(existingShape);
     }
 
     RootedShape parentRoot(cx, parent);
     Shape* shape = Shape::new_(cx, child, parentRoot->numFixedSlots());
     if (!shape)
         return nullptr;
 
     if (!insertChild(cx, parentRoot, shape))