Bug 1233642 - Part 2: Self-host Array.prototype.concat. r=efaust,bholley
authorTooru Fujisawa <arai_a@mac.com>
Fri, 18 Dec 2015 17:02:27 +0900
changeset 292153 70e78d669f9de949dcbe972f74742045ca094fae
parent 292152 ef8f61469172c5f62bf990e1b38f5c25526ce3a0
child 292154 35b9afd414ed452fdfee91b89a6dc51de9e3868c
push id74764
push userarai_a@mac.com
push dateThu, 07 Apr 2016 10:49:15 +0000
treeherdermozilla-inbound@4d0f975a2311 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersefaust, bholley
bugs1233642
milestone48.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 1233642 - Part 2: Self-host Array.prototype.concat. r=efaust,bholley
js/src/builtin/Array.js
js/src/jit/BaselineIC.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/InlinableNatives.h
js/src/jit/IonBuilder.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/jit/shared/LIR-shared.h
js/src/jit/shared/LOpcodes-shared.h
js/src/js.msg
js/src/jsarray.cpp
js/src/jsarray.h
js/src/tests/ecma_3/Array/regress-322135-02.js
js/xpconnect/tests/chrome/test_xrayToJS.xul
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -864,8 +864,94 @@ function ArrayToString() {
     // Steps 3-4.
     var func = array.join;
 
     // Steps 5-6.
     if (!IsCallable(func))
         return callFunction(std_Object_toString, array);
     return callContentFunction(func, array);
 }
+
+// ES 2016 draft Mar 25, 2016 22.1.3.1.
+// Note: Array.prototype.concat.length is 1.
+function ArrayConcat(arg1) {
+    // Step 1.
+    var O = ToObject(this);
+
+    // Step 2.
+    var A = std_Array(0);
+
+    // Step 3.
+    var n = 0;
+
+    // Step 4 (implicit in |arguments|).
+
+    // Step 5.
+    var i = 0, argsLen = arguments.length;
+
+    // Step 5.a (first element).
+    var E = O;
+
+    var k, len;
+    while (true) {
+        // Steps 5.b-c.
+        // IsArray should be replaced with IsConcatSpreadable (bug 1041586).
+        if (IsArray(E)) {
+            // Step 5.c.ii.
+            len = ToLength(E.length);
+
+            // Step 5.c.iii.
+            if (n + len > MAX_NUMERIC_INDEX)
+                ThrowTypeError(JSMSG_TOO_LONG_ARRAY);
+
+            if (IsPackedArray(E)) {
+                // Step 5.c.i, 5.c.iv, and 5.c.iv.5.
+                for (k = 0; k < len; k++) {
+                    // Steps 5.c.iv.1-3.
+                    // IsPackedArray(E) ensures that |k in E| is always true.
+                    _DefineDataProperty(A, n, E[k]);
+
+                    // Step 5.c.iv.4.
+                    n++;
+                }
+            } else {
+                // Step 5.c.i, 5.c.iv, and 5.c.iv.5.
+                for (k = 0; k < len; k++) {
+                    // Steps 5.c.iv.1-3.
+                    if (k in E)
+                        _DefineDataProperty(A, n, E[k]);
+
+                    // Step 5.c.iv.4.
+                    n++;
+                }
+            }
+        } else {
+            // Step 5.d.i.
+            if (n >= MAX_NUMERIC_INDEX)
+                ThrowTypeError(JSMSG_TOO_LONG_ARRAY);
+
+            // Step 5.d.ii.
+            _DefineDataProperty(A, n, E);
+
+            // Step 5.d.iii.
+            n++;
+        }
+
+        if (i >= argsLen)
+            break;
+        // Step 5.a (subsequent elements).
+        E = arguments[i];
+        i++;
+    }
+
+    // Step 6.
+    A.length = n;
+
+    // Step 7.
+    return A;
+}
+
+function ArrayStaticConcat(arr, arg1) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.concat');
+    var args = callFunction(std_Array_slice, arguments, 1);
+    return callFunction(std_Function_apply, ArrayConcat, arr, args);
+}
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -5641,17 +5641,17 @@ GetTemplateObjectForNative(JSContext* cx
             // don't end up with a template whose structure might change later.
             res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, count, TenuredObject));
             if (!res)
                 return false;
             return true;
         }
     }
 
