OLD | NEW |
1 // Copyright 2015 the V8 project authors. All rights reserved. | 1 // Copyright 2015 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <ostream> | 5 #include <ostream> |
6 | 6 |
7 #include "src/accessors.h" | 7 #include "src/accessors.h" |
8 #include "src/compilation-dependencies.h" | 8 #include "src/compilation-dependencies.h" |
9 #include "src/compiler/property-access-info.h" | 9 #include "src/compiler/access-info.h" |
10 #include "src/field-index-inl.h" | 10 #include "src/field-index-inl.h" |
11 #include "src/objects-inl.h" // TODO(mstarzinger): Temporary cycle breaker! | 11 #include "src/objects-inl.h" // TODO(mstarzinger): Temporary cycle breaker! |
12 #include "src/type-cache.h" | 12 #include "src/type-cache.h" |
13 #include "src/types-inl.h" | 13 #include "src/types-inl.h" |
14 | 14 |
15 namespace v8 { | 15 namespace v8 { |
16 namespace internal { | 16 namespace internal { |
17 namespace compiler { | 17 namespace compiler { |
18 | 18 |
19 std::ostream& operator<<(std::ostream& os, PropertyAccessMode access_mode) { | 19 namespace { |
| 20 |
| 21 bool CanInlineElementAccess(Handle<Map> map) { |
| 22 // TODO(bmeurer): IsJSObjectMap |
| 23 // TODO(bmeurer): !map->has_dictionary_elements() |
| 24 // TODO(bmeurer): !map->has_sloppy_arguments_elements() |
| 25 return map->IsJSArrayMap() && map->has_fast_elements() && |
| 26 !map->has_indexed_interceptor() && !map->is_access_check_needed(); |
| 27 } |
| 28 |
| 29 |
| 30 bool CanInlinePropertyAccess(Handle<Map> map) { |
| 31 // TODO(bmeurer): Add support for Number primitives. |
| 32 // if (map->instance_type() == HEAP_NUMBER_TYPE) return false; |
| 33 if (map->instance_type() < FIRST_NONSTRING_TYPE) return true; |
| 34 return map->IsJSObjectMap() && !map->is_dictionary_map() && |
| 35 !map->has_named_interceptor() && |
| 36 // TODO(verwaest): Whitelist contexts to which we have access. |
| 37 !map->is_access_check_needed(); |
| 38 } |
| 39 |
| 40 } // namespace |
| 41 |
| 42 |
| 43 std::ostream& operator<<(std::ostream& os, AccessMode access_mode) { |
20 switch (access_mode) { | 44 switch (access_mode) { |
21 case PropertyAccessMode::kLoad: | 45 case AccessMode::kLoad: |
22 return os << "Load"; | 46 return os << "Load"; |
23 case PropertyAccessMode::kStore: | 47 case AccessMode::kStore: |
24 return os << "Store"; | 48 return os << "Store"; |
25 } | 49 } |
26 UNREACHABLE(); | 50 UNREACHABLE(); |
27 return os; | 51 return os; |
28 } | 52 } |
29 | 53 |
30 | 54 |
31 // static | 55 // static |
32 PropertyAccessInfo PropertyAccessInfo::NotFound(Type* receiver_type, | 56 PropertyAccessInfo PropertyAccessInfo::NotFound(Type* receiver_type, |
33 MaybeHandle<JSObject> holder) { | 57 MaybeHandle<JSObject> holder) { |
(...skipping 11 matching lines...) Expand all Loading... |
45 | 69 |
46 // static | 70 // static |
47 PropertyAccessInfo PropertyAccessInfo::DataField( | 71 PropertyAccessInfo PropertyAccessInfo::DataField( |
48 Type* receiver_type, FieldIndex field_index, Type* field_type, | 72 Type* receiver_type, FieldIndex field_index, Type* field_type, |
49 MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map) { | 73 MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map) { |
50 return PropertyAccessInfo(holder, transition_map, field_index, field_type, | 74 return PropertyAccessInfo(holder, transition_map, field_index, field_type, |
51 receiver_type); | 75 receiver_type); |
52 } | 76 } |
53 | 77 |
54 | 78 |
| 79 ElementAccessInfo::ElementAccessInfo() : receiver_type_(Type::None()) {} |
| 80 |
| 81 |
55 PropertyAccessInfo::PropertyAccessInfo() | 82 PropertyAccessInfo::PropertyAccessInfo() |
56 : kind_(kInvalid), receiver_type_(Type::None()), field_type_(Type::Any()) {} | 83 : kind_(kInvalid), receiver_type_(Type::None()), field_type_(Type::Any()) {} |
57 | 84 |
58 | 85 |
59 PropertyAccessInfo::PropertyAccessInfo(MaybeHandle<JSObject> holder, | 86 PropertyAccessInfo::PropertyAccessInfo(MaybeHandle<JSObject> holder, |
60 Type* receiver_type) | 87 Type* receiver_type) |
61 : kind_(kNotFound), | 88 : kind_(kNotFound), |
62 receiver_type_(receiver_type), | 89 receiver_type_(receiver_type), |
63 holder_(holder), | 90 holder_(holder), |
64 field_type_(Type::Any()) {} | 91 field_type_(Type::Any()) {} |
(...skipping 14 matching lines...) Expand all Loading... |
79 FieldIndex field_index, Type* field_type, | 106 FieldIndex field_index, Type* field_type, |
80 Type* receiver_type) | 107 Type* receiver_type) |
81 : kind_(kDataField), | 108 : kind_(kDataField), |
82 receiver_type_(receiver_type), | 109 receiver_type_(receiver_type), |
83 transition_map_(transition_map), | 110 transition_map_(transition_map), |
84 holder_(holder), | 111 holder_(holder), |
85 field_index_(field_index), | 112 field_index_(field_index), |
86 field_type_(field_type) {} | 113 field_type_(field_type) {} |
87 | 114 |
88 | 115 |
89 PropertyAccessInfoFactory::PropertyAccessInfoFactory( | 116 AccessInfoFactory::AccessInfoFactory(CompilationDependencies* dependencies, |
90 CompilationDependencies* dependencies, Handle<Context> native_context, | 117 Handle<Context> native_context, Zone* zone) |
91 Zone* zone) | |
92 : dependencies_(dependencies), | 118 : dependencies_(dependencies), |
93 native_context_(native_context), | 119 native_context_(native_context), |
94 isolate_(native_context->GetIsolate()), | 120 isolate_(native_context->GetIsolate()), |
95 type_cache_(TypeCache::Get()), | 121 type_cache_(TypeCache::Get()), |
96 zone_(zone) {} | 122 zone_(zone) {} |
97 | 123 |
98 | 124 |
99 namespace { | 125 bool AccessInfoFactory::ComputeElementAccessInfo( |
| 126 Handle<Map> map, AccessMode access_mode, ElementAccessInfo* access_info) { |
| 127 // Check if it is safe to inline element access for the {map}. |
| 128 if (!CanInlineElementAccess(map)) return false; |
100 | 129 |
101 bool CanInlinePropertyAccess(Handle<Map> map) { | 130 // TODO(bmeurer): Add support for holey elements. |
102 // TODO(bmeurer): Do something about the number stuff. | 131 ElementsKind elements_kind = map->elements_kind(); |
103 if (map->instance_type() == HEAP_NUMBER_TYPE) return false; | 132 if (IsHoleyElementsKind(elements_kind)) return false; |
104 if (map->instance_type() < FIRST_NONSTRING_TYPE) return true; | 133 |
105 return map->IsJSObjectMap() && !map->is_dictionary_map() && | 134 // Certain (monomorphic) stores need a prototype chain check because shape |
106 !map->has_named_interceptor() && | 135 // changes could allow callbacks on elements in the chain that are not |
107 // TODO(verwaest): Whitelist contexts to which we have access. | 136 // compatible with monomorphic keyed stores. |
108 !map->is_access_check_needed(); | 137 MaybeHandle<JSObject> holder; |
| 138 if (access_mode == AccessMode::kStore && map->prototype()->IsJSObject()) { |
| 139 for (PrototypeIterator i(map); !i.IsAtEnd(); i.Advance()) { |
| 140 Handle<JSReceiver> prototype = |
| 141 PrototypeIterator::GetCurrent<JSReceiver>(i); |
| 142 if (!prototype->IsJSObject()) return false; |
| 143 holder = Handle<JSObject>::cast(prototype); |
| 144 } |
| 145 } |
| 146 |
| 147 *access_info = |
| 148 ElementAccessInfo(Type::Class(map, zone()), elements_kind, holder); |
| 149 return true; |
109 } | 150 } |
110 | 151 |
111 } // namespace | 152 |
| 153 bool AccessInfoFactory::ComputeElementAccessInfos( |
| 154 MapHandleList const& maps, AccessMode access_mode, |
| 155 ZoneVector<ElementAccessInfo>* access_infos) { |
| 156 for (Handle<Map> map : maps) { |
| 157 if (Map::TryUpdate(map).ToHandle(&map)) { |
| 158 ElementAccessInfo access_info; |
| 159 if (!ComputeElementAccessInfo(map, access_mode, &access_info)) { |
| 160 return false; |
| 161 } |
| 162 access_infos->push_back(access_info); |
| 163 } |
| 164 } |
| 165 return true; |
| 166 } |
112 | 167 |
113 | 168 |
114 bool PropertyAccessInfoFactory::ComputePropertyAccessInfo( | 169 bool AccessInfoFactory::ComputePropertyAccessInfo( |
115 Handle<Map> map, Handle<Name> name, PropertyAccessMode access_mode, | 170 Handle<Map> map, Handle<Name> name, AccessMode access_mode, |
116 PropertyAccessInfo* access_info) { | 171 PropertyAccessInfo* access_info) { |
117 // Check if it is safe to inline property access for the {map}. | 172 // Check if it is safe to inline property access for the {map}. |
118 if (!CanInlinePropertyAccess(map)) return false; | 173 if (!CanInlinePropertyAccess(map)) return false; |
119 | 174 |
120 // Compute the receiver type. | 175 // Compute the receiver type. |
121 Handle<Map> receiver_map = map; | 176 Handle<Map> receiver_map = map; |
122 | 177 |
123 // We support fast inline cases for certain JSObject getters. | 178 // We support fast inline cases for certain JSObject getters. |
124 if (access_mode == PropertyAccessMode::kLoad && | 179 if (access_mode == AccessMode::kLoad && |
125 LookupSpecialFieldAccessor(map, name, access_info)) { | 180 LookupSpecialFieldAccessor(map, name, access_info)) { |
126 return true; | 181 return true; |
127 } | 182 } |
128 | 183 |
129 MaybeHandle<JSObject> holder; | 184 MaybeHandle<JSObject> holder; |
130 do { | 185 do { |
131 // Lookup the named property on the {map}. | 186 // Lookup the named property on the {map}. |
132 Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate()); | 187 Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate()); |
133 int const number = descriptors->SearchWithCache(*name, *map); | 188 int const number = descriptors->SearchWithCache(*name, *map); |
134 if (number != DescriptorArray::kNotFound) { | 189 if (number != DescriptorArray::kNotFound) { |
135 PropertyDetails const details = descriptors->GetDetails(number); | 190 PropertyDetails const details = descriptors->GetDetails(number); |
136 if (access_mode == PropertyAccessMode::kStore) { | 191 if (access_mode == AccessMode::kStore) { |
137 // Don't bother optimizing stores to read-only properties. | 192 // Don't bother optimizing stores to read-only properties. |
138 if (details.IsReadOnly()) { | 193 if (details.IsReadOnly()) { |
139 return false; | 194 return false; |
140 } | 195 } |
141 // Check for store to data property on a prototype. | 196 // Check for store to data property on a prototype. |
142 if (details.kind() == kData && !holder.is_null()) { | 197 if (details.kind() == kData && !holder.is_null()) { |
143 // Store to property not found on the receiver but on a prototype, we | 198 // Store to property not found on the receiver but on a prototype, we |
144 // need to transition to a new data property. | 199 // need to transition to a new data property. |
145 // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) | 200 // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) |
146 return LookupTransition(receiver_map, name, holder, access_info); | 201 return LookupTransition(receiver_map, name, holder, access_info); |
(...skipping 16 matching lines...) Expand all Loading... |
163 field_type = type_cache_.kFloat64; | 218 field_type = type_cache_.kFloat64; |
164 } else if (field_representation.IsHeapObject()) { | 219 } else if (field_representation.IsHeapObject()) { |
165 // Extract the field type from the property details (make sure its | 220 // Extract the field type from the property details (make sure its |
166 // representation is TaggedPointer to reflect the heap object case). | 221 // representation is TaggedPointer to reflect the heap object case). |
167 field_type = Type::Intersect( | 222 field_type = Type::Intersect( |
168 Type::Convert<HeapType>( | 223 Type::Convert<HeapType>( |
169 handle(descriptors->GetFieldType(number), isolate()), zone()), | 224 handle(descriptors->GetFieldType(number), isolate()), zone()), |
170 Type::TaggedPointer(), zone()); | 225 Type::TaggedPointer(), zone()); |
171 if (field_type->Is(Type::None())) { | 226 if (field_type->Is(Type::None())) { |
172 // Store is not safe if the field type was cleared. | 227 // Store is not safe if the field type was cleared. |
173 if (access_mode == PropertyAccessMode::kStore) return false; | 228 if (access_mode == AccessMode::kStore) return false; |
174 | 229 |
175 // The field type was cleared by the GC, so we don't know anything | 230 // The field type was cleared by the GC, so we don't know anything |
176 // about the contents now. | 231 // about the contents now. |
177 // TODO(bmeurer): It would be awesome to make this saner in the | 232 // TODO(bmeurer): It would be awesome to make this saner in the |
178 // runtime/GC interaction. | 233 // runtime/GC interaction. |
179 field_type = Type::TaggedPointer(); | 234 field_type = Type::TaggedPointer(); |
180 } else if (!Type::Any()->Is(field_type)) { | 235 } else if (!Type::Any()->Is(field_type)) { |
181 // Add proper code dependencies in case of stable field map(s). | 236 // Add proper code dependencies in case of stable field map(s). |
182 Handle<Map> field_owner_map(map->FindFieldOwner(number), isolate()); | 237 Handle<Map> field_owner_map(map->FindFieldOwner(number), isolate()); |
183 dependencies()->AssumeFieldType(field_owner_map); | 238 dependencies()->AssumeFieldType(field_owner_map); |
(...skipping 25 matching lines...) Expand all Loading... |
209 // Implemented according to ES6 section 7.3.2 GetV (V, P). | 264 // Implemented according to ES6 section 7.3.2 GetV (V, P). |
210 Handle<JSFunction> constructor; | 265 Handle<JSFunction> constructor; |
211 if (Map::GetConstructorFunction(map, native_context()) | 266 if (Map::GetConstructorFunction(map, native_context()) |
212 .ToHandle(&constructor)) { | 267 .ToHandle(&constructor)) { |
213 map = handle(constructor->initial_map(), isolate()); | 268 map = handle(constructor->initial_map(), isolate()); |
214 DCHECK(map->prototype()->IsJSObject()); | 269 DCHECK(map->prototype()->IsJSObject()); |
215 } else if (map->prototype()->IsNull()) { | 270 } else if (map->prototype()->IsNull()) { |
216 // Store to property not found on the receiver or any prototype, we need | 271 // Store to property not found on the receiver or any prototype, we need |
217 // to transition to a new data property. | 272 // to transition to a new data property. |
218 // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) | 273 // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) |
219 if (access_mode == PropertyAccessMode::kStore) { | 274 if (access_mode == AccessMode::kStore) { |
220 return LookupTransition(receiver_map, name, holder, access_info); | 275 return LookupTransition(receiver_map, name, holder, access_info); |
221 } | 276 } |
222 // The property was not found, return undefined or throw depending | 277 // The property was not found, return undefined or throw depending |
223 // on the language mode of the load operation. | 278 // on the language mode of the load operation. |
224 // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver) | 279 // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver) |
225 *access_info = PropertyAccessInfo::NotFound( | 280 *access_info = PropertyAccessInfo::NotFound( |
226 Type::Class(receiver_map, zone()), holder); | 281 Type::Class(receiver_map, zone()), holder); |
227 return true; | 282 return true; |
228 } else { | 283 } else { |
229 return false; | 284 return false; |
230 } | 285 } |
231 } | 286 } |
232 Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate()); | 287 Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate()); |
233 if (map_prototype->map()->is_deprecated()) { | 288 if (map_prototype->map()->is_deprecated()) { |
234 // Try to migrate the prototype object so we don't embed the deprecated | 289 // Try to migrate the prototype object so we don't embed the deprecated |
235 // map into the optimized code. | 290 // map into the optimized code. |
236 JSObject::TryMigrateInstance(map_prototype); | 291 JSObject::TryMigrateInstance(map_prototype); |
237 } | 292 } |
238 map = handle(map_prototype->map(), isolate()); | 293 map = handle(map_prototype->map(), isolate()); |
239 holder = map_prototype; | 294 holder = map_prototype; |
240 } while (CanInlinePropertyAccess(map)); | 295 } while (CanInlinePropertyAccess(map)); |
241 return false; | 296 return false; |
242 } | 297 } |
243 | 298 |
244 | 299 |
245 bool PropertyAccessInfoFactory::ComputePropertyAccessInfos( | 300 bool AccessInfoFactory::ComputePropertyAccessInfos( |
246 MapHandleList const& maps, Handle<Name> name, | 301 MapHandleList const& maps, Handle<Name> name, AccessMode access_mode, |
247 PropertyAccessMode access_mode, | |
248 ZoneVector<PropertyAccessInfo>* access_infos) { | 302 ZoneVector<PropertyAccessInfo>* access_infos) { |
249 for (Handle<Map> map : maps) { | 303 for (Handle<Map> map : maps) { |
250 if (Map::TryUpdate(map).ToHandle(&map)) { | 304 if (Map::TryUpdate(map).ToHandle(&map)) { |
251 PropertyAccessInfo access_info; | 305 PropertyAccessInfo access_info; |
252 if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) { | 306 if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) { |
253 return false; | 307 return false; |
254 } | 308 } |
255 access_infos->push_back(access_info); | 309 access_infos->push_back(access_info); |
256 } | 310 } |
257 } | 311 } |
258 return true; | 312 return true; |
259 } | 313 } |
260 | 314 |
261 | 315 |
262 bool PropertyAccessInfoFactory::LookupSpecialFieldAccessor( | 316 bool AccessInfoFactory::LookupSpecialFieldAccessor( |
263 Handle<Map> map, Handle<Name> name, PropertyAccessInfo* access_info) { | 317 Handle<Map> map, Handle<Name> name, PropertyAccessInfo* access_info) { |
264 // Check for special JSObject field accessors. | 318 // Check for special JSObject field accessors. |
265 int offset; | 319 int offset; |
266 if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) { | 320 if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) { |
267 FieldIndex field_index = FieldIndex::ForInObjectOffset(offset); | 321 FieldIndex field_index = FieldIndex::ForInObjectOffset(offset); |
268 Type* field_type = Type::Tagged(); | 322 Type* field_type = Type::Tagged(); |
269 if (map->IsStringMap()) { | 323 if (map->IsStringMap()) { |
270 DCHECK(Name::Equals(factory()->length_string(), name)); | 324 DCHECK(Name::Equals(factory()->length_string(), name)); |
271 // The String::length property is always a smi in the range | 325 // The String::length property is always a smi in the range |
272 // [0, String::kMaxLength]. | 326 // [0, String::kMaxLength]. |
(...skipping 14 matching lines...) Expand all Loading... |
287 } | 341 } |
288 } | 342 } |
289 *access_info = PropertyAccessInfo::DataField(Type::Class(map, zone()), | 343 *access_info = PropertyAccessInfo::DataField(Type::Class(map, zone()), |
290 field_index, field_type); | 344 field_index, field_type); |
291 return true; | 345 return true; |
292 } | 346 } |
293 return false; | 347 return false; |
294 } | 348 } |
295 | 349 |
296 | 350 |
297 bool PropertyAccessInfoFactory::LookupTransition( | 351 bool AccessInfoFactory::LookupTransition(Handle<Map> map, Handle<Name> name, |
298 Handle<Map> map, Handle<Name> name, MaybeHandle<JSObject> holder, | 352 MaybeHandle<JSObject> holder, |
299 PropertyAccessInfo* access_info) { | 353 PropertyAccessInfo* access_info) { |
300 // Check if the {map} has a data transition with the given {name}. | 354 // Check if the {map} has a data transition with the given {name}. |
301 if (map->unused_property_fields() == 0) return false; | 355 if (map->unused_property_fields() == 0) return false; |
302 Handle<Map> transition_map; | 356 Handle<Map> transition_map; |
303 if (TransitionArray::SearchTransition(map, kData, name, NONE) | 357 if (TransitionArray::SearchTransition(map, kData, name, NONE) |
304 .ToHandle(&transition_map)) { | 358 .ToHandle(&transition_map)) { |
305 int const number = transition_map->LastAdded(); | 359 int const number = transition_map->LastAdded(); |
306 PropertyDetails const details = | 360 PropertyDetails const details = |
307 transition_map->instance_descriptors()->GetDetails(number); | 361 transition_map->instance_descriptors()->GetDetails(number); |
308 // Don't bother optimizing stores to read-only properties. | 362 // Don't bother optimizing stores to read-only properties. |
309 if (details.IsReadOnly()) return false; | 363 if (details.IsReadOnly()) return false; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
342 dependencies()->AssumeMapNotDeprecated(transition_map); | 396 dependencies()->AssumeMapNotDeprecated(transition_map); |
343 *access_info = | 397 *access_info = |
344 PropertyAccessInfo::DataField(Type::Class(map, zone()), field_index, | 398 PropertyAccessInfo::DataField(Type::Class(map, zone()), field_index, |
345 field_type, holder, transition_map); | 399 field_type, holder, transition_map); |
346 return true; | 400 return true; |
347 } | 401 } |
348 return false; | 402 return false; |
349 } | 403 } |
350 | 404 |
351 | 405 |
352 Factory* PropertyAccessInfoFactory::factory() const { | 406 Factory* AccessInfoFactory::factory() const { return isolate()->factory(); } |
353 return isolate()->factory(); | |
354 } | |
355 | 407 |
356 } // namespace compiler | 408 } // namespace compiler |
357 } // namespace internal | 409 } // namespace internal |
358 } // namespace v8 | 410 } // namespace v8 |
OLD | NEW |