Version 7.0.193.1 (cherry-pick)

Merged 38b1d20d8a7e60b478142098054dc430859c55d7

Revert "[builtins] Enable Torque Array.prototype.splice"

R=yangguo@chromium.org

Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng;luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: Ic60b12469781f6d39cc297a9454fb39db1a98a38
Reviewed-on: https://chromium-review.googlesource.com/1168492
Reviewed-by: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/7.0.193@{#2}
Cr-Branched-From: d0934c4fc244255056197d5f5b1373bd0f090acc-refs/heads/master@{#54988}
diff --git a/BUILD.gn b/BUILD.gn
index 11ddf5e..87ba7d1 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -872,7 +872,6 @@
   "src/builtins/array.tq",
   "src/builtins/array-foreach.tq",
   "src/builtins/array-sort.tq",
-  "src/builtins/array-splice.tq",
   "src/builtins/typed-array.tq",
   "src/builtins/data-view.tq",
   "test/torque/test-torque.tq",
diff --git a/include/v8-version.h b/include/v8-version.h
index cce457b..4caa14c 100644
--- a/include/v8-version.h
+++ b/include/v8-version.h
@@ -11,7 +11,7 @@
 #define V8_MAJOR_VERSION 7
 #define V8_MINOR_VERSION 0
 #define V8_BUILD_NUMBER 193
-#define V8_PATCH_LEVEL 0
+#define V8_PATCH_LEVEL 1
 
 // Use 1 for candidates and 0 otherwise.
 // (Boolean macro values are not supported by all preprocessors.)
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index 4dde85c..0d0c4c0 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -1738,8 +1738,13 @@
                           1, false);
     SimpleInstallFunction(isolate_, proto, "slice",
                           Builtins::kArrayPrototypeSlice, 2, false);
-    SimpleInstallFunction(isolate_, proto, "splice", Builtins::kArraySplice, 2,
-                          false);
+    if (FLAG_enable_experimental_builtins) {
+      SimpleInstallFunction(isolate_, proto, "splice",
+                            Builtins::kArraySpliceTorque, 2, false);
+    } else {
+      SimpleInstallFunction(isolate_, proto, "splice", Builtins::kArraySplice,
+                            2, false);
+    }
     SimpleInstallFunction(isolate_, proto, "includes", Builtins::kArrayIncludes,
                           1, false);
     SimpleInstallFunction(isolate_, proto, "indexOf", Builtins::kArrayIndexOf,
diff --git a/src/builtins/array-foreach.tq b/src/builtins/array-foreach.tq
index c00dc84..3ef8f51 100644
--- a/src/builtins/array-foreach.tq
+++ b/src/builtins/array-foreach.tq
@@ -61,10 +61,6 @@
       let callbackfn: Callable = cast<Callable>(callback) otherwise Unexpected;
       let k: Smi = cast<Smi>(initialK) otherwise Unexpected;
       let number_length: Number = cast<Number>(length) otherwise Unexpected;
-      // The unsafe cast is safe because all continuation points in forEach are
-      // after the ToObject(O) call that ensures we are dealing with a
-      // JSReceiver.
-      let jsreceiver: JSReceiver = unsafe_cast<JSReceiver>(object);
 
       return ArrayForEachTorqueContinuation(
           context, receiver, number_length, callbackfn, thisArg, k);
diff --git a/src/builtins/array-splice.tq b/src/builtins/array-splice.tq
deleted file mode 100644
index c9f964e..0000000
--- a/src/builtins/array-splice.tq
+++ /dev/null
@@ -1,384 +0,0 @@
-// Copyright 2018 the V8 project authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module array {
-  macro FastSplice<FixedArrayType : type, ElementType : type>(
-      args: constexpr Arguments, a: JSArray, length: Smi, newLength: Smi,
-      lengthDelta: Smi, actualStart: Smi, insertCount: Smi,
-      actualDeleteCount: Smi): void labels Bailout {
-    let elements: FixedArrayType = unsafe_cast<FixedArrayType>(a.elements);
-    let elementsMap: Map = elements.map;
-
-    // If the spliced array is larger then the
-    // source array, then allocate a new FixedArrayType to hold the result.
-    let newElements: FixedArrayType = elements;
-    if (elementsMap == kCOWMap || lengthDelta > 0) {
-      newElements = ExtractFixedArray(elements, 0, actualStart, newLength);
-      newElements.map = elementsMap;
-      a.elements = newElements;
-    }
-
-    // Copy over inserted elements.
-    let k: Smi = actualStart;
-    if (insertCount > 0) {
-      for (let e: Object of args [2: ]) {
-        // The argument elements were already validated to be an appropriate
-        // {ElementType} to store in {FixedArrayType}.
-        newElements[k++] = unsafe_cast<ElementType>(e);
-      }
-    }
-
-    // Copy over elements after deleted elements.
-    let count: Smi = length - actualStart - actualDeleteCount;
-    while (count > 0) {
-      CopyArrayElement(elements, newElements, k - lengthDelta, k);
-      k++;
-      count--;
-    }
-
-    // Fill rest of spliced FixedArray with the hole, but only if the
-    // destination FixedArray is the original array's, since otherwise the array
-    // is pre-filled with holes.
-    if (elements == newElements) {
-      let limit: Smi = elements.length;
-      while (k < limit) {
-        StoreArrayHole(newElements, k);
-        k++;
-      }
-    }
-
-    // Update the array's length after all the FixedArray shuffling is done.
-    a.length = newLength;
-  }
-
-  macro FastArraySplice(
-      context: Context, args: constexpr Arguments, o: JSReceiver,
-      originalLengthNumber: Number, actualStartNumber: Number, insertCount: Smi,
-      actualDeleteCountNumber: Number): Object
-  labels Bailout {
-    let originalLength: Smi = cast<Smi>(originalLengthNumber) otherwise Bailout;
-    let actualStart: Smi = cast<Smi>(actualStartNumber) otherwise Bailout;
-    let actualDeleteCount: Smi =
-        cast<Smi>(actualDeleteCountNumber) otherwise Bailout;
-    let lengthDelta: Smi = insertCount - actualDeleteCount;
-    let newLength: Smi = originalLength + lengthDelta;
-
-    let a: JSArray = cast<JSArray>(o) otherwise Bailout;
-
-    let map: Map = a.map;
-    if (!IsPrototypeInitialArrayPrototype(context, map)) goto Bailout;
-    if (IsNoElementsProtectorCellInvalid()) goto Bailout;
-    if (IsArraySpeciesProtectorCellInvalid()) goto Bailout;
-
-    // Fast path only works on fast elements kind and with writable length.
-    let elementsKind: ElementsKind = EnsureArrayPushable(map) otherwise Bailout;
-    if (!IsFastElementsKind(elementsKind)) goto Bailout;
-
-    let oldElementsKind: ElementsKind = elementsKind;
-    for (let e: Object of args [2: ]) {
-      if (IsFastSmiElementsKind(elementsKind)) {
-        if (TaggedIsNotSmi(e)) {
-          let heapObject: HeapObject = unsafe_cast<HeapObject>(e);
-          elementsKind = IsHeapNumber(heapObject) ?
-              AllowDoubleElements(elementsKind) :
-              AllowNonNumberElements(elementsKind);
-        }
-      } else if (IsDoubleElementsKind(elementsKind)) {
-        if (!IsNumber(e)) {
-          elementsKind = AllowNonNumberElements(elementsKind);
-        }
-      }
-    }
-
-    if (elementsKind != oldElementsKind) {
-      let smi_elements_kind: Smi = convert<Smi>(convert<int32>(elementsKind));
-      TransitionElementsKindWithKind(context, a, smi_elements_kind);
-    }
-
-    // Make sure that the length hasn't been changed by side-effect.
-    let length: Smi = cast<Smi>(a.length) otherwise Bailout;
-    if (originalLength != length) goto Bailout;
-
-    let deletedResult: JSArray =
-        ExtractFastJSArray(context, a, actualStart, actualDeleteCount);
-
-    if (newLength == 0) {
-      a.elements = kEmptyFixedArray;
-      a.length = 0;
-      return deletedResult;
-    }
-
-    if (IsFastSmiOrTaggedElementsKind(elementsKind)) {
-      FastSplice<FixedArray, Object>(
-          args, a, length, newLength, lengthDelta, actualStart, insertCount,
-          actualDeleteCount) otherwise Bailout;
-    } else {
-      FastSplice<FixedDoubleArray, Number>(
-          args, a, length, newLength, lengthDelta, actualStart, insertCount,
-          actualDeleteCount) otherwise Bailout;
-    }
-
-    return deletedResult;
-  }
-
-  macro FillDeletedElementsArray(
-      context: Context, o: JSReceiver, actualStart: Number,
-      actualDeleteCount: Number, a: JSReceiver): Object {
-    // 10. Let k be 0.
-    let k: Number = 0;
-
-    // 11. Repeat, while k < actualDeleteCount
-    while (k < actualDeleteCount) {
-      // a. Let from be ! ToString(actualStart + k).
-      let from: Number = actualStart + k;
-
-      // b. Let fromPresent be ? HasProperty(O, from).
-      let fromPresent: Boolean = TorqueHasProperty(context, o, from);
-
-      // c. If fromPresent is true, then
-      if (fromPresent == True) {
-        // i. Let fromValue be ? Get(O, from).
-        let fromValue: Object = GetProperty(context, o, from);
-
-        // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(k), fromValue).
-        CreateDataProperty(context, a, k, fromValue);
-      }
-
-      // d. Increment k by 1.
-      k++;
-    }
-    // 12. Perform ? Set(A, "length", actualDeleteCount, true).
-    SetProperty(context, a, 'length', actualDeleteCount, kStrict);
-    return a;
-  }
-
-  // HandleForwardCase implements step 15. "If itemCount < actualDeleteCount,
-  // then...""
-  macro HandleForwardCase(
-      context: Context, o: JSReceiver, len: Number, itemCount: Number,
-      actualStart: Number, actualDeleteCount: Number): void {
-    // 15. If itemCount < actualDeleteCount, then
-    // a. Let k be actualStart.
-    let k: Number = actualStart;
-
-    // b. Repeat, while k < (len - actualDeleteCount)
-    while (k < (len - actualDeleteCount)) {
-      // i. Let from be ! ToString(k + actualDeleteCount).
-      let from: Number = k + actualDeleteCount;
-      // ii. Let to be ! ToString(k + itemCount).
-      let to: Number = k + itemCount;
-
-      // iii. Let fromPresent be ? HasProperty(O, from).
-      let fromPresent: Boolean = TorqueHasProperty(context, o, from);
-
-      // iv. If fromPresent is true, then
-      if (fromPresent == True) {
-        // 1. Let fromValue be ? Get(O, from).
-        let fromValue: Object = GetProperty(context, o, from);
-
-        // 2. Perform ? Set(O, to, fromValue, true).
-        SetProperty(context, o, to, fromValue, kStrict);
-
-        // v. Else fromPresent is false,
-      } else {
-        // 1. Perform ? DeletePropertyOrThrow(O, to).
-        DeleteProperty(context, o, to, kStrict);
-      }
-      // vi. Increase k by 1.
-      k++;
-    }
-
-    // c. Let k be len.
-    k = len;
-
-    // d. Repeat, while k > (len - actualDeleteCount + itemCount)
-    while (k > (len - actualDeleteCount + itemCount)) {
-      // i. Perform ? DeletePropertyOrThrow(O, ! ToString(k - 1)).
-      DeleteProperty(context, o, k - 1, kStrict);
-      // ii. Decrease k by 1.
-      k--;
-    }
-  }
-
-  // HandleBackwardCase implements step 16. "Else if itemCount >
-  // actualDeleteCount, then..."
-  macro HandleBackwardCase(
-      context: Context, o: JSReceiver, len: Number, itemCount: Number,
-      actualStart: Number, actualDeleteCount: Number): void {
-    // 16. Else if itemCount > actualDeleteCount, then
-    // a. Let k be (len - actualDeleteCount).
-    let k: Number = len - actualDeleteCount;
-
-    // b. Repeat, while k > actualStart
-    while (k > actualStart) {
-      // i. Let from be ! ToString(k + actualDeleteCount - 1).
-      let from: Number = k + actualDeleteCount - 1;
-
-      // ii. Let to be ! ToString(k + itemCount - 1).
-      let to: Number = k + itemCount - 1;
-
-      // iii. Let fromPresent be ? HasProperty(O, from).
-      let fromPresent: Boolean = TorqueHasProperty(context, o, from);
-
-      // iv. If fromPresent is true, then
-      if (fromPresent == True) {
-        // 1. Let fromValue be ? Get(O, from).
-        let fromValue: Object = GetProperty(context, o, from);
-
-        // 2. Perform ? Set(O, to, fromValue, true).
-        SetProperty(context, o, to, fromValue, kStrict);
-
-        // v. Else fromPresent is false,
-      } else {
-        // 1. Perform ? DeletePropertyOrThrow(O, to).
-        DeleteProperty(context, o, to, kStrict);
-      }
-
-      // vi. Decrease k by 1.
-      k--;
-    }
-  }
-
-  macro SlowSplice(
-      context: Context, arguments: constexpr Arguments, o: JSReceiver,
-      len: Number, actualStart: Number, insertCount: Smi,
-      actualDeleteCount: Number): Object {
-    let affected: Number = len - actualStart - actualDeleteCount;
-
-    // 9. Let A be ? ArraySpeciesCreate(O, actualDeleteCount).
-    let a: JSReceiver = ArraySpeciesCreate(context, o, actualDeleteCount);
-    let itemCount: Number = insertCount;
-
-    if (UseSparseVariant(context, o, len, affected)) {
-      // UseSparseVariant returns true only if {o} is a JSArray.
-      assert(IsJSArray(o));
-      let array: JSArray = unsafe_cast<JSArray>(o);
-      NormalizeElements(context, array);
-
-      try {
-        let a_array: JSArray = cast<JSArray>(a) otherwise NotArray;
-        NormalizeElements(context, a_array);
-      }
-      label NotArray {}
-
-      SparseSlice(context, array, actualStart, actualDeleteCount, len, a);
-      let additionalArgumentCount: Smi = 0;
-      if (arguments.length > 2) {
-        additionalArgumentCount = convert<Smi>(arguments.length - 2);
-      }
-      SparseMove(
-          context, array, actualStart, actualDeleteCount, len,
-          additionalArgumentCount);
-    } else {
-      // Steps 9 through 12: creating the array of deleted elements.
-      FillDeletedElementsArray(context, o, actualStart, actualDeleteCount, a);
-
-      // 13. Let items be a List whose elements are, in left-to-right order,
-      //     the portion of the actual argument list starting with the third
-      //     argument. The list is empty if fewer than three arguments were
-      //     passed.
-      // 14. Let itemCount be the Number of elements in items.
-      // (done above).
-
-      // 15. If itemCount < actualDeleteCount, then
-      if (itemCount < actualDeleteCount) {
-        HandleForwardCase(
-            context, o, len, itemCount, actualStart, actualDeleteCount);
-        // 16. Else if itemCount > actualDeleteCount, then
-      } else if (itemCount > actualDeleteCount) {
-        HandleBackwardCase(
-            context, o, len, itemCount, actualStart, actualDeleteCount);
-      }
-    }
-
-    // 17. Let k be actualStart.
-    let k: Number = actualStart;
-
-    // 18. Repeat, while items is not empty
-    //   a. Remove the first element from items and let E be the value of that
-    //   element.
-    if (arguments.length > 2) {
-      for (let e: Object of arguments [2: ]) {
-        // b. Perform ? Set(O, ! ToString(k), E, true).
-        SetProperty(context, o, k, e, kStrict);
-
-        // c. Increase k by 1.
-        k = k + 1;
-      }
-    }
-
-    // 19. Perform ? Set(O, "length", len - actualDeleteCount + itemCount,
-    // true).
-    SetProperty(
-        context, o, 'length', len - actualDeleteCount + itemCount, kStrict);
-
-    return a;
-  }
-
-  // https://tc39.github.io/ecma262/#sec-array.prototype.splice
-  javascript builtin ArraySplice(
-      context: Context, receiver: Object, ...arguments): Object {
-    // 1. Let O be ? ToObject(this value).
-    let o: JSReceiver = ToObject(context, receiver);
-
-    // 2. Let len be ? ToLength(? Get(O, "length")).
-    let len: Number = GetLengthProperty(context, o);
-
-    // 3. Let relativeStart be ? ToInteger(start).
-    let start: Object = arguments[0];
-    let relativeStart: Number = ToInteger_Inline(context, start);
-
-    // 4. If relativeStart < 0, let actualStart be max((len + relativeStart),
-    // 0);
-    //    else let actualStart be min(relativeStart, len).
-    let actualStart: Number = relativeStart < 0 ?
-        max((len + relativeStart), 0) :
-        min(relativeStart, len);
-
-    let insertCount: Smi;
-    let actualDeleteCount: Number;
-    // 5. If the Number of actual arguments is 0, then
-    if (arguments.length == 0) {
-      // a. Let insertCount be 0.
-      insertCount = 0;
-      // b. Let actualDeleteCount be 0.
-      actualDeleteCount = 0;
-      // 6. Else if the Number of actual arguments is 1, then
-    } else if (arguments.length == 1) {
-      // a. Let insertCount be 0.
-      insertCount = 0;
-      // b. Let actualDeleteCount be len - actualStart.
-      actualDeleteCount = len - actualStart;
-      // 7. Else,
-    } else {
-      // a. Let insertCount be the Number of actual arguments minus 2.
-      insertCount = convert<Smi>(arguments.length) - 2;
-      // b. Let dc be ? ToInteger(deleteCount).
-      let deleteCount: Object = arguments[1];
-      let dc: Number = ToInteger_Inline(context, deleteCount);
-      // c. Let actualDeleteCount be min(max(dc, 0), len - actualStart).
-      actualDeleteCount = min(max(dc, 0), len - actualStart);
-    }
-
-    // 8. If len + insertCount - actualDeleteCount > 2^53-1, throw a
-    //    Bailout exception.
-    let new_length: Number = len + insertCount - actualDeleteCount;
-    if (new_length > kMaxSafeInteger) {
-      ThrowTypeError(context, kInvalidArrayLength, start);
-    }
-
-    try {
-      return FastArraySplice(
-          context, arguments, o, len, actualStart, insertCount,
-          actualDeleteCount) otherwise Bailout;
-    }
-    label Bailout {}
-
-    // If the fast case fails, just continue with the slow, correct,
-    // spec-compliant case.
-    return SlowSplice(
-        context, arguments, o, len, actualStart, insertCount,
-        actualDeleteCount);
-  }
-}
diff --git a/src/builtins/array.tq b/src/builtins/array.tq
index 3fc1468..5fc6853 100644
--- a/src/builtins/array.tq
+++ b/src/builtins/array.tq
@@ -13,197 +13,294 @@
       }
   }
 
