Chromium Code Reviews| 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 "src/compiler/js-native-context-specialization.h" | 5 #include "src/compiler/js-native-context-specialization.h" |
| 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/access-builder.h" | 9 #include "src/compiler/access-builder.h" |
| 10 #include "src/compiler/js-graph.h" | 10 #include "src/compiler/js-graph.h" |
| (...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 241 simplified()->StoreField(AccessBuilder::ForPropertyCellValue()), | 241 simplified()->StoreField(AccessBuilder::ForPropertyCellValue()), |
| 242 jsgraph()->Constant(property_cell), value, effect, control); | 242 jsgraph()->Constant(property_cell), value, effect, control); |
| 243 return Replace(node, value, effect, control); | 243 return Replace(node, value, effect, control); |
| 244 } | 244 } |
| 245 | 245 |
| 246 | 246 |
| 247 // This class encapsulates all information required to access a certain | 247 // This class encapsulates all information required to access a certain |
| 248 // object property, either on the object itself or on the prototype chain. | 248 // object property, either on the object itself or on the prototype chain. |
| 249 class JSNativeContextSpecialization::PropertyAccessInfo final { | 249 class JSNativeContextSpecialization::PropertyAccessInfo final { |
| 250 public: | 250 public: |
| 251 enum Kind { kInvalid, kDataConstant, kDataField }; | 251 enum Kind { kInvalid, kDataConstant, kDataField, kTransitionToField }; |
| 252 | 252 |
| 253 static PropertyAccessInfo DataConstant(Type* receiver_type, | 253 static PropertyAccessInfo DataConstant(Type* receiver_type, |
| 254 Handle<Object> constant, | 254 Handle<Object> constant, |
| 255 MaybeHandle<JSObject> holder) { | 255 MaybeHandle<JSObject> holder) { |
| 256 return PropertyAccessInfo(holder, constant, receiver_type); | 256 return PropertyAccessInfo(holder, constant, receiver_type); |
| 257 } | 257 } |
| 258 static PropertyAccessInfo DataField(Type* receiver_type, | 258 static PropertyAccessInfo DataField( |
| 259 FieldIndex field_index, Type* field_type, | 259 Type* receiver_type, FieldIndex field_index, Type* field_type, |
| 260 MaybeHandle<JSObject> holder) { | 260 MaybeHandle<JSObject> holder = MaybeHandle<JSObject>()) { |
| 261 return PropertyAccessInfo(holder, field_index, field_type, receiver_type); | 261 return PropertyAccessInfo(holder, field_index, field_type, receiver_type); |
| 262 } | 262 } |
| 263 static PropertyAccessInfo TransitionToField(Type* receiver_type, | |
| 264 FieldIndex field_index, | |
| 265 Type* field_type, | |
| 266 Handle<Map> transition_map, | |
| 267 MaybeHandle<JSObject> holder) { | |
| 268 return PropertyAccessInfo(holder, transition_map, field_index, field_type, | |
| 269 receiver_type); | |
| 270 } | |
| 263 | 271 |
| 264 PropertyAccessInfo() : kind_(kInvalid) {} | 272 PropertyAccessInfo() : kind_(kInvalid) {} |
| 265 PropertyAccessInfo(MaybeHandle<JSObject> holder, Handle<Object> constant, | 273 PropertyAccessInfo(MaybeHandle<JSObject> holder, Handle<Object> constant, |
| 266 Type* receiver_type) | 274 Type* receiver_type) |
| 267 : kind_(kDataConstant), | 275 : kind_(kDataConstant), |
| 268 receiver_type_(receiver_type), | 276 receiver_type_(receiver_type), |
| 269 constant_(constant), | 277 constant_(constant), |
| 270 holder_(holder) {} | 278 holder_(holder) {} |
| 271 PropertyAccessInfo(MaybeHandle<JSObject> holder, FieldIndex field_index, | 279 PropertyAccessInfo(MaybeHandle<JSObject> holder, FieldIndex field_index, |
| 272 Type* field_type, Type* receiver_type) | 280 Type* field_type, Type* receiver_type) |
| 273 : kind_(kDataField), | 281 : kind_(kDataField), |
| 274 receiver_type_(receiver_type), | 282 receiver_type_(receiver_type), |
| 275 holder_(holder), | 283 holder_(holder), |
| 276 field_index_(field_index), | 284 field_index_(field_index), |
| 277 field_type_(field_type) {} | 285 field_type_(field_type) {} |
| 286 PropertyAccessInfo(MaybeHandle<JSObject> holder, Handle<Map> transition_map, | |
| 287 FieldIndex field_index, Type* field_type, | |
| 288 Type* receiver_type) | |
| 289 : kind_(kTransitionToField), | |
| 290 receiver_type_(receiver_type), | |
| 291 transition_map_(transition_map), | |
| 292 holder_(holder), | |
| 293 field_index_(field_index), | |
| 294 field_type_(field_type) {} | |
| 278 | 295 |
| 279 bool IsDataConstant() const { return kind() == kDataConstant; } | 296 bool IsDataConstant() const { return kind() == kDataConstant; } |
| 280 bool IsDataField() const { return kind() == kDataField; } | 297 bool IsDataField() const { return kind() == kDataField; } |
| 298 bool IsTransitionToField() const { return kind() == kTransitionToField; } | |
| 281 | 299 |
| 282 Kind kind() const { return kind_; } | 300 Kind kind() const { return kind_; } |
| 283 MaybeHandle<JSObject> holder() const { return holder_; } | 301 MaybeHandle<JSObject> holder() const { return holder_; } |
| 284 Handle<Object> constant() const { return constant_; } | 302 Handle<Object> constant() const { return constant_; } |
| 303 Handle<Object> transition_map() const { return transition_map_; } | |
| 285 FieldIndex field_index() const { return field_index_; } | 304 FieldIndex field_index() const { return field_index_; } |
| 286 Type* field_type() const { return field_type_; } | 305 Type* field_type() const { return field_type_; } |
| 287 Type* receiver_type() const { return receiver_type_; } | 306 Type* receiver_type() const { return receiver_type_; } |
| 288 | 307 |
| 289 private: | 308 private: |
| 290 Kind kind_; | 309 Kind kind_; |
| 291 Type* receiver_type_; | 310 Type* receiver_type_; |
| 292 Handle<Object> constant_; | 311 Handle<Object> constant_; |
| 312 Handle<Map> transition_map_; | |
| 293 MaybeHandle<JSObject> holder_; | 313 MaybeHandle<JSObject> holder_; |
| 294 FieldIndex field_index_; | 314 FieldIndex field_index_; |
| 295 Type* field_type_ = Type::Any(); | 315 Type* field_type_ = Type::Any(); |
| 296 }; | 316 }; |
| 297 | 317 |
| 298 | 318 |
| 299 namespace { | 319 namespace { |
| 300 | 320 |
| 301 bool CanInlinePropertyAccess(Handle<Map> map) { | 321 bool CanInlinePropertyAccess(Handle<Map> map) { |
| 302 // TODO(bmeurer): Do something about the number stuff. | 322 // TODO(bmeurer): Do something about the number stuff. |
| 303 if (map->instance_type() == HEAP_NUMBER_TYPE) return false; | 323 if (map->instance_type() == HEAP_NUMBER_TYPE) return false; |
| 304 if (map->instance_type() < FIRST_NONSTRING_TYPE) return true; | 324 if (map->instance_type() < FIRST_NONSTRING_TYPE) return true; |
| 305 return map->IsJSObjectMap() && !map->is_dictionary_map() && | 325 return map->IsJSObjectMap() && !map->is_dictionary_map() && |
| 306 !map->has_named_interceptor() && | 326 !map->has_named_interceptor() && |
| 307 // TODO(verwaest): Whitelist contexts to which we have access. | 327 // TODO(verwaest): Whitelist contexts to which we have access. |
| 308 !map->is_access_check_needed(); | 328 !map->is_access_check_needed(); |
| 309 } | 329 } |
| 310 | 330 |
| 311 } // namespace | 331 } // namespace |
| 312 | 332 |
| 313 | 333 |
| 314 bool JSNativeContextSpecialization::ComputePropertyAccessInfo( | 334 bool JSNativeContextSpecialization::ComputePropertyAccessInfo( |
| 315 Handle<Map> map, Handle<Name> name, PropertyAccessMode access_mode, | 335 Handle<Map> map, Handle<Name> name, PropertyAccessMode access_mode, |
| 316 PropertyAccessInfo* access_info) { | 336 PropertyAccessInfo* access_info) { |
| 317 MaybeHandle<JSObject> holder; | 337 // Check if it is safe to inline property access for the {map}. |
| 338 if (!CanInlinePropertyAccess(map)) return false; | |
| 339 | |
| 340 // Compute the receiver type. | |
| 318 Handle<Map> receiver_map = map; | 341 Handle<Map> receiver_map = map; |
| 319 Type* receiver_type = Type::Class(receiver_map, graph()->zone()); | 342 Type* receiver_type = Type::Class(receiver_map, graph()->zone()); |
| 320 while (CanInlinePropertyAccess(map)) { | 343 |
| 344 // We support fast inline cases for certain JSObject getters. | |
| 345 if (access_mode == kLoad) { | |
| 321 // Check for special JSObject field accessors. | 346 // Check for special JSObject field accessors. |
| 322 int offset; | 347 int offset; |
| 323 if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) { | 348 if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) { |
| 324 // Don't bother optimizing stores to special JSObject field accessors. | |
| 325 if (access_mode == kStore) { | |
| 326 break; | |
| 327 } | |
| 328 FieldIndex field_index = FieldIndex::ForInObjectOffset(offset); | 349 FieldIndex field_index = FieldIndex::ForInObjectOffset(offset); |
| 329 Type* field_type = Type::Tagged(); | 350 Type* field_type = Type::Tagged(); |
| 330 if (map->IsStringMap()) { | 351 if (map->IsStringMap()) { |
| 331 DCHECK(Name::Equals(factory()->length_string(), name)); | 352 DCHECK(Name::Equals(factory()->length_string(), name)); |
| 332 // The String::length property is always a smi in the range | 353 // The String::length property is always a smi in the range |
| 333 // [0, String::kMaxLength]. | 354 // [0, String::kMaxLength]. |
| 334 field_type = type_cache_.kStringLengthType; | 355 field_type = type_cache_.kStringLengthType; |
| 335 } else if (map->IsJSArrayMap()) { | 356 } else if (map->IsJSArrayMap()) { |
| 336 DCHECK(Name::Equals(factory()->length_string(), name)); | 357 DCHECK(Name::Equals(factory()->length_string(), name)); |
| 337 // The JSArray::length property is a smi in the range | 358 // The JSArray::length property is a smi in the range |
| 338 // [0, FixedDoubleArray::kMaxLength] in case of fast double | 359 // [0, FixedDoubleArray::kMaxLength] in case of fast double |
| 339 // elements, a smi in the range [0, FixedArray::kMaxLength] | 360 // elements, a smi in the range [0, FixedArray::kMaxLength] |
| 340 // in case of other fast elements, and [0, kMaxUInt32] in | 361 // in case of other fast elements, and [0, kMaxUInt32] in |
| 341 // case of other arrays. | 362 // case of other arrays. |
| 342 if (IsFastDoubleElementsKind(map->elements_kind())) { | 363 if (IsFastDoubleElementsKind(map->elements_kind())) { |
| 343 field_type = type_cache_.kFixedDoubleArrayLengthType; | 364 field_type = type_cache_.kFixedDoubleArrayLengthType; |
| 344 } else if (IsFastElementsKind(map->elements_kind())) { | 365 } else if (IsFastElementsKind(map->elements_kind())) { |
| 345 field_type = type_cache_.kFixedArrayLengthType; | 366 field_type = type_cache_.kFixedArrayLengthType; |
| 346 } else { | 367 } else { |
| 347 field_type = type_cache_.kJSArrayLengthType; | 368 field_type = type_cache_.kJSArrayLengthType; |
| 348 } | 369 } |
| 349 } | 370 } |
| 350 *access_info = PropertyAccessInfo::DataField(receiver_type, field_index, | 371 *access_info = |
| 351 field_type, holder); | 372 PropertyAccessInfo::DataField(receiver_type, field_index, field_type); |
| 352 return true; | 373 return true; |
| 353 } | 374 } |
| 375 } | |
| 354 | 376 |
| 377 MaybeHandle<JSObject> holder; | |
| 378 while (true) { | |
| 355 // Lookup the named property on the {map}. | 379 // Lookup the named property on the {map}. |
| 356 Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate()); | 380 Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate()); |
| 357 int const number = descriptors->SearchWithCache(*name, *map); | 381 int const number = descriptors->SearchWithCache(*name, *map); |
| 358 if (number != DescriptorArray::kNotFound) { | 382 if (number != DescriptorArray::kNotFound) { |
| 359 if (access_mode == kStore && !map.is_identical_to(receiver_map)) { | 383 PropertyDetails const details = descriptors->GetDetails(number); |
| 360 return false; | 384 if (access_mode == kStore) { |
| 385 // Don't bother optimizing stores to read-only properties. | |
| 386 if (details.IsReadOnly()) { | |
| 387 return false; | |
| 388 } | |
| 389 // Check for store to data property on a prototype. | |
| 390 if (details.kind() == kData && !holder.is_null()) { | |
| 391 // We need to add the data field to the receiver. Leave the loop | |
| 392 // and check whether we already have a transition for this field. | |
| 393 // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) | |
| 394 break; | |
| 395 } | |
| 361 } | 396 } |
| 362 PropertyDetails const details = descriptors->GetDetails(number); | |
| 363 if (details.type() == DATA_CONSTANT) { | 397 if (details.type() == DATA_CONSTANT) { |
| 364 *access_info = PropertyAccessInfo::DataConstant( | 398 *access_info = PropertyAccessInfo::DataConstant( |
| 365 receiver_type, handle(descriptors->GetValue(number), isolate()), | 399 receiver_type, handle(descriptors->GetValue(number), isolate()), |
| 366 holder); | 400 holder); |
| 367 return true; | 401 return true; |
| 368 } else if (details.type() == DATA) { | 402 } else if (details.type() == DATA) { |
| 369 // Don't bother optimizing stores to read-only properties. | |
| 370 if (access_mode == kStore && details.IsReadOnly()) { | |
| 371 break; | |
| 372 } | |
| 373 int index = descriptors->GetFieldIndex(number); | 403 int index = descriptors->GetFieldIndex(number); |
| 374 Representation field_representation = details.representation(); | 404 Representation field_representation = details.representation(); |
| 375 FieldIndex field_index = FieldIndex::ForPropertyIndex( | 405 FieldIndex field_index = FieldIndex::ForPropertyIndex( |
| 376 *map, index, field_representation.IsDouble()); | 406 *map, index, field_representation.IsDouble()); |
| 377 Type* field_type = Type::Tagged(); | 407 Type* field_type = Type::Tagged(); |
| 378 if (field_representation.IsSmi()) { | 408 if (field_representation.IsSmi()) { |
| 379 field_type = type_cache_.kSmi; | 409 field_type = type_cache_.kSmi; |
| 380 } else if (field_representation.IsDouble()) { | 410 } else if (field_representation.IsDouble()) { |
| 381 if (access_mode == kStore) { | 411 if (access_mode == kStore) { |
| 382 // TODO(bmeurer): Add support for storing to double fields. | 412 // TODO(bmeurer): Add support for storing to double fields. |
| 383 break; | 413 return false; |
| 384 } | 414 } |
| 385 field_type = type_cache_.kFloat64; | 415 field_type = type_cache_.kFloat64; |
| 386 } else if (field_representation.IsHeapObject()) { | 416 } else if (field_representation.IsHeapObject()) { |
| 387 // Extract the field type from the property details (make sure its | 417 // Extract the field type from the property details (make sure its |
| 388 // representation is TaggedPointer to reflect the heap object case). | 418 // representation is TaggedPointer to reflect the heap object case). |
| 389 field_type = Type::Intersect( | 419 field_type = Type::Intersect( |
| 390 Type::Convert<HeapType>( | 420 Type::Convert<HeapType>( |
| 391 handle(descriptors->GetFieldType(number), isolate()), | 421 handle(descriptors->GetFieldType(number), isolate()), |
| 392 graph()->zone()), | 422 graph()->zone()), |
| 393 Type::TaggedPointer(), graph()->zone()); | 423 Type::TaggedPointer(), graph()->zone()); |
| 394 if (field_type->Is(Type::None())) { | 424 if (field_type->Is(Type::None())) { |
| 395 if (access_mode == kStore) { | 425 // Store is not safe if the field type was cleared. |
| 396 // Store is not safe if the field type was cleared. | 426 if (access_mode == kStore) return false; |
| 397 break; | |
| 398 } | |
| 399 | 427 |
| 400 // The field type was cleared by the GC, so we don't know anything | 428 // The field type was cleared by the GC, so we don't know anything |
| 401 // about the contents now. | 429 // about the contents now. |
| 402 // TODO(bmeurer): It would be awesome to make this saner in the | 430 // TODO(bmeurer): It would be awesome to make this saner in the |
| 403 // runtime/GC interaction. | 431 // runtime/GC interaction. |
| 404 field_type = Type::TaggedPointer(); | 432 field_type = Type::TaggedPointer(); |
| 405 } else if (!Type::Any()->Is(field_type)) { | 433 } else if (!Type::Any()->Is(field_type)) { |
| 406 // Add proper code dependencies in case of stable field map(s). | 434 // Add proper code dependencies in case of stable field map(s). |
| 407 Handle<Map> field_owner_map(map->FindFieldOwner(number), isolate()); | 435 Handle<Map> field_owner_map(map->FindFieldOwner(number), isolate()); |
| 408 dependencies()->AssumeFieldType(field_owner_map); | 436 dependencies()->AssumeFieldType(field_owner_map); |
| 409 } | 437 } |
| 410 DCHECK(field_type->Is(Type::TaggedPointer())); | 438 DCHECK(field_type->Is(Type::TaggedPointer())); |
| 411 } | 439 } |
| 412 *access_info = PropertyAccessInfo::DataField(receiver_type, field_index, | 440 *access_info = PropertyAccessInfo::DataField(receiver_type, field_index, |
| 413 field_type, holder); | 441 field_type, holder); |
| 414 return true; | 442 return true; |
| 415 } else { | 443 } else { |
| 416 // TODO(bmeurer): Add support for accessors. | 444 // TODO(bmeurer): Add support for accessors. |
| 417 break; | 445 return false; |
| 418 } | 446 } |
| 419 } | 447 } |
| 420 | 448 |
| 421 // Don't search on the prototype chain for special indices in case of | 449 // Don't search on the prototype chain for special indices in case of |
| 422 // integer indexed exotic objects (see ES6 section 9.4.5). | 450 // integer indexed exotic objects (see ES6 section 9.4.5). |
| 423 if (map->IsJSTypedArrayMap() && name->IsString() && | 451 if (map->IsJSTypedArrayMap() && name->IsString() && |
| 424 IsSpecialIndex(isolate()->unicode_cache(), String::cast(*name))) { | 452 IsSpecialIndex(isolate()->unicode_cache(), String::cast(*name))) { |
| 425 break; | 453 return false; |
| 426 } | 454 } |
| 427 | 455 |
| 428 // Walk up the prototype chain. | 456 // Walk up the prototype chain. |
| 429 if (!map->prototype()->IsJSObject()) { | 457 if (!map->prototype()->IsJSObject()) { |
| 430 // Perform the implicit ToObject for primitives here. | 458 // Perform the implicit ToObject for primitives here. |
| 431 // Implemented according to ES6 section 7.3.2 GetV (V, P). | 459 // Implemented according to ES6 section 7.3.2 GetV (V, P). |
| 432 Handle<JSFunction> constructor; | 460 Handle<JSFunction> constructor; |
| 433 if (Map::GetConstructorFunction(map, native_context()) | 461 if (Map::GetConstructorFunction(map, native_context()) |
| 434 .ToHandle(&constructor)) { | 462 .ToHandle(&constructor)) { |
| 435 map = handle(constructor->initial_map(), isolate()); | 463 map = handle(constructor->initial_map(), isolate()); |
| 436 DCHECK(map->prototype()->IsJSObject()); | 464 DCHECK(map->prototype()->IsJSObject()); |
| 465 } else if (map->prototype()->IsNull()) { | |
| 466 // Store to property not found on the receiver or any prototype, we need | |
| 467 // to transition to a new data property. | |
| 468 // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) | |
| 469 if (access_mode == kStore) { | |
| 470 break; | |
| 471 } | |
| 472 // TODO(bmeurer): Handle the not found case if the prototype is null. | |
| 473 return false; | |
| 437 } else { | 474 } else { |
| 438 // TODO(bmeurer): Handle the not found case if the prototype is null. | 475 return false; |
| 439 break; | |
| 440 } | 476 } |
| 441 } | 477 } |
| 442 Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate()); | 478 Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate()); |
| 443 if (map_prototype->map()->is_deprecated()) { | 479 if (map_prototype->map()->is_deprecated()) { |
| 444 // Try to migrate the prototype object so we don't embed the deprecated | 480 // Try to migrate the prototype object so we don't embed the deprecated |
| 445 // map into the optimized code. | 481 // map into the optimized code. |
| 446 JSObject::TryMigrateInstance(map_prototype); | 482 JSObject::TryMigrateInstance(map_prototype); |
| 447 } | 483 } |
| 448 map = handle(map_prototype->map(), isolate()); | 484 map = handle(map_prototype->map(), isolate()); |
| 449 holder = map_prototype; | 485 holder = map_prototype; |
| 486 | |
| 487 // Check if it is safe to inline property access for the {map}. | |
| 488 if (!CanInlinePropertyAccess(map)) return false; | |
| 489 } | |
| 490 DCHECK_EQ(kStore, access_mode); | |
| 491 | |
| 492 // Check if the {receiver_map} has a data transition with the given {name}. | |
|
Jarin
2015/10/29 09:02:33
The structure of this code is strange. This whole
Benedikt Meurer
2015/10/29 09:03:47
Yes, but currently we have two "break" points. I w
| |
| 493 if (receiver_map->unused_property_fields() == 0) return false; | |
| 494 if (Map* transition = TransitionArray::SearchTransition(*receiver_map, kData, | |
| 495 *name, NONE)) { | |
| 496 Handle<Map> transition_map(transition, isolate()); | |
| 497 int const number = transition_map->LastAdded(); | |
| 498 PropertyDetails const details = | |
| 499 transition_map->instance_descriptors()->GetDetails(number); | |
| 500 // Don't bother optimizing stores to read-only properties. | |
| 501 if (details.IsReadOnly()) return false; | |
| 502 // TODO(bmeurer): Handle transition to data constant? | |
| 503 if (details.type() != DATA) return false; | |
| 504 int const index = details.field_index(); | |
| 505 Representation field_representation = details.representation(); | |
| 506 FieldIndex field_index = FieldIndex::ForPropertyIndex( | |
| 507 *transition_map, index, field_representation.IsDouble()); | |
| 508 Type* field_type = Type::Tagged(); | |
| 509 if (field_representation.IsSmi()) { | |
| 510 field_type = type_cache_.kSmi; | |
| 511 } else if (field_representation.IsDouble()) { | |
| 512 // TODO(bmeurer): Add support for storing to double fields. | |
| 513 return false; | |
| 514 } else if (field_representation.IsHeapObject()) { | |
| 515 // Extract the field type from the property details (make sure its | |
| 516 // representation is TaggedPointer to reflect the heap object case). | |
| 517 field_type = Type::Intersect( | |
| 518 Type::Convert<HeapType>( | |
| 519 handle( | |
| 520 transition_map->instance_descriptors()->GetFieldType(number), | |
| 521 isolate()), | |
| 522 graph()->zone()), | |
| 523 Type::TaggedPointer(), graph()->zone()); | |
| 524 if (field_type->Is(Type::None())) { | |
| 525 // Store is not safe if the field type was cleared. | |
| 526 return false; | |
| 527 } else if (!Type::Any()->Is(field_type)) { | |
| 528 // Add proper code dependencies in case of stable field map(s). | |
| 529 Handle<Map> field_owner_map(transition_map->FindFieldOwner(number), | |
| 530 isolate()); | |
| 531 dependencies()->AssumeFieldType(field_owner_map); | |
| 532 } | |
| 533 DCHECK(field_type->Is(Type::TaggedPointer())); | |
| 534 } | |
| 535 dependencies()->AssumeMapNotDeprecated(transition_map); | |
| 536 *access_info = PropertyAccessInfo::TransitionToField( | |
| 537 receiver_type, field_index, field_type, transition_map, holder); | |
| 538 return true; | |
| 450 } | 539 } |
| 451 return false; | 540 return false; |
| 452 } | 541 } |
| 453 | 542 |
| 454 | 543 |
| 455 bool JSNativeContextSpecialization::ComputePropertyAccessInfos( | 544 bool JSNativeContextSpecialization::ComputePropertyAccessInfos( |
| 456 MapHandleList const& maps, Handle<Name> name, | 545 MapHandleList const& maps, Handle<Name> name, |
| 457 PropertyAccessMode access_mode, | 546 PropertyAccessMode access_mode, |
| 458 ZoneVector<PropertyAccessInfo>* access_infos) { | 547 ZoneVector<PropertyAccessInfo>* access_infos) { |
| 459 for (Handle<Map> map : maps) { | 548 for (Handle<Map> map : maps) { |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 557 (this_control_count == 1) | 646 (this_control_count == 1) |
| 558 ? this_controls.front() | 647 ? this_controls.front() |
| 559 : graph()->NewNode(common()->Merge(this_control_count), | 648 : graph()->NewNode(common()->Merge(this_control_count), |
| 560 this_control_count, &this_controls.front()); | 649 this_control_count, &this_controls.front()); |
| 561 } | 650 } |
| 562 | 651 |
| 563 // Determine actual holder and perform prototype chain checks. | 652 // Determine actual holder and perform prototype chain checks. |
| 564 Handle<JSObject> holder; | 653 Handle<JSObject> holder; |
| 565 if (access_info.holder().ToHandle(&holder)) { | 654 if (access_info.holder().ToHandle(&holder)) { |
| 566 AssumePrototypesStable(receiver_type, holder); | 655 AssumePrototypesStable(receiver_type, holder); |
| 567 if (access_mode == kLoad) { | |
| 568 this_receiver = jsgraph()->Constant(holder); | |
| 569 } | |
| 570 } | 656 } |
| 571 | 657 |
| 572 // Generate the actual property access. | 658 // Generate the actual property access. |
| 573 if (access_info.IsDataConstant()) { | 659 if (access_info.IsDataConstant()) { |
| 574 this_value = jsgraph()->Constant(access_info.constant()); | 660 this_value = jsgraph()->Constant(access_info.constant()); |
| 575 if (access_mode == kStore) { | 661 if (access_mode == kStore) { |
| 576 Node* check = graph()->NewNode( | 662 Node* check = graph()->NewNode( |
| 577 simplified()->ReferenceEqual(Type::Tagged()), value, this_value); | 663 simplified()->ReferenceEqual(Type::Tagged()), value, this_value); |
| 578 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 664 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
| 579 check, this_control); | 665 check, this_control); |
| 580 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | 666 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); |
| 581 this_control = graph()->NewNode(common()->IfTrue(), branch); | 667 this_control = graph()->NewNode(common()->IfTrue(), branch); |
| 582 } | 668 } |
| 583 } else { | 669 } else { |
| 584 DCHECK(access_info.IsDataField()); | 670 DCHECK(access_info.IsDataField() || access_info.IsTransitionToField()); |
| 585 FieldIndex const field_index = access_info.field_index(); | 671 FieldIndex const field_index = access_info.field_index(); |
| 586 Type* const field_type = access_info.field_type(); | 672 Type* const field_type = access_info.field_type(); |
| 673 if (access_mode == kLoad && access_info.holder().ToHandle(&holder)) { | |
| 674 this_receiver = jsgraph()->Constant(holder); | |
| 675 } | |
| 676 Node* this_storage = this_receiver; | |
| 587 if (!field_index.is_inobject()) { | 677 if (!field_index.is_inobject()) { |
| 588 this_receiver = this_effect = graph()->NewNode( | 678 this_storage = this_effect = graph()->NewNode( |
| 589 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), | 679 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), |
| 590 this_receiver, this_effect, this_control); | 680 this_storage, this_effect, this_control); |
| 591 } | 681 } |
| 592 FieldAccess field_access = {kTaggedBase, field_index.offset(), name, | 682 FieldAccess field_access = {kTaggedBase, field_index.offset(), name, |
| 593 field_type, kMachAnyTagged}; | 683 field_type, kMachAnyTagged}; |
| 594 if (access_mode == kLoad) { | 684 if (access_mode == kLoad) { |
| 595 if (field_type->Is(Type::UntaggedFloat64())) { | 685 if (field_type->Is(Type::UntaggedFloat64())) { |
| 596 if (!field_index.is_inobject() || field_index.is_hidden_field() || | 686 if (!field_index.is_inobject() || field_index.is_hidden_field() || |
| 597 !FLAG_unbox_double_fields) { | 687 !FLAG_unbox_double_fields) { |
| 598 this_receiver = this_effect = | 688 this_storage = this_effect = |
| 599 graph()->NewNode(simplified()->LoadField(field_access), | 689 graph()->NewNode(simplified()->LoadField(field_access), |
| 600 this_receiver, this_effect, this_control); | 690 this_storage, this_effect, this_control); |
| 601 field_access.offset = HeapNumber::kValueOffset; | 691 field_access.offset = HeapNumber::kValueOffset; |
| 602 field_access.name = MaybeHandle<Name>(); | 692 field_access.name = MaybeHandle<Name>(); |
| 603 } | 693 } |
| 604 field_access.machine_type = kMachFloat64; | 694 field_access.machine_type = kMachFloat64; |
| 605 } | 695 } |
| 606 this_value = this_effect = | 696 this_value = this_effect = |
| 607 graph()->NewNode(simplified()->LoadField(field_access), | 697 graph()->NewNode(simplified()->LoadField(field_access), |
| 608 this_receiver, this_effect, this_control); | 698 this_storage, this_effect, this_control); |
| 609 } else { | 699 } else { |
| 610 DCHECK_EQ(kStore, access_mode); | 700 DCHECK_EQ(kStore, access_mode); |
| 611 if (field_type->Is(Type::TaggedSigned())) { | 701 if (field_type->Is(Type::TaggedSigned())) { |
| 612 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); | 702 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); |
| 613 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 703 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
| 614 check, this_control); | 704 check, this_control); |
| 615 exit_controls.push_back( | 705 exit_controls.push_back( |
| 616 graph()->NewNode(common()->IfFalse(), branch)); | 706 graph()->NewNode(common()->IfFalse(), branch)); |
| 617 this_control = graph()->NewNode(common()->IfTrue(), branch); | 707 this_control = graph()->NewNode(common()->IfTrue(), branch); |
| 618 } else if (field_type->Is(Type::TaggedPointer())) { | 708 } else if (field_type->Is(Type::TaggedPointer())) { |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 644 this_control = | 734 this_control = |
| 645 (this_control_count == 1) | 735 (this_control_count == 1) |
| 646 ? this_controls.front() | 736 ? this_controls.front() |
| 647 : graph()->NewNode(common()->Merge(this_control_count), | 737 : graph()->NewNode(common()->Merge(this_control_count), |
| 648 this_control_count, | 738 this_control_count, |
| 649 &this_controls.front()); | 739 &this_controls.front()); |
| 650 } | 740 } |
| 651 } else { | 741 } else { |
| 652 DCHECK(field_type->Is(Type::Tagged())); | 742 DCHECK(field_type->Is(Type::Tagged())); |
| 653 } | 743 } |
| 744 if (access_info.IsTransitionToField()) { | |
| 745 this_effect = graph()->NewNode(common()->BeginRegion(), this_effect); | |
| 746 this_effect = graph()->NewNode( | |
| 747 simplified()->StoreField(AccessBuilder::ForMap()), this_receiver, | |
| 748 jsgraph()->Constant(access_info.transition_map()), this_effect, | |
| 749 this_control); | |
| 750 } | |
| 654 this_effect = graph()->NewNode(simplified()->StoreField(field_access), | 751 this_effect = graph()->NewNode(simplified()->StoreField(field_access), |
| 655 this_receiver, this_value, this_effect, | 752 this_storage, this_value, this_effect, |
| 656 this_control); | 753 this_control); |
| 754 if (access_info.IsTransitionToField()) { | |
| 755 this_effect = | |
| 756 graph()->NewNode(common()->FinishRegion(), | |
| 757 jsgraph()->UndefinedConstant(), this_effect); | |
| 758 } | |
| 657 } | 759 } |
| 658 } | 760 } |
| 659 | 761 |
| 660 // Remember the final state for this property access. | 762 // Remember the final state for this property access. |
| 661 values.push_back(this_value); | 763 values.push_back(this_value); |
| 662 effects.push_back(this_effect); | 764 effects.push_back(this_effect); |
| 663 controls.push_back(this_control); | 765 controls.push_back(this_control); |
| 664 } | 766 } |
| 665 | 767 |
| 666 // Collect the fallthru control as final "exit" control. | 768 // Collect the fallthru control as final "exit" control. |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 828 } | 930 } |
| 829 | 931 |
| 830 | 932 |
| 831 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { | 933 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { |
| 832 return jsgraph()->simplified(); | 934 return jsgraph()->simplified(); |
| 833 } | 935 } |
| 834 | 936 |
| 835 } // namespace compiler | 937 } // namespace compiler |
| 836 } // namespace internal | 938 } // namespace internal |
| 837 } // namespace v8 | 939 } // namespace v8 |
| OLD | NEW |