Establish distributivity for type union & intersection
This requires introducing proper bounds on all leaf types, so that intersection between bitsets and these types can be accurately represented. Extending a union also becomes more involved. (On the upside, the modified union/intersect algorithm would now allow support for proper variance for function types.) Not sure if it is worth landing this. Distributivity isn't really a crucial property for our use cases. It seems fine if intersection is slightly lossy. R=bmeurer@chromium.org, jarin@chromium.org BUG= Review URL: https://codereview.chromium.org/251753005 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21528 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
276adeda1a
commit
7ac892c8bd
@ -8,6 +8,7 @@
|
|||||||
#include "conversions.h"
|
#include "conversions.h"
|
||||||
#include "objects.h"
|
#include "objects.h"
|
||||||
#include "property-details.h"
|
#include "property-details.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
@ -23,6 +24,16 @@ inline bool Representation::CanContainDouble(double value) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Representation Representation::FromType(Type* type) {
|
||||||
|
DisallowHeapAllocation no_allocation;
|
||||||
|
if (type->Is(Type::None())) return Representation::None();
|
||||||
|
if (type->Is(Type::SignedSmall())) return Representation::Smi();
|
||||||
|
if (type->Is(Type::Signed32())) return Representation::Integer32();
|
||||||
|
if (type->Is(Type::Number())) return Representation::Double();
|
||||||
|
return Representation::Tagged();
|
||||||
|
}
|
||||||
|
|
||||||
} } // namespace v8::internal
|
} } // namespace v8::internal
|
||||||
|
|
||||||
#endif // V8_PROPERTY_DETAILS_INL_H_
|
#endif // V8_PROPERTY_DETAILS_INL_H_
|
||||||
|
180
src/types-inl.h
180
src/types-inl.h
@ -13,7 +13,7 @@
|
|||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
// -------------------------------------------------------------------------- //
|
// -----------------------------------------------------------------------------
|
||||||
// TypeImpl
|
// TypeImpl
|
||||||
|
|
||||||
template<class Config>
|
template<class Config>
|
||||||
@ -25,6 +25,18 @@ TypeImpl<Config>* TypeImpl<Config>::cast(typename Config::Base* object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Most precise _current_ type of a value (usually its class).
|
||||||
|
template<class Config>
|
||||||
|
typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::NowOf(
|
||||||
|
i::Object* value, Region* region) {
|
||||||
|
if (value->IsSmi() ||
|
||||||
|
i::HeapObject::cast(value)->map()->instance_type() == HEAP_NUMBER_TYPE) {
|
||||||
|
return Of(value, region);
|
||||||
|
}
|
||||||
|
return Class(i::handle(i::HeapObject::cast(value)->map()), region);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template<class Config>
|
template<class Config>
|
||||||
bool TypeImpl<Config>::NowContains(i::Object* value) {
|
bool TypeImpl<Config>::NowContains(i::Object* value) {
|
||||||
DisallowHeapAllocation no_allocation;
|
DisallowHeapAllocation no_allocation;
|
||||||
@ -39,7 +51,7 @@ bool TypeImpl<Config>::NowContains(i::Object* value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------- //
|
// -----------------------------------------------------------------------------
|
||||||
// ZoneTypeConfig
|
// ZoneTypeConfig
|
||||||
|
|
||||||
// static
|
// static
|
||||||
@ -70,13 +82,7 @@ bool ZoneTypeConfig::is_struct(Type* type, int tag) {
|
|||||||
|
|
||||||
// static
|
// static
|
||||||
bool ZoneTypeConfig::is_class(Type* type) {
|
bool ZoneTypeConfig::is_class(Type* type) {
|
||||||
return is_struct(type, Type::StructuralType::kClassTag);
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// static
|
|
||||||
bool ZoneTypeConfig::is_constant(Type* type) {
|
|
||||||
return is_struct(type, Type::StructuralType::kConstantTag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -96,16 +102,8 @@ ZoneTypeConfig::Struct* ZoneTypeConfig::as_struct(Type* type) {
|
|||||||
|
|
||||||
// static
|
// static
|
||||||
i::Handle<i::Map> ZoneTypeConfig::as_class(Type* type) {
|
i::Handle<i::Map> ZoneTypeConfig::as_class(Type* type) {
|
||||||
ASSERT(is_class(type));
|
UNREACHABLE();
|
||||||
return i::Handle<i::Map>(static_cast<i::Map**>(as_struct(type)[3]));
|
return i::Handle<i::Map>();
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// static
|
|
||||||
i::Handle<i::Object> ZoneTypeConfig::as_constant(Type* type) {
|
|
||||||
ASSERT(is_constant(type));
|
|
||||||
return i::Handle<i::Object>(
|
|
||||||
static_cast<i::Object**>(as_struct(type)[3]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -122,84 +120,80 @@ ZoneTypeConfig::Type* ZoneTypeConfig::from_bitset(int bitset, Zone* Zone) {
|
|||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
ZoneTypeConfig::Type* ZoneTypeConfig::from_struct(Struct* structured) {
|
ZoneTypeConfig::Type* ZoneTypeConfig::from_struct(Struct* structure) {
|
||||||
return reinterpret_cast<Type*>(structured);
|
return reinterpret_cast<Type*>(structure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
ZoneTypeConfig::Type* ZoneTypeConfig::from_class(
|
ZoneTypeConfig::Type* ZoneTypeConfig::from_class(
|
||||||
i::Handle<i::Map> map, int lub, Zone* zone) {
|
i::Handle<i::Map> map, Zone* zone) {
|
||||||
Struct* structured = struct_create(Type::StructuralType::kClassTag, 2, zone);
|
return from_bitset(0);
|
||||||
structured[2] = from_bitset(lub);
|
|
||||||
structured[3] = map.location();
|
|
||||||
return from_struct(structured);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// static
|
|
||||||
ZoneTypeConfig::Type* ZoneTypeConfig::from_constant(
|
|
||||||
i::Handle<i::Object> value, int lub, Zone* zone) {
|
|
||||||
Struct* structured =
|
|
||||||
struct_create(Type::StructuralType::kConstantTag, 2, zone);
|
|
||||||
structured[2] = from_bitset(lub);
|
|
||||||
structured[3] = value.location();
|
|
||||||
return from_struct(structured);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
ZoneTypeConfig::Struct* ZoneTypeConfig::struct_create(
|
ZoneTypeConfig::Struct* ZoneTypeConfig::struct_create(
|
||||||
int tag, int length, Zone* zone) {
|
int tag, int length, Zone* zone) {
|
||||||
Struct* structured = reinterpret_cast<Struct*>(
|
Struct* structure = reinterpret_cast<Struct*>(
|
||||||
zone->New(sizeof(void*) * (length + 2))); // NOLINT
|
zone->New(sizeof(void*) * (length + 2))); // NOLINT
|
||||||
structured[0] = reinterpret_cast<void*>(tag);
|
structure[0] = reinterpret_cast<void*>(tag);
|
||||||
structured[1] = reinterpret_cast<void*>(length);
|
structure[1] = reinterpret_cast<void*>(length);
|
||||||
return structured;
|
return structure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void ZoneTypeConfig::struct_shrink(Struct* structured, int length) {
|
void ZoneTypeConfig::struct_shrink(Struct* structure, int length) {
|
||||||
ASSERT(0 <= length && length <= struct_length(structured));
|
ASSERT(0 <= length && length <= struct_length(structure));
|
||||||
structured[1] = reinterpret_cast<void*>(length);
|
structure[1] = reinterpret_cast<void*>(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
int ZoneTypeConfig::struct_tag(Struct* structured) {
|
int ZoneTypeConfig::struct_tag(Struct* structure) {
|
||||||
return static_cast<int>(reinterpret_cast<intptr_t>(structured[0]));
|
return static_cast<int>(reinterpret_cast<intptr_t>(structure[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
int ZoneTypeConfig::struct_length(Struct* structured) {
|
int ZoneTypeConfig::struct_length(Struct* structure) {
|
||||||
return static_cast<int>(reinterpret_cast<intptr_t>(structured[1]));
|
return static_cast<int>(reinterpret_cast<intptr_t>(structure[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
Type* ZoneTypeConfig::struct_get(Struct* structured, int i) {
|
Type* ZoneTypeConfig::struct_get(Struct* structure, int i) {
|
||||||
ASSERT(0 <= i && i <= struct_length(structured));
|
ASSERT(0 <= i && i <= struct_length(structure));
|
||||||
return static_cast<Type*>(structured[2 + i]);
|
return static_cast<Type*>(structure[2 + i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void ZoneTypeConfig::struct_set(Struct* structured, int i, Type* type) {
|
void ZoneTypeConfig::struct_set(Struct* structure, int i, Type* x) {
|
||||||
ASSERT(0 <= i && i <= struct_length(structured));
|
ASSERT(0 <= i && i <= struct_length(structure));
|
||||||
structured[2 + i] = type;
|
structure[2 + i] = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
int ZoneTypeConfig::lub_bitset(Type* type) {
|
template<class V>
|
||||||
ASSERT(is_class(type) || is_constant(type));
|
i::Handle<V> ZoneTypeConfig::struct_get_value(Struct* structure, int i) {
|
||||||
return as_bitset(struct_get(as_struct(type), 0));
|
ASSERT(0 <= i && i <= struct_length(structure));
|
||||||
|
return i::Handle<V>(static_cast<V**>(structure[2 + i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------- //
|
// static
|
||||||
|
template<class V>
|
||||||
|
void ZoneTypeConfig::struct_set_value(
|
||||||
|
Struct* structure, int i, i::Handle<V> x) {
|
||||||
|
ASSERT(0 <= i && i <= struct_length(structure));
|
||||||
|
structure[2 + i] = x.location();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
// HeapTypeConfig
|
// HeapTypeConfig
|
||||||
|
|
||||||
// static
|
// static
|
||||||
@ -228,12 +222,6 @@ bool HeapTypeConfig::is_class(Type* type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
|
||||||
bool HeapTypeConfig::is_constant(Type* type) {
|
|
||||||
return type->IsBox();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
bool HeapTypeConfig::is_struct(Type* type, int tag) {
|
bool HeapTypeConfig::is_struct(Type* type, int tag) {
|
||||||
return type->IsFixedArray() && struct_tag(as_struct(type)) == tag;
|
return type->IsFixedArray() && struct_tag(as_struct(type)) == tag;
|
||||||
@ -252,13 +240,6 @@ i::Handle<i::Map> HeapTypeConfig::as_class(Type* type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
|
||||||
i::Handle<i::Object> HeapTypeConfig::as_constant(Type* type) {
|
|
||||||
i::Box* box = i::Box::cast(type);
|
|
||||||
return i::handle(box->value(), box->GetIsolate());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
i::Handle<HeapTypeConfig::Struct> HeapTypeConfig::as_struct(Type* type) {
|
i::Handle<HeapTypeConfig::Struct> HeapTypeConfig::as_struct(Type* type) {
|
||||||
return i::handle(Struct::cast(type));
|
return i::handle(Struct::cast(type));
|
||||||
@ -280,71 +261,74 @@ i::Handle<HeapTypeConfig::Type> HeapTypeConfig::from_bitset(
|
|||||||
|
|
||||||
// static
|
// static
|
||||||
i::Handle<HeapTypeConfig::Type> HeapTypeConfig::from_class(
|
i::Handle<HeapTypeConfig::Type> HeapTypeConfig::from_class(
|
||||||
i::Handle<i::Map> map, int lub, Isolate* isolate) {
|
i::Handle<i::Map> map, Isolate* isolate) {
|
||||||
return i::Handle<Type>::cast(i::Handle<Object>::cast(map));
|
return i::Handle<Type>::cast(i::Handle<Object>::cast(map));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
|
||||||
i::Handle<HeapTypeConfig::Type> HeapTypeConfig::from_constant(
|
|
||||||
i::Handle<i::Object> value, int lub, Isolate* isolate) {
|
|
||||||
i::Handle<Box> box = isolate->factory()->NewBox(value);
|
|
||||||
return i::Handle<Type>::cast(i::Handle<Object>::cast(box));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
i::Handle<HeapTypeConfig::Type> HeapTypeConfig::from_struct(
|
i::Handle<HeapTypeConfig::Type> HeapTypeConfig::from_struct(
|
||||||
i::Handle<Struct> structured) {
|
i::Handle<Struct> structure) {
|
||||||
return i::Handle<Type>::cast(i::Handle<Object>::cast(structured));
|
return i::Handle<Type>::cast(i::Handle<Object>::cast(structure));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
i::Handle<HeapTypeConfig::Struct> HeapTypeConfig::struct_create(
|
i::Handle<HeapTypeConfig::Struct> HeapTypeConfig::struct_create(
|
||||||
int tag, int length, Isolate* isolate) {
|
int tag, int length, Isolate* isolate) {
|
||||||
i::Handle<Struct> structured = isolate->factory()->NewFixedArray(length + 1);
|
i::Handle<Struct> structure = isolate->factory()->NewFixedArray(length + 1);
|
||||||
structured->set(0, i::Smi::FromInt(tag));
|
structure->set(0, i::Smi::FromInt(tag));
|
||||||
return structured;
|
return structure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void HeapTypeConfig::struct_shrink(i::Handle<Struct> structured, int length) {
|
void HeapTypeConfig::struct_shrink(i::Handle<Struct> structure, int length) {
|
||||||
structured->Shrink(length + 1);
|
structure->Shrink(length + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
int HeapTypeConfig::struct_tag(i::Handle<Struct> structured) {
|
int HeapTypeConfig::struct_tag(i::Handle<Struct> structure) {
|
||||||
return static_cast<i::Smi*>(structured->get(0))->value();
|
return static_cast<i::Smi*>(structure->get(0))->value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
int HeapTypeConfig::struct_length(i::Handle<Struct> structured) {
|
int HeapTypeConfig::struct_length(i::Handle<Struct> structure) {
|
||||||
return structured->length() - 1;
|
return structure->length() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
i::Handle<HeapTypeConfig::Type> HeapTypeConfig::struct_get(
|
i::Handle<HeapTypeConfig::Type> HeapTypeConfig::struct_get(
|
||||||
i::Handle<Struct> structured, int i) {
|
i::Handle<Struct> structure, int i) {
|
||||||
Type* type = static_cast<Type*>(structured->get(i + 1));
|
Type* type = static_cast<Type*>(structure->get(i + 1));
|
||||||
return i::handle(type, structured->GetIsolate());
|
return i::handle(type, structure->GetIsolate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void HeapTypeConfig::struct_set(
|
void HeapTypeConfig::struct_set(
|
||||||
i::Handle<Struct> structured, int i, i::Handle<Type> type) {
|
i::Handle<Struct> structure, int i, i::Handle<Type> type) {
|
||||||
structured->set(i + 1, *type);
|
structure->set(i + 1, *type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
int HeapTypeConfig::lub_bitset(Type* type) {
|
template<class V>
|
||||||
return 0; // kNone, which causes recomputation.
|
i::Handle<V> HeapTypeConfig::struct_get_value(
|
||||||
|
i::Handle<Struct> structure, int i) {
|
||||||
|
V* x = static_cast<V*>(structure->get(i + 1));
|
||||||
|
return i::handle(x, structure->GetIsolate());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// static
|
||||||
|
template<class V>
|
||||||
|
void HeapTypeConfig::struct_set_value(
|
||||||
|
i::Handle<Struct> structure, int i, i::Handle<V> x) {
|
||||||
|
structure->set(i + 1, *x);
|
||||||
}
|
}
|
||||||
|
|
||||||
} } // namespace v8::internal
|
} } // namespace v8::internal
|
||||||
|
640
src/types.cc
640
src/types.cc
@ -10,122 +10,43 @@
|
|||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
template<class Config>
|
// -----------------------------------------------------------------------------
|
||||||
int TypeImpl<Config>::NumClasses() {
|
// Glb and lub computation.
|
||||||
DisallowHeapAllocation no_allocation;
|
|
||||||
if (this->IsClass()) {
|
|
||||||
return 1;
|
|
||||||
} else if (this->IsUnion()) {
|
|
||||||
UnionHandle unioned = handle(this->AsUnion());
|
|
||||||
int result = 0;
|
|
||||||
for (int i = 0; i < unioned->Length(); ++i) {
|
|
||||||
if (unioned->Get(i)->IsClass()) ++result;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// The largest bitset subsumed by this type.
|
||||||
template<class Config>
|
|
||||||
int TypeImpl<Config>::NumConstants() {
|
|
||||||
DisallowHeapAllocation no_allocation;
|
|
||||||
if (this->IsConstant()) {
|
|
||||||
return 1;
|
|
||||||
} else if (this->IsUnion()) {
|
|
||||||
UnionHandle unioned = handle(this->AsUnion());
|
|
||||||
int result = 0;
|
|
||||||
for (int i = 0; i < unioned->Length(); ++i) {
|
|
||||||
if (unioned->Get(i)->IsConstant()) ++result;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<class Config> template<class T>
|
|
||||||
typename TypeImpl<Config>::TypeHandle
|
|
||||||
TypeImpl<Config>::Iterator<T>::get_type() {
|
|
||||||
ASSERT(!Done());
|
|
||||||
return type_->IsUnion() ? type_->AsUnion()->Get(index_) : type_;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// C++ cannot specialise nested templates, so we have to go through this
|
|
||||||
// contortion with an auxiliary template to simulate it.
|
|
||||||
template<class Config, class T>
|
|
||||||
struct TypeImplIteratorAux {
|
|
||||||
static bool matches(typename TypeImpl<Config>::TypeHandle type);
|
|
||||||
static i::Handle<T> current(typename TypeImpl<Config>::TypeHandle type);
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Config>
|
|
||||||
struct TypeImplIteratorAux<Config, i::Map> {
|
|
||||||
static bool matches(typename TypeImpl<Config>::TypeHandle type) {
|
|
||||||
return type->IsClass();
|
|
||||||
}
|
|
||||||
static i::Handle<i::Map> current(typename TypeImpl<Config>::TypeHandle type) {
|
|
||||||
return type->AsClass()->Map();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Config>
|
|
||||||
struct TypeImplIteratorAux<Config, i::Object> {
|
|
||||||
static bool matches(typename TypeImpl<Config>::TypeHandle type) {
|
|
||||||
return type->IsConstant();
|
|
||||||
}
|
|
||||||
static i::Handle<i::Object> current(
|
|
||||||
typename TypeImpl<Config>::TypeHandle type) {
|
|
||||||
return type->AsConstant()->Value();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Config> template<class T>
|
|
||||||
bool TypeImpl<Config>::Iterator<T>::matches(TypeHandle type) {
|
|
||||||
return TypeImplIteratorAux<Config, T>::matches(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Config> template<class T>
|
|
||||||
i::Handle<T> TypeImpl<Config>::Iterator<T>::Current() {
|
|
||||||
return TypeImplIteratorAux<Config, T>::current(get_type());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<class Config> template<class T>
|
|
||||||
void TypeImpl<Config>::Iterator<T>::Advance() {
|
|
||||||
DisallowHeapAllocation no_allocation;
|
|
||||||
++index_;
|
|
||||||
if (type_->IsUnion()) {
|
|
||||||
UnionHandle unioned = handle(type_->AsUnion());
|
|
||||||
for (; index_ < unioned->Length(); ++index_) {
|
|
||||||
if (matches(unioned->Get(index_))) return;
|
|
||||||
}
|
|
||||||
} else if (index_ == 0 && matches(type_)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
index_ = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Get the largest bitset subsumed by this type.
|
|
||||||
template<class Config>
|
template<class Config>
|
||||||
int TypeImpl<Config>::BitsetType::Glb(TypeImpl* type) {
|
int TypeImpl<Config>::BitsetType::Glb(TypeImpl* type) {
|
||||||
DisallowHeapAllocation no_allocation;
|
DisallowHeapAllocation no_allocation;
|
||||||
if (type->IsBitset()) {
|
if (type->IsBitset()) {
|
||||||
return type->AsBitset();
|
return type->AsBitset();
|
||||||
} else if (type->IsUnion()) {
|
} else if (type->IsUnion()) {
|
||||||
// All but the first are non-bitsets and thus would yield kNone anyway.
|
UnionHandle unioned = handle(type->AsUnion());
|
||||||
return type->AsUnion()->Get(0)->BitsetGlb();
|
int bitset = kNone;
|
||||||
|
for (int i = 0; i < unioned->Length(); ++i) {
|
||||||
|
bitset |= unioned->Get(i)->BitsetGlb();
|
||||||
|
}
|
||||||
|
return bitset;
|
||||||
|
} else if (type->IsClass()) {
|
||||||
|
// Little hack to avoid the need for a region for handlification here...
|
||||||
|
return REPRESENTATION(Config::is_class(type)
|
||||||
|
? Lub(*Config::as_class(type))
|
||||||
|
: type->AsClass()->Bound(NULL)->AsBitset());
|
||||||
|
} else if (type->IsConstant()) {
|
||||||
|
return REPRESENTATION(type->AsConstant()->Bound()->AsBitset());
|
||||||
|
} else if (type->IsContext()) {
|
||||||
|
return REPRESENTATION(type->AsContext()->Bound()->AsBitset());
|
||||||
|
} else if (type->IsArray()) {
|
||||||
|
return REPRESENTATION(type->AsArray()->Bound()->AsBitset());
|
||||||
|
} else if (type->IsFunction()) {
|
||||||
|
return REPRESENTATION(type->AsFunction()->Bound()->AsBitset());
|
||||||
} else {
|
} else {
|
||||||
|
UNREACHABLE();
|
||||||
return kNone;
|
return kNone;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Get the smallest bitset subsuming this type.
|
// The smallest bitset subsuming this type.
|
||||||
template<class Config>
|
template<class Config>
|
||||||
int TypeImpl<Config>::BitsetType::Lub(TypeImpl* type) {
|
int TypeImpl<Config>::BitsetType::Lub(TypeImpl* type) {
|
||||||
DisallowHeapAllocation no_allocation;
|
DisallowHeapAllocation no_allocation;
|
||||||
@ -139,17 +60,47 @@ int TypeImpl<Config>::BitsetType::Lub(TypeImpl* type) {
|
|||||||
}
|
}
|
||||||
return bitset;
|
return bitset;
|
||||||
} else if (type->IsClass()) {
|
} else if (type->IsClass()) {
|
||||||
int bitset = Config::lub_bitset(type);
|
// Little hack to avoid the need for a region for handlification here...
|
||||||
return bitset ? bitset : Lub(*type->AsClass()->Map());
|
return Config::is_class(type) ? Lub(*Config::as_class(type)) :
|
||||||
|
type->AsClass()->Bound(NULL)->AsBitset();
|
||||||
} else if (type->IsConstant()) {
|
} else if (type->IsConstant()) {
|
||||||
int bitset = Config::lub_bitset(type);
|
return type->AsConstant()->Bound()->AsBitset();
|
||||||
return bitset ? bitset : Lub(*type->AsConstant()->Value());
|
} else if (type->IsContext()) {
|
||||||
|
return type->AsContext()->Bound()->AsBitset();
|
||||||
|
} else if (type->IsArray()) {
|
||||||
|
return type->AsArray()->Bound()->AsBitset();
|
||||||
|
} else if (type->IsFunction()) {
|
||||||
|
return type->AsFunction()->Bound()->AsBitset();
|
||||||
|
} else {
|
||||||
|
UNREACHABLE();
|
||||||
|
return kNone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The smallest bitset subsuming this type, ignoring explicit bounds.
|
||||||
|
template<class Config>
|
||||||
|
int TypeImpl<Config>::BitsetType::InherentLub(TypeImpl* type) {
|
||||||
|
DisallowHeapAllocation no_allocation;
|
||||||
|
if (type->IsBitset()) {
|
||||||
|
return type->AsBitset();
|
||||||
|
} else if (type->IsUnion()) {
|
||||||
|
UnionHandle unioned = handle(type->AsUnion());
|
||||||
|
int bitset = kNone;
|
||||||
|
for (int i = 0; i < unioned->Length(); ++i) {
|
||||||
|
bitset |= unioned->Get(i)->InherentBitsetLub();
|
||||||
|
}
|
||||||
|
return bitset;
|
||||||
|
} else if (type->IsClass()) {
|
||||||
|
return Lub(*type->AsClass()->Map());
|
||||||
|
} else if (type->IsConstant()) {
|
||||||
|
return Lub(*type->AsConstant()->Value());
|
||||||
|
} else if (type->IsContext()) {
|
||||||
|
return kInternal & kTaggedPtr;
|
||||||
} else if (type->IsArray()) {
|
} else if (type->IsArray()) {
|
||||||
return kArray;
|
return kArray;
|
||||||
} else if (type->IsFunction()) {
|
} else if (type->IsFunction()) {
|
||||||
return kFunction;
|
return kFunction;
|
||||||
} else if (type->IsContext()) {
|
|
||||||
return kInternal & kTaggedPtr;
|
|
||||||
} else {
|
} else {
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
return kNone;
|
return kNone;
|
||||||
@ -295,17 +246,8 @@ int TypeImpl<Config>::BitsetType::Lub(i::Map* map) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Most precise _current_ type of a value (usually its class).
|
// -----------------------------------------------------------------------------
|
||||||
template<class Config>
|
// Predicates.
|
||||||
typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::NowOf(
|
|
||||||
i::Object* value, Region* region) {
|
|
||||||
if (value->IsSmi() ||
|
|
||||||
i::HeapObject::cast(value)->map()->instance_type() == HEAP_NUMBER_TYPE) {
|
|
||||||
return Of(value, region);
|
|
||||||
}
|
|
||||||
return Class(i::handle(i::HeapObject::cast(value)->map()), region);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Check this <= that.
|
// Check this <= that.
|
||||||
template<class Config>
|
template<class Config>
|
||||||
@ -317,14 +259,23 @@ bool TypeImpl<Config>::SlowIs(TypeImpl* that) {
|
|||||||
if (that->IsBitset()) {
|
if (that->IsBitset()) {
|
||||||
return (BitsetType::Lub(this) | that->AsBitset()) == that->AsBitset();
|
return (BitsetType::Lub(this) | that->AsBitset()) == that->AsBitset();
|
||||||
}
|
}
|
||||||
|
if (this->IsBitset() && SEMANTIC(this->AsBitset()) == BitsetType::kNone) {
|
||||||
|
// Bitsets only have non-bitset supertypes along the representation axis.
|
||||||
|
int that_bitset = that->BitsetGlb();
|
||||||
|
return (this->AsBitset() | that_bitset) == that_bitset;
|
||||||
|
}
|
||||||
|
|
||||||
if (that->IsClass()) {
|
if (that->IsClass()) {
|
||||||
return this->IsClass()
|
return this->IsClass()
|
||||||
&& *this->AsClass()->Map() == *that->AsClass()->Map();
|
&& *this->AsClass()->Map() == *that->AsClass()->Map()
|
||||||
|
&& ((Config::is_class(that) && Config::is_class(this)) ||
|
||||||
|
BitsetType::New(this->BitsetLub())->Is(
|
||||||
|
BitsetType::New(that->BitsetLub())));
|
||||||
}
|
}
|
||||||
if (that->IsConstant()) {
|
if (that->IsConstant()) {
|
||||||
return this->IsConstant()
|
return this->IsConstant()
|
||||||
&& *this->AsConstant()->Value() == *that->AsConstant()->Value();
|
&& *this->AsConstant()->Value() == *that->AsConstant()->Value()
|
||||||
|
&& this->AsConstant()->Bound()->Is(that->AsConstant()->Bound());
|
||||||
}
|
}
|
||||||
if (that->IsContext()) {
|
if (that->IsContext()) {
|
||||||
return this->IsContext()
|
return this->IsContext()
|
||||||
@ -461,10 +412,10 @@ bool TypeImpl<Config>::Maybe(TypeImpl* that) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check if value is contained in (inhabits) type.
|
||||||
template<class Config>
|
template<class Config>
|
||||||
bool TypeImpl<Config>::Contains(i::Object* value) {
|
bool TypeImpl<Config>::Contains(i::Object* value) {
|
||||||
DisallowHeapAllocation no_allocation;
|
DisallowHeapAllocation no_allocation;
|
||||||
|
|
||||||
for (Iterator<i::Object> it = this->Constants(); !it.Done(); it.Advance()) {
|
for (Iterator<i::Object> it = this->Constants(); !it.Done(); it.Advance()) {
|
||||||
if (*it.Current() == value) return true;
|
if (*it.Current() == value) return true;
|
||||||
}
|
}
|
||||||
@ -473,44 +424,166 @@ bool TypeImpl<Config>::Contains(i::Object* value) {
|
|||||||
|
|
||||||
|
|
||||||
template<class Config>
|
template<class Config>
|
||||||
bool TypeImpl<Config>::InUnion(UnionHandle unioned, int current_size) {
|
bool TypeImpl<Config>::UnionType::Wellformed() {
|
||||||
ASSERT(!this->IsUnion());
|
ASSERT(this->Length() >= 2);
|
||||||
for (int i = 0; i < current_size; ++i) {
|
for (int i = 0; i < this->Length(); ++i) {
|
||||||
if (this->Is(unioned->Get(i))) return true;
|
ASSERT(!this->Get(i)->IsUnion());
|
||||||
|
if (i > 0) ASSERT(!this->Get(i)->IsBitset());
|
||||||
|
for (int j = 0; j < this->Length(); ++j) {
|
||||||
|
if (i != j) ASSERT(!this->Get(i)->Is(this->Get(j)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Get non-bitsets from this which are not subsumed by union, store at result,
|
// -----------------------------------------------------------------------------
|
||||||
// starting at index. Returns updated index.
|
// Union and intersection
|
||||||
|
|
||||||
|
template<class Config>
|
||||||
|
typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Narrow(
|
||||||
|
int bitset, Region* region) {
|
||||||
|
TypeHandle bound = BitsetType::New(bitset, region);
|
||||||
|
if (this->IsClass()) {
|
||||||
|
return ClassType::New(this->AsClass()->Map(), bound, region);
|
||||||
|
} else if (this->IsConstant()) {
|
||||||
|
return ConstantType::New(this->AsConstant()->Value(), bound, region);
|
||||||
|
} else if (this->IsContext()) {
|
||||||
|
return ContextType::New(this->AsContext()->Outer(), bound, region);
|
||||||
|
} else if (this->IsArray()) {
|
||||||
|
return ArrayType::New(this->AsArray()->Element(), bound, region);
|
||||||
|
} else if (this->IsFunction()) {
|
||||||
|
FunctionType* function = this->AsFunction();
|
||||||
|
int arity = function->Arity();
|
||||||
|
FunctionHandle type = FunctionType::New(
|
||||||
|
function->Result(), function->Receiver(), bound, arity, region);
|
||||||
|
for (int i = 0; i < arity; ++i) {
|
||||||
|
type->InitParameter(i, function->Parameter(i));
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
return TypeHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class Config>
|
||||||
|
int TypeImpl<Config>::BoundBy(TypeImpl* that) {
|
||||||
|
ASSERT(!this->IsUnion());
|
||||||
|
if (that->IsUnion()) {
|
||||||
|
UnionType* unioned = that->AsUnion();
|
||||||
|
int length = unioned->Length();
|
||||||
|
int bitset = BitsetType::kNone;
|
||||||
|
for (int i = 0; i < length; ++i) {
|
||||||
|
bitset |= BoundBy(unioned->Get(i)->unhandle());
|
||||||
|
}
|
||||||
|
return bitset;
|
||||||
|
} else if (that->IsClass() && this->IsClass() &&
|
||||||
|
*this->AsClass()->Map() == *that->AsClass()->Map()) {
|
||||||
|
return that->BitsetLub();
|
||||||
|
} else if (that->IsConstant() && this->IsConstant() &&
|
||||||
|
*this->AsConstant()->Value() == *that->AsConstant()->Value()) {
|
||||||
|
return that->AsConstant()->Bound()->AsBitset();
|
||||||
|
} else if (that->IsContext() && this->IsContext() && this->Is(that)) {
|
||||||
|
return that->AsContext()->Bound()->AsBitset();
|
||||||
|
} else if (that->IsArray() && this->IsArray() && this->Is(that)) {
|
||||||
|
return that->AsArray()->Bound()->AsBitset();
|
||||||
|
} else if (that->IsFunction() && this->IsFunction() && this->Is(that)) {
|
||||||
|
return that->AsFunction()->Bound()->AsBitset();
|
||||||
|
}
|
||||||
|
return that->BitsetGlb();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class Config>
|
||||||
|
int TypeImpl<Config>::IndexInUnion(
|
||||||
|
int bound, UnionHandle unioned, int current_size) {
|
||||||
|
ASSERT(!this->IsUnion());
|
||||||
|
for (int i = 0; i < current_size; ++i) {
|
||||||
|
TypeHandle that = unioned->Get(i);
|
||||||
|
if (that->IsBitset()) {
|
||||||
|
if ((bound | that->AsBitset()) == that->AsBitset()) return i;
|
||||||
|
} else if (that->IsClass() && this->IsClass()) {
|
||||||
|
if (*this->AsClass()->Map() == *that->AsClass()->Map()) return i;
|
||||||
|
} else if (that->IsConstant() && this->IsConstant()) {
|
||||||
|
if (*this->AsConstant()->Value() == *that->AsConstant()->Value())
|
||||||
|
return i;
|
||||||
|
} else if (that->IsContext() && this->IsContext()) {
|
||||||
|
if (this->Is(that)) return i;
|
||||||
|
} else if (that->IsArray() && this->IsArray()) {
|
||||||
|
if (this->Is(that)) return i;
|
||||||
|
} else if (that->IsFunction() && this->IsFunction()) {
|
||||||
|
if (this->Is(that)) return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Get non-bitsets from type, bounded by upper.
|
||||||
|
// Store at result starting at index. Returns updated index.
|
||||||
template<class Config>
|
template<class Config>
|
||||||
int TypeImpl<Config>::ExtendUnion(
|
int TypeImpl<Config>::ExtendUnion(
|
||||||
UnionHandle result, TypeHandle type, int current_size) {
|
UnionHandle result, int size, TypeHandle type,
|
||||||
int old_size = current_size;
|
TypeHandle other, bool is_intersect, Region* region) {
|
||||||
|
int old_size = size;
|
||||||
if (type->IsUnion()) {
|
if (type->IsUnion()) {
|
||||||
UnionHandle unioned = handle(type->AsUnion());
|
UnionHandle unioned = handle(type->AsUnion());
|
||||||
for (int i = 0; i < unioned->Length(); ++i) {
|
for (int i = 0; i < unioned->Length(); ++i) {
|
||||||
TypeHandle type = unioned->Get(i);
|
TypeHandle type_i = unioned->Get(i);
|
||||||
ASSERT(i == 0 || !(type->IsBitset() || type->Is(unioned->Get(0))));
|
ASSERT(i == 0 || !(type_i->IsBitset() || type_i->Is(unioned->Get(0))));
|
||||||
if (!type->IsBitset() && !type->InUnion(result, old_size)) {
|
if (!type_i->IsBitset()) {
|
||||||
result->Set(current_size++, type);
|
size = ExtendUnion(result, size, type_i, other, is_intersect, region);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!type->IsBitset()) {
|
} else if (!type->IsBitset()) {
|
||||||
// For all structural types, subtyping implies equivalence.
|
|
||||||
ASSERT(type->IsClass() || type->IsConstant() ||
|
ASSERT(type->IsClass() || type->IsConstant() ||
|
||||||
type->IsArray() || type->IsFunction() ||
|
type->IsArray() || type->IsFunction() || type->IsContext());
|
||||||
type->IsContext());
|
int inherent_bound = type->InherentBitsetLub();
|
||||||
if (!type->InUnion(result, old_size)) {
|
int old_bound = type->BitsetLub();
|
||||||
result->Set(current_size++, type);
|
int other_bound = type->BoundBy(other->unhandle()) & inherent_bound;
|
||||||
|
int new_bound =
|
||||||
|
is_intersect ? (old_bound & other_bound) : (old_bound | other_bound);
|
||||||
|
if (new_bound != BitsetType::kNone) {
|
||||||
|
int i = type->IndexInUnion(new_bound, result, old_size);
|
||||||
|
if (i == -1) {
|
||||||
|
i = size++;
|
||||||
|
} else if (result->Get(i)->IsBitset()) {
|
||||||
|
return size; // Already fully subsumed.
|
||||||
|
} else {
|
||||||
|
int type_i_bound = result->Get(i)->BitsetLub();
|
||||||
|
new_bound |= type_i_bound;
|
||||||
|
if (new_bound == type_i_bound) return size;
|
||||||
|
}
|
||||||
|
if (new_bound != old_bound) type = type->Narrow(new_bound, region);
|
||||||
|
result->Set(i, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return current_size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Union is O(1) on simple bit unions, but O(n*m) on structured unions.
|
// If bitset is subsumed by another entry in the result, remove it.
|
||||||
|
// (Only bitsets with empty semantic axis can be subtypes of non-bitsets.)
|
||||||
|
template<class Config>
|
||||||
|
int TypeImpl<Config>::NormalizeUnion(UnionHandle result, int size, int bitset) {
|
||||||
|
if (bitset != BitsetType::kNone && SEMANTIC(bitset) == BitsetType::kNone) {
|
||||||
|
for (int i = 1; i < size; ++i) {
|
||||||
|
int glb = result->Get(i)->BitsetGlb();
|
||||||
|
if ((bitset | glb) == glb) {
|
||||||
|
for (int j = 1; j < size; ++j) {
|
||||||
|
result->Set(j - 1, result->Get(j));
|
||||||
|
}
|
||||||
|
--size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Union is O(1) on simple bitsets, but O(n*m) on structured unions.
|
||||||
template<class Config>
|
template<class Config>
|
||||||
typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Union(
|
typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Union(
|
||||||
TypeHandle type1, TypeHandle type2, Region* region) {
|
TypeHandle type1, TypeHandle type2, Region* region) {
|
||||||
@ -546,47 +619,21 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Union(
|
|||||||
if (bitset != BitsetType::kNone) {
|
if (bitset != BitsetType::kNone) {
|
||||||
unioned->Set(size++, BitsetType::New(bitset, region));
|
unioned->Set(size++, BitsetType::New(bitset, region));
|
||||||
}
|
}
|
||||||
size = ExtendUnion(unioned, type1, size);
|
size = ExtendUnion(unioned, size, type1, type2, false, region);
|
||||||
size = ExtendUnion(unioned, type2, size);
|
size = ExtendUnion(unioned, size, type2, type1, false, region);
|
||||||
|
size = NormalizeUnion(unioned, size, bitset);
|
||||||
|
|
||||||
if (size == 1) {
|
if (size == 1) {
|
||||||
return unioned->Get(0);
|
return unioned->Get(0);
|
||||||
} else {
|
} else {
|
||||||
unioned->Shrink(size);
|
unioned->Shrink(size);
|
||||||
|
ASSERT(unioned->Wellformed());
|
||||||
return unioned;
|
return unioned;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Get non-bitsets from type which are also in other, store at result,
|
// Intersection is O(1) on simple bitsets, but O(n*m) on structured unions.
|
||||||
// starting at index. Returns updated index.
|
|
||||||
template<class Config>
|
|
||||||
int TypeImpl<Config>::ExtendIntersection(
|
|
||||||
UnionHandle result, TypeHandle type, TypeHandle other, int current_size) {
|
|
||||||
int old_size = current_size;
|
|
||||||
if (type->IsUnion()) {
|
|
||||||
UnionHandle unioned = handle(type->AsUnion());
|
|
||||||
for (int i = 0; i < unioned->Length(); ++i) {
|
|
||||||
TypeHandle type = unioned->Get(i);
|
|
||||||
ASSERT(i == 0 || !(type->IsBitset() || type->Is(unioned->Get(0))));
|
|
||||||
if (!type->IsBitset() && type->Is(other) &&
|
|
||||||
!type->InUnion(result, old_size)) {
|
|
||||||
result->Set(current_size++, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (!type->IsBitset()) {
|
|
||||||
// For all structural types, subtyping implies equivalence.
|
|
||||||
ASSERT(type->IsClass() || type->IsConstant() ||
|
|
||||||
type->IsArray() || type->IsFunction() || type->IsContext());
|
|
||||||
if (type->Is(other) && !type->InUnion(result, old_size)) {
|
|
||||||
result->Set(current_size++, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return current_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Intersection is O(1) on simple bit unions, but O(n*m) on structured unions.
|
|
||||||
template<class Config>
|
template<class Config>
|
||||||
typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Intersect(
|
typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Intersect(
|
||||||
TypeHandle type1, TypeHandle type2, Region* region) {
|
TypeHandle type1, TypeHandle type2, Region* region) {
|
||||||
@ -622,8 +669,9 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Intersect(
|
|||||||
if (bitset != BitsetType::kNone) {
|
if (bitset != BitsetType::kNone) {
|
||||||
unioned->Set(size++, BitsetType::New(bitset, region));
|
unioned->Set(size++, BitsetType::New(bitset, region));
|
||||||
}
|
}
|
||||||
size = ExtendIntersection(unioned, type1, type2, size);
|
size = ExtendUnion(unioned, size, type1, type2, true, region);
|
||||||
size = ExtendIntersection(unioned, type2, type1, size);
|
size = ExtendUnion(unioned, size, type2, type1, true, region);
|
||||||
|
size = NormalizeUnion(unioned, size, bitset);
|
||||||
|
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
return None(region);
|
return None(region);
|
||||||
@ -631,11 +679,118 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Intersect(
|
|||||||
return unioned->Get(0);
|
return unioned->Get(0);
|
||||||
} else {
|
} else {
|
||||||
unioned->Shrink(size);
|
unioned->Shrink(size);
|
||||||
|
ASSERT(unioned->Wellformed());
|
||||||
return unioned;
|
return unioned;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Iteration.
|
||||||
|
|
||||||
|
template<class Config>
|
||||||
|
int TypeImpl<Config>::NumClasses() {
|
||||||
|
DisallowHeapAllocation no_allocation;
|
||||||
|
if (this->IsClass()) {
|
||||||
|
return 1;
|
||||||
|
} else if (this->IsUnion()) {
|
||||||
|
UnionHandle unioned = handle(this->AsUnion());
|
||||||
|
int result = 0;
|
||||||
|
for (int i = 0; i < unioned->Length(); ++i) {
|
||||||
|
if (unioned->Get(i)->IsClass()) ++result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class Config>
|
||||||
|
int TypeImpl<Config>::NumConstants() {
|
||||||
|
DisallowHeapAllocation no_allocation;
|
||||||
|
if (this->IsConstant()) {
|
||||||
|
return 1;
|
||||||
|
} else if (this->IsUnion()) {
|
||||||
|
UnionHandle unioned = handle(this->AsUnion());
|
||||||
|
int result = 0;
|
||||||
|
for (int i = 0; i < unioned->Length(); ++i) {
|
||||||
|
if (unioned->Get(i)->IsConstant()) ++result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class Config> template<class T>
|
||||||
|
typename TypeImpl<Config>::TypeHandle
|
||||||
|
TypeImpl<Config>::Iterator<T>::get_type() {
|
||||||
|
ASSERT(!Done());
|
||||||
|
return type_->IsUnion() ? type_->AsUnion()->Get(index_) : type_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// C++ cannot specialise nested templates, so we have to go through this
|
||||||
|
// contortion with an auxiliary template to simulate it.
|
||||||
|
template<class Config, class T>
|
||||||
|
struct TypeImplIteratorAux {
|
||||||
|
static bool matches(typename TypeImpl<Config>::TypeHandle type);
|
||||||
|
static i::Handle<T> current(typename TypeImpl<Config>::TypeHandle type);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Config>
|
||||||
|
struct TypeImplIteratorAux<Config, i::Map> {
|
||||||
|
static bool matches(typename TypeImpl<Config>::TypeHandle type) {
|
||||||
|
return type->IsClass();
|
||||||
|
}
|
||||||
|
static i::Handle<i::Map> current(typename TypeImpl<Config>::TypeHandle type) {
|
||||||
|
return type->AsClass()->Map();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Config>
|
||||||
|
struct TypeImplIteratorAux<Config, i::Object> {
|
||||||
|
static bool matches(typename TypeImpl<Config>::TypeHandle type) {
|
||||||
|
return type->IsConstant();
|
||||||
|
}
|
||||||
|
static i::Handle<i::Object> current(
|
||||||
|
typename TypeImpl<Config>::TypeHandle type) {
|
||||||
|
return type->AsConstant()->Value();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Config> template<class T>
|
||||||
|
bool TypeImpl<Config>::Iterator<T>::matches(TypeHandle type) {
|
||||||
|
return TypeImplIteratorAux<Config, T>::matches(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Config> template<class T>
|
||||||
|
i::Handle<T> TypeImpl<Config>::Iterator<T>::Current() {
|
||||||
|
return TypeImplIteratorAux<Config, T>::current(get_type());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class Config> template<class T>
|
||||||
|
void TypeImpl<Config>::Iterator<T>::Advance() {
|
||||||
|
DisallowHeapAllocation no_allocation;
|
||||||
|
++index_;
|
||||||
|
if (type_->IsUnion()) {
|
||||||
|
UnionHandle unioned = handle(type_->AsUnion());
|
||||||
|
for (; index_ < unioned->Length(); ++index_) {
|
||||||
|
if (matches(unioned->Get(index_))) return;
|
||||||
|
}
|
||||||
|
} else if (index_ == 0 && matches(type_)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
index_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Conversion between low-level representations.
|
||||||
|
|
||||||
template<class Config>
|
template<class Config>
|
||||||
template<class OtherType>
|
template<class OtherType>
|
||||||
typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Convert(
|
typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Convert(
|
||||||
@ -643,9 +798,13 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Convert(
|
|||||||
if (type->IsBitset()) {
|
if (type->IsBitset()) {
|
||||||
return BitsetType::New(type->AsBitset(), region);
|
return BitsetType::New(type->AsBitset(), region);
|
||||||
} else if (type->IsClass()) {
|
} else if (type->IsClass()) {
|
||||||
return ClassType::New(type->AsClass()->Map(), region);
|
return ClassType::New(
|
||||||
|
type->AsClass()->Map(),
|
||||||
|
BitsetType::New(type->BitsetLub(), region), region);
|
||||||
} else if (type->IsConstant()) {
|
} else if (type->IsConstant()) {
|
||||||
return ConstantType::New(type->AsConstant()->Value(), region);
|
return ConstantType::New(
|
||||||
|
type->AsConstant()->Value(),
|
||||||
|
Convert<OtherType>(type->AsConstant()->Bound(), region), region);
|
||||||
} else if (type->IsContext()) {
|
} else if (type->IsContext()) {
|
||||||
TypeHandle outer = Convert<OtherType>(type->AsContext()->Outer(), region);
|
TypeHandle outer = Convert<OtherType>(type->AsContext()->Outer(), region);
|
||||||
return ContextType::New(outer, region);
|
return ContextType::New(outer, region);
|
||||||
@ -658,11 +817,13 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Convert(
|
|||||||
return unioned;
|
return unioned;
|
||||||
} else if (type->IsArray()) {
|
} else if (type->IsArray()) {
|
||||||
return ArrayType::New(
|
return ArrayType::New(
|
||||||
Convert<OtherType>(type->AsArray()->Element(), region), region);
|
Convert<OtherType>(type->AsArray()->Element(), region),
|
||||||
|
Convert<OtherType>(type->AsArray()->Bound(), region), region);
|
||||||
} else if (type->IsFunction()) {
|
} else if (type->IsFunction()) {
|
||||||
FunctionHandle function = FunctionType::New(
|
FunctionHandle function = FunctionType::New(
|
||||||
Convert<OtherType>(type->AsFunction()->Result(), region),
|
Convert<OtherType>(type->AsFunction()->Result(), region),
|
||||||
Convert<OtherType>(type->AsFunction()->Receiver(), region),
|
Convert<OtherType>(type->AsFunction()->Receiver(), region),
|
||||||
|
Convert<OtherType>(type->AsFunction()->Bound(), region),
|
||||||
type->AsFunction()->Arity(), region);
|
type->AsFunction()->Arity(), region);
|
||||||
for (int i = 0; i < function->Arity(); ++i) {
|
for (int i = 0; i < function->Arity(); ++i) {
|
||||||
function->InitParameter(i,
|
function->InitParameter(i,
|
||||||
@ -676,16 +837,8 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Convert(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO(rossberg): this does not belong here.
|
// -----------------------------------------------------------------------------
|
||||||
Representation Representation::FromType(Type* type) {
|
// Printing.
|
||||||
DisallowHeapAllocation no_allocation;
|
|
||||||
if (type->Is(Type::None())) return Representation::None();
|
|
||||||
if (type->Is(Type::SignedSmall())) return Representation::Smi();
|
|
||||||
if (type->Is(Type::Signed32())) return Representation::Integer32();
|
|
||||||
if (type->Is(Type::Number())) return Representation::Double();
|
|
||||||
return Representation::Tagged();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<class Config>
|
template<class Config>
|
||||||
const char* TypeImpl<Config>::BitsetType::Name(int bitset) {
|
const char* TypeImpl<Config>::BitsetType::Name(int bitset) {
|
||||||
@ -744,61 +897,59 @@ void TypeImpl<Config>::BitsetType::PrintTo(StringStream* stream, int bitset) {
|
|||||||
template<class Config>
|
template<class Config>
|
||||||
void TypeImpl<Config>::PrintTo(StringStream* stream, PrintDimension dim) {
|
void TypeImpl<Config>::PrintTo(StringStream* stream, PrintDimension dim) {
|
||||||
DisallowHeapAllocation no_allocation;
|
DisallowHeapAllocation no_allocation;
|
||||||
if (this->IsBitset()) {
|
if (dim != REPRESENTATION_DIM) {
|
||||||
int bitset = this->AsBitset();
|
if (this->IsBitset()) {
|
||||||
switch (dim) {
|
BitsetType::PrintTo(stream, SEMANTIC(this->AsBitset()));
|
||||||
case BOTH_DIMS:
|
} else if (this->IsClass()) {
|
||||||
BitsetType::PrintTo(stream, SEMANTIC(bitset));
|
stream->Add("Class(%p < ", static_cast<void*>(*this->AsClass()->Map()));
|
||||||
stream->Add("/");
|
BitsetType::New(BitsetType::Lub(this))->PrintTo(stream, dim);
|
||||||
BitsetType::PrintTo(stream, REPRESENTATION(bitset));
|
stream->Add(")");
|
||||||
break;
|
return;
|
||||||
case SEMANTIC_DIM:
|
} else if (this->IsConstant()) {
|
||||||
BitsetType::PrintTo(stream, SEMANTIC(bitset));
|
stream->Add("Constant(%p : ",
|
||||||
break;
|
static_cast<void*>(*this->AsConstant()->Value()));
|
||||||
case REPRESENTATION_DIM:
|
BitsetType::New(BitsetType::Lub(this))->PrintTo(stream, dim);
|
||||||
BitsetType::PrintTo(stream, REPRESENTATION(bitset));
|
stream->Add(")");
|
||||||
break;
|
return;
|
||||||
|
} else if (this->IsContext()) {
|
||||||
|
stream->Add("Context(");
|
||||||
|
this->AsContext()->Outer()->PrintTo(stream, dim);
|
||||||
|
stream->Add(")");
|
||||||
|
} else if (this->IsUnion()) {
|
||||||
|
stream->Add("(");
|
||||||
|
UnionHandle unioned = handle(this->AsUnion());
|
||||||
|
for (int i = 0; i < unioned->Length(); ++i) {
|
||||||
|
TypeHandle type_i = unioned->Get(i);
|
||||||
|
if (i > 0) stream->Add(" | ");
|
||||||
|
type_i->PrintTo(stream, dim);
|
||||||
|
}
|
||||||
|
stream->Add(")");
|
||||||
|
return;
|
||||||
|
} else if (this->IsArray()) {
|
||||||
|
stream->Add("Array(");
|
||||||
|
AsArray()->Element()->PrintTo(stream, dim);
|
||||||
|
stream->Add(")");
|
||||||
|
} else if (this->IsFunction()) {
|
||||||
|
if (!this->AsFunction()->Receiver()->IsAny()) {
|
||||||
|
this->AsFunction()->Receiver()->PrintTo(stream, dim);
|
||||||
|
stream->Add(".");
|
||||||
|
}
|
||||||
|
stream->Add("(");
|
||||||
|
for (int i = 0; i < this->AsFunction()->Arity(); ++i) {
|
||||||
|
if (i > 0) stream->Add(", ");
|
||||||
|
this->AsFunction()->Parameter(i)->PrintTo(stream, dim);
|
||||||
|
}
|
||||||
|
stream->Add(")->");
|
||||||
|
this->AsFunction()->Result()->PrintTo(stream, dim);
|
||||||
|
} else {
|
||||||
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
} else if (this->IsConstant()) {
|
}
|
||||||
stream->Add("Constant(%p : ",
|
if (dim == BOTH_DIMS) {
|
||||||
static_cast<void*>(*this->AsConstant()->Value()));
|
stream->Add("/");
|
||||||
BitsetType::New(BitsetType::Lub(this))->PrintTo(stream, dim);
|
}
|
||||||
stream->Add(")");
|
if (dim != SEMANTIC_DIM) {
|
||||||
} else if (this->IsClass()) {
|
BitsetType::PrintTo(stream, REPRESENTATION(this->BitsetLub()));
|
||||||
stream->Add("Class(%p < ", static_cast<void*>(*this->AsClass()->Map()));
|
|
||||||
BitsetType::New(BitsetType::Lub(this))->PrintTo(stream, dim);
|
|
||||||
stream->Add(")");
|
|
||||||
} else if (this->IsContext()) {
|
|
||||||
stream->Add("Context(");
|
|
||||||
this->AsContext()->Outer()->PrintTo(stream, dim);
|
|
||||||
stream->Add(")");
|
|
||||||
} else if (this->IsUnion()) {
|
|
||||||
stream->Add("(");
|
|
||||||
UnionHandle unioned = handle(this->AsUnion());
|
|
||||||
for (int i = 0; i < unioned->Length(); ++i) {
|
|
||||||
TypeHandle type_i = unioned->Get(i);
|
|
||||||
if (i > 0) stream->Add(" | ");
|
|
||||||
type_i->PrintTo(stream, dim);
|
|
||||||
}
|
|
||||||
stream->Add(")");
|
|
||||||
} else if (this->IsArray()) {
|
|
||||||
stream->Add("[");
|
|
||||||
AsArray()->Element()->PrintTo(stream, dim);
|
|
||||||
stream->Add("]");
|
|
||||||
} else if (this->IsFunction()) {
|
|
||||||
if (!this->AsFunction()->Receiver()->IsAny()) {
|
|
||||||
this->AsFunction()->Receiver()->PrintTo(stream, dim);
|
|
||||||
stream->Add(".");
|
|
||||||
}
|
|
||||||
stream->Add("(");
|
|
||||||
for (int i = 0; i < this->AsFunction()->Arity(); ++i) {
|
|
||||||
if (i > 0) stream->Add(", ");
|
|
||||||
this->AsFunction()->Parameter(i)->PrintTo(stream, dim);
|
|
||||||
}
|
|
||||||
stream->Add(")->");
|
|
||||||
this->AsFunction()->Result()->PrintTo(stream, dim);
|
|
||||||
} else {
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -820,6 +971,9 @@ void TypeImpl<Config>::TypePrint(PrintDimension dim) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Instantiations.
|
||||||
|
|
||||||
template class TypeImpl<ZoneTypeConfig>;
|
template class TypeImpl<ZoneTypeConfig>;
|
||||||
template class TypeImpl<ZoneTypeConfig>::Iterator<i::Map>;
|
template class TypeImpl<ZoneTypeConfig>::Iterator<i::Map>;
|
||||||
template class TypeImpl<ZoneTypeConfig>::Iterator<i::Object>;
|
template class TypeImpl<ZoneTypeConfig>::Iterator<i::Object>;
|
||||||
|
382
src/types.h
382
src/types.h
@ -132,6 +132,9 @@ namespace internal {
|
|||||||
// them. For zone types, no query method touches the heap, only constructors do.
|
// them. For zone types, no query method touches the heap, only constructors do.
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Values for bitset types
|
||||||
|
|
||||||
#define MASK_BITSET_TYPE_LIST(V) \
|
#define MASK_BITSET_TYPE_LIST(V) \
|
||||||
V(Representation, static_cast<int>(0xffc00000)) \
|
V(Representation, static_cast<int>(0xffc00000)) \
|
||||||
V(Semantic, static_cast<int>(0x003fffff))
|
V(Semantic, static_cast<int>(0x003fffff))
|
||||||
@ -207,6 +210,9 @@ namespace internal {
|
|||||||
SEMANTIC_BITSET_TYPE_LIST(V)
|
SEMANTIC_BITSET_TYPE_LIST(V)
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// The abstract Type class, parameterized over the low-level representation.
|
||||||
|
|
||||||
// struct Config {
|
// struct Config {
|
||||||
// typedef TypeImpl<Config> Type;
|
// typedef TypeImpl<Config> Type;
|
||||||
// typedef Base;
|
// typedef Base;
|
||||||
@ -217,16 +223,13 @@ namespace internal {
|
|||||||
// template<class T> static Handle<T>::type cast(Handle<Type>::type);
|
// template<class T> static Handle<T>::type cast(Handle<Type>::type);
|
||||||
// static bool is_bitset(Type*);
|
// static bool is_bitset(Type*);
|
||||||
// static bool is_class(Type*);
|
// static bool is_class(Type*);
|
||||||
// static bool is_constant(Type*);
|
|
||||||
// static bool is_struct(Type*, int tag);
|
// static bool is_struct(Type*, int tag);
|
||||||
// static int as_bitset(Type*);
|
// static int as_bitset(Type*);
|
||||||
// static i::Handle<i::Map> as_class(Type*);
|
// static i::Handle<i::Map> as_class(Type*);
|
||||||
// static i::Handle<i::Object> as_constant(Type*);
|
|
||||||
// static Handle<Struct>::type as_struct(Type*);
|
// static Handle<Struct>::type as_struct(Type*);
|
||||||
// static Type* from_bitset(int bitset);
|
// static Type* from_bitset(int bitset);
|
||||||
// static Handle<Type>::type from_bitset(int bitset, Region*);
|
// static Handle<Type>::type from_bitset(int bitset, Region*);
|
||||||
// static Handle<Type>::type from_class(i::Handle<Map>, int lub, Region*);
|
// static Handle<Type>::type from_class(i::Handle<Map>, Region*);
|
||||||
// static Handle<Type>::type from_constant(i::Handle<Object>, int, Region*);
|
|
||||||
// static Handle<Type>::type from_struct(Handle<Struct>::type, int tag);
|
// static Handle<Type>::type from_struct(Handle<Struct>::type, int tag);
|
||||||
// static Handle<Struct>::type struct_create(int tag, int length, Region*);
|
// static Handle<Struct>::type struct_create(int tag, int length, Region*);
|
||||||
// static void struct_shrink(Handle<Struct>::type, int length);
|
// static void struct_shrink(Handle<Struct>::type, int length);
|
||||||
@ -234,11 +237,16 @@ namespace internal {
|
|||||||
// static int struct_length(Handle<Struct>::type);
|
// static int struct_length(Handle<Struct>::type);
|
||||||
// static Handle<Type>::type struct_get(Handle<Struct>::type, int);
|
// static Handle<Type>::type struct_get(Handle<Struct>::type, int);
|
||||||
// static void struct_set(Handle<Struct>::type, int, Handle<Type>::type);
|
// static void struct_set(Handle<Struct>::type, int, Handle<Type>::type);
|
||||||
// static int lub_bitset(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>);
|
||||||
// }
|
// }
|
||||||
template<class Config>
|
template<class Config>
|
||||||
class TypeImpl : public Config::Base {
|
class TypeImpl : public Config::Base {
|
||||||
public:
|
public:
|
||||||
|
// Auxiliary types.
|
||||||
|
|
||||||
class BitsetType; // Internal
|
class BitsetType; // Internal
|
||||||
class StructuralType; // Internal
|
class StructuralType; // Internal
|
||||||
class UnionType; // Internal
|
class UnionType; // Internal
|
||||||
@ -258,6 +266,8 @@ class TypeImpl : public Config::Base {
|
|||||||
typedef typename Config::template Handle<UnionType>::type UnionHandle;
|
typedef typename Config::template Handle<UnionType>::type UnionHandle;
|
||||||
typedef typename Config::Region Region;
|
typedef typename Config::Region Region;
|
||||||
|
|
||||||
|
// Constructors.
|
||||||
|
|
||||||
#define DEFINE_TYPE_CONSTRUCTOR(type, value) \
|
#define DEFINE_TYPE_CONSTRUCTOR(type, value) \
|
||||||
static TypeImpl* type() { return BitsetType::New(BitsetType::k##type); } \
|
static TypeImpl* type() { return BitsetType::New(BitsetType::k##type); } \
|
||||||
static TypeHandle type(Region* region) { \
|
static TypeHandle type(Region* region) { \
|
||||||
@ -321,9 +331,9 @@ class TypeImpl : public Config::Base {
|
|||||||
return Of(*value, region);
|
return Of(*value, region);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsInhabited() {
|
// Predicates.
|
||||||
return !this->IsBitset() || BitsetType::IsInhabited(this->AsBitset());
|
|
||||||
}
|
bool IsInhabited() { return BitsetType::IsInhabited(this->BitsetLub()); }
|
||||||
|
|
||||||
bool Is(TypeImpl* that) { return this == that || this->SlowIs(that); }
|
bool Is(TypeImpl* that) { return this == that || this->SlowIs(that); }
|
||||||
template<class TypeHandle>
|
template<class TypeHandle>
|
||||||
@ -341,9 +351,9 @@ class TypeImpl : public Config::Base {
|
|||||||
bool Contains(i::Object* val);
|
bool Contains(i::Object* val);
|
||||||
bool Contains(i::Handle<i::Object> val) { return this->Contains(*val); }
|
bool Contains(i::Handle<i::Object> val) { return this->Contains(*val); }
|
||||||
|
|
||||||
// State-dependent versions of Of and Is that consider subtyping between
|
// State-dependent versions of the above that consider subtyping between
|
||||||
// a constant and its map class.
|
// a constant and its map class.
|
||||||
static TypeHandle NowOf(i::Object* value, Region* region);
|
inline static TypeHandle NowOf(i::Object* value, Region* region);
|
||||||
static TypeHandle NowOf(i::Handle<i::Object> value, Region* region) {
|
static TypeHandle NowOf(i::Handle<i::Object> value, Region* region) {
|
||||||
return NowOf(*value, region);
|
return NowOf(*value, region);
|
||||||
}
|
}
|
||||||
@ -355,12 +365,21 @@ class TypeImpl : public Config::Base {
|
|||||||
|
|
||||||
bool NowStable();
|
bool NowStable();
|
||||||
|
|
||||||
bool IsClass() { return Config::is_class(this); }
|
// Inspection.
|
||||||
bool IsConstant() { return Config::is_constant(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() {
|
bool IsContext() {
|
||||||
return Config::is_struct(this, StructuralType::kContextTag);
|
return Config::is_struct(this, StructuralType::kContextTag);
|
||||||
}
|
}
|
||||||
bool IsArray() { return Config::is_struct(this, StructuralType::kArrayTag); }
|
bool IsArray() {
|
||||||
|
return Config::is_struct(this, StructuralType::kArrayTag);
|
||||||
|
}
|
||||||
bool IsFunction() {
|
bool IsFunction() {
|
||||||
return Config::is_struct(this, StructuralType::kFunctionTag);
|
return Config::is_struct(this, StructuralType::kFunctionTag);
|
||||||
}
|
}
|
||||||
@ -384,25 +403,37 @@ class TypeImpl : public Config::Base {
|
|||||||
return Iterator<i::Object>(Config::handle(this));
|
return Iterator<i::Object>(Config::handle(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Casting and conversion.
|
||||||
|
|
||||||
static inline TypeImpl* cast(typename Config::Base* object);
|
static inline TypeImpl* cast(typename Config::Base* object);
|
||||||
|
|
||||||
template<class OtherTypeImpl>
|
template<class OtherTypeImpl>
|
||||||
static TypeHandle Convert(
|
static TypeHandle Convert(
|
||||||
typename OtherTypeImpl::TypeHandle type, Region* region);
|
typename OtherTypeImpl::TypeHandle type, Region* region);
|
||||||
|
|
||||||
|
// Printing.
|
||||||
|
|
||||||
enum PrintDimension { BOTH_DIMS, SEMANTIC_DIM, REPRESENTATION_DIM };
|
enum PrintDimension { BOTH_DIMS, SEMANTIC_DIM, REPRESENTATION_DIM };
|
||||||
|
|
||||||
void PrintTo(StringStream* stream, PrintDimension = BOTH_DIMS);
|
void PrintTo(StringStream* stream, PrintDimension = BOTH_DIMS);
|
||||||
void TypePrint(PrintDimension = BOTH_DIMS);
|
void TypePrint(PrintDimension = BOTH_DIMS);
|
||||||
void TypePrint(FILE* out, PrintDimension = BOTH_DIMS);
|
void TypePrint(FILE* out, PrintDimension = BOTH_DIMS);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// Friends.
|
||||||
|
|
||||||
template<class> friend class Iterator;
|
template<class> friend class Iterator;
|
||||||
template<class> friend class TypeImpl;
|
template<class> friend class TypeImpl;
|
||||||
|
|
||||||
|
// Handle conversion.
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
static typename Config::template Handle<T>::type handle(T* type) {
|
static typename Config::template Handle<T>::type handle(T* type) {
|
||||||
return Config::handle(type);
|
return Config::handle(type);
|
||||||
}
|
}
|
||||||
|
TypeImpl* unhandle() { return this; }
|
||||||
|
|
||||||
|
// Internal inspection.
|
||||||
|
|
||||||
bool IsNone() { return this == None(); }
|
bool IsNone() { return this == None(); }
|
||||||
bool IsAny() { return this == Any(); }
|
bool IsAny() { return this == Any(); }
|
||||||
@ -415,19 +446,27 @@ class TypeImpl : public Config::Base {
|
|||||||
}
|
}
|
||||||
UnionType* AsUnion() { return UnionType::cast(this); }
|
UnionType* AsUnion() { return UnionType::cast(this); }
|
||||||
|
|
||||||
bool SlowIs(TypeImpl* that);
|
// Auxiliary functions.
|
||||||
|
|
||||||
bool InUnion(UnionHandle unioned, int current_size);
|
|
||||||
static int ExtendUnion(
|
|
||||||
UnionHandle unioned, TypeHandle t, int current_size);
|
|
||||||
static int ExtendIntersection(
|
|
||||||
UnionHandle unioned, TypeHandle t, TypeHandle other, int current_size);
|
|
||||||
|
|
||||||
int BitsetGlb() { return BitsetType::Glb(this); }
|
int BitsetGlb() { return BitsetType::Glb(this); }
|
||||||
int BitsetLub() { return BitsetType::Lub(this); }
|
int BitsetLub() { return BitsetType::Lub(this); }
|
||||||
|
int InherentBitsetLub() { return BitsetType::InherentLub(this); }
|
||||||
|
|
||||||
|
bool SlowIs(TypeImpl* that);
|
||||||
|
|
||||||
|
TypeHandle Narrow(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 int NormalizeUnion(UnionHandle unioned, int current_size, int bitset);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Bitset types (internal).
|
||||||
|
|
||||||
template<class Config>
|
template<class Config>
|
||||||
class TypeImpl<Config>::BitsetType : public TypeImpl<Config> {
|
class TypeImpl<Config>::BitsetType : public TypeImpl<Config> {
|
||||||
private:
|
private:
|
||||||
@ -442,7 +481,7 @@ class TypeImpl<Config>::BitsetType : public TypeImpl<Config> {
|
|||||||
|
|
||||||
int Bitset() { return Config::as_bitset(this); }
|
int Bitset() { return Config::as_bitset(this); }
|
||||||
|
|
||||||
static BitsetType* New(int bitset) {
|
static TypeImpl* New(int bitset) {
|
||||||
return static_cast<BitsetType*>(Config::from_bitset(bitset));
|
return static_cast<BitsetType*>(Config::from_bitset(bitset));
|
||||||
}
|
}
|
||||||
static TypeHandle New(int bitset, Region* region) {
|
static TypeHandle New(int bitset, Region* region) {
|
||||||
@ -460,6 +499,7 @@ class TypeImpl<Config>::BitsetType : public TypeImpl<Config> {
|
|||||||
static int Lub(int32_t value);
|
static int Lub(int32_t value);
|
||||||
static int Lub(uint32_t value);
|
static int Lub(uint32_t value);
|
||||||
static int Lub(i::Map* map);
|
static int Lub(i::Map* map);
|
||||||
|
static int InherentLub(TypeImpl* type);
|
||||||
|
|
||||||
static const char* Name(int bitset);
|
static const char* Name(int bitset);
|
||||||
static void PrintTo(StringStream* stream, int bitset);
|
static void PrintTo(StringStream* stream, int bitset);
|
||||||
@ -467,8 +507,10 @@ class TypeImpl<Config>::BitsetType : public TypeImpl<Config> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Internal
|
// -----------------------------------------------------------------------------
|
||||||
// A structured type contains a tag and a variable number of type fields.
|
// Superclass for non-bitset types (internal).
|
||||||
|
// Contains a tag and a variable number of type or value fields.
|
||||||
|
|
||||||
template<class Config>
|
template<class Config>
|
||||||
class TypeImpl<Config>::StructuralType : public TypeImpl<Config> {
|
class TypeImpl<Config>::StructuralType : public TypeImpl<Config> {
|
||||||
protected:
|
protected:
|
||||||
@ -489,79 +531,40 @@ class TypeImpl<Config>::StructuralType : public TypeImpl<Config> {
|
|||||||
return Config::struct_length(Config::as_struct(this));
|
return Config::struct_length(Config::as_struct(this));
|
||||||
}
|
}
|
||||||
TypeHandle Get(int i) {
|
TypeHandle Get(int i) {
|
||||||
|
ASSERT(0 <= i && i < this->Length());
|
||||||
return Config::struct_get(Config::as_struct(this), i);
|
return Config::struct_get(Config::as_struct(this), i);
|
||||||
}
|
}
|
||||||
void Set(int i, TypeHandle type) {
|
void Set(int i, TypeHandle type) {
|
||||||
|
ASSERT(0 <= i && i < this->Length());
|
||||||
Config::struct_set(Config::as_struct(this), i, type);
|
Config::struct_set(Config::as_struct(this), i, type);
|
||||||
}
|
}
|
||||||
void Shrink(int length) {
|
void Shrink(int length) {
|
||||||
|
ASSERT(2 <= length && length <= this->Length());
|
||||||
Config::struct_shrink(Config::as_struct(this), length);
|
Config::struct_shrink(Config::as_struct(this), length);
|
||||||
}
|
}
|
||||||
|
template<class V> i::Handle<V> GetValue(int i) {
|
||||||
|
ASSERT(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) {
|
||||||
|
ASSERT(0 <= i && i < this->Length());
|
||||||
|
Config::struct_set_value(Config::as_struct(this), i, x);
|
||||||
|
}
|
||||||
|
|
||||||
static TypeHandle New(Tag tag, int length, Region* region) {
|
static TypeHandle New(Tag tag, int length, Region* region) {
|
||||||
|
ASSERT(1 <= length);
|
||||||
return Config::from_struct(Config::struct_create(tag, length, region));
|
return Config::from_struct(Config::struct_create(tag, length, region));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template<class Config>
|
// -----------------------------------------------------------------------------
|
||||||
class TypeImpl<Config>::ClassType : public TypeImpl<Config> {
|
// Union types (internal).
|
||||||
public:
|
|
||||||
i::Handle<i::Map> Map() { return Config::as_class(this); }
|
|
||||||
|
|
||||||
static ClassHandle New(i::Handle<i::Map> map, Region* region) {
|
|
||||||
return Config::template cast<ClassType>(
|
|
||||||
Config::from_class(map, BitsetType::Lub(*map), region));
|
|
||||||
}
|
|
||||||
|
|
||||||
static ClassType* cast(TypeImpl* type) {
|
|
||||||
ASSERT(type->IsClass());
|
|
||||||
return static_cast<ClassType*>(type);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template<class Config>
|
|
||||||
class TypeImpl<Config>::ConstantType : public TypeImpl<Config> {
|
|
||||||
public:
|
|
||||||
i::Handle<i::Object> Value() { return Config::as_constant(this); }
|
|
||||||
|
|
||||||
static ConstantHandle New(i::Handle<i::Object> value, Region* region) {
|
|
||||||
return Config::template cast<ConstantType>(
|
|
||||||
Config::from_constant(value, BitsetType::Lub(*value), region));
|
|
||||||
}
|
|
||||||
|
|
||||||
static ConstantType* cast(TypeImpl* type) {
|
|
||||||
ASSERT(type->IsConstant());
|
|
||||||
return static_cast<ConstantType*>(type);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
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) {
|
|
||||||
ASSERT(type->IsContext());
|
|
||||||
return static_cast<ContextType*>(type);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Internal
|
|
||||||
// A union is a structured type with the following invariants:
|
// A union is a structured type with the following invariants:
|
||||||
// - its length is at least 2
|
// - its length is at least 2
|
||||||
// - at most one field is a bitset, and it must go into index 0
|
// - at most one field is a bitset, and it must go into index 0
|
||||||
// - no field is a union
|
// - no field is a union
|
||||||
|
// - no field is a subtype of any other field
|
||||||
template<class Config>
|
template<class Config>
|
||||||
class TypeImpl<Config>::UnionType : public StructuralType {
|
class TypeImpl<Config>::UnionType : public StructuralType {
|
||||||
public:
|
public:
|
||||||
@ -574,19 +577,136 @@ class TypeImpl<Config>::UnionType : public StructuralType {
|
|||||||
ASSERT(type->IsUnion());
|
ASSERT(type->IsUnion());
|
||||||
return static_cast<UnionType*>(type);
|
return static_cast<UnionType*>(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Wellformed();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Class types.
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
ClassHandle type = Config::template cast<ClassType>(
|
||||||
|
StructuralType::New(StructuralType::kClassTag, 2, region));
|
||||||
|
type->Set(0, bound);
|
||||||
|
type->SetValue(1, map);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ClassType* cast(TypeImpl* type) {
|
||||||
|
ASSERT(type->IsClass());
|
||||||
|
return static_cast<ClassType*>(type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Constant types.
|
||||||
|
|
||||||
|
template<class Config>
|
||||||
|
class TypeImpl<Config>::ConstantType : public StructuralType {
|
||||||
|
public:
|
||||||
|
TypeHandle Bound() { return this->Get(0); }
|
||||||
|
i::Handle<i::Object> Value() { return this->template GetValue<i::Object>(1); }
|
||||||
|
|
||||||
|
static ConstantHandle New(
|
||||||
|
i::Handle<i::Object> value, TypeHandle bound, Region* region) {
|
||||||
|
ConstantHandle type = Config::template cast<ConstantType>(
|
||||||
|
StructuralType::New(StructuralType::kConstantTag, 2, region));
|
||||||
|
type->Set(0, bound);
|
||||||
|
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) {
|
||||||
|
ASSERT(type->IsConstant());
|
||||||
|
return static_cast<ConstantType*>(type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Context types.
|
||||||
|
|
||||||
|
template<class Config>
|
||||||
|
class TypeImpl<Config>::ContextType : public StructuralType {
|
||||||
|
public:
|
||||||
|
TypeHandle Bound() { return this->Get(0); }
|
||||||
|
TypeHandle Outer() { return this->Get(1); }
|
||||||
|
|
||||||
|
static ContextHandle New(TypeHandle outer, TypeHandle bound, Region* region) {
|
||||||
|
ContextHandle type = Config::template cast<ContextType>(
|
||||||
|
StructuralType::New(StructuralType::kContextTag, 2, region));
|
||||||
|
type->Set(0, bound);
|
||||||
|
type->Set(1, 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) {
|
||||||
|
ASSERT(type->IsContext());
|
||||||
|
return static_cast<ContextType*>(type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Array types.
|
||||||
|
|
||||||
template<class Config>
|
template<class Config>
|
||||||
class TypeImpl<Config>::ArrayType : public StructuralType {
|
class TypeImpl<Config>::ArrayType : public StructuralType {
|
||||||
public:
|
public:
|
||||||
TypeHandle Element() { return this->Get(0); }
|
TypeHandle Bound() { return this->Get(0); }
|
||||||
|
TypeHandle Element() { return this->Get(1); }
|
||||||
|
|
||||||
|
static ArrayHandle New(TypeHandle element, TypeHandle bound, Region* region) {
|
||||||
|
ASSERT(SEMANTIC(bound->AsBitset()) == SEMANTIC(BitsetType::kArray));
|
||||||
|
ArrayHandle type = Config::template cast<ArrayType>(
|
||||||
|
StructuralType::New(StructuralType::kArrayTag, 2, region));
|
||||||
|
type->Set(0, bound);
|
||||||
|
type->Set(1, element);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
static ArrayHandle New(TypeHandle element, Region* region) {
|
static ArrayHandle New(TypeHandle element, Region* region) {
|
||||||
ArrayHandle type = Config::template cast<ArrayType>(
|
TypeHandle bound = BitsetType::New(BitsetType::kArray, region);
|
||||||
StructuralType::New(StructuralType::kArrayTag, 1, region));
|
return New(element, bound, region);
|
||||||
type->Set(0, element);
|
|
||||||
return type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ArrayType* cast(TypeImpl* type) {
|
static ArrayType* cast(TypeImpl* type) {
|
||||||
@ -596,23 +716,36 @@ class TypeImpl<Config>::ArrayType : public StructuralType {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Function types.
|
||||||
|
|
||||||
template<class Config>
|
template<class Config>
|
||||||
class TypeImpl<Config>::FunctionType : public StructuralType {
|
class TypeImpl<Config>::FunctionType : public StructuralType {
|
||||||
public:
|
public:
|
||||||
int Arity() { return this->Length() - 2; }
|
int Arity() { return this->Length() - 3; }
|
||||||
TypeHandle Result() { return this->Get(0); }
|
TypeHandle Bound() { return this->Get(0); }
|
||||||
TypeHandle Receiver() { return this->Get(1); }
|
TypeHandle Result() { return this->Get(1); }
|
||||||
TypeHandle Parameter(int i) { return this->Get(2 + i); }
|
TypeHandle Receiver() { return this->Get(2); }
|
||||||
|
TypeHandle Parameter(int i) { return this->Get(3 + i); }
|
||||||
|
|
||||||
void InitParameter(int i, TypeHandle type) { this->Set(2 + i, type); }
|
void InitParameter(int i, TypeHandle type) { this->Set(3 + i, type); }
|
||||||
|
|
||||||
|
static FunctionHandle New(
|
||||||
|
TypeHandle result, TypeHandle receiver, TypeHandle bound,
|
||||||
|
int arity, Region* region) {
|
||||||
|
ASSERT(SEMANTIC(bound->AsBitset()) == SEMANTIC(BitsetType::kFunction));
|
||||||
|
FunctionHandle type = Config::template cast<FunctionType>(
|
||||||
|
StructuralType::New(StructuralType::kFunctionTag, 3 + arity, region));
|
||||||
|
type->Set(0, bound);
|
||||||
|
type->Set(1, result);
|
||||||
|
type->Set(2, receiver);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
static FunctionHandle New(
|
static FunctionHandle New(
|
||||||
TypeHandle result, TypeHandle receiver, int arity, Region* region) {
|
TypeHandle result, TypeHandle receiver, int arity, Region* region) {
|
||||||
FunctionHandle type = Config::template cast<FunctionType>(
|
TypeHandle bound = BitsetType::New(BitsetType::kFunction, region);
|
||||||
StructuralType::New(StructuralType::kFunctionTag, 2 + arity, region));
|
return New(result, receiver, bound, arity, region);
|
||||||
type->Set(0, result);
|
|
||||||
type->Set(1, receiver);
|
|
||||||
return type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static FunctionType* cast(TypeImpl* type) {
|
static FunctionType* cast(TypeImpl* type) {
|
||||||
@ -622,6 +755,9 @@ class TypeImpl<Config>::FunctionType : public StructuralType {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Type iterators.
|
||||||
|
|
||||||
template<class Config> template<class T>
|
template<class Config> template<class T>
|
||||||
class TypeImpl<Config>::Iterator {
|
class TypeImpl<Config>::Iterator {
|
||||||
public:
|
public:
|
||||||
@ -645,8 +781,10 @@ class TypeImpl<Config>::Iterator {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Zone-allocated types are either (odd) integers to represent bitsets, or
|
// -----------------------------------------------------------------------------
|
||||||
|
// Zone-allocated types; they are either (odd) integers to represent bitsets, or
|
||||||
// (even) pointers to structures for everything else.
|
// (even) pointers to structures for everything else.
|
||||||
|
|
||||||
struct ZoneTypeConfig {
|
struct ZoneTypeConfig {
|
||||||
typedef TypeImpl<ZoneTypeConfig> Type;
|
typedef TypeImpl<ZoneTypeConfig> Type;
|
||||||
class Base {};
|
class Base {};
|
||||||
@ -659,36 +797,36 @@ struct ZoneTypeConfig {
|
|||||||
|
|
||||||
static inline bool is_bitset(Type* type);
|
static inline bool is_bitset(Type* type);
|
||||||
static inline bool is_class(Type* type);
|
static inline bool is_class(Type* type);
|
||||||
static inline bool is_constant(Type* type);
|
|
||||||
static inline bool is_struct(Type* type, int tag);
|
static inline bool is_struct(Type* type, int tag);
|
||||||
|
|
||||||
static inline int as_bitset(Type* type);
|
static inline int as_bitset(Type* type);
|
||||||
static inline Struct* as_struct(Type* type);
|
|
||||||
static inline i::Handle<i::Map> as_class(Type* type);
|
static inline i::Handle<i::Map> as_class(Type* type);
|
||||||
static inline i::Handle<i::Object> as_constant(Type* type);
|
static inline Struct* as_struct(Type* type);
|
||||||
|
|
||||||
static inline Type* from_bitset(int bitset);
|
static inline Type* from_bitset(int bitset);
|
||||||
static inline Type* from_bitset(int bitset, Zone* zone);
|
static inline Type* from_bitset(int 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_struct(Struct* structured);
|
||||||
static inline Type* from_class(i::Handle<i::Map> map, int lub, Zone* zone);
|
|
||||||
static inline Type* from_constant(
|
|
||||||
i::Handle<i::Object> value, int lub, Zone* zone);
|
|
||||||
|
|
||||||
static inline Struct* struct_create(int tag, int length, Zone* zone);
|
static inline Struct* struct_create(int tag, int length, Zone* zone);
|
||||||
static inline void struct_shrink(Struct* structured, int length);
|
static inline void struct_shrink(Struct* structure, int length);
|
||||||
static inline int struct_tag(Struct* structured);
|
static inline int struct_tag(Struct* structure);
|
||||||
static inline int struct_length(Struct* structured);
|
static inline int struct_length(Struct* structure);
|
||||||
static inline Type* struct_get(Struct* structured, int i);
|
static inline Type* struct_get(Struct* structure, int i);
|
||||||
static inline void struct_set(Struct* structured, int i, Type* type);
|
static inline void struct_set(Struct* structure, int i, Type* type);
|
||||||
|
template<class V>
|
||||||
static inline int lub_bitset(Type* type);
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef TypeImpl<ZoneTypeConfig> Type;
|
typedef TypeImpl<ZoneTypeConfig> Type;
|
||||||
|
|
||||||
|
|
||||||
// Heap-allocated types are either smis for bitsets, maps for classes, boxes for
|
// -----------------------------------------------------------------------------
|
||||||
|
// Heap-allocated types; either smis for bitsets, maps for classes, boxes for
|
||||||
// constants, or fixed arrays for unions.
|
// constants, or fixed arrays for unions.
|
||||||
|
|
||||||
struct HeapTypeConfig {
|
struct HeapTypeConfig {
|
||||||
typedef TypeImpl<HeapTypeConfig> Type;
|
typedef TypeImpl<HeapTypeConfig> Type;
|
||||||
typedef i::Object Base;
|
typedef i::Object Base;
|
||||||
@ -701,38 +839,40 @@ struct HeapTypeConfig {
|
|||||||
|
|
||||||
static inline bool is_bitset(Type* type);
|
static inline bool is_bitset(Type* type);
|
||||||
static inline bool is_class(Type* type);
|
static inline bool is_class(Type* type);
|
||||||
static inline bool is_constant(Type* type);
|
|
||||||
static inline bool is_struct(Type* type, int tag);
|
static inline bool is_struct(Type* type, int tag);
|
||||||
|
|
||||||
static inline int as_bitset(Type* type);
|
static inline int as_bitset(Type* type);
|
||||||
static inline i::Handle<i::Map> as_class(Type* type);
|
static inline i::Handle<i::Map> as_class(Type* type);
|
||||||
static inline i::Handle<i::Object> as_constant(Type* type);
|
|
||||||
static inline i::Handle<Struct> as_struct(Type* type);
|
static inline i::Handle<Struct> as_struct(Type* type);
|
||||||
|
|
||||||
static inline Type* from_bitset(int bitset);
|
static inline Type* from_bitset(int bitset);
|
||||||
static inline i::Handle<Type> from_bitset(int bitset, Isolate* isolate);
|
static inline i::Handle<Type> from_bitset(int bitset, Isolate* isolate);
|
||||||
static inline i::Handle<Type> from_class(
|
static inline i::Handle<Type> from_class(
|
||||||
i::Handle<i::Map> map, int lub, Isolate* isolate);
|
i::Handle<i::Map> map, Isolate* isolate);
|
||||||
static inline i::Handle<Type> from_constant(
|
static inline i::Handle<Type> from_struct(i::Handle<Struct> structure);
|
||||||
i::Handle<i::Object> value, int lub, Isolate* isolate);
|
|
||||||
static inline i::Handle<Type> from_struct(i::Handle<Struct> structured);
|
|
||||||
|
|
||||||
static inline i::Handle<Struct> struct_create(
|
static inline i::Handle<Struct> struct_create(
|
||||||
int tag, int length, Isolate* isolate);
|
int tag, int length, Isolate* isolate);
|
||||||
static inline void struct_shrink(i::Handle<Struct> structured, int length);
|
static inline void struct_shrink(i::Handle<Struct> structure, int length);
|
||||||
static inline int struct_tag(i::Handle<Struct> structured);
|
static inline int struct_tag(i::Handle<Struct> structure);
|
||||||
static inline int struct_length(i::Handle<Struct> structured);
|
static inline int struct_length(i::Handle<Struct> structure);
|
||||||
static inline i::Handle<Type> struct_get(i::Handle<Struct> structured, int i);
|
static inline i::Handle<Type> struct_get(i::Handle<Struct> structure, int i);
|
||||||
static inline void struct_set(
|
static inline void struct_set(
|
||||||
i::Handle<Struct> structured, int i, i::Handle<Type> type);
|
i::Handle<Struct> structure, int i, i::Handle<Type> type);
|
||||||
|
template<class V>
|
||||||
static inline int lub_bitset(Type* type);
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef TypeImpl<HeapTypeConfig> HeapType;
|
typedef TypeImpl<HeapTypeConfig> HeapType;
|
||||||
|
|
||||||
|
|
||||||
// A simple struct to represent a pair of lower/upper type bounds.
|
// -----------------------------------------------------------------------------
|
||||||
|
// Type bounds. A simple struct to represent a pair of lower/upper types.
|
||||||
|
|
||||||
template<class Config>
|
template<class Config>
|
||||||
struct BoundsImpl {
|
struct BoundsImpl {
|
||||||
typedef TypeImpl<Config> Type;
|
typedef TypeImpl<Config> Type;
|
||||||
|
@ -27,8 +27,13 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "cctest.h"
|
#define private public /* To test private methods :) */
|
||||||
|
#define protected public
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#undef private
|
||||||
|
#undef protected
|
||||||
|
|
||||||
|
#include "cctest.h"
|
||||||
#include "utils/random-number-generator.h"
|
#include "utils/random-number-generator.h"
|
||||||
|
|
||||||
using namespace v8::internal;
|
using namespace v8::internal;
|
||||||
@ -81,8 +86,10 @@ struct HeapRep {
|
|||||||
return t->IsFixedArray() && Smi::cast(AsStruct(t)->get(0))->value() == tag;
|
return t->IsFixedArray() && Smi::cast(AsStruct(t)->get(0))->value() == tag;
|
||||||
}
|
}
|
||||||
static bool IsBitset(Handle<HeapType> t) { return t->IsSmi(); }
|
static bool IsBitset(Handle<HeapType> t) { return t->IsSmi(); }
|
||||||
static bool IsClass(Handle<HeapType> t) { return t->IsMap(); }
|
static bool IsClass(Handle<HeapType> t) {
|
||||||
static bool IsConstant(Handle<HeapType> t) { return t->IsBox(); }
|
return t->IsMap() || IsStruct(t, 0);
|
||||||
|
}
|
||||||
|
static bool IsConstant(Handle<HeapType> t) { return IsStruct(t, 1); }
|
||||||
static bool IsContext(Handle<HeapType> t) { return IsStruct(t, 2); }
|
static bool IsContext(Handle<HeapType> t) { return IsStruct(t, 2); }
|
||||||
static bool IsArray(Handle<HeapType> t) { return IsStruct(t, 3); }
|
static bool IsArray(Handle<HeapType> t) { return IsStruct(t, 3); }
|
||||||
static bool IsFunction(Handle<HeapType> t) { return IsStruct(t, 4); }
|
static bool IsFunction(Handle<HeapType> t) { return IsStruct(t, 4); }
|
||||||
@ -90,10 +97,10 @@ struct HeapRep {
|
|||||||
|
|
||||||
static Struct* AsStruct(Handle<HeapType> t) { return FixedArray::cast(*t); }
|
static Struct* AsStruct(Handle<HeapType> t) { return FixedArray::cast(*t); }
|
||||||
static int AsBitset(Handle<HeapType> t) { return Smi::cast(*t)->value(); }
|
static int AsBitset(Handle<HeapType> t) { return Smi::cast(*t)->value(); }
|
||||||
static Map* AsClass(Handle<HeapType> t) { return Map::cast(*t); }
|
static Map* AsClass(Handle<HeapType> t) {
|
||||||
static Object* AsConstant(Handle<HeapType> t) {
|
return t->IsMap() ? Map::cast(*t) : Map::cast(AsStruct(t)->get(2));
|
||||||
return Box::cast(*t)->value();
|
|
||||||
}
|
}
|
||||||
|
static Object* AsConstant(Handle<HeapType> t) { return AsStruct(t)->get(2); }
|
||||||
static HeapType* AsContext(Handle<HeapType> t) {
|
static HeapType* AsContext(Handle<HeapType> t) {
|
||||||
return HeapType::cast(AsStruct(t)->get(1));
|
return HeapType::cast(AsStruct(t)->get(1));
|
||||||
}
|
}
|
||||||
@ -315,6 +322,8 @@ class Types {
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Region* region() { return region_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Region* region_;
|
Region* region_;
|
||||||
RandomNumberGenerator rng_;
|
RandomNumberGenerator rng_;
|
||||||
@ -347,6 +356,8 @@ struct Tests : Rep {
|
|||||||
Rep::IsClass(type1) == Rep::IsClass(type2) &&
|
Rep::IsClass(type1) == Rep::IsClass(type2) &&
|
||||||
Rep::IsConstant(type1) == Rep::IsConstant(type2) &&
|
Rep::IsConstant(type1) == Rep::IsConstant(type2) &&
|
||||||
Rep::IsContext(type1) == Rep::IsContext(type2) &&
|
Rep::IsContext(type1) == Rep::IsContext(type2) &&
|
||||||
|
Rep::IsArray(type1) == Rep::IsArray(type2) &&
|
||||||
|
Rep::IsFunction(type1) == Rep::IsFunction(type2) &&
|
||||||
Rep::IsUnion(type1) == Rep::IsUnion(type2) &&
|
Rep::IsUnion(type1) == Rep::IsUnion(type2) &&
|
||||||
type1->NumClasses() == type2->NumClasses() &&
|
type1->NumClasses() == type2->NumClasses() &&
|
||||||
type1->NumConstants() == type2->NumConstants() &&
|
type1->NumConstants() == type2->NumConstants() &&
|
||||||
@ -356,7 +367,8 @@ struct Tests : Rep {
|
|||||||
Rep::AsClass(type1) == Rep::AsClass(type2)) &&
|
Rep::AsClass(type1) == Rep::AsClass(type2)) &&
|
||||||
(!Rep::IsConstant(type1) ||
|
(!Rep::IsConstant(type1) ||
|
||||||
Rep::AsConstant(type1) == Rep::AsConstant(type2)) &&
|
Rep::AsConstant(type1) == Rep::AsConstant(type2)) &&
|
||||||
(!Rep::IsUnion(type1) ||
|
// TODO(rossberg): Check details of arrays, functions, bounds.
|
||||||
|
(!Rep::IsUnion(type1) ||
|
||||||
Rep::Length(Rep::AsUnion(type1)) == Rep::Length(Rep::AsUnion(type2)));
|
Rep::Length(Rep::AsUnion(type1)) == Rep::Length(Rep::AsUnion(type2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -723,6 +735,39 @@ struct Tests : Rep {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Bounds() {
|
||||||
|
// Ordering: (T->BitsetGlb())->Is(T->BitsetLub())
|
||||||
|
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
|
||||||
|
TypeHandle type = *it;
|
||||||
|
TypeHandle glb = Type::BitsetType::New(type->BitsetGlb(), T.region());
|
||||||
|
TypeHandle lub = Type::BitsetType::New(type->BitsetLub(), T.region());
|
||||||
|
CHECK(glb->Is(lub));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lower bound: (T->BitsetGlb())->Is(T)
|
||||||
|
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
|
||||||
|
TypeHandle type = *it;
|
||||||
|
TypeHandle glb = Type::BitsetType::New(type->BitsetGlb(), T.region());
|
||||||
|
CHECK(glb->Is(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upper bound: T->Is(T->BitsetLub())
|
||||||
|
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
|
||||||
|
TypeHandle type = *it;
|
||||||
|
TypeHandle lub = Type::BitsetType::New(type->BitsetLub(), T.region());
|
||||||
|
CHECK(type->Is(lub));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inherent bound: (T->BitsetLub())->Is(T->InherentBitsetLub())
|
||||||
|
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
|
||||||
|
TypeHandle type = *it;
|
||||||
|
TypeHandle lub = Type::BitsetType::New(type->BitsetLub(), T.region());
|
||||||
|
TypeHandle inherent =
|
||||||
|
Type::BitsetType::New(type->InherentBitsetLub(), T.region());
|
||||||
|
CHECK(lub->Is(inherent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Is() {
|
void Is() {
|
||||||
// Least Element (Bottom): None->Is(T)
|
// Least Element (Bottom): None->Is(T)
|
||||||
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
|
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
|
||||||
@ -1583,13 +1628,13 @@ struct Tests : Rep {
|
|||||||
CheckEqual(
|
CheckEqual(
|
||||||
T.Intersect(T.Object, T.Union(T.ObjectConstant1, T.ObjectClass)),
|
T.Intersect(T.Object, T.Union(T.ObjectConstant1, T.ObjectClass)),
|
||||||
T.Union(T.ObjectConstant1, T.ObjectClass));
|
T.Union(T.ObjectConstant1, T.ObjectClass));
|
||||||
CheckEqual(
|
CHECK(
|
||||||
T.Intersect(T.Union(T.ArrayClass, T.ObjectConstant1), T.Number),
|
!T.Intersect(T.Union(T.ArrayClass, T.ObjectConstant1), T.Number)
|
||||||
T.None);
|
->IsInhabited());
|
||||||
|
|
||||||
// Class-constant
|
// Class-constant
|
||||||
CheckEqual(T.Intersect(T.ObjectConstant1, T.ObjectClass), T.None);
|
CHECK(!T.Intersect(T.ObjectConstant1, T.ObjectClass)->IsInhabited());
|
||||||
CheckEqual(T.Intersect(T.ArrayClass, T.ObjectConstant2), T.None);
|
CHECK(!T.Intersect(T.ArrayClass, T.ObjectConstant2)->IsInhabited());
|
||||||
|
|
||||||
// Array-union
|
// Array-union
|
||||||
CheckEqual(
|
CheckEqual(
|
||||||
@ -1598,9 +1643,9 @@ struct Tests : Rep {
|
|||||||
CheckEqual(
|
CheckEqual(
|
||||||
T.Intersect(T.AnyArray, T.Union(T.Object, T.SmiConstant)),
|
T.Intersect(T.AnyArray, T.Union(T.Object, T.SmiConstant)),
|
||||||
T.AnyArray);
|
T.AnyArray);
|
||||||
CheckEqual(
|
CHECK(
|
||||||
T.Intersect(T.Union(T.AnyArray, T.ArrayConstant), T.NumberArray),
|
!T.Intersect(T.Union(T.AnyArray, T.ArrayConstant), T.NumberArray)
|
||||||
T.None);
|
->IsInhabited());
|
||||||
|
|
||||||
// Function-union
|
// Function-union
|
||||||
CheckEqual(
|
CheckEqual(
|
||||||
@ -1609,9 +1654,9 @@ struct Tests : Rep {
|
|||||||
CheckEqual(
|
CheckEqual(
|
||||||
T.Intersect(T.NumberFunction1, T.Union(T.Object, T.SmiConstant)),
|
T.Intersect(T.NumberFunction1, T.Union(T.Object, T.SmiConstant)),
|
||||||
T.NumberFunction1);
|
T.NumberFunction1);
|
||||||
CheckEqual(
|
CHECK(
|
||||||
T.Intersect(T.Union(T.MethodFunction, T.Name), T.NumberFunction2),
|
!T.Intersect(T.Union(T.MethodFunction, T.Name), T.NumberFunction2)
|
||||||
T.None);
|
->IsInhabited());
|
||||||
|
|
||||||
// Class-union
|
// Class-union
|
||||||
CheckEqual(
|
CheckEqual(
|
||||||
@ -1620,9 +1665,9 @@ struct Tests : Rep {
|
|||||||
CheckEqual(
|
CheckEqual(
|
||||||
T.Intersect(T.ArrayClass, T.Union(T.Object, T.SmiConstant)),
|
T.Intersect(T.ArrayClass, T.Union(T.Object, T.SmiConstant)),
|
||||||
T.ArrayClass);
|
T.ArrayClass);
|
||||||
CheckEqual(
|
CHECK(
|
||||||
T.Intersect(T.Union(T.ObjectClass, T.ArrayConstant), T.ArrayClass),
|
!T.Intersect(T.Union(T.ObjectClass, T.ArrayConstant), T.ArrayClass)
|
||||||
T.None);
|
->IsInhabited());
|
||||||
|
|
||||||
// Constant-union
|
// Constant-union
|
||||||
CheckEqual(
|
CheckEqual(
|
||||||
@ -1632,10 +1677,10 @@ struct Tests : Rep {
|
|||||||
CheckEqual(
|
CheckEqual(
|
||||||
T.Intersect(T.SmiConstant, T.Union(T.Number, T.ObjectConstant2)),
|
T.Intersect(T.SmiConstant, T.Union(T.Number, T.ObjectConstant2)),
|
||||||
T.SmiConstant);
|
T.SmiConstant);
|
||||||
CheckEqual(
|
CHECK(
|
||||||
T.Intersect(
|
!T.Intersect(
|
||||||
T.Union(T.ArrayConstant, T.ObjectClass), T.ObjectConstant1),
|
T.Union(T.ArrayConstant, T.ObjectClass), T.ObjectConstant1)
|
||||||
T.None);
|
->IsInhabited());
|
||||||
|
|
||||||
// Union-union
|
// Union-union
|
||||||
CheckEqual(
|
CheckEqual(
|
||||||
@ -1663,6 +1708,44 @@ struct Tests : Rep {
|
|||||||
T.Union(T.ObjectConstant2, T.ObjectConstant1));
|
T.Union(T.ObjectConstant2, T.ObjectConstant1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Distributivity() {
|
||||||
|
// Distributivity:
|
||||||
|
// Union(T1, Intersect(T2, T3)) = Intersect(Union(T1, T2), Union(T1, T3))
|
||||||
|
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
|
||||||
|
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
|
||||||
|
for (TypeIterator it3 = T.types.begin(); it3 != T.types.end(); ++it3) {
|
||||||
|
TypeHandle type1 = *it1;
|
||||||
|
TypeHandle type2 = *it2;
|
||||||
|
TypeHandle type3 = *it3;
|
||||||
|
TypeHandle union12 = T.Union(type1, type2);
|
||||||
|
TypeHandle union13 = T.Union(type1, type3);
|
||||||
|
TypeHandle intersect23 = T.Intersect(type2, type3);
|
||||||
|
TypeHandle union1_23 = T.Union(type1, intersect23);
|
||||||
|
TypeHandle intersect12_13 = T.Intersect(union12, union13);
|
||||||
|
CHECK(Equal(union1_23, intersect12_13));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distributivity:
|
||||||
|
// Intersect(T1, Union(T2, T3)) = Union(Intersect(T1, T2), Intersect(T1,T3))
|
||||||
|
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
|
||||||
|
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
|
||||||
|
for (TypeIterator it3 = T.types.begin(); it3 != T.types.end(); ++it3) {
|
||||||
|
TypeHandle type1 = *it1;
|
||||||
|
TypeHandle type2 = *it2;
|
||||||
|
TypeHandle type3 = *it3;
|
||||||
|
TypeHandle intersect12 = T.Intersect(type1, type2);
|
||||||
|
TypeHandle intersect13 = T.Intersect(type1, type3);
|
||||||
|
TypeHandle union23 = T.Union(type2, type3);
|
||||||
|
TypeHandle intersect1_23 = T.Intersect(type1, union23);
|
||||||
|
TypeHandle union12_13 = T.Union(intersect12, intersect13);
|
||||||
|
CHECK(Equal(intersect1_23, union12_13));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<class Type2, class TypeHandle2, class Region2, class Rep2>
|
template<class Type2, class TypeHandle2, class Region2, class Rep2>
|
||||||
void Convert() {
|
void Convert() {
|
||||||
Types<Type2, TypeHandle2, Region2> T2(
|
Types<Type2, TypeHandle2, Region2> T2(
|
||||||
@ -1729,6 +1812,13 @@ TEST(NowOf) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(Bounds) {
|
||||||
|
CcTest::InitializeVM();
|
||||||
|
ZoneTests().Bounds();
|
||||||
|
HeapTests().Bounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST(Is) {
|
TEST(Is) {
|
||||||
CcTest::InitializeVM();
|
CcTest::InitializeVM();
|
||||||
ZoneTests().Is();
|
ZoneTests().Is();
|
||||||
@ -1792,6 +1882,13 @@ TEST(Intersect2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(Distributivity) {
|
||||||
|
CcTest::InitializeVM();
|
||||||
|
ZoneTests().Distributivity();
|
||||||
|
HeapTests().Distributivity();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST(Convert) {
|
TEST(Convert) {
|
||||||
CcTest::InitializeVM();
|
CcTest::InitializeVM();
|
||||||
ZoneTests().Convert<HeapType, Handle<HeapType>, Isolate, HeapRep>();
|
ZoneTests().Convert<HeapType, Handle<HeapType>, Isolate, HeapRep>();
|
||||||
|
Loading…
Reference in New Issue
Block a user