Commit b309f3d2 authored by titzer@chromium.org's avatar titzer@chromium.org

[turbofan] (reland) Compute tighter ranges for modulus in Typer.

R=jarin@chromium.org
BUG=

Review URL: https://codereview.chromium.org/694703004

Cr-Commit-Position: refs/heads/master@{#25193}
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@25193 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 652e1355
...@@ -232,6 +232,7 @@ class Typer::Visitor : public NullNodeVisitor { ...@@ -232,6 +232,7 @@ class Typer::Visitor : public NullNodeVisitor {
static Type* JSSubtractRanger(Type::RangeType*, Type::RangeType*, Typer*); static Type* JSSubtractRanger(Type::RangeType*, Type::RangeType*, Typer*);
static Type* JSMultiplyRanger(Type::RangeType*, Type::RangeType*, Typer*); static Type* JSMultiplyRanger(Type::RangeType*, Type::RangeType*, Typer*);
static Type* JSDivideRanger(Type::RangeType*, Type::RangeType*, Typer*); static Type* JSDivideRanger(Type::RangeType*, Type::RangeType*, Typer*);
static Type* JSModulusRanger(Type::RangeType*, Type::RangeType*, Typer*);
static Type* JSCompareTyper(Type*, Type*, Typer*); static Type* JSCompareTyper(Type*, Type*, Typer*);
...@@ -984,17 +985,57 @@ Type* Typer::Visitor::JSDivideTyper(Type* lhs, Type* rhs, Typer* t) { ...@@ -984,17 +985,57 @@ Type* Typer::Visitor::JSDivideTyper(Type* lhs, Type* rhs, Typer* t) {
} }
Type* Typer::Visitor::JSModulusRanger(Type::RangeType* lhs,
Type::RangeType* rhs, Typer* t) {
double lmin = lhs->Min()->Number();
double lmax = lhs->Max()->Number();
double rmin = rhs->Min()->Number();
double rmax = rhs->Max()->Number();
double labs = std::max(std::abs(lmin), std::abs(lmax));
double rabs = std::max(std::abs(rmin), std::abs(rmax)) - 1;
double abs = std::min(labs, rabs);
bool maybe_minus_zero = false;
double omin = 0;
double omax = 0;
if (lmin >= 0) { // {lhs} positive.
omin = 0;
omax = abs;
} else if (lmax <= 0) { // {lhs} negative.
omin = 0 - abs;
omax = 0;
maybe_minus_zero = true;
} else {
omin = 0 - abs;
omax = abs;
maybe_minus_zero = true;
}
Factory* f = t->isolate()->factory();
Type* result = Type::Range(f->NewNumber(omin), f->NewNumber(omax), t->zone());
if (maybe_minus_zero)
result = Type::Union(result, Type::MinusZero(), t->zone());
return result;
}
Type* Typer::Visitor::JSModulusTyper(Type* lhs, Type* rhs, Typer* t) { Type* Typer::Visitor::JSModulusTyper(Type* lhs, Type* rhs, Typer* t) {
lhs = ToNumber(lhs, t); lhs = ToNumber(lhs, t);
rhs = ToNumber(rhs, t); rhs = ToNumber(rhs, t);
if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN(); if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN();
// Division is tricky, so all we do is try ruling out nan.
// TODO(neis): try ruling out -0 as well? if (lhs->Maybe(Type::NaN()) || rhs->Maybe(t->zeroish) ||
bool maybe_nan = lhs->Min() == -V8_INFINITY || lhs->Max() == +V8_INFINITY) {
lhs->Maybe(Type::NaN()) || rhs->Maybe(t->zeroish) || // Result maybe NaN.
((lhs->Min() == -V8_INFINITY || lhs->Max() == +V8_INFINITY) && return Type::Number();
(rhs->Min() == -V8_INFINITY || rhs->Max() == +V8_INFINITY)); }
return maybe_nan ? Type::Number() : Type::OrderedNumber();
lhs = Rangify(lhs, t);
rhs = Rangify(rhs, t);
if (lhs->IsRange() && rhs->IsRange()) {
return JSModulusRanger(lhs->AsRange(), rhs->AsRange(), t);
}
return Type::OrderedNumber();
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <functional> #include <functional>
#include "src/codegen.h"
#include "src/compiler/node-properties-inl.h" #include "src/compiler/node-properties-inl.h"
#include "src/compiler/typer.h" #include "src/compiler/typer.h"
#include "test/cctest/cctest.h" #include "test/cctest/cctest.h"
...@@ -14,7 +15,7 @@ using namespace v8::internal; ...@@ -14,7 +15,7 @@ using namespace v8::internal;
using namespace v8::internal::compiler; using namespace v8::internal::compiler;
// TODO(titzer): generate a large set of deterministic inputs for these tests.
class TyperTester : public HandleAndZoneScope, public GraphAndBuilders { class TyperTester : public HandleAndZoneScope, public GraphAndBuilders {
public: public:
TyperTester() TyperTester()
...@@ -79,11 +80,15 @@ class TyperTester : public HandleAndZoneScope, public GraphAndBuilders { ...@@ -79,11 +80,15 @@ class TyperTester : public HandleAndZoneScope, public GraphAndBuilders {
Type* RandomRange(bool int32 = false) { Type* RandomRange(bool int32 = false) {
std::vector<double>& numbers = int32 ? int32s : integers; std::vector<double>& numbers = int32 ? int32s : integers;
double i = numbers[rng_->NextInt(static_cast<int>(numbers.size()))];
double j = numbers[rng_->NextInt(static_cast<int>(numbers.size()))];
return NewRange(i, j);
}
Type* NewRange(double i, double j) {
Factory* f = isolate()->factory(); Factory* f = isolate()->factory();
int i = rng_->NextInt(static_cast<int>(numbers.size())); i::Handle<i::Object> min = f->NewNumber(i);
int j = rng_->NextInt(static_cast<int>(numbers.size())); i::Handle<i::Object> max = f->NewNumber(j);
i::Handle<i::Object> min = f->NewNumber(numbers[i]);
i::Handle<i::Object> max = f->NewNumber(numbers[j]);
if (min->Number() > max->Number()) std::swap(min, max); if (min->Number() > max->Number()) std::swap(min, max);
return Type::Range(min, max, main_zone()); return Type::Range(min, max, main_zone());
} }
...@@ -110,18 +115,47 @@ class TyperTester : public HandleAndZoneScope, public GraphAndBuilders { ...@@ -110,18 +115,47 @@ class TyperTester : public HandleAndZoneScope, public GraphAndBuilders {
return RandomInt(range->Min()->Number(), range->Max()->Number()); return RandomInt(range->Min()->Number(), range->Max()->Number());
} }
// Careful, this function runs O(max_width^5) trials.
template <class BinaryFunction>
void TestBinaryArithOpCloseToZero(const Operator* op, BinaryFunction opfun,
int max_width) {
const int min_min = -2 - max_width / 2;
const int max_min = 2 + max_width / 2;
for (int width = 0; width < max_width; width++) {
for (int lmin = min_min; lmin <= max_min; lmin++) {
for (int rmin = min_min; rmin <= max_min; rmin++) {
Type* r1 = NewRange(lmin, lmin + width);
Type* r2 = NewRange(rmin, rmin + width);
Type* expected_type = TypeBinaryOp(op, r1, r2);
for (int x1 = lmin; x1 < lmin + width; x1++) {
for (int x2 = rmin; x2 < rmin + width; x2++) {
double result_value = opfun(x1, x2);
Type* result_type = Type::Constant(
isolate()->factory()->NewNumber(result_value), main_zone());
CHECK(result_type->Is(expected_type));
}
}
}
}
}
}
template <class BinaryFunction> template <class BinaryFunction>
void TestBinaryArithOp(const Operator* op, BinaryFunction opfun) { void TestBinaryArithOp(const Operator* op, BinaryFunction opfun) {
TestBinaryArithOpCloseToZero(op, opfun, 8);
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; ++i) {
Type::RangeType* r1 = RandomRange()->AsRange(); Type::RangeType* r1 = RandomRange()->AsRange();
Type::RangeType* r2 = RandomRange()->AsRange(); Type::RangeType* r2 = RandomRange()->AsRange();
Type* expected_type = TypeBinaryOp(op, r1, r2); Type* expected_type = TypeBinaryOp(op, r1, r2);
double x1 = RandomInt(r1); for (int i = 0; i < 10; i++) {
double x2 = RandomInt(r2); double x1 = RandomInt(r1);
double result_value = opfun(x1, x2); double x2 = RandomInt(r2);
Type* result_type = Type::Constant( double result_value = opfun(x1, x2);
isolate()->factory()->NewNumber(result_value), main_zone()); Type* result_type = Type::Constant(
CHECK(result_type->Is(expected_type)); isolate()->factory()->NewNumber(result_value), main_zone());
CHECK(result_type->Is(expected_type));
}
} }
} }
...@@ -131,13 +165,16 @@ class TyperTester : public HandleAndZoneScope, public GraphAndBuilders { ...@@ -131,13 +165,16 @@ class TyperTester : public HandleAndZoneScope, public GraphAndBuilders {
Type::RangeType* r1 = RandomRange()->AsRange(); Type::RangeType* r1 = RandomRange()->AsRange();
Type::RangeType* r2 = RandomRange()->AsRange(); Type::RangeType* r2 = RandomRange()->AsRange();
Type* expected_type = TypeBinaryOp(op, r1, r2); Type* expected_type = TypeBinaryOp(op, r1, r2);
double x1 = RandomInt(r1); for (int i = 0; i < 10; i++) {
double x2 = RandomInt(r2); double x1 = RandomInt(r1);
bool result_value = opfun(x1, x2); double x2 = RandomInt(r2);
Type* result_type = Type::Constant(result_value ? bool result_value = opfun(x1, x2);
isolate()->factory()->true_value() : Type* result_type =
isolate()->factory()->false_value(), main_zone()); Type::Constant(result_value ? isolate()->factory()->true_value()
CHECK(result_type->Is(expected_type)); : isolate()->factory()->false_value(),
main_zone());
CHECK(result_type->Is(expected_type));
}
} }
} }
...@@ -147,12 +184,14 @@ class TyperTester : public HandleAndZoneScope, public GraphAndBuilders { ...@@ -147,12 +184,14 @@ class TyperTester : public HandleAndZoneScope, public GraphAndBuilders {
Type::RangeType* r1 = RandomRange(true)->AsRange(); Type::RangeType* r1 = RandomRange(true)->AsRange();
Type::RangeType* r2 = RandomRange(true)->AsRange(); Type::RangeType* r2 = RandomRange(true)->AsRange();
Type* expected_type = TypeBinaryOp(op, r1, r2); Type* expected_type = TypeBinaryOp(op, r1, r2);
int32_t x1 = static_cast<int32_t>(RandomInt(r1)); for (int i = 0; i < 10; i++) {
int32_t x2 = static_cast<int32_t>(RandomInt(r2)); int32_t x1 = static_cast<int32_t>(RandomInt(r1));
double result_value = opfun(x1, x2); int32_t x2 = static_cast<int32_t>(RandomInt(r2));
Type* result_type = Type::Constant( double result_value = opfun(x1, x2);
isolate()->factory()->NewNumber(result_value), main_zone()); Type* result_type = Type::Constant(
CHECK(result_type->Is(expected_type)); isolate()->factory()->NewNumber(result_value), main_zone());
CHECK(result_type->Is(expected_type));
}
} }
} }
...@@ -216,6 +255,12 @@ TEST(TypeJSDivide) { ...@@ -216,6 +255,12 @@ TEST(TypeJSDivide) {
} }
TEST(TypeJSModulus) {
TyperTester t;
t.TestBinaryArithOp(t.javascript_.Modulus(), modulo);
}
TEST(TypeJSBitwiseOr) { TEST(TypeJSBitwiseOr) {
TyperTester t; TyperTester t;
t.TestBinaryBitOp(t.javascript_.BitwiseOr(), bit_or); t.TestBinaryBitOp(t.javascript_.BitwiseOr(), bit_or);
...@@ -325,10 +370,10 @@ TEST(TypeJSStrictNotEqual) { ...@@ -325,10 +370,10 @@ TEST(TypeJSStrictNotEqual) {
V(Modulus) V(Modulus)
TEST(Monotonicity) { #define TEST_FUNC(name) \
TyperTester t; TEST(Monotonicity_##name) { \
#define TEST_OP(name) \ TyperTester t; \
t.TestBinaryMonotonicity(t.javascript_.name()); t.TestBinaryMonotonicity(t.javascript_.name()); \
JSBINOP_LIST(TEST_OP) }
#undef TEST_OP JSBINOP_LIST(TEST_FUNC)
} #undef TEST_FUNC
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