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/code-factory.h" | 8 #include "src/code-factory.h" |
9 #include "src/compilation-dependencies.h" | 9 #include "src/compilation-dependencies.h" |
10 #include "src/compiler/access-builder.h" | 10 #include "src/compiler/access-builder.h" |
(...skipping 289 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
300 } | 300 } |
301 effect = graph()->NewNode( | 301 effect = graph()->NewNode( |
302 simplified()->StoreField(AccessBuilder::ForPropertyCellValue()), | 302 simplified()->StoreField(AccessBuilder::ForPropertyCellValue()), |
303 jsgraph()->Constant(property_cell), value, effect, control); | 303 jsgraph()->Constant(property_cell), value, effect, control); |
304 return Replace(node, value, effect, control); | 304 return Replace(node, value, effect, control); |
305 } | 305 } |
306 | 306 |
307 | 307 |
308 Reduction JSNativeContextSpecialization::ReduceNamedAccess( | 308 Reduction JSNativeContextSpecialization::ReduceNamedAccess( |
309 Node* node, Node* value, MapHandleList const& receiver_maps, | 309 Node* node, Node* value, MapHandleList const& receiver_maps, |
310 Handle<Name> name, PropertyAccessMode access_mode, | 310 Handle<Name> name, AccessMode access_mode, LanguageMode language_mode, |
311 LanguageMode language_mode, Node* index) { | 311 Node* index) { |
312 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || | 312 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || |
313 node->opcode() == IrOpcode::kJSStoreNamed || | 313 node->opcode() == IrOpcode::kJSStoreNamed || |
314 node->opcode() == IrOpcode::kJSLoadProperty || | 314 node->opcode() == IrOpcode::kJSLoadProperty || |
315 node->opcode() == IrOpcode::kJSStoreProperty); | 315 node->opcode() == IrOpcode::kJSStoreProperty); |
316 Node* receiver = NodeProperties::GetValueInput(node, 0); | 316 Node* receiver = NodeProperties::GetValueInput(node, 0); |
317 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); | 317 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); |
318 Node* effect = NodeProperties::GetEffectInput(node); | 318 Node* effect = NodeProperties::GetEffectInput(node); |
319 Node* control = NodeProperties::GetControlInput(node); | 319 Node* control = NodeProperties::GetControlInput(node); |
320 | 320 |
321 // Not much we can do if deoptimization support is disabled. | 321 // Not much we can do if deoptimization support is disabled. |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
410 } | 410 } |
411 | 411 |
412 // Determine actual holder and perform prototype chain checks. | 412 // Determine actual holder and perform prototype chain checks. |
413 Handle<JSObject> holder; | 413 Handle<JSObject> holder; |
414 if (access_info.holder().ToHandle(&holder)) { | 414 if (access_info.holder().ToHandle(&holder)) { |
415 AssumePrototypesStable(receiver_type, holder); | 415 AssumePrototypesStable(receiver_type, holder); |
416 } | 416 } |
417 | 417 |
418 // Generate the actual property access. | 418 // Generate the actual property access. |
419 if (access_info.IsNotFound()) { | 419 if (access_info.IsNotFound()) { |
420 DCHECK_EQ(PropertyAccessMode::kLoad, access_mode); | 420 DCHECK_EQ(AccessMode::kLoad, access_mode); |
421 if (is_strong(language_mode)) { | 421 if (is_strong(language_mode)) { |
422 // TODO(bmeurer/mstarzinger): Add support for lowering inside try | 422 // TODO(bmeurer/mstarzinger): Add support for lowering inside try |
423 // blocks rewiring the IfException edge to a runtime call/throw. | 423 // blocks rewiring the IfException edge to a runtime call/throw. |
424 exit_controls.push_back(this_control); | 424 exit_controls.push_back(this_control); |
425 continue; | 425 continue; |
426 } else { | 426 } else { |
427 this_value = jsgraph()->UndefinedConstant(); | 427 this_value = jsgraph()->UndefinedConstant(); |
428 } | 428 } |
429 } else if (access_info.IsDataConstant()) { | 429 } else if (access_info.IsDataConstant()) { |
430 this_value = jsgraph()->Constant(access_info.constant()); | 430 this_value = jsgraph()->Constant(access_info.constant()); |
431 if (access_mode == PropertyAccessMode::kStore) { | 431 if (access_mode == AccessMode::kStore) { |
432 Node* check = graph()->NewNode( | 432 Node* check = graph()->NewNode( |
433 simplified()->ReferenceEqual(Type::Tagged()), value, this_value); | 433 simplified()->ReferenceEqual(Type::Tagged()), value, this_value); |
434 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 434 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
435 check, this_control); | 435 check, this_control); |
436 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | 436 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); |
437 this_control = graph()->NewNode(common()->IfTrue(), branch); | 437 this_control = graph()->NewNode(common()->IfTrue(), branch); |
438 } | 438 } |
439 } else { | 439 } else { |
440 DCHECK(access_info.IsDataField()); | 440 DCHECK(access_info.IsDataField()); |
441 FieldIndex const field_index = access_info.field_index(); | 441 FieldIndex const field_index = access_info.field_index(); |
442 Type* const field_type = access_info.field_type(); | 442 Type* const field_type = access_info.field_type(); |
443 if (access_mode == PropertyAccessMode::kLoad && | 443 if (access_mode == AccessMode::kLoad && |
444 access_info.holder().ToHandle(&holder)) { | 444 access_info.holder().ToHandle(&holder)) { |
445 this_receiver = jsgraph()->Constant(holder); | 445 this_receiver = jsgraph()->Constant(holder); |
446 } | 446 } |
447 Node* this_storage = this_receiver; | 447 Node* this_storage = this_receiver; |
448 if (!field_index.is_inobject()) { | 448 if (!field_index.is_inobject()) { |
449 this_storage = this_effect = graph()->NewNode( | 449 this_storage = this_effect = graph()->NewNode( |
450 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), | 450 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), |
451 this_storage, this_effect, this_control); | 451 this_storage, this_effect, this_control); |
452 } | 452 } |
453 FieldAccess field_access = {kTaggedBase, field_index.offset(), name, | 453 FieldAccess field_access = {kTaggedBase, field_index.offset(), name, |
454 field_type, kMachAnyTagged}; | 454 field_type, kMachAnyTagged}; |
455 if (access_mode == PropertyAccessMode::kLoad) { | 455 if (access_mode == AccessMode::kLoad) { |
456 if (field_type->Is(Type::UntaggedFloat64())) { | 456 if (field_type->Is(Type::UntaggedFloat64())) { |
457 if (!field_index.is_inobject() || field_index.is_hidden_field() || | 457 if (!field_index.is_inobject() || field_index.is_hidden_field() || |
458 !FLAG_unbox_double_fields) { | 458 !FLAG_unbox_double_fields) { |
459 this_storage = this_effect = | 459 this_storage = this_effect = |
460 graph()->NewNode(simplified()->LoadField(field_access), | 460 graph()->NewNode(simplified()->LoadField(field_access), |
461 this_storage, this_effect, this_control); | 461 this_storage, this_effect, this_control); |
462 field_access.offset = HeapNumber::kValueOffset; | 462 field_access.offset = HeapNumber::kValueOffset; |
463 field_access.name = MaybeHandle<Name>(); | 463 field_access.name = MaybeHandle<Name>(); |
464 } | 464 } |
465 field_access.machine_type = kMachFloat64; | 465 field_access.machine_type = kMachFloat64; |
466 } | 466 } |
467 this_value = this_effect = | 467 this_value = this_effect = |
468 graph()->NewNode(simplified()->LoadField(field_access), | 468 graph()->NewNode(simplified()->LoadField(field_access), |
469 this_storage, this_effect, this_control); | 469 this_storage, this_effect, this_control); |
470 } else { | 470 } else { |
471 DCHECK_EQ(PropertyAccessMode::kStore, access_mode); | 471 DCHECK_EQ(AccessMode::kStore, access_mode); |
472 if (field_type->Is(Type::UntaggedFloat64())) { | 472 if (field_type->Is(Type::UntaggedFloat64())) { |
473 Node* check = | 473 Node* check = |
474 graph()->NewNode(simplified()->ObjectIsNumber(), this_value); | 474 graph()->NewNode(simplified()->ObjectIsNumber(), this_value); |
475 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 475 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
476 check, this_control); | 476 check, this_control); |
477 exit_controls.push_back( | 477 exit_controls.push_back( |
478 graph()->NewNode(common()->IfFalse(), branch)); | 478 graph()->NewNode(common()->IfFalse(), branch)); |
479 this_control = graph()->NewNode(common()->IfTrue(), branch); | 479 this_control = graph()->NewNode(common()->IfTrue(), branch); |
480 this_value = graph()->NewNode(common()->Guard(Type::Number()), | 480 this_value = graph()->NewNode(common()->Guard(Type::Number()), |
481 this_value, this_control); | 481 this_value, this_control); |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
574 } | 574 } |
575 } | 575 } |
576 } | 576 } |
577 | 577 |
578 // Remember the final state for this property access. | 578 // Remember the final state for this property access. |
579 values.push_back(this_value); | 579 values.push_back(this_value); |
580 effects.push_back(this_effect); | 580 effects.push_back(this_effect); |
581 controls.push_back(this_control); | 581 controls.push_back(this_control); |
582 } | 582 } |
583 | 583 |
584 // Collect the fallthru control as final "exit" control. | 584 // Collect the fallthrough control as final "exit" control. |
585 if (fallthrough_control != control) { | 585 if (fallthrough_control != control) { |
586 // Mark the last fallthru branch as deferred. | 586 // Mark the last fallthrough branch as deferred. |
587 Node* branch = NodeProperties::GetControlInput(fallthrough_control); | 587 Node* branch = NodeProperties::GetControlInput(fallthrough_control); |
588 DCHECK_EQ(IrOpcode::kBranch, branch->opcode()); | 588 DCHECK_EQ(IrOpcode::kBranch, branch->opcode()); |
589 if (fallthrough_control->opcode() == IrOpcode::kIfTrue) { | 589 if (fallthrough_control->opcode() == IrOpcode::kIfTrue) { |
590 NodeProperties::ChangeOp(branch, common()->Branch(BranchHint::kFalse)); | 590 NodeProperties::ChangeOp(branch, common()->Branch(BranchHint::kFalse)); |
591 } else { | 591 } else { |
592 DCHECK_EQ(IrOpcode::kIfFalse, fallthrough_control->opcode()); | 592 DCHECK_EQ(IrOpcode::kIfFalse, fallthrough_control->opcode()); |
593 NodeProperties::ChangeOp(branch, common()->Branch(BranchHint::kTrue)); | 593 NodeProperties::ChangeOp(branch, common()->Branch(BranchHint::kTrue)); |
594 } | 594 } |
595 } | 595 } |
596 exit_controls.push_back(fallthrough_control); | 596 exit_controls.push_back(fallthrough_control); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
640 | 640 |
641 // Extract receiver maps from the LOAD_IC using the LoadICNexus. | 641 // Extract receiver maps from the LOAD_IC using the LoadICNexus. |
642 MapHandleList receiver_maps; | 642 MapHandleList receiver_maps; |
643 if (!p.feedback().IsValid()) return NoChange(); | 643 if (!p.feedback().IsValid()) return NoChange(); |
644 LoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); | 644 LoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
645 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); | 645 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); |
646 DCHECK_LT(0, receiver_maps.length()); | 646 DCHECK_LT(0, receiver_maps.length()); |
647 | 647 |
648 // Try to lower the named access based on the {receiver_maps}. | 648 // Try to lower the named access based on the {receiver_maps}. |
649 return ReduceNamedAccess(node, value, receiver_maps, p.name(), | 649 return ReduceNamedAccess(node, value, receiver_maps, p.name(), |
650 PropertyAccessMode::kLoad, p.language_mode()); | 650 AccessMode::kLoad, p.language_mode()); |
651 } | 651 } |
652 | 652 |
653 | 653 |
654 Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) { | 654 Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) { |
655 DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode()); | 655 DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode()); |
656 NamedAccess const& p = NamedAccessOf(node->op()); | 656 NamedAccess const& p = NamedAccessOf(node->op()); |
657 Node* const value = NodeProperties::GetValueInput(node, 1); | 657 Node* const value = NodeProperties::GetValueInput(node, 1); |
658 | 658 |
659 // Extract receiver maps from the STORE_IC using the StoreICNexus. | 659 // Extract receiver maps from the STORE_IC using the StoreICNexus. |
660 MapHandleList receiver_maps; | 660 MapHandleList receiver_maps; |
661 if (!p.feedback().IsValid()) return NoChange(); | 661 if (!p.feedback().IsValid()) return NoChange(); |
662 StoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); | 662 StoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
663 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); | 663 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); |
664 DCHECK_LT(0, receiver_maps.length()); | 664 DCHECK_LT(0, receiver_maps.length()); |
665 | 665 |
666 // Try to lower the named access based on the {receiver_maps}. | 666 // Try to lower the named access based on the {receiver_maps}. |
667 return ReduceNamedAccess(node, value, receiver_maps, p.name(), | 667 return ReduceNamedAccess(node, value, receiver_maps, p.name(), |
668 PropertyAccessMode::kStore, p.language_mode()); | 668 AccessMode::kStore, p.language_mode()); |
| 669 } |
| 670 |
| 671 |
| 672 Reduction JSNativeContextSpecialization::ReduceElementAccess( |
| 673 Node* node, Node* index, Node* value, MapHandleList const& receiver_maps, |
| 674 AccessMode access_mode, LanguageMode language_mode) { |
| 675 DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || |
| 676 node->opcode() == IrOpcode::kJSStoreProperty); |
| 677 Node* receiver = NodeProperties::GetValueInput(node, 0); |
| 678 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); |
| 679 Node* effect = NodeProperties::GetEffectInput(node); |
| 680 Node* control = NodeProperties::GetControlInput(node); |
| 681 |
| 682 // Not much we can do if deoptimization support is disabled. |
| 683 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); |
| 684 |
| 685 // Compute element access infos for the receiver maps. |
| 686 ZoneVector<ElementAccessInfo> access_infos(zone()); |
| 687 if (!access_info_factory().ComputeElementAccessInfos( |
| 688 receiver_maps, access_mode, &access_infos)) { |
| 689 return NoChange(); |
| 690 } |
| 691 |
| 692 // Nothing to do if we have no non-deprecated maps. |
| 693 if (access_infos.empty()) return NoChange(); |
| 694 |
| 695 // The final states for every polymorphic branch. We join them with |
| 696 // Merge+Phi+EffectPhi at the bottom. |
| 697 ZoneVector<Node*> values(zone()); |
| 698 ZoneVector<Node*> effects(zone()); |
| 699 ZoneVector<Node*> controls(zone()); |
| 700 |
| 701 // The list of "exiting" controls, which currently go to a single deoptimize. |
| 702 // TODO(bmeurer): Consider using an IC as fallback. |
| 703 Node* const exit_effect = effect; |
| 704 ZoneVector<Node*> exit_controls(zone()); |
| 705 |
| 706 // Ensure that {receiver} is a heap object. |
| 707 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); |
| 708 Node* branch = |
| 709 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); |
| 710 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); |
| 711 control = graph()->NewNode(common()->IfFalse(), branch); |
| 712 |
| 713 // Load the {receiver} map. The resulting effect is the dominating effect for |
| 714 // all (polymorphic) branches. |
| 715 Node* receiver_map = effect = |
| 716 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
| 717 receiver, effect, control); |
| 718 |
| 719 // Generate code for the various different element access patterns. |
| 720 Node* fallthrough_control = control; |
| 721 for (ElementAccessInfo const& access_info : access_infos) { |
| 722 Node* this_receiver = receiver; |
| 723 Node* this_value = value; |
| 724 Node* this_index = index; |
| 725 Node* this_effect = effect; |
| 726 Node* this_control; |
| 727 |
| 728 // Perform map check on {receiver}. |
| 729 Type* receiver_type = access_info.receiver_type(); |
| 730 { |
| 731 ZoneVector<Node*> this_controls(zone()); |
| 732 for (auto i = access_info.receiver_type()->Classes(); !i.Done(); |
| 733 i.Advance()) { |
| 734 Handle<Map> map = i.Current(); |
| 735 Node* check = |
| 736 graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), |
| 737 receiver_map, jsgraph()->Constant(map)); |
| 738 Node* branch = |
| 739 graph()->NewNode(common()->Branch(), check, fallthrough_control); |
| 740 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); |
| 741 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); |
| 742 } |
| 743 int const this_control_count = static_cast<int>(this_controls.size()); |
| 744 this_control = |
| 745 (this_control_count == 1) |
| 746 ? this_controls.front() |
| 747 : graph()->NewNode(common()->Merge(this_control_count), |
| 748 this_control_count, &this_controls.front()); |
| 749 } |
| 750 |
| 751 // Certain stores need a prototype chain check because shape changes |
| 752 // could allow callbacks on elements in the prototype chain that are |
| 753 // not compatible with (monomorphic) keyed stores. |
| 754 Handle<JSObject> holder; |
| 755 if (access_info.holder().ToHandle(&holder)) { |
| 756 AssumePrototypesStable(receiver_type, holder); |
| 757 } |
| 758 |
| 759 // Check that the {index} is actually a Number. |
| 760 if (!NumberMatcher(this_index).HasValue()) { |
| 761 Node* check = |
| 762 graph()->NewNode(simplified()->ObjectIsNumber(), this_index); |
| 763 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
| 764 check, this_control); |
| 765 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); |
| 766 this_control = graph()->NewNode(common()->IfTrue(), branch); |
| 767 this_index = graph()->NewNode(common()->Guard(Type::Number()), this_index, |
| 768 this_control); |
| 769 } |
| 770 |
| 771 // Convert the {index} to an unsigned32 value and check if the result is |
| 772 // equal to the original {index}. |
| 773 if (!NumberMatcher(this_index).IsInRange(0.0, kMaxUInt32)) { |
| 774 Node* this_index32 = |
| 775 graph()->NewNode(simplified()->NumberToUint32(), this_index); |
| 776 Node* check = graph()->NewNode(simplified()->NumberEqual(), this_index32, |
| 777 this_index); |
| 778 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
| 779 check, this_control); |
| 780 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); |
| 781 this_control = graph()->NewNode(common()->IfTrue(), branch); |
| 782 this_index = this_index32; |
| 783 } |
| 784 |
| 785 // TODO(bmeurer): We currently specialize based on elements kind. We should |
| 786 // also be able to properly support strings and other JSObjects here. |
| 787 ElementsKind elements_kind = access_info.elements_kind(); |
| 788 |
| 789 // Load the elements for the {receiver}. |
| 790 Node* this_elements = this_effect = graph()->NewNode( |
| 791 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), |
| 792 this_receiver, this_effect, this_control); |
| 793 |
| 794 // Don't try to store to a copy-on-write backing store. |
| 795 if (access_mode == AccessMode::kStore && |
| 796 IsFastSmiOrObjectElementsKind(elements_kind)) { |
| 797 Node* this_elements_map = this_effect = |
| 798 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
| 799 this_elements, this_effect, this_control); |
| 800 check = graph()->NewNode( |
| 801 simplified()->ReferenceEqual(Type::Any()), this_elements_map, |
| 802 jsgraph()->HeapConstant(factory()->fixed_array_map())); |
| 803 branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, |
| 804 this_control); |
| 805 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); |
| 806 this_control = graph()->NewNode(common()->IfTrue(), branch); |
| 807 } |
| 808 |
| 809 // Load the length of the {receiver}. |
| 810 FieldAccess length_access = { |
| 811 kTaggedBase, JSArray::kLengthOffset, factory()->name_string(), |
| 812 type_cache_.kJSArrayLengthType, kMachAnyTagged}; |
| 813 if (IsFastDoubleElementsKind(elements_kind)) { |
| 814 length_access.type = type_cache_.kFixedDoubleArrayLengthType; |
| 815 } else if (IsFastElementsKind(elements_kind)) { |
| 816 length_access.type = type_cache_.kFixedArrayLengthType; |
| 817 } |
| 818 Node* this_length = this_effect = |
| 819 graph()->NewNode(simplified()->LoadField(length_access), this_receiver, |
| 820 this_effect, this_control); |
| 821 |
| 822 // Check that the {index} is in the valid range for the {receiver}. |
| 823 Node* check = graph()->NewNode(simplified()->NumberLessThan(), this_index, |
| 824 this_length); |
| 825 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, |
| 826 this_control); |
| 827 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); |
| 828 this_control = graph()->NewNode(common()->IfTrue(), branch); |
| 829 |
| 830 // Compute the element access. |
| 831 Type* element_type = Type::Any(); |
| 832 MachineType element_machine_type = kMachAnyTagged; |
| 833 if (IsFastDoubleElementsKind(elements_kind)) { |
| 834 element_type = type_cache_.kFloat64; |
| 835 element_machine_type = kMachFloat64; |
| 836 } else if (IsFastSmiElementsKind(elements_kind)) { |
| 837 element_type = type_cache_.kSmi; |
| 838 } |
| 839 ElementAccess element_access = {kTaggedBase, FixedArray::kHeaderSize, |
| 840 element_type, element_machine_type}; |
| 841 |
| 842 // Access the actual element. |
| 843 if (access_mode == AccessMode::kLoad) { |
| 844 this_value = this_effect = graph()->NewNode( |
| 845 simplified()->LoadElement(element_access), this_elements, this_index, |
| 846 this_effect, this_control); |
| 847 } else { |
| 848 DCHECK_EQ(AccessMode::kStore, access_mode); |
| 849 if (IsFastSmiElementsKind(elements_kind)) { |
| 850 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), this_value); |
| 851 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
| 852 check, this_control); |
| 853 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); |
| 854 this_control = graph()->NewNode(common()->IfTrue(), branch); |
| 855 } else if (IsFastDoubleElementsKind(elements_kind)) { |
| 856 Node* check = |
| 857 graph()->NewNode(simplified()->ObjectIsNumber(), this_value); |
| 858 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
| 859 check, this_control); |
| 860 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); |
| 861 this_control = graph()->NewNode(common()->IfTrue(), branch); |
| 862 this_value = graph()->NewNode(common()->Guard(Type::Number()), |
| 863 this_value, this_control); |
| 864 } |
| 865 this_effect = graph()->NewNode(simplified()->StoreElement(element_access), |
| 866 this_elements, this_index, this_value, |
| 867 this_effect, this_control); |
| 868 } |
| 869 |
| 870 // Remember the final state for this element access. |
| 871 values.push_back(this_value); |
| 872 effects.push_back(this_effect); |
| 873 controls.push_back(this_control); |
| 874 } |
| 875 |
| 876 // Collect the fallthrough control as final "exit" control. |
| 877 if (fallthrough_control != control) { |
| 878 // Mark the last fallthrough branch as deferred. |
| 879 Node* branch = NodeProperties::GetControlInput(fallthrough_control); |
| 880 DCHECK_EQ(IrOpcode::kBranch, branch->opcode()); |
| 881 if (fallthrough_control->opcode() == IrOpcode::kIfTrue) { |
| 882 NodeProperties::ChangeOp(branch, common()->Branch(BranchHint::kFalse)); |
| 883 } else { |
| 884 DCHECK_EQ(IrOpcode::kIfFalse, fallthrough_control->opcode()); |
| 885 NodeProperties::ChangeOp(branch, common()->Branch(BranchHint::kTrue)); |
| 886 } |
| 887 } |
| 888 exit_controls.push_back(fallthrough_control); |
| 889 |
| 890 // Generate the single "exit" point, where we get if either all map/instance |
| 891 // type checks failed, or one of the assumptions inside one of the cases |
| 892 // failes (i.e. failing prototype chain check). |
| 893 // TODO(bmeurer): Consider falling back to IC here if deoptimization is |
| 894 // disabled. |
| 895 int const exit_control_count = static_cast<int>(exit_controls.size()); |
| 896 Node* exit_control = |
| 897 (exit_control_count == 1) |
| 898 ? exit_controls.front() |
| 899 : graph()->NewNode(common()->Merge(exit_control_count), |
| 900 exit_control_count, &exit_controls.front()); |
| 901 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, |
| 902 exit_effect, exit_control); |
| 903 // TODO(bmeurer): This should be on the AdvancedReducer somehow. |
| 904 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); |
| 905 |
| 906 // Generate the final merge point for all (polymorphic) branches. |
| 907 int const control_count = static_cast<int>(controls.size()); |
| 908 if (control_count == 0) { |
| 909 value = effect = control = jsgraph()->Dead(); |
| 910 } else if (control_count == 1) { |
| 911 value = values.front(); |
| 912 effect = effects.front(); |
| 913 control = controls.front(); |
| 914 } else { |
| 915 control = graph()->NewNode(common()->Merge(control_count), control_count, |
| 916 &controls.front()); |
| 917 values.push_back(control); |
| 918 value = graph()->NewNode(common()->Phi(kMachAnyTagged, control_count), |
| 919 control_count + 1, &values.front()); |
| 920 effects.push_back(control); |
| 921 effect = graph()->NewNode(common()->EffectPhi(control_count), |
| 922 control_count + 1, &effects.front()); |
| 923 } |
| 924 return Replace(node, value, effect, control); |
669 } | 925 } |
670 | 926 |
671 | 927 |
672 Reduction JSNativeContextSpecialization::ReduceKeyedAccess( | 928 Reduction JSNativeContextSpecialization::ReduceKeyedAccess( |
673 Node* node, Node* index, Node* value, FeedbackNexus const& nexus, | 929 Node* node, Node* index, Node* value, FeedbackNexus const& nexus, |
674 PropertyAccessMode access_mode, LanguageMode language_mode) { | 930 AccessMode access_mode, LanguageMode language_mode) { |
675 DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || | 931 DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || |
676 node->opcode() == IrOpcode::kJSStoreProperty); | 932 node->opcode() == IrOpcode::kJSStoreProperty); |
677 | 933 |
678 // Extract receiver maps from the {nexus}. | 934 // Extract receiver maps from the {nexus}. |
679 MapHandleList receiver_maps; | 935 MapHandleList receiver_maps; |
680 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); | 936 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); |
681 DCHECK_LT(0, receiver_maps.length()); | 937 DCHECK_LT(0, receiver_maps.length()); |
682 | 938 |
683 // Optimize access for constant {index}. | 939 // Optimize access for constant {index}. |
684 HeapObjectMatcher mindex(index); | 940 HeapObjectMatcher mindex(index); |
685 if (mindex.HasValue() && mindex.Value()->IsPrimitive()) { | 941 if (mindex.HasValue() && mindex.Value()->IsPrimitive()) { |
686 // Keyed access requires a ToPropertyKey on the {index} first before | 942 // Keyed access requires a ToPropertyKey on the {index} first before |
687 // looking up the property on the object (see ES6 section 12.3.2.1). | 943 // looking up the property on the object (see ES6 section 12.3.2.1). |
688 // We can only do this for non-observable ToPropertyKey invocations, | 944 // We can only do this for non-observable ToPropertyKey invocations, |
689 // so we limit the constant indices to primitives at this point. | 945 // so we limit the constant indices to primitives at this point. |
690 Handle<Name> name; | 946 Handle<Name> name; |
691 if (Object::ToName(isolate(), mindex.Value()).ToHandle(&name)) { | 947 if (Object::ToName(isolate(), mindex.Value()).ToHandle(&name)) { |
692 uint32_t array_index; | 948 uint32_t array_index; |
693 if (name->AsArrayIndex(&array_index)) { | 949 if (name->AsArrayIndex(&array_index)) { |
694 // TODO(bmeurer): Optimize element access with constant {index}. | 950 // Use the constant array index. |
| 951 index = jsgraph()->Constant(static_cast<double>(array_index)); |
695 } else { | 952 } else { |
696 name = factory()->InternalizeName(name); | 953 name = factory()->InternalizeName(name); |
697 return ReduceNamedAccess(node, value, receiver_maps, name, access_mode, | 954 return ReduceNamedAccess(node, value, receiver_maps, name, access_mode, |
698 language_mode); | 955 language_mode); |
699 } | 956 } |
700 } | 957 } |
701 } | 958 } |
702 | 959 |
703 // Check if we have feedback for a named access. | 960 // Check if we have feedback for a named access. |
704 if (Name* name = nexus.FindFirstName()) { | 961 if (Name* name = nexus.FindFirstName()) { |
705 return ReduceNamedAccess(node, value, receiver_maps, | 962 return ReduceNamedAccess(node, value, receiver_maps, |
706 handle(name, isolate()), access_mode, | 963 handle(name, isolate()), access_mode, |
707 language_mode, index); | 964 language_mode, index); |
708 } | 965 } |
709 | 966 |
710 return NoChange(); | 967 // Try to lower the element access based on the {receiver_maps}. |
| 968 return ReduceElementAccess(node, index, value, receiver_maps, access_mode, |
| 969 language_mode); |
711 } | 970 } |
712 | 971 |
713 | 972 |
714 Reduction JSNativeContextSpecialization::ReduceJSLoadProperty(Node* node) { | 973 Reduction JSNativeContextSpecialization::ReduceJSLoadProperty(Node* node) { |
715 DCHECK_EQ(IrOpcode::kJSLoadProperty, node->opcode()); | 974 DCHECK_EQ(IrOpcode::kJSLoadProperty, node->opcode()); |
716 PropertyAccess const& p = PropertyAccessOf(node->op()); | 975 PropertyAccess const& p = PropertyAccessOf(node->op()); |
717 Node* const index = NodeProperties::GetValueInput(node, 1); | 976 Node* const index = NodeProperties::GetValueInput(node, 1); |
718 Node* const value = jsgraph()->Dead(); | 977 Node* const value = jsgraph()->Dead(); |
719 | 978 |
720 // Extract receiver maps from the KEYED_LOAD_IC using the KeyedLoadICNexus. | 979 // Extract receiver maps from the KEYED_LOAD_IC using the KeyedLoadICNexus. |
721 if (!p.feedback().IsValid()) return NoChange(); | 980 if (!p.feedback().IsValid()) return NoChange(); |
722 KeyedLoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); | 981 KeyedLoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
723 | 982 |
724 // Try to lower the keyed access based on the {nexus}. | 983 // Try to lower the keyed access based on the {nexus}. |
725 return ReduceKeyedAccess(node, index, value, nexus, PropertyAccessMode::kLoad, | 984 return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kLoad, |
726 p.language_mode()); | 985 p.language_mode()); |
727 } | 986 } |
728 | 987 |
729 | 988 |
730 Reduction JSNativeContextSpecialization::ReduceJSStoreProperty(Node* node) { | 989 Reduction JSNativeContextSpecialization::ReduceJSStoreProperty(Node* node) { |
731 DCHECK_EQ(IrOpcode::kJSStoreProperty, node->opcode()); | 990 DCHECK_EQ(IrOpcode::kJSStoreProperty, node->opcode()); |
732 PropertyAccess const& p = PropertyAccessOf(node->op()); | 991 PropertyAccess const& p = PropertyAccessOf(node->op()); |
733 Node* const index = NodeProperties::GetValueInput(node, 1); | 992 Node* const index = NodeProperties::GetValueInput(node, 1); |
734 Node* const value = NodeProperties::GetValueInput(node, 2); | 993 Node* const value = NodeProperties::GetValueInput(node, 2); |
735 | 994 |
736 // Extract receiver maps from the KEYED_STORE_IC using the KeyedStoreICNexus. | 995 // Extract receiver maps from the KEYED_STORE_IC using the KeyedStoreICNexus. |
737 if (!p.feedback().IsValid()) return NoChange(); | 996 if (!p.feedback().IsValid()) return NoChange(); |
738 KeyedStoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); | 997 KeyedStoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
739 | 998 |
740 // Try to lower the keyed access based on the {nexus}. | 999 // Try to lower the keyed access based on the {nexus}. |
741 return ReduceKeyedAccess(node, index, value, nexus, | 1000 return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kStore, |
742 PropertyAccessMode::kStore, p.language_mode()); | 1001 p.language_mode()); |
743 } | 1002 } |
744 | 1003 |
745 | 1004 |
746 Reduction JSNativeContextSpecialization::Replace(Node* node, | 1005 Reduction JSNativeContextSpecialization::Replace(Node* node, |
747 Handle<Object> value) { | 1006 Handle<Object> value) { |
748 return Replace(node, jsgraph()->Constant(value)); | 1007 return Replace(node, jsgraph()->Constant(value)); |
749 } | 1008 } |
750 | 1009 |
751 | 1010 |
752 bool JSNativeContextSpecialization::LookupInScriptContextTable( | 1011 bool JSNativeContextSpecialization::LookupInScriptContextTable( |
(...skipping 20 matching lines...) Expand all Loading... |
773 // Determine actual holder and perform prototype chain checks. | 1032 // Determine actual holder and perform prototype chain checks. |
774 for (auto i = receiver_type->Classes(); !i.Done(); i.Advance()) { | 1033 for (auto i = receiver_type->Classes(); !i.Done(); i.Advance()) { |
775 Handle<Map> map = i.Current(); | 1034 Handle<Map> map = i.Current(); |
776 // Perform the implicit ToObject for primitives here. | 1035 // Perform the implicit ToObject for primitives here. |
777 // Implemented according to ES6 section 7.3.2 GetV (V, P). | 1036 // Implemented according to ES6 section 7.3.2 GetV (V, P). |
778 Handle<JSFunction> constructor; | 1037 Handle<JSFunction> constructor; |
779 if (Map::GetConstructorFunction(map, native_context()) | 1038 if (Map::GetConstructorFunction(map, native_context()) |
780 .ToHandle(&constructor)) { | 1039 .ToHandle(&constructor)) { |
781 map = handle(constructor->initial_map(), isolate()); | 1040 map = handle(constructor->initial_map(), isolate()); |
782 } | 1041 } |
783 for (PrototypeIterator j(map);; j.Advance()) { | 1042 for (PrototypeIterator j(map); !j.IsAtEnd(); j.Advance()) { |
784 // Check that the {prototype} still has the same map. All prototype | 1043 // Check that the {prototype} still has the same map. All prototype |
785 // maps are guaranteed to be stable, so it's sufficient to add a | 1044 // maps are guaranteed to be stable, so it's sufficient to add a |
786 // stability dependency here. | 1045 // stability dependency here. |
787 Handle<JSReceiver> const prototype = | 1046 Handle<JSReceiver> const prototype = |
788 PrototypeIterator::GetCurrent<JSReceiver>(j); | 1047 PrototypeIterator::GetCurrent<JSReceiver>(j); |
789 dependencies()->AssumeMapStable(handle(prototype->map(), isolate())); | 1048 dependencies()->AssumeMapStable(handle(prototype->map(), isolate())); |
790 // Stop once we get to the holder. | 1049 // Stop once we get to the holder. |
791 if (prototype.is_identical_to(holder)) break; | 1050 if (prototype.is_identical_to(holder)) break; |
792 } | 1051 } |
793 } | 1052 } |
(...skipping 30 matching lines...) Expand all Loading... |
824 } | 1083 } |
825 | 1084 |
826 | 1085 |
827 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { | 1086 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { |
828 return jsgraph()->simplified(); | 1087 return jsgraph()->simplified(); |
829 } | 1088 } |
830 | 1089 |
831 } // namespace compiler | 1090 } // namespace compiler |
832 } // namespace internal | 1091 } // namespace internal |
833 } // namespace v8 | 1092 } // namespace v8 |
OLD | NEW |