[compiler] Optimize String#includes

This CL adds the reduction for String#includes
and merges the reduction of String#indexOf and
String#includes in JSCallReducer.

This CL does two things:
- Add StringIndexOfIncludesVariant to distinguish
String#indexOf and String#includes.
- Add ReduceStringPrototypeIndexOfIncludes to reduce
for String#indexOf and String#includes.

Bug: v8:12732
Change-Id: Ied75485cf1511956e97ef986fc34a711aae3d1ce
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3552279
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79648}
This commit is contained in:
jameslahm 2022-03-29 16:35:27 +08:00 committed by V8 LUCI CQ
parent 1e948174dc
commit 9641ce6438
3 changed files with 175 additions and 4 deletions

View File

@ -4791,7 +4791,11 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
case Builtin::kReturnReceiver:
return ReduceReturnReceiver(node);
case Builtin::kStringPrototypeIndexOf:
return ReduceStringPrototypeIndexOf(node);
return ReduceStringPrototypeIndexOfIncludes(
node, StringIndexOfIncludesVariant::kIndexOf);
case Builtin::kStringPrototypeIncludes:
return ReduceStringPrototypeIndexOfIncludes(
node, StringIndexOfIncludesVariant::kIncludes);
case Builtin::kStringPrototypeCharAt:
return ReduceStringPrototypeCharAt(node);
case Builtin::kStringPrototypeCharCodeAt:
@ -5248,7 +5252,9 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
}
// ES #sec-string.prototype.indexof
Reduction JSCallReducer::ReduceStringPrototypeIndexOf(Node* node) {
// ES #sec-string.prototype.includes
Reduction JSCallReducer::ReduceStringPrototypeIndexOfIncludes(
Node* node, StringIndexOfIncludesVariant variant) {
JSCallNode n(node);
CallParameters const& p = n.Parameters();
if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
@ -5289,7 +5295,17 @@ Reduction JSCallReducer::ReduceStringPrototypeIndexOf(Node* node) {
node->ReplaceInput(2, new_position);
node->TrimInputCount(3);
NodeProperties::ChangeOp(node, simplified()->StringIndexOf());
return Changed(node);
if (variant == StringIndexOfIncludesVariant::kIndexOf) {
return Changed(node);
} else {
DCHECK(variant == StringIndexOfIncludesVariant::kIncludes);
Node* result =
graph()->NewNode(simplified()->BooleanNot(),
graph()->NewNode(simplified()->NumberEqual(), node,
jsgraph()->SmiConstant(-1)));
return Replace(result);
}
}
return NoChange();
}

View File

@ -145,7 +145,10 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
Reduction ReduceJSCallWithSpread(Node* node);
Reduction ReduceRegExpPrototypeTest(Node* node);
Reduction ReduceReturnReceiver(Node* node);
Reduction ReduceStringPrototypeIndexOf(Node* node);
enum class StringIndexOfIncludesVariant { kIncludes, kIndexOf };
Reduction ReduceStringPrototypeIndexOfIncludes(
Node* node, StringIndexOfIncludesVariant variant);
Reduction ReduceStringPrototypeSubstring(Node* node);
Reduction ReduceStringPrototypeSlice(Node* node);
Reduction ReduceStringPrototypeSubstr(Node* node);

View File

@ -0,0 +1,152 @@
// Copyright 2022 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 --no-always-opt
(function optimize() {
function f() {
return 'abc'.includes('a');
}
%PrepareFunctionForOptimization(f);
assertEquals(true, f());
assertEquals(true, f());
assertEquals(true, f());
%OptimizeFunctionOnNextCall(f);
assertEquals(true, f());
assertTrue(isOptimized(f));
function f2() {
return 'abc'.includes('a', 1);
}
%PrepareFunctionForOptimization(f2);
assertEquals(false, f2());
assertEquals(false, f2());
assertEquals(false, f2());
%OptimizeFunctionOnNextCall(f2);
assertEquals(false, f2());
assertTrue(isOptimized(f2));
function f3() {
return 'abc'.includes('b');
}
%PrepareFunctionForOptimization(f3);
assertEquals(true, f3());
assertEquals(true, f3());
assertEquals(true, f3());
%OptimizeFunctionOnNextCall(f3);
assertEquals(true, f3());
assertTrue(isOptimized(f3));
function f4() {
return 'abcbc'.includes('bc', 2);
}
%PrepareFunctionForOptimization(f4);
assertEquals(true, f4());
assertEquals(true, f4());
assertEquals(true, f4());
%OptimizeFunctionOnNextCall(f4);
assertEquals(true, f4());
assertTrue(isOptimized(f4));
function f5() {
return 'abcbc'.includes('b', -1);
}
%PrepareFunctionForOptimization(f5);
assertEquals(true, f5());
assertEquals(true, f5());
assertEquals(true, f5());
%OptimizeFunctionOnNextCall(f5);
assertEquals(true, f5());
assertTrue(isOptimized(f5));
function f6() {
return 'abcbc'.includes('b', -10737418);
}
%PrepareFunctionForOptimization(f6);
assertEquals(true, f6());
assertEquals(true, f6());
assertEquals(true, f6());
%OptimizeFunctionOnNextCall(f6);
assertEquals(true, f6());
assertTrue(isOptimized(f6));
})();
(function optimizeOSR() {
function f() {
var result;
for (var i = 0; i < 100000; i++) {
result = 'abc'.includes('a');
}
return result;
}
assertEquals(true, f());
function f2() {
var result;
for (var i = 0; i < 100000; i++) {
result = 'abc'.includes('a', 1);
}
return result;
}
assertEquals(false, f2());
function f3() {
var result;
for (var i = 0; i < 100000; i++) {
result = 'abc'.includes('b');
}
return result;
}
assertEquals(true, f3());
function f4() {
var result;
for (var i = 0; i < 100000; i++) {
result = 'abcbc'.includes('bc', 2);
}
return result;
}
assertEquals(true, f4());
})();
(function bailout() {
function f(str) {
return String.prototype.includes.call(str, 'a')
}
%PrepareFunctionForOptimization(f);
assertEquals(true, f('abc'));
%OptimizeFunctionOnNextCall(f);
assertEquals(true, f({
toString: () => {
return 'abc'
}
}));
assertFalse(isOptimized(f));
function f2(str) {
return 'abc'.includes(str)
}
%PrepareFunctionForOptimization(f2);
assertEquals(true, f2('a'));
%OptimizeFunctionOnNextCall(f2);
assertEquals(true, f2({
toString: () => {
return 'a'
}
}));
assertFalse(isOptimized(f2));
function f3(index) {
return 'abc'.includes('a', index)
}
%PrepareFunctionForOptimization(f3);
assertEquals(true, f3(0));
%OptimizeFunctionOnNextCall(f3);
assertEquals(true, f3({
valueOf: () => {
return 0
}
}));
assertFalse(isOptimized(f3));
})();