blob: 5d437e26b243654691102507ca6a6986f2e46fad [file] [log] [blame]
ulan@chromium.orgdfe53072013-06-06 14:14:51 +00001// Copyright 2013 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#ifndef V8_TYPES_H_
29#define V8_TYPES_H_
30
31#include "v8.h"
32
33#include "objects.h"
34
35namespace v8 {
36namespace internal {
37
38
39// A simple type system for compiler-internal use. It is based entirely on
40// union types, and all subtyping hence amounts to set inclusion. Besides the
41// obvious primitive types and some predefined unions, the type language also
42// can express class types (a.k.a. specific maps) and singleton types (i.e.,
43// concrete constants).
44//
45// The following equations and inequations hold:
46//
47// None <= T
48// T <= Any
49//
50// Oddball = Boolean \/ Null \/ Undefined
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +000051// Number = Signed32 \/ Unsigned32 \/ Double
52// Smi <= Signed32
ulan@chromium.orgdfe53072013-06-06 14:14:51 +000053// Name = String \/ Symbol
54// UniqueName = InternalizedString \/ Symbol
55// InternalizedString < String
56//
danno@chromium.org41728482013-06-12 22:31:22 +000057// Allocated = Receiver \/ Number \/ Name
58// Detectable = Allocated - Undetectable
59// Undetectable < Object
ulan@chromium.orgdfe53072013-06-06 14:14:51 +000060// Receiver = Object \/ Proxy
61// Array < Object
62// Function < Object
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +000063// RegExp < Object
ulan@chromium.orgdfe53072013-06-06 14:14:51 +000064//
65// Class(map) < T iff instance_type(map) < T
66// Constant(x) < T iff instance_type(map(x)) < T
67//
68// Note that Constant(x) < Class(map(x)) does _not_ hold, since x's map can
69// change! (Its instance type cannot, however.)
70// TODO(rossberg): the latter is not currently true for proxies, because of fix,
71// but will hold once we implement direct proxies.
72//
73// There are two main functions for testing types:
74//
75// T1->Is(T2) -- tests whether T1 is included in T2 (i.e., T1 <= T2)
76// T1->Maybe(T2) -- tests whether T1 and T2 overlap (i.e., T1 /\ T2 =/= 0)
77//
danno@chromium.org41728482013-06-12 22:31:22 +000078// Typically, the former is to be used to select representations (e.g., via
79// T->Is(Integer31())), and the to check whether a specific case needs handling
80// (e.g., via T->Maybe(Number())).
ulan@chromium.orgdfe53072013-06-06 14:14:51 +000081//
82// There is no functionality to discover whether a type is a leaf in the
83// lattice. That is intentional. It should always be possible to refine the
84// lattice (e.g., splitting up number types further) without invalidating any
85// existing assumptions or tests.
86//
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +000087// Consequently, do not use pointer equality for type tests, always use Is!
88//
ulan@chromium.orgdfe53072013-06-06 14:14:51 +000089// Internally, all 'primitive' types, and their unions, are represented as
danno@chromium.org1fd77d52013-06-07 16:01:45 +000090// bitsets via smis. Class is a heap pointer to the respective map. Only
91// Constant's, or unions containing Class'es or Constant's, require allocation.
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +000092// Note that the bitset representation is closed under both Union and Intersect.
ulan@chromium.orgdfe53072013-06-06 14:14:51 +000093//
94// The type representation is heap-allocated, so cannot (currently) be used in
rossberg@chromium.org92597162013-08-23 13:28:00 +000095// a concurrent compilation context.
ulan@chromium.orgdfe53072013-06-06 14:14:51 +000096
danno@chromium.orgbee51992013-07-10 14:57:15 +000097
98#define PRIMITIVE_TYPE_LIST(V) \
99 V(None, 0) \
100 V(Null, 1 << 0) \
101 V(Undefined, 1 << 1) \
102 V(Boolean, 1 << 2) \
103 V(Smi, 1 << 3) \
104 V(OtherSigned32, 1 << 4) \
105 V(Unsigned32, 1 << 5) \
106 V(Double, 1 << 6) \
107 V(Symbol, 1 << 7) \
108 V(InternalizedString, 1 << 8) \
109 V(OtherString, 1 << 9) \
110 V(Undetectable, 1 << 10) \
111 V(Array, 1 << 11) \
112 V(Function, 1 << 12) \
113 V(RegExp, 1 << 13) \
114 V(OtherObject, 1 << 14) \
115 V(Proxy, 1 << 15) \
116 V(Internal, 1 << 16)
117
118#define COMPOSED_TYPE_LIST(V) \
119 V(Oddball, kBoolean | kNull | kUndefined) \
120 V(Signed32, kSmi | kOtherSigned32) \
121 V(Number, kSigned32 | kUnsigned32 | kDouble) \
122 V(String, kInternalizedString | kOtherString) \
123 V(UniqueName, kSymbol | kInternalizedString) \
124 V(Name, kSymbol | kString) \
125 V(NumberOrString, kNumber | kString) \
126 V(Object, kUndetectable | kArray | kFunction | \
127 kRegExp | kOtherObject) \
128 V(Receiver, kObject | kProxy) \
129 V(Allocated, kDouble | kName | kReceiver) \
130 V(Any, kOddball | kNumber | kAllocated | kInternal) \
machenbach@chromium.org528ce022013-09-23 14:09:36 +0000131 V(NonNumber, kAny - kNumber) \
danno@chromium.orgbee51992013-07-10 14:57:15 +0000132 V(Detectable, kAllocated - kUndetectable)
133
134#define TYPE_LIST(V) \
135 PRIMITIVE_TYPE_LIST(V) \
136 COMPOSED_TYPE_LIST(V)
137
138
139
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000140class Type : public Object {
141 public:
danno@chromium.orgbee51992013-07-10 14:57:15 +0000142 #define DEFINE_TYPE_CONSTRUCTOR(type, value) \
143 static Type* type() { return from_bitset(k##type); }
144 TYPE_LIST(DEFINE_TYPE_CONSTRUCTOR)
145 #undef DEFINE_TYPE_CONSTRUCTOR
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000146
147 static Type* Class(Handle<Map> map) { return from_handle(map); }
148 static Type* Constant(Handle<HeapObject> value) {
danno@chromium.org1fd77d52013-06-07 16:01:45 +0000149 return Constant(value, value->GetIsolate());
150 }
151 static Type* Constant(Handle<v8::internal::Object> value, Isolate* isolate) {
152 return from_handle(isolate->factory()->NewBox(value));
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000153 }
154
155 static Type* Union(Handle<Type> type1, Handle<Type> type2);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +0000156 static Type* Intersect(Handle<Type> type1, Handle<Type> type2);
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000157 static Type* Optional(Handle<Type> type); // type \/ Undefined
158
mvstanton@chromium.orgdd6d9ee2013-10-11 10:35:37 +0000159 bool Is(Type* that) { return (this == that) ? true : SlowIs(that); }
danno@chromium.org41728482013-06-12 22:31:22 +0000160 bool Is(Handle<Type> that) { return this->Is(*that); }
161 bool Maybe(Type* that);
162 bool Maybe(Handle<Type> that) { return this->Maybe(*that); }
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000163
danno@chromium.org41728482013-06-12 22:31:22 +0000164 bool IsClass() { return is_class(); }
165 bool IsConstant() { return is_constant(); }
166 Handle<Map> AsClass() { return as_class(); }
167 Handle<v8::internal::Object> AsConstant() { return as_constant(); }
168
169 int NumClasses();
170 int NumConstants();
171
172 template<class T>
173 class Iterator {
174 public:
175 bool Done() const { return index_ < 0; }
176 Handle<T> Current();
177 void Advance();
178
179 private:
180 friend class Type;
181
182 Iterator() : index_(-1) {}
183 explicit Iterator(Handle<Type> type) : type_(type), index_(-1) {
184 Advance();
185 }
186
187 inline bool matches(Handle<Type> type);
188 inline Handle<Type> get_type();
189
190 Handle<Type> type_;
191 int index_;
192 };
193
194 Iterator<Map> Classes() {
195 if (this->is_bitset()) return Iterator<Map>();
196 return Iterator<Map>(this->handle());
197 }
198 Iterator<v8::internal::Object> Constants() {
199 if (this->is_bitset()) return Iterator<v8::internal::Object>();
200 return Iterator<v8::internal::Object>(this->handle());
201 }
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000202
danno@chromium.orgbee51992013-07-10 14:57:15 +0000203 static Type* cast(v8::internal::Object* object) {
204 Type* t = static_cast<Type*>(object);
205 ASSERT(t->is_bitset() || t->is_class() ||
206 t->is_constant() || t->is_union());
207 return t;
208 }
209
210#ifdef OBJECT_PRINT
211 void TypePrint();
212 void TypePrint(FILE* out);
213#endif
214
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000215 private:
216 // A union is a fixed array containing types. Invariants:
217 // - its length is at least 2
218 // - at most one field is a bitset, and it must go into index 0
219 // - no field is a union
220 typedef FixedArray Unioned;
221
222 enum {
danno@chromium.orgbee51992013-07-10 14:57:15 +0000223 #define DECLARE_TYPE(type, value) k##type = (value),
224 TYPE_LIST(DECLARE_TYPE)
225 #undef DECLARE_TYPE
226 kUnusedEOL = 0
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000227 };
228
mstarzinger@chromium.orga2e1a402013-10-15 08:25:05 +0000229 bool is_none() { return this == None(); }
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000230 bool is_bitset() { return this->IsSmi(); }
231 bool is_class() { return this->IsMap(); }
danno@chromium.org1fd77d52013-06-07 16:01:45 +0000232 bool is_constant() { return this->IsBox(); }
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000233 bool is_union() { return this->IsFixedArray(); }
234
mvstanton@chromium.orgdd6d9ee2013-10-11 10:35:37 +0000235 bool SlowIs(Type* that);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +0000236
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000237 int as_bitset() { return Smi::cast(this)->value(); }
238 Handle<Map> as_class() { return Handle<Map>::cast(handle()); }
danno@chromium.org41728482013-06-12 22:31:22 +0000239 Handle<v8::internal::Object> as_constant() {
240 Handle<Box> box = Handle<Box>::cast(handle());
241 return v8::internal::handle(box->value(), box->GetIsolate());
242 }
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000243 Handle<Unioned> as_union() { return Handle<Unioned>::cast(handle()); }
244
245 Handle<Type> handle() { return handle_via_isolate_of(this); }
246 Handle<Type> handle_via_isolate_of(Type* type) {
247 ASSERT(type->IsHeapObject());
248 return v8::internal::handle(this, HeapObject::cast(type)->GetIsolate());
249 }
250
251 static Type* from_bitset(int bitset) {
252 return static_cast<Type*>(Object::cast(Smi::FromInt(bitset)));
253 }
254 static Type* from_handle(Handle<HeapObject> handle) {
255 return static_cast<Type*>(Object::cast(*handle));
256 }
257
258 static Handle<Type> union_get(Handle<Unioned> unioned, int i) {
259 Type* type = static_cast<Type*>(unioned->get(i));
260 ASSERT(!type->is_union());
261 return type->handle_via_isolate_of(from_handle(unioned));
262 }
263
264 int LubBitset(); // least upper bound that's a bitset
265 int GlbBitset(); // greatest lower bound that's a bitset
266 bool InUnion(Handle<Unioned> unioned, int current_size);
267 int ExtendUnion(Handle<Unioned> unioned, int current_size);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +0000268 int ExtendIntersection(
269 Handle<Unioned> unioned, Handle<Type> type, int current_size);
danno@chromium.orgbee51992013-07-10 14:57:15 +0000270
271 static const char* GetComposedName(int type) {
272 switch (type) {
273 #define PRINT_COMPOSED_TYPE(type, value) \
274 case k##type: \
275 return # type;
276 COMPOSED_TYPE_LIST(PRINT_COMPOSED_TYPE)
277 #undef PRINT_COMPOSED_TYPE
278 }
279 return NULL;
280 }
281
282 static const char* GetPrimitiveName(int type) {
283 switch (type) {
284 #define PRINT_PRIMITIVE_TYPE(type, value) \
285 case k##type: \
286 return # type;
287 PRIMITIVE_TYPE_LIST(PRINT_PRIMITIVE_TYPE)
288 #undef PRINT_PRIMITIVE_TYPE
289 default:
290 UNREACHABLE();
291 return "InvalidType";
292 }
293 }
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000294};
295
danno@chromium.org169691d2013-07-15 08:01:13 +0000296
297// A simple struct to represent a pair of lower/upper type bounds.
298struct Bounds {
299 Handle<Type> lower;
300 Handle<Type> upper;
301
302 Bounds() {}
mstarzinger@chromium.orga2e1a402013-10-15 08:25:05 +0000303 Bounds(Handle<Type> l, Handle<Type> u) : lower(l), upper(u) {
304 ASSERT(lower->Is(upper));
305 }
306 Bounds(Type* l, Type* u, Isolate* isl) : lower(l, isl), upper(u, isl) {
307 ASSERT(lower->Is(upper));
308 }
309 explicit Bounds(Handle<Type> t) : lower(t), upper(t) {
310 ASSERT(lower->Is(upper));
311 }
312 Bounds(Type* t, Isolate* isl) : lower(t, isl), upper(t, isl) {
313 ASSERT(lower->Is(upper));
314 }
danno@chromium.org169691d2013-07-15 08:01:13 +0000315
danno@chromium.org59400602013-08-13 17:09:37 +0000316 // Unrestricted bounds.
317 static Bounds Unbounded(Isolate* isl) {
318 return Bounds(Type::None(), Type::Any(), isl);
319 }
320
danno@chromium.org169691d2013-07-15 08:01:13 +0000321 // Meet: both b1 and b2 are known to hold.
322 static Bounds Both(Bounds b1, Bounds b2, Isolate* isl) {
mstarzinger@chromium.orga2e1a402013-10-15 08:25:05 +0000323 Handle<Type> lower(Type::Union(b1.lower, b2.lower), isl);
324 Handle<Type> upper(Type::Intersect(b1.upper, b2.upper), isl);
325 // Lower bounds are considered approximate, correct as necessary.
326 lower = handle(Type::Intersect(lower, upper), isl);
327 return Bounds(lower, upper);
danno@chromium.org169691d2013-07-15 08:01:13 +0000328 }
329
330 // Join: either b1 or b2 is known to hold.
331 static Bounds Either(Bounds b1, Bounds b2, Isolate* isl) {
332 return Bounds(
333 handle(Type::Intersect(b1.lower, b2.lower), isl),
334 handle(Type::Union(b1.upper, b2.upper), isl));
335 }
336
337 static Bounds NarrowLower(Bounds b, Handle<Type> t, Isolate* isl) {
mstarzinger@chromium.orga2e1a402013-10-15 08:25:05 +0000338 // Lower bounds are considered approximate, correct as necessary.
339 t = handle(Type::Intersect(t, b.upper), isl);
danno@chromium.org169691d2013-07-15 08:01:13 +0000340 return Bounds(handle(Type::Union(b.lower, t), isl), b.upper);
341 }
342 static Bounds NarrowUpper(Bounds b, Handle<Type> t, Isolate* isl) {
mstarzinger@chromium.orga2e1a402013-10-15 08:25:05 +0000343 return Bounds(
344 handle(Type::Intersect(b.lower, t), isl),
345 handle(Type::Intersect(b.upper, t), isl));
danno@chromium.org169691d2013-07-15 08:01:13 +0000346 }
347};
348
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000349} } // namespace v8::internal
350
351#endif // V8_TYPES_H_