From fdcdfff048e69e3229c68a9361d453414c5c2665 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Mon, 4 Dec 2017 14:56:06 +0100 Subject: [PATCH] Bug 1422726 - Optimize addEnumerableDataProperty by adding a fast path for the most common case. r=bhackett --HG-- extra : rebase_source : 4f12ef98636ea4523c01ff75f3be79ee905d160a --- js/src/vm/Shape.cpp | 90 +++++++++++++++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 24 deletions(-) diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index 55b5f178701e..12f6583bad3f 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -618,15 +618,78 @@ NativeObject::addDataPropertyInternal(JSContext* cx, 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; @@ -1676,30 +1739,9 @@ PropertyTree::inlinedGetChild(JSContext* cx, Shape* parent, Handle c } 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);