1e2770123b
Avoid write barriers when storing values in the root set, and use cheaper write barriers for storing maps or tagged pointers. Also improve the generated code for write barriers, utilizing the out of line code mechanism that is available to TurboFan backends, which moves the unlikely case out of the hot path. R=jarin@chromium.org, mstarzinger@chromium.org Review URL: https://codereview.chromium.org/1414183006 Cr-Commit-Position: refs/heads/master@{#31914}
1186 lines
42 KiB
C++
1186 lines
42 KiB
C++
// 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
|
|
// Undetectable < Object
|
|
// Detectable = Receiver \/ Number \/ Name - Undetectable
|
|
//
|
|
// 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.
|
|
//
|
|
// There are two type representations, using different allocation:
|
|
//
|
|
// - class Type (zone-allocated, for compiler and concurrent compilation)
|
|
// - class HeapType (heap-allocated, for persistent types)
|
|
//
|
|
// Both provide the same API, and the Convert method can be used to interconvert
|
|
// them. For zone types, no query method touches the heap, only constructors do.
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Values for bitset types
|
|
|
|
// clang-format off
|
|
|
|
#define MASK_BITSET_TYPE_LIST(V) \
|
|
V(Representation, 0xff800000u) \
|
|
V(Semantic, 0x007ffffeu)
|
|
|
|
#define REPRESENTATION(k) ((k) & BitsetType::kRepresentation)
|
|
#define SEMANTIC(k) ((k) & BitsetType::kSemantic)
|
|
|
|
#define REPRESENTATION_BITSET_TYPE_LIST(V) \
|
|
V(None, 0) \
|
|
V(UntaggedBit, 1u << 23 | kSemantic) \
|
|
V(UntaggedIntegral8, 1u << 24 | kSemantic) \
|
|
V(UntaggedIntegral16, 1u << 25 | kSemantic) \
|
|
V(UntaggedIntegral32, 1u << 26 | kSemantic) \
|
|
V(UntaggedFloat32, 1u << 27 | kSemantic) \
|
|
V(UntaggedFloat64, 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(Undetectable, 1u << 16 | REPRESENTATION(kTaggedPointer)) \
|
|
V(OtherObject, 1u << 17 | 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(NumberOrString, kNumber | kString) \
|
|
V(NumberOrUndefined, kNumber | kUndefined) \
|
|
V(PlainPrimitive, kNumberOrString | kBoolean | kNullOrUndefined) \
|
|
V(Primitive, kSymbol | kSimd | kPlainPrimitive) \
|
|
V(DetectableReceiver, kFunction | kOtherObject | kProxy) \
|
|
V(Detectable, kDetectableReceiver | kNumber | kName) \
|
|
V(Object, kFunction | kOtherObject | kUndetectable) \
|
|
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)
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// The abstract Type class, parameterized over the low-level representation.
|
|
|
|
// struct Config {
|
|
// typedef TypeImpl<Config> Type;
|
|
// typedef Base;
|
|
// typedef Struct;
|
|
// typedef Range;
|
|
// typedef Region;
|
|
// template<class> struct Handle { typedef type; } // No template typedefs...
|
|
//
|
|
// template<class T> static Handle<T>::type null_handle();
|
|
// template<class T> static Handle<T>::type handle(T* t); // !is_bitset(t)
|
|
// template<class T> static Handle<T>::type cast(Handle<Type>::type);
|
|
//
|
|
// static bool is_bitset(Type*);
|
|
// static bool is_class(Type*);
|
|
// static bool is_struct(Type*, int tag);
|
|
// static bool is_range(Type*);
|
|
//
|
|
// static bitset as_bitset(Type*);
|
|
// static i::Handle<i::Map> as_class(Type*);
|
|
// static Handle<Struct>::type as_struct(Type*);
|
|
// static Handle<Range>::type as_range(Type*);
|
|
//
|
|
// static Type* from_bitset(bitset);
|
|
// static Handle<Type>::type from_bitset(bitset, Region*);
|
|
// static Handle<Type>::type from_class(i::Handle<Map>, Region*);
|
|
// static Handle<Type>::type from_struct(Handle<Struct>::type, int tag);
|
|
// static Handle<Type>::type from_range(Handle<Range>::type);
|
|
//
|
|
// static Handle<Struct>::type struct_create(int tag, int length, Region*);
|
|
// static void struct_shrink(Handle<Struct>::type, int length);
|
|
// static int struct_tag(Handle<Struct>::type);
|
|
// static int struct_length(Handle<Struct>::type);
|
|
// static Handle<Type>::type struct_get(Handle<Struct>::type, int);
|
|
// static void struct_set(Handle<Struct>::type, int, Handle<Type>::type);
|
|
// template<class V>
|
|
// static i::Handle<V> struct_get_value(Handle<Struct>::type, int);
|
|
// template<class V>
|
|
// static void struct_set_value(Handle<Struct>::type, int, i::Handle<V>);
|
|
//
|
|
// static Handle<Range>::type range_create(Region*);
|
|
// static int range_get_bitset(Handle<Range>::type);
|
|
// static void range_set_bitset(Handle<Range>::type, int);
|
|
// static double range_get_double(Handle<Range>::type, int);
|
|
// static void range_set_double(Handle<Range>::type, int, double, Region*);
|
|
// }
|
|
template<class Config>
|
|
class TypeImpl : public Config::Base {
|
|
public:
|
|
// Auxiliary types.
|
|
|
|
typedef uint32_t bitset; // Internal
|
|
class BitsetType; // Internal
|
|
class StructuralType; // Internal
|
|
class UnionType; // Internal
|
|
|
|
class ClassType;
|
|
class ConstantType;
|
|
class RangeType;
|
|
class ContextType;
|
|
class ArrayType;
|
|
class FunctionType;
|
|
|
|
typedef typename Config::template Handle<TypeImpl>::type TypeHandle;
|
|
typedef typename Config::template Handle<ClassType>::type ClassHandle;
|
|
typedef typename Config::template Handle<ConstantType>::type ConstantHandle;
|
|
typedef typename Config::template Handle<RangeType>::type RangeHandle;
|
|
typedef typename Config::template Handle<ContextType>::type ContextHandle;
|
|
typedef typename Config::template Handle<ArrayType>::type ArrayHandle;
|
|
typedef typename Config::template Handle<FunctionType>::type FunctionHandle;
|
|
typedef typename Config::template Handle<UnionType>::type UnionHandle;
|
|
typedef typename Config::Region Region;
|
|
|
|
// Constructors.
|
|
|
|
#define DEFINE_TYPE_CONSTRUCTOR(type, value) \
|
|
static TypeImpl* type() { \
|
|
return BitsetType::New(BitsetType::k##type); \
|
|
} \
|
|
static TypeHandle type(Region* region) { \
|
|
return BitsetType::New(BitsetType::k##type, region); \
|
|
}
|
|
PROPER_BITSET_TYPE_LIST(DEFINE_TYPE_CONSTRUCTOR)
|
|
#undef DEFINE_TYPE_CONSTRUCTOR
|
|
|
|
static TypeImpl* SignedSmall() {
|
|
return BitsetType::New(BitsetType::SignedSmall());
|
|
}
|
|
static TypeHandle SignedSmall(Region* region) {
|
|
return BitsetType::New(BitsetType::SignedSmall(), region);
|
|
}
|
|
static TypeImpl* UnsignedSmall() {
|
|
return BitsetType::New(BitsetType::UnsignedSmall());
|
|
}
|
|
static TypeHandle UnsignedSmall(Region* region) {
|
|
return BitsetType::New(BitsetType::UnsignedSmall(), region);
|
|
}
|
|
|
|
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) {
|
|
return ConstantType::New(value, region);
|
|
}
|
|
static TypeHandle Range(double min, double max, Region* region) {
|
|
return RangeType::New(
|
|
min, max, BitsetType::New(REPRESENTATION(BitsetType::kTagged |
|
|
BitsetType::kUntaggedNumber),
|
|
region),
|
|
region);
|
|
}
|
|
static TypeHandle Context(TypeHandle outer, Region* region) {
|
|
return ContextType::New(outer, region);
|
|
}
|
|
static TypeHandle Array(TypeHandle element, Region* region) {
|
|
return ArrayType::New(element, region);
|
|
}
|
|
static FunctionHandle Function(
|
|
TypeHandle result, TypeHandle receiver, int arity, Region* region) {
|
|
return FunctionType::New(result, receiver, arity, region);
|
|
}
|
|
static TypeHandle Function(TypeHandle result, Region* region) {
|
|
return Function(result, Any(region), 0, region);
|
|
}
|
|
static TypeHandle Function(
|
|
TypeHandle result, TypeHandle param0, Region* region) {
|
|
FunctionHandle function = Function(result, Any(region), 1, region);
|
|
function->InitParameter(0, param0);
|
|
return function;
|
|
}
|
|
static TypeHandle Function(
|
|
TypeHandle result, TypeHandle param0, TypeHandle param1, Region* region) {
|
|
FunctionHandle function = Function(result, Any(region), 2, region);
|
|
function->InitParameter(0, param0);
|
|
function->InitParameter(1, param1);
|
|
return function;
|
|
}
|
|
static TypeHandle Function(
|
|
TypeHandle result, TypeHandle param0, TypeHandle param1,
|
|
TypeHandle param2, Region* region) {
|
|
FunctionHandle function = Function(result, Any(region), 3, region);
|
|
function->InitParameter(0, param0);
|
|
function->InitParameter(1, param1);
|
|
function->InitParameter(2, param2);
|
|
return function;
|
|
}
|
|
static TypeHandle Function(TypeHandle result, int arity, TypeHandle* params,
|
|
Region* region) {
|
|
FunctionHandle function = Function(result, Any(region), arity, region);
|
|
for (int i = 0; i < arity; ++i) {
|
|
function->InitParameter(i, params[i]);
|
|
}
|
|
return function;
|
|
}
|
|
|
|
#define CONSTRUCT_SIMD_TYPE(NAME, Name, name, lane_count, lane_type) \
|
|
static TypeHandle Name(Isolate* isolate, Region* region);
|
|
SIMD128_TYPES(CONSTRUCT_SIMD_TYPE)
|
|
#undef CONSTRUCT_SIMD_TYPE
|
|
|
|
static TypeHandle Union(TypeHandle type1, TypeHandle type2, Region* reg);
|
|
static TypeHandle Intersect(TypeHandle type1, TypeHandle type2, Region* reg);
|
|
|
|
static TypeHandle Of(double value, Region* region) {
|
|
return Config::from_bitset(BitsetType::ExpandInternals(
|
|
BitsetType::Lub(value)), region);
|
|
}
|
|
static TypeHandle Of(i::Object* value, Region* region) {
|
|
return Config::from_bitset(BitsetType::ExpandInternals(
|
|
BitsetType::Lub(value)), region);
|
|
}
|
|
static TypeHandle Of(i::Handle<i::Object> value, Region* region) {
|
|
return Of(*value, region);
|
|
}
|
|
|
|
// 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); }
|
|
template<class TypeHandle>
|
|
bool Is(TypeHandle that) { return this->Is(*that); }
|
|
|
|
bool Maybe(TypeImpl* that);
|
|
template<class TypeHandle>
|
|
bool Maybe(TypeHandle that) { return this->Maybe(*that); }
|
|
|
|
bool Equals(TypeImpl* that) { return this->Is(that) && that->Is(this); }
|
|
template<class TypeHandle>
|
|
bool Equals(TypeHandle that) { return this->Equals(*that); }
|
|
|
|
// 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.
|
|
inline static TypeHandle NowOf(i::Object* value, Region* region);
|
|
static TypeHandle NowOf(i::Handle<i::Object> value, Region* region) {
|
|
return NowOf(*value, region);
|
|
}
|
|
bool NowIs(TypeImpl* that);
|
|
template<class TypeHandle>
|
|
bool NowIs(TypeHandle that) { return this->NowIs(*that); }
|
|
inline bool NowContains(i::Object* val);
|
|
bool NowContains(i::Handle<i::Object> val) { return this->NowContains(*val); }
|
|
|
|
bool NowStable();
|
|
|
|
// Inspection.
|
|
|
|
bool IsRange() { return Config::is_range(this); }
|
|
bool IsClass() {
|
|
return Config::is_class(this)
|
|
|| Config::is_struct(this, StructuralType::kClassTag);
|
|
}
|
|
bool IsConstant() {
|
|
return Config::is_struct(this, StructuralType::kConstantTag);
|
|
}
|
|
bool IsContext() {
|
|
return Config::is_struct(this, StructuralType::kContextTag);
|
|
}
|
|
bool IsArray() {
|
|
return Config::is_struct(this, StructuralType::kArrayTag);
|
|
}
|
|
bool IsFunction() {
|
|
return Config::is_struct(this, StructuralType::kFunctionTag);
|
|
}
|
|
|
|
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); }
|
|
|
|
// 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.
|
|
RangeType* GetRange();
|
|
|
|
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());
|
|
}
|
|
|
|
int NumClasses();
|
|
int NumConstants();
|
|
|
|
template<class T> class Iterator;
|
|
Iterator<i::Map> Classes() {
|
|
if (this->IsBitset()) return Iterator<i::Map>();
|
|
return Iterator<i::Map>(Config::handle(this));
|
|
}
|
|
Iterator<i::Object> Constants() {
|
|
if (this->IsBitset()) return Iterator<i::Object>();
|
|
return Iterator<i::Object>(Config::handle(this));
|
|
}
|
|
|
|
// Casting and conversion.
|
|
|
|
static inline TypeImpl* cast(typename Config::Base* object);
|
|
|
|
template<class OtherTypeImpl>
|
|
static TypeHandle Convert(
|
|
typename OtherTypeImpl::TypeHandle type, Region* region);
|
|
|
|
// Printing.
|
|
|
|
enum PrintDimension { BOTH_DIMS, SEMANTIC_DIM, REPRESENTATION_DIM };
|
|
|
|
void PrintTo(std::ostream& os, PrintDimension dim = BOTH_DIMS); // NOLINT
|
|
|
|
#ifdef DEBUG
|
|
void Print();
|
|
#endif
|
|
|
|
bool IsUnionForTesting() { return IsUnion(); }
|
|
|
|
protected:
|
|
// Friends.
|
|
|
|
template<class> friend class Iterator;
|
|
template<class> friend class TypeImpl;
|
|
|
|
// Handle conversion.
|
|
|
|
template<class T>
|
|
static typename Config::template Handle<T>::type handle(T* type) {
|
|
return Config::handle(type);
|
|
}
|
|
TypeImpl* unhandle() { return this; }
|
|
|
|
// Internal inspection.
|
|
|
|
bool IsNone() { return this == None(); }
|
|
bool IsAny() { return this == Any(); }
|
|
bool IsBitset() { return Config::is_bitset(this); }
|
|
bool IsUnion() { return Config::is_struct(this, StructuralType::kUnionTag); }
|
|
|
|
bitset AsBitset() {
|
|
DCHECK(this->IsBitset());
|
|
return static_cast<BitsetType*>(this)->Bitset();
|
|
}
|
|
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);
|
|
|
|
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);
|
|
};
|
|
|
|
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(
|
|
RangeHandle type, UnionHandle result, int size, Region* region);
|
|
|
|
static Limits IntersectRangeAndBitset(TypeHandle range, TypeHandle bits,
|
|
Region* region);
|
|
static Limits ToLimits(bitset bits, 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, Limits* limits, Region* region);
|
|
static TypeHandle NormalizeUnion(UnionHandle unioned, int size,
|
|
Region* region);
|
|
static TypeHandle NormalizeRangeAndBitset(RangeHandle range, bitset* bits,
|
|
Region* region);
|
|
};
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Bitset types (internal).
|
|
|
|
template<class Config>
|
|
class TypeImpl<Config>::BitsetType : public TypeImpl<Config> {
|
|
protected:
|
|
friend class TypeImpl<Config>;
|
|
|
|
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 Config::as_bitset(this); }
|
|
|
|
static TypeImpl* New(bitset bits) {
|
|
return Config::from_bitset(bits);
|
|
}
|
|
static TypeHandle New(bitset bits, Region* region) {
|
|
return Config::from_bitset(bits, region);
|
|
}
|
|
|
|
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(TypeImpl* type); // greatest lower bound that's a bitset
|
|
static bitset Glb(double min, double max);
|
|
static bitset Lub(TypeImpl* 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);
|
|
|
|
private:
|
|
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).
|
|
// Contains a tag and a variable number of type or value fields.
|
|
|
|
template<class Config>
|
|
class TypeImpl<Config>::StructuralType : public TypeImpl<Config> {
|
|
protected:
|
|
template<class> friend class TypeImpl;
|
|
friend struct ZoneTypeConfig; // For tags.
|
|
friend struct HeapTypeConfig;
|
|
|
|
enum Tag {
|
|
kClassTag,
|
|
kConstantTag,
|
|
kContextTag,
|
|
kArrayTag,
|
|
kFunctionTag,
|
|
kUnionTag
|
|
};
|
|
|
|
int Length() {
|
|
return Config::struct_length(Config::as_struct(this));
|
|
}
|
|
TypeHandle Get(int i) {
|
|
DCHECK(0 <= i && i < this->Length());
|
|
return Config::struct_get(Config::as_struct(this), i);
|
|
}
|
|
void Set(int i, TypeHandle type) {
|
|
DCHECK(0 <= i && i < this->Length());
|
|
Config::struct_set(Config::as_struct(this), i, type);
|
|
}
|
|
void Shrink(int length) {
|
|
DCHECK(2 <= length && length <= this->Length());
|
|
Config::struct_shrink(Config::as_struct(this), length);
|
|
}
|
|
template<class V> i::Handle<V> GetValue(int i) {
|
|
DCHECK(0 <= i && i < this->Length());
|
|
return Config::template struct_get_value<V>(Config::as_struct(this), i);
|
|
}
|
|
template<class V> void SetValue(int i, i::Handle<V> x) {
|
|
DCHECK(0 <= i && i < this->Length());
|
|
Config::struct_set_value(Config::as_struct(this), i, x);
|
|
}
|
|
|
|
static TypeHandle New(Tag tag, int length, Region* region) {
|
|
DCHECK(1 <= length);
|
|
return Config::from_struct(Config::struct_create(tag, length, region));
|
|
}
|
|
};
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// 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
|
|
template<class Config>
|
|
class TypeImpl<Config>::UnionType : public StructuralType {
|
|
public:
|
|
static UnionHandle New(int length, Region* region) {
|
|
return Config::template cast<UnionType>(
|
|
StructuralType::New(StructuralType::kUnionTag, length, region));
|
|
}
|
|
|
|
static UnionType* cast(TypeImpl* type) {
|
|
DCHECK(type->IsUnion());
|
|
return static_cast<UnionType*>(type);
|
|
}
|
|
|
|
bool Wellformed();
|
|
};
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Class types.
|
|
|
|
template<class Config>
|
|
class TypeImpl<Config>::ClassType : public StructuralType {
|
|
public:
|
|
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, Region* region) {
|
|
ClassHandle type =
|
|
Config::template cast<ClassType>(Config::from_class(map, 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) {
|
|
DCHECK(type->IsClass());
|
|
return static_cast<ClassType*>(type);
|
|
}
|
|
|
|
private:
|
|
template<class> friend class TypeImpl;
|
|
bitset Lub() {
|
|
return Config::is_class(this) ?
|
|
BitsetType::Lub(*Config::as_class(this)) :
|
|
this->Get(0)->AsBitset();
|
|
}
|
|
};
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Constant types.
|
|
|
|
template<class Config>
|
|
class TypeImpl<Config>::ConstantType : public StructuralType {
|
|
public:
|
|
i::Handle<i::Object> Value() { return this->template GetValue<i::Object>(1); }
|
|
|
|
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, BitsetType::New(BitsetType::Lub(*value), region));
|
|
type->SetValue(1, value);
|
|
return type;
|
|
}
|
|
|
|
static ConstantType* cast(TypeImpl* type) {
|
|
DCHECK(type->IsConstant());
|
|
return static_cast<ConstantType*>(type);
|
|
}
|
|
|
|
private:
|
|
template<class> friend class TypeImpl;
|
|
bitset Lub() { return this->Get(0)->AsBitset(); }
|
|
};
|
|
// TODO(neis): Also cache value if numerical.
|
|
// TODO(neis): Allow restricting the representation.
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Range types.
|
|
|
|
template <class Config>
|
|
class TypeImpl<Config>::RangeType : public TypeImpl<Config> {
|
|
public:
|
|
double Min() { return Config::range_get_double(Config::as_range(this), 0); }
|
|
double Max() { return Config::range_get_double(Config::as_range(this), 1); }
|
|
|
|
static RangeHandle New(double min, double max, TypeHandle representation,
|
|
Region* region) {
|
|
DCHECK(IsInteger(min) && IsInteger(max));
|
|
DCHECK(min <= max);
|
|
bitset representation_bits = representation->AsBitset();
|
|
DCHECK(REPRESENTATION(representation_bits) == representation_bits);
|
|
|
|
typename Config::template Handle<typename Config::Range>::type range =
|
|
Config::range_create(region);
|
|
|
|
bitset bits = SEMANTIC(BitsetType::Lub(min, max)) | representation_bits;
|
|
Config::range_set_bitset(range, bits);
|
|
Config::range_set_double(range, 0, min, region);
|
|
Config::range_set_double(range, 1, max, region);
|
|
return Config::template cast<RangeType>(Config::from_range(range));
|
|
}
|
|
|
|
static RangeHandle New(Limits lim, bitset representation, Region* region) {
|
|
return New(lim.min, lim.max, BitsetType::New(representation, region),
|
|
region);
|
|
}
|
|
|
|
static RangeType* cast(TypeImpl* type) {
|
|
DCHECK(type->IsRange());
|
|
return static_cast<RangeType*>(type);
|
|
}
|
|
|
|
private:
|
|
template<class> friend class TypeImpl;
|
|
bitset Lub() {
|
|
return Config::range_get_bitset(Config::as_range(this));
|
|
}
|
|
};
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Context types.
|
|
|
|
template<class Config>
|
|
class TypeImpl<Config>::ContextType : public StructuralType {
|
|
public:
|
|
TypeHandle Outer() { return this->Get(0); }
|
|
|
|
static ContextHandle New(TypeHandle outer, Region* region) {
|
|
ContextHandle type = Config::template cast<ContextType>(
|
|
StructuralType::New(StructuralType::kContextTag, 1, region));
|
|
type->Set(0, outer);
|
|
return type;
|
|
}
|
|
|
|
static ContextType* cast(TypeImpl* type) {
|
|
DCHECK(type->IsContext());
|
|
return static_cast<ContextType*>(type);
|
|
}
|
|
};
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Array types.
|
|
|
|
template<class Config>
|
|
class TypeImpl<Config>::ArrayType : public StructuralType {
|
|
public:
|
|
TypeHandle Element() { return this->Get(0); }
|
|
|
|
static ArrayHandle New(TypeHandle element, Region* region) {
|
|
ArrayHandle type = Config::template cast<ArrayType>(
|
|
StructuralType::New(StructuralType::kArrayTag, 1, region));
|
|
type->Set(0, element);
|
|
return type;
|
|
}
|
|
|
|
static ArrayType* cast(TypeImpl* type) {
|
|
DCHECK(type->IsArray());
|
|
return static_cast<ArrayType*>(type);
|
|
}
|
|
};
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Function types.
|
|
|
|
template<class Config>
|
|
class TypeImpl<Config>::FunctionType : public StructuralType {
|
|
public:
|
|
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(2 + i, type); }
|
|
|
|
static FunctionHandle New(
|
|
TypeHandle result, TypeHandle receiver, int arity, Region* 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) {
|
|
DCHECK(type->IsFunction());
|
|
return static_cast<FunctionType*>(type);
|
|
}
|
|
};
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Type iterators.
|
|
|
|
template<class Config> template<class T>
|
|
class TypeImpl<Config>::Iterator {
|
|
public:
|
|
bool Done() const { return index_ < 0; }
|
|
i::Handle<T> Current();
|
|
void Advance();
|
|
|
|
private:
|
|
template<class> friend class TypeImpl;
|
|
|
|
Iterator() : index_(-1) {}
|
|
explicit Iterator(TypeHandle type) : type_(type), index_(-1) {
|
|
Advance();
|
|
}
|
|
|
|
inline bool matches(TypeHandle type);
|
|
inline TypeHandle get_type();
|
|
|
|
TypeHandle type_;
|
|
int index_;
|
|
};
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Zone-allocated types; they are either (odd) integers to represent bitsets, or
|
|
// (even) pointers to structures for everything else.
|
|
|
|
struct ZoneTypeConfig {
|
|
typedef TypeImpl<ZoneTypeConfig> Type;
|
|
class Base {};
|
|
typedef void* Struct;
|
|
// Hack: the Struct and Range types can be aliased in memory, the first
|
|
// pointer word of each both must be the tag (kRangeStructTag for Range,
|
|
// anything else for Struct) so that we can differentiate them.
|
|
struct Range {
|
|
void* tag;
|
|
int bitset;
|
|
double limits[2];
|
|
};
|
|
typedef i::Zone Region;
|
|
template<class T> struct Handle { typedef T* type; };
|
|
|
|
static const int kRangeStructTag = 0x1000;
|
|
|
|
template<class T> static inline T* null_handle() { return nullptr; }
|
|
template<class T> static inline T* handle(T* type);
|
|
template<class T> static inline T* cast(Type* type);
|
|
|
|
static inline bool is_bitset(Type* type);
|
|
static inline bool is_class(Type* type);
|
|
static inline bool is_struct(Type* type, int tag);
|
|
static inline bool is_range(Type* type);
|
|
|
|
static inline Type::bitset as_bitset(Type* type);
|
|
static inline i::Handle<i::Map> as_class(Type* type);
|
|
static inline Struct* as_struct(Type* type);
|
|
static inline Range* as_range(Type* type);
|
|
|
|
static inline Type* from_bitset(Type::bitset);
|
|
static inline Type* from_bitset(Type::bitset, Zone* zone);
|
|
static inline Type* from_class(i::Handle<i::Map> map, Zone* zone);
|
|
static inline Type* from_struct(Struct* structured);
|
|
static inline Type* from_range(Range* range);
|
|
|
|
static inline Struct* struct_create(int tag, int length, Zone* zone);
|
|
static inline void struct_shrink(Struct* structure, int length);
|
|
static inline int struct_tag(Struct* structure);
|
|
static inline int struct_length(Struct* structure);
|
|
static inline Type* struct_get(Struct* structure, int i);
|
|
static inline void struct_set(Struct* structure, int i, Type* type);
|
|
template<class V>
|
|
static inline i::Handle<V> struct_get_value(Struct* structure, int i);
|
|
template<class V> static inline void struct_set_value(
|
|
Struct* structure, int i, i::Handle<V> x);
|
|
|
|
static inline Range* range_create(Zone* zone);
|
|
static inline int range_get_bitset(Range* range);
|
|
static inline void range_set_bitset(Range* range, int);
|
|
static inline double range_get_double(Range*, int index);
|
|
static inline void range_set_double(Range*, int index, double value, Zone*);
|
|
};
|
|
|
|
typedef TypeImpl<ZoneTypeConfig> Type;
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Heap-allocated types; either smis for bitsets, maps for classes, boxes for
|
|
// constants, or fixed arrays for unions.
|
|
|
|
struct HeapTypeConfig {
|
|
typedef TypeImpl<HeapTypeConfig> Type;
|
|
typedef i::Object Base;
|
|
typedef i::FixedArray Struct;
|
|
typedef i::FixedArray Range;
|
|
typedef i::Isolate Region;
|
|
template<class T> struct Handle { typedef i::Handle<T> type; };
|
|
|
|
static const int kRangeStructTag = 0xffff;
|
|
|
|
template<class T> static inline i::Handle<T> null_handle() {
|
|
return i::Handle<T>();
|
|
}
|
|
template<class T> static inline i::Handle<T> handle(T* type);
|
|
template<class T> static inline i::Handle<T> cast(i::Handle<Type> type);
|
|
|
|
static inline bool is_bitset(Type* type);
|
|
static inline bool is_class(Type* type);
|
|
static inline bool is_struct(Type* type, int tag);
|
|
static inline bool is_range(Type* type);
|
|
|
|
static inline Type::bitset as_bitset(Type* type);
|
|
static inline i::Handle<i::Map> as_class(Type* type);
|
|
static inline i::Handle<Struct> as_struct(Type* type);
|
|
static inline i::Handle<Range> as_range(Type* type);
|
|
|
|
static inline Type* from_bitset(Type::bitset);
|
|
static inline i::Handle<Type> from_bitset(Type::bitset, Isolate* isolate);
|
|
static inline i::Handle<Type> from_class(
|
|
i::Handle<i::Map> map, Isolate* isolate);
|
|
static inline i::Handle<Type> from_struct(i::Handle<Struct> structure);
|
|
static inline i::Handle<Type> from_range(i::Handle<Range> range);
|
|
|
|
static inline i::Handle<Struct> struct_create(
|
|
int tag, int length, Isolate* isolate);
|
|
static inline void struct_shrink(i::Handle<Struct> structure, int length);
|
|
static inline int struct_tag(i::Handle<Struct> structure);
|
|
static inline int struct_length(i::Handle<Struct> structure);
|
|
static inline i::Handle<Type> struct_get(i::Handle<Struct> structure, int i);
|
|
static inline void struct_set(
|
|
i::Handle<Struct> structure, int i, i::Handle<Type> type);
|
|
template<class V>
|
|
static inline i::Handle<V> struct_get_value(
|
|
i::Handle<Struct> structure, int i);
|
|
template<class V>
|
|
static inline void struct_set_value(
|
|
i::Handle<Struct> structure, int i, i::Handle<V> x);
|
|
|
|
static inline i::Handle<Range> range_create(Isolate* isolate);
|
|
static inline int range_get_bitset(i::Handle<Range> range);
|
|
static inline void range_set_bitset(i::Handle<Range> range, int value);
|
|
static inline double range_get_double(i::Handle<Range> range, int index);
|
|
static inline void range_set_double(i::Handle<Range> range, int index,
|
|
double value, Isolate* isolate);
|
|
};
|
|
|
|
typedef TypeImpl<HeapTypeConfig> HeapType;
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Type bounds. A simple struct to represent a pair of lower/upper types.
|
|
|
|
template<class Config>
|
|
struct BoundsImpl {
|
|
typedef TypeImpl<Config> Type;
|
|
typedef typename Type::TypeHandle TypeHandle;
|
|
typedef typename Type::Region Region;
|
|
|
|
TypeHandle lower;
|
|
TypeHandle upper;
|
|
|
|
BoundsImpl() : // Make sure accessing uninitialized bounds crashes big-time.
|
|
lower(Config::template null_handle<Type>()),
|
|
upper(Config::template null_handle<Type>()) {}
|
|
explicit BoundsImpl(TypeHandle t) : lower(t), upper(t) {}
|
|
BoundsImpl(TypeHandle l, TypeHandle u) : lower(l), upper(u) {
|
|
DCHECK(lower->Is(upper));
|
|
}
|
|
|
|
// Unrestricted bounds.
|
|
static BoundsImpl Unbounded() {
|
|
return BoundsImpl(Type::None(), Type::Any());
|
|
}
|
|
|
|
// Meet: both b1 and b2 are known to hold.
|
|
static BoundsImpl Both(BoundsImpl b1, BoundsImpl b2, Region* region) {
|
|
TypeHandle lower = Type::Union(b1.lower, b2.lower, region);
|
|
TypeHandle upper = Type::Intersect(b1.upper, b2.upper, region);
|
|
// Lower bounds are considered approximate, correct as necessary.
|
|
if (!lower->Is(upper)) lower = upper;
|
|
return BoundsImpl(lower, upper);
|
|
}
|
|
|
|
// Join: either b1 or b2 is known to hold.
|
|
static BoundsImpl Either(BoundsImpl b1, BoundsImpl b2, Region* region) {
|
|
TypeHandle lower = Type::Intersect(b1.lower, b2.lower, region);
|
|
TypeHandle upper = Type::Union(b1.upper, b2.upper, region);
|
|
return BoundsImpl(lower, upper);
|
|
}
|
|
|
|
static BoundsImpl NarrowLower(BoundsImpl b, TypeHandle t, Region* region) {
|
|
TypeHandle lower = Type::Union(b.lower, t, region);
|
|
// Lower bounds are considered approximate, correct as necessary.
|
|
if (!lower->Is(b.upper)) lower = b.upper;
|
|
return BoundsImpl(lower, b.upper);
|
|
}
|
|
static BoundsImpl NarrowUpper(BoundsImpl b, TypeHandle t, Region* region) {
|
|
TypeHandle lower = b.lower;
|
|
TypeHandle upper = Type::Intersect(b.upper, t, region);
|
|
// Lower bounds are considered approximate, correct as necessary.
|
|
if (!lower->Is(upper)) lower = upper;
|
|
return BoundsImpl(lower, upper);
|
|
}
|
|
|
|
bool Narrows(BoundsImpl that) {
|
|
return that.lower->Is(this->lower) && this->upper->Is(that.upper);
|
|
}
|
|
};
|
|
|
|
typedef BoundsImpl<ZoneTypeConfig> Bounds;
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|
|
|
|
#endif // V8_TYPES_H_
|