| Index: src/compiler/typer.cc
|
| diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc
|
| index b6604c7f384b323cb3306adf70a07402485d0d16..56c6b8bc93785446a0884ef232064765e02f1245 100644
|
| --- a/src/compiler/typer.cc
|
| +++ b/src/compiler/typer.cc
|
| @@ -32,8 +32,8 @@ Typer::Typer(Graph* graph, MaybeHandle<Context> context)
|
|
|
| Handle<Object> zero = f->NewNumber(0);
|
| Handle<Object> one = f->NewNumber(1);
|
| - Handle<Object> positive_infinity = f->NewNumber(+V8_INFINITY);
|
| - Handle<Object> negative_infinity = f->NewNumber(-V8_INFINITY);
|
| + Handle<Object> infinity = f->NewNumber(+V8_INFINITY);
|
| + Handle<Object> minusinfinity = f->NewNumber(-V8_INFINITY);
|
|
|
| negative_signed32 = Type::Union(
|
| Type::SignedSmall(), Type::OtherSigned32(), zone);
|
| @@ -49,7 +49,9 @@ Typer::Typer(Graph* graph, MaybeHandle<Context> context)
|
| singleton_zero, Type::Union(Type::NaN(), Type::MinusZero(), zone), zone);
|
| falsish = Type::Union(Type::Undetectable(),
|
| Type::Union(zeroish, undefined_or_null, zone), zone);
|
| - integer = Type::Range(negative_infinity, positive_infinity, zone);
|
| + integer = Type::Range(minusinfinity, infinity, zone);
|
| + weakint = Type::Union(
|
| + integer, Type::Union(Type::NaN(), Type::MinusZero(), zone), zone);
|
|
|
| Type* number = Type::Number();
|
| Type* signed32 = Type::Signed32();
|
| @@ -57,8 +59,6 @@ Typer::Typer(Graph* graph, MaybeHandle<Context> context)
|
| Type* integral32 = Type::Integral32();
|
| Type* object = Type::Object();
|
| Type* undefined = Type::Undefined();
|
| - Type* weakint = Type::Union(
|
| - integer, Type::Union(Type::NaN(), Type::MinusZero(), zone), zone);
|
|
|
| number_fun0_ = Type::Function(number, zone);
|
| number_fun1_ = Type::Function(number, number, zone);
|
| @@ -164,7 +164,7 @@ class Typer::Visitor : public NullNodeVisitor {
|
|
|
| Bounds BoundsOrNone(Node* node) {
|
| return NodeProperties::IsTyped(node)
|
| - ? NodeProperties::GetBounds(node) : Bounds(Type::None(zone()));
|
| + ? NodeProperties::GetBounds(node) : Bounds(Type::None());
|
| }
|
|
|
| Bounds Operand(Node* node, int i) {
|
| @@ -197,6 +197,7 @@ class Typer::Visitor : public NullNodeVisitor {
|
|
|
| static Type* Invert(Type*, Typer*);
|
| static Type* FalsifyUndefined(Type*, Typer*);
|
| + static Type* Rangify(Type*, Typer*);
|
|
|
| static Type* ToPrimitive(Type*, Typer*);
|
| static Type* ToBoolean(Type*, Typer*);
|
| @@ -250,11 +251,11 @@ class Typer::NarrowVisitor : public Typer::Visitor {
|
| GenericGraphVisit::Control Pre(Node* node) {
|
| if (OperatorProperties::HasValueOutput(node->op())) {
|
| Bounds previous = NodeProperties::GetBounds(node);
|
| - Bounds bounds = TypeNode(node);
|
| - NodeProperties::SetBounds(node, Bounds::Both(bounds, previous, zone()));
|
| - DCHECK(bounds.Narrows(previous));
|
| + Bounds current = TypeNode(node);
|
| + NodeProperties::SetBounds(node, Bounds::Both(current, previous, zone()));
|
| + DCHECK(current.Narrows(previous));
|
| // Stop when nothing changed (but allow re-entry in case it does later).
|
| - return previous.Narrows(bounds)
|
| + return previous.Narrows(current)
|
| ? GenericGraphVisit::DEFER : GenericGraphVisit::REENTER;
|
| } else {
|
| return GenericGraphVisit::SKIP;
|
| @@ -274,12 +275,17 @@ class Typer::WidenVisitor : public Typer::Visitor {
|
| GenericGraphVisit::Control Pre(Node* node) {
|
| if (OperatorProperties::HasValueOutput(node->op())) {
|
| Bounds previous = BoundsOrNone(node);
|
| - Bounds bounds = TypeNode(node);
|
| - DCHECK(previous.lower->Is(bounds.lower));
|
| - DCHECK(previous.upper->Is(bounds.upper));
|
| - NodeProperties::SetBounds(node, bounds);
|
| + Bounds current = TypeNode(node);
|
| + DCHECK(previous.lower->Is(current.lower));
|
| + DCHECK(previous.upper->Is(current.upper));
|
| +
|
| + // Speed up termination in the presence of range types:
|
| + current.upper = MaybeWeaken(current.upper, previous.upper);
|
| + current.lower = MaybeWeaken(current.lower, previous.lower);
|
| +
|
| + NodeProperties::SetBounds(node, current);
|
| // Stop when nothing changed (but allow re-entry in case it does later).
|
| - return bounds.Narrows(previous)
|
| + return previous.Narrows(current) && current.Narrows(previous)
|
| ? GenericGraphVisit::DEFER : GenericGraphVisit::REENTER;
|
| } else {
|
| return GenericGraphVisit::SKIP;
|
| @@ -289,6 +295,14 @@ class Typer::WidenVisitor : public Typer::Visitor {
|
| GenericGraphVisit::Control Post(Node* node) {
|
| return GenericGraphVisit::REENTER;
|
| }
|
| +
|
| + private:
|
| + Type* MaybeWeaken(Type* current, Type* previous) {
|
| + if (current->IsRange() && previous->IsRange() && !current->Is(previous)) {
|
| + return current->AsRange()->Weaken(zone());
|
| + }
|
| + return current;
|
| + }
|
| };
|
|
|
|
|
| @@ -379,6 +393,15 @@ Type* Typer::Visitor::FalsifyUndefined(Type* type, Typer* t) {
|
| }
|
|
|
|
|
| +Type* Typer::Visitor::Rangify(Type* type, Typer* t) {
|
| + if (!type->Is(t->integer)) return type; // Give up.
|
| + if (type->IsRange()) return type; // Shortcut.
|
| + Factory* f = t->isolate()->factory();
|
| + return Type::Range(
|
| + f->NewNumber(type->Min()), f->NewNumber(type->Max()), t->zone());
|
| +}
|
| +
|
| +
|
| // Type conversion.
|
|
|
|
|
| @@ -453,7 +476,7 @@ Bounds Typer::Visitor::TypeParameter(Node* node) {
|
|
|
|
|
| Bounds Typer::Visitor::TypeInt32Constant(Node* node) {
|
| - Factory* f = zone()->isolate()->factory();
|
| + Factory* f = isolate()->factory();
|
| Handle<Object> number = f->NewNumber(OpParameter<int32_t>(node));
|
| return Bounds(Type::Intersect(
|
| Type::Range(number, number, zone()), Type::UntaggedInt32(), zone()));
|
| @@ -480,7 +503,7 @@ Bounds Typer::Visitor::TypeFloat64Constant(Node* node) {
|
|
|
|
|
| Bounds Typer::Visitor::TypeNumberConstant(Node* node) {
|
| - Factory* f = zone()->isolate()->factory();
|
| + Factory* f = isolate()->factory();
|
| return Bounds(Type::Constant(
|
| f->NewNumber(OpParameter<double>(node)), zone()));
|
| }
|
| @@ -657,7 +680,7 @@ Type* Typer::Visitor::JSGreaterThanOrEqualTyper(
|
|
|
|
|
| Type* Typer::Visitor::JSBitwiseOrTyper(Type* lhs, Type* rhs, Typer* t) {
|
| - Factory* f = t->zone()->isolate()->factory();
|
| + Factory* f = t->isolate()->factory();
|
| lhs = NumberToInt32(ToNumber(lhs, t), t);
|
| rhs = NumberToInt32(ToNumber(rhs, t), t);
|
| double lmin = lhs->Min();
|
| @@ -681,7 +704,7 @@ Type* Typer::Visitor::JSBitwiseOrTyper(Type* lhs, Type* rhs, Typer* t) {
|
|
|
|
|
| Type* Typer::Visitor::JSBitwiseAndTyper(Type* lhs, Type* rhs, Typer* t) {
|
| - Factory* f = t->zone()->isolate()->factory();
|
| + Factory* f = t->isolate()->factory();
|
| lhs = NumberToInt32(ToNumber(lhs, t), t);
|
| rhs = NumberToInt32(ToNumber(rhs, t), t);
|
| double lmin = lhs->Min();
|
| @@ -729,7 +752,7 @@ Type* Typer::Visitor::JSShiftLeftTyper(Type* lhs, Type* rhs, Typer* t) {
|
|
|
| Type* Typer::Visitor::JSShiftRightTyper(Type* lhs, Type* rhs, Typer* t) {
|
| lhs = NumberToInt32(ToNumber(lhs, t), t);
|
| - Factory* f = t->zone()->isolate()->factory();
|
| + Factory* f = t->isolate()->factory();
|
| if (lhs->Min() >= 0) {
|
| // Right-shifting a non-negative value cannot make it negative, nor larger.
|
| Handle<Object> min = f->NewNumber(0);
|
| @@ -748,7 +771,7 @@ Type* Typer::Visitor::JSShiftRightTyper(Type* lhs, Type* rhs, Typer* t) {
|
|
|
| Type* Typer::Visitor::JSShiftRightLogicalTyper(Type* lhs, Type* rhs, Typer* t) {
|
| lhs = NumberToUint32(ToNumber(lhs, t), t);
|
| - Factory* f = t->zone()->isolate()->factory();
|
| + Factory* f = t->isolate()->factory();
|
| // Logical right-shifting any value cannot make it larger.
|
| Handle<Object> min = f->NewNumber(0);
|
| Handle<Object> max = f->NewNumber(lhs->Max());
|
| @@ -759,6 +782,63 @@ Type* Typer::Visitor::JSShiftRightLogicalTyper(Type* lhs, Type* rhs, Typer* t) {
|
| // JS arithmetic operators.
|
|
|
|
|
| +// Returns the array's least element, ignoring NaN.
|
| +// There must be at least one non-NaN element.
|
| +// Any -0 is converted to 0.
|
| +static double array_min(double a[], size_t n) {
|
| + DCHECK(n != 0);
|
| + double x = a[0];
|
| + for (size_t i = 1; i < n; ++i) {
|
| + x = std::min(std::isnan(a[i]) ? +V8_INFINITY : a[i], x);
|
| + }
|
| + DCHECK(!std::isnan(x));
|
| + return x == 0 ? 0 : x; // -0 -> 0
|
| +}
|
| +
|
| +
|
| +// Returns the array's greatest element, ignoring NaN.
|
| +// There must be at least one non-NaN element.
|
| +// Any -0 is converted to 0.
|
| +static double array_max(double a[], size_t n) {
|
| + DCHECK(n != 0);
|
| + double x = a[0];
|
| + for (size_t i = 1; i < n; ++i) {
|
| + x = std::max(std::isnan(a[i]) ? -V8_INFINITY : a[i], x);
|
| + }
|
| + DCHECK(!std::isnan(x));
|
| + return x == 0 ? 0 : x; // -0 -> 0
|
| +}
|
| +
|
| +
|
| +Type* Typer::Visitor::JSAddRanger(
|
| + Type::RangeType* lhs, Type::RangeType* rhs, Typer* t) {
|
| + double results[4];
|
| + results[0] = lhs->Min()->Number() + rhs->Min()->Number();
|
| + results[1] = lhs->Min()->Number() + rhs->Max()->Number();
|
| + results[2] = lhs->Max()->Number() + rhs->Min()->Number();
|
| + results[3] = lhs->Max()->Number() + rhs->Max()->Number();
|
| + // Since none of the inputs can be -0, the result cannot be -0 either.
|
| + // However, it can be nan (the sum of two infinities of opposite sign).
|
| + // On the other hand, if none of the "results" above is nan, then the actual
|
| + // result cannot be nan either.
|
| + int nans = 0;
|
| + for (int i = 0; i < 4; ++i) {
|
| + if (std::isnan(results[i])) ++nans;
|
| + }
|
| + if (nans == 4) return Type::NaN(); // [-inf..-inf] + [inf..inf] or vice versa
|
| + Factory* f = t->isolate()->factory();
|
| + Type* range = Type::Range(
|
| + f->NewNumber(array_min(results, 4)), f->NewNumber(array_max(results, 4)),
|
| + t->zone());
|
| + return nans == 0 ? range : Type::Union(range, Type::NaN(), t->zone());
|
| + // Examples:
|
| + // [-inf, -inf] + [+inf, +inf] = NaN
|
| + // [-inf, -inf] + [n, +inf] = [-inf, -inf] \/ NaN
|
| + // [-inf, +inf] + [n, +inf] = [-inf, +inf] \/ NaN
|
| + // [-inf, m] + [n, +inf] = [-inf, +inf] \/ NaN
|
| +}
|
| +
|
| +
|
| Type* Typer::Visitor::JSAddTyper(Type* lhs, Type* rhs, Typer* t) {
|
| lhs = ToPrimitive(lhs, t);
|
| rhs = ToPrimitive(rhs, t);
|
| @@ -769,29 +849,97 @@ Type* Typer::Visitor::JSAddTyper(Type* lhs, Type* rhs, Typer* t) {
|
| return Type::NumberOrString();
|
| }
|
| }
|
| - lhs = ToNumber(lhs, t);
|
| - rhs = ToNumber(rhs, t);
|
| + lhs = Rangify(ToNumber(lhs, t), t);
|
| + rhs = Rangify(ToNumber(rhs, t), t);
|
| if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN();
|
| - // TODO(neis): Do some analysis.
|
| + if (lhs->IsRange() && rhs->IsRange()) {
|
| + return JSAddRanger(lhs->AsRange(), rhs->AsRange(), t);
|
| + }
|
| // TODO(neis): Deal with numeric bitsets here and elsewhere.
|
| return Type::Number();
|
| }
|
|
|
|
|
| +Type* Typer::Visitor::JSSubtractRanger(
|
| + Type::RangeType* lhs, Type::RangeType* rhs, Typer* t) {
|
| + double results[4];
|
| + results[0] = lhs->Min()->Number() - rhs->Min()->Number();
|
| + results[1] = lhs->Min()->Number() - rhs->Max()->Number();
|
| + results[2] = lhs->Max()->Number() - rhs->Min()->Number();
|
| + results[3] = lhs->Max()->Number() - rhs->Max()->Number();
|
| + // Since none of the inputs can be -0, the result cannot be -0.
|
| + // However, it can be nan (the subtraction of two infinities of same sign).
|
| + // On the other hand, if none of the "results" above is nan, then the actual
|
| + // result cannot be nan either.
|
| + int nans = 0;
|
| + for (int i = 0; i < 4; ++i) {
|
| + if (std::isnan(results[i])) ++nans;
|
| + }
|
| + if (nans == 4) return Type::NaN(); // [inf..inf] - [inf..inf] (all same sign)
|
| + Factory* f = t->isolate()->factory();
|
| + Type* range = Type::Range(
|
| + f->NewNumber(array_min(results, 4)), f->NewNumber(array_max(results, 4)),
|
| + t->zone());
|
| + return nans == 0 ? range : Type::Union(range, Type::NaN(), t->zone());
|
| + // Examples:
|
| + // [-inf, +inf] - [-inf, +inf] = [-inf, +inf] \/ NaN
|
| + // [-inf, -inf] - [-inf, -inf] = NaN
|
| + // [-inf, -inf] - [n, +inf] = [-inf, -inf] \/ NaN
|
| + // [m, +inf] - [-inf, n] = [-inf, +inf] \/ NaN
|
| +}
|
| +
|
| +
|
| Type* Typer::Visitor::JSSubtractTyper(Type* lhs, Type* rhs, Typer* t) {
|
| - lhs = ToNumber(lhs, t);
|
| - rhs = ToNumber(rhs, t);
|
| + lhs = Rangify(ToNumber(lhs, t), t);
|
| + rhs = Rangify(ToNumber(rhs, t), t);
|
| if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN();
|
| - // TODO(neis): Do some analysis.
|
| + if (lhs->IsRange() && rhs->IsRange()) {
|
| + return JSSubtractRanger(lhs->AsRange(), rhs->AsRange(), t);
|
| + }
|
| return Type::Number();
|
| }
|
|
|
|
|
| +Type* Typer::Visitor::JSMultiplyRanger(
|
| + Type::RangeType* lhs, Type::RangeType* rhs, Typer* t) {
|
| + double results[4];
|
| + double lmin = lhs->Min()->Number();
|
| + double lmax = lhs->Max()->Number();
|
| + double rmin = rhs->Min()->Number();
|
| + double rmax = rhs->Max()->Number();
|
| + 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(t->singleton_zero) &&
|
| + (rmin == -V8_INFINITY || rmax == +V8_INFINITY)) ||
|
| + (rhs->Maybe(t->singleton_zero) &&
|
| + (lmin == -V8_INFINITY || lmax == +V8_INFINITY));
|
| + if (maybe_nan) return t->weakint; // Giving up.
|
| + bool maybe_minuszero =
|
| + (lhs->Maybe(t->singleton_zero) && rmin < 0) ||
|
| + (rhs->Maybe(t->singleton_zero) && lmin < 0);
|
| + Factory* f = t->isolate()->factory();
|
| + Type* range = Type::Range(
|
| + f->NewNumber(array_min(results, 4)), f->NewNumber(array_max(results, 4)),
|
| + t->zone());
|
| + return maybe_minuszero ?
|
| + Type::Union(range, Type::MinusZero(), t->zone()) : range;
|
| +}
|
| +
|
| +
|
| Type* Typer::Visitor::JSMultiplyTyper(Type* lhs, Type* rhs, Typer* t) {
|
| - lhs = ToNumber(lhs, t);
|
| - rhs = ToNumber(rhs, t);
|
| + lhs = Rangify(ToNumber(lhs, t), t);
|
| + rhs = Rangify(ToNumber(rhs, t), t);
|
| if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN();
|
| - // TODO(neis): Do some analysis.
|
| + if (lhs->IsRange() && rhs->IsRange()) {
|
| + return JSMultiplyRanger(lhs->AsRange(), rhs->AsRange(), t);
|
| + }
|
| return Type::Number();
|
| }
|
|
|
| @@ -800,8 +948,14 @@ Type* Typer::Visitor::JSDivideTyper(Type* lhs, Type* rhs, Typer* t) {
|
| lhs = ToNumber(lhs, t);
|
| rhs = ToNumber(rhs, t);
|
| if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN();
|
| - // TODO(neis): Do some analysis.
|
| - return Type::Number();
|
| + // Division is tricky, so all we do is try ruling out nan.
|
| + // TODO(neis): try ruling out -0 as well?
|
| + bool maybe_nan =
|
| + lhs->Maybe(Type::NaN()) || rhs->Maybe(Type::NaN()) ||
|
| + rhs->Maybe(t->singleton_zero) ||
|
| + ((lhs->Min() == -V8_INFINITY || lhs->Max() == +V8_INFINITY) &&
|
| + (rhs->Min() == -V8_INFINITY || rhs->Max() == +V8_INFINITY));
|
| + return maybe_nan ? Type::Number() : Type::OrderedNumber();
|
| }
|
|
|
|
|
|
|