| 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/property-access-info.h" |
| 10 #include "src/field-index-inl.h" | 10 #include "src/field-index-inl.h" |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 80 | 80 |
| 81 | 81 |
| 82 bool PropertyAccessInfoFactory::ComputePropertyAccessInfo( | 82 bool PropertyAccessInfoFactory::ComputePropertyAccessInfo( |
| 83 Handle<Map> map, Handle<Name> name, PropertyAccessMode access_mode, | 83 Handle<Map> map, Handle<Name> name, PropertyAccessMode access_mode, |
| 84 PropertyAccessInfo* access_info) { | 84 PropertyAccessInfo* access_info) { |
| 85 // Check if it is safe to inline property access for the {map}. | 85 // Check if it is safe to inline property access for the {map}. |
| 86 if (!CanInlinePropertyAccess(map)) return false; | 86 if (!CanInlinePropertyAccess(map)) return false; |
| 87 | 87 |
| 88 // Compute the receiver type. | 88 // Compute the receiver type. |
| 89 Handle<Map> receiver_map = map; | 89 Handle<Map> receiver_map = map; |
| 90 Type* receiver_type = Type::Class(receiver_map, zone()); | |
| 91 | 90 |
| 92 // We support fast inline cases for certain JSObject getters. | 91 // We support fast inline cases for certain JSObject getters. |
| 93 if (access_mode == PropertyAccessMode::kLoad) { | 92 if (access_mode == PropertyAccessMode::kLoad && |
| 94 // Check for special JSObject field accessors. | 93 LookupSpecialFieldAccessor(map, name, access_info)) { |
| 95 int offset; | 94 return true; |
| 96 if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) { | |
| 97 FieldIndex field_index = FieldIndex::ForInObjectOffset(offset); | |
| 98 Type* field_type = Type::Tagged(); | |
| 99 if (map->IsStringMap()) { | |
| 100 DCHECK(Name::Equals(factory()->length_string(), name)); | |
| 101 // The String::length property is always a smi in the range | |
| 102 // [0, String::kMaxLength]. | |
| 103 field_type = type_cache_.kStringLengthType; | |
| 104 } else if (map->IsJSArrayMap()) { | |
| 105 DCHECK(Name::Equals(factory()->length_string(), name)); | |
| 106 // The JSArray::length property is a smi in the range | |
| 107 // [0, FixedDoubleArray::kMaxLength] in case of fast double | |
| 108 // elements, a smi in the range [0, FixedArray::kMaxLength] | |
| 109 // in case of other fast elements, and [0, kMaxUInt32] in | |
| 110 // case of other arrays. | |
| 111 if (IsFastDoubleElementsKind(map->elements_kind())) { | |
| 112 field_type = type_cache_.kFixedDoubleArrayLengthType; | |
| 113 } else if (IsFastElementsKind(map->elements_kind())) { | |
| 114 field_type = type_cache_.kFixedArrayLengthType; | |
| 115 } else { | |
| 116 field_type = type_cache_.kJSArrayLengthType; | |
| 117 } | |
| 118 } | |
| 119 *access_info = | |
| 120 PropertyAccessInfo::DataField(receiver_type, field_index, field_type); | |
| 121 return true; | |
| 122 } | |
| 123 } | 95 } |
| 124 | 96 |
| 125 MaybeHandle<JSObject> holder; | 97 MaybeHandle<JSObject> holder; |
| 126 while (true) { | 98 while (true) { |
| 127 // Lookup the named property on the {map}. | 99 // Lookup the named property on the {map}. |
| 128 Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate()); | 100 Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate()); |
| 129 int const number = descriptors->SearchWithCache(*name, *map); | 101 int const number = descriptors->SearchWithCache(*name, *map); |
| 130 if (number != DescriptorArray::kNotFound) { | 102 if (number != DescriptorArray::kNotFound) { |
| 131 PropertyDetails const details = descriptors->GetDetails(number); | 103 PropertyDetails const details = descriptors->GetDetails(number); |
| 132 if (access_mode == PropertyAccessMode::kStore) { | 104 if (access_mode == PropertyAccessMode::kStore) { |
| 133 // Don't bother optimizing stores to read-only properties. | 105 // Don't bother optimizing stores to read-only properties. |
| 134 if (details.IsReadOnly()) { | 106 if (details.IsReadOnly()) { |
| 135 return false; | 107 return false; |
| 136 } | 108 } |
| 137 // Check for store to data property on a prototype. | 109 // Check for store to data property on a prototype. |
| 138 if (details.kind() == kData && !holder.is_null()) { | 110 if (details.kind() == kData && !holder.is_null()) { |
| 139 // We need to add the data field to the receiver. Leave the loop | 111 // Store to property not found on the receiver but on a prototype, we |
| 140 // and check whether we already have a transition for this field. | 112 // need to transition to a new data property. |
| 141 // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) | 113 // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) |
| 142 break; | 114 return LookupTransition(receiver_map, name, holder, access_info); |
| 143 } | 115 } |
| 144 } | 116 } |
| 145 if (details.type() == DATA_CONSTANT) { | 117 if (details.type() == DATA_CONSTANT) { |
| 146 *access_info = PropertyAccessInfo::DataConstant( | 118 *access_info = PropertyAccessInfo::DataConstant( |
| 147 receiver_type, handle(descriptors->GetValue(number), isolate()), | 119 Type::Class(receiver_map, zone()), |
| 148 holder); | 120 handle(descriptors->GetValue(number), isolate()), holder); |
| 149 return true; | 121 return true; |
| 150 } else if (details.type() == DATA) { | 122 } else if (details.type() == DATA) { |
| 151 int index = descriptors->GetFieldIndex(number); | 123 int index = descriptors->GetFieldIndex(number); |
| 152 Representation field_representation = details.representation(); | 124 Representation field_representation = details.representation(); |
| 153 FieldIndex field_index = FieldIndex::ForPropertyIndex( | 125 FieldIndex field_index = FieldIndex::ForPropertyIndex( |
| 154 *map, index, field_representation.IsDouble()); | 126 *map, index, field_representation.IsDouble()); |
| 155 Type* field_type = Type::Tagged(); | 127 Type* field_type = Type::Tagged(); |
| 156 if (field_representation.IsSmi()) { | 128 if (field_representation.IsSmi()) { |
| 157 field_type = type_cache_.kSmi; | 129 field_type = type_cache_.kSmi; |
| 158 } else if (field_representation.IsDouble()) { | 130 } else if (field_representation.IsDouble()) { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 173 // TODO(bmeurer): It would be awesome to make this saner in the | 145 // TODO(bmeurer): It would be awesome to make this saner in the |
| 174 // runtime/GC interaction. | 146 // runtime/GC interaction. |
| 175 field_type = Type::TaggedPointer(); | 147 field_type = Type::TaggedPointer(); |
| 176 } else if (!Type::Any()->Is(field_type)) { | 148 } else if (!Type::Any()->Is(field_type)) { |
| 177 // Add proper code dependencies in case of stable field map(s). | 149 // Add proper code dependencies in case of stable field map(s). |
| 178 Handle<Map> field_owner_map(map->FindFieldOwner(number), isolate()); | 150 Handle<Map> field_owner_map(map->FindFieldOwner(number), isolate()); |
| 179 dependencies()->AssumeFieldType(field_owner_map); | 151 dependencies()->AssumeFieldType(field_owner_map); |
| 180 } | 152 } |
| 181 DCHECK(field_type->Is(Type::TaggedPointer())); | 153 DCHECK(field_type->Is(Type::TaggedPointer())); |
| 182 } | 154 } |
| 183 *access_info = PropertyAccessInfo::DataField(receiver_type, field_index, | 155 *access_info = PropertyAccessInfo::DataField( |
| 184 field_type, holder); | 156 Type::Class(receiver_map, zone()), field_index, field_type, holder); |
| 185 return true; | 157 return true; |
| 186 } else { | 158 } else { |
| 187 // TODO(bmeurer): Add support for accessors. | 159 // TODO(bmeurer): Add support for accessors. |
| 188 return false; | 160 return false; |
| 189 } | 161 } |
| 190 } | 162 } |
| 191 | 163 |
| 192 // Don't search on the prototype chain for special indices in case of | 164 // Don't search on the prototype chain for special indices in case of |
| 193 // integer indexed exotic objects (see ES6 section 9.4.5). | 165 // integer indexed exotic objects (see ES6 section 9.4.5). |
| 194 if (map->IsJSTypedArrayMap() && name->IsString() && | 166 if (map->IsJSTypedArrayMap() && name->IsString() && |
| (...skipping 11 matching lines...) Expand all Loading... |
| 206 Handle<JSFunction> constructor; | 178 Handle<JSFunction> constructor; |
| 207 if (Map::GetConstructorFunction(map, native_context()) | 179 if (Map::GetConstructorFunction(map, native_context()) |
| 208 .ToHandle(&constructor)) { | 180 .ToHandle(&constructor)) { |
| 209 map = handle(constructor->initial_map(), isolate()); | 181 map = handle(constructor->initial_map(), isolate()); |
| 210 DCHECK(map->prototype()->IsJSObject()); | 182 DCHECK(map->prototype()->IsJSObject()); |
| 211 } else if (map->prototype()->IsNull()) { | 183 } else if (map->prototype()->IsNull()) { |
| 212 // Store to property not found on the receiver or any prototype, we need | 184 // Store to property not found on the receiver or any prototype, we need |
| 213 // to transition to a new data property. | 185 // to transition to a new data property. |
| 214 // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) | 186 // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) |
| 215 if (access_mode == PropertyAccessMode::kStore) { | 187 if (access_mode == PropertyAccessMode::kStore) { |
| 216 break; | 188 return LookupTransition(receiver_map, name, holder, access_info); |
| 217 } | 189 } |
| 218 // TODO(bmeurer): Handle the not found case if the prototype is null. | 190 // TODO(bmeurer): Handle the not found case if the prototype is null. |
| 219 return false; | 191 return false; |
| 220 } else { | 192 } else { |
| 221 return false; | 193 return false; |
| 222 } | 194 } |
| 223 } | 195 } |
| 224 Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate()); | 196 Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate()); |
| 225 if (map_prototype->map()->is_deprecated()) { | 197 if (map_prototype->map()->is_deprecated()) { |
| 226 // Try to migrate the prototype object so we don't embed the deprecated | 198 // Try to migrate the prototype object so we don't embed the deprecated |
| 227 // map into the optimized code. | 199 // map into the optimized code. |
| 228 JSObject::TryMigrateInstance(map_prototype); | 200 JSObject::TryMigrateInstance(map_prototype); |
| 229 } | 201 } |
| 230 map = handle(map_prototype->map(), isolate()); | 202 map = handle(map_prototype->map(), isolate()); |
| 231 holder = map_prototype; | 203 holder = map_prototype; |
| 232 | 204 |
| 233 // Check if it is safe to inline property access for the {map}. | 205 // Check if it is safe to inline property access for the {map}. |
| 234 if (!CanInlinePropertyAccess(map)) return false; | 206 if (!CanInlinePropertyAccess(map)) return false; |
| 235 } | 207 } |
| 236 DCHECK_EQ(PropertyAccessMode::kStore, access_mode); | 208 return false; |
| 209 } |
| 237 | 210 |
| 238 // Check if the {receiver_map} has a data transition with the given {name}. | 211 |
| 239 if (receiver_map->unused_property_fields() == 0) return false; | 212 bool PropertyAccessInfoFactory::LookupSpecialFieldAccessor( |
| 240 if (Map* transition = TransitionArray::SearchTransition(*receiver_map, kData, | 213 Handle<Map> map, Handle<Name> name, PropertyAccessInfo* access_info) { |
| 241 *name, NONE)) { | 214 // Check for special JSObject field accessors. |
| 242 Handle<Map> transition_map(transition, isolate()); | 215 int offset; |
| 216 if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) { |
| 217 FieldIndex field_index = FieldIndex::ForInObjectOffset(offset); |
| 218 Type* field_type = Type::Tagged(); |
| 219 if (map->IsStringMap()) { |
| 220 DCHECK(Name::Equals(factory()->length_string(), name)); |
| 221 // The String::length property is always a smi in the range |
| 222 // [0, String::kMaxLength]. |
| 223 field_type = type_cache_.kStringLengthType; |
| 224 } else if (map->IsJSArrayMap()) { |
| 225 DCHECK(Name::Equals(factory()->length_string(), name)); |
| 226 // The JSArray::length property is a smi in the range |
| 227 // [0, FixedDoubleArray::kMaxLength] in case of fast double |
| 228 // elements, a smi in the range [0, FixedArray::kMaxLength] |
| 229 // in case of other fast elements, and [0, kMaxUInt32] in |
| 230 // case of other arrays. |
| 231 if (IsFastDoubleElementsKind(map->elements_kind())) { |
| 232 field_type = type_cache_.kFixedDoubleArrayLengthType; |
| 233 } else if (IsFastElementsKind(map->elements_kind())) { |
| 234 field_type = type_cache_.kFixedArrayLengthType; |
| 235 } else { |
| 236 field_type = type_cache_.kJSArrayLengthType; |
| 237 } |
| 238 } |
| 239 *access_info = PropertyAccessInfo::DataField(Type::Class(map, zone()), |
| 240 field_index, field_type); |
| 241 return true; |
| 242 } |
| 243 return false; |
| 244 } |
| 245 |
| 246 |
| 247 bool PropertyAccessInfoFactory::LookupTransition( |
| 248 Handle<Map> map, Handle<Name> name, MaybeHandle<JSObject> holder, |
| 249 PropertyAccessInfo* access_info) { |
| 250 // Check if the {map} has a data transition with the given {name}. |
| 251 if (map->unused_property_fields() == 0) return false; |
| 252 Handle<Map> transition_map; |
| 253 if (TransitionArray::SearchTransition(map, kData, name, NONE) |
| 254 .ToHandle(&transition_map)) { |
| 243 int const number = transition_map->LastAdded(); | 255 int const number = transition_map->LastAdded(); |
| 244 PropertyDetails const details = | 256 PropertyDetails const details = |
| 245 transition_map->instance_descriptors()->GetDetails(number); | 257 transition_map->instance_descriptors()->GetDetails(number); |
| 246 // Don't bother optimizing stores to read-only properties. | 258 // Don't bother optimizing stores to read-only properties. |
| 247 if (details.IsReadOnly()) return false; | 259 if (details.IsReadOnly()) return false; |
| 248 // TODO(bmeurer): Handle transition to data constant? | 260 // TODO(bmeurer): Handle transition to data constant? |
| 249 if (details.type() != DATA) return false; | 261 if (details.type() != DATA) return false; |
| 250 int const index = details.field_index(); | 262 int const index = details.field_index(); |
| 251 Representation field_representation = details.representation(); | 263 Representation field_representation = details.representation(); |
| 252 FieldIndex field_index = FieldIndex::ForPropertyIndex( | 264 FieldIndex field_index = FieldIndex::ForPropertyIndex( |
| 253 *transition_map, index, field_representation.IsDouble()); | 265 *transition_map, index, field_representation.IsDouble()); |
| 254 Type* field_type = Type::Tagged(); | 266 Type* field_type = Type::Tagged(); |
| 255 if (field_representation.IsSmi()) { | 267 if (field_representation.IsSmi()) { |
| 256 field_type = type_cache_.kSmi; | 268 field_type = type_cache_.kSmi; |
| 257 } else if (field_representation.IsDouble()) { | 269 } else if (field_representation.IsDouble()) { |
| 258 // TODO(bmeurer): Add support for storing to double fields. | 270 field_type = type_cache_.kFloat64; |
| 259 return false; | |
| 260 } else if (field_representation.IsHeapObject()) { | 271 } else if (field_representation.IsHeapObject()) { |
| 261 // Extract the field type from the property details (make sure its | 272 // Extract the field type from the property details (make sure its |
| 262 // representation is TaggedPointer to reflect the heap object case). | 273 // representation is TaggedPointer to reflect the heap object case). |
| 263 field_type = Type::Intersect( | 274 field_type = Type::Intersect( |
| 264 Type::Convert<HeapType>( | 275 Type::Convert<HeapType>( |
| 265 handle( | 276 handle( |
| 266 transition_map->instance_descriptors()->GetFieldType(number), | 277 transition_map->instance_descriptors()->GetFieldType(number), |
| 267 isolate()), | 278 isolate()), |
| 268 zone()), | 279 zone()), |
| 269 Type::TaggedPointer(), zone()); | 280 Type::TaggedPointer(), zone()); |
| 270 if (field_type->Is(Type::None())) { | 281 if (field_type->Is(Type::None())) { |
| 271 // Store is not safe if the field type was cleared. | 282 // Store is not safe if the field type was cleared. |
| 272 return false; | 283 return false; |
| 273 } else if (!Type::Any()->Is(field_type)) { | 284 } else if (!Type::Any()->Is(field_type)) { |
| 274 // Add proper code dependencies in case of stable field map(s). | 285 // Add proper code dependencies in case of stable field map(s). |
| 275 Handle<Map> field_owner_map(transition_map->FindFieldOwner(number), | 286 Handle<Map> field_owner_map(transition_map->FindFieldOwner(number), |
| 276 isolate()); | 287 isolate()); |
| 277 dependencies()->AssumeFieldType(field_owner_map); | 288 dependencies()->AssumeFieldType(field_owner_map); |
| 278 } | 289 } |
| 279 DCHECK(field_type->Is(Type::TaggedPointer())); | 290 DCHECK(field_type->Is(Type::TaggedPointer())); |
| 280 } | 291 } |
| 281 dependencies()->AssumeMapNotDeprecated(transition_map); | 292 dependencies()->AssumeMapNotDeprecated(transition_map); |
| 282 *access_info = PropertyAccessInfo::DataField( | 293 *access_info = |
| 283 receiver_type, field_index, field_type, holder, transition_map); | 294 PropertyAccessInfo::DataField(Type::Class(map, zone()), field_index, |
| 295 field_type, holder, transition_map); |
| 284 return true; | 296 return true; |
| 285 } | 297 } |
| 286 return false; | 298 return false; |
| 287 } | 299 } |
| 288 | 300 |
| 289 | 301 |
| 290 bool PropertyAccessInfoFactory::ComputePropertyAccessInfos( | 302 bool PropertyAccessInfoFactory::ComputePropertyAccessInfos( |
| 291 MapHandleList const& maps, Handle<Name> name, | 303 MapHandleList const& maps, Handle<Name> name, |
| 292 PropertyAccessMode access_mode, | 304 PropertyAccessMode access_mode, |
| 293 ZoneVector<PropertyAccessInfo>* access_infos) { | 305 ZoneVector<PropertyAccessInfo>* access_infos) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 304 } | 316 } |
| 305 | 317 |
| 306 | 318 |
| 307 Factory* PropertyAccessInfoFactory::factory() const { | 319 Factory* PropertyAccessInfoFactory::factory() const { |
| 308 return isolate()->factory(); | 320 return isolate()->factory(); |
| 309 } | 321 } |
| 310 | 322 |
| 311 } // namespace compiler | 323 } // namespace compiler |
| 312 } // namespace internal | 324 } // namespace internal |
| 313 } // namespace v8 | 325 } // namespace v8 |
| OLD | NEW |