| // Copyright 2014 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. |
| |
| #ifndef V8_TYPES_H_ |
| #define V8_TYPES_H_ |
| |
| #include "src/conversions.h" |
| #include "src/handles.h" |
| #include "src/objects.h" |
| #include "src/ostreams.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| // SUMMARY |
| // |
| // A simple type system for compiler-internal use. It is based entirely on |
| // union types, and all subtyping hence amounts to set inclusion. Besides the |
| // obvious primitive types and some predefined unions, the type language also |
| // can express class types (a.k.a. specific maps) and singleton types (i.e., |
| // concrete constants). |
| // |
| // 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: |
| // |
| // None <= T |
| // T <= Any |
| // |
| // Number = Signed32 \/ Unsigned32 \/ Double |
| // Smi <= Signed32 |
| // Name = String \/ Symbol |
| // UniqueName = InternalizedString \/ Symbol |
| // InternalizedString < String |
| // |
| // Receiver = Object \/ Proxy |
| // Array < Object |
| // Function < Object |
| // RegExp < Object |
| // OtherUndetectable < Object |
| // DetectableReceiver = Receiver - OtherUndetectable |
| // |
| // Class(map) < T iff instance_type(map) < T |
| // Constant(x) < T iff instance_type(map(x)) < T |
| // Array(T) < Array |
| // Function(R, S, T0, T1, ...) < Function |
| // Context(T) < Internal |
| // |
| // Both structural Array and Function types are invariant in all parameters; |
| // relaxing this would make Union and Intersect operations more involved. |
| // There is no subtyping relation between Array, Function, or Context types |
| // and respective Constant types, since these types cannot be reconstructed |
| // for arbitrary heap values. |
| // Note also that Constant(x) < Class(map(x)) does _not_ hold, since x's map can |
| // change! (Its instance type cannot, however.) |
| // TODO(rossberg): the latter is not currently true for proxies, because of fix, |
| // but will hold once we implement direct proxies. |
| // 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: |
| // |
| // None <= R |
| // R <= Any |
| // |
| // UntaggedInt = UntaggedInt1 \/ UntaggedInt8 \/ |
| // UntaggedInt16 \/ UntaggedInt32 |
| // UntaggedFloat = UntaggedFloat32 \/ UntaggedFloat64 |
| // UntaggedNumber = UntaggedInt \/ UntaggedFloat |
| // Untagged = UntaggedNumber \/ UntaggedPtr |
| // Tagged = TaggedInt \/ TaggedPtr |
| // |
| // Subtyping relates the two dimensions, for example: |
| // |
| // Number <= Tagged \/ UntaggedNumber |
| // Object <= TaggedPtr \/ UntaggedPtr |
| // |
| // That holds because the semantic type constructors defined by the API create |
| // types that allow for all possible representations, and dually, the ones for |
| // representation types initially include all semantic ranges. Representations |
| // can then e.g. be narrowed for a given semantic type using intersection: |
| // |
| // 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 may be an infinity, in which case that infinity |
| // itself is also included in the range. A range never contains NaN or -0. |
| // |
| // If a value v happens to be an integer n, then Constant(v) is considered a |
| // subtype of Range(n, n) (and therefore also a subtype of any larger range). |
| // In order to avoid large unions, however, it is usually a good idea to use |
| // Range rather than Constant. |
| // |
| // |
| // PREDICATES |
| // |
| // There are two main functions for testing types: |
| // |
| // T1->Is(T2) -- tests whether T1 is included in T2 (i.e., T1 <= T2) |
| // T1->Maybe(T2) -- tests whether T1 and T2 overlap (i.e., T1 /\ T2 =/= 0) |
| // |
| // Typically, the former is to be used to select representations (e.g., via |
| // T->Is(SignedSmall())), and the latter to check whether a specific case needs |
| // handling (e.g., via T->Maybe(Number())). |
| // |
| // There is no functionality to discover whether a type is a leaf in the |
| // lattice. That is intentional. It should always be possible to refine the |
| // lattice (e.g., splitting up number types further) without invalidating any |
| // existing assumptions or tests. |
| // Consequently, do not normally use Equals for type tests, always use Is! |
| // |
| // The NowIs operator implements state-sensitive subtying, as described above. |
| // 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 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 |
| // bitsets. Bit 0 is reserved for tagging. Class is a heap pointer to the |
| // respective map. Only structured types require allocation. |
| // Note that the bitset representation is closed under both Union and Intersect. |
| |
| |
| // ----------------------------------------------------------------------------- |
| // Values for bitset types |
| |
| // clang-format off |
| |
| #define MASK_BITSET_TYPE_LIST(V) \ |
| V(Representation, 0xffc00000u) \ |
| V(Semantic, 0x003ffffeu) |
| |
| #define REPRESENTATION(k) ((k) & BitsetType::kRepresentation) |
| #define SEMANTIC(k) ((k) & BitsetType::kSemantic) |
| |
| #define REPRESENTATION_BITSET_TYPE_LIST(V) \ |
| V(None, 0) \ |
| V(UntaggedBit, 1u << 22 | kSemantic) \ |
| V(UntaggedIntegral8, 1u << 23 | kSemantic) \ |
| V(UntaggedIntegral16, 1u << 24 | kSemantic) \ |
| V(UntaggedIntegral32, 1u << 25 | kSemantic) \ |
| V(UntaggedFloat32, 1u << 26 | kSemantic) \ |
| V(UntaggedFloat64, 1u << 27 | kSemantic) \ |
| V(UntaggedSimd128, 1u << 28 | kSemantic) \ |
| V(UntaggedPointer, 1u << 29 | kSemantic) \ |
| V(TaggedSigned, 1u << 30 | kSemantic) \ |
| V(TaggedPointer, 1u << 31 | kSemantic) \ |
| \ |
| V(UntaggedIntegral, kUntaggedBit | kUntaggedIntegral8 | \ |
| kUntaggedIntegral16 | kUntaggedIntegral32) \ |
| V(UntaggedFloat, kUntaggedFloat32 | kUntaggedFloat64) \ |
| V(UntaggedNumber, kUntaggedIntegral | kUntaggedFloat) \ |
| V(Untagged, kUntaggedNumber | kUntaggedPointer) \ |
| V(Tagged, kTaggedSigned | kTaggedPointer) |
| |
| #define INTERNAL_BITSET_TYPE_LIST(V) \ |
| V(OtherUnsigned31, 1u << 1 | REPRESENTATION(kTagged | kUntaggedNumber)) \ |
| V(OtherUnsigned32, 1u << 2 | REPRESENTATION(kTagged | kUntaggedNumber)) \ |
| V(OtherSigned32, 1u << 3 | REPRESENTATION(kTagged | kUntaggedNumber)) \ |
| V(OtherNumber, 1u << 4 | REPRESENTATION(kTagged | kUntaggedNumber)) |
| |
| #define SEMANTIC_BITSET_TYPE_LIST(V) \ |
| V(Negative31, 1u << 5 | REPRESENTATION(kTagged | kUntaggedNumber)) \ |
| V(Null, 1u << 6 | REPRESENTATION(kTaggedPointer)) \ |
| V(Undefined, 1u << 7 | REPRESENTATION(kTaggedPointer)) \ |
| V(Boolean, 1u << 8 | REPRESENTATION(kTaggedPointer)) \ |
| V(Unsigned30, 1u << 9 | REPRESENTATION(kTagged | kUntaggedNumber)) \ |
| V(MinusZero, 1u << 10 | REPRESENTATION(kTagged | kUntaggedNumber)) \ |
| V(NaN, 1u << 11 | REPRESENTATION(kTagged | kUntaggedNumber)) \ |
| V(Symbol, 1u << 12 | REPRESENTATION(kTaggedPointer)) \ |
| V(InternalizedString, 1u << 13 | REPRESENTATION(kTaggedPointer)) \ |
| V(OtherString, 1u << 14 | REPRESENTATION(kTaggedPointer)) \ |
| V(Simd, 1u << 15 | REPRESENTATION(kTaggedPointer)) \ |
| V(OtherObject, 1u << 17 | REPRESENTATION(kTaggedPointer)) \ |
| V(OtherUndetectable, 1u << 16 | REPRESENTATION(kTaggedPointer)) \ |
| V(Proxy, 1u << 18 | REPRESENTATION(kTaggedPointer)) \ |
| V(Function, 1u << 19 | REPRESENTATION(kTaggedPointer)) \ |
| V(Internal, 1u << 20 | REPRESENTATION(kTagged | kUntagged)) \ |
| \ |
| V(Signed31, kUnsigned30 | kNegative31) \ |
| V(Signed32, kSigned31 | kOtherUnsigned31 | kOtherSigned32) \ |
| V(Negative32, kNegative31 | kOtherSigned32) \ |
| V(Unsigned31, kUnsigned30 | kOtherUnsigned31) \ |
| V(Unsigned32, kUnsigned30 | kOtherUnsigned31 | \ |
| kOtherUnsigned32) \ |
| V(Integral32, kSigned32 | kUnsigned32) \ |
| V(PlainNumber, kIntegral32 | kOtherNumber) \ |
| V(OrderedNumber, kPlainNumber | kMinusZero) \ |
| V(MinusZeroOrNaN, kMinusZero | kNaN) \ |
| V(Number, kOrderedNumber | kNaN) \ |
| V(String, kInternalizedString | kOtherString) \ |
| V(UniqueName, kSymbol | kInternalizedString) \ |
| V(Name, kSymbol | kString) \ |
| V(BooleanOrNumber, kBoolean | kNumber) \ |
| V(BooleanOrNullOrUndefined, kBoolean | kNull | kUndefined) \ |
| V(NullOrUndefined, kNull | kUndefined) \ |
| V(Undetectable, kNullOrUndefined | kOtherUndetectable) \ |
| V(NumberOrString, kNumber | kString) \ |
| V(NumberOrUndefined, kNumber | kUndefined) \ |
| V(PlainPrimitive, kNumberOrString | kBoolean | kNullOrUndefined) \ |
| V(Primitive, kSymbol | kSimd | kPlainPrimitive) \ |
| V(DetectableReceiver, kFunction | kOtherObject | kProxy) \ |
| V(Object, kFunction | kOtherObject | kOtherUndetectable) \ |
| V(Receiver, kObject | kProxy) \ |
| V(StringOrReceiver, kString | kReceiver) \ |
| V(Unique, kBoolean | kUniqueName | kNull | kUndefined | \ |
| kReceiver) \ |
| V(NonNumber, kUnique | kString | kInternal) \ |
| V(Any, 0xfffffffeu) |
| |
| // clang-format on |
| |
| /* |
| * The following diagrams show how integers (in the mathematical sense) are |
| * divided among the different atomic numerical types. |
| * |
| * ON OS32 N31 U30 OU31 OU32 ON |
| * ______[_______[_______[_______[_______[_______[_______ |
| * -2^31 -2^30 0 2^30 2^31 2^32 |
| * |
| * E.g., OtherUnsigned32 (OU32) covers all integers from 2^31 to 2^32-1. |
| * |
| * Some of the atomic numerical bitsets are internal only (see |
| * INTERNAL_BITSET_TYPE_LIST). To a types user, they should only occur in |
| * union with certain other bitsets. For instance, OtherNumber should only |
| * occur as part of PlainNumber. |
| */ |
| |
| #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) \ |
| REPRESENTATION_BITSET_TYPE_LIST(V) \ |
| INTERNAL_BITSET_TYPE_LIST(V) \ |
| SEMANTIC_BITSET_TYPE_LIST(V) |
| |
| class Type; |
| |
| // ----------------------------------------------------------------------------- |
| // Bitset types (internal). |
| |
| class BitsetType { |
| public: |
| typedef uint32_t bitset; // Internal |
| |
| enum : uint32_t { |
| #define DECLARE_TYPE(type, value) k##type = (value), |
| BITSET_TYPE_LIST(DECLARE_TYPE) |
| #undef DECLARE_TYPE |
| kUnusedEOL = 0 |
| }; |
| |
| static bitset SignedSmall(); |
| static bitset UnsignedSmall(); |
| |
| bitset Bitset() { |
| return static_cast<bitset>(reinterpret_cast<uintptr_t>(this) ^ 1u); |
| } |
| |
| static bool IsInhabited(bitset bits) { |
| return SEMANTIC(bits) != kNone && REPRESENTATION(bits) != kNone; |
| } |
| |
| static bool SemanticIsInhabited(bitset bits) { |
| return SEMANTIC(bits) != kNone; |
| } |
| |
| static bool Is(bitset bits1, bitset bits2) { |
| return (bits1 | bits2) == bits2; |
| } |
| |
| static double Min(bitset); |
| static double Max(bitset); |
| |
| static bitset Glb(Type* type); // greatest lower bound that's a bitset |
| static bitset Glb(double min, double max); |
| static bitset Lub(Type* type); // least upper bound that's a bitset |
| static bitset Lub(i::Map* map); |
| static bitset Lub(i::Object* value); |
| static bitset Lub(double value); |
| static bitset Lub(double min, double max); |
| static bitset ExpandInternals(bitset bits); |
| |
| static const char* Name(bitset); |
| static void Print(std::ostream& os, bitset); // NOLINT |
| #ifdef DEBUG |
| static void Print(bitset); |
| #endif |
| |
| static bitset NumberBits(bitset bits); |
| |
| static bool IsBitset(Type* type) { |
| return reinterpret_cast<uintptr_t>(type) & 1; |
| } |
| |
| static Type* NewForTesting(bitset bits) { return New(bits); } |
| |
| private: |
| friend class Type; |
| |
| static Type* New(bitset bits) { |
| return reinterpret_cast<Type*>(static_cast<uintptr_t>(bits | 1u)); |
| } |
| |
| struct Boundary { |
| bitset internal; |
| bitset external; |
| double min; |
| }; |
| static const Boundary BoundariesArray[]; |
| static inline const Boundary* Boundaries(); |
| static inline size_t BoundariesSize(); |
| }; |
| |
| // ----------------------------------------------------------------------------- |
| // Superclass for non-bitset types (internal). |
| class TypeBase { |
| protected: |
| friend class Type; |
| |
| enum Kind { |
| kClass, |
| kConstant, |
| kContext, |
| kArray, |
| kFunction, |
| kTuple, |
| kUnion, |
| kRange |
| }; |
| |
| Kind kind() const { return kind_; } |
| explicit TypeBase(Kind kind) : kind_(kind) {} |
| |
| static bool IsKind(Type* type, Kind kind) { |
| if (BitsetType::IsBitset(type)) return false; |
| TypeBase* base = reinterpret_cast<TypeBase*>(type); |
| return base->kind() == kind; |
| } |
| |
| // The hacky conversion to/from Type*. |
| static Type* AsType(TypeBase* type) { return reinterpret_cast<Type*>(type); } |
| static TypeBase* FromType(Type* type) { |
| return reinterpret_cast<TypeBase*>(type); |
| } |
| |
| private: |
| Kind kind_; |
| }; |
| |
| // ----------------------------------------------------------------------------- |
| // Class types. |
| |
| class ClassType : public TypeBase { |
| public: |
| i::Handle<i::Map> Map() { return map_; } |
| |
| private: |
| friend class Type; |
| friend class BitsetType; |
| |
| static Type* New(i::Handle<i::Map> map, Zone* zone) { |
| return AsType(new (zone->New(sizeof(ClassType))) |
| ClassType(BitsetType::Lub(*map), map)); |
| } |
| |
| static ClassType* cast(Type* type) { |
| DCHECK(IsKind(type, kClass)); |
| return static_cast<ClassType*>(FromType(type)); |
| } |
| |
| ClassType(BitsetType::bitset bitset, i::Handle<i::Map> map) |
| : TypeBase(kClass), bitset_(bitset), map_(map) {} |
| |
| BitsetType::bitset Lub() { return bitset_; } |
| |
| BitsetType::bitset bitset_; |
| Handle<i::Map> map_; |
| }; |
| |
| // ----------------------------------------------------------------------------- |
| // Constant types. |
| |
| class ConstantType : public TypeBase { |
| public: |
| i::Handle<i::Object> Value() { return object_; } |
| |
| private: |
| friend class Type; |
| friend class BitsetType; |
| |
| static Type* New(i::Handle<i::Object> value, Zone* zone) { |
| BitsetType::bitset bitset = BitsetType::Lub(*value); |
| return AsType(new (zone->New(sizeof(ConstantType))) |
| ConstantType(bitset, value)); |
| } |
| |
| static ConstantType* cast(Type* type) { |
| DCHECK(IsKind(type, kConstant)); |
| return static_cast<ConstantType*>(FromType(type)); |
| } |
| |
| ConstantType(BitsetType::bitset bitset, i::Handle<i::Object> object) |
| : TypeBase(kConstant), bitset_(bitset), object_(object) {} |
| |
| BitsetType::bitset Lub() { return bitset_; } |
| |
| BitsetType::bitset bitset_; |
| Handle<i::Object> object_; |
| }; |
| // TODO(neis): Also cache value if numerical. |
| // TODO(neis): Allow restricting the representation. |
| |
| // ----------------------------------------------------------------------------- |
| // Range types. |
| |
| class RangeType : public TypeBase { |
| public: |
| struct Limits { |
| double min; |
| double max; |
| Limits(double min, double max) : min(min), max(max) {} |
| explicit Limits(RangeType* range) : min(range->Min()), max(range->Max()) {} |
| bool IsEmpty(); |
| static Limits Empty() { return Limits(1, 0); } |
| static Limits Intersect(Limits lhs, Limits rhs); |
| static Limits Union(Limits lhs, Limits rhs); |
| }; |
| |
| double Min() { return limits_.min; } |
| double Max() { return limits_.max; } |
| |
| private: |
| friend class Type; |
| friend class BitsetType; |
| friend class UnionType; |
| |
| static Type* New(double min, double max, BitsetType::bitset representation, |
| Zone* zone) { |
| return New(Limits(min, max), representation, zone); |
| } |
| |
| static bool IsInteger(double x) { |
| return nearbyint(x) == x && !i::IsMinusZero(x); // Allows for infinities. |
| } |
| |
| static Type* New(Limits lim, BitsetType::bitset representation, Zone* zone) { |
| DCHECK(IsInteger(lim.min) && IsInteger(lim.max)); |
| DCHECK(lim.min <= lim.max); |
| DCHECK(REPRESENTATION(representation) == representation); |
| BitsetType::bitset bits = |
| SEMANTIC(BitsetType::Lub(lim.min, lim.max)) | representation; |
| |
| return AsType(new (zone->New(sizeof(RangeType))) RangeType(bits, lim)); |
| } |
| |
| static RangeType* cast(Type* type) { |
| DCHECK(IsKind(type, kRange)); |
| return static_cast<RangeType*>(FromType(type)); |
| } |
| |
| RangeType(BitsetType::bitset bitset, Limits limits) |
| : TypeBase(kRange), bitset_(bitset), limits_(limits) {} |
| |
| BitsetType::bitset Lub() { return bitset_; } |
| |
| BitsetType::bitset bitset_; |
| Limits limits_; |
| }; |
| |
| // ----------------------------------------------------------------------------- |
| // Context types. |
| |
| class ContextType : public TypeBase { |
| public: |
| Type* Outer() { return outer_; } |
| |
| private: |
| friend class Type; |
| |
| static Type* New(Type* outer, Zone* zone) { |
| return AsType(new (zone->New(sizeof(ContextType))) ContextType(outer)); |
| } |
| |
| static ContextType* cast(Type* type) { |
| DCHECK(IsKind(type, kContext)); |
| return static_cast<ContextType*>(FromType(type)); |
| } |
| |
| explicit ContextType(Type* outer) : TypeBase(kContext), outer_(outer) {} |
| |
| Type* outer_; |
| }; |
| |
| // ----------------------------------------------------------------------------- |
| // Array types. |
| |
| class ArrayType : public TypeBase { |
| public: |
| Type* Element() { return element_; } |
| |
| private: |
| friend class Type; |
| |
| explicit ArrayType(Type* element) : TypeBase(kArray), element_(element) {} |
| |
| static Type* New(Type* element, Zone* zone) { |
| return AsType(new (zone->New(sizeof(ArrayType))) ArrayType(element)); |
| } |
| |
| static ArrayType* cast(Type* type) { |
| DCHECK(IsKind(type, kArray)); |
| return static_cast<ArrayType*>(FromType(type)); |
| } |
| |
| Type* element_; |
| }; |
| |
| // ----------------------------------------------------------------------------- |
| // Superclass for types with variable number of type fields. |
| class StructuralType : public TypeBase { |
| public: |
| int LengthForTesting() { return Length(); } |
| |
| protected: |
| friend class Type; |
| |
| int Length() { return length_; } |
| |
| Type* Get(int i) { |
| DCHECK(0 <= i && i < this->Length()); |
| return elements_[i]; |
| } |
| |
| void Set(int i, Type* type) { |
| DCHECK(0 <= i && i < this->Length()); |
| elements_[i] = type; |
| } |
| |
| void Shrink(int length) { |
| DCHECK(2 <= length && length <= this->Length()); |
| length_ = length; |
| } |
| |
| StructuralType(Kind kind, int length, i::Zone* zone) |
| : TypeBase(kind), length_(length) { |
| elements_ = reinterpret_cast<Type**>(zone->New(sizeof(Type*) * length)); |
| } |
| |
| private: |
| int length_; |
| Type** elements_; |
| }; |
| |
| // ----------------------------------------------------------------------------- |
| // Function types. |
| |
| class FunctionType : public StructuralType { |
| public: |
| int Arity() { return this->Length() - 2; } |
| Type* Result() { return this->Get(0); } |
| Type* Receiver() { return this->Get(1); } |
| Type* Parameter(int i) { return this->Get(2 + i); } |
| |
| void InitParameter(int i, Type* type) { this->Set(2 + i, type); } |
| |
| private: |
| friend class Type; |
| |
| FunctionType(Type* result, Type* receiver, int arity, Zone* zone) |
| : StructuralType(kFunction, 2 + arity, zone) { |
| Set(0, result); |
| Set(1, receiver); |
| } |
| |
| static Type* New(Type* result, Type* receiver, int arity, Zone* zone) { |
| return AsType(new (zone->New(sizeof(FunctionType))) |
| FunctionType(result, receiver, arity, zone)); |
| } |
| |
| static FunctionType* cast(Type* type) { |
| DCHECK(IsKind(type, kFunction)); |
| return static_cast<FunctionType*>(FromType(type)); |
| } |
| }; |
| |
| // ----------------------------------------------------------------------------- |
| // Tuple types. |
| |
| class TupleType : public StructuralType { |
| public: |
| int Arity() { return this->Length(); } |
| Type* Element(int i) { return this->Get(i); } |
| |
| void InitElement(int i, Type* type) { this->Set(i, type); } |
| |
| private: |
| friend class Type; |
| |
| TupleType(int length, Zone* zone) : StructuralType(kTuple, length, zone) {} |
| |
| static Type* New(int length, Zone* zone) { |
| return AsType(new (zone->New(sizeof(TupleType))) TupleType(length, zone)); |
| } |
| |
| static TupleType* cast(Type* type) { |
| DCHECK(IsKind(type, kTuple)); |
| return static_cast<TupleType*>(FromType(type)); |
| } |
| }; |
| |
| // ----------------------------------------------------------------------------- |
| // Union types (internal). |
| // A union is a structured type with the following invariants: |
| // - its length is at least 2 |
| // - at most one field is a bitset, and it must go into index 0 |
| // - no field is a union |
| // - no field is a subtype of any other field |
| class UnionType : public StructuralType { |
| private: |
| friend Type; |
| friend BitsetType; |
| |
| UnionType(int length, Zone* zone) : StructuralType(kUnion, length, zone) {} |
| |
| static Type* New(int length, Zone* zone) { |
| return AsType(new (zone->New(sizeof(UnionType))) UnionType(length, zone)); |
| } |
| |
| static UnionType* cast(Type* type) { |
| DCHECK(IsKind(type, kUnion)); |
| return static_cast<UnionType*>(FromType(type)); |
| } |
| |
| bool Wellformed(); |
| }; |
| |
| class Type { |
| public: |
| typedef BitsetType::bitset bitset; // Internal |
| |
| // Constructors. |
| #define DEFINE_TYPE_CONSTRUCTOR(type, value) \ |
| static Type* type() { return BitsetType::New(BitsetType::k##type); } |
| PROPER_BITSET_TYPE_LIST(DEFINE_TYPE_CONSTRUCTOR) |
| #undef DEFINE_TYPE_CONSTRUCTOR |
| |
| static Type* SignedSmall() { |
| return BitsetType::New(BitsetType::SignedSmall()); |
| } |
| static Type* UnsignedSmall() { |
| return BitsetType::New(BitsetType::UnsignedSmall()); |
| } |
| |
| static Type* Class(i::Handle<i::Map> map, Zone* zone) { |
| return ClassType::New(map, zone); |
| } |
| static Type* Constant(i::Handle<i::Object> value, Zone* zone) { |
| return ConstantType::New(value, zone); |
| } |
| static Type* Range(double min, double max, Zone* zone) { |
| return RangeType::New(min, max, REPRESENTATION(BitsetType::kTagged | |
| BitsetType::kUntaggedNumber), |
| zone); |
| } |
| static Type* Context(Type* outer, Zone* zone) { |
| return ContextType::New(outer, zone); |
| } |
| static Type* Array(Type* element, Zone* zone) { |
| return ArrayType::New(element, zone); |
| } |
| static Type* Function(Type* result, Type* receiver, int arity, Zone* zone) { |
| return FunctionType::New(result, receiver, arity, zone); |
| } |
| static Type* Function(Type* result, Zone* zone) { |
| return Function(result, Any(), 0, zone); |
| } |
| static Type* Function(Type* result, Type* param0, Zone* zone) { |
| Type* function = Function(result, Any(), 1, zone); |
| function->AsFunction()->InitParameter(0, param0); |
| return function; |
| } |
| static Type* Function(Type* result, Type* param0, Type* param1, Zone* zone) { |
| Type* function = Function(result, Any(), 2, zone); |
| function->AsFunction()->InitParameter(0, param0); |
| function->AsFunction()->InitParameter(1, param1); |
| return function; |
| } |
| static Type* Function(Type* result, Type* param0, Type* param1, Type* param2, |
| Zone* zone) { |
| Type* function = Function(result, Any(), 3, zone); |
| function->AsFunction()->InitParameter(0, param0); |
| function->AsFunction()->InitParameter(1, param1); |
| function->AsFunction()->InitParameter(2, param2); |
| return function; |
| } |
| static Type* Function(Type* result, int arity, Type** params, Zone* zone) { |
| Type* function = Function(result, Any(), arity, zone); |
| for (int i = 0; i < arity; ++i) { |
| function->AsFunction()->InitParameter(i, params[i]); |
| } |
| return function; |
| } |
| static Type* Tuple(Type* first, Type* second, Type* third, Zone* zone) { |
| Type* tuple = TupleType::New(3, zone); |
| tuple->AsTuple()->InitElement(0, first); |
| tuple->AsTuple()->InitElement(1, second); |
| tuple->AsTuple()->InitElement(2, third); |
| return tuple; |
| } |
| |
| #define CONSTRUCT_SIMD_TYPE(NAME, Name, name, lane_count, lane_type) \ |
| static Type* Name(Isolate* isolate, Zone* zone); |
| SIMD128_TYPES(CONSTRUCT_SIMD_TYPE) |
| #undef CONSTRUCT_SIMD_TYPE |
| |
| static Type* Union(Type* type1, Type* type2, Zone* reg); |
| static Type* Intersect(Type* type1, Type* type2, Zone* reg); |
| |
| static Type* Of(double value, Zone* zone) { |
| return BitsetType::New(BitsetType::ExpandInternals(BitsetType::Lub(value))); |
| } |
| static Type* Of(i::Object* value, Zone* zone) { |
| return BitsetType::New(BitsetType::ExpandInternals(BitsetType::Lub(value))); |
| } |
| static Type* Of(i::Handle<i::Object> value, Zone* zone) { |
| return Of(*value, zone); |
| } |
| |
| // Extraction of components. |
| static Type* Representation(Type* t, Zone* zone); |
| static Type* Semantic(Type* t, Zone* zone); |
| |
| // Predicates. |
| bool IsInhabited() { return BitsetType::IsInhabited(this->BitsetLub()); } |
| |
| bool Is(Type* that) { return this == that || this->SlowIs(that); } |
| bool Maybe(Type* that); |
| bool Equals(Type* that) { return this->Is(that) && that->Is(this); } |
| |
| // 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); } |
| |
| // State-dependent versions of the above that consider subtyping between |
| // a constant and its map class. |
| static Type* NowOf(i::Object* value, Zone* zone); |
| static Type* NowOf(i::Handle<i::Object> value, Zone* zone) { |
| return NowOf(*value, zone); |
| } |
| bool NowIs(Type* that); |
| bool NowContains(i::Object* val); |
| bool NowContains(i::Handle<i::Object> val) { return this->NowContains(*val); } |
| |
| bool NowStable(); |
| |
| // Inspection. |
| bool IsRange() { return IsKind(TypeBase::kRange); } |
| bool IsClass() { return IsKind(TypeBase::kClass); } |
| bool IsConstant() { return IsKind(TypeBase::kConstant); } |
| bool IsContext() { return IsKind(TypeBase::kContext); } |
| bool IsArray() { return IsKind(TypeBase::kArray); } |
| bool IsFunction() { return IsKind(TypeBase::kFunction); } |
| bool IsTuple() { return IsKind(TypeBase::kTuple); } |
| |
| ClassType* AsClass() { return ClassType::cast(this); } |
| ConstantType* AsConstant() { return ConstantType::cast(this); } |
| RangeType* AsRange() { return RangeType::cast(this); } |
| ContextType* AsContext() { return ContextType::cast(this); } |
| ArrayType* AsArray() { return ArrayType::cast(this); } |
| FunctionType* AsFunction() { return FunctionType::cast(this); } |
| TupleType* AsTuple() { return TupleType::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(); |
| |
| // Extracts a range from the type: if the type is a range or a union |
| // containing a range, that range is returned; otherwise, NULL is returned. |
| Type* GetRange(); |
| |
| static bool IsInteger(i::Object* x); |
| static bool IsInteger(double x) { |
| return nearbyint(x) == x && !i::IsMinusZero(x); // Allows for infinities. |
| } |
| |
| int NumClasses(); |
| int NumConstants(); |
| |
| template <class T> |
| class Iterator { |
| public: |
| bool Done() const { return index_ < 0; } |
| i::Handle<T> Current(); |
| void Advance(); |
| |
| private: |
| friend class Type; |
| |
| Iterator() : index_(-1) {} |
| explicit Iterator(Type* type) : type_(type), index_(-1) { Advance(); } |
| |
| inline bool matches(Type* type); |
| inline Type* get_type(); |
| |
| Type* type_; |
| int index_; |
| }; |
| |
| Iterator<i::Map> Classes() { |
| if (this->IsBitset()) return Iterator<i::Map>(); |
| return Iterator<i::Map>(this); |
| } |
| Iterator<i::Object> Constants() { |
| if (this->IsBitset()) return Iterator<i::Object>(); |
| return Iterator<i::Object>(this); |
| } |
| |
| // Printing. |
| |
| enum PrintDimension { BOTH_DIMS, SEMANTIC_DIM, REPRESENTATION_DIM }; |
| |
| void PrintTo(std::ostream& os, PrintDimension dim = BOTH_DIMS); // NOLINT |
| |
| #ifdef DEBUG |
| void Print(); |
| #endif |
| |
| // Helpers for testing. |
| bool IsBitsetForTesting() { return IsBitset(); } |
| bool IsUnionForTesting() { return IsUnion(); } |
| bitset AsBitsetForTesting() { return AsBitset(); } |
| UnionType* AsUnionForTesting() { return AsUnion(); } |
| |
| private: |
| // Friends. |
| template <class> |
| friend class Iterator; |
| friend BitsetType; |
| friend UnionType; |
| |
| // Internal inspection. |
| bool IsKind(TypeBase::Kind kind) { return TypeBase::IsKind(this, kind); } |
| |
| bool IsNone() { return this == None(); } |
| bool IsAny() { return this == Any(); } |
| bool IsBitset() { return BitsetType::IsBitset(this); } |
| bool IsUnion() { return IsKind(TypeBase::kUnion); } |
| |
| bitset AsBitset() { |
| DCHECK(this->IsBitset()); |
| return reinterpret_cast<BitsetType*>(this)->Bitset(); |
| } |
| UnionType* AsUnion() { return UnionType::cast(this); } |
| |
| bitset Representation(); |
| |
| // Auxiliary functions. |
| bool SemanticMaybe(Type* that); |
| |
| bitset BitsetGlb() { return BitsetType::Glb(this); } |
| bitset BitsetLub() { return BitsetType::Lub(this); } |
| |
| bool SlowIs(Type* that); |
| bool SemanticIs(Type* that); |
| |
| static bool Overlap(RangeType* lhs, RangeType* rhs); |
| static bool Contains(RangeType* lhs, RangeType* rhs); |
| static bool Contains(RangeType* range, ConstantType* constant); |
| static bool Contains(RangeType* range, i::Object* val); |
| |
| static int UpdateRange(Type* type, UnionType* result, int size, Zone* zone); |
| |
| static RangeType::Limits IntersectRangeAndBitset(Type* range, Type* bits, |
| Zone* zone); |
| static RangeType::Limits ToLimits(bitset bits, Zone* zone); |
| |
| bool SimplyEquals(Type* that); |
| |
| static int AddToUnion(Type* type, UnionType* result, int size, Zone* zone); |
| static int IntersectAux(Type* type, Type* other, UnionType* result, int size, |
| RangeType::Limits* limits, Zone* zone); |
| static Type* NormalizeUnion(Type* unioned, int size, Zone* zone); |
| static Type* NormalizeRangeAndBitset(Type* range, bitset* bits, Zone* zone); |
| }; |
| |
| // ----------------------------------------------------------------------------- |
| // Type bounds. A simple struct to represent a pair of lower/upper types. |
| |
| struct Bounds { |
| Type* lower; |
| Type* upper; |
| |
| Bounds() |
| : // Make sure accessing uninitialized bounds crashes big-time. |
| lower(nullptr), |
| upper(nullptr) {} |
| explicit Bounds(Type* t) : lower(t), upper(t) {} |
| Bounds(Type* l, Type* u) : lower(l), upper(u) { DCHECK(lower->Is(upper)); } |
| |
| // Unrestricted bounds. |
| static Bounds Unbounded() { return Bounds(Type::None(), Type::Any()); } |
| |
| // Meet: both b1 and b2 are known to hold. |
| static Bounds Both(Bounds b1, Bounds b2, Zone* zone) { |
| Type* lower = Type::Union(b1.lower, b2.lower, zone); |
| Type* upper = Type::Intersect(b1.upper, b2.upper, zone); |
| // Lower bounds are considered approximate, correct as necessary. |
| if (!lower->Is(upper)) lower = upper; |
| return Bounds(lower, upper); |
| } |
| |
| // Join: either b1 or b2 is known to hold. |
| static Bounds Either(Bounds b1, Bounds b2, Zone* zone) { |
| Type* lower = Type::Intersect(b1.lower, b2.lower, zone); |
| Type* upper = Type::Union(b1.upper, b2.upper, zone); |
| return Bounds(lower, upper); |
| } |
| |
| static Bounds NarrowLower(Bounds b, Type* t, Zone* zone) { |
| Type* lower = Type::Union(b.lower, t, zone); |
| // Lower bounds are considered approximate, correct as necessary. |
| if (!lower->Is(b.upper)) lower = b.upper; |
| return Bounds(lower, b.upper); |
| } |
| static Bounds NarrowUpper(Bounds b, Type* t, Zone* zone) { |
| Type* lower = b.lower; |
| Type* upper = Type::Intersect(b.upper, t, zone); |
| // Lower bounds are considered approximate, correct as necessary. |
| if (!lower->Is(upper)) lower = upper; |
| return Bounds(lower, upper); |
| } |
| |
| bool Narrows(Bounds that) { |
| return that.lower->Is(this->lower) && this->upper->Is(that.upper); |
| } |
| }; |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_TYPES_H_ |