-    if (native == js::array_concat || native == js::array_slice) {
+    if (native == js::array_slice) {
         if (args.thisv().isObject()) {
             JSObject* obj = &args.thisv().toObject();
             if (!obj->isSingleton()) {
                 if (obj->group()->maybePreliminaryObjects()) {
                     *skipAttach = true;
                     return true;
                 }
                 res.set(NewFullyAllocatedArrayTryReuseGroup(cx, &args.thisv().toObject(), 0,
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -7971,65 +7971,16 @@ CodeGenerator::visitArrayPushT(LArrayPus
     ConstantOrRegister value;
     if (lir->value()->isConstant())
         value = ConstantOrRegister(lir->value()->toConstant()->toJSValue());
     else
         value = TypedOrValueRegister(lir->mir()->value()->type(), ToAnyRegister(lir->value()));
     emitArrayPush(lir, lir->mir(), obj, value, elementsTemp, length);
 }
 
-typedef JSObject* (*ArrayConcatDenseFn)(JSContext*, HandleObject, HandleObject, HandleObject);
-static const VMFunction ArrayConcatDenseInfo = FunctionInfo<ArrayConcatDenseFn>(ArrayConcatDense);
-
-void
-CodeGenerator::visitArrayConcat(LArrayConcat* lir)
-{
-    Register lhs = ToRegister(lir->lhs());
-    Register rhs = ToRegister(lir->rhs());
-    Register temp1 = ToRegister(lir->temp1());
-    Register temp2 = ToRegister(lir->temp2());
-
-    // If 'length == initializedLength' for both arrays we try to allocate an object
-    // inline and pass it to the stub. Else, we just pass nullptr and the stub falls
-    // back to a slow path.
-    Label fail, call;
-    if (lir->mir()->unboxedThis()) {
-        masm.load32(Address(lhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1);
-        masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1);
-        masm.branch32(Assembler::NotEqual, Address(lhs, UnboxedArrayObject::offsetOfLength()), temp1, &fail);
-    } else {
-        masm.loadPtr(Address(lhs, NativeObject::offsetOfElements()), temp1);
-        masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
-        masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
-    }
-    if (lir->mir()->unboxedArg()) {
-        masm.load32(Address(rhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1);
-        masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1);
-        masm.branch32(Assembler::NotEqual, Address(rhs, UnboxedArrayObject::offsetOfLength()), temp1, &fail);
-    } else {
-        masm.loadPtr(Address(rhs, NativeObject::offsetOfElements()), temp1);
-        masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
-        masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
-    }
-
-    // Try to allocate an object.
-    masm.createGCObject(temp1, temp2, lir->mir()->templateObj(), lir->mir()->initialHeap(), &fail);
-    masm.jump(&call);
-    {
-        masm.bind(&fail);
-        masm.movePtr(ImmPtr(nullptr), temp1);
-    }
-    masm.bind(&call);
-
-    pushArg(temp1);
-    pushArg(ToRegister(lir->rhs()));
-    pushArg(ToRegister(lir->lhs()));
-    callVM(ArrayConcatDenseInfo, lir);
-}
-
 typedef JSObject* (*ArraySliceDenseFn)(JSContext*, HandleObject, int32_t, int32_t, HandleObject);
 static const VMFunction ArraySliceDenseInfo = FunctionInfo<ArraySliceDenseFn>(array_slice_dense);
 
 void
 CodeGenerator::visitArraySlice(LArraySlice* lir)
 {
     Register object = ToRegister(lir->object());
     Register begin = ToRegister(lir->begin());
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -292,17 +292,16 @@ class CodeGenerator : public CodeGenerat
     void emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, Register obj,
                            Register elementsTemp, Register lengthTemp, TypedOrValueRegister out);
     void visitArrayPopShiftV(LArrayPopShiftV* lir);
     void visitArrayPopShiftT(LArrayPopShiftT* lir);
     void emitArrayPush(LInstruction* lir, const MArrayPush* mir, Register obj,
                        ConstantOrRegister value, Register elementsTemp, Register length);
     void visitArrayPushV(LArrayPushV* lir);
     void visitArrayPushT(LArrayPushT* lir);
-    void visitArrayConcat(LArrayConcat* lir);
     void visitArraySlice(LArraySlice* lir);
     void visitArrayJoin(LArrayJoin* lir);
     void visitLoadUnboxedScalar(LLoadUnboxedScalar* lir);
     void visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole* lir);
     void visitStoreUnboxedScalar(LStoreUnboxedScalar* lir);
     void visitStoreTypedArrayElementHole(LStoreTypedArrayElementHole* lir);
     void visitAtomicIsLockFree(LAtomicIsLockFree* lir);
     void visitGuardSharedTypedArray(LGuardSharedTypedArray* lir);
--- a/js/src/jit/InlinableNatives.h
+++ b/js/src/jit/InlinableNatives.h
@@ -9,17 +9,16 @@
 
 #define INLINABLE_NATIVE_LIST(_)    \
     _(Array)                        \
     _(ArrayIsArray)                 \
     _(ArrayJoin)                    \
     _(ArrayPop)                     \
     _(ArrayShift)                   \
     _(ArrayPush)                    \
-    _(ArrayConcat)                  \
     _(ArraySlice)                   \
     _(ArraySplice)                  \
                                     \
     _(AtomicsCompareExchange)       \
     _(AtomicsExchange)              \
     _(AtomicsLoad)                  \
     _(AtomicsStore)                 \
     _(AtomicsAdd)                   \
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -783,17 +783,16 @@ class IonBuilder
     // The known MIR type of getInlineReturnTypeSet.
     MIRType getInlineReturnType();
 
     // Array natives.
     InliningStatus inlineArray(CallInfo& callInfo);
     InliningStatus inlineArrayIsArray(CallInfo& callInfo);
     InliningStatus inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode);
     InliningStatus inlineArrayPush(CallInfo& callInfo);
-    InliningStatus inlineArrayConcat(CallInfo& callInfo);
     InliningStatus inlineArraySlice(CallInfo& callInfo);
     InliningStatus inlineArrayJoin(CallInfo& callInfo);
     InliningStatus inlineArraySplice(CallInfo& callInfo);
 
     // Math natives.
     InliningStatus inlineMathAbs(CallInfo& callInfo);
     InliningStatus inlineMathFloor(CallInfo& callInfo);
     InliningStatus inlineMathCeil(CallInfo& callInfo);
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3182,31 +3182,16 @@ LIRGenerator::visitArrayPush(MArrayPush*
         define(lir, ins);
         assignSafepoint(lir, ins);
         break;
       }
     }
 }
 
 void
-LIRGenerator::visitArrayConcat(MArrayConcat* ins)
-{
-    MOZ_ASSERT(ins->type() == MIRType_Object);
-    MOZ_ASSERT(ins->lhs()->type() == MIRType_Object);
-    MOZ_ASSERT(ins->rhs()->type() == MIRType_Object);
-
-    LArrayConcat* lir = new(alloc()) LArrayConcat(useFixed(ins->lhs(), CallTempReg1),
-                                                  useFixed(ins->rhs(), CallTempReg2),
-                                                  tempFixed(CallTempReg3),
-                                                  tempFixed(CallTempReg4));
-    defineReturn(lir, ins);
-    assignSafepoint(lir, ins);
-}
-
-void
 LIRGenerator::visitArraySlice(MArraySlice* ins)
 {
     MOZ_ASSERT(ins->type() == MIRType_Object);
     MOZ_ASSERT(ins->object()->type() == MIRType_Object);
     MOZ_ASSERT(ins->begin()->type() == MIRType_Int32);
     MOZ_ASSERT(ins->end()->type() == MIRType_Int32);
 
     LArraySlice* lir = new(alloc()) LArraySlice(useFixed(ins->object(), CallTempReg0),
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -216,17 +216,16 @@ class LIRGenerator : public LIRGenerator
     void visitStoreElement(MStoreElement* ins);
     void visitStoreElementHole(MStoreElementHole* ins);
     void visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull* ins);
     void visitStoreUnboxedString(MStoreUnboxedString* ins);
     void visitConvertUnboxedObjectToNative(MConvertUnboxedObjectToNative* ins);
     void visitEffectiveAddress(MEffectiveAddress* ins);
     void visitArrayPopShift(MArrayPopShift* ins);
     void visitArrayPush(MArrayPush* ins);
-    void visitArrayConcat(MArrayConcat* ins);
     void visitArraySlice(MArraySlice* ins);
     void visitArrayJoin(MArrayJoin* ins);
     void visitLoadUnboxedScalar(MLoadUnboxedScalar* ins);
     void visitLoadTypedArrayElementHole(MLoadTypedArrayElementHole* ins);
     void visitLoadTypedArrayElementStatic(MLoadTypedArrayElementStatic* ins);
     void visitStoreUnboxedScalar(MStoreUnboxedScalar* ins);
     void visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole* ins);
     void visitClampToUint8(MClampToUint8* ins);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -72,18 +72,16 @@ IonBuilder::inlineNativeCall(CallInfo& c
       case InlinableNative::ArrayJoin:
         return inlineArrayJoin(callInfo);
       case InlinableNative::ArrayPop:
         return inlineArrayPopShift(callInfo, MArrayPopShift::Pop);
       case InlinableNative::ArrayShift:
         return inlineArrayPopShift(callInfo, MArrayPopShift::Shift);
       case InlinableNative::ArrayPush:
         return inlineArrayPush(callInfo);
-      case InlinableNative::ArrayConcat:
-        return inlineArrayConcat(callInfo);
       case InlinableNative::ArraySlice:
         return inlineArraySlice(callInfo);
       case InlinableNative::ArraySplice:
         return inlineArraySplice(callInfo);
 
       // Atomic natives.
       case InlinableNative::AtomicsCompareExchange:
         return inlineAtomicsCompareExchange(callInfo);
@@ -761,138 +759,16 @@ IonBuilder::inlineArrayPush(CallInfo& ca
     current->push(ins);
 
     if (!resumeAfter(ins))
         return InliningStatus_Error;
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineArrayConcat(CallInfo& callInfo)
-{
-    if (callInfo.argc() != 1 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
-
-    MDefinition* thisArg = convertUnboxedObjects(callInfo.thisArg());
-    MDefinition* objArg = convertUnboxedObjects(callInfo.getArg(0));
-
-    // Ensure |this|, argument and result are objects.
-    if (getInlineReturnType() != MIRType_Object)
-        return InliningStatus_NotInlined;
-    if (thisArg->type() != MIRType_Object)
-        return InliningStatus_NotInlined;
-    if (objArg->type() != MIRType_Object)
-        return InliningStatus_NotInlined;
-
-    // |this| and the argument must be dense arrays.
-    TemporaryTypeSet* thisTypes = thisArg->resultTypeSet();
-    TemporaryTypeSet* argTypes = objArg->resultTypeSet();
-    if (!thisTypes || !argTypes)
-        return InliningStatus_NotInlined;
-
-    const Class* thisClasp = thisTypes->getKnownClass(constraints());
-    if (thisClasp != &ArrayObject::class_ && thisClasp != &UnboxedArrayObject::class_)
-        return InliningStatus_NotInlined;
-    bool unboxedThis = (thisClasp == &UnboxedArrayObject::class_);
-    if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
-                                  OBJECT_FLAG_LENGTH_OVERFLOW))
-    {
-        trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
-        return InliningStatus_NotInlined;
-    }
-
-    const Class* argClasp = argTypes->getKnownClass(constraints());
-    if (argClasp != &ArrayObject::class_ && argClasp != &UnboxedArrayObject::class_)
-        return InliningStatus_NotInlined;
-    bool unboxedArg = (argClasp == &UnboxedArrayObject::class_);
-    if (argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
-                                 OBJECT_FLAG_LENGTH_OVERFLOW))
-    {
-        trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
-        return InliningStatus_NotInlined;
-    }
-
-    // Watch out for indexed properties on the prototype.
-    if (ArrayPrototypeHasIndexedProperty(this, script())) {
-        trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
-        return InliningStatus_NotInlined;
-    }
-
-    // Require the 'this' types to have a specific type matching the current
-    // global, so we can create the result object inline.
-    if (thisTypes->getObjectCount() != 1)
-        return InliningStatus_NotInlined;
-
-    ObjectGroup* thisGroup = thisTypes->getGroup(0);
-    if (!thisGroup)
-        return InliningStatus_NotInlined;
-    TypeSet::ObjectKey* thisKey = TypeSet::ObjectKey::get(thisGroup);
-    if (thisKey->unknownProperties())
-        return InliningStatus_NotInlined;
-
-    // Don't inline if 'this' is packed and the argument may not be packed
-    // (the result array will reuse the 'this' type).
-    if (!thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED) &&
-        argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED))
-    {
-        trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
-        return InliningStatus_NotInlined;
-    }
-
-    // Constraints modeling this concat have not been generated by inference,
-    // so check that type information already reflects possible side effects of
-    // this call.
-    HeapTypeSetKey thisElemTypes = thisKey->property(JSID_VOID);
-
-    TemporaryTypeSet* resTypes = getInlineReturnTypeSet();
-    if (!resTypes->hasType(TypeSet::ObjectType(thisKey)))
-        return InliningStatus_NotInlined;
-
-    for (unsigned i = 0; i < argTypes->getObjectCount(); i++) {
-        TypeSet::ObjectKey* key = argTypes->getObject(i);
-        if (!key)
-            continue;
-
-        if (key->unknownProperties())
-            return InliningStatus_NotInlined;
-
-        HeapTypeSetKey elemTypes = key->property(JSID_VOID);
-        if (!elemTypes.knownSubset(constraints(), thisElemTypes))
-            return InliningStatus_NotInlined;
-
-        if (thisGroup->clasp() == &UnboxedArrayObject::class_ &&
-            !CanStoreUnboxedType(alloc(), thisGroup->unboxedLayout().elementType(),
-                                 MIRType_Value, elemTypes.maybeTypes()))
-        {
-            return InliningStatus_NotInlined;
-        }
-    }
-
-    // Inline the call.
-    JSObject* templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat);
-    if (!templateObj || templateObj->group() != thisGroup)
-        return InliningStatus_NotInlined;
-
-    callInfo.setImplicitlyUsedUnchecked();
-
-    MArrayConcat* ins = MArrayConcat::New(alloc(), constraints(), thisArg, objArg,
-                                          templateObj,
-                                          templateObj->group()->initialHeap(constraints()),
-                                          unboxedThis, unboxedArg);
-    current->add(ins);
-    current->push(ins);
-
-    if (!resumeAfter(ins))
-        return InliningStatus_Error;
-    return InliningStatus_Inlined;
-}
-
-IonBuilder::InliningStatus
 IonBuilder::inlineArraySlice(CallInfo& callInfo)
 {
     if (callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
     MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -9834,76 +9834,16 @@ class MArrayPush
         return AliasSet::Store(AliasSet::ObjectFields |
                                AliasSet::BoxedOrUnboxedElements(unboxedType()));
     }
     void computeRange(TempAllocator& alloc) override;
 
     ALLOW_CLONE(MArrayPush)
 };
 
