Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(148)

Unified Diff: src/compiler/operation-typer.cc

Issue 2202883005: [turbofan] Unify number operation typing rules. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Remove useless cementation. DCHECKs instead of defensive programming are way more useful. Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/compiler/operation-typer.h ('k') | src/compiler/simplified-lowering.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « src/compiler/operation-typer.h ('k') | src/compiler/simplified-lowering.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698