Bug 1166709 - After converting unboxed objects created by some initializer to natives, create native objects at that allocation site in the future, r=jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 28 May 2015 18:56:32 -0600
changeset 246130 c21058eb90ffe26db3c5b4cc1469e611800a9124
parent 246129 0d4659ab6d49c34087610dc813798b119af5f5c1
child 246131 62927f7a5705d70d906642eaa4d0f01c270670e2
push id60374
push userbhackett@mozilla.com
push dateFri, 29 May 2015 00:56:40 +0000
treeherdermozilla-inbound@c21058eb90ff [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1166709
milestone41.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 1166709 - After converting unboxed objects created by some initializer to natives, create native objects at that allocation site in the future, r=jandem.
js/src/vm/Interpreter.cpp
js/src/vm/ObjectGroup.cpp
js/src/vm/ObjectGroup.h
js/src/vm/UnboxedObject.cpp
js/src/vm/UnboxedObject.h
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -4638,18 +4638,21 @@ js::NewObjectOperation(JSContext* cx, Ha
 
     RootedObjectGroup group(cx);
     if (ObjectGroup::useSingletonForAllocationSite(script, pc, JSProto_Object)) {
         newKind = SingletonObject;
     } else {
         group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Object);
         if (!group)
             return nullptr;
-        if (group->maybePreliminaryObjects())
+        if (group->maybePreliminaryObjects()) {
             group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
+            if (group->maybeUnboxedLayout())
+                group->maybeUnboxedLayout()->setAllocationSite(script, pc);
+        }
 
         if (group->shouldPreTenure() || group->maybePreliminaryObjects())
             newKind = TenuredObject;
 
         if (group->maybeUnboxedLayout())
             return UnboxedPlainObject::create(cx, group, newKind);
     }
 
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -1184,16 +1184,31 @@ ObjectGroup::allocationSiteGroup(JSConte
     }
 
     if (!table->add(p, key, res))
         return nullptr;
 
     return res;
 }
 
+void
+ObjectGroupCompartment::replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc,
+                                                   JSProtoKey kind, ObjectGroup* group)
+{
+    AllocationSiteKey key;
+    key.script = script;
+    key.offset = script->pcToOffset(pc);
+    key.kind = kind;
+
+    AllocationSiteTable::Ptr p = allocationSiteTable->lookup(key);
+    MOZ_ASSERT(p);
+    allocationSiteTable->remove(p);
+    allocationSiteTable->putNew(key, group);
+}
+
 /* static */ ObjectGroup*
 ObjectGroup::callingAllocationSiteGroup(JSContext* cx, JSProtoKey key)
 {
     jsbytecode* pc;
     RootedScript script(cx, cx->currentScript(&pc));
     if (script)
         return allocationSiteGroup(cx, script, pc, key);
     return defaultNewGroup(cx, key);
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -239,18 +239,18 @@ class ObjectGroup : public gc::TenuredCe
         Addendum_PreliminaryObjects,
 
         // When objects in this group have an unboxed representation, the
         // addendum stores an UnboxedLayout (which might have a TypeNewScript
         // as well, if the group is also constructed using 'new').
         Addendum_UnboxedLayout,
 
         // If this group is used by objects that have been converted from an
-        // unboxed representation, the addendum points to the original unboxed
-        // group.
+        // unboxed representation and/or have the same allocation kind as such
+        // objects, the addendum points to that unboxed group.
         Addendum_OriginalUnboxedGroup,
 
         // When used by typed objects, the addendum stores a TypeDescr.
         Addendum_TypeDescr
     };
 
     // If non-null, holds additional information about this object, whose
     // format is indicated by the object's addendum kind.
@@ -673,16 +673,19 @@ class ObjectGroupCompartment
 
     // Table for referencing types of objects keyed to an allocation site.
     AllocationSiteTable* allocationSiteTable;
 
   public:
     ObjectGroupCompartment();
     ~ObjectGroupCompartment();
 
+    void replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc,
+                                    JSProtoKey kind, ObjectGroup* group);
+
     void removeDefaultNewGroup(const Class* clasp, TaggedProto proto, JSObject* associated);
     void replaceDefaultNewGroup(const Class* clasp, TaggedProto proto, JSObject* associated,
                                 ObjectGroup* group);
 
     static ObjectGroup* makeGroup(ExclusiveContext* cx, const Class* clasp,
                                   Handle<TaggedProto> proto,
                                   ObjectGroupFlags initialFlags = 0);
 
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/UnboxedObject-inl.h"
 
+#include "jit/BaselineIC.h"
 #include "jit/JitCommon.h"
 #include "jit/Linker.h"
 
 #include "jsobjinlines.h"
 
 #include "gc/Nursery-inl.h"
 #include "vm/Shape-inl.h"
 
@@ -35,18 +36,21 @@ UnboxedLayout::trace(JSTracer* trc)
         newScript()->trace(trc);
 
     if (nativeGroup_)
         TraceEdge(trc, &nativeGroup_, "unboxed_layout_nativeGroup");
 
     if (nativeShape_)
         TraceEdge(trc, &nativeShape_, "unboxed_layout_nativeShape");
 