-// Array.prototype.concat on two dense arrays.
-class MArrayConcat
-  : public MBinaryInstruction,
-    public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
-{
-    CompilerObject templateObj_;
-    gc::InitialHeap initialHeap_;
-    bool unboxedThis_, unboxedArg_;
-
-    MArrayConcat(CompilerConstraintList* constraints, MDefinition* lhs, MDefinition* rhs,
-                 JSObject* templateObj, gc::InitialHeap initialHeap,
-                 bool unboxedThis, bool unboxedArg)
-      : MBinaryInstruction(lhs, rhs),
-        templateObj_(templateObj),
-        initialHeap_(initialHeap),
-        unboxedThis_(unboxedThis),
-        unboxedArg_(unboxedArg)
-    {
-        setResultType(MIRType_Object);
-        setResultTypeSet(MakeSingletonTypeSet(constraints, templateObj));
-    }
-
-  public:
-    INSTRUCTION_HEADER(ArrayConcat)
-
-    static MArrayConcat* New(TempAllocator& alloc, CompilerConstraintList* constraints,
-                             MDefinition* lhs, MDefinition* rhs,
-                             JSObject* templateObj, gc::InitialHeap initialHeap,
-                             bool unboxedThis, bool unboxedArg)
-    {
-        return new(alloc) MArrayConcat(constraints, lhs, rhs, templateObj,
-                                       initialHeap, unboxedThis, unboxedArg);
-    }
-
-    JSObject* templateObj() const {
-        return templateObj_;
-    }
-
-    gc::InitialHeap initialHeap() const {
-        return initialHeap_;
-    }
-
-    bool unboxedThis() const {
-        return unboxedThis_;
-    }
-
-    bool unboxedArg() const {
-        return unboxedArg_;
-    }
-
-    AliasSet getAliasSet() const override {
-        return AliasSet::Store(AliasSet::BoxedOrUnboxedElements(unboxedThis() ? JSVAL_TYPE_INT32 : JSVAL_TYPE_MAGIC) |
-                               AliasSet::BoxedOrUnboxedElements(unboxedArg() ? JSVAL_TYPE_INT32 : JSVAL_TYPE_MAGIC) |
-                               AliasSet::ObjectFields);
-    }
-    bool possiblyCalls() const override {
-        return true;
-    }
-};
-
 // Array.prototype.slice on a dense array.
 class MArraySlice
   : public MTernaryInstruction,
     public Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2>>::Data
 {
     CompilerObject templateObj_;
     gc::InitialHeap initialHeap_;
     JSValueType unboxedType_;
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -205,17 +205,16 @@ namespace jit {
     _(StoreElement)                                                         \
     _(StoreElementHole)                                                     \
     _(StoreUnboxedScalar)                                                   \
     _(StoreUnboxedObjectOrNull)                                             \
     _(StoreUnboxedString)                                                   \
     _(ConvertUnboxedObjectToNative)                                         \
     _(ArrayPopShift)                                                        \
     _(ArrayPush)                                                            \
-    _(ArrayConcat)                                                          \
     _(ArraySlice)                                                           \
     _(ArrayJoin)                                                            \
     _(LoadTypedArrayElementHole)                                            \
     _(LoadTypedArrayElementStatic)                                          \
     _(StoreTypedArrayElementHole)                                           \
     _(StoreTypedArrayElementStatic)                                         \
     _(AtomicIsLockFree)                                                     \
     _(GuardSharedTypedArray)                                                \
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -366,35 +366,16 @@ ArrayShiftDense(JSContext* cx, HandleObj
     // If the result is |undefined|, the array was probably empty and we
     // have to monitor the return value.
     rval.set(argv[0]);
     if (rval.isUndefined())
         TypeScript::Monitor(cx, rval);
     return true;
 }
 
-JSObject*
-ArrayConcatDense(JSContext* cx, HandleObject obj1, HandleObject obj2, HandleObject objRes)
-{
-    if (objRes) {
-        // Fast path if we managed to allocate an object inline.
-        if (!js::array_concat_dense(cx, obj1, obj2, objRes))
-            return nullptr;
-        return objRes;
-    }
-
-    JS::AutoValueArray<3> argv(cx);
-    argv[0].setUndefined();
-    argv[1].setObject(*obj1);
-    argv[2].setObject(*obj2);
-    if (!js::array_concat(cx, 1, argv.begin()))
-        return nullptr;
-    return &argv[0].toObject();
-}
-
 JSString*
 ArrayJoin(JSContext* cx, HandleObject array, HandleString sep)
 {
     JS::AutoValueArray<3> argv(cx);
     argv[0].setUndefined();
     argv[1].setObject(*array);
     argv[2].setString(sep);
     if (!js::array_join(cx, 1, argv.begin()))
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -608,17 +608,16 @@ bool GreaterThan(JSContext* cx, MutableH
 bool GreaterThanOrEqual(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res);
 
 template<bool Equal>
 bool StringsEqual(JSContext* cx, HandleString left, HandleString right, bool* res);
 
 bool ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval);
 bool ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length);
 bool ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval);
-JSObject* ArrayConcatDense(JSContext* cx, HandleObject obj1, HandleObject obj2, HandleObject res);
 JSString* ArrayJoin(JSContext* cx, HandleObject array, HandleString sep);
 
 bool CharCodeAt(JSContext* cx, HandleString str, int32_t index, uint32_t* code);
 JSFlatString* StringFromCharCode(JSContext* cx, int32_t code);
 
 bool SetProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, HandleValue value,
                  bool strict, jsbytecode* pc);
 
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -5446,45 +5446,16 @@ class LArrayPushT : public LInstructionH
     const LAllocation* value() {
         return getOperand(1);
     }
     const LDefinition* temp() {
         return getTemp(0);
     }
 };
 
