Commit e7e512da authored by Ujjwal Sharma's avatar Ujjwal Sharma Committed by Commit Bot

[turbofan] Add fast path for single-character String#startsWith()

This CL adds a fast path to String#startsWith(s) if s is a
single character string.

Bug: v8:8400
Change-Id: Ibd6a9d1e46d98f41c198d2b579208e25003eedb0
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1525362Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarSigurd Schneider <sigurds@chromium.org>
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61504}
parent cca9ae3c
...@@ -3550,6 +3550,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node, ...@@ -3550,6 +3550,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
return ReduceStringPrototypeSlice(node); return ReduceStringPrototypeSlice(node);
case Builtins::kStringPrototypeSubstr: case Builtins::kStringPrototypeSubstr:
return ReduceStringPrototypeSubstr(node); return ReduceStringPrototypeSubstr(node);
case Builtins::kStringPrototypeStartsWith:
return ReduceStringPrototypeStartsWith(node);
#ifdef V8_INTL_SUPPORT #ifdef V8_INTL_SUPPORT
case Builtins::kStringPrototypeToLowerCaseIntl: case Builtins::kStringPrototypeToLowerCaseIntl:
return ReduceStringPrototypeToLowerCaseIntl(node); return ReduceStringPrototypeToLowerCaseIntl(node);
...@@ -5002,6 +5004,84 @@ Reduction JSCallReducer::ReduceStringPrototypeStringAt( ...@@ -5002,6 +5004,84 @@ Reduction JSCallReducer::ReduceStringPrototypeStringAt(
return Replace(value); return Replace(value);
} }
// ES section 21.1.3.20
// String.prototype.startsWith ( searchString [ , position ] )
Reduction JSCallReducer::ReduceStringPrototypeStartsWith(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op());
if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
return NoChange();
}
if (node->op()->ValueInputCount() < 3) {
Node* value = jsgraph()->FalseConstant();
ReplaceWithValue(node, value);
return Replace(value);
}
Node* string = NodeProperties::GetValueInput(node, 1);
Node* search_string = NodeProperties::GetValueInput(node, 2);
Node* position = node->op()->ValueInputCount() >= 4
? NodeProperties::GetValueInput(node, 3)
: jsgraph()->ZeroConstant();
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
HeapObjectMatcher m(search_string);
if (m.HasValue()) {
ObjectRef target_ref = m.Ref(broker());
if (target_ref.IsString()) {
StringRef str = target_ref.AsString();
if (str.length() == 1) {
string = effect = graph()->NewNode(
simplified()->CheckString(p.feedback()), string, effect, control);
position = effect = graph()->NewNode(
simplified()->CheckSmi(p.feedback()), position, effect, control);
Node* string_length =
graph()->NewNode(simplified()->StringLength(), string);
Node* unsigned_position = graph()->NewNode(
simplified()->NumberMax(), position, jsgraph()->ZeroConstant());
Node* check = graph()->NewNode(simplified()->NumberLessThan(),
unsigned_position, string_length);
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kNone),
check, control);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* efalse = effect;
Node* vfalse = jsgraph()->FalseConstant();
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* etrue = effect;
Node* vtrue;
{
Node* masked_position =
graph()->NewNode(simplified()->PoisonIndex(), unsigned_position);
Node* string_first = efalse =
graph()->NewNode(simplified()->StringCharCodeAt(), string,
masked_position, efalse, control);
Node* search_first = jsgraph()->Constant(str.GetFirstChar());
vtrue = graph()->NewNode(simplified()->NumberEqual(), string_first,
search_first);
}
control = graph()->NewNode(common()->Merge(2), if_true, if_false);
Node* value =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
vtrue, vfalse, control);
effect =
graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
}
}
return NoChange();
}
// ES section 21.1.3.1 String.prototype.charAt ( pos ) // ES section 21.1.3.1 String.prototype.charAt ( pos )
Reduction JSCallReducer::ReduceStringPrototypeCharAt(Node* node) { Reduction JSCallReducer::ReduceStringPrototypeCharAt(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
......
...@@ -122,6 +122,7 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer { ...@@ -122,6 +122,7 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
Reduction ReduceStringPrototypeStringAt( Reduction ReduceStringPrototypeStringAt(
const Operator* string_access_operator, Node* node); const Operator* string_access_operator, Node* node);
Reduction ReduceStringPrototypeCharAt(Node* node); Reduction ReduceStringPrototypeCharAt(Node* node);
Reduction ReduceStringPrototypeStartsWith(Node* node);
#ifdef V8_INTL_SUPPORT #ifdef V8_INTL_SUPPORT
Reduction ReduceStringPrototypeToLowerCaseIntl(Node* node); Reduction ReduceStringPrototypeToLowerCaseIntl(Node* node);
......
// Copyright 2019 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 --opt
(function() {
function foo(string) { return string.startsWith('a'); }
%PrepareFunctionForOptimization(foo);
assertEquals(false, foo(''));
assertEquals(true, foo('a'));
assertEquals(false, foo('ba'));
assertEquals(true, foo('abc'));
%OptimizeFunctionOnNextCall(foo);
assertEquals(false, foo(''));
assertEquals(true, foo('a'));
assertEquals(false, foo('ba'));
assertEquals(true, foo('abc'));
assertOptimized(foo);
})();
(function() {
function f() { return "abc".startsWith(); }
%PrepareFunctionForOptimization(f);
assertEquals(false, f());
assertEquals(false, f());
%OptimizeFunctionOnNextCall(f);
assertEquals(false, f());
assertOptimized(f);
})();
(function() {
function g(n) { return "abc".startsWith("a", n); }
%PrepareFunctionForOptimization(g);
assertEquals(true, g(-1));
assertEquals(true, g(0));
assertEquals(false, g(1));
assertEquals(false, g(2));
assertEquals(false, g(3));
assertEquals(false, g(4));
%OptimizeFunctionOnNextCall(g);
assertEquals(true, g(-1));
assertEquals(true, g(0));
assertEquals(false, g(1));
assertEquals(false, g(2));
assertEquals(false, g(3));
assertEquals(false, g(4));
assertOptimized(g);
})();
(function() {
function g(n) { return "cba".startsWith("a", n); }
%PrepareFunctionForOptimization(g);
assertEquals(false, g(-1));
assertEquals(false, g(0));
assertEquals(false, g(1));
assertEquals(true, g(2));
assertEquals(false, g(3));
assertEquals(false, g(4));
%OptimizeFunctionOnNextCall(g);
assertEquals(false, g(-1));
assertEquals(false, g(0));
assertEquals(false, g(1));
assertEquals(true, g(2));
assertEquals(false, g(3));
assertEquals(false, g(4));
assertOptimized(g);
})();
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