[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:
parent
1e948174dc
commit
9641ce6438
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
152
test/mjsunit/optimized-string-includes.js
Normal file
152
test/mjsunit/optimized-string-includes.js
Normal 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));
|
||||
})();
|
Loading…
Reference in New Issue
Block a user