Chromium Code Reviews| Index: src/types.h |
| diff --git a/src/types.h b/src/types.h |
| index cca8b3167b4bb9b86773d7fcf40959490a9164f0..1099691d85663c3b436ba2524550d37cc4a72c40 100644 |
| --- a/src/types.h |
| +++ b/src/types.h |
| @@ -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, -1) |
| -#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. |
| @@ -279,17 +318,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) { |
| @@ -357,7 +396,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); } |
| @@ -404,6 +443,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(); |
| @@ -466,16 +512,45 @@ class TypeImpl : public Config::Base { |
| int BitsetGlb() { return BitsetType::Glb(this); } |
| int BitsetLub() { return BitsetType::Lub(this); } |
| - int InherentBitsetLub() { return BitsetType::InherentLub(this); } |
| bool SlowIs(TypeImpl* that); |
| - TypeHandle Rebound(int bitset, Region* region); |
| - int BoundBy(TypeImpl* that); |
| - int IndexInUnion(int 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); |
| }; |
| @@ -497,20 +572,27 @@ class TypeImpl<Config>::BitsetType : public TypeImpl<Config> { |
| int Bitset() { return Config::as_bitset(this); } |
| static TypeImpl* New(int bitset) { |
| + DCHECK(bitset == kNone || IsInhabited(bitset)); |
| return static_cast<BitsetType*>(Config::from_bitset(bitset)); |
| } |
| static TypeHandle New(int bitset, Region* region) { |
| + DCHECK(bitset == kNone || IsInhabited(bitset)); |
| return Config::from_bitset(bitset, region); |
| } |
| + // TODO(neis): Eventually allow again for types with empty semantics |
| + // part and modify intersection and possibly subtyping accordingly. |
| static bool IsInhabited(int bitset) { |
| - return (bitset & kRepresentation) && (bitset & kSemantic); |
| + return bitset & kSemantic; |
| } |
| static bool Is(int bitset1, int bitset2) { |
| return (bitset1 | bitset2) == bitset2; |
| } |
| + static double Min(int bitset); |
| + static double Max(int bitset); |
| + |
| static int Glb(TypeImpl* type); // greatest lower bound that's a bitset |
| static int Lub(TypeImpl* type); // least upper bound that's a bitset |
| static int Lub(i::Object* value); |
| @@ -518,12 +600,29 @@ class TypeImpl<Config>::BitsetType : public TypeImpl<Config> { |
| static int Lub(int32_t value); |
| static int Lub(uint32_t value); |
| static int Lub(i::Map* map); |
| - static int Lub(double min, double max); |
| - static int InherentLub(TypeImpl* type); |
| + static int Lub(Limits lim); |
| static const char* Name(int bitset); |
| static void Print(OStream& os, int bitset); // NOLINT |
| - using TypeImpl::PrintTo; |
| +#ifdef DEBUG |
| + static void Print(int bitset); |
| +#endif |
| + |
| + private: |
| + struct BitsetMin{ |
| + int bitset; |
| + 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. |
| + } |
| }; |
| @@ -609,36 +708,25 @@ class TypeImpl<Config>::UnionType : public StructuralType { |
| 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); |
| + int BitsetLub() { |
|
rossberg
2014/09/23 15:20:38
This can't go here like this, as bitsets are an im
neis1
2014/09/24 07:21:38
Oops, forgot about that. I wanted to avoid the "l
|
| + return Config::is_class(this) ? |
| + BitsetType::Lub(*Config::as_class(this)) : this->Get(0)->AsBitset(); |
| } |
| 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) { |
| @@ -654,29 +742,24 @@ class TypeImpl<Config>::ClassType : public StructuralType { |
| template<class Config> |
| class TypeImpl<Config>::ConstantType : public StructuralType { |
| public: |
| - TypeHandle Bound() { return this->Get(0); } |
| + int BitsetLub() { return this->Get(0)->AsBitset(); } |
|
rossberg
2014/09/23 15:20:38
Same here.
neis1
2014/09/24 07:21:38
Done.
|
| 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. |
| // ----------------------------------------------------------------------------- |
| @@ -685,27 +768,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) { |
| @@ -713,6 +792,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. |
| // ----------------------------------------------------------------------------- |
| @@ -721,25 +802,15 @@ 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); } |
| + TypeHandle Outer() { return this->Get(0); } |
| - static ContextHandle New(TypeHandle outer, TypeHandle bound, Region* region) { |
| - DCHECK(BitsetType::Is( |
| - bound->AsBitset(), BitsetType::kInternal & BitsetType::kTaggedPtr)); |
| + static ContextHandle New(TypeHandle outer, Region* region) { |
| ContextHandle type = Config::template cast<ContextType>( |
| - StructuralType::New(StructuralType::kContextTag, 2, region)); |
| - type->Set(0, bound); |
| - type->Set(1, outer); |
| + StructuralType::New(StructuralType::kContextTag, 1, region)); |
| + type->Set(0, outer); |
| return type; |
| } |
| - static ContextHandle New(TypeHandle outer, Region* region) { |
| - TypeHandle bound = BitsetType::New( |
| - BitsetType::kInternal & BitsetType::kTaggedPtr, region); |
| - return New(outer, bound, region); |
| - } |
| - |
| static ContextType* cast(TypeImpl* type) { |
| DCHECK(type->IsContext()); |
| return static_cast<ContextType*>(type); |
| @@ -753,23 +824,15 @@ 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); } |
| + TypeHandle Element() { return this->Get(0); } |
| - static ArrayHandle New(TypeHandle element, TypeHandle bound, Region* region) { |
| - DCHECK(BitsetType::Is(bound->AsBitset(), BitsetType::kArray)); |
| + static ArrayHandle New(TypeHandle element, Region* region) { |
| ArrayHandle type = Config::template cast<ArrayType>( |
| - StructuralType::New(StructuralType::kArrayTag, 2, region)); |
| - type->Set(0, bound); |
| - type->Set(1, element); |
| + StructuralType::New(StructuralType::kArrayTag, 1, region)); |
| + type->Set(0, element); |
| return type; |
| } |
| - static ArrayHandle New(TypeHandle element, Region* region) { |
| - TypeHandle bound = BitsetType::New(BitsetType::kArray, region); |
| - return New(element, bound, region); |
| - } |
| - |
| static ArrayType* cast(TypeImpl* type) { |
| DCHECK(type->IsArray()); |
| return static_cast<ArrayType*>(type); |
| @@ -783,32 +846,22 @@ 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); } |
| + void InitParameter(int i, TypeHandle type) { this->Set(2 + i, type); } |
| static FunctionHandle New( |
| - TypeHandle result, TypeHandle receiver, TypeHandle bound, |
| - int arity, Region* region) { |
| - DCHECK(BitsetType::Is(bound->AsBitset(), BitsetType::kFunction)); |
| + TypeHandle result, TypeHandle receiver, int arity, Region* region) { |
| 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); |
| + StructuralType::New(StructuralType::kFunctionTag, 2 + arity, region)); |
| + type->Set(0, result); |
| + type->Set(1, receiver); |
| return 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); |
| - } |
| - |
| static FunctionType* cast(TypeImpl* type) { |
| DCHECK(type->IsFunction()); |
| return static_cast<FunctionType*>(type); |
| @@ -853,11 +906,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); |
| @@ -900,11 +948,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); |