-  macro IsArray(o: Object): bool {
-    try {
-      let array: JSArray = cast<JSArray>(o) otherwise NotArray;
-      return true;
-    }
-    label NotArray {
-      return false;
-    }
-  }
+  macro FastArraySplice(
+      context: Context, args: constexpr Arguments, o: Object,
+      originalLengthNumber: Number, actualStartNumber: Number, insertCount: Smi,
+      actualDeleteCountNumber: Number): Object
+  labels Bailout {
+    let originalLength: Smi = cast<Smi>(originalLengthNumber) otherwise Bailout;
+    let actualStart: Smi = cast<Smi>(actualStartNumber) otherwise Bailout;
+    let actualDeleteCount: Smi =
+        cast<Smi>(actualDeleteCountNumber) otherwise Bailout;
+    let lengthDelta: Smi = insertCount - actualDeleteCount;
+    let newLength: Smi = originalLength + lengthDelta;
 
-  macro StoreArrayHole(elements: FixedDoubleArray, k: Smi): void {
-    StoreFixedDoubleArrayHoleSmi(elements, k);
-  }
+    let a: JSArray = cast<JSArray>(o) otherwise Bailout;
 
-  macro StoreArrayHole(elements: FixedArray, k: Smi): void {
-    elements[k] = Hole;
-  }
+    let map: Map = a.map;
+    if (!IsPrototypeInitialArrayPrototype(context, map)) goto Bailout;
+    if (IsNoElementsProtectorCellInvalid()) goto Bailout;
+    if (IsArraySpeciesProtectorCellInvalid()) goto Bailout;
 
-  macro CopyArrayElement(
-      elements: FixedArray, newElements: FixedArray, from: Smi, to: Smi): void {
-    let e: Object = elements[from];
-    newElements[to] = e;
-  }
+    // Fast path only works on fast elements kind and with writable length.
+    let elementsKind: ElementsKind = EnsureArrayPushable(map) otherwise Bailout;
+    if (!IsFastElementsKind(elementsKind)) goto Bailout;
 
-  macro CopyArrayElement(
-      elements: FixedDoubleArray, newElements: FixedDoubleArray, from: Smi,
-      to: Smi): void {
-    try {
-      let floatValue: float64 = LoadDoubleWithHoleCheck(elements, from)
-      otherwise FoundHole;
-      newElements[to] = floatValue;
-    }
-    label FoundHole {
-      StoreArrayHole(newElements, to);
-    }
-  }
+    // For now, only support non-double fast elements
+    if (!IsFastSmiOrTaggedElementsKind(elementsKind)) goto Bailout;
 
-  ///////////////////////////////////////////////////////////////////////////////////
-  // Sparse array support
-  // This is a port of the sparse array support in src/js/array.js.
-  //
-  extern runtime HasComplexElements(Context, JSArray): Boolean;
-  extern runtime EstimateNumberOfElements(Context, JSArray): Number;
-  extern runtime GetArrayKeys(Context, Object, Object): JSArray|Number;
-  extern runtime MoveArrayContents(Context, JSArray, JSArray): void;
-
-  macro UseSparseVariant(
-      context: Context, o: JSReceiver, length: Number, touched: Number): bool {
-    try {
-      if (length < 1000) {
-        return false;
+    if (IsFastSmiElementsKind(elementsKind)) {
+      for (let e: Object of args [2: ]) {
+        if (TaggedIsNotSmi(e)) goto Bailout;
       }
-      let array: JSArray = cast<JSArray>(o) otherwise ReturnFalse;
-      if (HasComplexElements(context, array) == True) {
-        return false;
+    }
+
+    // Make sure that the length hasn't been changed by side-effect.
+    let length: Smi = cast<Smi>(a.length) otherwise Bailout;
+    if (originalLength != length) goto Bailout;
+
+    let deletedResult: JSArray =
+        ExtractFastJSArray(context, a, actualStart, actualDeleteCount);
+
+    if (newLength == 0) {
+      a.elements = kEmptyFixedArray;
+      a.length = 0;
+      return deletedResult;
+    }
+
+    let elements: FixedArray = cast<FixedArray>(a.elements) otherwise Bailout;
+    let elementsMap: Map = elements.map;
+
+    // If the source is a COW array or the spliced array is larger then the
+    // source array, then allocate a new FixedArray to hold the result.
+    let newElements: FixedArray = elements;
+    if ((elementsMap == kCOWMap) || (lengthDelta > 0)) {
+      newElements = ExtractFixedArray(
+          elements, 0, actualStart, newLength, kAllFixedArrays);
+      newElements.map = elementsMap;
+      a.elements = newElements;
+    }
+
+    // Double check that the array is still in fast elements mode
+    assert(IsFastSmiElementsKind(a.map.elements_kind));
+
+    // Copy over inserted elements.
+    let k: Smi = actualStart;
+    if (insertCount > 0) {
+      for (let e: Object of args [2: ]) {
+        newElements[k++] = e;
       }
-      let smi_length: Smi = cast<Smi>(length) otherwise ReturnTrue;
-      let elements_threshold: Smi = smi_length >>> 2;  // = length / 4.
-      let estimated_elements_number: Number =
-          EstimateNumberOfElements(context, array);
-      let estimated_elements: Smi =
-          cast<Smi>(estimated_elements_number) otherwise ReturnFalse;
-      if (estimated_elements < elements_threshold) {
-        if (touched > (estimated_elements >>> 2)) {
-          return true;
-        }
+    }
+
+    // Copy over elements after deleted elements.
+    let count: Smi = length - actualStart - actualDeleteCount;
+    while (count > 0) {
+      let e: Object = elements[k - lengthDelta];
+      newElements[k++] = e;
+      count--;
+    }
+
+    // Fill rest of spliced FixedArray with the hole, but only if the
+    // destination FixedArray is the original array's, since otherwise the array
+    // is pre-filled with holes.
+    if (elements == newElements) {
+      let limit: Smi = elements.length;
+      while (k < limit) {
+        newElements[k++] = Hole;
       }
-      return false;
     }
-    label ReturnFalse {
-      return false;
-    }
-    label ReturnTrue {
-      return true;
-    }
+
+    // Update the array's length after all the FixedArray shuffling is done.
+    a.length = newLength;
+
+    return deletedResult;
   }
 
-  macro SparseSlice(
-      context: Context, o: JSReceiver, actualStart: Number,
-      actualDeleteCount: Number, len: Number, a: JSReceiver): void {
-    let indices: JSArray|Number =
-        GetArrayKeys(context, o, actualStart + actualDeleteCount);
-    if (IsNumber(indices)) {
-      let limit: Number = unsafe_cast<Number>(indices);
-      for (let i: Number = actualStart; i < limit; i++) {
-        let fromPresent: Boolean = TorqueHasProperty(context, o, i);
-        if (fromPresent == True) {
-          let fromValue: Object = GetProperty(context, o, i);
-          CreateDataProperty(context, a, i - actualStart, fromValue);
-        }
-      }
+  // https://tc39.github.io/ecma262/#sec-array.prototype.splice
+  javascript builtin ArraySpliceTorque(
+      context: Context, receiver: Object, ...arguments): Object {
+    // 1. Let O be ? ToObject(this value).
+    let o: JSReceiver = ToObject(context, receiver);
+
+    // 2. Let len be ? ToLength(? Get(O, "length")).
+    let len: Number = GetLengthProperty(context, o);
+
+    // 3. Let relativeStart be ? ToInteger(start).
+    let start: Object = arguments[0];
+    let relativeStart: Number = ToInteger_Inline(context, start);
+
+    // 4. If relativeStart < 0, let actualStart be max((len + relativeStart),
+    // 0);
+    //    else let actualStart be min(relativeStart, len).
+    let actualStart: Number = relativeStart < 0 ?
+        max((len + relativeStart), 0) :
+        min(relativeStart, len);
+
+    let insertCount: Smi;
+    let actualDeleteCount: Number;
+    // 5. If the Number of actual arguments is 0, then
+    if (arguments.length == 0) {
+      // a. Let insertCount be 0.
+      insertCount = 0;
+      // b. Let actualDeleteCount be 0.
+      actualDeleteCount = 0;
+      // 6. Else if the Number of actual arguments is 1, then
+    } else if (arguments.length == 1) {
+      // a. Let insertCount be 0.
+      insertCount = 0;
+      // b. Let actualDeleteCount be len - actualStart.
+      actualDeleteCount = len - actualStart;
+      // 7. Else,
     } else {
-      let indices_array: JSArray = unsafe_cast<JSArray>(indices);
-      assert(IsFixedArray(indices_array.elements));
-      let indices_elements: FixedArray =
-          unsafe_cast<FixedArray>(indices_array.elements);
-      let length: Smi = unsafe_cast<Smi>(indices_array.length);
-      for (let k: Smi = 0; k < length; k++) {
-        let key: Number = unsafe_cast<Number>(indices_elements[k]);
-        if (key >= actualStart) {
-          let fromPresent: Boolean = TorqueHasProperty(context, o, key);
-          if (fromPresent == True) {
-            let fromValue: Object = GetProperty(context, o, key);
-            CreateDataProperty(context, a, key - actualStart, fromValue);
-          }
-        }
+      // a. Let insertCount be the Number of actual arguments minus 2.
+      insertCount = convert<Smi>(arguments.length) - 2;
+      // b. Let dc be ? ToInteger(deleteCount).
+      let deleteCount: Object = arguments[1];
+      let dc: Number = ToInteger_Inline(context, deleteCount);
+      // c. Let actualDeleteCount be min(max(dc, 0), len - actualStart).
+      actualDeleteCount = min(max(dc, 0), len - actualStart);
+    }
+
+    // 8. If len + insertCount - actualDeleteCount > 2^53-1, throw a
+    //    Bailout exception.
+    if (len + insertCount - actualDeleteCount > kMaxSafeInteger) {
+      ThrowRangeError(context, kInvalidArrayLength);
+    }
+
+    try {
+      return FastArraySplice(
+          context, arguments, o, len, actualStart, insertCount,
+          actualDeleteCount) otherwise Bailout;
+    }
+    label Bailout {}
+    // If the fast case fails, just continue with the slow, correct,
+    // spec-compliant case.
+
+    // 9. Let A be ? ArraySpeciesCreate(O, actualDeleteCount).
+    let a: Object = ArraySpeciesCreate(context, o, actualDeleteCount);
+
+    // 10. Let k be 0.
+    let k: Number = 0;
+
+    // 11. Repeat, while k < actualDeleteCount
+    while (k < actualDeleteCount) {
+      // a. Let from be ! ToString(actualStart + k).
+      let from: String = ToString_Inline(context, actualStart + k);
+
+      // b. Let fromPresent be ? HasProperty(O, from).
+      let fromPresent: Oddball = TorqueHasProperty(context, o, from);
+
+      // c. If fromPresent is true, then
+      if (fromPresent == True) {
+        // i. Let fromValue be ? Get(O, from).
+        let fromValue: Object = GetProperty(context, o, from);
+
+        // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(k), fromValue).
+        CreateDataProperty(context, a, ToString_Inline(context, k), fromValue);
       }
-    }
-  }
 
-  macro SparseMove(
-      context: Context, o: JSArray, actualStart: Number,
-      actualDeleteCount: Number, len: Number,
-      additionalArgumentCount: Number): void {
-    // Bail out if no moving is necessary.
-    if (additionalArgumentCount == actualDeleteCount) {
-      return;
+      // d. Increment k by 1.
+      k = k + 1;
     }
-    // Move data to new array.
-    let new_array: JSArray = InternalArrayCreate(
-        context,
-        // Clamp array length to 2^32-1 to avoid early RangeError.
-        NumberMin(
-            len - actualDeleteCount + additionalArgumentCount, 0xffffffff));
 
-    let big_indices: Object = Undefined;
-    let indices: JSArray|Number = GetArrayKeys(context, o, len);
-    if (IsNumber(indices)) {
-      let limit: Number = unsafe_cast<Number>(indices);
-      for (let i: Number = 0; i < actualStart && i < limit; i++) {
-        let fromPresent: Boolean = TorqueHasProperty(context, o, i);
+    // 12. Perform ? Set(A, "length", actualDeleteCount, true).
+    SetProperty(context, a, 'length', actualDeleteCount, kStrict);
+
+    // 13. Let items be a List whose elements are, in left-to-right order,
+    //     the portion of the actual argument list starting with the third
+    //     argument. The list is empty if fewer than three arguments were
+    //     passed.
+    // 14. Let itemCount be the Number of elements in items.
+    let itemCount: Number = insertCount;
+
+    // 15. If itemCount < actualDeleteCount, then
+    if (itemCount < actualDeleteCount) {
+      // a. Let k be actualStart.
+      let k: Number = actualStart;
+
+      // b. Repeat, while k < (len - actualDeleteCount)
+      while (k < (len - actualDeleteCount)) {
+        // i. Let from be ! ToString(k + actualDeleteCount).
+        let from: String = ToString_Inline(context, k + actualDeleteCount);
+        // ii. Let to be ! ToString(k + itemCount).
+        let to: String = ToString_Inline(context, k + itemCount);
+
+        // iii. Let fromPresent be ? HasProperty(O, from).
+        let fromPresent: Oddball = TorqueHasProperty(context, o, from);
+
+        // iv. If fromPresent is true, then
         if (fromPresent == True) {
-          let fromValue: Object = GetProperty(context, o, i);
-          CreateDataProperty(context, new_array, i, fromValue);
+          // 1. Let fromValue be ? Get(O, from).
+          let fromValue: Object = GetProperty(context, o, from);
+
+          // 2. Perform ? Set(O, to, fromValue, true).
+          SetProperty(context, o, to, fromValue, kStrict);
+
+          // v. Else fromPresent is false,
+        } else {
+          // 1. Perform ? DeletePropertyOrThrow(O, to).
+          DeleteProperty(context, o, to, kStrict);
         }
+        // vi. Increase k by 1.
+        k = k + 1;
       }
 
-      for (let i: Number = actualStart + actualDeleteCount; i < limit; i++) {
-        let fromPresent: Boolean = TorqueHasProperty(context, o, i);
+      // c. Let k be len.
+      k = len;
+      // d. Repeat, while k > (len - actualDeleteCount + itemCount)
+      while (k > (len - actualDeleteCount + itemCount)) {
+        // i. Perform ? DeletePropertyOrThrow(O, ! ToString(k - 1)).
+        DeleteProperty(context, o, ToString_Inline(context, k - 1), kStrict);
+
+        // ii. Decrease k by 1.
+        k = k - 1;
+      }
+      // 16. Else if itemCount > actualDeleteCount, then
+    } else if (itemCount > actualDeleteCount) {
+      // a. Let k be (len - actualDeleteCount).
+      let k: Number = len - actualDeleteCount;
+
+      // b. Repeat, while k > actualStart
+      while (k > actualStart) {
+        // i. Let from be ! ToString(k + actualDeleteCount - 1).
+        let from: String = ToString_Inline(context, k + actualDeleteCount - 1);
+
+        // ii. Let to be ! ToString(k + itemCount - 1).
+        let to: String = ToString_Inline(context, k + itemCount - 1);
+
+        // iii. Let fromPresent be ? HasProperty(O, from).
+        let fromPresent: Oddball = TorqueHasProperty(context, o, from);
+
+        // iv. If fromPresent is true, then
         if (fromPresent == True) {
-          let fromValue: Object = GetProperty(context, o, i);
-          let toIndex: Number = i - actualDeleteCount + additionalArgumentCount;
-          CreateDataProperty(context, new_array, toIndex, fromValue);
+          // 1. Let fromValue be ? Get(O, from).
+          let fromValue: Object = GetProperty(context, o, from);
+
+          // 2. Perform ? Set(O, to, fromValue, true).
+          SetProperty(context, o, to, fromValue, kStrict);
+
+          // v. Else fromPresent is false,
+        } else {
+          // 1. Perform ? DeletePropertyOrThrow(O, to).
+          DeleteProperty(context, o, to, kStrict);
         }
-      }
-    } else {
-      assert(IsJSArray(unsafe_cast<HeapObject>(indices)));
-      let indices_array: JSArray = unsafe_cast<JSArray>(indices);
-      let indices_elements: FixedArray =
-          unsafe_cast<FixedArray>(indices_array.elements);
-      let length: Smi = unsafe_cast<Smi>(indices_array.length);
-      for (let k: Smi = 0; k < length; ++k) {
-        let key: Number = unsafe_cast<Number>(indices_elements[k]);
-        if (key < actualStart) {
-          let fromPresent: Boolean = TorqueHasProperty(context, o, key);
-          if (fromPresent == True) {
-            let fromValue: Object = GetProperty(context, o, key);
-            CreateDataProperty(context, new_array, key, fromValue);
-          }
-        } else if (key >= (actualStart + actualDeleteCount)) {
-          let fromPresent: Boolean = TorqueHasProperty(context, o, key);
-          if (fromPresent == True) {
-            let fromValue: Object = GetProperty(context, o, key);
-            let new_key: Number =
-                key - actualDeleteCount + additionalArgumentCount;
-            CreateDataProperty(context, new_array, new_key, fromValue);
-            if (new_key > 0xfffffffe) {
-              if (big_indices == Undefined) {
-                big_indices = InternalArrayCreate(context, 0);
-              }
-              let big_indices_array: JSArray =
-                  unsafe_cast<JSArray>(big_indices);
-              CreateDataProperty(
-                  context, big_indices_array, big_indices_array.length,
-                  new_key);
-            }
-          }
-        }
+
+        // vi. Decrease k by 1.
+        k = k - 1;
       }
     }
-    // Move contents of new_array into this array
-    MoveArrayContents(context, new_array, o);
-    // Add any moved values that aren't elements anymore.
-    if (big_indices != Undefined) {
-      let big_indices_array: JSArray = unsafe_cast<JSArray>(big_indices);
-      let length: Smi = unsafe_cast<Smi>(big_indices_array.length);
-      for (let i: Smi = 0; i < length; ++i) {
-        let key: Object = GetProperty(context, big_indices_array, i);
-        let value: Object = GetProperty(context, new_array, key);
-        CreateDataProperty(context, o, key, value);
+
+    // 17. Let k be actualStart.
+    k = actualStart;
+
+    // 18. Repeat, while items is not empty
+    //   a. Remove the first element from items and let E be the value of that
+    //   element.
+    if (arguments.length > 2) {
+      for (let e: Object of arguments [2: ]) {
+        // b. Perform ? Set(O, ! ToString(k), E, true).
+        SetProperty(context, o, ToString_Inline(context, k), e, kStrict);
+
+        // c. Increase k by 1.
+        k = k + 1;
       }
     }
+
+    // 19. Perform ? Set(O, "length", len - actualDeleteCount + itemCount,
+    // true).
+    SetProperty(
+        context, o, 'length', len - actualDeleteCount + itemCount, kStrict);
+
+    return a;
   }
 }
diff --git a/src/builtins/base.tq b/src/builtins/base.tq
index 2251d59..a784d98 100644
--- a/src/builtins/base.tq
+++ b/src/builtins/base.tq
@@ -106,10 +106,6 @@
 type FixedBigUint64Array extends FixedTypedArray;
 type FixedBigInt64Array extends FixedTypedArray;
 
-const kFixedArrays: constexpr ExtractFixedArrayFlags generates
-'ExtractFixedArrayFlag::kFixedArrays';
-const kFixedDoubleArrays: constexpr ExtractFixedArrayFlags generates
-'ExtractFixedArrayFlag::kFixedDoubleArrays';
 const kAllFixedArrays: constexpr ExtractFixedArrayFlags generates
 'ExtractFixedArrayFlag::kAllFixedArrays';
 
@@ -168,7 +164,6 @@
 
 extern macro Print(constexpr string);
 extern macro Print(constexpr string, Object);
-extern macro Comment(constexpr string);
 extern macro Print(Object);
 extern macro DebugBreak();
 extern macro ToInteger_Inline(Context, Object): Number;
@@ -193,8 +188,7 @@
 extern macro ThrowTypeError(Context, constexpr MessageTemplate, Object): never;
 extern macro ThrowTypeError(
     Context, constexpr MessageTemplate, Object, Object, Object): never;
-extern macro ArraySpeciesCreate(Context, Object, Number): JSReceiver;
-extern macro InternalArrayCreate(Context, Number): JSArray;
+extern macro ArraySpeciesCreate(Context, Object, Number): Object;
 extern macro EnsureArrayPushable(Map): ElementsKind labels Bailout;
 
 extern builtin ToObject(Context, Object): JSReceiver;
@@ -203,9 +197,7 @@
 extern macro IsString(HeapObject): bool;
 extern builtin ToString(Context, Object): String;
 
-extern runtime NormalizeElements(Context, JSObject);
-extern runtime TransitionElementsKindWithKind(Context, JSObject, Smi);
-extern runtime CreateDataProperty(Context, JSReceiver, Object, Object);
+extern runtime CreateDataProperty(Context, Object, Object, Object);
 extern runtime SetProperty(Context, Object, Object, Object, LanguageMode);
 extern runtime DeleteProperty(Context, Object, Object, LanguageMode);
 
@@ -234,10 +226,6 @@
 extern operator '==' macro ElementsKindEqual(
     constexpr ElementsKind, constexpr ElementsKind): constexpr bool;
 extern operator '==' macro ElementsKindEqual(ElementsKind, ElementsKind): bool;
-operator '!=' macro ElementsKindNotEqual(
-    k1: ElementsKind, k2: ElementsKind): bool {
-  return !ElementsKindEqual(k1, k2);
-}
 extern macro IsFastElementsKind(constexpr ElementsKind): constexpr bool;
 extern macro IsDoubleElementsKind(constexpr ElementsKind): constexpr bool;
 
@@ -257,13 +245,10 @@
 extern operator '!=' macro Float64NotEqual(float64, float64): bool;
 
 extern operator
-'==' macro BranchIfNumberEqual(Number, Number): never labels Taken, NotTaken;
-extern operator
 '<' macro BranchIfNumberLessThan(Number, Number): never labels Taken, NotTaken;
 extern operator
 '<=' macro BranchIfNumberLessThanOrEqual(Number, Number): never labels Taken,
     NotTaken;
-
 extern operator
 '>' macro BranchIfNumberGreaterThan(Number, Number): never labels Taken,
     NotTaken;
@@ -330,8 +315,8 @@
 extern operator
 '[]' macro GetArgumentValue(constexpr Arguments, intptr): Object;
 
-extern macro TaggedIsSmi(Object): bool;
-extern macro TaggedIsNotSmi(Object): bool;
+extern operator 'is<Smi>' macro TaggedIsSmi(Object): bool;
+extern operator 'isnt<Smi>' macro TaggedIsNotSmi(Object): bool;
 extern macro TaggedIsPositiveSmi(Object): bool;
 
 extern macro HeapObjectToJSDataView(HeapObject): JSDataView labels CastError;
@@ -477,13 +462,6 @@
 macro convert<A : type>(i: constexpr int31): A {
   return i;
 }
-extern macro ConvertElementsKindToInt(ElementsKind): int32;
-
-macro convert<A : type>(elements_kind: ElementsKind): A;
-convert<int32>(elements_kind: ElementsKind): int32 {
-  return ConvertElementsKindToInt(elements_kind);
-}
-
 macro convert<A : type>(i: int32): A;
 convert<Number>(i: int32): Number {
   return ChangeInt32ToTagged(i);
@@ -570,9 +548,6 @@
   return UnsafeCastNumberToHeapNumber(n);
 }
 macro unsafe_cast<A : type>(o: Object): A;
-unsafe_cast<Object>(o: Object): Object {
-  return o;
-}
 unsafe_cast<FixedArray>(o: Object): FixedArray {
   return UnsafeCastObjectToFixedArray(o);
 }
@@ -659,10 +634,6 @@
     FixedArray, constexpr int31, Object): void;
 extern operator
 '[]=' macro StoreFixedArrayElementSmi(FixedArray, Smi, Object): void;
-operator '[]=' macro StoreFixedDoubleArrayNumber(
-    a: FixedDoubleArray, index: Smi, value: Number): void {
-  a[index] = convert<float64>(value);
-}
 
 extern operator '.instance_type' macro LoadMapInstanceType(Map): int32;
 
@@ -688,28 +659,6 @@
 extern macro IsFastSmiElementsKind(ElementsKind): bool;
 extern macro IsHoleyFastElementsKind(ElementsKind): bool;
 
-macro AllowDoubleElements(kind: ElementsKind): ElementsKind {
-  if (kind == PACKED_SMI_ELEMENTS) {
-    return PACKED_DOUBLE_ELEMENTS;
-  } else if (kind == HOLEY_SMI_ELEMENTS) {
-    return HOLEY_DOUBLE_ELEMENTS;
-  }
-  return kind;
-}
-
-macro AllowNonNumberElements(kind: ElementsKind): ElementsKind {
-  if (kind == PACKED_SMI_ELEMENTS) {
-    return PACKED_ELEMENTS;
-  } else if (kind == HOLEY_SMI_ELEMENTS) {
-    return HOLEY_ELEMENTS;
-  } else if (kind == PACKED_DOUBLE_ELEMENTS) {
-    return PACKED_ELEMENTS;
-  } else if (kind == HOLEY_DOUBLE_ELEMENTS) {
-    return HOLEY_ELEMENTS;
-  }
-  return kind;
-}
-
 extern macro AllocateFixedArray(constexpr ElementsKind, intptr): FixedArray;
 
 extern macro CopyFixedArrayElements(
@@ -724,13 +673,8 @@
 extern macro IsElementsKindGreaterThan(
     ElementsKind, constexpr ElementsKind): bool;
 
-extern operator
-'[]=' macro StoreFixedDoubleArrayElementSmi(
-    FixedDoubleArray, Smi, float64): void;
-
 extern macro LoadDoubleWithHoleCheck(FixedDoubleArray, Smi): float64
 labels IfHole;
-extern macro StoreFixedDoubleArrayHoleSmi(FixedDoubleArray, Smi): void;
 
 extern macro Call(Context, Callable, Object): Object;
 extern macro Call(Context, Callable, Object, Object): Object;
@@ -741,9 +685,8 @@
 extern macro Call(
     Context, Callable, Object, Object, Object, Object, Object, Object): Object;
 
-extern macro ExtractFixedArray(FixedArray, Smi, Smi, Smi): FixedArray;
 extern macro ExtractFixedArray(
-    FixedDoubleArray, Smi, Smi, Smi): FixedDoubleArray;
+    FixedArray, Smi, Smi, Smi, constexpr ExtractFixedArrayFlags): FixedArray;
 
 extern builtin ExtractFastJSArray(Context, JSArray, Smi, Smi): JSArray;
 
@@ -778,17 +721,11 @@
   }
 }
 
-extern macro TransitionElementsKind(
-    JSObject, Map, ElementsKind, ElementsKind): void labels Bailout;
-
 extern macro IsCallable(HeapObject): bool;
 extern macro IsJSArray(HeapObject): bool;
-extern macro IsJSReceiver(HeapObject): bool;
-extern macro IsFixedArray(HeapObject): bool;
 extern macro TaggedIsCallable(Object): bool;
 extern macro IsDetachedBuffer(JSArrayBuffer): bool;
 extern macro IsHeapNumber(HeapObject): bool;
-extern macro IsNumber(Object): bool;
 extern macro IsExtensibleMap(Map): bool;
 extern macro IsCustomElementsReceiverInstanceType(int32): bool;
 extern macro Typeof(Object): Object;
diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc
index 8fd2fe0..ceeee5f 100644
--- a/src/builtins/builtins-array.cc
+++ b/src/builtins/builtins-array.cc
@@ -23,6 +23,34 @@
 
 namespace {
 
+inline bool ClampedToInteger(Isolate* isolate, Object* object, int* out) {
+  // This is an extended version of ECMA-262 7.1.11 handling signed values
+  // Try to convert object to a number and clamp values to [kMinInt, kMaxInt]
+  if (object->IsSmi()) {
+    *out = Smi::ToInt(object);
+    return true;
+  } else if (object->IsHeapNumber()) {
+    double value = HeapNumber::cast(object)->value();
+    if (std::isnan(value)) {
+      *out = 0;
+    } else if (value > kMaxInt) {
+      *out = kMaxInt;
+    } else if (value < kMinInt) {
+      *out = kMinInt;
+    } else {
+      *out = static_cast<int>(value);
+    }
+    return true;
+  } else if (object->IsNullOrUndefined(isolate)) {
+    *out = 0;
+    return true;
+  } else if (object->IsBoolean()) {
+    *out = object->IsTrue(isolate);
+    return true;
+  }
+  return false;
+}
+
 inline bool IsJSArrayFastElementMovingAllowed(Isolate* isolate,
                                               JSArray* receiver) {
   return JSObject::PrototypeHasNoElements(isolate, receiver);
@@ -502,6 +530,67 @@
   return Smi::FromInt(new_length);
 }
 
+BUILTIN(ArraySplice) {
+  HandleScope scope(isolate);
+  Handle<Object> receiver = args.receiver();
+  if (V8_UNLIKELY(
+          !EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 3,
+                                                 args.length() - 3) ||
+          // If this is a subclass of Array, then call out to JS.
+          !Handle<JSArray>::cast(receiver)->HasArrayPrototype(isolate) ||
+          // If anything with @@species has been messed with, call out to JS.
+          !isolate->IsArraySpeciesLookupChainIntact())) {
+    return CallJsIntrinsic(isolate, isolate->array_splice(), args);
+  }
+  Handle<JSArray> array = Handle<JSArray>::cast(receiver);
+
+  int argument_count = args.length() - 1;
+  int relative_start = 0;
+  if (argument_count > 0) {
+    DisallowHeapAllocation no_gc;
+    if (!ClampedToInteger(isolate, args[1], &relative_start)) {
+      AllowHeapAllocation allow_allocation;
+      return CallJsIntrinsic(isolate, isolate->array_splice(), args);
+    }
+  }
+  int len = Smi::ToInt(array->length());
+  // clip relative start to [0, len]
+  int actual_start = (relative_start < 0) ? Max(len + relative_start, 0)
+                                          : Min(relative_start, len);
+
+  int actual_delete_count;
+  if (argument_count == 1) {
+    // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
+    // given as a request to delete all the elements from the start.
+    // And it differs from the case of undefined delete count.
+    // This does not follow ECMA-262, but we do the same for compatibility.
+    DCHECK_GE(len - actual_start, 0);
+    actual_delete_count = len - actual_start;
+  } else {
+    int delete_count = 0;
+    DisallowHeapAllocation no_gc;
+    if (argument_count > 1) {
+      if (!ClampedToInteger(isolate, args[2], &delete_count)) {
+        AllowHeapAllocation allow_allocation;
+        return CallJsIntrinsic(isolate, isolate->array_splice(), args);
+      }
+    }
+    actual_delete_count = Min(Max(delete_count, 0), len - actual_start);
+  }
+
+  int add_count = (argument_count > 1) ? (argument_count - 2) : 0;
+  int new_length = len - actual_delete_count + add_count;
+
+  if (new_length != len && JSArray::HasReadOnlyLength(array)) {
+    AllowHeapAllocation allow_allocation;
+    return CallJsIntrinsic(isolate, isolate->array_splice(), args);
+  }
+  ElementsAccessor* accessor = array->GetElementsAccessor();
+  Handle<JSArray> result_array = accessor->Splice(
+      array, actual_start, actual_delete_count, &args, add_count);
+  return *result_array;
+}
+
 // Array Concat -------------------------------------------------------------
 
 namespace {
diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h
index 661ca86..1f9ee70 100644
--- a/src/builtins/builtins-definitions.h
+++ b/src/builtins/builtins-definitions.h
@@ -315,6 +315,8 @@
   TFJ(ArrayPrototypeShift, SharedFunctionInfo::kDontAdaptArgumentsSentinel)    \
   /* ES6 #sec-array.prototype.slice */                                         \
   TFJ(ArrayPrototypeSlice, SharedFunctionInfo::kDontAdaptArgumentsSentinel)    \
+  /* ES6 #sec-array.prototype.splice */                                        \
+  CPP(ArraySplice)                                                             \
   /* ES6 #sec-array.prototype.unshift */                                       \
   CPP(ArrayUnshift)                                                            \
   /* Support for Array.from and other array-copying idioms */                  \
diff --git a/src/builtins/growable-fixed-array-gen.cc b/src/builtins/growable-fixed-array-gen.cc
index 3a155e2..eae9ff5 100644
--- a/src/builtins/growable-fixed-array-gen.cc
+++ b/src/builtins/growable-fixed-array-gen.cc
@@ -90,8 +90,8 @@
 
   CodeStubAssembler::ExtractFixedArrayFlags flags;
   flags |= CodeStubAssembler::ExtractFixedArrayFlag::kFixedArrays;
-  TNode<FixedArray> to_array = CAST(ExtractFixedArray(
-      from_array, nullptr, element_count, new_capacity, flags));
+  TNode<FixedArray> to_array = ExtractFixedArray(
+      from_array, nullptr, element_count, new_capacity, flags);
 
   return to_array;
 }
diff --git a/src/code-stub-assembler.cc b/src/code-stub-assembler.cc
index c1f0288..2131829 100644
--- a/src/code-stub-assembler.cc
+++ b/src/code-stub-assembler.cc
@@ -3858,7 +3858,7 @@
   return UncheckedCast<FixedArray>(array);
 }
 
-TNode<FixedArrayBase> CodeStubAssembler::ExtractFixedArray(
+TNode<FixedArray> CodeStubAssembler::ExtractFixedArray(
     Node* fixed_array, Node* first, Node* count, Node* capacity,
     ExtractFixedArrayFlags extract_flags, ParameterMode parameter_mode) {
   VARIABLE(var_result, MachineRepresentation::kTagged);
@@ -4084,33 +4084,6 @@
       mode);
 }
 
-void CodeStubAssembler::StoreFixedDoubleArrayHole(
-    TNode<FixedDoubleArray> array, Node* index, ParameterMode parameter_mode) {
-  CSA_SLOW_ASSERT(this, MatchesParameterMode(index, parameter_mode));
-  Node* offset =
-      ElementOffsetFromIndex(index, PACKED_DOUBLE_ELEMENTS, parameter_mode,
-                             FixedArray::kHeaderSize - kHeapObjectTag);
-  CSA_ASSERT(this, IsOffsetInBounds(
-                       offset, LoadAndUntagFixedArrayBaseLength(array),
-                       FixedDoubleArray::kHeaderSize, PACKED_DOUBLE_ELEMENTS));
-  Node* double_hole =
-      Is64() ? ReinterpretCast<UintPtrT>(Int64Constant(kHoleNanInt64))
-             : ReinterpretCast<UintPtrT>(Int32Constant(kHoleNanLower32));
-  // TODO(danno): When we have a Float32/Float64 wrapper class that
-  // preserves double bits during manipulation, remove this code/change
-  // this to an indexed Float64 store.
-  if (Is64()) {
-    StoreNoWriteBarrier(MachineRepresentation::kWord64, array, offset,
-                        double_hole);
-  } else {
-    StoreNoWriteBarrier(MachineRepresentation::kWord32, array, offset,
-                        double_hole);
-    StoreNoWriteBarrier(MachineRepresentation::kWord32, array,
-                        IntPtrAdd(offset, IntPtrConstant(kPointerSize)),
-                        double_hole);
-  }
-}
-
 void CodeStubAssembler::CopyFixedArrayElements(
     ElementsKind from_kind, Node* from_array, ElementsKind to_kind,
     Node* to_array, Node* first_element, Node* element_count, Node* capacity,
@@ -9844,9 +9817,6 @@
 
       // Both {left} and {right} are Smi, so just perform a fast Smi comparison.
       switch (op) {
-        case Operation::kEqual:
-          BranchIfSmiEqual(smi_left, smi_right, if_true, if_false);
-          break;
         case Operation::kLessThan:
           BranchIfSmiLessThan(smi_left, smi_right, if_true, if_false);
           break;
@@ -9893,10 +9863,6 @@
   BIND(&do_float_comparison);
   {
     switch (op) {
-      case Operation::kEqual:
-        Branch(Float64Equal(var_left_float.value(), var_right_float.value()),
-               if_true, if_false);
-        break;
       case Operation::kLessThan:
         Branch(Float64LessThan(var_left_float.value(), var_right_float.value()),
                if_true, if_false);
@@ -11805,15 +11771,6 @@
                      len);
 }
 
-Node* CodeStubAssembler::InternalArrayCreate(TNode<Context> context,
-                                             TNode<Number> len) {
-  Node* native_context = LoadNativeContext(context);
-  Node* const constructor = LoadContextElement(
-      native_context, Context::INTERNAL_ARRAY_FUNCTION_INDEX);
-  return ConstructJS(CodeFactory::Construct(isolate()), context, constructor,
-                     len);
-}
-
 Node* CodeStubAssembler::IsDetachedBuffer(Node* buffer) {
   CSA_ASSERT(this, HasInstanceType(buffer, JS_ARRAY_BUFFER_TYPE));
 
diff --git a/src/code-stub-assembler.h b/src/code-stub-assembler.h
index 52de928..e889189 100644
--- a/src/code-stub-assembler.h
+++ b/src/code-stub-assembler.h
@@ -1179,19 +1179,6 @@
       Node* object, Node* index, Node* value,
       ParameterMode parameter_mode = INTPTR_PARAMETERS);
 
-  Node* StoreFixedDoubleArrayElementSmi(TNode<FixedDoubleArray> object,
-                                        TNode<Smi> index,
-                                        TNode<Float64T> value) {
-    return StoreFixedDoubleArrayElement(object, index, value, SMI_PARAMETERS);
-  }
-
-  void StoreFixedDoubleArrayHole(TNode<FixedDoubleArray> array, Node* index,
-                                 ParameterMode mode = INTPTR_PARAMETERS);
-  void StoreFixedDoubleArrayHoleSmi(TNode<FixedDoubleArray> array,
-                                    TNode<Smi> index) {
-    StoreFixedDoubleArrayHole(array, index, SMI_PARAMETERS);
-  }
-
   Node* StoreFeedbackVectorSlot(
       Node* object, Node* index, Node* value,
       WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER,
@@ -1410,7 +1397,6 @@
 
   Node* ArraySpeciesCreate(TNode<Context> context, TNode<Object> originalArray,
                            TNode<Number> len);
-  Node* InternalArrayCreate(TNode<Context> context, TNode<Number> len);
 
   void FillFixedArrayWithValue(ElementsKind kind, Node* array, Node* from_index,
                                Node* to_index,
@@ -1480,10 +1466,6 @@
     return UncheckedCast<FixedDoubleArray>(base);
   }
 
-  TNode<Int32T> ConvertElementsKindToInt(TNode<Int32T> elements_kind) {
-    return UncheckedCast<Int32T>(elements_kind);
-  }
-
   enum class ExtractFixedArrayFlag {
     kFixedArrays = 1,
     kFixedDoubleArrays = 2,
@@ -1517,27 +1499,20 @@
   // passed as the |source| parameter.
   // * |parameter_mode| determines the parameter mode of |first|, |count| and
   // |capacity|.
-  TNode<FixedArrayBase> ExtractFixedArray(
+  TNode<FixedArray> ExtractFixedArray(
       Node* source, Node* first, Node* count = nullptr,
       Node* capacity = nullptr,
       ExtractFixedArrayFlags extract_flags =
           ExtractFixedArrayFlag::kAllFixedArrays,
       ParameterMode parameter_mode = INTPTR_PARAMETERS);
 
-  TNode<FixedArray> ExtractFixedArray(TNode<FixedArray> source,
-                                      TNode<Smi> first, TNode<Smi> count,
-                                      TNode<Smi> capacity) {
-    return CAST(ExtractFixedArray(source, first, count, capacity,
-                                  ExtractFixedArrayFlag::kFixedArrays,
-                                  SMI_PARAMETERS));
-  }
-
-  TNode<FixedDoubleArray> ExtractFixedArray(TNode<FixedDoubleArray> source,
-                                            TNode<Smi> first, TNode<Smi> count,
-                                            TNode<Smi> capacity) {
-    return CAST(ExtractFixedArray(source, first, count, capacity,
-                                  ExtractFixedArrayFlag::kFixedDoubleArrays,
-                                  SMI_PARAMETERS));
+  TNode<FixedArray> ExtractFixedArray(
+      TNode<FixedArray> source, TNode<Smi> first, TNode<Smi> count,
+      TNode<Smi> capacity,
+      ExtractFixedArrayFlags extract_flags =
+          ExtractFixedArrayFlag::kAllFixedArrays) {
+    return ExtractFixedArray(source, first, count, capacity, extract_flags,
+                             SMI_PARAMETERS);
   }
 
   // Copy the entire contents of a FixedArray or FixedDoubleArray to a new
@@ -2501,12 +2476,6 @@
                               ElementsKind to_kind, bool is_jsarray,
                               Label* bailout);
 
-  void TransitionElementsKind(TNode<JSReceiver> object, TNode<Map> map,
-                              ElementsKind from_kind, ElementsKind to_kind,
-                              Label* bailout) {
-    TransitionElementsKind(object, map, from_kind, to_kind, true, bailout);
-  }
-
   void TrapAllocationMemento(Node* object, Label* memento_found);
 
   TNode<IntPtrT> PageFromAddress(TNode<IntPtrT> address);
@@ -2598,32 +2567,26 @@
   void BranchIfNumberRelationalComparison(Operation op, Node* left, Node* right,
                                           Label* if_true, Label* if_false);
 
-  void BranchIfNumberEqual(TNode<Number> left, TNode<Number> right,
-                           Label* if_true, Label* if_false) {
-    BranchIfNumberRelationalComparison(Operation::kEqual, left, right, if_true,
-                                       if_false);
-  }
-
-  void BranchIfNumberLessThan(TNode<Number> left, TNode<Number> right,
-                              Label* if_true, Label* if_false) {
+  void BranchIfNumberLessThan(Node* left, Node* right, Label* if_true,
+                              Label* if_false) {
     BranchIfNumberRelationalComparison(Operation::kLessThan, left, right,
                                        if_true, if_false);
   }
 
-  void BranchIfNumberLessThanOrEqual(TNode<Number> left, TNode<Number> right,
-                                     Label* if_true, Label* if_false) {
+  void BranchIfNumberLessThanOrEqual(Node* left, Node* right, Label* if_true,
+                                     Label* if_false) {
     BranchIfNumberRelationalComparison(Operation::kLessThanOrEqual, left, right,
                                        if_true, if_false);
   }
 
-  void BranchIfNumberGreaterThan(TNode<Number> left, TNode<Number> right,
-                                 Label* if_true, Label* if_false) {
+  void BranchIfNumberGreaterThan(Node* left, Node* right, Label* if_true,
+                                 Label* if_false) {
     BranchIfNumberRelationalComparison(Operation::kGreaterThan, left, right,
                                        if_true, if_false);
   }
 
-  void BranchIfNumberGreaterThanOrEqual(TNode<Number> left, TNode<Number> right,
-                                        Label* if_true, Label* if_false) {
+  void BranchIfNumberGreaterThanOrEqual(Node* left, Node* right, Label* if_true,
+                                        Label* if_false) {
     BranchIfNumberRelationalComparison(Operation::kGreaterThanOrEqual, left,
                                        right, if_true, if_false);
   }
diff --git a/src/contexts.h b/src/contexts.h
index ea21d8b..372462c 100644
--- a/src/contexts.h
+++ b/src/contexts.h
@@ -72,6 +72,7 @@
 
 #define NATIVE_CONTEXT_IMPORTED_FIELDS(V)                                      \
   V(ARRAY_SHIFT_INDEX, JSFunction, array_shift)                                \
+  V(ARRAY_SPLICE_INDEX, JSFunction, array_splice)                              \
   V(ARRAY_UNSHIFT_INDEX, JSFunction, array_unshift)                            \
   V(ARRAY_ENTRIES_ITERATOR_INDEX, JSFunction, array_entries_iterator)          \
   V(ARRAY_FOR_EACH_ITERATOR_INDEX, JSFunction, array_for_each_iterator)        \
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index 464f421..5f10865 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -870,6 +870,8 @@
 DEFINE_INT(stack_trace_limit, 10, "number of stack frames to capture")
 DEFINE_BOOL(builtins_in_stack_traces, false,
             "show built-in functions in stack traces")
+DEFINE_BOOL(enable_experimental_builtins, false,
+            "enable new csa-based experimental builtins")
 DEFINE_BOOL(disallow_code_generation_from_strings, false,
             "disallow eval and friends")
 DEFINE_BOOL(expose_async_hooks, false, "expose async_hooks object")
diff --git a/src/js/array.js b/src/js/array.js
index 8ecedf6..896b20f 100644
--- a/src/js/array.js
+++ b/src/js/array.js
@@ -200,6 +200,35 @@
   return TO_STRING(x);
 }
 
+
+// This function implements the optimized splice implementation that can use
+// special array operations to handle sparse arrays in a sensible fashion.
+function SparseSlice(array, start_i, del_count, len, deleted_elements) {
+  // Move deleted elements to a new array (the return value from splice).
+  var indices = %GetArrayKeys(array, start_i + del_count);
+  if (IS_NUMBER(indices)) {
+    var limit = indices;
+    for (var i = start_i; i < limit; ++i) {
+      var current = array[i];
+      if (!IS_UNDEFINED(current) || i in array) {
+        %CreateDataProperty(deleted_elements, i - start_i, current);
+      }
+    }
+  } else {
+    var length = indices.length;
+    for (var k = 0; k < length; ++k) {
+      var key = indices[k];
+      if (key >= start_i) {
+        var current = array[key];
+        if (!IS_UNDEFINED(current) || key in array) {
+          %CreateDataProperty(deleted_elements, key - start_i, current);
+        }
+      }
+    }
+  }
+}
+
+
 // This function implements the optimized splice implementation that can use
 // special array operations to handle sparse arrays in a sensible fashion.
 function SparseMove(array, start_i, del_count, len, num_additional_args) {
@@ -536,6 +565,83 @@
   return null;
 }
 
+function ComputeSpliceStartIndex(start_i, len) {
+  if (start_i < 0) {
+    start_i += len;
+    return start_i < 0 ? 0 : start_i;
+  }
+
+  return start_i > len ? len : start_i;
+}
+
+
+function ComputeSpliceDeleteCount(delete_count, num_arguments, len, start_i) {
+  // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
+  // given as a request to delete all the elements from the start.
+  // And it differs from the case of undefined delete count.
+  // This does not follow ECMA-262, but we do the same for
+  // compatibility.
+  var del_count = 0;
+  if (num_arguments == 1)
+    return len - start_i;
+
+  del_count = TO_INTEGER(delete_count);
+  if (del_count < 0)
+    return 0;
+
+  if (del_count > len - start_i)
+    return len - start_i;
+
+  return del_count;
+}
+
+
+function ArraySpliceFallback(start, delete_count) {
+  var num_arguments = arguments.length;
+  var array = TO_OBJECT(this);
+  var len = TO_LENGTH(array.length);
+  var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
+  var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
+                                           start_i);
+  var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
+
+  const new_len = len - del_count + num_elements_to_add;
+  if (new_len >= 2**53) throw %make_type_error(kInvalidArrayLength);
+
+  var deleted_elements = ArraySpeciesCreate(array, del_count);
+  deleted_elements.length = del_count;
+
+  var changed_elements = del_count;
+  if (num_elements_to_add != del_count) {
+    // If the slice needs to do a actually move elements after the insertion
+    // point, then include those in the estimate of changed elements.
+    changed_elements += len - start_i - del_count;
+  }
+  if (UseSparseVariant(array, len, IS_ARRAY(array), changed_elements)) {
+    %NormalizeElements(array);
+    if (IS_ARRAY(deleted_elements)) %NormalizeElements(deleted_elements);
+    SparseSlice(array, start_i, del_count, len, deleted_elements);
+    SparseMove(array, start_i, del_count, len, num_elements_to_add);
+  } else {
+    SimpleSlice(array, start_i, del_count, len, deleted_elements);
+    SimpleMove(array, start_i, del_count, len, num_elements_to_add);
+  }
+
+  // Insert the arguments into the resulting array in
+  // place of the deleted elements.
+  var i = start_i;
+  var arguments_index = 2;
+  var arguments_length = arguments.length;
+  while (arguments_index < arguments_length) {
+    array[i++] = arguments[arguments_index++];
+  }
+  array.length = new_len;
+
+  // Return the deleted elements.
+  return deleted_elements;
+}
+
+
 function InnerArraySort(array, length, comparefn) {
   // In-place QuickSort algorithm.
   // For short (length <= 10) arrays, insertion sort is used for efficiency.
@@ -892,6 +998,7 @@
   "array_values_iterator", ArrayValues,
   // Fallback implementations of Array builtins.
   "array_shift", ArrayShiftFallback,
+  "array_splice", ArraySpliceFallback,
   "array_unshift", ArrayUnshiftFallback,
 ]);
 
diff --git a/src/runtime/runtime-array.cc b/src/runtime/runtime-array.cc
index a30f637..df67e6c 100644
--- a/src/runtime/runtime-array.cc
+++ b/src/runtime/runtime-array.cc
@@ -30,16 +30,6 @@
   return *object;
 }
 
-RUNTIME_FUNCTION(Runtime_TransitionElementsKindWithKind) {
-  HandleScope scope(isolate);
-  DCHECK_EQ(2, args.length());
-  CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
-  CONVERT_ARG_HANDLE_CHECKED(Smi, elements_kind_smi, 1);
-  ElementsKind to_kind = static_cast<ElementsKind>(elements_kind_smi->value());
-  JSObject::TransitionElementsKind(object, to_kind);
-  return *object;
-}
-
 namespace {
 // Find the next free position. undefined and holes are both considered
 // free spots. Returns "Nothing" if an exception occurred.
diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h
index 213b9a5..5845432 100644
--- a/src/runtime/runtime.h
+++ b/src/runtime/runtime.h
@@ -36,22 +36,21 @@
 // A variable number of arguments is specified by a -1, additional restrictions
 // are specified by inline comments
 
-#define FOR_EACH_INTRINSIC_ARRAY(F)       \
-  F(ArrayIncludes_Slow, 3, 1)             \
-  F(ArrayIndexOf, 3, 1)                   \
-  F(ArrayIsArray, 1, 1)                   \
-  F(ArraySpeciesConstructor, 1, 1)        \
-  F(EstimateNumberOfElements, 1, 1)       \
-  F(GetArrayKeys, 2, 1)                   \
-  F(GrowArrayElements, 2, 1)              \
-  F(HasComplexElements, 1, 1)             \
-  F(IsArray, 1, 1)                        \
-  F(MoveArrayContents, 2, 1)              \
-  F(NewArray, -1 /* >= 3 */, 1)           \
-  F(NormalizeElements, 1, 1)              \
-  F(PrepareElementsForSort, 2, 1)         \
-  F(TransitionElementsKind, 2, 1)         \
-  F(TransitionElementsKindWithKind, 2, 1) \
+#define FOR_EACH_INTRINSIC_ARRAY(F) \
+  F(ArrayIncludes_Slow, 3, 1)       \
+  F(ArrayIndexOf, 3, 1)             \
+  F(ArrayIsArray, 1, 1)             \
+  F(ArraySpeciesConstructor, 1, 1)  \
+  F(EstimateNumberOfElements, 1, 1) \
+  F(GetArrayKeys, 2, 1)             \
+  F(GrowArrayElements, 2, 1)        \
+  F(HasComplexElements, 1, 1)       \
+  F(IsArray, 1, 1)                  \
+  F(MoveArrayContents, 2, 1)        \
+  F(NewArray, -1 /* >= 3 */, 1)     \
+  F(NormalizeElements, 1, 1)        \
+  F(PrepareElementsForSort, 2, 1)   \
+  F(TransitionElementsKind, 2, 1)   \
   F(TrySliceSimpleNonFastElements, 3, 1)
 
 #define FOR_EACH_INTRINSIC_ATOMICS(F)  \
diff --git a/test/test262/test262.status b/test/test262/test262.status
index 6da5dce..396937d 100644
--- a/test/test262/test262.status
+++ b/test/test262/test262.status
@@ -474,6 +474,9 @@
   # https://bugs.chromium.org/p/v8/issues/detail?id=7993
   'intl402/RelativeTimeFormat/prototype/toStringTag/toStringTag': [FAIL],
 
+  # https://bugs.chromium.org/p/v8/issues/detail?id=7814
+  'built-ins/Array/prototype/splice/property-traps-order-with-species': [FAIL],
+
   # https://bugs.chromium.org/p/v8/issues/detail?id=6705
   'built-ins/Object/assign/strings-and-symbol-order': [FAIL],
 
@@ -515,6 +518,8 @@
   # https://bugs.chromium.org/p/v8/issues/detail?id=6538
   'built-ins/Array/prototype/unshift/throws-if-integer-limit-exceeded': [SKIP],
   'built-ins/Array/prototype/reverse/length-exceeding-integer-limit-with-proxy': [FAIL],
+  'built-ins/Array/prototype/splice/create-species-length-exceeding-integer-limit': [FAIL],
+  'built-ins/Array/prototype/splice/throws-if-integer-limit-exceeded': [SKIP],
 
   # https://bugs.chromium.org/p/v8/issues/detail?id=6541
   'language/export/escaped-as-export-specifier': [FAIL],