Backed out 3 changesets (bug 1521127) for build bustages at builds/worker/workspace/build/src/js/src/vm/ObjectGroup.cpp on a CLOSED TREE
authorCoroiu Cristina <ccoroiu@mozilla.com>
Wed, 23 Jan 2019 15:05:09 +0200
changeset 515092 1f92e62a285c1e626ecb745857d6177bbaf660cc
parent 515091 1bf3cec003abff4b27c11b357e49f72c93dd5148
child 515093 1763f1757e15416cecb9f84e01e185ece6b82099
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1521127
milestone66.0a1
backs out10f6f2e55f095b42b71617da52b1c99245dbfb75
232668044fbfc003f8ab57e80f0acddda67b2f31
6ac126ad6e18cdc00fc6b0b15a1242c1028a135f
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
Backed out 3 changesets (bug 1521127) for build bustages at builds/worker/workspace/build/src/js/src/vm/ObjectGroup.cpp on a CLOSED TREE Backed out changeset 10f6f2e55f09 (bug 1521127) Backed out changeset 232668044fbf (bug 1521127) Backed out changeset 6ac126ad6e18 (bug 1521127)
js/src/builtin/DataViewObject.cpp
js/src/builtin/Object.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/CacheIR.cpp
js/src/jit/MCallOptimize.cpp
js/src/jsfriendapi.cpp
js/src/vm/GlobalObject.h
js/src/vm/Interpreter.cpp
js/src/vm/JSObject.cpp
js/src/vm/JSObject.h
js/src/vm/ObjectGroup.cpp
js/src/vm/ObjectGroup.h
js/src/vm/TypedArrayObject.cpp
js/src/vm/UnboxedObject.cpp
--- a/js/src/builtin/DataViewObject.cpp
+++ b/js/src/builtin/DataViewObject.cpp
@@ -928,17 +928,17 @@ JS_FRIEND_API uint32_t JS_GetDataViewByt
     return 0;
   }
   return obj->as<DataViewObject>().byteLength();
 }
 
 JS_FRIEND_API JSObject* JS_NewDataView(JSContext* cx, HandleObject buffer,
                                        uint32_t byteOffset,
                                        int32_t byteLength) {
-  JSProtoKey key = JSProto_DataView;
+  JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(&DataViewObject::class_);
   RootedObject constructor(cx, GlobalObject::getOrCreateConstructor(cx, key));
   if (!constructor) {
     return nullptr;
   }
 
   FixedConstructArgs<3> cargs(cx);
 
   cargs[0].setObject(*buffer);
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -2168,17 +2168,17 @@ static bool FinishObjectClassInit(JSCont
    * Eventually we'd like to have standard classes be there from the start,
    * and thus we would know we were always setting what had previously been a
    * null [[Prototype]], but right now some code assumes it can set the
    * [[Prototype]] before standard classes have been initialized.  For now,
    * only set the [[Prototype]] if it hasn't already been set.
    */
   Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
   if (global->shouldSplicePrototype()) {
-    if (!JSObject::splicePrototype(cx, global, tagged)) {
+    if (!JSObject::splicePrototype(cx, global, global->getClass(), tagged)) {
       return false;
     }
   }
   return true;
 }
 
 static const ClassSpec PlainObjectClassSpec = {
     CreateObjectConstructor, CreateObjectPrototype,
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -3289,17 +3289,18 @@ static bool TryAttachFunCallStub(JSConte
     return true;
   }
 
   return true;
 }
 
 static bool GetTemplateObjectForNative(JSContext* cx, HandleFunction target,
                                        const CallArgs& args,
-                                       MutableHandleObject res) {
+                                       MutableHandleObject res,
+                                       bool* skipAttach) {
   Native native = target->native();
 
   // Check for natives to which template objects can be attached. This is
   // done to provide templates to Ion for inlining these natives later on.
 
   if (native == ArrayConstructor || native == array_construct) {
     // Note: the template array won't be used if its length is inaccurately
     // computed here.  (We allocate here because compilation may occur on a
@@ -3308,16 +3309,26 @@ static bool GetTemplateObjectForNative(J
     if (args.length() != 1) {
       count = args.length();
     } else if (args.length() == 1 && args[0].isInt32() &&
                args[0].toInt32() >= 0) {
       count = args[0].toInt32();
     }
 
     if (count <= ArrayObject::EagerAllocationMaxLength) {
+      ObjectGroup* group =
+          ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array);
+      if (!group) {
+        return false;
+      }
+      if (group->maybePreliminaryObjectsDontCheckGeneration()) {
+        *skipAttach = true;
+        return true;
+      }
+
       // With this and other array templates, analyze the group so that
       // we don't end up with a template whose structure might change later.
       res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, count,
                                                              TenuredObject));
       return !!res;
     }
   }
 
@@ -3335,16 +3346,20 @@ static bool GetTemplateObjectForNative(J
       return true;
     }
   }
 
   if (native == js::array_slice) {
     if (args.thisv().isObject()) {
       RootedObject obj(cx, &args.thisv().toObject());
       if (!obj->isSingleton()) {
+        if (obj->group()->maybePreliminaryObjectsDontCheckGeneration()) {
+          *skipAttach = true;
+          return true;
+        }
         res.set(NewFullyAllocatedArrayTryReuseGroup(cx, obj, 0, TenuredObject));
         return !!res;
       }
     }
   }
 
   if (native == StringConstructor) {
     RootedString emptyString(cx, cx->runtime()->emptyString);
@@ -3674,20 +3689,26 @@ static bool TryAttachCallStub(JSContext*
               "  Megamorphic Call_Native stubs. TODO: add Call_AnyNative!");
       return true;
     }
 
     bool isCrossRealm = cx->realm() != fun->realm();
 
     RootedObject templateObject(cx);
     if (MOZ_LIKELY(!isSpread && !isSuper && !isCrossRealm)) {
+      bool skipAttach = false;
       CallArgs args = CallArgsFromVp(argc, vp);
-      if (!GetTemplateObjectForNative(cx, fun, args, &templateObject)) {
+      if (!GetTemplateObjectForNative(cx, fun, args, &templateObject,
+                                      &skipAttach)) {
         return false;
       }
+      if (skipAttach) {
+        *handled = true;
+        return true;
+      }
       MOZ_ASSERT_IF(templateObject,
                     !templateObject->group()
                          ->maybePreliminaryObjectsDontCheckGeneration());
     }
 
     bool ignoresReturnValue =
         op == JSOP_CALL_IGNORES_RV && fun->isNative() && fun->hasJitInfo() &&
         fun->jitInfo()->type() == JSJitInfo::IgnoresReturnValueNative;
@@ -6046,17 +6067,18 @@ static bool DoNewArray(JSContext* cx, Ba
     RootedScript script(cx, frame->script());
     jsbytecode* pc = stub->icEntry()->pc(script);
 
     obj = NewArrayOperation(cx, script, pc, length);
     if (!obj) {
       return false;
     }
 
-    if (!obj->isSingleton()) {
+    if (!obj->isSingleton() &&
+        !obj->group()->maybePreliminaryObjectsDontCheckGeneration()) {
       JSObject* templateObject =
           NewArrayOperation(cx, script, pc, length, TenuredObject);
       if (!templateObject) {
         return false;
       }
       stub->setTemplateObject(templateObject);
     }
   }
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -4976,16 +4976,22 @@ bool CallIRGenerator::tryAttachArrayPush
   }
 
   if (thisobj->hasLazyGroup()) {
     return false;
   }
 
   RootedArrayObject thisarray(cx_, &thisobj->as<ArrayObject>());
 
+  // And the object group for the array is not collecting preliminary objects.
+  AutoSweepObjectGroup sweep(thisobj->group());
+  if (thisobj->group()->maybePreliminaryObjects(sweep)) {
+    return false;
+  }
+
   // Check for other indexed properties or class hooks.
   if (!CanAttachAddElement(thisarray, /* isInit = */ false)) {
     return false;
   }
 
   // Can't add new elements to arrays with non-writable length.
   if (!thisarray->lengthIsWritable()) {
     return false;
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1836,16 +1836,20 @@ IonBuilder::InliningResult IonBuilder::i
     return resultConstStringSplit;
   }
 
   JSContext* cx = TlsContext.get();
   ObjectGroup* group = ObjectGroupRealm::getStringSplitStringGroup(cx);
   if (!group) {
     return InliningStatus_NotInlined;
   }
+  AutoSweepObjectGroup sweep(group);
+  if (group->maybePreliminaryObjects(sweep)) {
+    return InliningStatus_NotInlined;
+  }
 
   TypeSet::ObjectKey* retKey = TypeSet::ObjectKey::get(group);
   if (retKey->unknownProperties()) {
     return InliningStatus_NotInlined;
   }
 
   HeapTypeSetKey key = retKey->property(JSID_VOID);
   if (!key.maybeTypes()) {
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -114,17 +114,17 @@ JS_FRIEND_API bool JS_SplicePrototype(JS
     /*
      * We can see non-singleton objects when trying to splice prototypes
      * due to mutable __proto__ (ugh).
      */
     return JS_SetPrototype(cx, obj, proto);
   }
 
   Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
-  return JSObject::splicePrototype(cx, obj, tagged);
+  return JSObject::splicePrototype(cx, obj, obj->getClass(), tagged);
 }
 
 JS_FRIEND_API JSObject* JS_NewObjectWithUniqueType(JSContext* cx,
                                                    const JSClass* clasp,
                                                    HandleObject proto) {
   /*
    * Create our object with a null proto and then splice in the correct proto
    * after we setSingleton, so that we don't pollute the default
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -272,21 +272,16 @@ class GlobalObject : public NativeObject
 
  public:
   static GlobalObject* new_(JSContext* cx, const Class* clasp,
                             JSPrincipals* principals,
                             JS::OnNewGlobalHookOption hookOption,
                             const JS::RealmOptions& options);
 
   /*
-   * For bootstrapping, whether to splice a prototype for the global object.
-   */
-  bool shouldSplicePrototype();
-
-  /*
    * Create a constructor function with the specified name and length using
    * ctor, a method which creates objects with the given class.
    */
   static JSFunction* createConstructor(
       JSContext* cx, JSNative ctor, JSAtom* name, unsigned length,
       gc::AllocKind kind = gc::AllocKind::FUNCTION,
       const JSJitInfo* jitInfo = nullptr);
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -5228,19 +5228,23 @@ JSObject* js::NewArrayOperation(JSContex
   RootedObjectGroup group(cx);
   if (ObjectGroup::useSingletonForAllocationSite(script, pc, JSProto_Array)) {
     newKind = SingletonObject;
   } else {
     group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
     if (!group) {
       return nullptr;
     }
-
     AutoSweepObjectGroup sweep(group);
-    if (group->shouldPreTenure(sweep)) {
+    if (group->maybePreliminaryObjects(sweep)) {
+      group->maybePreliminaryObjects(sweep)->maybeAnalyze(cx, group);
+    }
+
+    if (group->shouldPreTenure(sweep) ||
+        group->maybePreliminaryObjects(sweep)) {
       newKind = TenuredObject;
     }
   }
 
   ArrayObject* obj = NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
   if (!obj) {
     return nullptr;
   }
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -996,18 +996,18 @@ JSObject* js::NewObjectWithGroupCommon(J
   return obj;
 }
 
 bool js::NewObjectScriptedCall(JSContext* cx, MutableHandleObject pobj) {
   jsbytecode* pc;
   RootedScript script(cx, cx->currentScript(&pc));
   gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_);
   NewObjectKind newKind = GenericObject;
-  if (script &&
-      ObjectGroup::useSingletonForAllocationSite(script, pc, JSProto_Object)) {
+  if (script && ObjectGroup::useSingletonForAllocationSite(
+                    script, pc, &PlainObject::class_)) {
     newKind = SingletonObject;
   }
   RootedObject obj(
       cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind));
   if (!obj) {
     return false;
   }
 
@@ -1061,17 +1061,17 @@ static inline JSObject* CreateThisForFun
                             CopyInitializerObject(cx, templateObject, newKind));
       if (!res) {
         return nullptr;
       }
 
       if (newKind == SingletonObject) {
         Rooted<TaggedProto> proto(
             cx, TaggedProto(templateObject->staticPrototype()));
-        if (!JSObject::splicePrototype(cx, res, proto)) {
+        if (!JSObject::splicePrototype(cx, res, &PlainObject::class_, proto)) {
           return nullptr;
         }
       } else {
         res->setGroup(group);
       }
       return res;
     }
 
@@ -2108,17 +2108,17 @@ static NativeObject* DefineConstructorAn
     ctor = fun;
     if (!LinkConstructorAndPrototype(cx, ctor, proto)) {
       goto bad;
     }
 
     /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
     Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
     if (ctor->getClass() == clasp &&
-        !JSObject::splicePrototype(cx, ctor, tagged)) {
+        !JSObject::splicePrototype(cx, ctor, clasp, tagged)) {
       goto bad;
     }
   }
 
   if (!DefinePropertiesAndFunctions(cx, proto, ps, fs) ||
       (ctor != proto &&
        !DefinePropertiesAndFunctions(cx, ctor, static_ps, static_fs))) {
     goto bad;
@@ -2251,18 +2251,19 @@ static bool ReshapeForProtoMutation(JSCo
     }
 
     pobj = pobj->staticPrototype();
   }
 
   return true;
 }
 
-static bool SetProto(JSContext* cx, HandleObject obj,
-                     Handle<js::TaggedProto> proto) {
+static bool SetClassAndProto(JSContext* cx, HandleObject obj,
+                             const Class* clasp,
+                             Handle<js::TaggedProto> proto) {
   // Regenerate object shape (and possibly prototype shape) to invalidate JIT
   // code that is affected by a prototype mutation.
   if (!ReshapeForProtoMutation(cx, obj)) {
     return false;
   }
 
   if (proto.isObject()) {
     RootedObject protoObj(cx, proto.toObject());
@@ -2271,17 +2272,17 @@ static bool SetProto(JSContext* cx, Hand
     }
   }
 
   if (obj->isSingleton()) {
     /*
      * Just splice the prototype, but mark the properties as unknown for
      * consistent behavior.
      */
-    if (!JSObject::splicePrototype(cx, obj, proto)) {
+    if (!JSObject::splicePrototype(cx, obj, clasp, proto)) {
       return false;
     }
     MarkObjectGroupUnknownProperties(cx, obj->group());
     return true;
   }
 
   RootedObjectGroup oldGroup(cx, obj->group());
 
@@ -2293,17 +2294,17 @@ static bool SetProto(JSContext* cx, Hand
     MOZ_ASSERT(obj->is<JSFunction>());
     newGroup = ObjectGroupRealm::makeGroup(cx, oldGroup->realm(),
                                            &JSFunction::class_, proto);
     if (!newGroup) {
       return false;
     }
     newGroup->setInterpretedFunction(oldGroup->maybeInterpretedFunction());
   } else {
-    newGroup = ObjectGroup::defaultNewGroup(cx, obj->getClass(), proto);
+    newGroup = ObjectGroup::defaultNewGroup(cx, clasp, proto);
     if (!newGroup) {
       return false;
     }
   }
 
   obj->setGroup(newGroup);
 
   // Add the object's property types to the new group.
@@ -2939,17 +2940,17 @@ bool js::SetPrototype(JSContext* cx, Han
 
   // Convert unboxed objects to their native representations before changing
   // their prototype/group, as they depend on the group for their layout.
   if (!MaybeConvertUnboxedObjectToNative(cx, obj)) {
     return false;
   }
 
   Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto));
-  if (!SetProto(cx, obj, taggedProto)) {
+  if (!SetClassAndProto(cx, obj, obj->getClass(), taggedProto)) {
     return false;
   }
 
   return result.succeed();
 }
 
 bool js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto) {
   ObjectOpResult result;
--- a/js/src/vm/JSObject.h
+++ b/js/src/vm/JSObject.h
@@ -387,19 +387,26 @@ class JSObject : public js::gc::Cell {
    * properties.
    */
   inline bool isNewGroupUnknown() const;
   static bool setNewGroupUnknown(JSContext* cx, js::ObjectGroupRealm& realm,
                                  const js::Class* clasp, JS::HandleObject obj);
 
   /* Set a new prototype for an object with a singleton type. */
   static bool splicePrototype(JSContext* cx, js::HandleObject obj,
+                              const js::Class* clasp,
                               js::Handle<js::TaggedProto> proto);
 
   /*
+   * For bootstrapping, whether to splice a prototype for Function.prototype
+   * or the global object.
+   */
+  bool shouldSplicePrototype();
+
+  /*
    * Environment chains.
    *
    * The environment chain of an object is the link in the search path when
    * a script does a name lookup on an environment object. For JS internal
    * environment objects --- Call, LexicalEnvironment, and WithEnvironment
    * --- the chain is stored in the first fixed slot of the object.  For
    * other environment objects, the chain goes directly to the global.
    *
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -14,17 +14,16 @@
 #include "builtin/DataViewObject.h"
 #include "gc/FreeOp.h"
 #include "gc/HashUtil.h"
 #include "gc/Policy.h"
 #include "gc/StoreBuffer.h"
 #include "js/CharacterEncoding.h"
 #include "js/UniquePtr.h"
 #include "vm/ArrayObject.h"
-#include "vm/GlobalObject.h"
 #include "vm/JSObject.h"
 #include "vm/RegExpObject.h"
 #include "vm/Shape.h"
 #include "vm/TaggedProto.h"
 
 #include "gc/Marking-inl.h"
 #include "vm/TypeInference-inl.h"
 #include "vm/UnboxedObject-inl.h"
@@ -247,41 +246,44 @@ void ObjectGroup::setAddendum(AddendumKi
   return useSingletonForAllocationSite(script, pc,
                                        JSCLASS_CACHED_PROTO_KEY(clasp));
 }
 
 /////////////////////////////////////////////////////////////////////
 // JSObject
 /////////////////////////////////////////////////////////////////////
 
-bool GlobalObject::shouldSplicePrototype() {
-  // During bootstrapping, we need to make sure not to splice a new prototype in
-  // for the global object if its __proto__ had previously been set to non-null,
-  // as this will change the prototype for all other objects with the same type.
-  return staticPrototype() == nullptr;
+bool JSObject::shouldSplicePrototype() {
+  /*
+   * During bootstrapping, if inference is enabled we need to make sure not
+   * to splice a new prototype in for Function.prototype or the global
+   * object if their __proto__ had previously been set to null, as this
+   * will change the prototype for all other objects with the same type.
+   */
+  if (staticPrototype() != nullptr) {
+    return false;
+  }
+  return isSingleton();
 }
 
 /* static */ bool JSObject::splicePrototype(JSContext* cx, HandleObject obj,
+                                            const Class* clasp,
                                             Handle<TaggedProto> proto) {
   MOZ_ASSERT(cx->compartment() == obj->compartment());
 
   /*
    * For singleton groups representing only a single JSObject, the proto
    * can be rearranged as needed without destroying type information for
    * the old or new types.
    */
   MOZ_ASSERT(obj->isSingleton());
 
   // Windows may not appear on prototype chains.
   MOZ_ASSERT_IF(proto.isObject(), !IsWindow(proto.toObject()));
 
-#ifdef DEBUG
-  const Class* oldClass = obj->getClass();
-#endif
-
   if (proto.isObject()) {
     RootedObject protoObj(cx, proto.toObject());
     if (!JSObject::setDelegate(cx, protoObj)) {
       return false;
     }
   }
 
   // Force type instantiation when splicing lazy group.
@@ -293,18 +295,17 @@ bool GlobalObject::shouldSplicePrototype
   if (proto.isObject()) {
     RootedObject protoObj(cx, proto.toObject());
     protoGroup = JSObject::getGroup(cx, protoObj);
     if (!protoGroup) {
       return false;
     }
   }
 
-  MOZ_ASSERT(group->clasp() == oldClass,
-             "splicing a prototype doesn't change a group's class");
+  group->setClasp(clasp);
   group->setProto(proto);
   return true;
 }
 
 /* static */ ObjectGroup* JSObject::makeLazyGroup(JSContext* cx,
                                                   HandleObject obj) {
   MOZ_ASSERT(obj->hasLazyGroup());
   MOZ_ASSERT(cx->compartment() == obj->compartment());
@@ -720,29 +721,48 @@ MOZ_ALWAYS_INLINE ObjectGroup* ObjectGro
 inline const Class* GetClassForProtoKey(JSProtoKey key) {
   switch (key) {
     case JSProto_Null:
     case JSProto_Object:
       return &PlainObject::class_;
     case JSProto_Array:
       return &ArrayObject::class_;
 
+    case JSProto_Number:
+      return &NumberObject::class_;
+    case JSProto_Boolean:
+      return &BooleanObject::class_;
+    case JSProto_String:
+      return &StringObject::class_;
+    case JSProto_Symbol:
+      return &SymbolObject::class_;
+    case JSProto_RegExp:
+      return &RegExpObject::class_;
+
     case JSProto_Int8Array:
     case JSProto_Uint8Array:
     case JSProto_Int16Array:
     case JSProto_Uint16Array:
     case JSProto_Int32Array:
     case JSProto_Uint32Array:
     case JSProto_Float32Array:
     case JSProto_Float64Array:
     case JSProto_Uint8ClampedArray:
       return &TypedArrayObject::classes[key - JSProto_Int8Array];
 
+    case JSProto_ArrayBuffer:
+      return &ArrayBufferObject::class_;
+
+    case JSProto_SharedArrayBuffer:
+      return &SharedArrayBufferObject::class_;
+
+    case JSProto_DataView:
+      return &DataViewObject::class_;
+
     default:
-      // We only expect to see plain objects, arrays, and typed arrays here.
       MOZ_CRASH("Bad proto key");
   }
 }
 
 /* static */ ObjectGroup* ObjectGroup::defaultNewGroup(JSContext* cx,
                                                        JSProtoKey key) {
   JSObject* proto = nullptr;
   if (key != JSProto_Null) {
@@ -1739,23 +1759,25 @@ ObjectGroup* ObjectGroupRealm::getString
   ObjectGroup* group = groups.stringSplitStringGroup.get();
   if (group) {
     return group;
   }
 
   // The following code is a specialized version of the code
   // for ObjectGroup::allocationSiteGroup().
 
+  const Class* clasp = GetClassForProtoKey(JSProto_Array);
+
   JSObject* proto = GlobalObject::getOrCreateArrayPrototype(cx, cx->global());
   if (!proto) {
     return nullptr;
   }
   Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
 
-  group = makeGroup(cx, cx->realm(), &ArrayObject::class_, tagged);
+  group = makeGroup(cx, cx->realm(), clasp, tagged, /* initialFlags = */ 0);
   if (!group) {
     return nullptr;
   }
 
   groups.stringSplitStringGroup.set(group);
   return group;
 }
 
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -516,16 +516,18 @@ class ObjectGroup : public gc::TenuredCe
 
   // Whether to make a singleton when calling 'new' at script/pc.
   static bool useSingletonForNewObject(JSContext* cx, JSScript* script,
                                        jsbytecode* pc);
 
   // Whether to make a singleton object at an allocation site.
   static bool useSingletonForAllocationSite(JSScript* script, jsbytecode* pc,
                                             JSProtoKey key);
+  static bool useSingletonForAllocationSite(JSScript* script, jsbytecode* pc,
+                                            const Class* clasp);
 
   // Static accessors for ObjectGroupRealm NewTable.
 
   static ObjectGroup* defaultNewGroup(JSContext* cx, const Class* clasp,
                                       TaggedProto proto,
                                       JSObject* associated = nullptr);
   static ObjectGroup* lazySingletonGroup(JSContext* cx, ObjectGroup* oldGroup,
                                          const Class* clasp, TaggedProto proto);
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -358,54 +358,57 @@ class TypedArrayObjectTemplate : public 
       setIndex(tarray, index, NativeType(d));
     } else {
       MOZ_ASSERT(sizeof(NativeType) <= 4);
       int32_t n = ToInt32(d);
       setIndex(tarray, index, NativeType(n));
     }
   }
 
-  static TypedArrayObject* newBuiltinClassInstance(JSContext* cx,
-                                                   gc::AllocKind allocKind,
-                                                   NewObjectKind newKind) {
-    JSObject* obj =
-        NewBuiltinClassInstance(cx, instanceClass(), allocKind, newKind);
-    return obj ? &obj->as<TypedArrayObject>() : nullptr;
-  }
-
   static TypedArrayObject* makeProtoInstance(JSContext* cx, HandleObject proto,
                                              gc::AllocKind allocKind) {
     MOZ_ASSERT(proto);
 
     JSObject* obj =
         NewObjectWithGivenProto(cx, instanceClass(), proto, allocKind);
     return obj ? &obj->as<TypedArrayObject>() : nullptr;
   }
 
   static TypedArrayObject* makeTypedInstance(JSContext* cx,
                                              CreateSingleton createSingleton,
                                              gc::AllocKind allocKind) {
+    const Class* clasp = instanceClass();
     if (createSingleton == CreateSingleton::Yes) {
-      return newBuiltinClassInstance(cx, allocKind, SingletonObject);
+      JSObject* obj =
+          NewBuiltinClassInstance(cx, clasp, allocKind, SingletonObject);
+      if (!obj) {
+        return nullptr;
+      }
+      return &obj->as<TypedArrayObject>();
     }
 
     jsbytecode* pc;
     RootedScript script(cx, cx->currentScript(&pc));
-    Rooted<TypedArrayObject*> obj(
-        cx, newBuiltinClassInstance(cx, allocKind, GenericObject));
+    NewObjectKind newKind = GenericObject;
+    if (script &&
+        ObjectGroup::useSingletonForAllocationSite(script, pc, clasp)) {
+      newKind = SingletonObject;
+    }
+    RootedObject obj(cx,
+                     NewBuiltinClassInstance(cx, clasp, allocKind, newKind));
     if (!obj) {
       return nullptr;
     }
 
     if (script && !ObjectGroup::setAllocationSiteObjectGroup(
-                      cx, script, pc, obj, /* singleton = */ false)) {
+                      cx, script, pc, obj, newKind == SingletonObject)) {
       return nullptr;
     }
 
-    return obj;
+    return &obj->as<TypedArrayObject>();
   }
 
   static TypedArrayObject* makeInstance(
       JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
       CreateSingleton createSingleton, uint32_t byteOffset, uint32_t len,
       HandleObject proto) {
     MOZ_ASSERT(len < INT32_MAX / BYTES_PER_ELEMENT);
 
@@ -413,17 +416,18 @@ class TypedArrayObjectTemplate : public 
         buffer ? gc::GetGCObjectKind(instanceClass())
                : AllocKindForLazyBuffer(len * BYTES_PER_ELEMENT);
 
     // Subclassing mandates that we hand in the proto every time. Most of
     // the time, though, that [[Prototype]] will not be interesting. If
     // it isn't, we can do some more TI optimizations.
     RootedObject checkProto(cx);
     if (proto) {
-      checkProto = GlobalObject::getOrCreatePrototype(cx, protoKey());
+      checkProto = GlobalObject::getOrCreatePrototype(
+          cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()));
       if (!checkProto) {
         return nullptr;
       }
     }
 
     AutoSetNewObjectMetadata metadata(cx);
     Rooted<TypedArrayObject*> obj(cx);
     if (proto && proto != checkProto) {
@@ -438,38 +442,44 @@ class TypedArrayObjectTemplate : public 
     return obj;
   }
 
   static TypedArrayObject* makeTemplateObject(JSContext* cx, int32_t len) {
     MOZ_ASSERT(len >= 0);
     size_t nbytes;
     MOZ_ALWAYS_TRUE(CalculateAllocSize<NativeType>(len, &nbytes));
     MOZ_ASSERT(nbytes < TypedArrayObject::SINGLETON_BYTE_LENGTH);
+    NewObjectKind newKind = TenuredObject;
     bool fitsInline = nbytes <= INLINE_BUFFER_LIMIT;
-    gc::AllocKind allocKind = !fitsInline ? gc::GetGCObjectKind(instanceClass())
+    const Class* clasp = instanceClass();
+    gc::AllocKind allocKind = !fitsInline ? gc::GetGCObjectKind(clasp)
                                           : AllocKindForLazyBuffer(nbytes);
 
     AutoSetNewObjectMetadata metadata(cx);
     jsbytecode* pc;
     RootedScript script(cx, cx->currentScript(&pc));
-    Rooted<TypedArrayObject*> tarray(
-        cx, newBuiltinClassInstance(cx, allocKind, TenuredObject));
-    if (!tarray) {
+    if (script &&
+        ObjectGroup::useSingletonForAllocationSite(script, pc, clasp)) {
+      newKind = SingletonObject;
+    }
+    JSObject* tmp = NewBuiltinClassInstance(cx, clasp, allocKind, newKind);
+    if (!tmp) {
       return nullptr;
     }
 
+    Rooted<TypedArrayObject*> tarray(cx, &tmp->as<TypedArrayObject>());
     initTypedArraySlots(tarray, len);
 
     // Template objects do not need memory for its elements, since there
     // won't be any elements to store. Therefore, we set the pointer to
     // nullptr and avoid allocating memory that will never be used.
     tarray->initPrivate(nullptr);
 
     if (script && !ObjectGroup::setAllocationSiteObjectGroup(
-                      cx, script, pc, tarray, /* singleton = */ false)) {
+                      cx, script, pc, tarray, newKind == SingletonObject)) {
       return nullptr;
     }
 
     return tarray;
   }
 
   static void initTypedArraySlots(TypedArrayObject* tarray, int32_t len) {
     MOZ_ASSERT(len >= 0);
@@ -772,17 +782,18 @@ class TypedArrayObjectTemplate : public 
                                &length)) {
       return nullptr;
     }
 
     // Make sure to get the [[Prototype]] for the created typed array from
     // this compartment.
     RootedObject protoRoot(cx, proto);
     if (!protoRoot) {
-      protoRoot = GlobalObject::getOrCreatePrototype(cx, protoKey());
+      protoRoot = GlobalObject::getOrCreatePrototype(
+          cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()));
       if (!protoRoot) {
         return nullptr;
       }
     }
 
     RootedObject typedArray(cx);
     {
       JSAutoRealm ar(cx, unwrappedBuffer);
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -1457,17 +1457,19 @@ bool js::TryConvertToUnboxedLayout(JSCon
                                       properties)) {
       return true;
     }
   }
 
   size_t layoutSize = 0;
   if (objectCount <= 1) {
     // If only one of the objects has been created, it is more likely
-    // to have new properties added later.
+    // to have new properties added later. This heuristic is not used
+    // for array objects, where we might want an unboxed representation
+    // even if there is only one large array.
     return true;
   }
 
   for (size_t i = 0; i < templateShape->slotSpan(); i++) {
     // We can't use an unboxed representation if e.g. all the objects have
     // a null value for one of the properties, as we can't decide what type
     // it is supposed to have.
     if (UnboxedTypeSize(properties[i].type) == 0) {