[turbofan] Optimize String#slice(-1) calls.

In TurboFan we can easily recognize calls to String.prototype.slice
where the start parameter is -1 and the end parameter is either
undefined or not present. These calls either return an empty string if
the input string is empty, or the last character of the input string
as a single character string. So we can just make use of the existing
StringCharAt operator.

This reduces the overhead of the String.prototype.slice calls from
optimized code in the chai test of the web-tooling-benchmark
significantly. We observe a 2-3% improvement on the test.

Bug: v8:6936, v8:7137
Change-Id: Iebe02667446880f5760e3e8c80f8b7cc712df663
Reviewed-on: https://chromium-review.googlesource.com/795726
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49704}
This commit is contained in:
Benedikt Meurer 2017-11-29 10:17:43 +01:00 committed by Commit Bot
parent c0a4680d70
commit 3200cc600c
3 changed files with 90 additions and 0 deletions

View File

@ -2705,6 +2705,60 @@ Reduction JSBuiltinReducer::ReduceStringIteratorNext(Node* node) {
return NoChange();
}
// ES section #sec-string.prototype.slice
Reduction JSBuiltinReducer::ReduceStringSlice(Node* node) {
if (Node* receiver = GetStringWitness(node)) {
Node* start = node->op()->ValueInputCount() >= 3
? NodeProperties::GetValueInput(node, 2)
: jsgraph()->UndefinedConstant();
Type* start_type = NodeProperties::GetType(start);
Node* end = node->op()->ValueInputCount() >= 4
? NodeProperties::GetValueInput(node, 3)
: jsgraph()->UndefinedConstant();
Type* end_type = NodeProperties::GetType(end);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
if (start_type->Is(type_cache_.kSingletonMinusOne) &&
end_type->Is(Type::Undefined())) {
Node* receiver_length = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForStringLength()), receiver,
effect, control);
Node* check =
graph()->NewNode(simplified()->NumberEqual(), receiver_length,
jsgraph()->ZeroConstant());
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
check, control);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* vtrue = jsgraph()->EmptyStringConstant();
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* vfalse;
{
// We need to convince TurboFan that {receiver_length}-1 is a valid
// Unsigned32 value, so we just apply NumberToUint32 to the result
// of the subtraction, which is a no-op and merely acts as a marker.
Node* index =
graph()->NewNode(simplified()->NumberSubtract(), receiver_length,
jsgraph()->OneConstant());
index = graph()->NewNode(simplified()->NumberToUint32(), index);
vfalse = graph()->NewNode(simplified()->StringCharAt(), receiver, index,
if_false);
}
control = graph()->NewNode(common()->Merge(2), if_true, if_false);
Node* value =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
vtrue, vfalse, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
}
return NoChange();
}
Reduction JSBuiltinReducer::ReduceStringToLowerCaseIntl(Node* node) {
if (Node* receiver = GetStringWitness(node)) {
RelaxEffectsAndControls(node);
@ -2975,6 +3029,8 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
return ReduceStringIterator(node);
case kStringIteratorNext:
return ReduceStringIteratorNext(node);
case kStringSlice:
return ReduceStringSlice(node);
case kStringToLowerCaseIntl:
return ReduceStringToLowerCaseIntl(node);
case kStringToUpperCaseIntl:

View File

@ -117,6 +117,7 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final
Reduction ReduceStringIndexOf(Node* node);
Reduction ReduceStringIterator(Node* node);
Reduction ReduceStringIteratorNext(Node* node);
Reduction ReduceStringSlice(Node* node);
Reduction ReduceStringToLowerCaseIntl(Node* node);
Reduction ReduceStringToUpperCaseIntl(Node* node);
Reduction ReduceArrayBufferIsView(Node* node);

View File

@ -0,0 +1,33 @@
// 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
(function() {
function foo(s) { return s.slice(-1); }
assertEquals('', foo(''));
assertEquals('a', foo('a'));
assertEquals('b', foo('ab'));
assertEquals('c', foo('abc'));
%OptimizeFunctionOnNextCall(foo);
assertEquals('', foo(''));
assertEquals('a', foo('a'));
assertEquals('b', foo('ab'));
assertEquals('c', foo('abc'));
})();
(function() {
function foo(s) { return s.slice(-1, undefined); }
assertEquals('', foo(''));
assertEquals('a', foo('a'));
assertEquals('b', foo('ab'));
assertEquals('c', foo('abc'));
%OptimizeFunctionOnNextCall(foo);
assertEquals('', foo(''));
assertEquals('a', foo('a'));
assertEquals('b', foo('ab'));
assertEquals('c', foo('abc'));
})();