Redesign of the internal type system.

Besides addressing a fundamental flaw, this significantly simplifies
several aspects of the system.  The downside is a loss of precision
and a loss of algebraic properties.

Range types are now fully implemented.

R=rossberg@chromium.org
BUG=

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24163 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
neis@chromium.org 2014-09-24 07:33:51 +00:00
parent 43538e57a4
commit 6fd04d829e
3 changed files with 1140 additions and 815 deletions

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@
#ifndef V8_TYPES_H_
#define V8_TYPES_H_
#include "src/conversions.h"
#include "src/factory.h"
#include "src/handles.h"
#include "src/ostreams.h"
@ -23,6 +24,7 @@ namespace internal {
// Types consist of two dimensions: semantic (value range) and representation.
// Both are related through subtyping.
//
//
// SEMANTIC DIMENSION
//
// The following equations and inequations hold for the semantic axis:
@ -61,6 +63,7 @@ namespace internal {
// However, we also define a 'temporal' variant of the subtyping relation that
// considers the _current_ state only, i.e., Constant(x) <_now Class(map(x)).
//
//
// REPRESENTATIONAL DIMENSION
//
// For the representation axis, the following holds:
@ -88,6 +91,16 @@ namespace internal {
// SignedSmall /\ TaggedInt (a 'smi')
// Number /\ TaggedPtr (a heap number)
//
//
// RANGE TYPES
//
// A range type represents a continuous integer interval by its minimum and
// maximum value. Either value might be an infinity.
//
// Constant(v) is considered a subtype of Range(x..y) if v happens to be an
// integer between x and y.
//
//
// PREDICATES
//
// There are two main functions for testing types:
@ -109,16 +122,18 @@ namespace internal {
// Any compilation decision based on such temporary properties requires runtime
// guarding!
//
//
// PROPERTIES
//
// Various formal properties hold for constructors, operators, and predicates
// over types. For example, constructors are injective, subtyping is a complete
// partial order, union and intersection satisfy the usual algebraic properties.
// over types. For example, constructors are injective and subtyping is a
// complete partial order.
//
// See test/cctest/test-types.cc for a comprehensive executable specification,
// especially with respect to the properties of the more exotic 'temporal'
// constructors and predicates (those prefixed 'Now').
//
//
// IMPLEMENTATION
//
// Internally, all 'primitive' types, and their unions, are represented as
@ -208,11 +223,35 @@ namespace internal {
kUndefined | kInternal) \
V(Any, 0xfffffffeu)
#define BITSET_TYPE_LIST(V) \
MASK_BITSET_TYPE_LIST(V) \
/*
* The following diagrams show how integers (in the mathematical sense) are
* divided among the different atomic numerical types.
*
* If SmiValuesAre31Bits():
*
* ON OS32 OSS US OU31 OU32 ON
* ______[_______[_______[_______[_______[_______[_______
* -2^31 -2^30 0 2^30 2^31 2^32
*
* Otherwise:
*
* ON OSS US OU32 ON
* ______[_______________[_______________[_______[_______
* -2^31 0 2^31 2^32
*
*
* E.g., OtherUnsigned32 (OU32) covers all integers from 2^31 to 2^32-1.
*
*/
#define PROPER_BITSET_TYPE_LIST(V) \
REPRESENTATION_BITSET_TYPE_LIST(V) \
SEMANTIC_BITSET_TYPE_LIST(V)
#define BITSET_TYPE_LIST(V) \
MASK_BITSET_TYPE_LIST(V) \
PROPER_BITSET_TYPE_LIST(V)
// -----------------------------------------------------------------------------
// The abstract Type class, parameterized over the low-level representation.
@ -282,17 +321,17 @@ class TypeImpl : public Config::Base {
static TypeHandle type(Region* region) { \
return BitsetType::New(BitsetType::k##type, region); \
}
BITSET_TYPE_LIST(DEFINE_TYPE_CONSTRUCTOR)
PROPER_BITSET_TYPE_LIST(DEFINE_TYPE_CONSTRUCTOR)
#undef DEFINE_TYPE_CONSTRUCTOR
static TypeHandle Class(i::Handle<i::Map> map, Region* region) {
return ClassType::New(map, region);
}
static TypeHandle Constant(i::Handle<i::Object> value, Region* region) {
// TODO(neis): Return RangeType for numerical values.
return ConstantType::New(value, region);
}
static TypeHandle Range(double min, double max, Region* region) {
static TypeHandle Range(
i::Handle<i::Object> min, i::Handle<i::Object> max, Region* region) {
return RangeType::New(min, max, region);
}
static TypeHandle Context(TypeHandle outer, Region* region) {
@ -360,7 +399,7 @@ class TypeImpl : public Config::Base {
template<class TypeHandle>
bool Equals(TypeHandle that) { return this->Equals(*that); }
// Equivalent to Constant(value)->Is(this), but avoiding allocation.
// Equivalent to Constant(val)->Is(this), but avoiding allocation.
bool Contains(i::Object* val);
bool Contains(i::Handle<i::Object> val) { return this->Contains(*val); }
@ -407,6 +446,13 @@ class TypeImpl : public Config::Base {
ArrayType* AsArray() { return ArrayType::cast(this); }
FunctionType* AsFunction() { return FunctionType::cast(this); }
// Minimum and maximum of a numeric type.
// These functions do not distinguish between -0 and +0. If the type equals
// kNaN, they return NaN; otherwise kNaN is ignored. Only call these
// functions on subtypes of Number.
double Min();
double Max();
int NumClasses();
int NumConstants();
@ -469,16 +515,45 @@ class TypeImpl : public Config::Base {
bitset BitsetGlb() { return BitsetType::Glb(this); }
bitset BitsetLub() { return BitsetType::Lub(this); }
bitset InherentBitsetLub() { return BitsetType::InherentLub(this); }
bool SlowIs(TypeImpl* that);
TypeHandle Rebound(bitset bound, Region* region);
bitset BoundBy(TypeImpl* that);
int IndexInUnion(bitset bound, UnionHandle unioned, int current_size);
static int ExtendUnion(
UnionHandle unioned, int current_size, TypeHandle t,
TypeHandle other, bool is_intersect, Region* region);
static bool IsInteger(double x) {
return nearbyint(x) == x && !i::IsMinusZero(x); // Allows for infinities.
}
static bool IsInteger(i::Object* x) {
return x->IsNumber() && IsInteger(x->Number());
}
struct Limits {
i::Handle<i::Object> min;
i::Handle<i::Object> max;
Limits(i::Handle<i::Object> min, i::Handle<i::Object> max) :
min(min), max(max) {}
explicit Limits(RangeType* range) :
min(range->Min()), max(range->Max()) {}
};
static Limits Intersect(Limits lhs, Limits rhs);
static Limits Union(Limits lhs, Limits rhs);
static bool Overlap(RangeType* lhs, RangeType* rhs);
static bool Contains(RangeType* lhs, RangeType* rhs);
static bool Contains(RangeType* range, i::Object* val);
RangeType* GetRange();
static int UpdateRange(
RangeHandle type, UnionHandle result, int size, Region* region);
bool SimplyEquals(TypeImpl* that);
template<class TypeHandle>
bool SimplyEquals(TypeHandle that) { return this->SimplyEquals(*that); }
static int AddToUnion(
TypeHandle type, UnionHandle result, int size, Region* region);
static int IntersectAux(
TypeHandle type, TypeHandle other,
UnionHandle result, int size, Region* region);
static TypeHandle NormalizeUnion(UnionHandle unioned, int size);
};
@ -499,19 +574,28 @@ class TypeImpl<Config>::BitsetType : public TypeImpl<Config> {
bitset Bitset() { return Config::as_bitset(this); }
static TypeImpl* New(bitset bits) { return Config::from_bitset(bits); }
static TypeImpl* New(bitset bits) {
DCHECK(bits == kNone || IsInhabited(bits));
return Config::from_bitset(bits);
}
static TypeHandle New(bitset bits, Region* region) {
DCHECK(bits == kNone || IsInhabited(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 & kRepresentation) && (bits & kSemantic);
return bits & kSemantic;
}
static bool Is(bitset bits1, bitset bits2) {
return (bits1 | bits2) == bits2;
}
static double Min(bitset);
static double Max(bitset);
static bitset Glb(TypeImpl* type); // greatest lower bound that's a bitset
static bitset Lub(TypeImpl* type); // least upper bound that's a bitset
static bitset Lub(i::Object* value);
@ -519,12 +603,29 @@ class TypeImpl<Config>::BitsetType : public TypeImpl<Config> {
static bitset Lub(int32_t value);
static bitset Lub(uint32_t value);
static bitset Lub(i::Map* map);
static bitset Lub(double min, double max);
static bitset InherentLub(TypeImpl* type);
static bitset Lub(Limits lim);
static const char* Name(bitset);
static void Print(OStream& os, bitset); // NOLINT
using TypeImpl::PrintTo;
#ifdef DEBUG
static void Print(bitset);
#endif
private:
struct BitsetMin{
bitset bits;
double min;
};
static const BitsetMin BitsetMins31[];
static const BitsetMin BitsetMins32[];
static const BitsetMin* BitsetMins() {
return i::SmiValuesAre31Bits() ? BitsetMins31 : BitsetMins32;
}
static size_t BitsetMinsSize() {
return i::SmiValuesAre31Bits() ? 7 : 5;
/* arraysize(BitsetMins31) : arraysize(BitsetMins32); */
// Using arraysize here doesn't compile on Windows.
}
};
@ -611,35 +712,25 @@ template<class Config>
class TypeImpl<Config>::ClassType : public StructuralType {
public:
TypeHandle Bound(Region* region) {
return Config::is_class(this)
? BitsetType::New(BitsetType::Lub(*Config::as_class(this)), region)
: this->Get(0);
return Config::is_class(this) ?
BitsetType::New(BitsetType::Lub(*Config::as_class(this)), region) :
this->Get(0);
}
i::Handle<i::Map> Map() {
return Config::is_class(this)
? Config::as_class(this)
: this->template GetValue<i::Map>(1);
}
static ClassHandle New(
i::Handle<i::Map> map, TypeHandle bound, Region* region) {
DCHECK(BitsetType::Is(bound->AsBitset(), BitsetType::Lub(*map)));
ClassHandle type = Config::template cast<ClassType>(
StructuralType::New(StructuralType::kClassTag, 2, region));
type->Set(0, bound);
type->SetValue(1, map);
return type;
return Config::is_class(this) ? Config::as_class(this) :
this->template GetValue<i::Map>(1);
}
static ClassHandle New(i::Handle<i::Map> map, Region* region) {
ClassHandle type =
Config::template cast<ClassType>(Config::from_class(map, region));
if (type->IsClass()) {
return type;
} else {
TypeHandle bound = BitsetType::New(BitsetType::Lub(*map), region);
return New(map, bound, region);
if (!type->IsClass()) {
type = Config::template cast<ClassType>(
StructuralType::New(StructuralType::kClassTag, 2, region));
type->Set(0, BitsetType::New(BitsetType::Lub(*map), region));
type->SetValue(1, map);
}
return type;
}
static ClassType* cast(TypeImpl* type) {
@ -658,26 +749,21 @@ class TypeImpl<Config>::ConstantType : public StructuralType {
TypeHandle Bound() { return this->Get(0); }
i::Handle<i::Object> Value() { return this->template GetValue<i::Object>(1); }
static ConstantHandle New(
i::Handle<i::Object> value, TypeHandle bound, Region* region) {
DCHECK(BitsetType::Is(bound->AsBitset(), BitsetType::Lub(*value)));
static ConstantHandle New(i::Handle<i::Object> value, Region* region) {
ConstantHandle type = Config::template cast<ConstantType>(
StructuralType::New(StructuralType::kConstantTag, 2, region));
type->Set(0, bound);
type->Set(0, BitsetType::New(BitsetType::Lub(*value), region));
type->SetValue(1, value);
return type;
}
static ConstantHandle New(i::Handle<i::Object> value, Region* region) {
TypeHandle bound = BitsetType::New(BitsetType::Lub(*value), region);
return New(value, bound, region);
}
static ConstantType* cast(TypeImpl* type) {
DCHECK(type->IsConstant());
return static_cast<ConstantType*>(type);
}
};
// TODO(neis): Also cache value if numerical.
// TODO(neis): Allow restricting the representation.
// -----------------------------------------------------------------------------
@ -686,27 +772,23 @@ class TypeImpl<Config>::ConstantType : public StructuralType {
template<class Config>
class TypeImpl<Config>::RangeType : public StructuralType {
public:
TypeHandle Bound() { return this->Get(0); }
double Min() { return this->template GetValue<i::HeapNumber>(1)->value(); }
double Max() { return this->template GetValue<i::HeapNumber>(2)->value(); }
int BitsetLub() { return this->Get(0)->AsBitset(); }
i::Handle<i::Object> Min() { return this->template GetValue<i::Object>(1); }
i::Handle<i::Object> Max() { return this->template GetValue<i::Object>(2); }
static RangeHandle New(
double min, double max, TypeHandle bound, Region* region) {
DCHECK(BitsetType::Is(bound->AsBitset(), BitsetType::Lub(min, max)));
i::Handle<i::Object> min, i::Handle<i::Object> max, Region* region) {
DCHECK(min->Number() <= max->Number());
RangeHandle type = Config::template cast<RangeType>(
StructuralType::New(StructuralType::kRangeTag, 3, region));
type->Set(0, bound);
Factory* factory = Config::isolate(region)->factory();
Handle<HeapNumber> minV = factory->NewHeapNumber(min);
Handle<HeapNumber> maxV = factory->NewHeapNumber(max);
type->SetValue(1, minV);
type->SetValue(2, maxV);
type->Set(0, BitsetType::New(BitsetType::Lub(Limits(min, max)), region));
type->SetValue(1, min);
type->SetValue(2, max);
return type;
}
static RangeHandle New(double min, double max, Region* region) {
TypeHandle bound = BitsetType::New(BitsetType::Lub(min, max), region);
return New(min, max, bound, region);
static RangeHandle New(Limits lim, Region* region) {
return New(lim.min, lim.max, region);
}
static RangeType* cast(TypeImpl* type) {
@ -714,6 +796,8 @@ class TypeImpl<Config>::RangeType : public StructuralType {
return static_cast<RangeType*>(type);
}
};
// TODO(neis): Also cache min and max values.
// TODO(neis): Allow restricting the representation.
// -----------------------------------------------------------------------------
@ -722,23 +806,13 @@ class TypeImpl<Config>::RangeType : public StructuralType {
template<class Config>
class TypeImpl<Config>::ContextType : public StructuralType {
public:
TypeHandle Bound() { return this->Get(0); }
TypeHandle Outer() { return this->Get(1); }
static ContextHandle New(TypeHandle outer, TypeHandle bound, Region* region) {
DCHECK(BitsetType::Is(
bound->AsBitset(), BitsetType::kInternal & BitsetType::kTaggedPtr));
ContextHandle type = Config::template cast<ContextType>(
StructuralType::New(StructuralType::kContextTag, 2, region));
type->Set(0, bound);
type->Set(1, outer);
return type;
}
TypeHandle Outer() { return this->Get(0); }
static ContextHandle New(TypeHandle outer, Region* region) {
TypeHandle bound = BitsetType::New(
BitsetType::kInternal & BitsetType::kTaggedPtr, region);
return New(outer, bound, region);
ContextHandle type = Config::template cast<ContextType>(
StructuralType::New(StructuralType::kContextTag, 1, region));
type->Set(0, outer);
return type;
}
static ContextType* cast(TypeImpl* type) {
@ -754,21 +828,13 @@ class TypeImpl<Config>::ContextType : public StructuralType {
template<class Config>
class TypeImpl<Config>::ArrayType : public StructuralType {
public:
TypeHandle Bound() { return this->Get(0); }
TypeHandle Element() { return this->Get(1); }
static ArrayHandle New(TypeHandle element, TypeHandle bound, Region* region) {
DCHECK(BitsetType::Is(bound->AsBitset(), BitsetType::kArray));
ArrayHandle type = Config::template cast<ArrayType>(
StructuralType::New(StructuralType::kArrayTag, 2, region));
type->Set(0, bound);
type->Set(1, element);
return type;
}
TypeHandle Element() { return this->Get(0); }
static ArrayHandle New(TypeHandle element, Region* region) {
TypeHandle bound = BitsetType::New(BitsetType::kArray, region);
return New(element, bound, region);
ArrayHandle type = Config::template cast<ArrayType>(
StructuralType::New(StructuralType::kArrayTag, 1, region));
type->Set(0, element);
return type;
}
static ArrayType* cast(TypeImpl* type) {
@ -784,30 +850,20 @@ class TypeImpl<Config>::ArrayType : public StructuralType {
template<class Config>
class TypeImpl<Config>::FunctionType : public StructuralType {
public:
int Arity() { return this->Length() - 3; }
TypeHandle Bound() { return this->Get(0); }
TypeHandle Result() { return this->Get(1); }
TypeHandle Receiver() { return this->Get(2); }
TypeHandle Parameter(int i) { return this->Get(3 + i); }
int Arity() { return this->Length() - 2; }
TypeHandle Result() { return this->Get(0); }
TypeHandle Receiver() { return this->Get(1); }
TypeHandle Parameter(int i) { return this->Get(2 + i); }
void InitParameter(int i, TypeHandle type) { this->Set(3 + i, type); }
static FunctionHandle New(
TypeHandle result, TypeHandle receiver, TypeHandle bound,
int arity, Region* region) {
DCHECK(BitsetType::Is(bound->AsBitset(), BitsetType::kFunction));
FunctionHandle type = Config::template cast<FunctionType>(
StructuralType::New(StructuralType::kFunctionTag, 3 + arity, region));
type->Set(0, bound);
type->Set(1, result);
type->Set(2, receiver);
return type;
}
void InitParameter(int i, TypeHandle type) { this->Set(2 + i, type); }
static FunctionHandle New(
TypeHandle result, TypeHandle receiver, int arity, Region* region) {
TypeHandle bound = BitsetType::New(BitsetType::kFunction, region);
return New(result, receiver, bound, arity, region);
FunctionHandle type = Config::template cast<FunctionType>(
StructuralType::New(StructuralType::kFunctionTag, 2 + arity, region));
type->Set(0, result);
type->Set(1, receiver);
return type;
}
static FunctionType* cast(TypeImpl* type) {
@ -854,11 +910,6 @@ struct ZoneTypeConfig {
typedef i::Zone Region;
template<class T> struct Handle { typedef T* type; };
// TODO(neis): This will be removed again once we have struct_get_double().
static inline i::Isolate* isolate(Region* region) {
return region->isolate();
}
template<class T> static inline T* handle(T* type);
template<class T> static inline T* cast(Type* type);
@ -901,11 +952,6 @@ struct HeapTypeConfig {
typedef i::Isolate Region;
template<class T> struct Handle { typedef i::Handle<T> type; };
// TODO(neis): This will be removed again once we have struct_get_double().
static inline i::Isolate* isolate(Region* region) {
return region;
}
template<class T> static inline i::Handle<T> handle(T* type);
template<class T> static inline i::Handle<T> cast(i::Handle<Type> type);

File diff suppressed because it is too large Load Diff