-class LArrayConcat : public LCallInstructionHelper<1, 2, 2>
-{
-  public:
-    LIR_HEADER(ArrayConcat)
-
-    LArrayConcat(const LAllocation& lhs, const LAllocation& rhs,
-                 const LDefinition& temp1, const LDefinition& temp2) {
-        setOperand(0, lhs);
-        setOperand(1, rhs);
-        setTemp(0, temp1);
-        setTemp(1, temp2);
-    }
-    const MArrayConcat* mir() const {
-        return mir_->toArrayConcat();
-    }
-    const LAllocation* lhs() {
-        return getOperand(0);
-    }
-    const LAllocation* rhs() {
-        return getOperand(1);
-    }
-    const LDefinition* temp1() {
-        return getTemp(0);
-    }
-    const LDefinition* temp2() {
-        return getTemp(1);
-    }
-};
-
 class LArraySlice : public LCallInstructionHelper<1, 3, 2>
 {
   public:
     LIR_HEADER(ArraySlice)
 
     LArraySlice(const LAllocation& obj, const LAllocation& begin, const LAllocation& end,
                 const LDefinition& temp1, const LDefinition& temp2) {
         setOperand(0, obj);
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -260,17 +260,16 @@
     _(StoreElementT)                \
     _(StoreUnboxedScalar)           \
     _(StoreUnboxedPointer)          \
     _(ConvertUnboxedObjectToNative) \
     _(ArrayPopShiftV)               \
     _(ArrayPopShiftT)               \
     _(ArrayPushV)                   \
     _(ArrayPushT)                   \
-    _(ArrayConcat)                  \
     _(ArraySlice)                   \
     _(ArrayJoin)                    \
     _(StoreElementHoleV)            \
     _(StoreElementHoleT)            \
     _(LoadTypedArrayElementHole)    \
     _(LoadTypedArrayElementStatic)  \
     _(StoreTypedArrayElementHole)   \
     _(StoreTypedArrayElementStatic) \
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -469,16 +469,19 @@ MSG_DEF(JSMSG_INVALID_PROTOTYPE,       0
 MSG_DEF(JSMSG_TYPEDOBJECT_BAD_ARGS,    0, JSEXN_TYPEERR, "invalid arguments")
 MSG_DEF(JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 0, JSEXN_TYPEERR, "handle unattached")
 MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS, 0, JSEXN_RANGEERR, "invalid field descriptor")
 MSG_DEF(JSMSG_TYPEDOBJECT_TOO_BIG,     0, JSEXN_ERR, "Type is too large to allocate")
 MSG_DEF(JSMSG_SIMD_FAILED_CONVERSION,  0, JSEXN_RANGEERR, "SIMD conversion loses precision")
 MSG_DEF(JSMSG_SIMD_TO_NUMBER,          0, JSEXN_TYPEERR, "can't convert SIMD value to number")
 
+// Array
+MSG_DEF(JSMSG_TOO_LONG_ARRAY,         0, JSEXN_TYPEERR, "Too long array")
+
 // Typed array
 MSG_DEF(JSMSG_BAD_INDEX,               0, JSEXN_RANGEERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_NON_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected ArrayBuffer, but species constructor returned non-ArrayBuffer")
 MSG_DEF(JSMSG_SAME_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected different ArrayBuffer, but species constructor returned same ArrayBuffer")
 MSG_DEF(JSMSG_SHORT_ARRAY_BUFFER_RETURNED, 2, JSEXN_TYPEERR, "expected ArrayBuffer with at least {0} bytes, but species constructor returns ArrayBuffer with {1} bytes")
 MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS,    0, JSEXN_TYPEERR, "invalid arguments")
 MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1, JSEXN_RANGEERR, "argument {0} must be >= 0")
 MSG_DEF(JSMSG_TYPED_ARRAY_DETACHED,    0, JSEXN_TYPEERR, "attempting to access detached ArrayBuffer")
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2533,194 +2533,16 @@ js::array_splice_impl(JSContext* cx, uns
 
     /* Step 17. */
     if (returnValueIsUsed)
         args.rval().setObject(*arr);
 
     return true;
 }
 
-template <JSValueType TypeOne, JSValueType TypeTwo>
-DenseElementResult
-ArrayConcatDenseKernel(JSContext* cx, JSObject* obj1, JSObject* obj2, JSObject* result)
-{
-    uint32_t initlen1 = GetBoxedOrUnboxedInitializedLength<TypeOne>(obj1);
-    MOZ_ASSERT(initlen1 == GetAnyBoxedOrUnboxedArrayLength(obj1));
-
-    uint32_t initlen2 = GetBoxedOrUnboxedInitializedLength<TypeTwo>(obj2);
-    MOZ_ASSERT(initlen2 == GetAnyBoxedOrUnboxedArrayLength(obj2));
-
-    /* No overflow here due to nelements limit. */
-    uint32_t len = initlen1 + initlen2;
-
-    MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<TypeOne>(result) == 0);
-
-    DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements<TypeOne>(cx, result, len);
-    if (rv != DenseElementResult::Success)
-        return rv;
-
-    CopyBoxedOrUnboxedDenseElements<TypeOne, TypeOne>(cx, result, obj1, 0, 0, initlen1);
-    CopyBoxedOrUnboxedDenseElements<TypeOne, TypeTwo>(cx, result, obj2, initlen1, 0, initlen2);
-
-    SetAnyBoxedOrUnboxedArrayLength(cx, result, len);
-    return DenseElementResult::Success;
-}
-
-DefineBoxedOrUnboxedFunctorPair4(ArrayConcatDenseKernel,
-                                 JSContext*, JSObject*, JSObject*, JSObject*);
-
-bool
-js::array_concat_dense(JSContext* cx, HandleObject obj1, HandleObject obj2,
-                       HandleObject result)
-{
-    ArrayConcatDenseKernelFunctor functor(cx, obj1, obj2, result);
-    DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, obj1, obj2);
-    MOZ_ASSERT(rv != DenseElementResult::Incomplete);
-    return rv == DenseElementResult::Success;
-}
-
-/*
- * Python-esque sequence operations.
- */
-bool
-js::array_concat(JSContext* cx, unsigned argc, Value* vp)
-{
-    AutoSPSEntry pseudoFrame(cx->runtime(), "Array.prototype.concat");
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
-    Value* p = args.array() - 1;
-
-    /* Create a new Array object and root it using *vp. */
-    RootedObject aobj(cx, ToObject(cx, args.thisv()));
-    if (!aobj)
-        return false;
-
-    RootedObject narr(cx);
-    uint32_t length;
-    bool isArray;
-    if (!IsArray(cx, aobj, &isArray))
-        return false;
-    if (isArray && !ObjectMayHaveExtraIndexedProperties(aobj)) {
-        if (!GetLengthProperty(cx, aobj, &length))
-            return false;
-
-        size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(aobj);
-        narr = NewFullyAllocatedArrayTryReuseGroup(cx, aobj, initlen);
-        if (!narr)
-            return false;
-        CopyAnyBoxedOrUnboxedDenseElements(cx, narr, aobj, 0, 0, initlen);
-        SetAnyBoxedOrUnboxedArrayLength(cx, narr, length);
-
-        args.rval().setObject(*narr);
-        if (argc == 0)
-            return true;
-        argc--;
-        p++;
-
-        if (length == initlen) {
-            while (argc) {
-                HandleValue v = HandleValue::fromMarkedLocation(p);
-                if (!v.isObject())
-                    break;
-                RootedObject obj(cx, &v.toObject());
-
-                // This should be IsConcatSpreadable
-                bool isArray;
-                if (!IsArray(cx, obj, &isArray))
-                    return false;
-                if (!isArray || ObjectMayHaveExtraIndexedProperties(obj))
-                    break;
-
-                uint32_t argLength;
-                if (!GetLengthProperty(cx, obj, &argLength))
-                    return false;
-
-                initlen = GetAnyBoxedOrUnboxedInitializedLength(obj);
-                if (argLength != initlen)
-                    break;
-
-                DenseElementResult result =
-                    EnsureAnyBoxedOrUnboxedDenseElements(cx, narr, length + argLength);
-                if (result == DenseElementResult::Failure)
-                    return false;
-                if (result == DenseElementResult::Incomplete)
-                    break;
-
-                SetAnyBoxedOrUnboxedInitializedLength(cx, narr, length + argLength);
-
-                bool success = true;
-                for (size_t i = 0; i < initlen; i++) {
-                    Value v = GetAnyBoxedOrUnboxedDenseElement(obj, i);
-                    if (!InitAnyBoxedOrUnboxedDenseElement(cx, narr, length + i, v)) {
-                        success = false;
-                        break;
-                    }
-                }
-                if (!success) {
-                    SetAnyBoxedOrUnboxedInitializedLength(cx, narr, length);
-                    break;
-                }
-
-                length += argLength;
-                SetAnyBoxedOrUnboxedArrayLength(cx, narr, length);
-
-                argc--;
-                p++;
-            }
-        }
-    } else {
-        narr = NewFullyAllocatedArrayTryReuseGroup(cx, aobj, 0);
-        if (!narr)
-            return false;
-        args.rval().setObject(*narr);
-        length = 0;
-    }
-
-    /* Loop over [0, argc] to concat args into narr, expanding all Arrays. */
-    for (unsigned i = 0; i <= argc; i++) {
-        if (!CheckForInterrupt(cx))
-            return false;
-        HandleValue v = HandleValue::fromMarkedLocation(&p[i]);
-        if (v.isObject()) {
-            RootedObject obj(cx, &v.toObject());
-            // This should be IsConcatSpreadable
-            bool isArray;
-            if (!IsArray(cx, obj, &isArray))
-                return false;
-            if (isArray) {
-                uint32_t alength;
-                if (!GetLengthProperty(cx, obj, &alength))
-                    return false;
-                RootedValue tmp(cx);
-                for (uint32_t slot = 0; slot < alength; slot++) {
-                    bool hole;
-                    if (!CheckForInterrupt(cx) || !GetElement(cx, obj, slot, &hole, &tmp))
-                        return false;
-
-                    /*
-                     * Per ECMA 262, 15.4.4.4, step 9, ignore nonexistent
-                     * properties.
-                     */
-                    if (!hole && !SetArrayElement(cx, narr, length + slot, tmp))
-                        return false;
-                }
-                length += alength;
-                continue;
-            }
-        }
-
-        if (!SetArrayElement(cx, narr, length, v))
-            return false;
-        length++;
-    }
-
-    return SetLengthProperty(cx, narr, length);
-}
-
 struct SortComparatorIndexes
 {
     bool operator()(uint32_t a, uint32_t b, bool* lessOrEqualp) {
         *lessOrEqualp = (a <= b);
         return true;
     }
 };
 
