Commit 0d42c9d0 authored by Marja Hölttä's avatar Marja Hölttä Committed by V8 LUCI CQ

[builtins] Unify ArrayPrototypeSlice & ReduceArrayPrototypeSlice

They need to agree about when to delegate to CloneFastJSArray, since it
produces arrays which are potentially COW. If they don't agree, TF
generates code which produces a COW array and then expects it to be
non-COW -> immediate deopt.

This CL gets rid of the discrepancy in the case when there's exactly
one argument and it's the number 0.

Some corner cases remain, e.g., 1st argument not a number but ToInteger
returns 0. These should be extremely rare in the real world.

Bug: v8:12194
Change-Id: I10230245c97f8997da4d79702f29ebff11297229
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3147910
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76745}
parent 034db9ef
......@@ -130,17 +130,6 @@ macro HandleFastSlice(
transitioning javascript builtin
ArrayPrototypeSlice(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// Handle array cloning case if the receiver is a fast array.
if (arguments.length == 0) {
typeswitch (receiver) {
case (a: FastJSArrayForCopy): {
return CloneFastJSArray(context, a);
}
case (JSAny): {
}
}
}
// 1. Let O be ? ToObject(this value).
const o: JSReceiver = ToObject_Inline(context, receiver);
......@@ -161,6 +150,21 @@ ArrayPrototypeSlice(
const end: JSAny = arguments[1];
const relativeEnd: Number = end == Undefined ? len : ToInteger_Inline(end);
// Handle array cloning case if the receiver is a fast array. This logic
// should be in sync with ArrayPrototypeSlice (to a reasonable degree). This
// is because CloneFastJSArray produces arrays which are potentially COW. If
// there's a discrepancy, TF generates code which produces a COW array and
// then expects it to be non-COW (or the other way around) -> immediate deopt.
if (relativeStart == 0 && end == Undefined) {
typeswitch (receiver) {
case (a: FastJSArrayForCopy): {
return CloneFastJSArray(context, a);
}
case (JSAny): {
}
}
}
// 6. If relativeEnd < 0, let final be max((len + relativeEnd), 0);
// else let final be min(relativeEnd, len).
const final: Number =
......
......@@ -5950,9 +5950,13 @@ Reduction JSCallReducer::ReduceArrayPrototypeSlice(Node* node) {
Effect effect = n.effect();
Control control = n.control();
// Optimize for the case where we simply clone the {receiver},
// i.e. when the {start} is zero and the {end} is undefined
// (meaning it will be set to {receiver}s "length" property).
// Optimize for the case where we simply clone the {receiver}, i.e. when the
// {start} is zero and the {end} is undefined (meaning it will be set to
// {receiver}s "length" property). This logic should be in sync with
// ReduceArrayPrototypeSlice (to a reasonable degree). This is because
// CloneFastJSArray produces arrays which are potentially COW. If there's a
// discrepancy, TF generates code which produces a COW array and then expects
// it to be non-COW (or the other way around) -> immediate deopt.
if (!NumberMatcher(start).Is(0) ||
!HeapObjectMatcher(end).Is(factory()->undefined_value())) {
return NoChange();
......
// Copyright 2021 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.
// Flags: --allow-natives-syntax --opt --no-always-opt --no-stress-opt
// Flags: --deopt-every-n-times=0 --no-force-slow-path
(function TestSliceWithoutParams() {
let array = [0, 1, 2];
function f() {
let array2 = array.slice();
array2[1] = array2[0];
}
%PrepareFunctionForOptimization(f);
f();
%OptimizeFunctionOnNextCall(f);
f();
// Assert that the function was not deoptimized.
assertOptimized(f);
})();
(function TestSliceWithStartZero() {
let array = [0, 1, 2];
function f() {
let array2 = array.slice(0);
array2[1] = array2[0];
}
%PrepareFunctionForOptimization(f);
f();
%OptimizeFunctionOnNextCall(f);
f();
// Assert that the function was not deoptimized.
assertOptimized(f);
})();
(function TestSliceWithStartNonZero() {
let array = [0, 1, 2];
function f() {
let array2 = array.slice(1);
array2[1] = array2[0];
}
%PrepareFunctionForOptimization(f);
f();
%OptimizeFunctionOnNextCall(f);
f();
// Assert that the function was not deoptimized.
assertOptimized(f);
})();
(function TestSliceWithStartZeroEndNonUndefined() {
let array = [0, 1, 2];
function f() {
let array2 = array.slice(0, 1);
array2[1] = array2[0];
}
%PrepareFunctionForOptimization(f);
f();
%OptimizeFunctionOnNextCall(f);
f();
// Assert that the function was not deoptimized.
assertOptimized(f);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment