[turbofan] Optimize string comparisons more
Bug: v8:7531, chromium:822170 Change-Id: Ida5b0714a584df3d830f95088db51c1841b63cc2 Reviewed-on: https://chromium-review.googlesource.com/966062 Reviewed-by: Jaroslav Sevcik <jarin@chromium.org> Commit-Queue: Sigurd Schneider <sigurds@chromium.org> Cr-Commit-Position: refs/heads/master@{#52153}
This commit is contained in:
parent
b8695906f2
commit
543c007ea6
@ -105,7 +105,9 @@ Reduction TypedOptimization::Reduce(Node* node) {
|
||||
case IrOpcode::kReferenceEqual:
|
||||
return ReduceReferenceEqual(node);
|
||||
case IrOpcode::kStringEqual:
|
||||
return ReduceStringEqual(node);
|
||||
case IrOpcode::kStringLessThan:
|
||||
case IrOpcode::kStringLessThanOrEqual:
|
||||
return ReduceStringComparison(node);
|
||||
case IrOpcode::kSameValue:
|
||||
return ReduceSameValue(node);
|
||||
case IrOpcode::kSelect:
|
||||
@ -350,26 +352,90 @@ Reduction TypedOptimization::ReduceReferenceEqual(Node* node) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
Reduction TypedOptimization::TryReduceStringEqualOfCharAtAndConstant(
|
||||
Node* equal, Node* char_at, Node* constant) {
|
||||
DCHECK_EQ(IrOpcode::kStringFromCharCode, char_at->opcode());
|
||||
const Operator* TypedOptimization::NumberComparisonFor(const Operator* op) {
|
||||
switch (op->opcode()) {
|
||||
case IrOpcode::kStringEqual:
|
||||
return simplified()->NumberEqual();
|
||||
case IrOpcode::kStringLessThan:
|
||||
return simplified()->NumberLessThan();
|
||||
case IrOpcode::kStringLessThanOrEqual:
|
||||
return simplified()->NumberLessThanOrEqual();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
Reduction
|
||||
TypedOptimization::TryReduceStringComparisonOfCharAtAndStringToConstant(
|
||||
Node* comparison, Handle<String> string, bool inverted) {
|
||||
switch (comparison->opcode()) {
|
||||
case IrOpcode::kStringEqual:
|
||||
if (string->length() != 1) {
|
||||
// String.fromCharCode(x) == "" is always false.
|
||||
return Replace(jsgraph()->BooleanConstant(false));
|
||||
}
|
||||
break;
|
||||
case IrOpcode::kStringLessThan:
|
||||
V8_FALLTHROUGH;
|
||||
case IrOpcode::kStringLessThanOrEqual:
|
||||
if (string->length() == 0) {
|
||||
// String.fromCharCode(x) <= "" is always false,
|
||||
// "" < String.fromCharCode(x) is always true.
|
||||
return Replace(jsgraph()->BooleanConstant(inverted));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
// Try to reduces a string comparison of the form
|
||||
// String.fromCharCode(x) {comparison} {constant} if inverted is false,
|
||||
// and {constant} {comparison} String.fromCharCode(x) if inverted is true.
|
||||
Reduction TypedOptimization::TryReduceStringComparisonOfCharAtAndConstant(
|
||||
Node* comparison, Node* from_char_code, Node* constant, bool inverted) {
|
||||
DCHECK_EQ(IrOpcode::kStringFromCharCode, from_char_code->opcode());
|
||||
HeapObjectMatcher m(constant);
|
||||
if (!m.HasValue() || !m.Value()->IsString()) return NoChange();
|
||||
Handle<String> string = Handle<String>::cast(m.Value());
|
||||
if (string->length() != 1) {
|
||||
// StringEqual can never be true. Out-of-bounds
|
||||
// accesses will be handled by the deopt through CheckBounds.
|
||||
return Replace(jsgraph()->BooleanConstant(false));
|
||||
|
||||
// Check if comparison can be resolved statically.
|
||||
Reduction red = TryReduceStringComparisonOfCharAtAndStringToConstant(
|
||||
comparison, string, inverted);
|
||||
if (red.Changed()) return red;
|
||||
|
||||
const Operator* comparison_op = NumberComparisonFor(comparison->op());
|
||||
Node* from_char_code_repl = NodeProperties::GetValueInput(from_char_code, 0);
|
||||
Node* constant_repl = jsgraph()->Constant(string->Get(0));
|
||||
|
||||
Node* number_comparison = nullptr;
|
||||
if (inverted) {
|
||||
// "x..." <= String.fromCharCode(z) is true if x < z.
|
||||
if (string->length() > 1 &&
|
||||
comparison->opcode() == IrOpcode::kStringLessThanOrEqual) {
|
||||
comparison_op = simplified()->NumberLessThan();
|
||||
}
|
||||
number_comparison =
|
||||
graph()->NewNode(comparison_op, constant_repl, from_char_code_repl);
|
||||
} else {
|
||||
// String.fromCharCode(z) < "x..." is true if z <= x.
|
||||
if (string->length() > 1 &&
|
||||
comparison->opcode() == IrOpcode::kStringLessThan) {
|
||||
comparison_op = simplified()->NumberLessThanOrEqual();
|
||||
}
|
||||
number_comparison =
|
||||
graph()->NewNode(comparison_op, from_char_code_repl, constant_repl);
|
||||
}
|
||||
Node* number_equal = graph()->NewNode(
|
||||
simplified()->NumberEqual(), NodeProperties::GetValueInput(char_at, 0),
|
||||
jsgraph()->Constant(string->Get(0)));
|
||||
ReplaceWithValue(equal, number_equal);
|
||||
return Replace(number_equal);
|
||||
ReplaceWithValue(comparison, number_comparison);
|
||||
return Replace(number_comparison);
|
||||
}
|
||||
|
||||
Reduction TypedOptimization::ReduceStringEqual(Node* node) {
|
||||
DCHECK_EQ(IrOpcode::kStringEqual, node->opcode());
|
||||
Reduction TypedOptimization::ReduceStringComparison(Node* node) {
|
||||
DCHECK(IrOpcode::kStringEqual == node->opcode() ||
|
||||
IrOpcode::kStringLessThan == node->opcode() ||
|
||||
IrOpcode::kStringLessThanOrEqual == node->opcode());
|
||||
Node* const lhs = NodeProperties::GetValueInput(node, 0);
|
||||
Node* const rhs = NodeProperties::GetValueInput(node, 1);
|
||||
if (lhs->opcode() == IrOpcode::kStringFromCharCode) {
|
||||
@ -392,14 +458,16 @@ Reduction TypedOptimization::ReduceStringEqual(Node* node) {
|
||||
simplified()->NumberBitwiseAnd(), right,
|
||||
jsgraph()->Constant(std::numeric_limits<uint16_t>::max()));
|
||||
}
|
||||
Node* equal = graph()->NewNode(simplified()->NumberEqual(), left, right);
|
||||
Node* equal =
|
||||
graph()->NewNode(NumberComparisonFor(node->op()), left, right);
|
||||
ReplaceWithValue(node, equal);
|
||||
return Replace(equal);
|
||||
} else {
|
||||
return TryReduceStringEqualOfCharAtAndConstant(node, lhs, rhs);
|
||||
return TryReduceStringComparisonOfCharAtAndConstant(node, lhs, rhs,
|
||||
false);
|
||||
}
|
||||
} else if (rhs->opcode() == IrOpcode::kStringFromCharCode) {
|
||||
return TryReduceStringEqualOfCharAtAndConstant(node, rhs, lhs);
|
||||
return TryReduceStringComparisonOfCharAtAndConstant(node, rhs, lhs, true);
|
||||
}
|
||||
return NoChange();
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ class V8_EXPORT_PRIVATE TypedOptimization final
|
||||
Reduction ReduceNumberToUint8Clamped(Node* node);
|
||||
Reduction ReducePhi(Node* node);
|
||||
Reduction ReduceReferenceEqual(Node* node);
|
||||
Reduction ReduceStringEqual(Node* node);
|
||||
Reduction ReduceStringComparison(Node* node);
|
||||
Reduction ReduceSameValue(Node* node);
|
||||
Reduction ReduceSelect(Node* node);
|
||||
Reduction ReduceSpeculativeToNumber(Node* node);
|
||||
@ -57,8 +57,13 @@ class V8_EXPORT_PRIVATE TypedOptimization final
|
||||
Reduction ReduceTypeOf(Node* node);
|
||||
Reduction ReduceToBoolean(Node* node);
|
||||
|
||||
Reduction TryReduceStringEqualOfCharAtAndConstant(Node* equal, Node* char_at,
|
||||
Node* constant);
|
||||
Reduction TryReduceStringComparisonOfCharAtAndConstant(Node* comparison,
|
||||
Node* from_char_code,
|
||||
Node* constant,
|
||||
bool inverted);
|
||||
Reduction TryReduceStringComparisonOfCharAtAndStringToConstant(
|
||||
Node* comparison, Handle<String> string, bool inverted);
|
||||
const Operator* NumberComparisonFor(const Operator* op);
|
||||
|
||||
CompilationDependencies* dependencies() const { return dependencies_; }
|
||||
Factory* factory() const;
|
||||
|
247
test/mjsunit/compiler/string-comparison-opt.js
Normal file
247
test/mjsunit/compiler/string-comparison-opt.js
Normal file
@ -0,0 +1,247 @@
|
||||
// Copyright 2018 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 f(a) {
|
||||
return a.charAt(1) == "";
|
||||
}
|
||||
assertEquals(false, f("aaa"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(false, f("aaa"));
|
||||
})();
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) < "";
|
||||
}
|
||||
assertEquals(false, f("aaa"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(false, f("aaa"));
|
||||
})();
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) <= "";
|
||||
}
|
||||
assertEquals(false, f("aaa"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(false, f("aaa"));
|
||||
})();
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) > "";
|
||||
}
|
||||
assertEquals(true, f("aaa"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(true, f("aaa"));
|
||||
})();
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) >= "";
|
||||
}
|
||||
assertEquals(true, f("aaa"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(true, f("aaa"));
|
||||
})();
|
||||
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) == a.charAt(2);
|
||||
}
|
||||
assertEquals(false, f("aab"));
|
||||
assertEquals(true, f("aaa"));
|
||||
assertEquals(false, f("acb"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(false, f("aab"));
|
||||
assertEquals(true, f("aaa"));
|
||||
assertEquals(false, f("acb"));
|
||||
})();
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) < a.charAt(2);
|
||||
}
|
||||
assertEquals(true, f("aab"));
|
||||
assertEquals(false, f("aaa"));
|
||||
assertEquals(false, f("acb"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(true, f("aab"));
|
||||
assertEquals(false, f("aaa"));
|
||||
assertEquals(false, f("acb"));
|
||||
})();
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) <= a.charAt(2);
|
||||
}
|
||||
assertEquals(true, f("aab"));
|
||||
assertEquals(true, f("aaa"));
|
||||
assertEquals(false, f("acb"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(true, f("aab"));
|
||||
assertEquals(true, f("aaa"));
|
||||
assertEquals(false, f("acb"));
|
||||
})();
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) > a.charAt(2);
|
||||
}
|
||||
assertEquals(false, f("aab"));
|
||||
assertEquals(false, f("aaa"));
|
||||
assertEquals(true, f("acb"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(false, f("aab"));
|
||||
assertEquals(false, f("aaa"));
|
||||
assertEquals(true, f("acb"));
|
||||
})();
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) >= a.charAt(2);
|
||||
}
|
||||
assertEquals(false, f("aab"));
|
||||
assertEquals(true, f("aaa"));
|
||||
assertEquals(true, f("acb"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(false, f("aab"));
|
||||
assertEquals(true, f("aaa"));
|
||||
assertEquals(true, f("acb"));
|
||||
})();
|
||||
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) == "b";
|
||||
}
|
||||
assertEquals(false, f("aaa"));
|
||||
assertEquals(true, f("bbb"));
|
||||
assertEquals(false, f("ccc"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(false, f("aaa"));
|
||||
assertEquals(true, f("bbb"));
|
||||
assertEquals(false, f("ccc"));
|
||||
})();
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) == "bb";
|
||||
}
|
||||
assertEquals(false, f("aaa"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(false, f("aaa"));
|
||||
})();
|
||||
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) < "b";
|
||||
}
|
||||
assertEquals(true, f("aaa"));
|
||||
assertEquals(false, f("bbb"));
|
||||
assertEquals(false, f("ccc"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(true, f("aaa"));
|
||||
assertEquals(false, f("bbb"));
|
||||
assertEquals(false, f("ccc"));
|
||||
})();
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) < "bb";
|
||||
}
|
||||
assertEquals(true, f("aaa"));
|
||||
assertEquals(true, f("bbb"));
|
||||
assertEquals(false, f("ccc"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(true, f("aaa"));
|
||||
assertEquals(true, f("bbb"));
|
||||
assertEquals(false, f("ccc"));
|
||||
})();
|
||||
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) <= "b";
|
||||
}
|
||||
assertEquals(true, f("aaa"));
|
||||
assertEquals(true, f("bbb"));
|
||||
assertEquals(false, f("ccc"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(true, f("aaa"));
|
||||
assertEquals(true, f("bbb"));
|
||||
assertEquals(false, f("ccc"));
|
||||
})();
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) <= "bb";
|
||||
}
|
||||
assertEquals(true, f("aaa"));
|
||||
assertEquals(true, f("bbb"));
|
||||
assertEquals(false, f("ccc"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(true, f("aaa"));
|
||||
assertEquals(true, f("bbb"));
|
||||
assertEquals(false, f("ccc"));
|
||||
})();
|
||||
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) > "b";
|
||||
}
|
||||
assertEquals(false, f("aaa"));
|
||||
assertEquals(false, f("bbb"));
|
||||
assertEquals(true, f("ccc"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(false, f("aaa"));
|
||||
assertEquals(false, f("bbb"));
|
||||
assertEquals(true, f("ccc"));
|
||||
})();
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) > "bb";
|
||||
}
|
||||
assertEquals(false, f("aaa"));
|
||||
assertEquals(false, f("bbb"));
|
||||
assertEquals(true, f("ccc"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(false, f("aaa"));
|
||||
assertEquals(false, f("bbb"));
|
||||
assertEquals(true, f("ccc"));
|
||||
})();
|
||||
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) >= "b";
|
||||
}
|
||||
assertEquals(false, f("aaa"));
|
||||
assertEquals(true, f("bbb"));
|
||||
assertEquals(true, f("ccc"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(false, f("aaa"));
|
||||
assertEquals(true, f("bbb"));
|
||||
assertEquals(true, f("ccc"));
|
||||
})();
|
||||
|
||||
(()=> {
|
||||
function f(a) {
|
||||
return a.charAt(1) >= "bb";
|
||||
}
|
||||
assertEquals(false, f("aaa"));
|
||||
assertEquals(false, f("bbb"));
|
||||
assertEquals(true, f("ccc"));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(false, f("aaa"));
|
||||
assertEquals(false, f("bbb"));
|
||||
assertEquals(true, f("ccc"));
|
||||
})();
|
Loading…
Reference in New Issue
Block a user