Commit 9b28aed3 authored by rossberg@chromium.org's avatar rossberg@chromium.org

Introduce representation types

Also:
- improve type pretty-printing,
- update doc comments,
- some renamings for consistency.

R=mstarzinger@chromium.org
BUG=

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20025 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent acf695e3
......@@ -941,7 +941,7 @@ HValue* CodeStubGraphBuilder<BinaryOpICStub>::BuildCodeInitializedStub() {
// If we encounter a generic argument, the number conversion is
// observable, thus we cannot afford to bail out after the fact.
if (!state.HasSideEffects()) {
if (result_type->Is(Type::Smi())) {
if (result_type->Is(Type::SignedSmall())) {
if (state.op() == Token::SHR) {
// TODO(olivf) Replace this by a SmiTagU Instruction.
// 0x40000000: this number would convert to negative when interpreting
......
......@@ -1674,7 +1674,7 @@ HValue* HGraphBuilder::BuildNumberToString(HValue* object, Type* type) {
}
if_objectissmi.Else();
{
if (type->Is(Type::Smi())) {
if (type->Is(Type::SignedSmall())) {
if_objectissmi.Deopt("Expected smi");
} else {
// Check if the object is a heap number.
......@@ -8980,7 +8980,7 @@ bool CanBeZero(HValue* right) {
HValue* HGraphBuilder::EnforceNumberType(HValue* number,
Type* expected) {
if (expected->Is(Type::Smi())) {
if (expected->Is(Type::SignedSmall())) {
return AddUncasted<HForceRepresentation>(number, Representation::Smi());
}
if (expected->Is(Type::Signed32())) {
......@@ -9023,7 +9023,7 @@ HValue* HGraphBuilder::TruncateToNumber(HValue* value, Type** expected) {
if (expected_obj->Is(Type::Undefined(zone()))) {
// This is already done by HChange.
*expected = Type::Union(expected_number, Type::Double(zone()), zone());
*expected = Type::Union(expected_number, Type::Float(zone()), zone());
return value;
}
......
......@@ -2372,7 +2372,7 @@ const char* BinaryOpIC::State::KindToString(Kind kind) {
Type* BinaryOpIC::State::KindToType(Kind kind, Zone* zone) {
switch (kind) {
case NONE: return Type::None(zone);
case SMI: return Type::Smi(zone);
case SMI: return Type::SignedSmall(zone);
case INT32: return Type::Signed32(zone);
case NUMBER: return Type::Number(zone);
case STRING: return Type::String(zone);
......@@ -2518,7 +2518,7 @@ Type* CompareIC::StateToType(
Handle<Map> map) {
switch (state) {
case CompareIC::UNINITIALIZED: return Type::None(zone);
case CompareIC::SMI: return Type::Smi(zone);
case CompareIC::SMI: return Type::SignedSmall(zone);
case CompareIC::NUMBER: return Type::Number(zone);
case CompareIC::STRING: return Type::String(zone);
case CompareIC::INTERNALIZED_STRING: return Type::InternalizedString(zone);
......
......@@ -152,14 +152,14 @@ int TypeImpl<Config>::LubBitset() {
template<class Config>
int TypeImpl<Config>::LubBitset(i::Object* value) {
if (value->IsSmi()) return kSmi;
if (value->IsSmi()) return kSignedSmall & kTaggedInt;
i::Map* map = i::HeapObject::cast(value)->map();
if (map->instance_type() == HEAP_NUMBER_TYPE) {
int32_t i;
uint32_t u;
if (value->ToInt32(&i)) return Smi::IsValid(i) ? kSmi : kOtherSigned32;
if (value->ToUint32(&u)) return kUnsigned32;
return kDouble;
return kTaggedPtr & (
value->ToInt32(&i) ? (Smi::IsValid(i) ? kSignedSmall : kOtherSigned32) :
value->ToUint32(&u) ? kUnsigned32 : kFloat);
}
if (map->instance_type() == ODDBALL_TYPE) {
if (value->IsUndefined()) return kUndefined;
......@@ -204,7 +204,7 @@ int TypeImpl<Config>::LubBitset(i::Map* map) {
case ODDBALL_TYPE:
return kOddball;
case HEAP_NUMBER_TYPE:
return kDouble;
return kFloat & kTaggedPtr;
case JS_VALUE_TYPE:
case JS_DATE_TYPE:
case JS_OBJECT_TYPE:
......@@ -247,7 +247,7 @@ int TypeImpl<Config>::LubBitset(i::Map* map) {
case EXECUTABLE_ACCESSOR_INFO_TYPE:
case ACCESSOR_PAIR_TYPE:
case FIXED_ARRAY_TYPE:
return kInternal;
return kInternal & kTaggedPtr;
default:
UNREACHABLE();
return kNone;
......@@ -273,13 +273,12 @@ int TypeImpl<Config>::GlbBitset() {
template<class Config>
typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::OfCurrently(
i::Handle<i::Object> value, Region* region) {
if (value->IsSmi()) return Smi(region);
i::Map* map = i::HeapObject::cast(*value)->map();
if (map->instance_type() == HEAP_NUMBER_TYPE ||
map->instance_type() == ODDBALL_TYPE) {
if (value->IsSmi() ||
i::HeapObject::cast(*value)->map()->instance_type() == HEAP_NUMBER_TYPE ||
i::HeapObject::cast(*value)->map()->instance_type() == ODDBALL_TYPE) {
return Of(value, region);
}
return Class(i::handle(map), region);
return Class(i::handle(i::HeapObject::cast(*value)->map()), region);
}
......@@ -340,10 +339,10 @@ template<class Config>
bool TypeImpl<Config>::Maybe(TypeImpl* that) {
// Fast path for bitsets.
if (this->IsBitset()) {
return (this->AsBitset() & that->LubBitset()) != 0;
return IsInhabited(this->AsBitset() & that->LubBitset());
}
if (that->IsBitset()) {
return (this->LubBitset() & that->AsBitset()) != 0;
return IsInhabited(this->LubBitset() & that->AsBitset());
}
// (T1 \/ ... \/ Tn) overlaps T <=> (T1 overlaps T) \/ ... \/ (Tn overlaps T)
......@@ -570,7 +569,7 @@ typename TypeImpl<Config>::TypeHandle TypeImpl<Config>::Convert(
// TODO(rossberg): this does not belong here.
Representation Representation::FromType(Type* type) {
if (type->Is(Type::None())) return Representation::None();
if (type->Is(Type::Smi())) return Representation::Smi();
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();
......@@ -579,8 +578,8 @@ Representation Representation::FromType(Type* type) {
#ifdef OBJECT_PRINT
template<class Config>
void TypeImpl<Config>::TypePrint() {
TypePrint(stdout);
void TypeImpl<Config>::TypePrint(PrintDimension dim) {
TypePrint(stdout, dim);
PrintF(stdout, "\n");
Flush(stdout);
}
......@@ -589,9 +588,17 @@ void TypeImpl<Config>::TypePrint() {
template<class Config>
const char* TypeImpl<Config>::bitset_name(int bitset) {
switch (bitset) {
#define PRINT_COMPOSED_TYPE(type, value) case k##type: return #type;
BITSET_TYPE_LIST(PRINT_COMPOSED_TYPE)
case kAny & kRepresentation: return "Any";
#define PRINT_COMPOSED_TYPE(type, value) \
case k##type & kRepresentation: return #type;
REPRESENTATION_BITSET_TYPE_LIST(PRINT_COMPOSED_TYPE)
#undef PRINT_COMPOSED_TYPE
#define PRINT_COMPOSED_TYPE(type, value) \
case k##type & kSemantic: return #type;
SEMANTIC_BITSET_TYPE_LIST(PRINT_COMPOSED_TYPE)
#undef PRINT_COMPOSED_TYPE
default:
return NULL;
}
......@@ -599,23 +606,54 @@ const char* TypeImpl<Config>::bitset_name(int bitset) {
template<class Config>
void TypeImpl<Config>::TypePrint(FILE* out) {
void TypeImpl<Config>::BitsetTypePrint(FILE* out, int bitset) {
const char* name = bitset_name(bitset);
if (name != NULL) {
PrintF(out, "%s", name);
} else {
static const int named_bitsets[] = {
#define BITSET_CONSTANT(type, value) k##type & kRepresentation,
REPRESENTATION_BITSET_TYPE_LIST(BITSET_CONSTANT)
#undef BITSET_CONSTANT
#define BITSET_CONSTANT(type, value) k##type & kSemantic,
SEMANTIC_BITSET_TYPE_LIST(BITSET_CONSTANT)
#undef BITSET_CONSTANT
};
bool is_first = true;
PrintF(out, "(");
for (int i(ARRAY_SIZE(named_bitsets) - 1); bitset != 0 && i >= 0; --i) {
int subset = named_bitsets[i];
if ((bitset & subset) == subset) {
if (!is_first) PrintF(out, " | ");
is_first = false;
PrintF(out, "%s", bitset_name(subset));
bitset -= subset;
}
}
ASSERT(bitset == 0);
PrintF(out, ")");
}
}
template<class Config>
void TypeImpl<Config>::TypePrint(FILE* out, PrintDimension dim) {
if (this->IsBitset()) {
int bitset = this->AsBitset();
const char* name = bitset_name(bitset);
if (name != NULL) {
PrintF(out, "%s", name);
} else {
bool is_first = true;
PrintF(out, "(");
for (int mask = 1; mask != 0; mask = mask << 1) {
if ((bitset & mask) != 0) {
if (!is_first) PrintF(out, " | ");
is_first = false;
PrintF(out, "%s", bitset_name(mask));
}
}
PrintF(out, ")");
switch (dim) {
case BOTH_DIMS:
BitsetTypePrint(out, bitset & kSemantic);
PrintF("/");
BitsetTypePrint(out, bitset & kRepresentation);
break;
case SEMANTIC_DIM:
BitsetTypePrint(out, bitset & kSemantic);
break;
case REPRESENTATION_DIM:
BitsetTypePrint(out, bitset & kRepresentation);
break;
}
} else if (this->IsConstant()) {
PrintF(out, "Constant(%p : ", static_cast<void*>(*this->AsConstant()));
......
......@@ -42,7 +42,10 @@ namespace internal {
// can express class types (a.k.a. specific maps) and singleton types (i.e.,
// concrete constants).
//
// The following equations and inequations hold:
// Types consist of two dimensions: semantic (value range) and representation.
// Both are related through subtyping.
//
// The following equations and inequations hold for the semantic axis:
//
// None <= T
// T <= Any
......@@ -54,13 +57,12 @@ namespace internal {
// UniqueName = InternalizedString \/ Symbol
// InternalizedString < String
//
// Allocated = Receiver \/ Number \/ Name
// Detectable = Allocated - Undetectable
// Undetectable < Object
// Receiver = Object \/ Proxy
// Array < Object
// Function < Object
// RegExp < Object
// Undetectable < Object
// Detectable = Receiver \/ Number \/ Name - Undetectable
//
// Class(map) < T iff instance_type(map) < T
// Constant(x) < T iff instance_type(map(x)) < T
......@@ -70,20 +72,43 @@ namespace internal {
// TODO(rossberg): the latter is not currently true for proxies, because of fix,
// but will hold once we implement direct proxies.
//
// For the representation axis, the following holds:
//
// None <= R
// R <= Any
//
// UntaggedInt <= UntaggedInt8 \/ UntaggedInt16 \/ UntaggedInt32)
// UntaggedFloat <= UntaggedFloat32 \/ UntaggedFloat64
// UntaggedNumber <= UntaggedInt \/ UntaggedFloat
// Untagged <= UntaggedNumber \/ UntaggedPtr
// Tagged <= TaggedInt \/ TaggedPtr
//
// Subtyping relates the two dimensions, for example:
//
// Number <= Tagged \/ UntaggedNumber
// Object <= TaggedPtr \/ UntaggedPtr
//
// That holds because the semantic type constructors defined by the API create
// types that allow for all possible representations, and dually, the ones for
// representation types initially include all semantic ranges. Representations
// can then e.g. be narrowed for a given semantic type using intersection:
//
// SignedSmall /\ TaggedInt (a 'smi')
// Number /\ TaggedPtr (a heap number)
//
// There are two main functions for testing types:
//
// T1->Is(T2) -- tests whether T1 is included in T2 (i.e., T1 <= T2)
// T1->Maybe(T2) -- tests whether T1 and T2 overlap (i.e., T1 /\ T2 =/= 0)
//
// Typically, the former is to be used to select representations (e.g., via
// T->Is(Integer31())), and the to check whether a specific case needs handling
// (e.g., via T->Maybe(Number())).
// T->Is(SignedSmall())), and the latter to check whether a specific case needs
// handling (e.g., via T->Maybe(Number())).
//
// There is no functionality to discover whether a type is a leaf in the
// lattice. That is intentional. It should always be possible to refine the
// lattice (e.g., splitting up number types further) without invalidating any
// existing assumptions or tests.
//
// Consequently, do not use pointer equality for type tests, always use Is!
//
// Internally, all 'primitive' types, and their unions, are represented as
......@@ -100,40 +125,68 @@ namespace internal {
// them. For zone types, no query method touches the heap, only constructors do.
#define BITSET_TYPE_LIST(V) \
V(None, 0) \
V(Null, 1 << 0) \
V(Undefined, 1 << 1) \
V(Boolean, 1 << 2) \
V(Smi, 1 << 3) \
V(OtherSigned32, 1 << 4) \
V(Unsigned32, 1 << 5) \
V(Double, 1 << 6) \
V(Symbol, 1 << 7) \
V(InternalizedString, 1 << 8) \
V(OtherString, 1 << 9) \
V(Undetectable, 1 << 10) \
V(Array, 1 << 11) \
V(Function, 1 << 12) \
V(RegExp, 1 << 13) \
V(OtherObject, 1 << 14) \
V(Proxy, 1 << 15) \
V(Internal, 1 << 16) \
#define MASK_BITSET_TYPE_LIST(V) \
V(Representation, static_cast<int>(0xff800000)) \
V(Semantic, static_cast<int>(0x007fffff))
#define REPRESENTATION(k) ((k) & kRepresentation)
#define SEMANTIC(k) ((k) & kSemantic)
#define REPRESENTATION_BITSET_TYPE_LIST(V) \
V(None, 0) \
V(UntaggedInt8, 1 << 23 | kSemantic) \
V(UntaggedInt16, 1 << 24 | kSemantic) \
V(UntaggedInt32, 1 << 25 | kSemantic) \
V(UntaggedFloat32, 1 << 26 | kSemantic) \
V(UntaggedFloat64, 1 << 27 | kSemantic) \
V(UntaggedPtr, 1 << 28 | kSemantic) \
V(TaggedInt, 1 << 29 | kSemantic) \
V(TaggedPtr, -1 << 30 | kSemantic) /* MSB has to be sign-extended */ \
\
V(UntaggedInt, kUntaggedInt8 | kUntaggedInt16 | kUntaggedInt32) \
V(UntaggedFloat, kUntaggedFloat32 | kUntaggedFloat64) \
V(UntaggedNumber, kUntaggedInt | kUntaggedFloat) \
V(Untagged, kUntaggedNumber | kUntaggedPtr) \
V(Tagged, kTaggedInt | kTaggedPtr)
#define SEMANTIC_BITSET_TYPE_LIST(V) \
V(Null, 1 << 0 | REPRESENTATION(kTaggedPtr)) \
V(Undefined, 1 << 1 | REPRESENTATION(kTaggedPtr)) \
V(Boolean, 1 << 2 | REPRESENTATION(kTaggedPtr)) \
V(SignedSmall, 1 << 3 | REPRESENTATION(kTagged | kUntaggedNumber)) \
V(OtherSigned32, 1 << 4 | REPRESENTATION(kTagged | kUntaggedNumber)) \
V(Unsigned32, 1 << 5 | REPRESENTATION(kTagged | kUntaggedNumber)) \
V(Float, 1 << 6 | REPRESENTATION(kTagged | kUntaggedNumber)) \
V(Symbol, 1 << 7 | REPRESENTATION(kTaggedPtr)) \
V(InternalizedString, 1 << 8 | REPRESENTATION(kTaggedPtr)) \
V(OtherString, 1 << 9 | REPRESENTATION(kTaggedPtr)) \
V(Undetectable, 1 << 10 | REPRESENTATION(kTaggedPtr)) \
V(Array, 1 << 11 | REPRESENTATION(kTaggedPtr)) \
V(Function, 1 << 12 | REPRESENTATION(kTaggedPtr)) \
V(RegExp, 1 << 13 | REPRESENTATION(kTaggedPtr)) \
V(OtherObject, 1 << 14 | REPRESENTATION(kTaggedPtr)) \
V(Proxy, 1 << 15 | REPRESENTATION(kTaggedPtr)) \
V(Internal, 1 << 16 | REPRESENTATION(kTagged | kUntagged)) \
\
V(Oddball, kBoolean | kNull | kUndefined) \
V(Signed32, kSmi | kOtherSigned32) \
V(Number, kSigned32 | kUnsigned32 | kDouble) \
V(String, kInternalizedString | kOtherString) \
V(UniqueName, kSymbol | kInternalizedString) \
V(Name, kSymbol | kString) \
V(NumberOrString, kNumber | kString) \
V(Object, kUndetectable | kArray | kFunction | \
kRegExp | kOtherObject) \
V(Receiver, kObject | kProxy) \
V(Allocated, kDouble | kName | kReceiver) \
V(Any, kOddball | kNumber | kAllocated | kInternal) \
V(NonNumber, kAny - kNumber) \
V(Detectable, kAllocated - kUndetectable)
V(Oddball, kBoolean | kNull | kUndefined) \
V(Signed32, kSignedSmall | kOtherSigned32) \
V(Number, kSigned32 | kUnsigned32 | kFloat) \
V(String, kInternalizedString | kOtherString) \
V(UniqueName, kSymbol | kInternalizedString) \
V(Name, kSymbol | kString) \
V(NumberOrString, kNumber | kString) \
V(DetectableObject, kArray | kFunction | kRegExp | kOtherObject) \
V(DetectableReceiver, kDetectableObject | kProxy) \
V(Detectable, kDetectableReceiver | kNumber | kName) \
V(Object, kDetectableObject | kUndetectable) \
V(Receiver, kObject | kProxy) \
V(NonNumber, kOddball | kName | kReceiver | kInternal) \
V(Any, kNumber | kNonNumber)
#define BITSET_TYPE_LIST(V) \
MASK_BITSET_TYPE_LIST(V) \
REPRESENTATION_BITSET_TYPE_LIST(V) \
SEMANTIC_BITSET_TYPE_LIST(V)
// struct Config {
......@@ -254,8 +307,9 @@ class TypeImpl : public Config::Base {
typename OtherTypeImpl::TypeHandle type, Region* region);
#ifdef OBJECT_PRINT
void TypePrint();
void TypePrint(FILE* out);
enum PrintDimension { BOTH_DIMS, SEMANTIC_DIM, REPRESENTATION_DIM };
void TypePrint(PrintDimension = BOTH_DIMS);
void TypePrint(FILE* out, PrintDimension = BOTH_DIMS);
#endif
private:
......@@ -292,6 +346,10 @@ class TypeImpl : public Config::Base {
bool SlowIs(TypeImpl* that);
static bool IsInhabited(int bitset) {
return (bitset & kRepresentation) && (bitset & kSemantic);
}
int LubBitset(); // least upper bound that's a bitset
int GlbBitset(); // greatest lower bound that's a bitset
......@@ -306,6 +364,7 @@ class TypeImpl : public Config::Base {
#ifdef OBJECT_PRINT
static const char* bitset_name(int bitset);
static void BitsetTypePrint(FILE* out, int bitset);
#endif
};
......
......@@ -612,7 +612,7 @@ void AstTyper::VisitCountOperation(CountOperation* expr) {
RECURSE(Visit(expr->expression()));
NarrowType(expr, Bounds(Type::Smi(zone()), Type::Number(zone())));
NarrowType(expr, Bounds(Type::SignedSmall(zone()), Type::Number(zone())));
VariableProxy* proxy = expr->expression()->AsVariableProxy();
if (proxy != NULL && proxy->var()->IsStackAllocated()) {
......@@ -668,7 +668,7 @@ void AstTyper::VisitBinaryOperation(BinaryOperation* expr) {
Type* upper = Type::Union(
expr->left()->bounds().upper, expr->right()->bounds().upper, zone());
if (!upper->Is(Type::Signed32())) upper = Type::Signed32(zone());
Type* lower = Type::Intersect(Type::Smi(zone()), upper, zone());
Type* lower = Type::Intersect(Type::SignedSmall(zone()), upper, zone());
NarrowType(expr, Bounds(lower, upper));
break;
}
......@@ -677,7 +677,8 @@ void AstTyper::VisitBinaryOperation(BinaryOperation* expr) {
case Token::SAR:
RECURSE(Visit(expr->left()));
RECURSE(Visit(expr->right()));
NarrowType(expr, Bounds(Type::Smi(zone()), Type::Signed32(zone())));
NarrowType(expr,
Bounds(Type::SignedSmall(zone()), Type::Signed32(zone())));
break;
case Token::SHR:
RECURSE(Visit(expr->left()));
......@@ -685,7 +686,7 @@ void AstTyper::VisitBinaryOperation(BinaryOperation* expr) {
// TODO(rossberg): The upper bound would be Unsigned32, but since there
// is no 'positive Smi' type for the lower bound, we use the smallest
// union of Smi and Unsigned32 as upper bound instead.
NarrowType(expr, Bounds(Type::Smi(zone()), Type::Number(zone())));
NarrowType(expr, Bounds(Type::SignedSmall(zone()), Type::Number(zone())));
break;
case Token::ADD: {
RECURSE(Visit(expr->left()));
......@@ -698,7 +699,7 @@ void AstTyper::VisitBinaryOperation(BinaryOperation* expr) {
l.lower->Is(Type::String()) || r.lower->Is(Type::String()) ?
Type::String(zone()) :
l.lower->Is(Type::Number()) && r.lower->Is(Type::Number()) ?
Type::Smi(zone()) : Type::None(zone());
Type::SignedSmall(zone()) : Type::None(zone());
Type* upper =
l.upper->Is(Type::String()) || r.upper->Is(Type::String()) ?
Type::String(zone()) :
......@@ -713,7 +714,7 @@ void AstTyper::VisitBinaryOperation(BinaryOperation* expr) {
case Token::MOD:
RECURSE(Visit(expr->left()));
RECURSE(Visit(expr->right()));
NarrowType(expr, Bounds(Type::Smi(zone()), Type::Number(zone())));
NarrowType(expr, Bounds(Type::SignedSmall(zone()), Type::Number(zone())));
break;
default:
UNREACHABLE();
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment