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

Unified Diff: src/compiler/typer.cc

Issue 658743002: Refine typing of addition, subtraction, multiplication, and division. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Rangify (unions of) constants to get monotonicity. Created 6 years, 2 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/typer.h ('k') | src/types.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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();
}
« no previous file with comments | « src/compiler/typer.h ('k') | src/types.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698