| Index: src/compiler/operation-typer.cc
|
| diff --git a/src/compiler/operation-typer.cc b/src/compiler/operation-typer.cc
|
| index c8367d197b77625ab89a40369ce8b3fbb68ba389..7f6650af36219cd3a878dca95d04464a736d353d 100644
|
| --- a/src/compiler/operation-typer.cc
|
| +++ b/src/compiler/operation-typer.cc
|
| @@ -18,9 +18,20 @@ namespace compiler {
|
| OperationTyper::OperationTyper(Isolate* isolate, Zone* zone)
|
| : zone_(zone), cache_(TypeCache::Get()) {
|
| Factory* factory = isolate->factory();
|
| + Type* infinity = Type::Constant(factory->infinity_value(), zone);
|
| + Type* minus_infinity = Type::Constant(factory->minus_infinity_value(), zone);
|
| + // Unfortunately, the infinities created in other places might be different
|
| + // ones (eg the result of NewNumber in TypeNumberConstant).
|
| + Type* truncating_to_zero =
|
| + Type::Union(Type::Union(infinity, minus_infinity, zone),
|
| + Type::MinusZeroOrNaN(), zone);
|
| + DCHECK(!truncating_to_zero->Maybe(Type::Integral32()));
|
| +
|
| singleton_false_ = Type::Constant(factory->false_value(), zone);
|
| singleton_true_ = Type::Constant(factory->true_value(), zone);
|
| singleton_the_hole_ = Type::Constant(factory->the_hole_value(), zone);
|
| + signed32ish_ = Type::Union(Type::Signed32(), truncating_to_zero, zone);
|
| + unsigned32ish_ = Type::Union(Type::Unsigned32(), truncating_to_zero, zone);
|
| }
|
|
|
| Type* OperationTyper::Merge(Type* left, Type* right) {
|
| @@ -214,12 +225,9 @@ Type* OperationTyper::MultiplyRanger(Type* lhs, Type* rhs) {
|
| 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
|
| + // 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)) ||
|
| @@ -241,20 +249,244 @@ Type* OperationTyper::ToNumber(Type* type) {
|
| if (type->Is(Type::Undefined())) return Type::NaN();
|
| return Type::Union(Type::NaN(), cache_.kSingletonZero, zone());
|
| }
|
| - if (type->Is(Type::NumberOrUndefined())) {
|
| - return Type::Union(Type::Intersect(type, Type::Number(), zone()),
|
| - Type::NaN(), zone());
|
| + if (type->Is(Type::Boolean())) {
|
| + if (type->Is(singleton_false_)) return cache_.kSingletonZero;
|
| + if (type->Is(singleton_true_)) return cache_.kSingletonOne;
|
| + return cache_.kZeroOrOne;
|
| + }
|
| + if (type->Is(Type::NumberOrOddball())) {
|
| + if (type->Is(Type::NumberOrUndefined())) {
|
| + type = Type::Union(type, Type::NaN(), zone());
|
| + } else if (type->Is(Type::NullOrNumber())) {
|
| + type = Type::Union(type, cache_.kSingletonZero, zone());
|
| + } else if (type->Is(Type::BooleanOrNullOrNumber())) {
|
| + type = Type::Union(type, cache_.kZeroOrOne, zone());
|
| + } else {
|
| + type = Type::Union(type, cache_.kZeroOrOneOrNaN, zone());
|
| + }
|
| + return Type::Intersect(type, Type::Number(), zone());
|
| }
|
| - if (type->Is(singleton_false_)) return cache_.kSingletonZero;
|
| - if (type->Is(singleton_true_)) return cache_.kSingletonOne;
|
| - if (type->Is(Type::Boolean())) return cache_.kZeroOrOne;
|
| - if (type->Is(Type::BooleanOrNumber())) {
|
| - return Type::Union(Type::Intersect(type, Type::Number(), zone()),
|
| - cache_.kZeroOrOne, zone());
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberAbs(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| +
|
| + if (!type->IsInhabited()) {
|
| + return Type::None();
|
| }
|
| +
|
| + bool const maybe_nan = type->Maybe(Type::NaN());
|
| + bool const maybe_minuszero = type->Maybe(Type::MinusZero());
|
| + type = Type::Intersect(type, Type::PlainNumber(), zone());
|
| + double const max = type->Max();
|
| + double const min = type->Min();
|
| + if (min < 0) {
|
| + if (type->Is(cache_.kInteger)) {
|
| + type = Type::Range(0.0, std::max(std::fabs(min), std::fabs(max)), zone());
|
| + } else {
|
| + type = Type::PlainNumber();
|
| + }
|
| + }
|
| + if (maybe_minuszero) {
|
| + type = Type::Union(type, cache_.kSingletonZero, zone());
|
| + }
|
| + if (maybe_nan) {
|
| + type = Type::Union(type, Type::NaN(), zone());
|
| + }
|
| + return type;
|
| +}
|
| +
|
| +Type* OperationTyper::NumberAcos(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberAcosh(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberAsin(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberAsinh(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberAtan(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberAtanh(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberCbrt(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberCeil(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + if (type->Is(cache_.kIntegerOrMinusZeroOrNaN)) return type;
|
| + // TODO(bmeurer): We could infer a more precise type here.
|
| + return cache_.kIntegerOrMinusZeroOrNaN;
|
| +}
|
| +
|
| +Type* OperationTyper::NumberClz32(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return cache_.kZeroToThirtyTwo;
|
| +}
|
| +
|
| +Type* OperationTyper::NumberCos(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberCosh(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberExp(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Union(Type::PlainNumber(), Type::NaN(), zone());
|
| +}
|
| +
|
| +Type* OperationTyper::NumberExpm1(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Union(Type::PlainNumber(), Type::NaN(), zone());
|
| +}
|
| +
|
| +Type* OperationTyper::NumberFloor(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + if (type->Is(cache_.kIntegerOrMinusZeroOrNaN)) return type;
|
| + // TODO(bmeurer): We could infer a more precise type here.
|
| + return cache_.kIntegerOrMinusZeroOrNaN;
|
| +}
|
| +
|
| +Type* OperationTyper::NumberFround(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberLog(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberLog1p(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberLog2(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberLog10(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberRound(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + if (type->Is(cache_.kIntegerOrMinusZeroOrNaN)) return type;
|
| + // TODO(bmeurer): We could infer a more precise type here.
|
| + return cache_.kIntegerOrMinusZeroOrNaN;
|
| +}
|
| +
|
| +Type* OperationTyper::NumberSign(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + if (type->Is(cache_.kZeroish)) return type;
|
| + bool maybe_minuszero = type->Maybe(Type::MinusZero());
|
| + bool maybe_nan = type->Maybe(Type::NaN());
|
| + type = Type::Intersect(type, Type::PlainNumber(), zone());
|
| + if (type->Max() < 0.0) {
|
| + type = cache_.kSingletonMinusOne;
|
| + } else if (type->Max() <= 0.0) {
|
| + type = cache_.kMinusOneOrZero;
|
| + } else if (type->Min() > 0.0) {
|
| + type = cache_.kSingletonOne;
|
| + } else if (type->Min() >= 0.0) {
|
| + type = cache_.kZeroOrOne;
|
| + } else {
|
| + type = Type::Range(-1.0, 1.0, zone());
|
| + }
|
| + if (maybe_minuszero) type = Type::Union(type, Type::MinusZero(), zone());
|
| + if (maybe_nan) type = Type::Union(type, Type::NaN(), zone());
|
| + return type;
|
| +}
|
| +
|
| +Type* OperationTyper::NumberSin(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberSinh(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberSqrt(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberTan(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberTanh(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| return Type::Number();
|
| }
|
|
|
| +Type* OperationTyper::NumberTrunc(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + if (type->Is(cache_.kIntegerOrMinusZeroOrNaN)) return type;
|
| + // TODO(bmeurer): We could infer a more precise type here.
|
| + return cache_.kIntegerOrMinusZeroOrNaN;
|
| +}
|
| +
|
| +Type* OperationTyper::NumberToInt32(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| +
|
| + if (type->Is(Type::Signed32())) return type;
|
| + if (type->Is(cache_.kZeroish)) return cache_.kSingletonZero;
|
| + if (type->Is(signed32ish_)) {
|
| + return Type::Intersect(Type::Union(type, cache_.kSingletonZero, zone()),
|
| + Type::Signed32(), zone());
|
| + }
|
| + return Type::Signed32();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberToUint32(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| +
|
| + if (type->Is(Type::Unsigned32())) return type;
|
| + if (type->Is(cache_.kZeroish)) return cache_.kSingletonZero;
|
| + if (type->Is(unsigned32ish_)) {
|
| + return Type::Intersect(Type::Union(type, cache_.kSingletonZero, zone()),
|
| + Type::Unsigned32(), zone());
|
| + }
|
| + return Type::Unsigned32();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberSilenceNaN(Type* type) {
|
| + DCHECK(type->Is(Type::Number()));
|
| + // TODO(turbofan): We should have a dedicated type for the signaling NaN.
|
| + return type;
|
| +}
|
| +
|
| Type* OperationTyper::NumberAdd(Type* lhs, Type* rhs) {
|
| DCHECK(lhs->Is(Type::Number()));
|
| DCHECK(rhs->Is(Type::Number()));
|
| @@ -406,34 +638,244 @@ Type* OperationTyper::NumberModulus(Type* lhs, Type* rhs) {
|
| return type;
|
| }
|
|
|
| -Type* OperationTyper::NumberAbs(Type* type) {
|
| - DCHECK(type->Is(Type::Number()));
|
| +Type* OperationTyper::NumberBitwiseOr(Type* lhs, Type* rhs) {
|
| + DCHECK(lhs->Is(Type::Number()));
|
| + DCHECK(rhs->Is(Type::Number()));
|
|
|
| - if (!type->IsInhabited()) {
|
| - return Type::None();
|
| + if (!lhs->IsInhabited() || !rhs->IsInhabited()) return Type::None();
|
| +
|
| + lhs = NumberToInt32(lhs);
|
| + rhs = NumberToInt32(rhs);
|
| +
|
| + double lmin = lhs->Min();
|
| + double rmin = rhs->Min();
|
| + double lmax = lhs->Max();
|
| + double rmax = rhs->Max();
|
| + // Or-ing any two values results in a value no smaller than their minimum.
|
| + // Even no smaller than their maximum if both values are non-negative.
|
| + double min =
|
| + lmin >= 0 && rmin >= 0 ? std::max(lmin, rmin) : std::min(lmin, rmin);
|
| + double max = kMaxInt;
|
| +
|
| + // Or-ing with 0 is essentially a conversion to int32.
|
| + if (rmin == 0 && rmax == 0) {
|
| + min = lmin;
|
| + max = lmax;
|
| + }
|
| + if (lmin == 0 && lmax == 0) {
|
| + min = rmin;
|
| + max = rmax;
|
| }
|
|
|
| - bool const maybe_nan = type->Maybe(Type::NaN());
|
| - bool const maybe_minuszero = type->Maybe(Type::MinusZero());
|
| - type = Type::Intersect(type, Type::PlainNumber(), zone());
|
| - double const max = type->Max();
|
| - double const min = type->Min();
|
| - if (min < 0) {
|
| - if (type->Is(cache_.kInteger)) {
|
| - type = Type::Range(0.0, std::max(std::fabs(min), std::fabs(max)), zone());
|
| - } else {
|
| - type = Type::PlainNumber();
|
| + if (lmax < 0 || rmax < 0) {
|
| + // Or-ing two values of which at least one is negative results in a negative
|
| + // value.
|
| + max = std::min(max, -1.0);
|
| + }
|
| + return Type::Range(min, max, zone());
|
| +}
|
| +
|
| +Type* OperationTyper::NumberBitwiseAnd(Type* lhs, Type* rhs) {
|
| + DCHECK(lhs->Is(Type::Number()));
|
| + DCHECK(rhs->Is(Type::Number()));
|
| +
|
| + if (!lhs->IsInhabited() || !rhs->IsInhabited()) return Type::None();
|
| +
|
| + lhs = NumberToInt32(lhs);
|
| + rhs = NumberToInt32(rhs);
|
| +
|
| + double lmin = lhs->Min();
|
| + double rmin = rhs->Min();
|
| + double lmax = lhs->Max();
|
| + double rmax = rhs->Max();
|
| + double min = kMinInt;
|
| + // And-ing any two values results in a value no larger than their maximum.
|
| + // Even no larger than their minimum if both values are non-negative.
|
| + double max =
|
| + lmin >= 0 && rmin >= 0 ? std::min(lmax, rmax) : std::max(lmax, rmax);
|
| + // And-ing with a non-negative value x causes the result to be between
|
| + // zero and x.
|
| + if (lmin >= 0) {
|
| + min = 0;
|
| + max = std::min(max, lmax);
|
| + }
|
| + if (rmin >= 0) {
|
| + min = 0;
|
| + max = std::min(max, rmax);
|
| + }
|
| + return Type::Range(min, max, zone());
|
| +}
|
| +
|
| +Type* OperationTyper::NumberBitwiseXor(Type* lhs, Type* rhs) {
|
| + DCHECK(lhs->Is(Type::Number()));
|
| + DCHECK(rhs->Is(Type::Number()));
|
| +
|
| + if (!lhs->IsInhabited() || !rhs->IsInhabited()) return Type::None();
|
| +
|
| + lhs = NumberToInt32(lhs);
|
| + rhs = NumberToInt32(rhs);
|
| +
|
| + double lmin = lhs->Min();
|
| + double rmin = rhs->Min();
|
| + double lmax = lhs->Max();
|
| + double rmax = rhs->Max();
|
| + if ((lmin >= 0 && rmin >= 0) || (lmax < 0 && rmax < 0)) {
|
| + // Xor-ing negative or non-negative values results in a non-negative value.
|
| + return Type::Unsigned31();
|
| + }
|
| + if ((lmax < 0 && rmin >= 0) || (lmin >= 0 && rmax < 0)) {
|
| + // Xor-ing a negative and a non-negative value results in a negative value.
|
| + // TODO(jarin) Use a range here.
|
| + return Type::Negative32();
|
| + }
|
| + return Type::Signed32();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberShiftLeft(Type* lhs, Type* rhs) {
|
| + DCHECK(lhs->Is(Type::Number()));
|
| + DCHECK(rhs->Is(Type::Number()));
|
| +
|
| + // TODO(turbofan): Infer a better type here.
|
| + return Type::Signed32();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberShiftRight(Type* lhs, Type* rhs) {
|
| + DCHECK(lhs->Is(Type::Number()));
|
| + DCHECK(rhs->Is(Type::Number()));
|
| +
|
| + if (!lhs->IsInhabited() || !rhs->IsInhabited()) return Type::None();
|
| +
|
| + lhs = NumberToInt32(lhs);
|
| + rhs = NumberToUint32(rhs);
|
| +
|
| + double min = kMinInt;
|
| + double max = kMaxInt;
|
| + if (lhs->Min() >= 0) {
|
| + // Right-shifting a non-negative value cannot make it negative, nor larger.
|
| + min = std::max(min, 0.0);
|
| + max = std::min(max, lhs->Max());
|
| + if (rhs->Min() > 0 && rhs->Max() <= 31) {
|
| + max = static_cast<int>(max) >> static_cast<int>(rhs->Min());
|
| }
|
| }
|
| - if (maybe_minuszero) {
|
| - type = Type::Union(type, cache_.kSingletonZero, zone());
|
| + if (lhs->Max() < 0) {
|
| + // Right-shifting a negative value cannot make it non-negative, nor smaller.
|
| + min = std::max(min, lhs->Min());
|
| + max = std::min(max, -1.0);
|
| + if (rhs->Min() > 0 && rhs->Max() <= 31) {
|
| + min = static_cast<int>(min) >> static_cast<int>(rhs->Min());
|
| + }
|
| }
|
| - if (maybe_nan) {
|
| + if (rhs->Min() > 0 && rhs->Max() <= 31) {
|
| + // Right-shifting by a positive value yields a small integer value.
|
| + double shift_min = kMinInt >> static_cast<int>(rhs->Min());
|
| + double shift_max = kMaxInt >> static_cast<int>(rhs->Min());
|
| + min = std::max(min, shift_min);
|
| + max = std::min(max, shift_max);
|
| + }
|
| + // TODO(jarin) Ideally, the following micro-optimization should be performed
|
| + // by the type constructor.
|
| + if (max == kMaxInt && min == kMinInt) return Type::Signed32();
|
| + return Type::Range(min, max, zone());
|
| +}
|
| +
|
| +Type* OperationTyper::NumberShiftRightLogical(Type* lhs, Type* rhs) {
|
| + DCHECK(lhs->Is(Type::Number()));
|
| + DCHECK(rhs->Is(Type::Number()));
|
| +
|
| + if (!lhs->IsInhabited()) return Type::None();
|
| +
|
| + lhs = NumberToUint32(lhs);
|
| +
|
| + // Logical right-shifting any value cannot make it larger.
|
| + return Type::Range(0.0, lhs->Max(), zone());
|
| +}
|
| +
|
| +Type* OperationTyper::NumberAtan2(Type* lhs, Type* rhs) {
|
| + DCHECK(lhs->Is(Type::Number()));
|
| + DCHECK(rhs->Is(Type::Number()));
|
| + return Type::Number();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberImul(Type* lhs, Type* rhs) {
|
| + DCHECK(lhs->Is(Type::Number()));
|
| + DCHECK(rhs->Is(Type::Number()));
|
| + // TODO(turbofan): We should be able to do better here.
|
| + return Type::Signed32();
|
| +}
|
| +
|
| +Type* OperationTyper::NumberMax(Type* lhs, Type* rhs) {
|
| + DCHECK(lhs->Is(Type::Number()));
|
| + DCHECK(rhs->Is(Type::Number()));
|
| + if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) {
|
| + return Type::NaN();
|
| + }
|
| + Type* type = Type::None();
|
| + // TODO(turbofan): Improve minus zero handling here.
|
| + if (lhs->Maybe(Type::NaN()) || rhs->Maybe(Type::NaN())) {
|
| + type = Type::Union(type, Type::NaN(), zone());
|
| + }
|
| + lhs = Type::Intersect(lhs, Type::OrderedNumber(), zone());
|
| + rhs = Type::Intersect(rhs, Type::OrderedNumber(), zone());
|
| + if (lhs->Is(cache_.kInteger) && rhs->Is(cache_.kInteger)) {
|
| + double max = std::max(lhs->Max(), rhs->Max());
|
| + double min = std::max(lhs->Min(), rhs->Min());
|
| + type = Type::Union(type, Type::Range(min, max, zone()), zone());
|
| + } else {
|
| + type = Type::Union(type, Type::Union(lhs, rhs, zone()), zone());
|
| + }
|
| + return type;
|
| +}
|
| +
|
| +Type* OperationTyper::NumberMin(Type* lhs, Type* rhs) {
|
| + DCHECK(lhs->Is(Type::Number()));
|
| + DCHECK(rhs->Is(Type::Number()));
|
| + if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) {
|
| + return Type::NaN();
|
| + }
|
| + Type* type = Type::None();
|
| + // TODO(turbofan): Improve minus zero handling here.
|
| + if (lhs->Maybe(Type::NaN()) || rhs->Maybe(Type::NaN())) {
|
| type = Type::Union(type, Type::NaN(), zone());
|
| }
|
| + lhs = Type::Intersect(lhs, Type::OrderedNumber(), zone());
|
| + rhs = Type::Intersect(rhs, Type::OrderedNumber(), zone());
|
| + if (lhs->Is(cache_.kInteger) && rhs->Is(cache_.kInteger)) {
|
| + double max = std::min(lhs->Max(), rhs->Max());
|
| + double min = std::min(lhs->Min(), rhs->Min());
|
| + type = Type::Union(type, Type::Range(min, max, zone()), zone());
|
| + } else {
|
| + type = Type::Union(type, Type::Union(lhs, rhs, zone()), zone());
|
| + }
|
| return type;
|
| }
|
|
|
| +Type* OperationTyper::NumberPow(Type* lhs, Type* rhs) {
|
| + DCHECK(lhs->Is(Type::Number()));
|
| + DCHECK(rhs->Is(Type::Number()));
|
| + // TODO(turbofan): We should be able to do better here.
|
| + return Type::Number();
|
| +}
|
| +
|
| +#define SPECULATIVE_NUMBER_BINOP(Name) \
|
| + Type* OperationTyper::Speculative##Name(Type* lhs, Type* rhs) { \
|
| + lhs = ToNumber(Type::Intersect(lhs, Type::NumberOrOddball(), zone())); \
|
| + rhs = ToNumber(Type::Intersect(rhs, Type::NumberOrOddball(), zone())); \
|
| + return Name(lhs, rhs); \
|
| + }
|
| +SPECULATIVE_NUMBER_BINOP(NumberAdd)
|
| +SPECULATIVE_NUMBER_BINOP(NumberSubtract)
|
| +SPECULATIVE_NUMBER_BINOP(NumberMultiply)
|
| +SPECULATIVE_NUMBER_BINOP(NumberDivide)
|
| +SPECULATIVE_NUMBER_BINOP(NumberModulus)
|
| +SPECULATIVE_NUMBER_BINOP(NumberBitwiseOr)
|
| +SPECULATIVE_NUMBER_BINOP(NumberBitwiseAnd)
|
| +SPECULATIVE_NUMBER_BINOP(NumberBitwiseXor)
|
| +SPECULATIVE_NUMBER_BINOP(NumberShiftLeft)
|
| +SPECULATIVE_NUMBER_BINOP(NumberShiftRight)
|
| +SPECULATIVE_NUMBER_BINOP(NumberShiftRightLogical)
|
| +#undef SPECULATIVE_NUMBER_BINOP
|
| +
|
| Type* OperationTyper::ToPrimitive(Type* type) {
|
| if (type->Is(Type::Primitive()) && !type->Maybe(Type::Receiver())) {
|
| return type;
|
| @@ -469,30 +911,6 @@ Type* OperationTyper::FalsifyUndefined(ComparisonOutcome outcome) {
|
| return singleton_true();
|
| }
|
|
|
| -Type* OperationTyper::TypeJSAdd(Type* lhs, Type* rhs) {
|
| - lhs = ToPrimitive(lhs);
|
| - rhs = ToPrimitive(rhs);
|
| -
|
| - if (!lhs->IsInhabited() || !rhs->IsInhabited()) {
|
| - return Type::None();
|
| - }
|
| -
|
| - if (lhs->Maybe(Type::String()) || rhs->Maybe(Type::String())) {
|
| - if (lhs->Is(Type::String()) || rhs->Is(Type::String())) {
|
| - return Type::String();
|
| - } else {
|
| - return Type::NumberOrString();
|
| - }
|
| - }
|
| - lhs = ToNumber(lhs);
|
| - rhs = ToNumber(rhs);
|
| - return NumberAdd(lhs, rhs);
|
| -}
|
| -
|
| -Type* OperationTyper::TypeJSSubtract(Type* lhs, Type* rhs) {
|
| - return NumberSubtract(ToNumber(lhs), ToNumber(rhs));
|
| -}
|
| -
|
| } // namespace compiler
|
| } // namespace internal
|
| } // namespace v8
|
|
|