-    if (replacementNewGroup_)
-        TraceEdge(trc, &replacementNewGroup_, "unboxed_layout_replacementNewGroup");
+    if (allocationScript_)
+        TraceEdge(trc, &allocationScript_, "unboxed_layout_allocationScript");
+
+    if (replacementGroup_)
+        TraceEdge(trc, &replacementGroup_, "unboxed_layout_replacementGroup");
 
     if (constructorCode_)
         TraceEdge(trc, &constructorCode_, "unboxed_layout_constructorCode");
 }
 
 size_t
 UnboxedLayout::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
 {
@@ -376,63 +380,102 @@ PropagatePropertyTypes(JSContext* cx, js
         return false;
     }
     MOZ_ASSERT(!types.empty());
     for (size_t j = 0; j < types.length(); j++)
         AddTypePropertyId(cx, newGroup, nullptr, id, types[j]);
     return true;
 }
 
+static PlainObject*
+MakeReplacementTemplateObject(JSContext* cx, HandleObjectGroup group, const UnboxedLayout &layout)
+{
+    PlainObject* obj = NewObjectWithGroup<PlainObject>(cx, group, layout.getAllocKind(),
+                                                       TenuredObject);
+    if (!obj)
+        return nullptr;
+
+    for (size_t i = 0; i < layout.properties().length(); i++) {
+        const UnboxedLayout::Property& property = layout.properties()[i];
+        if (!obj->addDataProperty(cx, NameToId(property.name), i, JSPROP_ENUMERATE))
+            return nullptr;
+        MOZ_ASSERT(obj->slotSpan() == i + 1);
+        MOZ_ASSERT(!obj->inDictionaryMode());
+    }
+
+    return obj;
+}
+
 /* static */ bool
 UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
 {
     AutoEnterAnalysis enter(cx);
 
     UnboxedLayout& layout = group->unboxedLayout();
     Rooted<TaggedProto> proto(cx, group->proto());
 
     MOZ_ASSERT(!layout.nativeGroup());
 
+    RootedObjectGroup replacementGroup(cx);
+
     // Immediately clear any new script on the group. This is done by replacing
     // the existing new script with one for a replacement default new group.
     // This is done so that the size of the replacment group's objects is the
     // same as that for the unboxed group, so that we do not see polymorphic
     // slot accesses later on for sites that see converted objects from this
     // group and objects that were allocated using the replacement new group.
-    RootedObjectGroup replacementNewGroup(cx);
     if (layout.newScript()) {
         MOZ_ASSERT(!layout.isArray());
 
-        replacementNewGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto);
-        if (!replacementNewGroup)
+        replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto);
+        if (!replacementGroup)
             return false;
 
-        PlainObject* templateObject = NewObjectWithGroup<PlainObject>(cx, replacementNewGroup,
-                                                                      layout.getAllocKind(),
-                                                                      TenuredObject);
+        PlainObject* templateObject = MakeReplacementTemplateObject(cx, replacementGroup, layout);
         if (!templateObject)
             return false;
 
-        for (size_t i = 0; i < layout.properties().length(); i++) {
-            const UnboxedLayout::Property& property = layout.properties()[i];
-            if (!templateObject->addDataProperty(cx, NameToId(property.name), i, JSPROP_ENUMERATE))
-                return false;
-            MOZ_ASSERT(templateObject->slotSpan() == i + 1);
-            MOZ_ASSERT(!templateObject->inDictionaryMode());
-        }
-
         TypeNewScript* replacementNewScript =
             TypeNewScript::makeNativeVersion(cx, layout.newScript(), templateObject);
         if (!replacementNewScript)
             return false;
 
-        replacementNewGroup->setNewScript(replacementNewScript);
-        gc::TraceTypeNewScript(replacementNewGroup);
+        replacementGroup->setNewScript(replacementNewScript);
+        gc::TraceTypeNewScript(replacementGroup);
+
+        group->clearNewScript(cx, replacementGroup);
+    }
+
+    // Similarly, if this group is keyed to an allocation site, replace its
+    // entry with a new group that has the same allocation kind and no unboxed
+    // layout.
+    if (layout.allocationScript()) {
+        MOZ_ASSERT(!layout.isArray());
+
+        RootedScript script(cx, layout.allocationScript());
+        jsbytecode* pc = layout.allocationPc();
 
-        group->clearNewScript(cx, replacementNewGroup);
+        replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto);
+        if (!replacementGroup)
+            return false;
+
+        replacementGroup->setOriginalUnboxedGroup(group);
+
+        cx->compartment()->objectGroups.replaceAllocationSiteGroup(script, pc,
+                                                                   JSProto_Object,
+                                                                   replacementGroup);
+
+        // Clear any baseline information at this opcode.
+        if (script->hasBaselineScript()) {
+            jit::ICEntry& entry = script->baselineScript()->icEntryFromPCOffset(script->pcToOffset(pc));
+            jit::ICFallbackStub* fallback = entry.fallbackStub();
+            for (jit::ICStubIterator iter = fallback->beginChain(); !iter.atEnd(); iter++)
+                iter.unlink(cx);
+            fallback->toNewObject_Fallback()->setTemplateObject(nullptr);
+        }
     }
 
     const Class* clasp = layout.isArray() ? &ArrayObject::class_ : &PlainObject::class_;
     size_t nfixed = layout.isArray() ? 0 : gc::GetGCKindSlots(layout.getAllocKind());
 
     if (layout.isArray()) {
         // The length shape to use for arrays is cached via a modified initial
         // shape for array objects. Create an array now to make sure this entry
@@ -479,17 +522,17 @@ UnboxedLayout::makeNativeGroup(JSContext
             HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id);
             if (nativeProperty->canSetDefinite(i))
                 nativeProperty->setDefinite(i);
         }
     }
 
     layout.nativeGroup_ = nativeGroup;
     layout.nativeShape_ = shape;
