120d433345
This is a reland of 6204768bab
The original issue exposed the problem that NumberEqual performs
implicit conversion of oddballs to numbers, which is incorrect for
abstract equality comparison (i.e. 0 == null must not be true).
This reland fixes this by applying the following steps:
* Introduced a new kNumberOrBoolean value for CompareOperationFeedback,
CompareOperationHint, TypeCheckKind and CheckedTaggedInputMode.
* In CodeStubAssembler::Equal: Further distinguish between boolean and
non-boolean oddballs and set feedback accoringly.
* In JSTypedLowering: Construct [Speculative]NumberEqual operator with
CompareOperationHint::kNumberOrBoolean, when this feedback is present.
JSOperatorBuilder and operator cache are extended accordingly.
* In SimplifiedLowering: Propagate a UseInfo with new
TypeCheckKind::kNumberOrBoolean.
* This leads to the generation of CheckedTaggedToFloat64 in
RepresentationChanger with new CheckedTaggedInputMode::kNumberOrBoolean.
* In EffectControlLinearizer: Handle this new mode. Accept and convert
number and boolean and deopt for rest.
Original change's description:
> [turbofan] Improve equality on NumberOrOddball
>
> This CL cleans up CompareOperationFeedback by replacing it with a
> composable set of flags. The interpreter is changed to collect
> more specific feedback for abstract equality, especially if oddballs
> are involved.
>
> TurboFan is changed to construct SpeculativeNumberEqual operator
> instead of the generic JSEqual in many more cases. This change has
> shown a local speedup of a factor of 3-10, because the specific
> operator is way faster than calling into the generic builtin, but
> it also enables additional optimizations, further improving
> runtime performance.
>
> Bug: v8:5660
> Change-Id: I856752caa707e9a4f742c6e7a9c75552fb431d28
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2162854
> Reviewed-by: Mythri Alle <mythria@chromium.org>
> Reviewed-by: Georg Neis <neis@chromium.org>
> Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#67645}
TBR: tebbi@chromium.org
Bug: v8:5660
Change-Id: I12e733149a1d2773cafb781a1d4b10aa1eb242a7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2193713
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: Mythri Alle <mythria@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68037}
306 lines
7.1 KiB
JavaScript
306 lines
7.1 KiB
JavaScript
// Copyright 2020 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
|
|
|
|
|
|
// smi == boolean
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertFalse(foo(2, true));
|
|
assertTrue(foo(1, true));
|
|
assertTrue(foo(0, false));
|
|
assertFalse(foo(2, false));
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertFalse(foo(2, true));
|
|
assertTrue(foo(1, true));
|
|
assertTrue(foo(0, false));
|
|
assertFalse(foo(2, false));
|
|
assertOptimized(foo);
|
|
|
|
assertFalse(foo(0, null));
|
|
assertUnoptimized(foo);
|
|
})();
|
|
|
|
|
|
// boolean == smi
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertFalse(foo(true, 2));
|
|
assertTrue(foo(true, 1));
|
|
assertTrue(foo(false, 0));
|
|
assertFalse(foo(false, 2));
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertFalse(foo(true, 2));
|
|
assertTrue(foo(true, 1));
|
|
assertTrue(foo(false, 0));
|
|
assertFalse(foo(false, 2));
|
|
assertOptimized(foo);
|
|
|
|
assertFalse(foo(null, 0));
|
|
assertUnoptimized(foo);
|
|
})();
|
|
|
|
|
|
// smi == undefined
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertFalse(foo(2, undefined));
|
|
assertFalse(foo(0, undefined));
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertFalse(foo(2, undefined));
|
|
assertFalse(foo(0, undefined));
|
|
assertOptimized(foo);
|
|
})();
|
|
|
|
|
|
// undefined == smi
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertFalse(foo(undefined, 2));
|
|
assertFalse(foo(undefined, 0));
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertFalse(foo(undefined, 2));
|
|
assertFalse(foo(undefined, 0));
|
|
assertOptimized(foo);
|
|
})();
|
|
|
|
|
|
// smi == null
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertFalse(foo(2, null));
|
|
assertFalse(foo(0, null));
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertFalse(foo(2, null));
|
|
assertFalse(foo(0, null));
|
|
assertOptimized(foo);
|
|
})();
|
|
|
|
|
|
// null == smi
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertFalse(foo(null, 2));
|
|
assertFalse(foo(null, 0));
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertFalse(foo(null, 2));
|
|
assertFalse(foo(null, 0));
|
|
assertOptimized(foo);
|
|
})();
|
|
|
|
|
|
// smi == oddball
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertFalse(foo(2, null));
|
|
assertFalse(foo(0, undefined));
|
|
assertFalse(foo(0, true));
|
|
assertTrue(foo(1, true));
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertFalse(foo(2, null));
|
|
assertFalse(foo(0, undefined));
|
|
assertFalse(foo(0, true));
|
|
assertTrue(foo(1, true));
|
|
assertOptimized(foo);
|
|
})();
|
|
|
|
|
|
// oddball == smi
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertFalse(foo(null, 2));
|
|
assertFalse(foo(undefined, 0));
|
|
assertFalse(foo(true, 0));
|
|
assertTrue(foo(true, 1));
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertFalse(foo(null, 2));
|
|
assertFalse(foo(undefined, 0));
|
|
assertFalse(foo(true, 0));
|
|
assertTrue(foo(true, 1));
|
|
assertOptimized(foo);
|
|
})();
|
|
|
|
|
|
// number == boolean
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertFalse(foo(-2.8, true));
|
|
assertTrue(foo(1, true));
|
|
assertTrue(foo(-0.0, false));
|
|
assertFalse(foo(2, false));
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertFalse(foo(-2.8, true));
|
|
assertTrue(foo(1, true));
|
|
assertTrue(foo(-0.0, false));
|
|
assertFalse(foo(2, false));
|
|
assertOptimized(foo);
|
|
|
|
assertFalse(foo(0, null));
|
|
assertUnoptimized(foo);
|
|
})();
|
|
|
|
|
|
// boolean == number
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertFalse(foo(true, -2.8));
|
|
assertTrue(foo(true, 1));
|
|
assertTrue(foo(false, -0.0));
|
|
assertFalse(foo(false, 2));
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertFalse(foo(true, -2.8));
|
|
assertTrue(foo(true, 1));
|
|
assertTrue(foo(false, -0.0));
|
|
assertFalse(foo(false, 2));
|
|
assertOptimized(foo);
|
|
|
|
assertFalse(foo(null, 0));
|
|
assertUnoptimized(foo);
|
|
})();
|
|
|
|
|
|
// number == undefined
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertFalse(foo(-2.8, undefined));
|
|
assertFalse(foo(-0.0, undefined));
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertFalse(foo(-2.8, undefined));
|
|
assertFalse(foo(-0.0, undefined));
|
|
assertOptimized(foo);
|
|
})();
|
|
|
|
|
|
// undefined == number
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertFalse(foo(undefined, -2.8));
|
|
assertFalse(foo(undefined, -0.0));
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertFalse(foo(undefined, -2.8));
|
|
assertFalse(foo(undefined, -0.0));
|
|
assertOptimized(foo);
|
|
})();
|
|
|
|
|
|
// number == null
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertFalse(foo(-2.8, null));
|
|
assertFalse(foo(-0.0, null));
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertFalse(foo(-2.8, null));
|
|
assertFalse(foo(-0.0, null));
|
|
assertOptimized(foo);
|
|
})();
|
|
|
|
|
|
// null == number
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertFalse(foo(null, -2.8));
|
|
assertFalse(foo(null, -0.0));
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertFalse(foo(null, -2.8));
|
|
assertFalse(foo(null, -0.0));
|
|
assertOptimized(foo);
|
|
})();
|
|
|
|
|
|
// number == oddball
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertFalse(foo(-2.8, null));
|
|
assertFalse(foo(-0.0, undefined));
|
|
assertFalse(foo(0, true));
|
|
assertTrue(foo(1.0, true));
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertFalse(foo(-2.8, null));
|
|
assertFalse(foo(-0.0, undefined));
|
|
assertFalse(foo(0, true));
|
|
assertTrue(foo(1.0, true));
|
|
assertOptimized(foo);
|
|
})();
|
|
|
|
|
|
// oddball == number
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertFalse(foo(null, -2.8));
|
|
assertFalse(foo(undefined, -0.0));
|
|
assertFalse(foo(true, 0));
|
|
assertTrue(foo(true, 1.0));
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertFalse(foo(null, -2.8));
|
|
assertFalse(foo(undefined, -0.0));
|
|
assertFalse(foo(true, 0));
|
|
assertTrue(foo(true, 1.0));
|
|
assertOptimized(foo);
|
|
})();
|
|
|
|
|
|
// oddball == oddball
|
|
(function() {
|
|
function foo(a, b) { return a == b; }
|
|
|
|
%PrepareFunctionForOptimization(foo);
|
|
assertTrue(foo(null, null));
|
|
assertTrue(foo(undefined, undefined));
|
|
assertTrue(foo(false, false));
|
|
assertTrue(foo(true, true));
|
|
assertTrue(foo(undefined, null));
|
|
assertFalse(foo(undefined, false));
|
|
assertFalse(foo(null, false));
|
|
assertFalse(foo(true, undefined));
|
|
assertFalse(foo(true, null));
|
|
assertFalse(foo(true, false));
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
assertTrue(foo(null, null));
|
|
assertOptimized(foo);
|
|
assertTrue(foo(undefined, undefined));
|
|
assertTrue(foo(false, false));
|
|
assertTrue(foo(true, true));
|
|
assertTrue(foo(undefined, null));
|
|
assertFalse(foo(undefined, false));
|
|
assertFalse(foo(null, false));
|
|
assertFalse(foo(true, undefined));
|
|
assertFalse(foo(true, null));
|
|
assertFalse(foo(true, false));
|
|
assertOptimized(foo);
|
|
})();
|