[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:
parent
c0a4680d70
commit
3200cc600c
@ -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);
|
||||
|
33
test/mjsunit/compiler/string-slice.js
Normal file
33
test/mjsunit/compiler/string-slice.js
Normal 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'));
|
||||
})();
|
Loading…
Reference in New Issue
Block a user