| 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 21 matching lines...) Expand all Loading... |
| 32 Editor* editor, JSGraph* jsgraph, Flags flags, | 32 Editor* editor, JSGraph* jsgraph, Flags flags, |
| 33 Handle<GlobalObject> global_object, CompilationDependencies* dependencies, | 33 Handle<GlobalObject> global_object, CompilationDependencies* dependencies, |
| 34 Zone* zone) | 34 Zone* zone) |
| 35 : AdvancedReducer(editor), | 35 : AdvancedReducer(editor), |
| 36 jsgraph_(jsgraph), | 36 jsgraph_(jsgraph), |
| 37 flags_(flags), | 37 flags_(flags), |
| 38 global_object_(global_object), | 38 global_object_(global_object), |
| 39 native_context_(global_object->native_context(), isolate()), | 39 native_context_(global_object->native_context(), isolate()), |
| 40 dependencies_(dependencies), | 40 dependencies_(dependencies), |
| 41 zone_(zone), | 41 zone_(zone), |
| 42 type_cache_(TypeCache::Get()) {} | 42 type_cache_(TypeCache::Get()), |
| 43 access_info_factory_(dependencies, native_context(), graph()->zone()) {} |
| 43 | 44 |
| 44 | 45 |
| 45 Reduction JSNativeContextSpecialization::Reduce(Node* node) { | 46 Reduction JSNativeContextSpecialization::Reduce(Node* node) { |
| 46 switch (node->opcode()) { | 47 switch (node->opcode()) { |
| 47 case IrOpcode::kJSCallFunction: | 48 case IrOpcode::kJSCallFunction: |
| 48 return ReduceJSCallFunction(node); | 49 return ReduceJSCallFunction(node); |
| 49 case IrOpcode::kJSLoadGlobal: | 50 case IrOpcode::kJSLoadGlobal: |
| 50 return ReduceJSLoadGlobal(node); | 51 return ReduceJSLoadGlobal(node); |
| 51 case IrOpcode::kJSStoreGlobal: | 52 case IrOpcode::kJSStoreGlobal: |
| 52 return ReduceJSStoreGlobal(node); | 53 return ReduceJSStoreGlobal(node); |
| (...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 291 break; | 292 break; |
| 292 } | 293 } |
| 293 } | 294 } |
| 294 effect = graph()->NewNode( | 295 effect = graph()->NewNode( |
| 295 simplified()->StoreField(AccessBuilder::ForPropertyCellValue()), | 296 simplified()->StoreField(AccessBuilder::ForPropertyCellValue()), |
| 296 jsgraph()->Constant(property_cell), value, effect, control); | 297 jsgraph()->Constant(property_cell), value, effect, control); |
| 297 return Replace(node, value, effect, control); | 298 return Replace(node, value, effect, control); |
| 298 } | 299 } |
| 299 | 300 |
| 300 | 301 |
| 301 // This class encapsulates all information required to access a certain | |
| 302 // object property, either on the object itself or on the prototype chain. | |
| 303 class JSNativeContextSpecialization::PropertyAccessInfo final { | |
| 304 public: | |
| 305 enum Kind { kInvalid, kDataConstant, kDataField, kTransitionToField }; | |
| 306 | |
| 307 static PropertyAccessInfo DataConstant(Type* receiver_type, | |
| 308 Handle<Object> constant, | |
| 309 MaybeHandle<JSObject> holder) { | |
| 310 return PropertyAccessInfo(holder, constant, receiver_type); | |
| 311 } | |
| 312 static PropertyAccessInfo DataField( | |
| 313 Type* receiver_type, FieldIndex field_index, Type* field_type, | |
| 314 MaybeHandle<JSObject> holder = MaybeHandle<JSObject>()) { | |
| 315 return PropertyAccessInfo(holder, field_index, field_type, receiver_type); | |
| 316 } | |
| 317 static PropertyAccessInfo TransitionToField(Type* receiver_type, | |
| 318 FieldIndex field_index, | |
| 319 Type* field_type, | |
| 320 Handle<Map> transition_map, | |
| 321 MaybeHandle<JSObject> holder) { | |
| 322 return PropertyAccessInfo(holder, transition_map, field_index, field_type, | |
| 323 receiver_type); | |
| 324 } | |
| 325 | |
| 326 PropertyAccessInfo() : kind_(kInvalid) {} | |
| 327 PropertyAccessInfo(MaybeHandle<JSObject> holder, Handle<Object> constant, | |
| 328 Type* receiver_type) | |
| 329 : kind_(kDataConstant), | |
| 330 receiver_type_(receiver_type), | |
| 331 constant_(constant), | |
| 332 holder_(holder) {} | |
| 333 PropertyAccessInfo(MaybeHandle<JSObject> holder, FieldIndex field_index, | |
| 334 Type* field_type, Type* receiver_type) | |
| 335 : kind_(kDataField), | |
| 336 receiver_type_(receiver_type), | |
| 337 holder_(holder), | |
| 338 field_index_(field_index), | |
| 339 field_type_(field_type) {} | |
| 340 PropertyAccessInfo(MaybeHandle<JSObject> holder, Handle<Map> transition_map, | |
| 341 FieldIndex field_index, Type* field_type, | |
| 342 Type* receiver_type) | |
| 343 : kind_(kTransitionToField), | |
| 344 receiver_type_(receiver_type), | |
| 345 transition_map_(transition_map), | |
| 346 holder_(holder), | |
| 347 field_index_(field_index), | |
| 348 field_type_(field_type) {} | |
| 349 | |
| 350 bool IsDataConstant() const { return kind() == kDataConstant; } | |
| 351 bool IsDataField() const { return kind() == kDataField; } | |
| 352 bool IsTransitionToField() const { return kind() == kTransitionToField; } | |
| 353 | |
| 354 Kind kind() const { return kind_; } | |
| 355 MaybeHandle<JSObject> holder() const { return holder_; } | |
| 356 Handle<Object> constant() const { return constant_; } | |
| 357 Handle<Object> transition_map() const { return transition_map_; } | |
| 358 FieldIndex field_index() const { return field_index_; } | |
| 359 Type* field_type() const { return field_type_; } | |
| 360 Type* receiver_type() const { return receiver_type_; } | |
| 361 | |
| 362 private: | |
| 363 Kind kind_; | |
| 364 Type* receiver_type_; | |
| 365 Handle<Object> constant_; | |
| 366 Handle<Map> transition_map_; | |
| 367 MaybeHandle<JSObject> holder_; | |
| 368 FieldIndex field_index_; | |
| 369 Type* field_type_ = Type::Any(); | |
| 370 }; | |
| 371 | |
| 372 | |
| 373 namespace { | |
| 374 | |
| 375 bool CanInlinePropertyAccess(Handle<Map> map) { | |
| 376 // TODO(bmeurer): Do something about the number stuff. | |
| 377 if (map->instance_type() == HEAP_NUMBER_TYPE) return false; | |
| 378 if (map->instance_type() < FIRST_NONSTRING_TYPE) return true; | |
| 379 return map->IsJSObjectMap() && !map->is_dictionary_map() && | |
| 380 !map->has_named_interceptor() && | |
| 381 // TODO(verwaest): Whitelist contexts to which we have access. | |
| 382 !map->is_access_check_needed(); | |
| 383 } | |
| 384 | |
| 385 } // namespace | |
| 386 | |
| 387 | |
| 388 bool JSNativeContextSpecialization::ComputePropertyAccessInfo( | |
| 389 Handle<Map> map, Handle<Name> name, PropertyAccessMode access_mode, | |
| 390 PropertyAccessInfo* access_info) { | |
| 391 // Check if it is safe to inline property access for the {map}. | |
| 392 if (!CanInlinePropertyAccess(map)) return false; | |
| 393 | |
| 394 // Compute the receiver type. | |
| 395 Handle<Map> receiver_map = map; | |
| 396 Type* receiver_type = Type::Class(receiver_map, graph()->zone()); | |
| 397 | |
| 398 // We support fast inline cases for certain JSObject getters. | |
| 399 if (access_mode == kLoad) { | |
| 400 // Check for special JSObject field accessors. | |
| 401 int offset; | |
| 402 if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) { | |
| 403 FieldIndex field_index = FieldIndex::ForInObjectOffset(offset); | |
| 404 Type* field_type = Type::Tagged(); | |
| 405 if (map->IsStringMap()) { | |
| 406 DCHECK(Name::Equals(factory()->length_string(), name)); | |
| 407 // The String::length property is always a smi in the range | |
| 408 // [0, String::kMaxLength]. | |
| 409 field_type = type_cache_.kStringLengthType; | |
| 410 } else if (map->IsJSArrayMap()) { | |
| 411 DCHECK(Name::Equals(factory()->length_string(), name)); | |
| 412 // The JSArray::length property is a smi in the range | |
| 413 // [0, FixedDoubleArray::kMaxLength] in case of fast double | |
| 414 // elements, a smi in the range [0, FixedArray::kMaxLength] | |
| 415 // in case of other fast elements, and [0, kMaxUInt32] in | |
| 416 // case of other arrays. | |
| 417 if (IsFastDoubleElementsKind(map->elements_kind())) { | |
| 418 field_type = type_cache_.kFixedDoubleArrayLengthType; | |
| 419 } else if (IsFastElementsKind(map->elements_kind())) { | |
| 420 field_type = type_cache_.kFixedArrayLengthType; | |
| 421 } else { | |
| 422 field_type = type_cache_.kJSArrayLengthType; | |
| 423 } | |
| 424 } | |
| 425 *access_info = | |
| 426 PropertyAccessInfo::DataField(receiver_type, field_index, field_type); | |
| 427 return true; | |
| 428 } | |
| 429 } | |
| 430 | |
| 431 MaybeHandle<JSObject> holder; | |
| 432 while (true) { | |
| 433 // Lookup the named property on the {map}. | |
| 434 Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate()); | |
| 435 int const number = descriptors->SearchWithCache(*name, *map); | |
| 436 if (number != DescriptorArray::kNotFound) { | |
| 437 PropertyDetails const details = descriptors->GetDetails(number); | |
| 438 if (access_mode == kStore) { | |
| 439 // Don't bother optimizing stores to read-only properties. | |
| 440 if (details.IsReadOnly()) { | |
| 441 return false; | |
| 442 } | |
| 443 // Check for store to data property on a prototype. | |
| 444 if (details.kind() == kData && !holder.is_null()) { | |
| 445 // We need to add the data field to the receiver. Leave the loop | |
| 446 // and check whether we already have a transition for this field. | |
| 447 // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) | |
| 448 break; | |
| 449 } | |
| 450 } | |
| 451 if (details.type() == DATA_CONSTANT) { | |
| 452 *access_info = PropertyAccessInfo::DataConstant( | |
| 453 receiver_type, handle(descriptors->GetValue(number), isolate()), | |
| 454 holder); | |
| 455 return true; | |
| 456 } else if (details.type() == DATA) { | |
| 457 int index = descriptors->GetFieldIndex(number); | |
| 458 Representation field_representation = details.representation(); | |
| 459 FieldIndex field_index = FieldIndex::ForPropertyIndex( | |
| 460 *map, index, field_representation.IsDouble()); | |
| 461 Type* field_type = Type::Tagged(); | |
| 462 if (field_representation.IsSmi()) { | |
| 463 field_type = type_cache_.kSmi; | |
| 464 } else if (field_representation.IsDouble()) { | |
| 465 field_type = type_cache_.kFloat64; | |
| 466 } else if (field_representation.IsHeapObject()) { | |
| 467 // Extract the field type from the property details (make sure its | |
| 468 // representation is TaggedPointer to reflect the heap object case). | |
| 469 field_type = Type::Intersect( | |
| 470 Type::Convert<HeapType>( | |
| 471 handle(descriptors->GetFieldType(number), isolate()), | |
| 472 graph()->zone()), | |
| 473 Type::TaggedPointer(), graph()->zone()); | |
| 474 if (field_type->Is(Type::None())) { | |
| 475 // Store is not safe if the field type was cleared. | |
| 476 if (access_mode == kStore) return false; | |
| 477 | |
| 478 // The field type was cleared by the GC, so we don't know anything | |
| 479 // about the contents now. | |
| 480 // TODO(bmeurer): It would be awesome to make this saner in the | |
| 481 // runtime/GC interaction. | |
| 482 field_type = Type::TaggedPointer(); | |
| 483 } else if (!Type::Any()->Is(field_type)) { | |
| 484 // Add proper code dependencies in case of stable field map(s). | |
| 485 Handle<Map> field_owner_map(map->FindFieldOwner(number), isolate()); | |
| 486 dependencies()->AssumeFieldType(field_owner_map); | |
| 487 } | |
| 488 DCHECK(field_type->Is(Type::TaggedPointer())); | |
| 489 } | |
| 490 *access_info = PropertyAccessInfo::DataField(receiver_type, field_index, | |
| 491 field_type, holder); | |
| 492 return true; | |
| 493 } else { | |
| 494 // TODO(bmeurer): Add support for accessors. | |
| 495 return false; | |
| 496 } | |
| 497 } | |
| 498 | |
| 499 // Don't search on the prototype chain for special indices in case of | |
| 500 // integer indexed exotic objects (see ES6 section 9.4.5). | |
| 501 if (map->IsJSTypedArrayMap() && name->IsString() && | |
| 502 IsSpecialIndex(isolate()->unicode_cache(), String::cast(*name))) { | |
| 503 return false; | |
| 504 } | |
| 505 | |
| 506 // Don't lookup private symbols on the prototype chain. | |
| 507 if (name->IsPrivate()) return false; | |
| 508 | |
| 509 // Walk up the prototype chain. | |
| 510 if (!map->prototype()->IsJSObject()) { | |
| 511 // Perform the implicit ToObject for primitives here. | |
| 512 // Implemented according to ES6 section 7.3.2 GetV (V, P). | |
| 513 Handle<JSFunction> constructor; | |
| 514 if (Map::GetConstructorFunction(map, native_context()) | |
| 515 .ToHandle(&constructor)) { | |
| 516 map = handle(constructor->initial_map(), isolate()); | |
| 517 DCHECK(map->prototype()->IsJSObject()); | |
| 518 } else if (map->prototype()->IsNull()) { | |
| 519 // Store to property not found on the receiver or any prototype, we need | |
| 520 // to transition to a new data property. | |
| 521 // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) | |
| 522 if (access_mode == kStore) { | |
| 523 break; | |
| 524 } | |
| 525 // TODO(bmeurer): Handle the not found case if the prototype is null. | |
| 526 return false; | |
| 527 } else { | |
| 528 return false; | |
| 529 } | |
| 530 } | |
| 531 Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate()); | |
| 532 if (map_prototype->map()->is_deprecated()) { | |
| 533 // Try to migrate the prototype object so we don't embed the deprecated | |
| 534 // map into the optimized code. | |
| 535 JSObject::TryMigrateInstance(map_prototype); | |
| 536 } | |
| 537 map = handle(map_prototype->map(), isolate()); | |
| 538 holder = map_prototype; | |
| 539 | |
| 540 // Check if it is safe to inline property access for the {map}. | |
| 541 if (!CanInlinePropertyAccess(map)) return false; | |
| 542 } | |
| 543 DCHECK_EQ(kStore, access_mode); | |
| 544 | |
| 545 // Check if the {receiver_map} has a data transition with the given {name}. | |
| 546 if (receiver_map->unused_property_fields() == 0) return false; | |
| 547 if (Map* transition = TransitionArray::SearchTransition(*receiver_map, kData, | |
| 548 *name, NONE)) { | |
| 549 Handle<Map> transition_map(transition, isolate()); | |
| 550 int const number = transition_map->LastAdded(); | |
| 551 PropertyDetails const details = | |
| 552 transition_map->instance_descriptors()->GetDetails(number); | |
| 553 // Don't bother optimizing stores to read-only properties. | |
| 554 if (details.IsReadOnly()) return false; | |
| 555 // TODO(bmeurer): Handle transition to data constant? | |
| 556 if (details.type() != DATA) return false; | |
| 557 int const index = details.field_index(); | |
| 558 Representation field_representation = details.representation(); | |
| 559 FieldIndex field_index = FieldIndex::ForPropertyIndex( | |
| 560 *transition_map, index, field_representation.IsDouble()); | |
| 561 Type* field_type = Type::Tagged(); | |
| 562 if (field_representation.IsSmi()) { | |
| 563 field_type = type_cache_.kSmi; | |
| 564 } else if (field_representation.IsDouble()) { | |
| 565 // TODO(bmeurer): Add support for storing to double fields. | |
| 566 return false; | |
| 567 } else if (field_representation.IsHeapObject()) { | |
| 568 // Extract the field type from the property details (make sure its | |
| 569 // representation is TaggedPointer to reflect the heap object case). | |
| 570 field_type = Type::Intersect( | |
| 571 Type::Convert<HeapType>( | |
| 572 handle( | |
| 573 transition_map->instance_descriptors()->GetFieldType(number), | |
| 574 isolate()), | |
| 575 graph()->zone()), | |
| 576 Type::TaggedPointer(), graph()->zone()); | |
| 577 if (field_type->Is(Type::None())) { | |
| 578 // Store is not safe if the field type was cleared. | |
| 579 return false; | |
| 580 } else if (!Type::Any()->Is(field_type)) { | |
| 581 // Add proper code dependencies in case of stable field map(s). | |
| 582 Handle<Map> field_owner_map(transition_map->FindFieldOwner(number), | |
| 583 isolate()); | |
| 584 dependencies()->AssumeFieldType(field_owner_map); | |
| 585 } | |
| 586 DCHECK(field_type->Is(Type::TaggedPointer())); | |
| 587 } | |
| 588 dependencies()->AssumeMapNotDeprecated(transition_map); | |
| 589 *access_info = PropertyAccessInfo::TransitionToField( | |
| 590 receiver_type, field_index, field_type, transition_map, holder); | |
| 591 return true; | |
| 592 } | |
| 593 return false; | |
| 594 } | |
| 595 | |
| 596 | |
| 597 bool JSNativeContextSpecialization::ComputePropertyAccessInfos( | |
| 598 MapHandleList const& maps, Handle<Name> name, | |
| 599 PropertyAccessMode access_mode, | |
| 600 ZoneVector<PropertyAccessInfo>* access_infos) { | |
| 601 for (Handle<Map> map : maps) { | |
| 602 if (Map::TryUpdate(map).ToHandle(&map)) { | |
| 603 PropertyAccessInfo access_info; | |
| 604 if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) { | |
| 605 return false; | |
| 606 } | |
| 607 access_infos->push_back(access_info); | |
| 608 } | |
| 609 } | |
| 610 return true; | |
| 611 } | |
| 612 | |
| 613 | |
| 614 Reduction JSNativeContextSpecialization::ReduceNamedAccess( | 302 Reduction JSNativeContextSpecialization::ReduceNamedAccess( |
| 615 Node* node, Node* value, MapHandleList const& receiver_maps, | 303 Node* node, Node* value, MapHandleList const& receiver_maps, |
| 616 Handle<Name> name, PropertyAccessMode access_mode) { | 304 Handle<Name> name, PropertyAccessMode access_mode) { |
| 617 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || | 305 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || |
| 618 node->opcode() == IrOpcode::kJSStoreNamed); | 306 node->opcode() == IrOpcode::kJSStoreNamed); |
| 619 Node* receiver = NodeProperties::GetValueInput(node, 0); | 307 Node* receiver = NodeProperties::GetValueInput(node, 0); |
| 620 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); | 308 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); |
| 621 Node* effect = NodeProperties::GetEffectInput(node); | 309 Node* effect = NodeProperties::GetEffectInput(node); |
| 622 Node* control = NodeProperties::GetControlInput(node); | 310 Node* control = NodeProperties::GetControlInput(node); |
| 623 | 311 |
| 624 // Not much we can do if deoptimization support is disabled. | 312 // Not much we can do if deoptimization support is disabled. |
| 625 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); | 313 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); |
| 626 | 314 |
| 627 // Compute property access infos for the receiver maps. | 315 // Compute property access infos for the receiver maps. |
| 628 ZoneVector<PropertyAccessInfo> access_infos(zone()); | 316 ZoneVector<PropertyAccessInfo> access_infos(zone()); |
| 629 if (!ComputePropertyAccessInfos(receiver_maps, name, access_mode, | 317 if (!access_info_factory().ComputePropertyAccessInfos( |
| 630 &access_infos)) { | 318 receiver_maps, name, access_mode, &access_infos)) { |
| 631 return NoChange(); | 319 return NoChange(); |
| 632 } | 320 } |
| 633 | 321 |
| 634 // Nothing to do if we have no non-deprecated maps. | 322 // Nothing to do if we have no non-deprecated maps. |
| 635 if (access_infos.empty()) return NoChange(); | 323 if (access_infos.empty()) return NoChange(); |
| 636 | 324 |
| 637 // The final states for every polymorphic branch. We join them with | 325 // The final states for every polymorphic branch. We join them with |
| 638 // Merge++Phi+EffectPhi at the bottom. | 326 // Merge++Phi+EffectPhi at the bottom. |
| 639 ZoneVector<Node*> values(zone()); | 327 ZoneVector<Node*> values(zone()); |
| 640 ZoneVector<Node*> effects(zone()); | 328 ZoneVector<Node*> effects(zone()); |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 704 | 392 |
| 705 // Determine actual holder and perform prototype chain checks. | 393 // Determine actual holder and perform prototype chain checks. |
| 706 Handle<JSObject> holder; | 394 Handle<JSObject> holder; |
| 707 if (access_info.holder().ToHandle(&holder)) { | 395 if (access_info.holder().ToHandle(&holder)) { |
| 708 AssumePrototypesStable(receiver_type, holder); | 396 AssumePrototypesStable(receiver_type, holder); |
| 709 } | 397 } |
| 710 | 398 |
| 711 // Generate the actual property access. | 399 // Generate the actual property access. |
| 712 if (access_info.IsDataConstant()) { | 400 if (access_info.IsDataConstant()) { |
| 713 this_value = jsgraph()->Constant(access_info.constant()); | 401 this_value = jsgraph()->Constant(access_info.constant()); |
| 714 if (access_mode == kStore) { | 402 if (access_mode == PropertyAccessMode::kStore) { |
| 715 Node* check = graph()->NewNode( | 403 Node* check = graph()->NewNode( |
| 716 simplified()->ReferenceEqual(Type::Tagged()), value, this_value); | 404 simplified()->ReferenceEqual(Type::Tagged()), value, this_value); |
| 717 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 405 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
| 718 check, this_control); | 406 check, this_control); |
| 719 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | 407 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); |
| 720 this_control = graph()->NewNode(common()->IfTrue(), branch); | 408 this_control = graph()->NewNode(common()->IfTrue(), branch); |
| 721 } | 409 } |
| 722 } else { | 410 } else { |
| 723 DCHECK(access_info.IsDataField() || access_info.IsTransitionToField()); | 411 DCHECK(access_info.IsDataField()); |
| 724 FieldIndex const field_index = access_info.field_index(); | 412 FieldIndex const field_index = access_info.field_index(); |
| 725 Type* const field_type = access_info.field_type(); | 413 Type* const field_type = access_info.field_type(); |
| 726 if (access_mode == kLoad && access_info.holder().ToHandle(&holder)) { | 414 if (access_mode == PropertyAccessMode::kLoad && |
| 415 access_info.holder().ToHandle(&holder)) { |
| 727 this_receiver = jsgraph()->Constant(holder); | 416 this_receiver = jsgraph()->Constant(holder); |
| 728 } | 417 } |
| 729 Node* this_storage = this_receiver; | 418 Node* this_storage = this_receiver; |
| 730 if (!field_index.is_inobject()) { | 419 if (!field_index.is_inobject()) { |
| 731 this_storage = this_effect = graph()->NewNode( | 420 this_storage = this_effect = graph()->NewNode( |
| 732 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), | 421 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), |
| 733 this_storage, this_effect, this_control); | 422 this_storage, this_effect, this_control); |
| 734 } | 423 } |
| 735 FieldAccess field_access = {kTaggedBase, field_index.offset(), name, | 424 FieldAccess field_access = {kTaggedBase, field_index.offset(), name, |
| 736 field_type, kMachAnyTagged}; | 425 field_type, kMachAnyTagged}; |
| 737 if (field_type->Is(Type::UntaggedFloat64())) { | 426 if (field_type->Is(Type::UntaggedFloat64())) { |
| 738 if (!field_index.is_inobject() || field_index.is_hidden_field() || | 427 if (!field_index.is_inobject() || field_index.is_hidden_field() || |
| 739 !FLAG_unbox_double_fields) { | 428 !FLAG_unbox_double_fields) { |
| 740 this_storage = this_effect = | 429 this_storage = this_effect = |
| 741 graph()->NewNode(simplified()->LoadField(field_access), | 430 graph()->NewNode(simplified()->LoadField(field_access), |
| 742 this_storage, this_effect, this_control); | 431 this_storage, this_effect, this_control); |
| 743 field_access.offset = HeapNumber::kValueOffset; | 432 field_access.offset = HeapNumber::kValueOffset; |
| 744 field_access.name = MaybeHandle<Name>(); | 433 field_access.name = MaybeHandle<Name>(); |
| 745 } | 434 } |
| 746 field_access.machine_type = kMachFloat64; | 435 field_access.machine_type = kMachFloat64; |
| 747 } | 436 } |
| 748 if (access_mode == kLoad) { | 437 if (access_mode == PropertyAccessMode::kLoad) { |
| 749 this_value = this_effect = | 438 this_value = this_effect = |
| 750 graph()->NewNode(simplified()->LoadField(field_access), | 439 graph()->NewNode(simplified()->LoadField(field_access), |
| 751 this_storage, this_effect, this_control); | 440 this_storage, this_effect, this_control); |
| 752 } else { | 441 } else { |
| 753 DCHECK_EQ(kStore, access_mode); | 442 DCHECK_EQ(PropertyAccessMode::kStore, access_mode); |
| 754 if (field_type->Is(Type::UntaggedFloat64())) { | 443 if (field_type->Is(Type::UntaggedFloat64())) { |
| 755 Node* check = | 444 Node* check = |
| 756 graph()->NewNode(simplified()->ObjectIsNumber(), this_value); | 445 graph()->NewNode(simplified()->ObjectIsNumber(), this_value); |
| 757 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 446 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
| 758 check, this_control); | 447 check, this_control); |
| 759 exit_controls.push_back( | 448 exit_controls.push_back( |
| 760 graph()->NewNode(common()->IfFalse(), branch)); | 449 graph()->NewNode(common()->IfFalse(), branch)); |
| 761 this_control = graph()->NewNode(common()->IfTrue(), branch); | 450 this_control = graph()->NewNode(common()->IfTrue(), branch); |
| 762 this_value = graph()->NewNode(common()->Guard(Type::Number()), | 451 this_value = graph()->NewNode(common()->Guard(Type::Number()), |
| 763 this_value, this_control); | 452 this_value, this_control); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 799 this_control = | 488 this_control = |
| 800 (this_control_count == 1) | 489 (this_control_count == 1) |
| 801 ? this_controls.front() | 490 ? this_controls.front() |
| 802 : graph()->NewNode(common()->Merge(this_control_count), | 491 : graph()->NewNode(common()->Merge(this_control_count), |
| 803 this_control_count, | 492 this_control_count, |
| 804 &this_controls.front()); | 493 &this_controls.front()); |
| 805 } | 494 } |
| 806 } else { | 495 } else { |
| 807 DCHECK(field_type->Is(Type::Tagged())); | 496 DCHECK(field_type->Is(Type::Tagged())); |
| 808 } | 497 } |
| 809 if (access_info.IsTransitionToField()) { | 498 Handle<Map> transition_map; |
| 499 if (access_info.transition_map().ToHandle(&transition_map)) { |
| 810 this_effect = graph()->NewNode(common()->BeginRegion(), this_effect); | 500 this_effect = graph()->NewNode(common()->BeginRegion(), this_effect); |
| 811 this_effect = graph()->NewNode( | 501 this_effect = graph()->NewNode( |
| 812 simplified()->StoreField(AccessBuilder::ForMap()), this_receiver, | 502 simplified()->StoreField(AccessBuilder::ForMap()), this_receiver, |
| 813 jsgraph()->Constant(access_info.transition_map()), this_effect, | 503 jsgraph()->Constant(transition_map), this_effect, this_control); |
| 814 this_control); | |
| 815 } | 504 } |
| 816 this_effect = graph()->NewNode(simplified()->StoreField(field_access), | 505 this_effect = graph()->NewNode(simplified()->StoreField(field_access), |
| 817 this_storage, this_value, this_effect, | 506 this_storage, this_value, this_effect, |
| 818 this_control); | 507 this_control); |
| 819 if (access_info.IsTransitionToField()) { | 508 if (!access_info.transition_map().is_null()) { |
| 820 this_effect = | 509 this_effect = |
| 821 graph()->NewNode(common()->FinishRegion(), | 510 graph()->NewNode(common()->FinishRegion(), |
| 822 jsgraph()->UndefinedConstant(), this_effect); | 511 jsgraph()->UndefinedConstant(), this_effect); |
| 823 } | 512 } |
| 824 } | 513 } |
| 825 } | 514 } |
| 826 | 515 |
| 827 // Remember the final state for this property access. | 516 // Remember the final state for this property access. |
| 828 values.push_back(this_value); | 517 values.push_back(this_value); |
| 829 effects.push_back(this_effect); | 518 effects.push_back(this_effect); |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 886 Node* const value = jsgraph()->Dead(); | 575 Node* const value = jsgraph()->Dead(); |
| 887 | 576 |
| 888 // Extract receiver maps from the LOAD_IC using the LoadICNexus. | 577 // Extract receiver maps from the LOAD_IC using the LoadICNexus. |
| 889 MapHandleList receiver_maps; | 578 MapHandleList receiver_maps; |
| 890 if (!p.feedback().IsValid()) return NoChange(); | 579 if (!p.feedback().IsValid()) return NoChange(); |
| 891 LoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); | 580 LoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
| 892 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); | 581 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); |
| 893 DCHECK_LT(0, receiver_maps.length()); | 582 DCHECK_LT(0, receiver_maps.length()); |
| 894 | 583 |
| 895 // Try to lower the named access based on the {receiver_maps}. | 584 // Try to lower the named access based on the {receiver_maps}. |
| 896 return ReduceNamedAccess(node, value, receiver_maps, p.name(), kLoad); | 585 return ReduceNamedAccess(node, value, receiver_maps, p.name(), |
| 586 PropertyAccessMode::kLoad); |
| 897 } | 587 } |
| 898 | 588 |
| 899 | 589 |
| 900 Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) { | 590 Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) { |
| 901 DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode()); | 591 DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode()); |
| 902 NamedAccess const& p = NamedAccessOf(node->op()); | 592 NamedAccess const& p = NamedAccessOf(node->op()); |
| 903 Node* const value = NodeProperties::GetValueInput(node, 1); | 593 Node* const value = NodeProperties::GetValueInput(node, 1); |
| 904 | 594 |
| 905 // Extract receiver maps from the STORE_IC using the StoreICNexus. | 595 // Extract receiver maps from the STORE_IC using the StoreICNexus. |
| 906 MapHandleList receiver_maps; | 596 MapHandleList receiver_maps; |
| 907 if (!p.feedback().IsValid()) return NoChange(); | 597 if (!p.feedback().IsValid()) return NoChange(); |
| 908 StoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); | 598 StoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
| 909 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); | 599 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); |
| 910 DCHECK_LT(0, receiver_maps.length()); | 600 DCHECK_LT(0, receiver_maps.length()); |
| 911 | 601 |
| 912 // Try to lower the named access based on the {receiver_maps}. | 602 // Try to lower the named access based on the {receiver_maps}. |
| 913 return ReduceNamedAccess(node, value, receiver_maps, p.name(), kStore); | 603 return ReduceNamedAccess(node, value, receiver_maps, p.name(), |
| 604 PropertyAccessMode::kStore); |
| 914 } | 605 } |
| 915 | 606 |
| 916 | 607 |
| 917 Reduction JSNativeContextSpecialization::Replace(Node* node, | 608 Reduction JSNativeContextSpecialization::Replace(Node* node, |
| 918 Handle<Object> value) { | 609 Handle<Object> value) { |
| 919 return Replace(node, jsgraph()->Constant(value)); | 610 return Replace(node, jsgraph()->Constant(value)); |
| 920 } | 611 } |
| 921 | 612 |
| 922 | 613 |
| 923 bool JSNativeContextSpecialization::LookupInScriptContextTable( | 614 bool JSNativeContextSpecialization::LookupInScriptContextTable( |
| 924 Handle<Name> name, ScriptContextTableLookupResult* result) { | 615 Handle<Name> name, ScriptContextTableLookupResult* result) { |
| 925 if (!name->IsString()) return false; | 616 if (!name->IsString()) return false; |
| 926 Handle<ScriptContextTable> script_context_table( | 617 Handle<ScriptContextTable> script_context_table( |
| 927 global_object()->native_context()->script_context_table()); | 618 native_context()->script_context_table()); |
| 928 ScriptContextTable::LookupResult lookup_result; | 619 ScriptContextTable::LookupResult lookup_result; |
| 929 if (!ScriptContextTable::Lookup(script_context_table, | 620 if (!ScriptContextTable::Lookup(script_context_table, |
| 930 Handle<String>::cast(name), &lookup_result)) { | 621 Handle<String>::cast(name), &lookup_result)) { |
| 931 return false; | 622 return false; |
| 932 } | 623 } |
| 933 Handle<Context> script_context = ScriptContextTable::GetContext( | 624 Handle<Context> script_context = ScriptContextTable::GetContext( |
| 934 script_context_table, lookup_result.context_index); | 625 script_context_table, lookup_result.context_index); |
| 935 result->context = script_context; | 626 result->context = script_context; |
| 936 result->immutable = IsImmutableVariableMode(lookup_result.mode); | 627 result->immutable = IsImmutableVariableMode(lookup_result.mode); |
| 937 result->index = lookup_result.slot_index; | 628 result->index = lookup_result.slot_index; |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 995 } | 686 } |
| 996 | 687 |
| 997 | 688 |
| 998 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { | 689 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { |
| 999 return jsgraph()->simplified(); | 690 return jsgraph()->simplified(); |
| 1000 } | 691 } |
| 1001 | 692 |
| 1002 } // namespace compiler | 693 } // namespace compiler |
| 1003 } // namespace internal | 694 } // namespace internal |
| 1004 } // namespace v8 | 695 } // namespace v8 |
| OLD | NEW |