Commit 3200cc60 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[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: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49704}
parent c0a4680d
......@@ -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:
......
......@@ -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);
......
// 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'));
})();
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