@@ -3092,17 +2914,17 @@ static const JSFunctionSpec array_method
     JS_FN("sort",               array_sort,         1,JSFUN_GENERIC_NATIVE),
     JS_INLINABLE_FN("push",     array_push,         1,JSFUN_GENERIC_NATIVE, ArrayPush),
     JS_INLINABLE_FN("pop",      array_pop,          0,JSFUN_GENERIC_NATIVE, ArrayPop),
     JS_INLINABLE_FN("shift",    array_shift,        0,JSFUN_GENERIC_NATIVE, ArrayShift),
     JS_FN("unshift",            array_unshift,      1,JSFUN_GENERIC_NATIVE),
     JS_INLINABLE_FN("splice",   array_splice,       2,JSFUN_GENERIC_NATIVE, ArraySplice),
 
     /* Pythonic sequence methods. */
-    JS_INLINABLE_FN("concat",   array_concat,       1,JSFUN_GENERIC_NATIVE, ArrayConcat),
+    JS_SELF_HOSTED_FN("concat",      "ArrayConcat",      1,0),
     JS_INLINABLE_FN("slice",    array_slice,        2,JSFUN_GENERIC_NATIVE, ArraySlice),
 
     JS_SELF_HOSTED_FN("lastIndexOf", "ArrayLastIndexOf", 1,0),
     JS_SELF_HOSTED_FN("indexOf",     "ArrayIndexOf",     1,0),
     JS_SELF_HOSTED_FN("forEach",     "ArrayForEach",     1,0),
     JS_SELF_HOSTED_FN("map",         "ArrayMap",         1,0),
     JS_SELF_HOSTED_FN("filter",      "ArrayFilter",      1,0),
     JS_SELF_HOSTED_FN("reduce",      "ArrayReduce",      1,0),
