Commit fb922b17 authored by Mike Stanton's avatar Mike Stanton Committed by Commit Bot

[TurboFan] Allow unreliable maps in array builtins

We don't currently inline array builtins if we detect any side effects
between the load of the receiver map and the call to the builtin.
The introduction of a map check allows us to be more permissive.

Bug: v8:7250
Change-Id: I6b3f9243f6506eff45c0d727c47a7e8cb8765640
Reviewed-on: https://chromium-review.googlesource.com/849005
Commit-Queue: Michael Stanton <mvstanton@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50620}
parent 820211cc
This diff is collapsed.
......@@ -19,12 +19,19 @@ function RunOptFastEvery(multiple) {
%NeverOptimizeFunction(OptFastEvery);
function OptFastEvery() { RunOptFastEvery(3); }
function side_effect(a) { return a; }
%NeverOptimizeFunction(side_effect);
function OptUnreliableEvery() {
result = array.every(func, side_effect(array));
}
DefineHigherOrderTests([
// name, test function, setup function, user callback
"DoubleEvery", mc("every"), DoubleSetup, v => v > 0.0,
"SmiEvery", mc("every"), SmiSetup, v => v != 34343,
"FastEvery", mc("every"), FastSetup, v => v !== 'hi',
"OptFastEvery", OptFastEvery, FastSetup, v => true,
"OptUnreliableEvery", OptUnreliableEvery, FastSetup, v => true
]);
})();
......@@ -46,6 +46,12 @@ function RunOptFastFilter(multiple) {
%NeverOptimizeFunction(OptFastFilter);
function OptFastFilter() { RunOptFastFilter(3); }
function side_effect(a) { return a; }
%NeverOptimizeFunction(side_effect);
function OptUnreliableFilter() {
result = array.filter(func, side_effect(array));
}
DefineHigherOrderTests([
// name, test function, setup function, user callback
"NaiveFilterReplacement", NaiveFilter, NaiveFilterSetup, v => true,
......@@ -53,7 +59,8 @@ DefineHigherOrderTests([
"SmiFilter", mc("filter"), SmiSetup, v => v % 2 === 0,
"FastFilter", mc("filter"), FastSetup, (_, i) => i % 2 === 0,
"GenericFilter", mc("filter", true), ObjectSetup, (_, i) => i % 2 === 0,
"OptFastFilter", OptFastFilter, FastSetup, undefined
"OptFastFilter", OptFastFilter, FastSetup, undefined,
"OptUnreliableFilter", OptUnreliableFilter, FastSetup, v => true
]);
})();
......@@ -19,6 +19,12 @@ function RunOptFast(multiple) {
%NeverOptimizeFunction(OptFast);
function OptFast() { RunOptFast(max_index); }
function side_effect(a) { return a; }
%NeverOptimizeFunction(side_effect);
function OptUnreliable() {
result = array.findIndex(func, side_effect(array));
}
function Naive() {
let index = -1;
const length = array == null ? 0 : array.length;
......@@ -50,7 +56,8 @@ DefineHigherOrderTests([
"SmiFindIndex", mc("findIndex"), SmiSetup, v => v === max_index,
"FastFindIndex", mc("findIndex"), FastSetup, v => v === `value ${max_index}`,
"GenericFindIndex", mc("findIndex", true), ObjectSetup, v => v === max_index,
"OptFastFindIndex", OptFast, FastSetup, undefined
"OptFastFindIndex", OptFast, FastSetup, undefined,
"OptUnreliableFindIndex", OptUnreliable, FastSetup, v => v === max_index
]);
})();
......@@ -19,6 +19,12 @@ function RunOptFast(multiple) {
%NeverOptimizeFunction(OptFast);
function OptFast() { RunOptFast(max_index); }
function side_effect(a) { return a; }
%NeverOptimizeFunction(side_effect);
function OptUnreliable() {
result = array.find(func, side_effect(array));
}
function Naive() {
let index = -1;
const length = array == null ? 0 : array.length;
......@@ -50,7 +56,8 @@ DefineHigherOrderTests([
"SmiFind", mc("find"), SmiSetup, v => v === max_index,
"FastFind", mc("find"), FastSetup, v => v === `value ${max_index}`,
"GenericFind", mc("find", true), ObjectSetup, v => v === max_index,
"OptFastFind", OptFast, FastSetup, undefined
"OptFastFind", OptFast, FastSetup, undefined,
"OptUnreliableFind", OptUnreliable, FastSetup, v => v === max_index
]);
})();
// 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.
(() => {
function Naive() {
let index = -1;
const length = array == null ? 0 : array.length;
for (let index = 0; index < length; index++) {
const value = array[index];
if (func(value, index, array)) {
result = value;
break;
}
}
}
function NaiveSetup() {
// Prime Naive with polymorphic cases.
array = [1, 2, 3];
Naive();
Naive();
array = [3.4]; Naive();
array = new Array(10); array[0] = 'hello'; Naive();
SmiSetup();
delete array[1];
}
// Make sure we inline the callback, pick up all possible TurboFan
// optimizations.
function RunOptFast(multiple) {
// Use of variable multiple in the callback function forces
// context creation without escape analysis.
//
// Also, the arrow function requires inlining based on
// SharedFunctionInfo.
result = array.forEach((v, i, a) => v === `value ${multiple}`);
}
// Don't optimize because I want to optimize RunOptFast with a parameter
// to be used in the callback.
%NeverOptimizeFunction(OptFast);
function OptFast() { RunOptFast(max_index); }
function side_effect(a) { return a; }
%NeverOptimizeFunction(side_effect);
function OptUnreliable() {
result = array.forEach(func, side_effect(array));
}
DefineHigherOrderTests([
"NaiveForEachReplacement", Naive, NaiveSetup, v => v === max_index,
"DoubleForEach", mc("forEach"), DoubleSetup, v => v === max_index + 0.5,
"SmiForEach", mc("forEach"), SmiSetup, v => v === max_index,
"FastForEach", mc("forEach"), FastSetup, v => v === `value ${max_index}`,
"GenericForEach", mc("forEach", true), ObjectSetup, v => v === max_index,
"OptFastForEach", OptFast, FastSetup, undefined,
"OptUnreliableForEach", OptUnreliable, FastSetup, v => v === `value ${max_index}`
]);
})();
......@@ -14,7 +14,6 @@ function NaiveMap() {
return result
}
function NaiveMapSetup() {
// Prime NaiveMap with polymorphic cases.
array = [1, 2, 3];
......@@ -42,6 +41,12 @@ function RunOptFastMap(multiple) {
%NeverOptimizeFunction(OptFastMap);
function OptFastMap() { RunOptFastMap(3); }
function side_effect(a) { return a; }
%NeverOptimizeFunction(side_effect);
function OptUnreliableMap() {
result = array.map(func, side_effect(array));
}
DefineHigherOrderTests([
// name, test function, setup function, user callback
"NaiveMapReplacement", NaiveMap, NaiveMapSetup, v => v,
......@@ -52,6 +57,7 @@ DefineHigherOrderTests([
"SmallSmiToFastMap", mc("map"), SmiSetup, v => "hi" + v,
"GenericMap", mc("map", true), ObjectSetup, v => v,
"OptFastMap", OptFastMap, FastSetup, undefined,
"OptUnreliableMap", OptUnreliableMap, FastSetup, v => v
]);
})();
......@@ -19,12 +19,20 @@ function RunOptFastReduceRight(multiple) {
%NeverOptimizeFunction(OptFastReduceRight);
function OptFastReduceRight() { RunOptFastReduceRight(3); }
function side_effect(a) { return a; }
%NeverOptimizeFunction(side_effect);
function OptUnreliableReduceRight() {
result = array.reduceRight(func, side_effect(array));
}
DefineHigherOrderTests([
// name, test function, setup function, user callback
"DoubleReduceRight", mc("reduceRight"), DoubleSetup, (p, v, i, o) => p + v,
"SmiReduceRight", mc("reduceRight"), SmiSetup, (p, v, i, a) => p + 1,
"FastReduceRight", mc("reduceRight"), FastSetup, (p, v, i, a) => p + v,
"OptFastReduceRight", OptFastReduceRight, FastSetup, undefined
"OptFastReduceRight", OptFastReduceRight, FastSetup, undefined,
"OptUnreliableReduceRight", OptUnreliableReduceRight, FastSetup,
(p, v, i, a) => p + v
]);
})();
......@@ -19,12 +19,20 @@ function RunOptFastReduce(multiple) {
%NeverOptimizeFunction(OptFastReduce);
function OptFastReduce() { RunOptFastReduce(3); }
function side_effect(a) { return a; }
%NeverOptimizeFunction(side_effect);
function OptUnreliableReduce() {
result = array.reduce(func, side_effect(array));
}
DefineHigherOrderTests([
// name, test function, setup function, user callback
"DoubleReduce", mc("reduce"), DoubleSetup, (p, v, i, o) => p + v,
"SmiReduce", mc("reduce"), SmiSetup, (p, v, i, a) => p + 1,
"FastReduce", mc("reduce"), FastSetup, (p, v, i, a) => p + v,
"OptFastReduce", OptFastReduce, FastSetup, undefined
"OptFastReduce", OptFastReduce, FastSetup, undefined,
"OptUnreliableReduce", OptUnreliableReduce, FastSetup,
(p, v, i, a) => p = v
]);
})();
......@@ -72,6 +72,7 @@ load('filter.js');
load('map.js');
load('every.js');
load('some.js');
load('for-each.js');
load('reduce.js');
load('reduce-right.js');
load('find.js');
......
......@@ -19,12 +19,19 @@ function RunOptFastSome(multiple) {
%NeverOptimizeFunction(OptFastSome);
function OptFastSome() { RunOptFastSome(3); }
function side_effect(a) { return a; }
%NeverOptimizeFunction(side_effect);
function OptUnreliableSome() {
result = array.some(func, side_effect(array));
}
DefineHigherOrderTests([
// name, test function, setup function, user callback
"DoubleSome", mc("some"), DoubleSetup, v => v < 0.0,
"SmiSome", mc("some"), SmiSetup, v => v === 34343,
"FastSome", mc("some"), FastSetup, v => v === 'hi',
"OptFastSome", OptFastSome, FastSetup, undefined
"OptFastSome", OptFastSome, FastSetup, undefined,
"OptUnreliableSome", OptUnreliableSome, FastSetup, v => v === 'hi'
]);
})();
......@@ -404,22 +404,32 @@
],
"results_regexp": "^%s\\-Array\\(Score\\): (.+)$",
"tests": [
{"name": "NaiveForEachReplacement"},
{"name": "DoubleForEach"},
{"name": "SmiForEach"},
{"name": "FastForEach"},
{"name": "GenericForEach"},
{"name": "OptFastForEach"},
{"name": "OptUnreliableForEach"},
{"name": "NaiveFilterReplacement"},
{"name": "DoubleFilter"},
{"name": "SmiFilter"},
{"name": "FastFilter"},
{"name": "GenericFilter"},
{"name": "OptFastFilter"},
{"name": "OptUnreliableFilter"},
{"name": "NaiveMapReplacement"},
{"name": "DoubleMap"},
{"name": "SmiMap"},
{"name": "FastMap"},
{"name": "GenericMap"},
{"name": "OptFastMap"},
{"name": "OptUnreliableMap"},
{"name": "DoubleEvery"},
{"name": "SmiEvery"},
{"name": "FastEvery"},
{"name": "OptFastEvery"},
{"name": "OptUnreliableEvery"},
{"name": "SmiJoin"},
{"name": "StringJoin"},
{"name": "SparseSmiJoin"},
......@@ -428,14 +438,17 @@
{"name": "SmiSome"},
{"name": "FastSome"},
{"name": "OptFastSome"},
{"name": "OptUnreliableSome"},
{"name": "DoubleReduce"},
{"name": "SmiReduce"},
{"name": "FastReduce"},
{"name": "OptFastReduce"},
{"name": "OptUnreliableReduce"},
{"name": "DoubleReduceRight"},
{"name": "SmiReduceRight"},
{"name": "FastReduceRight"},
{"name": "OptFastReduceRight"},
{"name": "OptUnreliableReduceRight"},
{"name": "SmiToString"},
{"name": "StringToString"},
{"name": "SparseSmiToString"},
......@@ -446,12 +459,14 @@
{"name": "FastFind"},
{"name": "GenericFind"},
{"name": "OptFastFind"},
{"name": "OptUnreliableFind"},
{"name": "NaiveFindIndexReplacement"},
{"name": "DoubleFindIndex"},
{"name": "SmiFindIndex"},
{"name": "FastFindIndex"},
{"name": "GenericFindIndex"},
{"name": "OptFastFindIndex"},
{"name": "OptUnreliableFindIndex"},
{"name": "EmptyArrayOf"},
{"name": "SmallSmiArrayOf"},
{"name": "LargeSmiArrayOf"},
......
......@@ -435,26 +435,6 @@
}
})();
// Messing with the Array prototype causes deoptimization.
(() => {
const a = [1, 2, 3];
let result = 0;
function prototypeChanged() {
a.every((v, i) => {
result += v;
return true;
});
}
prototypeChanged();
prototypeChanged();
%OptimizeFunctionOnNextCall(prototypeChanged);
prototypeChanged();
a.constructor = {};
prototypeChanged();
assertUnoptimized(prototypeChanged);
assertEquals(24, result);
})();
// Verify holes are skipped.
(() => {
const a = [1, 2, , 3, 4];
......@@ -488,6 +468,24 @@
assertArrayEquals([1.5, 2.5, 3.5, 4.5], withHoles());
})();
// Ensure that we handle side-effects between load and call.
(() => {
function side_effect(a, b) { if (b) a.foo = 3; return a; }
%NeverOptimizeFunction(side_effect);
function unreliable(a, b) {
return a.every(x => true, side_effect(a, b));
}
let a = [1, 2, 3];
unreliable(a, false);
unreliable(a, false);
%OptimizeFunctionOnNextCall(unreliable);
unreliable(a, false);
// Now actually do change the map.
unreliable(a, true);
})();
// Handle callback is not callable.
(() => {
const a = [1, 2, 3, 4, 5];
......@@ -500,3 +498,23 @@
%OptimizeFunctionOnNextCall(notCallable);
assertThrows(notCallable, TypeError);
})();
// Messing with the Array prototype causes deoptimization.
(() => {
const a = [1, 2, 3];
let result = 0;
function prototypeChanged() {
a.every((v, i) => {
result += v;
return true;
});
}
prototypeChanged();
prototypeChanged();
%OptimizeFunctionOnNextCall(prototypeChanged);
prototypeChanged();
a.constructor = {};
prototypeChanged();
assertUnoptimized(prototypeChanged);
assertEquals(24, result);
})();
......@@ -428,6 +428,24 @@
assertArrayEquals([1.5, 2.5, undefined, 3.5, 4.5], withHoles());
})();
// Ensure that we handle side-effects between load and call.
(() => {
function side_effect(a, b) { if (b) a.foo = 3; return a; }
%NeverOptimizeFunction(side_effect);
function unreliable(a, b) {
return a.find(x => false, side_effect(a, b));
}
let a = [1, 2, 3];
unreliable(a, false);
unreliable(a, false);
%OptimizeFunctionOnNextCall(unreliable);
unreliable(a, false);
// Now actually do change the map.
unreliable(a, true);
})();
// Handle callback is not callable.
(() => {
const a = [1, 2, 3, 4, 5];
......
......@@ -428,6 +428,24 @@
assertArrayEquals([1.5, 2.5, undefined, 3.5, 4.5], withHoles());
})();
// Ensure that we handle side-effects between load and call.
(() => {
function side_effect(a, b) { if (b) a.foo = 3; return a; }
%NeverOptimizeFunction(side_effect);
function unreliable(a, b) {
return a.findIndex(x => false, side_effect(a, b));
}
let a = [1, 2, 3];
unreliable(a, false);
unreliable(a, false);
%OptimizeFunctionOnNextCall(unreliable);
unreliable(a, false);
// Now actually do change the map.
unreliable(a, true);
})();
// Handle callback is not callable.
(() => {
const a = [1, 2, 3, 4, 5];
......
......@@ -452,6 +452,24 @@
assertArrayEquals([1.5, 2.5, 3.5, 4.5], callback_values);
})();
// Ensure that we handle side-effects between load and call.
(() => {
function side_effect(a, b) { if (b) a.foo = 3; return a; }
%NeverOptimizeFunction(side_effect);
function unreliable(a, b) {
return a.filter(x => x % 2 === 0, side_effect(a, b));
}
let a = [1, 2, 3];
unreliable(a, false);
unreliable(a, false);
%OptimizeFunctionOnNextCall(unreliable);
unreliable(a, false);
// Now actually do change the map.
unreliable(a, true);
})();
// Messing with the Array species constructor causes deoptimization.
(function() {
var result = 0;
......
......@@ -374,3 +374,22 @@ var c = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25];
%OptimizeFunctionOnNextCall(withHoles);
assertArrayEquals([1.5, 2.5, 3.5, 4.5], withHoles());
})();
// Ensure that we handle side-effects between load and call.
(() => {
function side_effect(a, b) { if (b) a.foo = 3; return a; }
%NeverOptimizeFunction(side_effect);
function unreliable(a, b) {
let sum = 0;
return a.forEach(x => sum += x, side_effect(a, b));
}
let a = [1, 2, 3];
unreliable(a, false);
unreliable(a, false);
%OptimizeFunctionOnNextCall(unreliable);
unreliable(a, false);
// Now actually do change the map.
unreliable(a, true);
})();
......@@ -503,6 +503,24 @@ var c = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25];
assertArrayEquals([1.5, 2.5, 3.5, 4.5], callback_values);
})();
// Ensure that we handle side-effects between load and call.
(() => {
function side_effect(a, b) { if (b) a.foo = 3; return a; }
%NeverOptimizeFunction(side_effect);
function unreliable(a, b) {
return a.map(x => x * 2, side_effect(a, b));
}
let a = [1, 2, 3];
unreliable(a, false);
unreliable(a, false);
%OptimizeFunctionOnNextCall(unreliable);
unreliable(a, false);
// Now actually do change the map.
unreliable(a, true);
})();
// Messing with the Array species constructor causes deoptimization.
(function() {
var result = 0;
......
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