Commit 8bd0bd52 authored by jarin's avatar jarin Committed by Commit bot

[turbofan] Make the representation type component independent of the semantic component.

R=rossberg@chromium.org
BUG=

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

Cr-Commit-Position: refs/heads/master@{#26621}
parent 30c71859
......@@ -436,14 +436,14 @@ void Typer::Decorator::Decorate(Node* node, bool incomplete) {
Bounds Typer::Visitor::TypeUnaryOp(Node* node, UnaryTyperFun f) {
Bounds input = Operand(node, 0);
Type* upper = input.upper->Is(Type::None())
? Type::None()
: f(input.upper, typer_);
Type* lower = input.lower->Is(Type::None())
? Type::None()
: (input.lower == input.upper || upper->IsConstant())
? upper // TODO(neis): Extend this to Range(x,x), NaN, MinusZero, ...?
: f(input.lower, typer_);
Type* upper =
input.upper->IsInhabited() ? f(input.upper, typer_) : Type::None();
Type* lower = input.lower->IsInhabited()
? ((input.lower == input.upper || upper->IsConstant())
? upper // TODO(neis): Extend this to Range(x,x),
// NaN, MinusZero, ...?
: f(input.lower, typer_))
: Type::None();
// TODO(neis): Figure out what to do with lower bound.
return Bounds(lower, upper);
}
......@@ -452,15 +452,16 @@ Bounds Typer::Visitor::TypeUnaryOp(Node* node, UnaryTyperFun f) {
Bounds Typer::Visitor::TypeBinaryOp(Node* node, BinaryTyperFun f) {
Bounds left = Operand(node, 0);
Bounds right = Operand(node, 1);
Type* upper = left.upper->Is(Type::None()) || right.upper->Is(Type::None())
? Type::None()
: f(left.upper, right.upper, typer_);
Type* lower = left.lower->Is(Type::None()) || right.lower->Is(Type::None())
? Type::None()
: ((left.lower == left.upper && right.lower == right.upper) ||
Type* upper = left.upper->IsInhabited() && right.upper->IsInhabited()
? f(left.upper, right.upper, typer_)
: Type::None();
Type* lower =
left.lower->IsInhabited() && right.lower->IsInhabited()
? (((left.lower == left.upper && right.lower == right.upper) ||
upper->IsConstant())
? upper
: f(left.lower, right.lower, typer_);
: f(left.lower, right.lower, typer_))
: Type::None();
// TODO(neis): Figure out what to do with lower bound.
return Bounds(lower, upper);
}
......
......@@ -15,7 +15,7 @@ namespace internal {
template <class T>
HType HType::FromType(typename T::TypeHandle type) {
if (T::Any()->Is(type)) return HType::Any();
if (type->Is(T::None())) return HType::None();
if (!type->IsInhabited()) return HType::None();
if (type->Is(T::SignedSmall())) return HType::Smi();
if (type->Is(T::Number())) return HType::TaggedNumber();
if (type->Is(T::Null())) return HType::Null();
......
......@@ -28,7 +28,6 @@ typename TypeImpl<Config>::Limits TypeImpl<Config>::Intersect(
Limits result(lhs);
if (lhs.min < rhs.min) result.min = rhs.min;
if (lhs.max > rhs.max) result.max = rhs.max;
result.representation = lhs.representation & rhs.representation;
return result;
}
......@@ -43,10 +42,11 @@ template <class Config>
typename TypeImpl<Config>::Limits TypeImpl<Config>::Union(Limits lhs,
Limits rhs) {
DisallowHeapAllocation no_allocation;
if (IsEmpty(lhs)) return rhs;
if (IsEmpty(rhs)) return lhs;
Limits result(lhs);
if (lhs.min > rhs.min) result.min = rhs.min;
if (lhs.max < rhs.max) result.max = rhs.max;
result.representation = lhs.representation | rhs.representation;
return result;
}
......@@ -66,8 +66,7 @@ bool TypeImpl<Config>::Contains(
typename TypeImpl<Config>::RangeType* lhs,
typename TypeImpl<Config>::RangeType* rhs) {
DisallowHeapAllocation no_allocation;
return BitsetType::Is(rhs->Bound(), lhs->Bound()) &&
lhs->Min() <= rhs->Min() && rhs->Max() <= lhs->Max();
return lhs->Min() <= rhs->Min() && rhs->Max() <= lhs->Max();
}
......@@ -76,7 +75,6 @@ bool TypeImpl<Config>::Contains(typename TypeImpl<Config>::RangeType* lhs,
typename TypeImpl<Config>::ConstantType* rhs) {
DisallowHeapAllocation no_allocation;
return IsInteger(*rhs->Value()) &&
BitsetType::Is(rhs->Bound()->AsBitset(), lhs->Bound()) &&
lhs->Min() <= rhs->Value()->Number() &&
rhs->Value()->Number() <= lhs->Max();
}
......@@ -87,7 +85,6 @@ bool TypeImpl<Config>::Contains(
typename TypeImpl<Config>::RangeType* range, i::Object* val) {
DisallowHeapAllocation no_allocation;
return IsInteger(val) &&
BitsetType::Is(BitsetType::Lub(val), range->Bound()) &&
range->Min() <= val->Number() && val->Number() <= range->Max();
}
......@@ -97,7 +94,7 @@ bool TypeImpl<Config>::Contains(
template<class Config>
double TypeImpl<Config>::Min() {
DCHECK(this->Is(Number()));
DCHECK(this->SemanticIs(Number()));
if (this->IsBitset()) return BitsetType::Min(this->AsBitset());
if (this->IsUnion()) {
double min = +V8_INFINITY;
......@@ -115,7 +112,7 @@ double TypeImpl<Config>::Min() {
template<class Config>
double TypeImpl<Config>::Max() {
DCHECK(this->Is(Number()));
DCHECK(this->SemanticIs(Number()));
if (this->IsBitset()) return BitsetType::Max(this->AsBitset());
if (this->IsUnion()) {
double max = -V8_INFINITY;
......@@ -140,23 +137,19 @@ template<class Config>
typename TypeImpl<Config>::bitset
TypeImpl<Config>::BitsetType::Glb(TypeImpl* type) {
DisallowHeapAllocation no_allocation;
// Fast case.
if (type->IsBitset()) {
return type->AsBitset();
} else if (type->IsUnion()) {
SLOW_DCHECK(type->AsUnion()->Wellformed());
return type->AsUnion()->Get(0)->BitsetGlb() |
type->AsUnion()->Get(1)->BitsetGlb(); // Shortcut.
SEMANTIC(type->AsUnion()->Get(1)->BitsetGlb()); // Shortcut.
} else if (type->IsRange()) {
bitset glb = SEMANTIC(
BitsetType::Glb(type->AsRange()->Min(), type->AsRange()->Max()));
if (glb == 0) {
return kNone;
} else {
return glb | REPRESENTATION(type->BitsetLub());
}
} else {
// (The remaining BitsetGlb's are None anyway).
return kNone;
return type->Representation();
}
}
......@@ -168,9 +161,12 @@ TypeImpl<Config>::BitsetType::Lub(TypeImpl* type) {
DisallowHeapAllocation no_allocation;
if (type->IsBitset()) return type->AsBitset();
if (type->IsUnion()) {
int bitset = kNone;
// Take the representation from the first element, which is always
// a bitset.
int bitset = type->AsUnion()->Get(0)->BitsetLub();
for (int i = 0, n = type->AsUnion()->Length(); i < n; ++i) {
bitset |= type->AsUnion()->Get(i)->BitsetLub();
// Other elements only contribute their semantic part.
bitset |= SEMANTIC(type->AsUnion()->Get(i)->BitsetLub());
}
return bitset;
}
......@@ -407,7 +403,7 @@ typename TypeImpl<Config>::bitset TypeImpl<Config>::BitsetType::Glb(
template <class Config>
double TypeImpl<Config>::BitsetType::Min(bitset bits) {
DisallowHeapAllocation no_allocation;
DCHECK(Is(bits, kNumber));
DCHECK(Is(SEMANTIC(bits), kNumber));
const Boundary* mins = Boundaries();
bool mz = SEMANTIC(bits & kMinusZero);
for (size_t i = 0; i < BoundariesSize(); ++i) {
......@@ -423,7 +419,7 @@ double TypeImpl<Config>::BitsetType::Min(bitset bits) {
template<class Config>
double TypeImpl<Config>::BitsetType::Max(bitset bits) {
DisallowHeapAllocation no_allocation;
DCHECK(Is(bits, kNumber));
DCHECK(Is(SEMANTIC(bits), kNumber));
const Boundary* mins = Boundaries();
bool mz = SEMANTIC(bits & kMinusZero);
if (BitsetType::Is(SEMANTIC(mins[BoundariesSize() - 1].bits), bits)) {
......@@ -482,22 +478,55 @@ bool TypeImpl<Config>::SimplyEquals(TypeImpl* that) {
}
template <class Config>
typename TypeImpl<Config>::bitset TypeImpl<Config>::Representation() {
return REPRESENTATION(this->BitsetLub());
}
// Check if [this] <= [that].
template<class Config>
bool TypeImpl<Config>::SlowIs(TypeImpl* that) {
DisallowHeapAllocation no_allocation;
// Fast bitset cases
if (that->IsBitset()) {
return BitsetType::Is(this->BitsetLub(), that->AsBitset());
}
if (this->IsBitset()) {
return BitsetType::Is(this->AsBitset(), that->BitsetGlb());
}
// Check the representations.
if (!BitsetType::Is(Representation(), that->Representation())) {
return false;
}
// Check the semantic part.
return SemanticIs(that);
}
// Check if SEMANTIC([this]) <= SEMANTIC([that]). The result of the method
// should be independent of the representation axis of the types.
template <class Config>
bool TypeImpl<Config>::SemanticIs(TypeImpl* that) {
DisallowHeapAllocation no_allocation;
if (this == that) return true;
if (that->IsBitset()) {
return BitsetType::Is(SEMANTIC(this->BitsetLub()), that->AsBitset());
}
if (this->IsBitset()) {
return BitsetType::Is(SEMANTIC(this->AsBitset()), that->BitsetGlb());
}
// (T1 \/ ... \/ Tn) <= T if (T1 <= T) /\ ... /\ (Tn <= T)
if (this->IsUnion()) {
for (int i = 0, n = this->AsUnion()->Length(); i < n; ++i) {
if (!this->AsUnion()->Get(i)->Is(that)) return false;
if (!this->AsUnion()->Get(i)->SemanticIs(that)) return false;
}
return true;
}
......@@ -505,7 +534,7 @@ bool TypeImpl<Config>::SlowIs(TypeImpl* that) {
// T <= (T1 \/ ... \/ Tn) if (T <= T1) \/ ... \/ (T <= Tn)
if (that->IsUnion()) {
for (int i = 0, n = that->AsUnion()->Length(); i < n; ++i) {
if (this->Is(that->AsUnion()->Get(i))) return true;
if (this->SemanticIs(that->AsUnion()->Get(i)->unhandle())) return true;
if (i > 1 && this->IsRange()) return false; // Shortcut.
}
return false;
......@@ -558,10 +587,22 @@ template<class Config>
bool TypeImpl<Config>::Maybe(TypeImpl* that) {
DisallowHeapAllocation no_allocation;
// Take care of the representation part (and also approximate
// the semantic part).
if (!BitsetType::IsInhabited(this->BitsetLub() & that->BitsetLub()))
return false;
return SemanticMaybe(that);
}
template <class Config>
bool TypeImpl<Config>::SemanticMaybe(TypeImpl* that) {
DisallowHeapAllocation no_allocation;
// (T1 \/ ... \/ Tn) overlaps T if (T1 overlaps T) \/ ... \/ (Tn overlaps T)
if (this->IsUnion()) {
for (int i = 0, n = this->AsUnion()->Length(); i < n; ++i) {
if (this->AsUnion()->Get(i)->Maybe(that)) return true;
if (this->AsUnion()->Get(i)->SemanticMaybe(that)) return true;
}
return false;
}
......@@ -569,12 +610,12 @@ bool TypeImpl<Config>::Maybe(TypeImpl* that) {
// T overlaps (T1 \/ ... \/ Tn) if (T overlaps T1) \/ ... \/ (T overlaps Tn)
if (that->IsUnion()) {
for (int i = 0, n = that->AsUnion()->Length(); i < n; ++i) {
if (this->Maybe(that->AsUnion()->Get(i))) return true;
if (this->SemanticMaybe(that->AsUnion()->Get(i)->unhandle())) return true;
}
return false;
}
if (!BitsetType::IsInhabited(this->BitsetLub() & that->BitsetLub()))
if (!BitsetType::SemanticIsInhabited(this->BitsetLub() & that->BitsetLub()))
return false;
if (this->IsBitset() && that->IsBitset()) return true;
......@@ -599,7 +640,7 @@ bool TypeImpl<Config>::Maybe(TypeImpl* that) {
}
}
if (that->IsRange()) {
return that->Maybe(this); // This case is handled above.
return that->SemanticMaybe(this); // This case is handled above.
}
if (this->IsBitset() || that->IsBitset()) return true;
......@@ -639,28 +680,27 @@ bool TypeImpl<Config>::UnionType::Wellformed() {
DisallowHeapAllocation no_allocation;
// This checks the invariants of the union representation:
// 1. There are at least two elements.
// 2. At most one element is a bitset, and it must be the first one.
// 3. At most one element is a range, and it must be the second one
// (even when the first element is not a bitset).
// 2. The first element is a bitset, no other element is a bitset.
// 3. At most one element is a range, and it must be the second one.
// 4. No element is itself a union.
// 5. No element is a subtype of any other.
// 5. No element (except the bitset) is a subtype of any other.
// 6. If there is a range, then the bitset type does not contain
// plain number bits.
DCHECK(this->Length() >= 2); // (1)
bitset number_bits = this->Get(0)->IsBitset()
? BitsetType::NumberBits(this->Get(0)->AsBitset()) : 0;
USE(number_bits);
DCHECK(this->Get(0)->IsBitset()); // (2a)
for (int i = 0; i < this->Length(); ++i) {
if (i != 0) DCHECK(!this->Get(i)->IsBitset()); // (2)
if (i != 0) DCHECK(!this->Get(i)->IsBitset()); // (2b)
if (i != 1) DCHECK(!this->Get(i)->IsRange()); // (3)
DCHECK(!this->Get(i)->IsUnion()); // (4)
for (int j = 0; j < this->Length(); ++j) {
if (i != j) DCHECK(!this->Get(i)->Is(this->Get(j))); // (5)
if (i != j && i != 0)
DCHECK(!this->Get(i)->SemanticIs(this->Get(j)->unhandle())); // (5)
}
}
DCHECK(!this->Get(1)->IsRange() || (number_bits == 0)); // (6)
DCHECK(!this->Get(1)->IsRange() ||
(BitsetType::NumberBits(this->Get(0)->AsBitset()) ==
BitsetType::kNone)); // (6)
return true;
}
......@@ -679,12 +719,10 @@ static bool AddIsSafe(int x, int y) {
template<class Config>
typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Intersect(
TypeHandle type1, TypeHandle type2, Region* region) {
bitset bits = type1->BitsetGlb() & type2->BitsetGlb();
if (!BitsetType::IsInhabited(bits)) bits = BitsetType::kNone;
// Fast case: bit sets.
if (type1->IsBitset() && type2->IsBitset()) {
return BitsetType::New(bits, region);
return BitsetType::New(type1->AsBitset() & type2->AsBitset(), region);
}
// Fast case: top or bottom types.
......@@ -696,6 +734,26 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Intersect(
if (type2->Is(type1)) return type2;
// Slow case: create union.
// Figure out the representation of the result first.
// The rest of the method should not change this representation and
// it should not make any decisions based on representations (i.e.,
// it should only use the semantic part of types).
const bitset representation =
type1->Representation() & type2->Representation();
// Semantic subtyping check - this is needed for consistency with the
// semi-fast case above - we should behave the same way regardless of
// representations. Intersection with a universal bitset should only update
// the representations.
if (type1->SemanticIs(type2->unhandle())) {
type2 = Any(region);
} else if (type2->SemanticIs(type1->unhandle())) {
type1 = Any(region);
}
bitset bits =
SEMANTIC(type1->BitsetGlb() & type2->BitsetGlb()) | representation;
int size1 = type1->IsUnion() ? type1->AsUnion()->Length() : 1;
int size2 = type2->IsUnion() ? type2->AsUnion()->Length() : 1;
if (!AddIsSafe(size1, size2)) return Any(region);
......@@ -707,8 +765,6 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Intersect(
// Deal with bitsets.
result->Set(size++, BitsetType::New(bits, region));
// Insert a placeholder for the range.
result->Set(size++, None(region));
Limits lims = Limits::Empty(region);
size = IntersectAux(type1, type2, result, size, &lims, region);
......@@ -716,14 +772,12 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Intersect(
// If the range is not empty, then insert it into the union and
// remove the number bits from the bitset.
if (!IsEmpty(lims)) {
size = UpdateRange(RangeType::New(lims, region), result, size, region);
size = UpdateRange(RangeType::New(lims, representation, region), result,
size, region);
// Remove the number bits.
bitset number_bits = BitsetType::NumberBits(bits);
bits &= ~number_bits;
if (SEMANTIC(bits) == BitsetType::kNone) {
bits = BitsetType::kNone;
}
result->Set(0, BitsetType::New(bits, region));
}
return NormalizeUnion(result, size);
......@@ -733,18 +787,17 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Intersect(
template<class Config>
int TypeImpl<Config>::UpdateRange(
RangeHandle range, UnionHandle result, int size, Region* region) {
TypeHandle old_range = result->Get(1);
DCHECK(old_range->IsRange() || old_range->IsNone());
if (range->Is(old_range)) return size;
if (!old_range->Is(range->unhandle())) {
range = RangeType::New(
Union(Limits(range->AsRange()), Limits(old_range->AsRange())), region);
}
if (size == 1) {
result->Set(size++, range);
} else {
// Make space for the range.
result->Set(size++, result->Get(1));
result->Set(1, range);
}
// Remove any components that just got subsumed.
for (int i = 2; i < size; ) {
if (result->Get(i)->Is(range->unhandle())) {
if (result->Get(i)->SemanticIs(range->unhandle())) {
result->Set(i, result->Get(--size));
} else {
++i;
......@@ -757,15 +810,13 @@ int TypeImpl<Config>::UpdateRange(
template <class Config>
typename TypeImpl<Config>::Limits TypeImpl<Config>::ToLimits(bitset bits,
Region* region) {
bitset representation = REPRESENTATION(bits);
bitset number_bits = BitsetType::NumberBits(bits);
if (representation == BitsetType::kNone && number_bits == BitsetType::kNone) {
if (number_bits == BitsetType::kNone) {
return Limits::Empty(region);
}
return Limits(BitsetType::Min(number_bits), BitsetType::Max(number_bits),
representation);
return Limits(BitsetType::Min(number_bits), BitsetType::Max(number_bits));
}
......@@ -797,7 +848,7 @@ int TypeImpl<Config>::IntersectAux(TypeHandle lhs, TypeHandle rhs,
return size;
}
if (!BitsetType::IsInhabited(lhs->BitsetLub() & rhs->BitsetLub())) {
if (!BitsetType::SemanticIsInhabited(lhs->BitsetLub() & rhs->BitsetLub())) {
return size;
}
......@@ -857,9 +908,8 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::NormalizeRangeAndBitset(
// If the range is contained within the bitset, return an empty range
// (but make sure we take the representation).
bitset range_lub = range->BitsetLub();
bitset range_lub = SEMANTIC(range->BitsetLub());
if (BitsetType::Is(BitsetType::NumberBits(range_lub), *bits)) {
*bits |= range_lub;
return None(region);
}
......@@ -870,19 +920,10 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::NormalizeRangeAndBitset(
double range_min = range->Min();
double range_max = range->Max();
bitset range_representation = REPRESENTATION(range->BitsetLub());
bitset bits_representation = REPRESENTATION(*bits);
bitset representation =
(bits_representation | range_representation) & BitsetType::kNumber;
// Remove the number bits from the bitset, they would just confuse us now.
*bits &= ~number_bits;
if (bits_representation == *bits) {
*bits = BitsetType::kNone;
}
if (representation == range_representation && range_min <= bitset_min &&
range_max >= bitset_max) {
if (range_min <= bitset_min && range_max >= bitset_max) {
// Bitset is contained within the range, just return the range.
return range;
}
......@@ -894,14 +935,13 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::NormalizeRangeAndBitset(
range_max = bitset_max;
}
return RangeType::New(range_min, range_max,
BitsetType::New(representation, region), region);
BitsetType::New(BitsetType::kNone, region), region);
}
template<class Config>
typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Union(
TypeHandle type1, TypeHandle type2, Region* region) {
// Fast case: bit sets.
if (type1->IsBitset() && type2->IsBitset()) {
return BitsetType::New(type1->AsBitset() | type2->AsBitset(), region);
......@@ -915,6 +955,13 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Union(
if (type1->Is(type2)) return type2;
if (type2->Is(type1)) return type1;
// Figure out the representation of the result.
// The rest of the method should not change this representation and
// it should make any decisions based on representations (i.e.,
// it should only use the semantic part of types).
const bitset representation =
type1->Representation() | type2->Representation();
// Slow case: create union.
int size1 = type1->IsUnion() ? type1->AsUnion()->Length() : 1;
int size2 = type2->IsUnion() ? type2->AsUnion()->Length() : 1;
......@@ -926,7 +973,7 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Union(
size = 0;
// Compute the new bitset.
bitset new_bitset = type1->BitsetGlb() | type2->BitsetGlb();
bitset new_bitset = SEMANTIC(type1->BitsetGlb() | type2->BitsetGlb());
// Deal with ranges.
TypeHandle range = None(region);
......@@ -934,16 +981,17 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Union(
RangeType* range2 = type2->GetRange();
if (range1 != NULL && range2 != NULL) {
Limits lims = Union(Limits(range1), Limits(range2));
RangeHandle union_range = RangeType::New(lims, region);
RangeHandle union_range = RangeType::New(lims, representation, region);
range = NormalizeRangeAndBitset(union_range, &new_bitset, region);
} else if (range1 != NULL) {
range = NormalizeRangeAndBitset(handle(range1), &new_bitset, region);
} else if (range2 != NULL) {
range = NormalizeRangeAndBitset(handle(range2), &new_bitset, region);
}
new_bitset = SEMANTIC(new_bitset) | representation;
TypeHandle bits = BitsetType::New(new_bitset, region);
result->Set(size++, bits);
result->Set(size++, range);
if (!range->IsNone()) result->Set(size++, range);
size = AddToUnion(type1, result, size, region);
size = AddToUnion(type2, result, size, region);
......@@ -964,7 +1012,7 @@ int TypeImpl<Config>::AddToUnion(
return size;
}
for (int i = 0; i < size; ++i) {
if (type->Is(result->Get(i))) return size;
if (type->SemanticIs(result->Get(i)->unhandle())) return size;
}
result->Set(size++, type);
return size;
......@@ -974,22 +1022,47 @@ int TypeImpl<Config>::AddToUnion(
template<class Config>
typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::NormalizeUnion(
UnionHandle unioned, int size) {
DCHECK(size >= 2);
// If range is subsumed by bitset, use its place for a different type.
if (unioned->Get(1)->Is(unioned->Get(0))) {
unioned->Set(1, unioned->Get(--size));
DCHECK(size >= 1);
DCHECK(unioned->Get(0)->IsBitset());
// If the union has just one element, return it.
if (size == 1) {
return unioned->Get(0);
}
bitset bits = unioned->Get(0)->AsBitset();
// If the union only consists of a range, we can get rid of the union.
if (size == 2 && SEMANTIC(bits) == BitsetType::kNone) {
bitset representation = REPRESENTATION(bits);
if (representation == unioned->Get(1)->Representation()) {
return unioned->Get(1);
}
// If bitset is None, use its place for a different type.
if (size >= 2 && unioned->Get(0)->IsNone()) {
unioned->Set(0, unioned->Get(--size));
// TODO(jarin) If the element at 1 is range of constant, slap
// the representation on it and return that.
}
if (size == 1) return unioned->Get(0);
unioned->Shrink(size);
SLOW_DCHECK(unioned->Wellformed());
return unioned;
}
// -----------------------------------------------------------------------------
// Component extraction
// static
template <class Config>
typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Representation(
TypeHandle t, Region* region) {
return BitsetType::New(t->Representation(), region);
}
// static
template <class Config>
typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Semantic(
TypeHandle t, Region* region) {
return Intersect(t, BitsetType::New(BitsetType::kSemantic, region), region);
}
// -----------------------------------------------------------------------------
// Iteration.
......
......@@ -430,8 +430,11 @@ class TypeImpl : public Config::Base {
return Of(*value, region);
}
// Predicates.
// Extraction of components.
static TypeHandle Representation(TypeHandle t, Region* region);
static TypeHandle Semantic(TypeHandle t, Region* region);
// Predicates.
bool IsInhabited() { return BitsetType::IsInhabited(this->BitsetLub()); }
bool Is(TypeImpl* that) { return this == that || this->SlowIs(that); }
......@@ -563,12 +566,16 @@ class TypeImpl : public Config::Base {
}
UnionType* AsUnion() { return UnionType::cast(this); }
bitset Representation();
// Auxiliary functions.
bool SemanticMaybe(TypeImpl* that);
bitset BitsetGlb() { return BitsetType::Glb(this); }
bitset BitsetLub() { return BitsetType::Lub(this); }
bool SlowIs(TypeImpl* that);
bool SemanticIs(TypeImpl* that);
static bool IsInteger(double x) {
return nearbyint(x) == x && !i::IsMinusZero(x); // Allows for infinities.
......@@ -580,16 +587,9 @@ class TypeImpl : public Config::Base {
struct Limits {
double min;
double max;
bitset representation;
Limits(double min, double max, bitset representation)
: min(min), max(max), representation(representation) {}
explicit Limits(RangeType* range)
: min(range->Min()),
max(range->Max()),
representation(REPRESENTATION(range->Bound())) {}
static Limits Empty(Region* region) {
return Limits(1, 0, BitsetType::kNone);
}
Limits(double min, double max) : min(min), max(max) {}
explicit Limits(RangeType* range) : min(range->Min()), max(range->Max()) {}
static Limits Empty(Region* region) { return Limits(1, 0); }
};
static bool IsEmpty(Limits lim);
......@@ -649,11 +649,13 @@ class TypeImpl<Config>::BitsetType : public TypeImpl<Config> {
if (FLAG_enable_slow_asserts) CheckNumberBits(bits);
return Config::from_bitset(bits, region);
}
// TODO(neis): Eventually allow again for types with empty semantics
// part and modify intersection and possibly subtyping accordingly.
static bool IsInhabited(bitset bits) {
return bits & kSemantic;
return SEMANTIC(bits) != kNone && REPRESENTATION(bits) != kNone;
}
static bool SemanticIsInhabited(bitset bits) {
return SEMANTIC(bits) != kNone;
}
static bool Is(bitset bits1, bitset bits2) {
......@@ -855,8 +857,8 @@ class TypeImpl<Config>::RangeType : public TypeImpl<Config> {
return Config::template cast<RangeType>(Config::from_range(range));
}
static RangeHandle New(Limits lim, Region* region) {
return New(lim.min, lim.max, BitsetType::New(lim.representation, region),
static RangeHandle New(Limits lim, bitset representation, Region* region) {
return New(lim.min, lim.max, BitsetType::New(representation, region),
region);
}
......@@ -866,7 +868,6 @@ class TypeImpl<Config>::RangeType : public TypeImpl<Config> {
}
};
// TODO(neis): Also cache min and max values.
// TODO(neis): Allow restricting the representation.
// -----------------------------------------------------------------------------
......
......@@ -88,7 +88,6 @@
'compiler/test-run-stackcheck.cc',
'compiler/test-run-variables.cc',
'compiler/test-simplified-lowering.cc',
'compiler/test-typer.cc',
'cctest.cc',
'gay-fixed.cc',
'gay-precision.cc',
......
......@@ -109,7 +109,8 @@ struct Tests : Rep {
: isolate(CcTest::i_isolate()),
scope(isolate),
zone(),
T(Rep::ToRegion(&zone, isolate), isolate) {}
T(Rep::ToRegion(&zone, isolate), isolate,
isolate->random_number_generator()) {}
bool Equal(TypeHandle type1, TypeHandle type2) {
return
......@@ -234,14 +235,82 @@ struct Tests : Rep {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
TypeHandle type1 = *it1;
TypeHandle type2 = *it2;
TypeHandle intersect12 = T.Intersect(type1, type2);
if (this->IsBitset(type1) && this->IsBitset(type2)) {
TypeHandle intersect12 = T.Intersect(type1, type2);
bitset bits = this->AsBitset(type1) & this->AsBitset(type2);
CHECK(
(Rep::BitsetType::IsInhabited(bits) ? bits : 0) ==
this->AsBitset(intersect12));
CHECK(bits == this->AsBitset(intersect12));
}
}
}
}
void PointwiseRepresentation() {
// Check we can decompose type into semantics and representation and
// then compose it back to get an equivalent type.
int counter = 0;
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
counter++;
printf("Counter: %i\n", counter);
fflush(stdout);
TypeHandle type1 = *it1;
TypeHandle representation = T.Representation(type1);
TypeHandle semantic = T.Semantic(type1);
TypeHandle composed = T.Union(representation, semantic);
CHECK(type1->Equals(composed));
}
// Pointwiseness of Union.
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
TypeHandle type1 = *it1;
TypeHandle type2 = *it2;
TypeHandle representation1 = T.Representation(type1);
TypeHandle semantic1 = T.Semantic(type1);
TypeHandle representation2 = T.Representation(type2);
TypeHandle semantic2 = T.Semantic(type2);
TypeHandle direct_union = T.Union(type1, type2);
TypeHandle representation_union =
T.Union(representation1, representation2);
TypeHandle semantic_union = T.Union(semantic1, semantic2);
TypeHandle composed_union =
T.Union(representation_union, semantic_union);
CHECK(direct_union->Equals(composed_union));
}
}
// Pointwiseness of Intersect.
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
TypeHandle type1 = *it1;
TypeHandle type2 = *it2;
TypeHandle representation1 = T.Representation(type1);
TypeHandle semantic1 = T.Semantic(type1);
TypeHandle representation2 = T.Representation(type2);
TypeHandle semantic2 = T.Semantic(type2);
TypeHandle direct_intersection = T.Intersect(type1, type2);
TypeHandle representation_intersection =
T.Intersect(representation1, representation2);
TypeHandle semantic_intersection = T.Intersect(semantic1, semantic2);
TypeHandle composed_intersection =
T.Union(representation_intersection, semantic_intersection);
CHECK(direct_intersection->Equals(composed_intersection));
}
}
// Pointwiseness of Is.
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
TypeHandle type1 = *it1;
TypeHandle type2 = *it2;
TypeHandle representation1 = T.Representation(type1);
TypeHandle semantic1 = T.Semantic(type1);
TypeHandle representation2 = T.Representation(type2);
TypeHandle semantic2 = T.Semantic(type2);
bool representation_is = representation1->Is(representation2);
bool semantic_is = semantic1->Is(semantic2);
bool direct_is = type1->Is(type2);
CHECK(direct_is == (semantic_is && representation_is));
}
}
}
......@@ -657,11 +726,11 @@ struct Tests : Rep {
}
}
// Rangification: If T->Is(Range(-inf,+inf)) and !T->Is(None), then
// Rangification: If T->Is(Range(-inf,+inf)) and T is inhabited, then
// T->Is(Range(T->Min(), T->Max())).
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
TypeHandle type = *it;
CHECK(!(type->Is(T.Integer) && !type->Is(T.None)) ||
CHECK(!type->Is(T.Integer) || !type->IsInhabited() ||
type->Is(T.Range(type->Min(), type->Max())));
}
}
......@@ -801,7 +870,7 @@ struct Tests : Rep {
(type1->IsContext() && type2->IsContext()) ||
(type1->IsArray() && type2->IsArray()) ||
(type1->IsFunction() && type2->IsFunction()) ||
type1->Equals(T.None));
!type1->IsInhabited());
}
}
}
......@@ -1305,7 +1374,7 @@ struct Tests : Rep {
CheckDisjoint(T.SignedFunction1, T.MethodFunction);
CheckOverlap(T.ObjectConstant1, T.ObjectClass); // !!!
CheckOverlap(T.ObjectConstant2, T.ObjectClass); // !!!
CheckOverlap(T.NumberClass, T.Intersect(T.Number, T.Untagged)); // !!!
CheckOverlap(T.NumberClass, T.Intersect(T.Number, T.Tagged)); // !!!
}
void Union1() {
......@@ -1694,24 +1763,24 @@ struct Tests : Rep {
// Bitset-class
CheckEqual(T.Intersect(T.ObjectClass, T.Object), T.ObjectClass);
CheckEqual(T.Intersect(T.ObjectClass, T.Array), T.None);
CheckEqual(T.Intersect(T.ObjectClass, T.Number), T.None);
CheckEqual(T.Semantic(T.Intersect(T.ObjectClass, T.Array)), T.None);
CheckEqual(T.Semantic(T.Intersect(T.ObjectClass, T.Number)), T.None);
// Bitset-array
CheckEqual(T.Intersect(T.NumberArray, T.Object), T.NumberArray);
CheckEqual(T.Intersect(T.AnyArray, T.Proxy), T.None);
CheckEqual(T.Semantic(T.Intersect(T.AnyArray, T.Proxy)), T.None);
// Bitset-function
CheckEqual(T.Intersect(T.MethodFunction, T.Object), T.MethodFunction);
CheckEqual(T.Intersect(T.NumberFunction1, T.Proxy), T.None);
CheckEqual(T.Semantic(T.Intersect(T.NumberFunction1, T.Proxy)), T.None);
// Bitset-union
CheckEqual(
T.Intersect(T.Object, T.Union(T.ObjectConstant1, T.ObjectClass)),
T.Union(T.ObjectConstant1, T.ObjectClass));
CHECK(
!T.Intersect(T.Union(T.ArrayClass, T.ObjectConstant1), T.Number)
->IsInhabited());
CheckEqual(T.Semantic(T.Intersect(T.Union(T.ArrayClass, T.ObjectConstant1),
T.Number)),
T.None);
// Class-constant
CHECK(T.Intersect(T.ObjectConstant1, T.ObjectClass)->IsInhabited()); // !!!
......@@ -1867,8 +1936,9 @@ struct Tests : Rep {
template<class Type2, class TypeHandle2, class Region2, class Rep2>
void Convert() {
Types<Type2, TypeHandle2, Region2> T2(
Rep2::ToRegion(&zone, isolate), isolate);
Types<Type2, TypeHandle2, Region2> T2(Rep2::ToRegion(&zone, isolate),
isolate,
isolate->random_number_generator());
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
TypeHandle type1 = *it;
TypeHandle2 type2 = T2.template Convert<Type>(type1);
......@@ -1901,6 +1971,13 @@ TEST(IsSomeType) {
}
TEST(PointwiseRepresentation) {
CcTest::InitializeVM();
// ZoneTests().PointwiseRepresentation();
HeapTests().PointwiseRepresentation();
}
TEST(BitsetType) {
CcTest::InitializeVM();
ZoneTests().Bitset();
......
......@@ -28,6 +28,7 @@
#ifndef V8_TEST_CCTEST_TYPES_H_
#define V8_TEST_CCTEST_TYPES_H_
#include "src/base/utils/random-number-generator.h"
#include "src/v8.h"
namespace v8 {
......@@ -37,8 +38,8 @@ namespace internal {
template<class Type, class TypeHandle, class Region>
class Types {
public:
Types(Region* region, Isolate* isolate)
: region_(region), rng_(isolate->random_number_generator()) {
Types(Region* region, Isolate* isolate, v8::base::RandomNumberGenerator* rng)
: region_(region), rng_(rng) {
#define DECLARE_TYPE(name, value) \
name = Type::name(region); \
types.push_back(name);
......@@ -132,6 +133,10 @@ class Types {
#define DECLARE_TYPE(name, value) TypeHandle name;
PROPER_BITSET_TYPE_LIST(DECLARE_TYPE)
#undef DECLARE_TYPE
#define DECLARE_TYPE(name, value) TypeHandle Mask##name##ForTesting;
MASK_BITSET_TYPE_LIST(DECLARE_TYPE)
#undef DECLARE_TYPE
TypeHandle SignedSmall;
TypeHandle UnsignedSmall;
......@@ -212,10 +217,19 @@ class Types {
TypeHandle Union(TypeHandle t1, TypeHandle t2) {
return Type::Union(t1, t2, region_);
}
TypeHandle Intersect(TypeHandle t1, TypeHandle t2) {
return Type::Intersect(t1, t2, region_);
}
TypeHandle Representation(TypeHandle t) {
return Type::Representation(t, region_);
}
// TypeHandle Semantic(TypeHandle t) { return Intersect(t,
// MaskSemanticForTesting); }
TypeHandle Semantic(TypeHandle t) { return Type::Semantic(t, region_); }
template<class Type2, class TypeHandle2>
TypeHandle Convert(TypeHandle2 t) {
return Type::template Convert<Type2>(t, region_);
......
// Copyright 2014 the V8 project authors. All rights reserved.
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
......@@ -7,27 +7,22 @@
#include "src/codegen.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/typer.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/graph-builder-tester.h"
#include "test/cctest/types-fuzz.h"
#include "test/unittests/compiler/graph-unittest.h"
using namespace v8::internal;
using namespace v8::internal::compiler;
// TODO(titzer): generate a large set of deterministic inputs for these tests.
class TyperTester : public HandleAndZoneScope, public GraphAndBuilders {
class TyperTest : public TypedGraphTest {
public:
TyperTester()
: GraphAndBuilders(main_zone()),
types_(main_zone(), isolate()),
typer_(isolate(), graph(), MaybeHandle<Context>()),
javascript_(main_zone()) {
Node* s = graph()->NewNode(common()->Start(3));
graph()->SetStart(s);
TyperTest()
: TypedGraphTest(3),
types_(zone(), isolate(), random_number_generator()),
javascript_(zone()) {
context_node_ = graph()->NewNode(common()->Parameter(2), graph()->start());
rng_ = isolate()->random_number_generator();
rng_ = random_number_generator();
integers.push_back(0);
integers.push_back(0);
......@@ -54,28 +49,19 @@ class TyperTester : public HandleAndZoneScope, public GraphAndBuilders {
}
Types<Type, Type*, Zone> types_;
Typer typer_;
JSOperatorBuilder javascript_;
Node* context_node_;
v8::base::RandomNumberGenerator* rng_;
std::vector<double> integers;
std::vector<double> int32s;
Isolate* isolate() { return main_isolate(); }
Graph* graph() { return main_graph_; }
CommonOperatorBuilder* common() { return &main_common_; }
Node* Parameter(int index = 0) {
return graph()->NewNode(common()->Parameter(index), graph()->start());
}
Type* TypeBinaryOp(const Operator* op, Type* lhs, Type* rhs) {
Node* p0 = Parameter(0);
Node* p1 = Parameter(1);
NodeProperties::SetBounds(p0, Bounds(lhs));
NodeProperties::SetBounds(p1, Bounds(rhs));
Node* n = graph()->NewNode(
op, p0, p1, context_node_, graph()->start(), graph()->start());
Node* n = graph()->NewNode(op, p0, p1, context_node_, graph()->start(),
graph()->start());
return NodeProperties::GetBounds(n).upper;
}
......@@ -88,14 +74,17 @@ class TyperTester : public HandleAndZoneScope, public GraphAndBuilders {
Type* NewRange(double i, double j) {
if (i > j) std::swap(i, j);
return Type::Range(i, j, main_zone());
return Type::Range(i, j, zone());
}
double RandomInt(double min, double max) {
switch (rng_->NextInt(4)) {
case 0: return min;
case 1: return max;
default: break;
case 0:
return min;
case 1:
return max;
default:
break;
}
if (min == +V8_INFINITY) return +V8_INFINITY;
if (max == -V8_INFINITY) return -V8_INFINITY;
......@@ -130,8 +119,8 @@ class TyperTester : public HandleAndZoneScope, public GraphAndBuilders {
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));
isolate()->factory()->NewNumber(result_value), zone());
EXPECT_TRUE(result_type->Is(expected_type));
}
}
}
......@@ -151,8 +140,8 @@ class TyperTester : public HandleAndZoneScope, public GraphAndBuilders {
double x2 = RandomInt(r2);
double result_value = opfun(x1, x2);
Type* result_type = Type::Constant(
isolate()->factory()->NewNumber(result_value), main_zone());
CHECK(result_type->Is(expected_type));
isolate()->factory()->NewNumber(result_value), zone());
EXPECT_TRUE(result_type->Is(expected_type));
}
}
}
......@@ -170,8 +159,8 @@ class TyperTester : public HandleAndZoneScope, public GraphAndBuilders {
Type* result_type =
Type::Constant(result_value ? isolate()->factory()->true_value()
: isolate()->factory()->false_value(),
main_zone());
CHECK(result_type->Is(expected_type));
zone());
EXPECT_TRUE(result_type->Is(expected_type));
}
}
}
......@@ -187,8 +176,8 @@ class TyperTester : public HandleAndZoneScope, public GraphAndBuilders {
int32_t x2 = static_cast<int32_t>(RandomInt(r2));
double result_value = opfun(x1, x2);
Type* result_type = Type::Constant(
isolate()->factory()->NewNumber(result_value), main_zone());
CHECK(result_type->Is(expected_type));
isolate()->factory()->NewNumber(result_value), zone());
EXPECT_TRUE(result_type->Is(expected_type));
}
}
}
......@@ -206,20 +195,26 @@ class TyperTester : public HandleAndZoneScope, public GraphAndBuilders {
Type* type1 = types_.Fuzz();
Type* type2 = types_.Fuzz();
Type* type = TypeBinaryOp(op, type1, type2);
Type* subtype1 = RandomSubtype(type1);;
Type* subtype2 = RandomSubtype(type2);;
Type* subtype1 = RandomSubtype(type1);
;
Type* subtype2 = RandomSubtype(type2);
;
Type* subtype = TypeBinaryOp(op, subtype1, subtype2);
CHECK(subtype->Is(type));
EXPECT_TRUE(subtype->Is(type));
}
}
};
static int32_t shift_left(int32_t x, int32_t y) { return x << y; }
static int32_t shift_right(int32_t x, int32_t y) { return x >> y; }
static int32_t bit_or(int32_t x, int32_t y) { return x | y; }
static int32_t bit_and(int32_t x, int32_t y) { return x & y; }
static int32_t bit_xor(int32_t x, int32_t y) { return x ^ y; }
namespace {
int32_t shift_left(int32_t x, int32_t y) { return x << y; }
int32_t shift_right(int32_t x, int32_t y) { return x >> y; }
int32_t bit_or(int32_t x, int32_t y) { return x | y; }
int32_t bit_and(int32_t x, int32_t y) { return x & y; }
int32_t bit_xor(int32_t x, int32_t y) { return x ^ y; }
} // namespace
//------------------------------------------------------------------------------
......@@ -229,115 +224,96 @@ static int32_t bit_xor(int32_t x, int32_t y) { return x ^ y; }
// to ranges as input types.
TEST(TypeJSAdd) {
TyperTester t;
t.TestBinaryArithOp(t.javascript_.Add(), std::plus<double>());
TEST_F(TyperTest, TypeJSAdd) {
TestBinaryArithOp(javascript_.Add(), std::plus<double>());
}
TEST(TypeJSSubtract) {
TyperTester t;
t.TestBinaryArithOp(t.javascript_.Subtract(), std::minus<double>());
TEST_F(TyperTest, TypeJSSubtract) {
TestBinaryArithOp(javascript_.Subtract(), std::minus<double>());
}
TEST(TypeJSMultiply) {
TyperTester t;
t.TestBinaryArithOp(t.javascript_.Multiply(), std::multiplies<double>());
TEST_F(TyperTest, TypeJSMultiply) {
TestBinaryArithOp(javascript_.Multiply(), std::multiplies<double>());
}
TEST(TypeJSDivide) {
TyperTester t;
t.TestBinaryArithOp(t.javascript_.Divide(), std::divides<double>());
TEST_F(TyperTest, TypeJSDivide) {
TestBinaryArithOp(javascript_.Divide(), std::divides<double>());
}
TEST(TypeJSModulus) {
TyperTester t;
t.TestBinaryArithOp(t.javascript_.Modulus(), modulo);
TEST_F(TyperTest, TypeJSModulus) {
TestBinaryArithOp(javascript_.Modulus(), modulo);
}
TEST(TypeJSBitwiseOr) {
TyperTester t;
t.TestBinaryBitOp(t.javascript_.BitwiseOr(), bit_or);
TEST_F(TyperTest, TypeJSBitwiseOr) {
TestBinaryBitOp(javascript_.BitwiseOr(), bit_or);
}
TEST(TypeJSBitwiseAnd) {
TyperTester t;
t.TestBinaryBitOp(t.javascript_.BitwiseAnd(), bit_and);
TEST_F(TyperTest, TypeJSBitwiseAnd) {
TestBinaryBitOp(javascript_.BitwiseAnd(), bit_and);
}
TEST(TypeJSBitwiseXor) {
TyperTester t;
t.TestBinaryBitOp(t.javascript_.BitwiseXor(), bit_xor);
TEST_F(TyperTest, TypeJSBitwiseXor) {
TestBinaryBitOp(javascript_.BitwiseXor(), bit_xor);
}
TEST(TypeJSShiftLeft) {
TyperTester t;
t.TestBinaryBitOp(t.javascript_.ShiftLeft(), shift_left);
TEST_F(TyperTest, TypeJSShiftLeft) {
TestBinaryBitOp(javascript_.ShiftLeft(), shift_left);
}
TEST(TypeJSShiftRight) {
TyperTester t;
t.TestBinaryBitOp(t.javascript_.ShiftRight(), shift_right);
TEST_F(TyperTest, TypeJSShiftRight) {
TestBinaryBitOp(javascript_.ShiftRight(), shift_right);
}
TEST(TypeJSLessThan) {
TyperTester t;
t.TestBinaryCompareOp(t.javascript_.LessThan(), std::less<double>());
TEST_F(TyperTest, TypeJSLessThan) {
TestBinaryCompareOp(javascript_.LessThan(), std::less<double>());
}
TEST(TypeJSLessThanOrEqual) {
TyperTester t;
t.TestBinaryCompareOp(
t.javascript_.LessThanOrEqual(), std::less_equal<double>());
TEST_F(TyperTest, TypeJSLessThanOrEqual) {
TestBinaryCompareOp(javascript_.LessThanOrEqual(), std::less_equal<double>());
}
TEST(TypeJSGreaterThan) {
TyperTester t;
t.TestBinaryCompareOp(t.javascript_.GreaterThan(), std::greater<double>());
TEST_F(TyperTest, TypeJSGreaterThan) {
TestBinaryCompareOp(javascript_.GreaterThan(), std::greater<double>());
}
TEST(TypeJSGreaterThanOrEqual) {
TyperTester t;
t.TestBinaryCompareOp(
t.javascript_.GreaterThanOrEqual(), std::greater_equal<double>());
TEST_F(TyperTest, TypeJSGreaterThanOrEqual) {
TestBinaryCompareOp(javascript_.GreaterThanOrEqual(),
std::greater_equal<double>());
}
TEST(TypeJSEqual) {
TyperTester t;
t.TestBinaryCompareOp(t.javascript_.Equal(), std::equal_to<double>());
TEST_F(TyperTest, TypeJSEqual) {
TestBinaryCompareOp(javascript_.Equal(), std::equal_to<double>());
}
TEST(TypeJSNotEqual) {
TyperTester t;
t.TestBinaryCompareOp(t.javascript_.NotEqual(), std::not_equal_to<double>());
TEST_F(TyperTest, TypeJSNotEqual) {
TestBinaryCompareOp(javascript_.NotEqual(), std::not_equal_to<double>());
}
// For numbers there's no difference between strict and non-strict equality.
TEST(TypeJSStrictEqual) {
TyperTester t;
t.TestBinaryCompareOp(t.javascript_.StrictEqual(), std::equal_to<double>());
TEST_F(TyperTest, TypeJSStrictEqual) {
TestBinaryCompareOp(javascript_.StrictEqual(), std::equal_to<double>());
}
TEST(TypeJSStrictNotEqual) {
TyperTester t;
t.TestBinaryCompareOp(
t.javascript_.StrictNotEqual(), std::not_equal_to<double>());
TEST_F(TyperTest, TypeJSStrictNotEqual) {
TestBinaryCompareOp(javascript_.StrictNotEqual(),
std::not_equal_to<double>());
}
......@@ -369,9 +345,22 @@ TEST(TypeJSStrictNotEqual) {
#define TEST_FUNC(name) \
TEST(Monotonicity_##name) { \
TyperTester t; \
t.TestBinaryMonotonicity(t.javascript_.name()); \
TEST_F(TyperTest, Monotonicity_##name) { \
TestBinaryMonotonicity(javascript_.name()); \
}
JSBINOP_LIST(TEST_FUNC)
#undef TEST_FUNC
//------------------------------------------------------------------------------
// Regression tests
TEST_F(TyperTest, TypeRegressInt32Constant) {
int values[] = {-5, 10};
for (auto i : values) {
Node* c = graph()->NewNode(common()->Int32Constant(i));
Type* type = NodeProperties::GetBounds(c).upper;
EXPECT_TRUE(type->Is(NewRange(i, i)));
}
}
......@@ -73,6 +73,7 @@
'compiler/scheduler-unittest.cc',
'compiler/simplified-operator-reducer-unittest.cc',
'compiler/simplified-operator-unittest.cc',
'compiler/typer-unittest.cc',
'compiler/value-numbering-reducer-unittest.cc',
'compiler/zone-pool-unittest.cc',
'libplatform/default-platform-unittest.cc',
......
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