From 66d75d41eceb50f273e0c5020bd9488e18a63ab1 Mon Sep 17 00:00:00 2001 From: Daniel Clifford Date: Wed, 4 Oct 2017 17:36:31 +0200 Subject: [PATCH] [builtins] Enable inlining of polymorphic receivers in Array.prototype.forEach In the process, also enable support for PACKED_DOUBLE_ELEMENTS arrays. Change-Id: I16dd79276f1023e30b072d45216396533077f53c Reviewed-on: https://chromium-review.googlesource.com/571006 Commit-Queue: Daniel Clifford Reviewed-by: Benedikt Meurer Cr-Commit-Position: refs/heads/master@{#48289} --- src/compiler/js-call-reducer.cc | 40 ++++--- test/mjsunit/optimized-foreach-polymorph.js | 111 ++++++++++++++++++++ 2 files changed, 135 insertions(+), 16 deletions(-) create mode 100644 test/mjsunit/optimized-foreach-polymorph.js diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc index e4414b2b07..9ec5b83f4f 100644 --- a/src/compiler/js-call-reducer.cc +++ b/src/compiler/js-call-reducer.cc @@ -625,13 +625,26 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle function, if (result != NodeProperties::kReliableReceiverMaps) { return NoChange(); } - if (receiver_maps.size() != 1) return NoChange(); - Handle receiver_map(receiver_maps[0]); - ElementsKind kind = receiver_map->elements_kind(); - // TODO(danno): Handle double packed elements - if (!IsFastElementsKind(kind) || IsDoubleElementsKind(kind) || - !CanInlineArrayIteratingBuiltin(receiver_map)) { - return NoChange(); + if (receiver_maps.size() == 0) return NoChange(); + + ElementsKind kind = IsDoubleElementsKind(receiver_maps[0]->elements_kind()) + ? PACKED_DOUBLE_ELEMENTS + : PACKED_ELEMENTS; + for (Handle receiver_map : receiver_maps) { + ElementsKind next_kind = receiver_map->elements_kind(); + if (!CanInlineArrayIteratingBuiltin(receiver_map)) { + return NoChange(); + } + if (!IsFastElementsKind(next_kind) || + (IsDoubleElementsKind(next_kind) && IsHoleyElementsKind(next_kind))) { + return NoChange(); + } + if (IsDoubleElementsKind(kind) != IsDoubleElementsKind(next_kind)) { + return NoChange(); + } + if (IsHoleyElementsKind(next_kind)) { + kind = HOLEY_ELEMENTS; + } } // Install code dependencies on the {receiver} prototype maps and the @@ -692,14 +705,9 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle function, graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); // Make sure the map hasn't changed during the iteration - Node* orig_map = jsgraph()->HeapConstant(receiver_map); - Node* array_map = effect = - graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), - receiver, effect, control); - Node* check_map = - graph()->NewNode(simplified()->ReferenceEqual(), array_map, orig_map); - effect = - graph()->NewNode(simplified()->CheckIf(), check_map, effect, control); + effect = graph()->NewNode( + simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver, + effect, control); // Make sure that the access is still in bounds, since the callback could have // changed the array's size. @@ -717,7 +725,7 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle function, effect, control); Node* element = graph()->NewNode( - simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()), + simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(kind)), elements, k, effect, control); Node* next_k = diff --git a/test/mjsunit/optimized-foreach-polymorph.js b/test/mjsunit/optimized-foreach-polymorph.js new file mode 100644 index 0000000000..ed4958354f --- /dev/null +++ b/test/mjsunit/optimized-foreach-polymorph.js @@ -0,0 +1,111 @@ +// Copyright 2017 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 --expose-gc --turbo-inline-array-builtins + +var a = [0, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,0,0]; +var b = [{}, {}]; +var c = [,,,,,2,3,4]; +var d = [0.5,3,4]; +var e = [,,,,0.5,3,4]; + +// Make sure that calls to forEach handle a certain degree of polymorphism (no +// hole check) +(function() { + var result = 0; + var polymorph1 = function(arg) { + var sum = function(v,i,o) { + result += i; + } + arg.forEach(sum); + } + polymorph1(a); + polymorph1(a); + polymorph1(b); + polymorph1(a); + polymorph1(a); + %OptimizeFunctionOnNextCall(polymorph1); + polymorph1(a); + polymorph1(b); + assertEquals(1757, result); +})(); + +// Make sure that calls to forEach handle a certain degree of polymorphism. +(function() { + var result = 0; + var polymorph1 = function(arg) { + var sum = function(v,i,o) { + result += i; + } + arg.forEach(sum); + } + polymorph1(a); + polymorph1(a); + polymorph1(b); + polymorph1(a); + polymorph1(c); + polymorph1(a); + %OptimizeFunctionOnNextCall(polymorph1); + polymorph1(a); + polymorph1(b); + assertEquals(1775, result); +})(); + +// Make sure that calls to forEach with mixed object/double arrays don't inline +// forEach. +(function() { + var result = 0; + var polymorph1 = function(arg) { + var sum = function(v,i,o) { + result += i; + } + arg.forEach(sum); + } + polymorph1(a); + polymorph1(a); + polymorph1(b); + polymorph1(a); + polymorph1(d); + polymorph1(a); + %OptimizeFunctionOnNextCall(polymorph1); + polymorph1(a); + polymorph1(b); + assertEquals(1760, result); +})(); + +// Make sure that calls to forEach with double arrays get the right result +(function() { + var result = 0; + var polymorph1 = function(arg) { + var sum = function(v,i,o) { + result += v; + } + arg.forEach(sum); + } + polymorph1(d); + polymorph1(d); + polymorph1(d); + %OptimizeFunctionOnNextCall(polymorph1); + polymorph1(d); + polymorph1(d); + assertEquals(37.5, result); +})(); + +// Make sure that calls to forEach with mixed double arrays get the right result +(function() { + var result = 0; + var polymorph1 = function(arg) { + var sum = function(v,i,o) { + result += v; + } + arg.forEach(sum); + } + polymorph1(d); + polymorph1(e); + polymorph1(d); + %OptimizeFunctionOnNextCall(polymorph1); + polymorph1(d); + polymorph1(e); + assertEquals(37.5, result); +})();