-    layout.replacementNewGroup_ = replacementNewGroup;
+    layout.replacementGroup_ = replacementGroup;
 
     nativeGroup->setOriginalUnboxedGroup(group);
 
     group->markStateChange(cx);
 
     return true;
 }
 
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -59,53 +59,57 @@ class UnboxedLayout : public mozilla::Li
 
   private:
     // If objects in this group have ever been converted to native objects,
     // these store the corresponding native group and initial shape for such
     // objects. Type information for this object is reflected in nativeGroup.
     HeapPtrObjectGroup nativeGroup_;
     HeapPtrShape nativeShape_;
 
+    // Any script/pc which the associated group is created for.
+    HeapPtrScript allocationScript_;
+    jsbytecode* allocationPc_;
+
+    // If nativeGroup is set and this object originally had a TypeNewScript or
+    // was keyed to an allocation site, this points to the group which replaced
+    // this one. This link is only needed to keep the replacement group from
+    // being GC'ed. If it were GC'ed and a new one regenerated later, that new
+    // group might have a different allocation kind from this group.
+    HeapPtrObjectGroup replacementGroup_;
+
     // The following members are only used for unboxed plain objects.
 
     // All properties on objects with this layout, in enumeration order.
     PropertyVector properties_;
 
     // Byte size of the data for objects with this layout.
     size_t size_;
 
     // Any 'new' script information associated with this layout.
     TypeNewScript* newScript_;
 
     // List for use in tracing objects with this layout. This has the same
     // structure as the trace list on a TypeDescr.
     int32_t* traceList_;
 
-    // If nativeGroup is set and this object originally had a TypeNewScript,
-    // this points to the default 'new' group which replaced this one (and
-    // which might itself have been cleared since). This link is only needed to
-    // keep the replacement group from being GC'ed. If it were GC'ed and a new
-    // one regenerated later, that new group might have a different allocation
-    // kind from this group.
-    HeapPtrObjectGroup replacementNewGroup_;
-
     // If this layout has been used to construct script or JSON constant
     // objects, this code might be filled in to more quickly fill in objects
     // from an array of values.
     HeapPtrJitCode constructorCode_;
 
     // The following members are only used for unboxed arrays.
 
     // The type of array elements.
     JSValueType elementType_;
 
   public:
     UnboxedLayout()
-      : nativeGroup_(nullptr), nativeShape_(nullptr), size_(0), newScript_(nullptr),
-        traceList_(nullptr), replacementNewGroup_(nullptr), constructorCode_(nullptr),
+      : nativeGroup_(nullptr), nativeShape_(nullptr),
+        allocationScript_(nullptr), allocationPc_(nullptr), replacementGroup_(nullptr),
+        size_(0), newScript_(nullptr), traceList_(nullptr), constructorCode_(nullptr),
         elementType_(JSVAL_TYPE_MAGIC)
     {}
 
     bool initProperties(const PropertyVector& properties, size_t size) {
         size_ = size;
         return properties_.appendAll(properties);
     }
 
@@ -116,17 +120,17 @@ class UnboxedLayout : public mozilla::Li
     ~UnboxedLayout() {
         if (newScript_)
             newScript_->clear();
         js_delete(newScript_);
         js_free(traceList_);
 
         nativeGroup_.init(nullptr);
         nativeShape_.init(nullptr);
-        replacementNewGroup_.init(nullptr);
+        replacementGroup_.init(nullptr);
         constructorCode_.init(nullptr);
     }
 
     bool isArray() {
         return elementType_ != JSVAL_TYPE_MAGIC;
     }
 
     void detachFromCompartment();
@@ -136,16 +140,29 @@ class UnboxedLayout : public mozilla::Li
     }
 
     TypeNewScript* newScript() const {
         return newScript_;
     }
 
     void setNewScript(TypeNewScript* newScript, bool writeBarrier = true);
 
+    JSScript* allocationScript() const {
+        return allocationScript_;
+    }
+
+    jsbytecode* allocationPc() const {
+        return allocationPc_;
+    }
+
+    void setAllocationSite(JSScript* script, jsbytecode* pc) {
+        allocationScript_ = script;
+        allocationPc_ = pc;
+    }
+
     const int32_t* traceList() const {
         return traceList_;
     }
 
     void setTraceList(int32_t* traceList) {
         traceList_ = traceList;
     }