Commit 585b4eef authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Improve NumberMultiply typing rule.

The NumberMultiply typing rule gave up in the presence of NaN inputs,
but we can still infer useful ranges here and just union the result
of that with the NaN propagation (similar for MinusZero propagation).
This way we can still makes sense of these ranges at the uses.

Bug: v8:8015
Change-Id: Ic4c5e8edc6c68776ff3baca9628ad7de0f8e2a92
Reviewed-on: https://chromium-review.googlesource.com/c/1261143
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarSigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56539}
parent 54723cae
......@@ -223,31 +223,36 @@ Type OperationTyper::SubtractRanger(double lhs_min, double lhs_max,
// [m, +inf] - [-inf, n] = [-inf, +inf] \/ NaN
}
Type OperationTyper::MultiplyRanger(Type lhs, Type rhs) {
Type OperationTyper::MultiplyRanger(double lhs_min, double lhs_max,
double rhs_min, double rhs_max) {
double results[4];
double lmin = lhs.AsRange()->Min();
double lmax = lhs.AsRange()->Max();
double rmin = rhs.AsRange()->Min();
double rmax = rhs.AsRange()->Max();
results[0] = lmin * rmin;
results[1] = lmin * rmax;
results[2] = lmax * rmin;
results[3] = lmax * rmax;
// If the result may be nan, we give up on calculating a precise type, because
// the discontinuity makes it too complicated. Note that even if none of the
// "results" above is nan, the actual result may still be, so we have to do a
// different check:
bool maybe_nan = (lhs.Maybe(cache_.kSingletonZero) &&
(rmin == -V8_INFINITY || rmax == +V8_INFINITY)) ||
(rhs.Maybe(cache_.kSingletonZero) &&
(lmin == -V8_INFINITY || lmax == +V8_INFINITY));
if (maybe_nan) return cache_.kIntegerOrMinusZeroOrNaN; // Giving up.
bool maybe_minuszero = (lhs.Maybe(cache_.kSingletonZero) && rmin < 0) ||
(rhs.Maybe(cache_.kSingletonZero) && lmin < 0);
Type range =
Type::Range(array_min(results, 4), array_max(results, 4), zone());
return maybe_minuszero ? Type::Union(range, Type::MinusZero(), zone())
: range;
results[0] = lhs_min * rhs_min;
results[1] = lhs_min * rhs_max;
results[2] = lhs_max * rhs_min;
results[3] = lhs_max * rhs_max;
// If the result may be nan, we give up on calculating a precise type,
// because the discontinuity makes it too complicated. Note that even if
// none of the "results" above is nan, the actual result may still be, so we
// have to do a different check:
for (int i = 0; i < 4; ++i) {
if (std::isnan(results[i])) {
return cache_.kIntegerOrMinusZeroOrNaN;
}
}
double min = array_min(results, 4);
double max = array_max(results, 4);
Type type = Type::Range(min, max, zone());
if (min <= 0.0 && 0.0 <= max && (lhs_min < 0.0 || rhs_min < 0.0)) {
type = Type::Union(type, Type::MinusZero(), zone());
}
// 0 * V8_INFINITY is NaN, regardless of sign
if (((lhs_min == -V8_INFINITY || lhs_max == V8_INFINITY) &&
(rhs_min <= 0.0 && 0.0 <= rhs_max)) ||
((rhs_min == -V8_INFINITY || rhs_max == V8_INFINITY) &&
(lhs_min <= 0.0 && 0.0 <= lhs_max))) {
type = Type::Union(type, Type::NaN(), zone());
}
return type;
}
Type OperationTyper::ConvertReceiver(Type type) {
......@@ -687,14 +692,44 @@ Type OperationTyper::NumberMultiply(Type lhs, Type rhs) {
DCHECK(rhs.Is(Type::Number()));
if (lhs.IsNone() || rhs.IsNone()) return Type::None();
lhs = Rangify(lhs);
rhs = Rangify(rhs);
if (lhs.Is(Type::NaN()) || rhs.Is(Type::NaN())) return Type::NaN();
if (lhs.IsRange() && rhs.IsRange()) {
return MultiplyRanger(lhs, rhs);
// Multiplication propagates NaN:
// NaN * x = NaN (regardless of sign of x)
// 0 * Infinity = NaN (regardless of signs)
bool maybe_nan = lhs.Maybe(Type::NaN()) || rhs.Maybe(Type::NaN()) ||
(lhs.Maybe(cache_.kZeroish) &&
(rhs.Min() == -V8_INFINITY || rhs.Max() == V8_INFINITY)) ||
(rhs.Maybe(cache_.kZeroish) &&
(lhs.Min() == -V8_INFINITY || lhs.Max() == V8_INFINITY));
lhs = Type::Intersect(lhs, Type::OrderedNumber(), zone());
DCHECK(!lhs.IsNone());
rhs = Type::Intersect(rhs, Type::OrderedNumber(), zone());
DCHECK(!rhs.IsNone());
// Try to rule out -0.
bool maybe_minuszero = lhs.Maybe(Type::MinusZero()) ||
rhs.Maybe(Type::MinusZero()) ||
(lhs.Maybe(cache_.kZeroish) && rhs.Min() < 0.0) ||
(rhs.Maybe(cache_.kZeroish) && lhs.Min() < 0.0);
if (lhs.Maybe(Type::MinusZero())) {
lhs = Type::Union(lhs, cache_.kSingletonZero, zone());
lhs = Type::Intersect(lhs, Type::PlainNumber(), zone());
}
return Type::Number();
if (rhs.Maybe(Type::MinusZero())) {
rhs = Type::Union(rhs, cache_.kSingletonZero, zone());
rhs = Type::Intersect(rhs, Type::PlainNumber(), zone());
}
// Compute the effective type, utilizing range information if possible.
Type type = (lhs.Is(cache_.kInteger) && rhs.Is(cache_.kInteger))
? MultiplyRanger(lhs.Min(), lhs.Max(), rhs.Min(), rhs.Max())
: Type::OrderedNumber();
// Take into account the -0 and NaN information computed earlier.
if (maybe_minuszero) type = Type::Union(type, Type::MinusZero(), zone());
if (maybe_nan) type = Type::Union(type, Type::NaN(), zone());
return type;
}
Type OperationTyper::NumberDivide(Type lhs, Type rhs) {
......
......@@ -87,7 +87,8 @@ class V8_EXPORT_PRIVATE OperationTyper {
double rhs_max);
Type SubtractRanger(double lhs_min, double lhs_max, double rhs_min,
double rhs_max);
Type MultiplyRanger(Type lhs, Type rhs);
Type MultiplyRanger(double lhs_min, double lhs_max, double rhs_min,
double rhs_max);
Zone* zone() const { return zone_; }
......
// 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 --opt
// Test the extreme case where -0 is produced by rounding errors.
(function() {
function bar(x) {
return 1e-308 * x;
}
bar(1);
function foo() {
return Object.is(-0, bar(-1e-308));
}
assertTrue(foo());
assertTrue(foo());
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo());
})();
// Test that multiplication of integer by 0 produces the correct results.
(function() {
function foo(x) {
return 0 * Math.round(x);
}
assertEquals(0, foo(0.1));
assertEquals(-0, foo(-0.1));
assertEquals(NaN, foo(NaN));
assertEquals(NaN, foo(Infinity));
assertEquals(NaN, foo(-Infinity));
%OptimizeFunctionOnNextCall(foo);
assertEquals(0, foo(0.1));
assertEquals(-0, foo(-0.1));
assertEquals(NaN, foo(NaN));
assertEquals(NaN, foo(Infinity));
assertEquals(NaN, foo(-Infinity));
})();
// Test that multiplication properly preserves -0 and NaN, and doesn't
// cut it short incorrectly.
(function() {
function foo(x, y) {
x = Math.sign(x);
y = Math.sign(y);
return Math.min(x * y, 0);
}
assertEquals(0, foo(1, 0));
assertEquals(-0, foo(1, -0));
assertEquals(NaN, foo(NaN, -0));
%OptimizeFunctionOnNextCall(foo);
assertEquals(0, foo(1, 0));
assertEquals(-0, foo(1, -0));
assertEquals(NaN, foo(NaN, -0));
})();
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