@@ -3124,16 +2946,17 @@ static const JSFunctionSpec array_method
 
     /* ES7 additions */
     JS_SELF_HOSTED_FN("includes",    "ArrayIncludes",    2,0),
     JS_FS_END
 };
 
 static const JSFunctionSpec array_static_methods[] = {
     JS_INLINABLE_FN("isArray",       array_isArray,        1,0, ArrayIsArray),
+    JS_SELF_HOSTED_FN("concat",      "ArrayStaticConcat", 2,0),
     JS_SELF_HOSTED_FN("lastIndexOf", "ArrayStaticLastIndexOf", 2,0),
     JS_SELF_HOSTED_FN("indexOf",     "ArrayStaticIndexOf", 2,0),
     JS_SELF_HOSTED_FN("forEach",     "ArrayStaticForEach", 2,0),
     JS_SELF_HOSTED_FN("map",         "ArrayStaticMap",   2,0),
     JS_SELF_HOSTED_FN("filter",      "ArrayStaticFilter", 2,0),
     JS_SELF_HOSTED_FN("every",       "ArrayStaticEvery", 2,0),
     JS_SELF_HOSTED_FN("some",        "ArrayStaticSome",  2,0),
     JS_SELF_HOSTED_FN("reduce",      "ArrayStaticReduce", 2,0),
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -162,23 +162,16 @@ array_push(JSContext* cx, unsigned argc,
 
 extern bool
 array_pop(JSContext* cx, unsigned argc, js::Value* vp);
 
 extern bool
 array_splice_impl(JSContext* cx, unsigned argc, js::Value* vp, bool pop);
 
 extern bool
-array_concat(JSContext* cx, unsigned argc, js::Value* vp);
-
-extern bool
-array_concat_dense(JSContext* cx, HandleObject arr1, HandleObject arr2,
-                   HandleObject result);
-
-extern bool
 array_join(JSContext* cx, unsigned argc, js::Value* vp);
 
 extern void
 ArrayShiftMoveElements(JSObject* obj);
 
 extern bool
 array_shift(JSContext* cx, unsigned argc, js::Value* vp);
 
--- a/js/src/tests/ecma_3/Array/regress-322135-02.js
+++ b/js/src/tests/ecma_3/Array/regress-322135-02.js
@@ -1,8 +1,9 @@
+// |reftest| skip -- slow (bug 1234947)
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* 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/. */
 
 //-----------------------------------------------------------------------------
 var BUGNUMBER = 322135;
 var summary = 'Array.prototype.concat on Array with length 2^32-1';
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -493,17 +493,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     // inconvenient (as it's a data property, unsupported by ClassSpec) and
     // low value.
     let propsToSkip = ['length', Symbol.unscopables];
 
     // On the constructor, we want to skip all the non-standard "generic"
     // functions.  We're trying to remove them anyway; no point doing extra work
     // to expose them over Xrays.
     let ctorPropsToSkip = ["join", "reverse", "sort", "push", "pop", "shift",
-                           "unshift", "splice", "concat", "slice"];
+                           "unshift", "splice", "slice"];
     testXray('Array', new iwin.Array(20), new iwin.Array(), propsToSkip,
              ctorPropsToSkip);
 
     let symbolProps = '';
     uniqueSymbol = iwin.eval('var uniqueSymbol = Symbol("uniqueSymbol"); uniqueSymbol');
     symbolProps = `trickyArray[uniqueSymbol] = 43;
                    trickyArray[Symbol.for("registrySymbolProp")] = 44;`;
     var trickyArray =